1 sc_require("views/view/base"); 2 3 // When in debug mode, core developers can log the view state. 4 //@if (debug) 5 SC.LOG_VIEW_STATES = false; 6 SC.LOG_VIEW_STATES_STYLE = { 7 0x0200: 'color: #67b7db; font-style: italic;', // UNRENDERED 8 0x0300: 'color: #67b7db; font-style: italic;', // UNATTACHED 9 0x0380: 'color: #67b7db; font-style: italic;', // ATTACHED_PARTIAL 10 0x03C0: 'color: #23abf5; font-style: italic;', // ATTACHED_SHOWN 11 0x03C4: 'color: #1fe7a8; font-style: italic;', // ATTACHED_SHOWN_ANIMATING 12 0x03A0: 'color: #67b7db; font-style: italic;', // ATTACHED_HIDDEN 13 0x03A1: 'color: #67b7db; font-style: italic;', // ATTACHED_HIDDEN_BY_PARENT 14 0x03C1: 'color: #b800db; font-style: italic;', // ATTACHED_BUILDING_IN 15 0x0381: 'color: #b800db; font-style: italic;', // ATTACHED_BUILDING_OUT 16 0x0382: 'color: #b800db; font-style: italic;', // ATTACHED_BUILDING_OUT_BY_PARENT 17 0x03C2: 'color: #b800db; font-style: italic;', // ATTACHED_SHOWING 18 0x03C3: 'color: #b800db; font-style: italic;' // ATTACHED_HIDING 19 }; 20 //@endif 21 22 23 SC.CoreView.mixin( 24 /** @scope SC.CoreView */ { 25 26 // State bit masks 27 28 // Logically always present, so not necessary, but here for logical ordering. 29 // IS_CREATED: 0x0200, // 10 0000 0000, 512 30 31 /** 32 The view has been rendered. 33 34 Use a logical AND (single `&`) to test rendered status. For example, 35 36 view.get('viewState') & SC.CoreView.IS_RENDERED 37 38 @static 39 @constant 40 */ 41 IS_RENDERED: 0x0100, // 01 0000 0000, 256 42 43 /** 44 The view has been attached. 45 46 Use a logical AND (single `&`) to test attached status. For example, 47 48 view.get('viewState') & SC.CoreView.IS_ATTACHED 49 50 @static 51 @constant 52 */ 53 IS_ATTACHED: 0x0080, // 00 1000 0000, 128 54 55 /** 56 The view is visible in the display. 57 58 Use a logical AND (single `&`) to test shown status. For example, 59 60 view.get('viewState') & SC.CoreView.IS_SHOWN 61 62 @static 63 @constant 64 */ 65 IS_SHOWN: 0x0040, // 00 0100 0000, 64 66 67 /** 68 The view is invisible in the display. 69 70 Use a logical AND (single `&`) to test hidden status. For example, 71 72 view.get('viewState') & SC.CoreView.IS_HIDDEN 73 74 @static 75 @constant 76 */ 77 IS_HIDDEN: 0x0020, // 00 0010 0000, 32 78 79 // Main states 80 81 /** 82 The view has been created, but has not been rendered or attached. 83 84 @static 85 @constant 86 */ 87 UNRENDERED: 0x0200, // 10 0000 0000, 512 (IS_CREATED) 88 89 /** 90 The view has been created and rendered, but has not been attached 91 (i.e. appended to the document). 92 93 @static 94 @constant 95 */ 96 UNATTACHED: 0x0300, // 11 0000 0000, 768 (IS_CREATED + IS_RENDERED) 97 98 /** 99 The view has been created and rendered and attached to a parent, but an ancestor is not 100 attached. 101 102 @static 103 @constant 104 */ 105 ATTACHED_PARTIAL: 0x0380, // 11 1000 0000, 896 (IS_CREATED + IS_RENDERED + IS_ATTACHED) 106 107 /** 108 The view has been created, rendered and attached, but is not visible in the 109 display. 110 111 Test with & SC.CoreView.IS_HIDDEN 112 @static 113 @constant 114 */ 115 ATTACHED_HIDDEN: 0x03A0, // 11 1010 0000, 928 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_HIDDEN) 116 117 /** 118 The view has been created, rendered and attached and is visible in the 119 display. 120 121 @static 122 @constant 123 */ 124 ATTACHED_SHOWN: 0x03C0, // 11 1100 0000, 960 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN) 125 126 // Minor states 127 128 /** 129 The view has been created, rendered and attached, but is not visible in the 130 display due to being hidden by a parent view. 131 132 @static 133 @constant 134 */ 135 ATTACHED_HIDDEN_BY_PARENT: 0x03A1, // 929 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_HIDDEN +) 136 137 /** 138 The view has been created, rendered and attached and is visible in the 139 display. It is currently transitioning according to the transitionIn 140 property before being fully shown (i.e ATTACHED_SHOWN). 141 142 @static 143 @constant 144 */ 145 ATTACHED_BUILDING_IN: 0x03C1, // 961 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 146 147 /** 148 The view has been created, rendered and attached. It is currently 149 transitioning according to the transitionOut property before being 150 detached (i.e. removed from the document). 151 152 @static 153 @constant 154 */ 155 ATTACHED_BUILDING_OUT: 0x03C4, // 964 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 156 157 /** 158 The view has been created, rendered and attached. It is currently 159 transitioning according to the transitionOut property before being 160 detached (i.e. removed from the document) because a parent view is 161 being detached. 162 163 @static 164 @constant 165 */ 166 ATTACHED_BUILDING_OUT_BY_PARENT: 0x03C5, // 965 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 167 168 /** 169 The view has been created, rendered and attached and is visible in the 170 display. It is currently transitioning according to the transitionShow 171 property before being fully shown (i.e ATTACHED_SHOWN). 172 173 @static 174 @constant 175 */ 176 ATTACHED_SHOWING: 0x03C2, // 962 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 177 178 /** 179 The view has been created, rendered and attached. It is currently 180 transitioning according to the transitionHide property before being fully 181 hidden. 182 183 @static 184 @constant 185 */ 186 ATTACHED_HIDING: 0x03C3, // 963 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 187 188 /** 189 The view has been created, rendered and attached, is visible in the 190 display and is being animated via a call to `animate()`. 191 192 @static 193 @constant 194 */ 195 ATTACHED_SHOWN_ANIMATING: 0x03C6 // 966 (IS_CREATED + IS_RENDERED + IS_ATTACHED + IS_SHOWN +) 196 197 }); 198 199 200 SC.CoreView.reopen( 201 /** @scope SC.CoreView.prototype */ { 202 203 //@if(debug) 204 /* BEGIN DEBUG ONLY PROPERTIES AND METHODS */ 205 206 /** @private Creates string representation of view, with view state. */ 207 toString: function () { 208 return "%@ (%@)".fmt(sc_super(), this._viewStateString()); 209 }, 210 211 /** @private Creates string representation of view state. */ 212 _viewStateString: function () { 213 var ret = [], state = this.get('viewState'); 214 215 for (var prop in SC.CoreView) { 216 if (prop.match(/[A-Z_]$/) && SC.CoreView[prop] === state) { 217 ret.push(prop); 218 } 219 } 220 221 return ret.join(" "); 222 }, 223 224 /* END DEBUG ONLY PROPERTIES AND METHODS */ 225 //@endif 226 227 // ------------------------------------------------------------------------ 228 // Properties 229 // 230 231 /* @private Internal variable used to store the number of children building out while we wait to be detached. */ 232 _sc_buildOutCount: null, 233 234 /* @private Internal variable used to track the original view being detached that we are delaying so that we can build out. */ 235 _owningView: null, 236 237 /* @private Internal variable used to store the original layout before running an automatic transition. */ 238 _preTransitionLayout: null, 239 240 /* @private Internal variable used to store the original frame before running an automatic transition. */ 241 _preTransitionFrame: null, 242 243 /* @private Internal variable used to cache layout properties which must be reset after the transition. */ 244 _transitionLayoutCache: null, 245 246 /** 247 The current state of the view as managed by its internal statechart. 248 249 In order to optimize the behavior of SC.View, such as only observing display 250 properties when in a rendered state or queueing updates when in a non-shown 251 state, SC.View includes a simple internal statechart that maintains the 252 current state of the view. 253 254 Views have several possible states: 255 256 * SC.CoreView.UNRENDERED 257 * SC.CoreView.UNATTACHED 258 * SC.CoreView.ATTACHED_PARTIAL 259 * SC.CoreView.ATTACHED_SHOWING 260 * SC.CoreView.ATTACHED_SHOWN 261 * SC.CoreView.ATTACHED_SHOWN_ANIMATING 262 * SC.CoreView.ATTACHED_HIDING 263 * SC.CoreView.ATTACHED_HIDDEN 264 * SC.CoreView.ATTACHED_HIDDEN_BY_PARENT 265 * SC.CoreView.ATTACHED_BUILDING_IN 266 * SC.CoreView.ATTACHED_BUILDING_OUT 267 * SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT 268 269 @type String 270 @default SC.CoreView.UNRENDERED 271 @readonly 272 */ 273 viewState: SC.CoreView.UNRENDERED, 274 275 /** 276 Whether the view's layer is attached to a parent or not. 277 278 When the view's layer is attached to a parent view, this value will be true. 279 280 @field 281 @type Boolean 282 @default false 283 @readonly 284 */ 285 isAttached: function () { 286 return (this.get('viewState') & SC.CoreView.IS_ATTACHED) > 0; 287 }.property('viewState').cacheable(), 288 289 /** @private 290 Whether the view's layer exists or not. 291 292 When the view's layer is created, this value will be true. This includes 293 the unattached view state and all of the attached states. 294 295 @field 296 @type Boolean 297 @default false 298 @readonly 299 */ 300 // NOTE: This property is of little value, so it's private in case we decide to toss it. 301 _isRendered: function () { 302 return (this.get('viewState') & SC.CoreView.IS_RENDERED) > 0; 303 }.property('viewState').cacheable(), 304 305 /** 306 Whether the view is fully or becoming shown or not. 307 308 When the view is shown in the window, this value will be true. Note that 309 if the view is transitioning out or hiding, this value will still be true. 310 311 This is not necessarily the same as `isVisible` although the two properties 312 are related. For instance, it's possible to set `isVisible` to `true` and 313 still have `isVisibleInWindow` be `false` or vice versa due to the 314 `isVisibleInWindow` state of the view's parent view. Therefore, 315 `isVisibleInWindow` represents the actual visible state of the view and 316 `isVisible` is used to attempt to alter that state. 317 318 @field 319 @type Boolean 320 @default false 321 @readonly 322 */ 323 isVisibleInWindow: function () { 324 return( this.get('viewState') & SC.CoreView.IS_SHOWN) > 0; 325 }.property('viewState').cacheable(), 326 327 328 // ------------------------------------------------------------------------ 329 // Actions (Locked down to the proper state) 330 // 331 332 /** @private Adopt this view action. */ 333 _doAdopt: function (parentView, beforeView) { 334 var curParentView = this.get('parentView'), 335 handled = true, 336 state = this.get('viewState'); 337 338 //@if (debug) 339 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 340 SC.Logger.log('%c%@ — _doAdopt(%@, %@): curParentView: %@'.fmt(this, parentView, beforeView, curParentView), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 341 } 342 //@endif 343 344 if (curParentView && curParentView !== parentView) { 345 //@if(debug) 346 // This should be avoided, because using the same view instance without explicitly orphaning it first is a dangerous practice. 347 SC.warn("Developer Warning: You should not adopt the view, %@, to a new parent without removing it from its old parent first.".fmt(this)); 348 //@endif 349 350 // Force orphaning the view. 351 this._doOrphan(); 352 curParentView = false; 353 } 354 355 // You can adopt childViews that have you set as their parent (i.e. created 356 // with createChildView()), but have not yet been fully adopted. 357 var siblings = parentView.get('childViews'); 358 if (!curParentView || siblings.indexOf(this) < 0) { 359 var idx, 360 parentViewState = parentView.get('viewState'), 361 parentNode, nextNode, nextView; 362 363 // Notify that the child view will be added to the parent view. 364 if (parentView.willAddChild) { parentView.willAddChild(this, beforeView); } 365 if (this.willAddToParent) { this.willAddToParent(parentView, beforeView); } 366 367 // Set the parentView. 368 this.set('parentView', parentView); 369 370 // Add to the new parent's childViews array. 371 if (siblings.needsClone) { parentView.set('childViews', []); } 372 idx = (beforeView) ? siblings.indexOf(beforeView) : siblings.length; 373 if (idx < 0) { idx = siblings.length; } 374 siblings.insertAt(idx, this); 375 376 // Pass the current designMode to the view (and its children). 377 this.updateDesignMode(this.get('designMode'), parentView.get('designMode')); 378 379 // Notify adopted. 380 this._adopted(beforeView); 381 382 // When a view is adopted, it should go to the same state as its new parent. 383 switch (state) { 384 case SC.CoreView.UNRENDERED: 385 switch (parentViewState) { 386 case SC.CoreView.UNRENDERED: 387 break; 388 default: 389 // Bypass the unrendered state for adopted views. 390 this._doRender(); 391 } 392 break; 393 case SC.CoreView.UNATTACHED: 394 switch (parentViewState) { 395 case SC.CoreView.UNRENDERED: 396 // Bring the child view down to the state of the parent. 397 this._doDestroyLayer(); 398 break; 399 default: 400 parentNode = parentView.get('containerLayer'); 401 nextView = siblings.objectAt(siblings.indexOf(this) + 1); 402 nextNode = (nextView) ? nextView.get('layer') : null; 403 404 this._doAttach(parentNode, nextNode); 405 } 406 break; 407 default: // ATTACHED_X 408 switch (parentViewState) { 409 case SC.CoreView.UNRENDERED: 410 // Bring the child view down to the state of the parent. 411 this._doDestroyLayer(); 412 break; 413 default: 414 parentNode = parentView.get('containerLayer'); 415 nextView = siblings.objectAt(siblings.indexOf(this) + 1); 416 nextNode = (nextView) ? nextView.get('layer') : null; 417 418 this._doAttach(parentNode, nextNode); 419 } 420 } 421 422 // Adopting a view that is building out. 423 } else if (state === SC.CoreView.ATTACHED_BUILDING_OUT) { 424 this._doAttach(); 425 426 // Can't do anything. 427 } else { 428 handled = false; 429 } 430 431 return handled; 432 }, 433 434 /** @private Attach this view action. */ 435 _doAttach: function (parentNode, nextNode) { 436 var state = this.get('viewState'), 437 transitionIn = this.get('transitionIn'), 438 parentView, 439 isHandled = false; 440 441 //@if (debug) 442 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 443 SC.Logger.log('%c%@ — _doAttach(%@, %@)'.fmt(this, parentNode, nextNode), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 444 } 445 //@endif 446 447 switch (state) { 448 449 // Normal case: view is not attached and is being attached. 450 case SC.CoreView.UNATTACHED: 451 var node = this.get('layer'); 452 453 this._executeQueuedUpdates(); 454 455 // Attach to parentNode. 456 // IE doesn't support insertBefore(blah, undefined) in version IE9. 457 parentNode.insertBefore(node, nextNode || null); 458 459 parentView = this.get('parentView'); 460 if (!parentView || (parentView && parentView.get('isAttached'))) { 461 // Attach the view. 462 this._executeDoAttach(); 463 464 // If there is a transition in, run it. 465 if (transitionIn) { 466 this._transitionIn(false); 467 } 468 } else { 469 // Attaching an unattached view to an unattached view, simply moves it to unattached by 470 // parent state. Don't do any notifications. 471 this._gotoAttachedPartialState(); 472 } 473 474 isHandled = true; 475 break; 476 477 // Special case: view switched from building out to building in. 478 case SC.CoreView.ATTACHED_BUILDING_OUT: 479 // If already building out, we need to cancel and possibly build in. Top-down so that any 480 // children that are hidden or building out on their own allow us to bail out early. 481 this._callOnChildViews('_parentDidCancelBuildOut'); 482 483 // Remove the shared building out count if it exists. 484 this._sc_buildOutCount = null; 485 486 // Note: We can be in ATTACHED_BUILDING_OUT state without a transition out while we wait for child views. 487 if (this.get('transitionOut')) { 488 // Cancel the building out transition (in place if we are going to switch to transitioning back in). 489 // this.cancelAnimation(transitionIn ? SC.LayoutState.CURRENT : undefined); 490 this.cancelAnimation(); 491 492 //@if (debug) 493 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 494 SC.Logger.log('%c — cancelling build out outright'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 495 } 496 //@endif 497 498 // Set the proper state. 499 this._gotoAttachedShownState(); 500 501 if (transitionIn) { 502 this._transitionIn(true); 503 } 504 505 // Waiting for child view transition outs. 506 } else { 507 508 // Set the proper state. 509 this._gotoAttachedShownState(); 510 } 511 512 isHandled = true; 513 break; 514 515 // Invalid states that have no effect. 516 case SC.CoreView.ATTACHED_HIDING: 517 case SC.CoreView.ATTACHED_HIDDEN: 518 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 519 case SC.CoreView.ATTACHED_BUILDING_IN: 520 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 521 case SC.CoreView.ATTACHED_SHOWING: 522 case SC.CoreView.UNRENDERED: 523 break; 524 525 // Improper states that have no effect, but should be discouraged. 526 case SC.CoreView.ATTACHED_SHOWN: 527 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 528 //@if(debug) 529 if (parentNode !== this.getPath('parentView.layer')) { 530 // This should be avoided, because moving the view layer without explicitly removing it first is a dangerous practice. 531 SC.warn("Developer Warning: You can not attach the view, %@, to a new node without properly detaching it first.".fmt(this)); 532 } 533 //@endif 534 break; 535 case SC.CoreView.ATTACHED_PARTIAL: 536 //@if(debug) 537 SC.warn("Developer Warning: You can not attach the child view, %@, directly.".fmt(this)); 538 //@endif 539 break; 540 } 541 542 return isHandled; 543 }, 544 545 /** @private Destroy the layer of this view action. */ 546 _doDestroyLayer: function () { 547 var state = this.get('viewState'), 548 isHandled = false; 549 550 //@if (debug) 551 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 552 SC.Logger.log('%c%@ — _doDestroyLayer()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 553 } 554 //@endif 555 556 switch (state) { 557 558 // Invalid states that have no effect. 559 case SC.CoreView.UNRENDERED: 560 case SC.CoreView.ATTACHED_HIDING: 561 case SC.CoreView.ATTACHED_HIDDEN: 562 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 563 case SC.CoreView.ATTACHED_BUILDING_IN: 564 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 565 case SC.CoreView.ATTACHED_SHOWING: 566 case SC.CoreView.ATTACHED_SHOWN: 567 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 568 case SC.CoreView.ATTACHED_BUILDING_OUT: 569 case SC.CoreView.ATTACHED_PARTIAL: 570 break; 571 572 // Normal case (SC.CoreView.UNATTACHED): view is rendered and its layer is being destroyed. 573 default: 574 // Notify that the layer will be destroyed. Bottom-up so that each child is in the proper 575 // state before its parent potentially alters its state. For example, a parent could modify 576 // children in `willDestroyLayer`. 577 this._callOnChildViews('_teardownLayer', false); 578 this._teardownLayer(); 579 580 isHandled = true; 581 } 582 583 return isHandled; 584 }, 585 586 /** @private Detach this view action. */ 587 _doDetach: function (immediately) { 588 var state = this.get('viewState'), 589 transitionOut = this.get('transitionOut'), 590 shouldHandle = true; 591 592 //@if (debug) 593 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 594 SC.Logger.log('%c%@ — _doDetach()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 595 } 596 //@endif 597 598 // Handle all 12 possible view states. 599 switch (state) { 600 601 // Scenario: The view is shown. 602 // Result: Allow the view and/or any of its child views to build out or else execute the detach. 603 case SC.CoreView.ATTACHED_SHOWN: 604 this._executeDoBuildOut(immediately); 605 606 break; 607 608 // Scenario: The view is attached but isn't visible (possibly because an ancestor is detached or hidden). 609 // Result: Detach the view immediately. 610 case SC.CoreView.ATTACHED_PARTIAL: 611 case SC.CoreView.ATTACHED_HIDDEN: 612 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 613 // Detach immediately. 614 this._executeDoDetach(); 615 616 break; 617 618 // Scenario: The view is in the middle of a hiding transition. 619 // Result: Cancel the animation and then run as normal. 620 case SC.CoreView.ATTACHED_HIDING: 621 // Cancel the animation (in the future we will be able to let it run out while building out). 622 this.cancelAnimation(); 623 624 // Set the proper state. 625 this._gotoAttachedHiddenState(); 626 627 // Detach immediately. 628 this._executeDoDetach(); 629 630 break; 631 632 // Scenario: The view is in the middle of an animation or showing transition. 633 // Result: Cancel the animation and then run as normal. 634 case SC.CoreView.ATTACHED_SHOWING: 635 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: // TODO: We need concurrent states! 636 // Cancel the animation (in the future we will be able to let it run out while building out). 637 this.cancelAnimation(); 638 639 // Set the proper state. 640 this._gotoAttachedShownState(); 641 642 this._executeDoBuildOut(immediately); 643 644 break; 645 646 // Scenario: The view is building in. 647 // Result: If it has a build out transition, swap to it. Otherwise, cancel. 648 case SC.CoreView.ATTACHED_BUILDING_IN: 649 // Cancel the build in transition. 650 // if (transitionOut) { 651 // //@if (debug) 652 // if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 653 // SC.Logger.log('%c — cancelling build in in place'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 654 // } 655 // //@endif 656 657 // this.cancelAnimation(SC.LayoutState.CURRENT); 658 // } else { 659 //@if (debug) 660 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 661 SC.Logger.log('%c — cancelling build in outright'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 662 } 663 //@endif 664 665 this.cancelAnimation(); 666 // } 667 668 // Set the proper state. 669 this._gotoAttachedShownState(); 670 671 // Build out in place. 672 this._executeDoBuildOut(immediately, true); 673 break; 674 675 // Scenario: View is already building out because of an ancestor. 676 // Result: Stop the transition so that it can continue in place on its own. 677 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 678 // Cancel the build out transition. 679 this.cancelAnimation(SC.LayoutState.CURRENT); // Fires didTransitionOut callback (necessary to clean up parent view build out wait) 680 681 // Set the proper state. (the view should only have been able to get to ATTACHED_BUILDING_OUT_BY_PARENT from ATTACHED_SHOWN). 682 this._gotoAttachedShownState(); 683 684 // TODO: Grab the build out count for all child views of this view. What a nightmare for an edge case! 685 686 // Build out in place. 687 this._executeDoBuildOut(immediately, true); 688 689 break; 690 691 // Scenario: View is already building out. 692 // Result: Stop if forced to. 693 case SC.CoreView.ATTACHED_BUILDING_OUT: 694 // If immediately is passed, cancel the build out prematurely. 695 if (immediately) { 696 // Note: *will* detach notice already sent. 697 this.cancelAnimation(); // Fires didTransitionOut callback (state changes to UNATTACHED/notifications sent). 698 699 // Detach immediately. 700 this._executeDoDetach(); 701 } 702 703 break; 704 705 // Invalid states. 706 default: 707 //@if(debug) 708 // Add some debugging only warnings for if the view statechart code is being improperly used. 709 // Telling the view to detach when it is already detached isn't correct: UNRENDERED, UNATTACHED 710 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _doDetach".fmt(this)); 711 //@endif 712 713 shouldHandle = false; 714 } 715 716 return shouldHandle; 717 }, 718 719 /** @private Hide this view action. */ 720 _doHide: function () { 721 var state = this.get('viewState'), 722 transitionHide = this.get('transitionHide'), 723 shouldHandle = true; 724 725 //@if (debug) 726 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 727 SC.Logger.log('%c%@ — _doHide()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 728 } 729 //@endif 730 731 // Handle all 12 possible view states. 732 switch (state) { 733 734 // Scenario: The view is shown. 735 // Result: Notify that the view will hide and then either hide or run hide transition. 736 case SC.CoreView.ATTACHED_SHOWN: 737 case SC.CoreView.ATTACHED_BUILDING_IN: 738 case SC.CoreView.ATTACHED_BUILDING_OUT: 739 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 740 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 741 // ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_BUILDING_OUT 742 743 // TODO: How do we check for conflicts in the hide transition against other concurrent transitions/animations? 744 if (transitionHide) { 745 // this.invokeNext(function () { 746 this._transitionHide(); 747 // }); 748 } else { 749 // Hide the view. 750 this._executeDoHide(); 751 } 752 753 break; 754 755 // Scenario: The view was showing at the time it was told to hide. 756 // Result: Cancel the animation. 757 case SC.CoreView.ATTACHED_SHOWING: 758 // Cancel the showing transition. 759 if (transitionHide) { 760 this.cancelAnimation(SC.LayoutState.CURRENT); 761 } else { 762 this.cancelAnimation(); 763 } 764 765 // Set the proper state. 766 this._gotoAttachedShownState(); 767 768 // Hide the view. 769 if (transitionHide) { 770 this._transitionHide(true); 771 } else { 772 this._executeDoHide(); 773 } 774 775 break; 776 777 // Scenario: The view is rendered but is not attached. 778 // Result: Queue an update to the visibility style. 779 case SC.CoreView.UNATTACHED: 780 case SC.CoreView.ATTACHED_PARTIAL: 781 // Queue the visibility update for the next time we display. 782 this._visibleStyleNeedsUpdate = true; 783 784 break; 785 786 // Scenario: The view is not even rendered. 787 // Result: Nothing is required. 788 case SC.CoreView.UNRENDERED: 789 shouldHandle = false; 790 break; 791 792 case SC.CoreView.ATTACHED_HIDDEN: // FAST PATH! 793 case SC.CoreView.ATTACHED_HIDING: // FAST PATH! 794 return false; 795 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: // FAST PATH! 796 // Note that visibility update is NOT conditional for this state. 797 this._doUpdateVisibleStyle(); 798 799 // Update states after *will* and before *did* notifications! 800 this._gotoAttachedHiddenState(); 801 802 return true; 803 804 // Invalid states. 805 default: 806 //@if(debug) 807 // Add some debugging only warnings for if the view statechart code is being improperly used. 808 // Telling the view to hide when it is already hidden isn't correct: 809 // 810 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _doHide".fmt(this)); 811 //@endif 812 813 shouldHandle = false; 814 } 815 816 return shouldHandle; 817 }, 818 819 /** @private Orphan this view action. */ 820 _doOrphan: function () { 821 var parentView = this.get('parentView'), 822 handled = true; 823 824 //@if (debug) 825 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 826 SC.Logger.log('%c%@ — _doOrphan()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 827 } 828 //@endif 829 830 if (parentView) { 831 var childViews = parentView.get('childViews'), 832 idx = childViews.indexOf(this); 833 834 // Completely remove the view from its parent. 835 this.set('parentView', null); 836 837 // Remove view from old parent's childViews array. 838 if (idx >= 0) { childViews.removeAt(idx); } 839 840 // Notify orphaned. 841 this._orphaned(parentView); 842 } else { 843 handled = false; 844 } 845 846 return handled; 847 }, 848 849 /** @private Render this view action. */ 850 _doRender: function () { 851 var state = this.get('viewState'), 852 shouldHandle = true; 853 854 //@if (debug) 855 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 856 SC.Logger.log('%c%@ — _doRender()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 857 } 858 //@endif 859 860 // Handle all 12 possible view states. 861 switch (state) { 862 863 // Scenario: The view is not rendered. 864 // Result: Render the layer and then notify. 865 case SC.CoreView.UNRENDERED: 866 // Render the view. 867 this._executeDoRender(); 868 869 // Bypass the unattached state for adopted views. 870 var parentView = this.get('parentView'); 871 if (parentView && parentView.get('_isRendered')) { 872 var parentNode = parentView.get('containerLayer'), 873 siblings = parentView.get('childViews'), 874 nextView = siblings.objectAt(siblings.indexOf(this) + 1), 875 nextNode = (nextView) ? nextView.get('layer') : null; 876 877 // Attach to parentNode 878 // IE doesn't support insertBefore(blah, undefined) in version IE9. 879 // parentNode.insertBefore(node, nextNode || null); 880 this._doAttach(parentNode, nextNode); 881 } 882 883 break; 884 885 // Invalid states. 886 default: 887 //@if(debug) 888 // Add some debugging only warnings for if the view statechart code is being improperly used. 889 // All other states should be impossible if parent was UNATTACHED: 890 // ATTACHED_SHOWING, ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_HIDING, ATTACHED_HIDDEN, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT, UNATTACHED, ATTACHED_PARTIAL 891 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _doRender".fmt(this)); 892 //@endif 893 shouldHandle = false; 894 } 895 896 return shouldHandle; 897 }, 898 899 /** @private Show this view action. */ 900 _doShow: function () { 901 var state = this.get('viewState'), 902 transitionShow = this.get('transitionShow'), 903 shouldHandle = true; 904 905 //@if (debug) 906 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 907 SC.Logger.log('%c%@ — _doShow()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 908 } 909 //@endif 910 911 // Handle all 12 possible view states. 912 switch (state) { 913 914 // Scenario: The view is hidden. 915 // Result: Depends on whether the parent view is shown or hidden by an ancestor. 916 case SC.CoreView.ATTACHED_HIDDEN: 917 var parentView = this.get('parentView'), 918 // Views without a parent are not limited by a parent's current state. 919 isParentShown = parentView ? parentView.get('viewState') & SC.CoreView.IS_SHOWN : true; 920 921 // Scenario: The view is hidden and its ancestors are all visible. 922 // Result: Notify that the view and relevant child views will be shown. 923 if (isParentShown) { 924 var notifyStack = []; // Only those views that changed state get added to the stack. 925 926 // Run any queued updates. 927 this._executeQueuedUpdates(); 928 929 // The children are updated top-down so that hidden or unattached children allow us to bail out early. 930 this._callOnChildViews('_parentWillShowInDocument', true, notifyStack); 931 932 // Notify for each child (that will change state) in reverse so that each child is in the proper 933 // state before its parent potentially alters its state. For example, a parent could modify 934 // children in `willShowInDocument`. 935 for (var i = notifyStack.length - 1; i >= 0; i--) { 936 var childView = notifyStack[i]; 937 938 childView._notifyWillShowInDocument(); 939 } 940 this._notifyWillShowInDocument(); 941 942 // Show the view. 943 this._executeDoShow(); 944 if (transitionShow) { 945 this._transitionShow(false); 946 } 947 948 // Scenario: The view is hidden, but one of its ancestors is also hidden. 949 // Result: Track that the visible style needs update and go to hidden by parent state. 950 } else { 951 // Queue the visibility update for the next time we display. 952 this._visibleStyleNeedsUpdate = true; 953 954 // Set the proper state. 955 this._gotoAttachedHiddenByParentState(); 956 } 957 958 break; 959 960 // Scenario: The view was hiding at the time it was told to show. 961 // Result: Revert or reverse the hiding transition. 962 case SC.CoreView.ATTACHED_HIDING: 963 // Cancel the hiding transition (in place if we are going to switch to transitioning back in). 964 this.cancelAnimation(transitionShow ? SC.LayoutState.CURRENT : SC.LayoutState.START); 965 966 // Set the proper state. 967 this._gotoAttachedShownState(); 968 969 if (transitionShow) { 970 this._transitionShow(true); 971 } 972 973 break; 974 975 // Scenario: The view is rendered but is not attached. 976 // Result: Queue an update to the visibility style. 977 case SC.CoreView.UNATTACHED: 978 case SC.CoreView.ATTACHED_PARTIAL: 979 // Queue the visibility update for the next time we display. 980 this._visibleStyleNeedsUpdate = true; 981 982 break; 983 984 // Scenario: The view is not even rendered. 985 // Result: Nothing is required. 986 case SC.CoreView.UNRENDERED: 987 shouldHandle = false; 988 break; 989 990 // Invalid states. 991 default: 992 //@if(debug) 993 // Add some debugging only warnings for if the view statechart code is being improperly used. 994 // Telling the view to show when it is already visible isn't correct: 995 // ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_SHOWING, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_BUILDING_OUT 996 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _doShow".fmt(this)); 997 //@endif 998 999 shouldHandle = false; 1000 } 1001 1002 return shouldHandle; 1003 }, 1004 1005 /** @private Update this view's contents action. */ 1006 _doUpdateContent: function (force) { 1007 var isVisibleInWindow = this.get('isVisibleInWindow'), 1008 handled = true; 1009 1010 //@if (debug) 1011 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 1012 SC.Logger.log('%c%@ — _doUpdateContent(%@)'.fmt(this, force), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 1013 } 1014 //@endif 1015 1016 if (this.get('_isRendered')) { 1017 if (isVisibleInWindow || force) { 1018 // Only in the visible states do we allow updates without being forced. 1019 this._executeDoUpdateContent(); 1020 } else { 1021 // Otherwise mark the view as needing an update when we enter a shown state again. 1022 this._contentNeedsUpdate = true; 1023 } 1024 } else { 1025 handled = false; 1026 } 1027 1028 return handled; 1029 }, 1030 1031 // ------------------------------------------------------------------------ 1032 // Events 1033 // 1034 1035 /** 1036 This method is called by transition plugins when the incoming or showing 1037 transition completes. You should only use this method if implementing a 1038 custom transition plugin. 1039 1040 @param {SC.ViewTransitionProtocol} transition The transition plugin used. 1041 @param {Object} options The original options used. One of transitionShowOptions or transitionInOptions. 1042 */ 1043 didTransitionIn: function () { 1044 var state = this.get('viewState'); 1045 1046 //@if (debug) 1047 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 1048 SC.Logger.log('%c%@ — didTransitionIn()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 1049 } 1050 //@endif 1051 1052 if (state === SC.CoreView.ATTACHED_SHOWING || 1053 state === SC.CoreView.ATTACHED_BUILDING_IN) { 1054 this._teardownTransition(); 1055 1056 // Set the proper state. 1057 this._gotoAttachedShownState(); 1058 } 1059 }, 1060 1061 /** 1062 This method is called by transition plugins when the outgoing or hiding 1063 transition completes. You should only use this method if implementing a 1064 custom transition plugin. 1065 1066 @param {SC.ViewTransitionProtocol} transition The transition plugin used. 1067 @param {Object} options The original options used. One of transitionHideOptions or transitionOutOptions. 1068 */ 1069 didTransitionOut: function () { 1070 var state = this.get('viewState'); 1071 1072 //@if (debug) 1073 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 1074 SC.Logger.log('%c%@ — didTransitionOut()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 1075 } 1076 //@endif 1077 1078 if (state === SC.CoreView.ATTACHED_BUILDING_OUT) { 1079 this._teardownTransition(); 1080 1081 this._executeDoDetach(); 1082 } else if (state === SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT) { 1083 var owningView = this._owningView; 1084 // We can't clean up the transition until the parent is done. For 1085 // example, a fast child build out inside of a slow parent build out. 1086 owningView._sc_buildOutCount--; 1087 1088 if (owningView._sc_buildOutCount === 0) { 1089 owningView._executeDoDetach(); 1090 1091 // Clean up. 1092 this._owningView = null; 1093 } 1094 } else if (state === SC.CoreView.ATTACHED_HIDING) { 1095 this._teardownTransition(); 1096 1097 // Hide immediately. 1098 this._executeDoHide(); 1099 } 1100 }, 1101 1102 /** @private The 'adopted' event. */ 1103 _adopted: function (beforeView) { 1104 // Notify all of our descendents that our parent has changed. They will update their `pane` value 1105 // for one. Bottom-up in case a parent view modifies children when its pane changes for any 1106 // reason. 1107 this._callOnChildViews('_ancestorDidChangeParent', false); 1108 1109 // Notify that the child view has been added to the parent view. 1110 var parentView = this.get('parentView'); 1111 if (this.didAddToParent) { this.didAddToParent(parentView, beforeView); } 1112 if (parentView.didAddChild) { parentView.didAddChild(this, beforeView); } 1113 }, 1114 1115 /** @private The 'orphaned' event. */ 1116 _orphaned: function (oldParentView) { 1117 // It's not necessary to send notice to destroyed views. 1118 if (!this.isDestroyed) { 1119 // Notify all of our descendents that our parent has changed. They will update their `pane` value 1120 // for one. Bottom-up in case a parent view modifies children when its pane changes for any 1121 // reason. 1122 this._callOnChildViews('_ancestorDidChangeParent', false); 1123 1124 if (oldParentView.didRemoveChild) { oldParentView.didRemoveChild(this); } 1125 if (this.didRemoveFromParent) { this.didRemoveFromParent(oldParentView); } 1126 } 1127 }, 1128 1129 // ------------------------------------------------------------------------ 1130 // States 1131 // 1132 1133 /** @private */ 1134 _gotoAttachedBuildingInState: function () { 1135 this.set('viewState', SC.CoreView.ATTACHED_BUILDING_IN); 1136 }, 1137 1138 /** @private */ 1139 _gotoAttachedBuildingOutState: function () { 1140 this.set('viewState', SC.CoreView.ATTACHED_BUILDING_OUT); 1141 }, 1142 1143 /** @private */ 1144 _gotoAttachedBuildingOutByParentState: function () { 1145 this.set('viewState', SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT); 1146 }, 1147 1148 /** @private */ 1149 _gotoAttachedHiddenState: function () { 1150 this.set('viewState', SC.CoreView.ATTACHED_HIDDEN); 1151 }, 1152 1153 /** @private */ 1154 _gotoAttachedHiddenByParentState: function () { 1155 this.set('viewState', SC.CoreView.ATTACHED_HIDDEN_BY_PARENT); 1156 }, 1157 1158 /** @private */ 1159 _gotoAttachedHidingState: function () { 1160 this.set('viewState', SC.CoreView.ATTACHED_HIDING); 1161 }, 1162 1163 /** @private */ 1164 _gotoAttachedShowingState: function () { 1165 this.set('viewState', SC.CoreView.ATTACHED_SHOWING); 1166 }, 1167 1168 /** @private */ 1169 _gotoAttachedShownState: function () { 1170 this.set('viewState', SC.CoreView.ATTACHED_SHOWN); 1171 }, 1172 1173 /** @private */ 1174 _gotoUnattachedState: function () { 1175 this.set('viewState', SC.CoreView.UNATTACHED); 1176 }, 1177 1178 /** @private */ 1179 _gotoAttachedPartialState: function () { 1180 this.set('viewState', SC.CoreView.ATTACHED_PARTIAL); 1181 }, 1182 1183 /** @private */ 1184 _gotoUnrenderedState: function () { 1185 this.set('viewState', SC.CoreView.UNRENDERED); 1186 }, 1187 1188 // ------------------------------------------------------------------------ 1189 // Methods 1190 // 1191 1192 /** @private Adds observers once a view has a layer. */ 1193 _sc_addRenderedStateObservers: function () { 1194 var displayProperties, 1195 len, idx; 1196 1197 // Register display property observers. 1198 displayProperties = this.get('displayProperties'); 1199 for (idx = 0, len = displayProperties.length; idx < len; idx++) { 1200 this.addObserver(displayProperties[idx], this, this.displayDidChange); 1201 } 1202 1203 // Begin observing isVisible & isFirstResponder. 1204 this.addObserver('isVisible', this, this._isVisibleDidChange); 1205 this.addObserver('isFirstResponder', this, this._isFirstResponderDidChange); 1206 }, 1207 1208 /** @private Called when an ancestor's parent changed. */ 1209 _ancestorDidChangeParent: function () { 1210 // When an ancestor changes, the pane may have changed. 1211 this.notifyPropertyChange('pane'); 1212 }, 1213 1214 /** @private Clear building in transition. */ 1215 _cancelTransition: function () { 1216 // Cancel conflicting transitions. This causes the animation callback to fire. 1217 this.cancelAnimation(); 1218 // this._teardownTransition(); 1219 }, 1220 1221 /** @private */ 1222 _doUpdateVisibleStyle: function () { 1223 var isVisible = this.get('isVisible'); 1224 1225 this.$().toggleClass('sc-hidden', !isVisible); 1226 this.$().attr('aria-hidden', isVisible ? null : true); 1227 1228 // Reset that an update is required. 1229 this._visibleStyleNeedsUpdate = false; 1230 }, 1231 1232 /** @private Destroys the layer and updates the state. */ 1233 _teardownLayer: function () { 1234 this._notifyWillDestroyLayer(); 1235 1236 var displayProperties, 1237 idx, len; 1238 1239 // Unregister display property observers. 1240 displayProperties = this.get('displayProperties'); 1241 for (idx = 0, len = displayProperties.length; idx < len; idx++) { 1242 this.removeObserver(displayProperties[idx], this, this.displayDidChange); 1243 } 1244 1245 // Stop observing isVisible & isFirstResponder. 1246 this.removeObserver('isVisible', this, this._isVisibleDidChange); 1247 this.removeObserver('isFirstResponder', this, this._isFirstResponderDidChange); 1248 1249 // Remove the layer reference. 1250 this.set('layer', null); 1251 1252 // Update states after *will* and before *did* notifications! 1253 this._gotoUnrenderedState(); 1254 }, 1255 1256 /** @private Attaches the view. */ 1257 _executeDoAttach: function () { 1258 var notifyStack = []; // Only those views that changed state get added to the stack. 1259 1260 // Run any queued updates. 1261 this._executeQueuedUpdates(); 1262 1263 // Update the state and children state. The children are updated top-down so that hidden or 1264 // unattached children allow us to bail out early. 1265 this._gotoSomeAttachedState(); 1266 this._callOnChildViews('_parentDidAttach', true, notifyStack); 1267 1268 // Notify for each child (that changed state) in reverse so that each child is in the proper 1269 // state before its parent potentially alters its state. For example, a parent could modify 1270 // children in `didAppendToDocument`. 1271 for (var i = notifyStack.length - 1; i >= 0; i--) { 1272 var childView = notifyStack[i]; 1273 1274 childView._notifyDidAttach(); 1275 } 1276 this._notifyDidAttach(); 1277 }, 1278 1279 /** @private Builds out the view. */ 1280 _executeDoBuildOut: function (immediately, inPlace) { 1281 if (immediately) { 1282 // Detach immediately. 1283 this._executeDoDetach(); 1284 } else { 1285 // In order to allow the removal of a parent to be delayed by its children's transitions, we 1286 // track which views are building out and finish only when they're all done. 1287 this._sc_buildOutCount = 0; 1288 1289 // Tell all the child views so that any with a transitionOut may run it. Top-down so that 1290 // any hidden or already building out child views allow us to bail out early. 1291 this._callOnChildViews('_parentWillBuildOutFromDocument', true, this); 1292 1293 var transitionOut = this.get('transitionOut'); 1294 if (transitionOut) { 1295 inPlace = inPlace || false; 1296 this._transitionOut(inPlace, this); 1297 1298 } else if (this._sc_buildOutCount > 0) { 1299 // Some children are building out, we will have to wait for them. 1300 this._gotoAttachedBuildingOutState(); 1301 } else { 1302 this._sc_buildOutCount = null; 1303 1304 // Detach immediately. 1305 this._executeDoDetach(); 1306 } 1307 } 1308 }, 1309 1310 /** @private Detaches the view and updates the state. */ 1311 _executeDoDetach: function () { 1312 var notifyStack = []; // Only those views that changed state get added to the stack. 1313 1314 // The children are updated top-down so that hidden or unattached children allow us to bail out early. 1315 this._callOnChildViews('_parentWillDetach', true, notifyStack); 1316 1317 // Notify for each child (that will change state) in reverse so that each child is in the proper 1318 // state before its parent potentially alters its state. For example, a parent could modify 1319 // children in `willRemoveFromDocument`. 1320 for (var i = notifyStack.length - 1; i >= 0; i--) { 1321 var childView = notifyStack[i]; 1322 1323 childView._notifyWillDetach(); 1324 } 1325 this._notifyWillDetach(); 1326 1327 // Cancel any remaining animations (e.g. a concurrent hide). 1328 var viewState = this.get('viewState'); 1329 switch (viewState) { 1330 case SC.CoreView.ATTACHED_HIDING: 1331 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1332 this.cancelAnimation(); 1333 break; 1334 } 1335 1336 // Detach the layer. 1337 var node = this.get('layer'); 1338 node.parentNode.removeChild(node); 1339 1340 // Update the state and children state. The children are updated top-down so that unattached 1341 // children allow us to bail out early. 1342 this._gotoUnattachedState(); 1343 this._callOnChildViews('_parentDidDetach', true); 1344 }, 1345 1346 /** @private Hides the view. */ 1347 _executeDoHide: function () { 1348 var notifyStack = []; // Only those views that changed state get added to the stack. 1349 1350 // The children are updated top-down so that hidden or unattached children allow us to bail out early. 1351 this._callOnChildViews('_parentWillHideInDocument', true, notifyStack); 1352 1353 // Notify for each child (that will change state) in reverse so that each child is in the proper 1354 // state before its parent potentially alters its state. For example, a parent could modify 1355 // children in `willHideInDocument`. 1356 for (var i = notifyStack.length - 1; i >= 0; i--) { 1357 var childView = notifyStack[i]; 1358 1359 childView._notifyWillHideInDocument(); 1360 } 1361 this._notifyWillHideInDocument(); 1362 1363 // Cancel any remaining animations (e.g. a concurrent build in or build out). 1364 var viewState = this.get('viewState'); 1365 switch (viewState) { 1366 case SC.CoreView.ATTACHED_BUILDING_IN: 1367 case SC.CoreView.ATTACHED_BUILDING_OUT: 1368 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 1369 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1370 this.cancelAnimation(); 1371 break; 1372 } 1373 1374 // Update the visible style. 1375 this._doUpdateVisibleStyle(); 1376 1377 // Update the state and children state. The children are updated top-down so that hidden or 1378 // unattached children allow us to bail out early. 1379 this._gotoAttachedHiddenState(); 1380 this._callOnChildViews('_parentDidHideInDocument', true); // , notifyStack 1381 1382 // Notify for each child (that changed state) in reverse so that each child is in the proper 1383 // state before its parent potentially alters its state. For example, a parent could modify 1384 // children in `didHideInDocument`. 1385 // for (var i = notifyStack.length - 1; i >= 0; i--) { 1386 // var childView = notifyStack[i]; 1387 1388 // childView._notifyDidHideInDocument(); 1389 // } 1390 this._notifyDidHideInDocument(); 1391 }, 1392 1393 /** @private Render the view's layer. */ 1394 _executeDoRender: function () { 1395 var notifyStack = []; // Only those views that changed state get added to the stack. 1396 1397 // Render the layer. 1398 var context = this.renderContext(this.get('tagName')); 1399 1400 this.renderToContext(context); 1401 this.set('layer', context.element()); 1402 1403 // Update the state and children state. The children are updated top-down so that invalid state 1404 // children allow us to bail out early. 1405 // if (this.get('parentView')) { 1406 // this._gotoAttachedPartialState(); 1407 this._gotoUnattachedState(); 1408 // } 1409 1410 this._callOnChildViews('_parentDidRender', true, notifyStack); 1411 1412 this._sc_addRenderedStateObservers(); 1413 1414 // Notify for each child (that changed state) in reverse so that each child is in the proper 1415 // state before its parent potentially alters its state. For example, a parent could modify 1416 // children in `didCreateLayer`. 1417 for (var i = notifyStack.length - 1; i >= 0; i--) { 1418 var childView = notifyStack[i]; 1419 1420 childView._notifyDidRender(); 1421 } 1422 this._notifyDidRender(); 1423 }, 1424 1425 /** @private Shows the view. */ 1426 _executeDoShow: function () { 1427 // var notifyStack = []; // Only those views that changed state get added to the stack. 1428 1429 // Update the visible style. 1430 this._doUpdateVisibleStyle(); 1431 1432 // Update the state and children state. The children are updated top-down so that hidden or 1433 // unattached children allow us to bail out early. This view's state is going to be transitioning, 1434 // but all child views are now considered shown. 1435 this._gotoAttachedShownState(); 1436 this._callOnChildViews('_parentDidShowInDocument', true); // , notifyStack 1437 1438 // Notify for each child (that changed state) in reverse so that each child is in the proper 1439 // state before its parent potentially alters its state. For example, a parent could modify 1440 // children in `didShowInDocument`. 1441 // for (var i = notifyStack.length - 1; i >= 0; i--) { 1442 // var childView = notifyStack[i]; 1443 1444 // childView._notifyDidShowInDocument(); 1445 // } 1446 this._notifyDidShowInDocument(); 1447 }, 1448 1449 /** @private Updates the layer. */ 1450 _executeDoUpdateContent: function () { 1451 var mixins = this.renderMixin, 1452 context = this.renderContext(this.get('layer')); 1453 1454 // If there is no update method, fallback to calling render with extra 1455 // firstTime argument set to false. 1456 if (!this.update) { 1457 this.render(context, false); 1458 } else { 1459 this.update(context.$()); 1460 } 1461 1462 // Call renderMixin methods. 1463 if (mixins) { 1464 var len = mixins.length; 1465 for (var idx = 0; idx < len; ++idx) { 1466 mixins[idx].call(this, context, false); 1467 } 1468 } 1469 1470 // Call applyAttributesToContext so that subclasses that override it can 1471 // insert further attributes. 1472 this.applyAttributesToContext(context); 1473 1474 context.update(); 1475 1476 // Legacy. 1477 this.set('layerNeedsUpdate', false); 1478 1479 // Reset that an update is required. 1480 this._contentNeedsUpdate = false; 1481 1482 // Notify. 1483 this.notifyPropertyChange('layer'); 1484 if (this.didUpdateLayer) { this.didUpdateLayer(); } 1485 1486 if (this.designer && this.designer.viewDidUpdateLayer) { 1487 this.designer.viewDidUpdateLayer(); //let the designer know 1488 } 1489 }, 1490 1491 /** @private */ 1492 _executeQueuedUpdates: function () { 1493 1494 // Update visibility style if necessary. 1495 if (this._visibleStyleNeedsUpdate) { 1496 this._doUpdateVisibleStyle(); 1497 } 1498 1499 // Update the content of the layer if necessary. 1500 if (this._contentNeedsUpdate) { 1501 this._executeDoUpdateContent(); 1502 } 1503 }, 1504 1505 /** @private 1506 Marks the view as needing a visibility update if the isVisible property 1507 changes. 1508 1509 This observer is connected when the view is attached and is disconnected 1510 when the view is detached. 1511 */ 1512 _isVisibleDidChange: function () { 1513 if (this.get('isVisible')) { 1514 // show the view if it's not being shown already, 1515 // unless the view is transitioning to being hidden 1516 var viewState = this.get('viewState'); 1517 if (!(viewState & SC.CoreView.IS_SHOWN) || (viewState == SC.CoreView.ATTACHED_HIDING)) { 1518 this._doShow(); 1519 } 1520 } else { 1521 this._doHide(); 1522 } 1523 }, 1524 1525 /** @private 1526 Adds the 'focus' class to the view. 1527 1528 This observer is connected when the view is attached and is disconnected 1529 when the view is detached. 1530 */ 1531 _isFirstResponderDidChange: function () { 1532 var isFirstResponder = this.get('isFirstResponder'); 1533 1534 this.$().toggleClass('focus', isFirstResponder); 1535 }, 1536 1537 /** @private Attempts to call `didAppendToDocument` on the view. */ 1538 _notifyDidAttach: function () { 1539 // If we don't have the layout module then we don't know the frame until appended to the document. 1540 this.notifyPropertyChange('frame'); 1541 1542 // Notify. 1543 if (this.didAppendToDocument) { this.didAppendToDocument(); } 1544 }, 1545 1546 /** @private Attempts to call `didHideInDocument` on the view. */ 1547 _notifyDidHideInDocument: function () { 1548 if (this.didHideInDocument) { this.didHideInDocument(); } 1549 }, 1550 1551 /** @private Attempts to call `didCreateLayer` on the view. */ 1552 _notifyDidRender: function () { 1553 var mixins = this.didCreateLayerMixin, 1554 idx, len; 1555 1556 // Send notice that the layer was created. 1557 if (this.didCreateLayer) { this.didCreateLayer(); } 1558 if (mixins) { 1559 len = mixins.length; 1560 for (idx = 0; idx < len; ++idx) { 1561 mixins[idx].call(this); 1562 } 1563 } 1564 }, 1565 1566 /** @private Attempts to call `didShowInDocument` on the view. */ 1567 _notifyDidShowInDocument: function () { 1568 if (this.didShowInDocument) { this.didShowInDocument(); } 1569 }, 1570 1571 /** @private Attempts to call `willDestroyLayer` on the view. */ 1572 _notifyWillDestroyLayer: function () { 1573 var idx, len, 1574 mixins; 1575 1576 mixins = this.willDestroyLayerMixin; 1577 if (mixins) { 1578 len = mixins.length; 1579 for (idx = 0; idx < len; ++idx) { 1580 mixins[idx].call(this); 1581 } 1582 } 1583 1584 if (this.willDestroyLayer) { this.willDestroyLayer(); } 1585 }, 1586 1587 /** @private Attempts to call `willRemoveFromDocument` on the view. */ 1588 _notifyWillDetach: function () { 1589 if (this.willRemoveFromDocument) { this.willRemoveFromDocument(); } 1590 }, 1591 1592 /** @private Attempts to call `willHideInDocument` on the view. */ 1593 _notifyWillHideInDocument: function () { 1594 if (this.willHideInDocument) { this.willHideInDocument(); } 1595 }, 1596 1597 /** @private Attempts to call `willShowInDocument` on the view. */ 1598 _notifyWillShowInDocument: function () { 1599 if (this.willShowInDocument) { this.willShowInDocument(); } 1600 }, 1601 1602 /** @private Routes according to parent did append. */ 1603 _parentDidAttach: function (notifyStack) { 1604 var state = this.get('viewState'), 1605 shouldContinue = true; 1606 1607 // Handle all 12 possible view states. 1608 switch (state) { 1609 1610 // Scenario: The child view was attached to the parent, which was unattached. 1611 // Result: Update the child and then move it to the proper attached state. 1612 case SC.CoreView.ATTACHED_PARTIAL: 1613 // Run any queued updates. 1614 this._executeQueuedUpdates(); 1615 1616 // Go to the proper state. 1617 this._gotoSomeAttachedState(); 1618 1619 // If there is a transition in, run it. TODO: Check state here? 1620 var transitionIn = this.get('transitionIn'); 1621 if (transitionIn) { 1622 this._transitionIn(false); 1623 } 1624 1625 break; 1626 1627 // Scenario: The child is unrendered or unattached. 1628 // Result: The child would need to be forced into this state by its parent (otherwise it should 1629 // be in an ATTACHED_PARTIAL state), so just leave it alone and don't notify. 1630 case SC.CoreView.UNRENDERED: // Render + attach? 1631 case SC.CoreView.UNATTACHED: // Attach? 1632 // There's no need to continue to further child views. 1633 shouldContinue = false; 1634 break; 1635 1636 // Invalid states. 1637 default: 1638 //@if(debug) 1639 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1640 // All other states should be impossible if parent was UNATTACHED: 1641 // ATTACHED_BUILDING_IN, ATTACHED_SHOWING, ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_HIDING, ATTACHED_HIDDEN, ATTACHED_HIDDEN_BY_PARENT 1642 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _parentDidAttach".fmt(this)); 1643 //@endif 1644 1645 // There's no need to continue to further child views. 1646 shouldContinue = false; 1647 } 1648 1649 if (shouldContinue) { 1650 // Allow children that have changed state to notify that they have been attached. 1651 notifyStack.push(this); 1652 } 1653 1654 return shouldContinue; 1655 }, 1656 1657 /** @private Updates according to parent did cancel build out. */ 1658 _parentDidCancelBuildOut: function () { 1659 var state = this.get('viewState'); 1660 1661 // If the view was building out because its parent was building out, attempt to reverse or 1662 // revert the transition. 1663 if (state === SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT) { 1664 var transitionIn = this.get('transitionIn'); 1665 1666 // Cancel the building out transition (in place if we are going to switch to transitioning back in). 1667 this.cancelAnimation(transitionIn ? SC.LayoutState.CURRENT : undefined); 1668 1669 // Set the proper state. 1670 this._gotoAttachedShownState(); 1671 1672 if (transitionIn) { 1673 this._transitionIn(true); 1674 } 1675 1676 // If the view was building out on its own or is hidden we can ignore it. 1677 } else if (state === SC.CoreView.ATTACHED_BUILDING_OUT || state & 1678 SC.CoreView.IS_HIDDEN) { 1679 // There's no need to continue to further child views. 1680 return false; 1681 } 1682 }, 1683 1684 /** @private Update child view states when the parent hides. Top-down! */ 1685 _parentDidHideInDocument: function () { // notifyStack 1686 var state = this.get('viewState'), 1687 shouldContinue = false; 1688 1689 // Handle all 12 possible view states. 1690 switch (state) { 1691 1692 // Scenario: The child view was shown. 1693 // Result: Go to hidden by parent state. 1694 case SC.CoreView.ATTACHED_SHOWN: 1695 // Go to the proper state. 1696 this._gotoAttachedHiddenByParentState(); 1697 1698 shouldContinue = true; 1699 break; 1700 1701 // Scenario: The child view was hidden or forced to unrendered or unattached state. 1702 // Result: Do nothing. 1703 case SC.CoreView.UNRENDERED: 1704 case SC.CoreView.UNATTACHED: 1705 case SC.CoreView.ATTACHED_HIDDEN: 1706 break; 1707 1708 // Invalid states. 1709 default: 1710 //@if(debug) 1711 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1712 // All animating states should have been canceled when parent will hide is called. 1713 // ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_SHOWING, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_SHOWN_ANIMATING 1714 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidHideInDocument".fmt(this)); 1715 //@endif 1716 } 1717 1718 // if (shouldContinue) { 1719 // // Allow children that have changed state to notify that they have been hidden. 1720 // notifyStack.push(this); 1721 // } 1722 1723 return shouldContinue; 1724 }, 1725 1726 /** @private Routes according to parent did detach. */ 1727 _parentDidDetach: function () { 1728 var state = this.get('viewState'); 1729 1730 if (state & SC.CoreView.IS_ATTACHED) { 1731 // Update states after *will* and before *did* notifications! 1732 this._gotoAttachedPartialState(); 1733 } else { 1734 // There's no need to continue to further child views. 1735 return false; 1736 } 1737 }, 1738 1739 /** @private Configure child views when parent did render. */ 1740 _parentDidRender: function (notifyStack) { 1741 var state = this.get('viewState'), 1742 shouldContinue = true; 1743 1744 // Handle all 12 possible view states. 1745 switch (state) { 1746 1747 // Scenario: The child view was unrendered and now is rendered. 1748 // Result: Add rendered state observers and move it to the proper rendered state. 1749 case SC.CoreView.UNRENDERED: 1750 this._sc_addRenderedStateObservers(); 1751 1752 // Go to the proper state. 1753 this._gotoAttachedPartialState(); 1754 break; 1755 1756 // Invalid states. 1757 default: 1758 //@if(debug) 1759 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1760 // All other states should be impossible if parent was UNRENDERED: 1761 // ATTACHED_BUILDING_IN, ATTACHED_SHOWING, ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_HIDING, ATTACHED_HIDDEN, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_PARTIAL, UNATTACHED 1762 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidRender".fmt(this)); 1763 //@endif 1764 1765 // There's no need to continue to further child views. 1766 shouldContinue = false; 1767 } 1768 1769 if (shouldContinue) { 1770 // Allow children that have changed state to notify that they have been rendered. 1771 notifyStack.push(this); 1772 } 1773 1774 return shouldContinue; 1775 }, 1776 1777 /** @private Update child view states when the parent shows. Top-down! */ 1778 _parentDidShowInDocument: function () { // notifyStack 1779 var state = this.get('viewState'), 1780 shouldContinue = true; 1781 1782 // Handle all 12 possible view states. 1783 switch (state) { 1784 1785 // Scenario: The child view is only hidden because of the parent. 1786 // Result: Go to shown state. This will notify. 1787 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 1788 this._gotoAttachedShownState(); 1789 1790 break; 1791 1792 // Scenario: The child view is hidden on its own or has been forced to an unrendered or unattached state. 1793 // Result: Do nothing and don't notify. 1794 case SC.CoreView.UNRENDERED: 1795 case SC.CoreView.UNATTACHED: 1796 case SC.CoreView.ATTACHED_HIDDEN: 1797 // There's no need to continue to further child views. 1798 shouldContinue = false; 1799 break; 1800 1801 // Invalid states. 1802 default: 1803 //@if(debug) 1804 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1805 // These states should be impossible if the parent was HIDDEN. 1806 // ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_SHOWING, ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT 1807 // This state should be impossible if its parent was UNATTACHED (it should have been trimmed above): 1808 // ATTACHED_PARTIAL 1809 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidShowInDocument".fmt(this)); 1810 //@endif 1811 // There's no need to continue to further child views. 1812 shouldContinue = false; 1813 } 1814 1815 // if (shouldContinue) { 1816 // // Allow children that have changed state to notify that they have been made visible. 1817 // notifyStack.push(this); 1818 // } 1819 1820 return shouldContinue; 1821 }, 1822 1823 /** @private Starts building out view if appropriate. */ 1824 _parentWillBuildOutFromDocument: function (owningView) { 1825 var state = this.get('viewState'), 1826 transitionOut = this.get('transitionOut'), 1827 shouldContinue = true; 1828 1829 switch (state) { 1830 case SC.CoreView.UNRENDERED: 1831 case SC.CoreView.UNATTACHED: 1832 case SC.CoreView.ATTACHED_BUILDING_OUT: 1833 case SC.CoreView.ATTACHED_HIDDEN: 1834 // There's no need to continue to further child views. 1835 shouldContinue = false; 1836 break; 1837 1838 // Scenario: The child view is building in at the same time that its ancestor wants to detach. 1839 // Result: If the child wants to build out, switch to building out by parent, otherwise let the build in run for as long as it can. 1840 case SC.CoreView.ATTACHED_BUILDING_IN: 1841 1842 // Cancel the build in transition. 1843 if (transitionOut) { 1844 this.cancelAnimation(SC.LayoutState.CURRENT); 1845 } else { 1846 this.cancelAnimation(); 1847 } 1848 1849 // Set the proper state. 1850 this._gotoAttachedShownState(); 1851 1852 // Build out the view by parent. 1853 if (transitionOut) { 1854 this._transitionOut(true, owningView); 1855 } 1856 1857 break; 1858 1859 // Scenario: The view is shown and possibly transitioning. 1860 // Result: Allow any transitions to continue concurrent with build out transition (may be conflicts). 1861 case SC.CoreView.ATTACHED_HIDING: 1862 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1863 case SC.CoreView.ATTACHED_SHOWING: 1864 case SC.CoreView.ATTACHED_SHOWN: 1865 1866 // Build out the view by parent. 1867 if (transitionOut) { 1868 this._transitionOut(false, owningView); 1869 } 1870 break; 1871 1872 // Invalid states. 1873 default: 1874 //@if(debug) 1875 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1876 // These states should not be reachable here: ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_OUT_BY_PARENT 1877 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillBuildOutFromDocument".fmt(this)); 1878 //@endif 1879 // There's no need to continue to further child views. 1880 shouldContinue = false; 1881 } 1882 1883 return shouldContinue; 1884 }, 1885 1886 /** @private Prepares according to parent will hide. This is called before the parent view hides 1887 completely, which may be after a hide transition completes. */ 1888 _parentWillHideInDocument: function () { // notifyStack 1889 var state = this.get('viewState'), 1890 shouldContinue = true; 1891 1892 // Handle all 12 possible view states. 1893 switch (state) { 1894 1895 // Scenario: The child view is visible. 1896 // Result: Do nothing and continue. 1897 case SC.CoreView.ATTACHED_SHOWN: 1898 break; 1899 1900 // Scenario: The child view is animating. 1901 // Result: Complete its animation immediately and continue. 1902 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1903 case SC.CoreView.ATTACHED_SHOWING: 1904 case SC.CoreView.ATTACHED_BUILDING_IN: 1905 case SC.CoreView.ATTACHED_BUILDING_OUT: 1906 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 1907 case SC.CoreView.ATTACHED_HIDING: 1908 this.cancelAnimation(); 1909 break; 1910 1911 // Scenario: The child view is hidden or has been forced to an unrendered or unattached state. 1912 // Result: Do nothing and don't notify. 1913 case SC.CoreView.UNRENDERED: 1914 case SC.CoreView.UNATTACHED: 1915 case SC.CoreView.ATTACHED_HIDDEN: 1916 // There's no need to continue to further child views. 1917 break; 1918 1919 // Invalid states. 1920 default: 1921 //@if(debug) 1922 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1923 // This state should be impossible if its parent was UNATTACHED or HIDDEN/HIDING (it should have been trimmed above): 1924 // ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT 1925 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillHideInDocument".fmt(this)); 1926 //@endif 1927 // There's no need to continue to further child views. 1928 shouldContinue = false; 1929 } 1930 1931 // if (shouldContinue) { 1932 // // Allow children that have changed state to notify that they will be shown. 1933 // notifyStack.push(this); 1934 // } 1935 1936 return shouldContinue; 1937 }, 1938 1939 /** @private Clean up before parent is detached. */ 1940 _parentWillDetach: function (notifyStack) { 1941 var state = this.get('viewState'), 1942 shouldContinue = true; 1943 1944 // Handle all 12 possible view states. 1945 switch (state) { 1946 1947 // Scenario: The child view is visible. 1948 // Result: Do nothing and continue. 1949 case SC.CoreView.ATTACHED_SHOWN: 1950 break; 1951 1952 // Scenario: The child view is animating. 1953 // Result: Complete its animation immediately and continue. 1954 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: // TODO: We need concurrent states! 1955 case SC.CoreView.ATTACHED_SHOWING: 1956 case SC.CoreView.ATTACHED_BUILDING_IN: // Was building in and didn't have a build out. 1957 case SC.CoreView.ATTACHED_BUILDING_OUT: // Was building out on its own at the same time. 1958 case SC.CoreView.ATTACHED_HIDING: 1959 this.cancelAnimation(); 1960 break; 1961 1962 // Scenario: The child view has forced to unattached or unrendered state, or it's hidden. 1963 // Result: Don't continue. 1964 case SC.CoreView.UNRENDERED: 1965 case SC.CoreView.UNATTACHED: 1966 case SC.CoreView.ATTACHED_HIDDEN: 1967 shouldContinue = false; 1968 break; 1969 1970 // Invalid states. 1971 default: 1972 //@if(debug) 1973 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1974 // These states should not be reachable here: ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_OUT_BY_PARENT 1975 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillDetach".fmt(this)); 1976 //@endif 1977 // There's no need to continue to further child views. 1978 shouldContinue = false; 1979 } 1980 1981 if (shouldContinue) { 1982 // Allow children that have changed state to notify that they will be shown. 1983 notifyStack.push(this); 1984 } 1985 1986 return shouldContinue; 1987 }, 1988 1989 /** @private Prepares according to parent will show. */ 1990 _parentWillShowInDocument: function (notifyStack) { 1991 var state = this.get('viewState'), 1992 shouldContinue = true; 1993 1994 // Handle all 12 possible view states. 1995 switch (state) { 1996 1997 // Scenario: The child view is only hidden because of the parent. 1998 // Result: Run queued updates. This will notify. 1999 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 2000 this._executeQueuedUpdates(); 2001 2002 break; 2003 2004 // Scenario: The child view is hidden on its own or has been forced to an unrendered or unattached state. 2005 // Result: Do nothing and don't notify. 2006 case SC.CoreView.UNRENDERED: 2007 case SC.CoreView.UNATTACHED: 2008 case SC.CoreView.ATTACHED_HIDDEN: 2009 // There's no need to continue to further child views. 2010 shouldContinue = false; 2011 break; 2012 2013 // Invalid states. 2014 default: 2015 //@if(debug) 2016 // Add some debugging only warnings for if the view statechart is breaking assumptions. 2017 // These states should be impossible if the parent was HIDDEN. 2018 // ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_SHOWING, ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT 2019 // This state should be impossible if its parent was UNATTACHED (it should have been trimmed above): 2020 // ATTACHED_PARTIAL 2021 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillShowInDocument".fmt(this)); 2022 //@endif 2023 // There's no need to continue to further child views. 2024 shouldContinue = false; 2025 } 2026 2027 if (shouldContinue) { 2028 // Allow children that have changed state to notify that they will be shown. 2029 notifyStack.push(this); 2030 } 2031 2032 return shouldContinue; 2033 }, 2034 2035 /** @private */ 2036 _setupTransition: function (transition) { 2037 // Get a copy of the layout. 2038 var layout = SC.clone(this.get('layout')); 2039 // Prepare for a transition. 2040 this._preTransitionLayout = layout; 2041 this._preTransitionFrame = this.get('borderFrame'); 2042 // Cache appropriate layout values. 2043 var layoutProperties = SC.get(transition, 'layoutProperties'); 2044 // If the transition specifies any layouts, cache them. 2045 if (layoutProperties && layoutProperties.length) { 2046 this._transitionLayoutCache = {}; 2047 var i, prop, len = layoutProperties.length; 2048 for (i = 0; i < len; i++) { 2049 prop = layoutProperties[i]; 2050 this._transitionLayoutCache[prop] = layout[prop] === undefined ? null : layout[prop]; 2051 } 2052 } 2053 }, 2054 2055 /** @private */ 2056 _teardownTransition: function () { 2057 // Make sure this isn't being called twice for the same transition. For example, 2058 // some transition plugins will send a didTransitionIn/Out event even if the 2059 // transition was cancelled. 2060 2061 // If we have a hash of cached layout properties, adjust back to it. 2062 if (this._transitionLayoutCache) { 2063 this.adjust(this._transitionLayoutCache); 2064 } 2065 // Otherwise, just set the layout back to what it was. 2066 else if (this._preTransitionLayout) { 2067 this.set('layout', this._preTransitionLayout); 2068 } 2069 // Clean up. 2070 this._preTransitionLayout = null; 2071 this._preTransitionFrame = null; 2072 this._transitionLayoutCache = null; 2073 }, 2074 2075 /** @private Attempts to run a transition hide, ensuring any incoming transitions are stopped in place. */ 2076 _transitionHide: function (inPlace) { 2077 var transitionHide = this.get('transitionHide'), 2078 options = this.get('transitionHideOptions') || {}; 2079 2080 //@if (debug) 2081 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2082 SC.Logger.log('%c%@ — _transitionHide()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2083 } 2084 //@endif 2085 2086 // switch (state) { 2087 // case SC.CoreView.ATTACHED_SHOWING: 2088 // case SC.CoreView.ATTACHED_BUILDING_IN: 2089 // this.cancelAnimation(SC.LayoutState.CURRENT); 2090 // inPlace = true; 2091 // break; 2092 // default: 2093 if (!inPlace) { 2094 this._setupTransition(transitionHide); 2095 } 2096 // } 2097 2098 // Set up the hiding transition. 2099 if (transitionHide.setup) { 2100 transitionHide.setup(this, options, inPlace); 2101 } 2102 2103 // Execute the hiding transition. 2104 transitionHide.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2105 2106 // Set the proper state. 2107 this._gotoAttachedHidingState(); 2108 }, 2109 2110 /** @private Attempts to run a transition in, ensuring any outgoing transitions are stopped in place. */ 2111 _transitionIn: function (inPlace) { 2112 var transitionIn = this.get('transitionIn'), 2113 options = this.get('transitionInOptions') || {}; 2114 2115 //@if (debug) 2116 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2117 SC.Logger.log('%c%@ — _transitionIn()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2118 } 2119 //@endif 2120 2121 if (!inPlace) { 2122 this._setupTransition(transitionIn); 2123 } 2124 2125 // Set up the incoming transition. 2126 if (transitionIn.setup) { 2127 transitionIn.setup(this, options, inPlace); 2128 } 2129 2130 // Execute the incoming transition. 2131 transitionIn.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2132 2133 // Set the proper state. 2134 this._gotoAttachedBuildingInState(); 2135 }, 2136 2137 /** @private Attempts to run a transition out, ensuring any incoming transitions are stopped in place. */ 2138 _transitionOut: function (inPlace, owningView) { 2139 var transitionOut = this.get('transitionOut'), 2140 options = this.get('transitionOutOptions') || {}; 2141 2142 if (!inPlace) { 2143 this._setupTransition(transitionOut); 2144 } 2145 2146 // Increment the shared building out count. 2147 owningView._sc_buildOutCount++; 2148 2149 // Set up the outgoing transition. 2150 if (transitionOut.setup) { 2151 transitionOut.setup(this, options, inPlace); 2152 } 2153 2154 // Execute the outgoing transition. 2155 transitionOut.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2156 2157 // Set the proper state. 2158 if (owningView === this) { 2159 this._gotoAttachedBuildingOutState(); 2160 } else { 2161 this._gotoAttachedBuildingOutByParentState(); 2162 } 2163 }, 2164 2165 /** @private Attempts to run a transition show, ensuring any hiding transitions are stopped in place. */ 2166 _transitionShow: function (inPlace) { 2167 var transitionShow = this.get('transitionShow'), 2168 options = this.get('transitionShowOptions') || {}; 2169 2170 //@if (debug) 2171 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2172 SC.Logger.log('%c%@ — _transitionShow()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2173 } 2174 //@endif 2175 2176 if (!inPlace) { 2177 this._setupTransition(transitionShow); 2178 } 2179 2180 // Set up the showing transition. 2181 if (transitionShow.setup) { 2182 transitionShow.setup(this, options, inPlace); 2183 } 2184 2185 // Execute the showing transition. 2186 transitionShow.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2187 2188 // Set the proper state. 2189 this._gotoAttachedShowingState(); 2190 }, 2191 2192 /** @private Goes to the proper attached state depending on its parents state*/ 2193 _gotoSomeAttachedState: function () { 2194 var parentView = this.get('parentView'), 2195 isParentHidden = parentView ? parentView.get('viewState') & SC.CoreView.IS_HIDDEN : false, 2196 // Views without a parent are not limited by a parent's current state. 2197 isParentShown = parentView ? parentView.get('viewState') & SC.CoreView.IS_SHOWN : true; 2198 2199 // Set the proper state. 2200 if (isParentShown) { 2201 if (this.get('isVisible')) { 2202 this._gotoAttachedShownState(); 2203 } else { 2204 this._gotoAttachedHiddenState(); 2205 } 2206 } else if (isParentHidden) { 2207 this._gotoAttachedHiddenByParentState(); 2208 } else { 2209 this._gotoAttachedPartialState(); 2210 } 2211 } 2212 2213 }); 2214