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 this._doShow(); 1515 } else { 1516 this._doHide(); 1517 } 1518 }, 1519 1520 /** @private 1521 Adds the 'focus' class to the view. 1522 1523 This observer is connected when the view is attached and is disconnected 1524 when the view is detached. 1525 */ 1526 _isFirstResponderDidChange: function () { 1527 var isFirstResponder = this.get('isFirstResponder'); 1528 1529 this.$().toggleClass('focus', isFirstResponder); 1530 }, 1531 1532 /** @private Attempts to call `didAppendToDocument` on the view. */ 1533 _notifyDidAttach: function () { 1534 // If we don't have the layout module then we don't know the frame until appended to the document. 1535 this.notifyPropertyChange('frame'); 1536 1537 // Notify. 1538 if (this.didAppendToDocument) { this.didAppendToDocument(); } 1539 }, 1540 1541 /** @private Attempts to call `didHideInDocument` on the view. */ 1542 _notifyDidHideInDocument: function () { 1543 if (this.didHideInDocument) { this.didHideInDocument(); } 1544 }, 1545 1546 /** @private Attempts to call `didCreateLayer` on the view. */ 1547 _notifyDidRender: function () { 1548 var mixins = this.didCreateLayerMixin, 1549 idx, len; 1550 1551 // Send notice that the layer was created. 1552 if (this.didCreateLayer) { this.didCreateLayer(); } 1553 if (mixins) { 1554 len = mixins.length; 1555 for (idx = 0; idx < len; ++idx) { 1556 mixins[idx].call(this); 1557 } 1558 } 1559 }, 1560 1561 /** @private Attempts to call `didShowInDocument` on the view. */ 1562 _notifyDidShowInDocument: function () { 1563 if (this.didShowInDocument) { this.didShowInDocument(); } 1564 }, 1565 1566 /** @private Attempts to call `willDestroyLayer` on the view. */ 1567 _notifyWillDestroyLayer: function () { 1568 var idx, len, 1569 mixins; 1570 1571 mixins = this.willDestroyLayerMixin; 1572 if (mixins) { 1573 len = mixins.length; 1574 for (idx = 0; idx < len; ++idx) { 1575 mixins[idx].call(this); 1576 } 1577 } 1578 1579 if (this.willDestroyLayer) { this.willDestroyLayer(); } 1580 }, 1581 1582 /** @private Attempts to call `willRemoveFromDocument` on the view. */ 1583 _notifyWillDetach: function () { 1584 if (this.willRemoveFromDocument) { this.willRemoveFromDocument(); } 1585 }, 1586 1587 /** @private Attempts to call `willHideInDocument` on the view. */ 1588 _notifyWillHideInDocument: function () { 1589 if (this.willHideInDocument) { this.willHideInDocument(); } 1590 }, 1591 1592 /** @private Attempts to call `willShowInDocument` on the view. */ 1593 _notifyWillShowInDocument: function () { 1594 if (this.willShowInDocument) { this.willShowInDocument(); } 1595 }, 1596 1597 /** @private Routes according to parent did append. */ 1598 _parentDidAttach: function (notifyStack) { 1599 var state = this.get('viewState'), 1600 shouldContinue = true; 1601 1602 // Handle all 12 possible view states. 1603 switch (state) { 1604 1605 // Scenario: The child view was attached to the parent, which was unattached. 1606 // Result: Update the child and then move it to the proper attached state. 1607 case SC.CoreView.ATTACHED_PARTIAL: 1608 // Run any queued updates. 1609 this._executeQueuedUpdates(); 1610 1611 // Go to the proper state. 1612 this._gotoSomeAttachedState(); 1613 1614 // If there is a transition in, run it. TODO: Check state here? 1615 var transitionIn = this.get('transitionIn'); 1616 if (transitionIn) { 1617 this._transitionIn(false); 1618 } 1619 1620 break; 1621 1622 // Scenario: The child is unrendered or unattached. 1623 // Result: The child would need to be forced into this state by its parent (otherwise it should 1624 // be in an ATTACHED_PARTIAL state), so just leave it alone and don't notify. 1625 case SC.CoreView.UNRENDERED: // Render + attach? 1626 case SC.CoreView.UNATTACHED: // Attach? 1627 // There's no need to continue to further child views. 1628 shouldContinue = false; 1629 break; 1630 1631 // Invalid states. 1632 default: 1633 //@if(debug) 1634 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1635 // All other states should be impossible if parent was UNATTACHED: 1636 // 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 1637 SC.warn("Core Developer Warning: Found invalid state for view, %@, in _parentDidAttach".fmt(this)); 1638 //@endif 1639 1640 // There's no need to continue to further child views. 1641 shouldContinue = false; 1642 } 1643 1644 if (shouldContinue) { 1645 // Allow children that have changed state to notify that they have been attached. 1646 notifyStack.push(this); 1647 } 1648 1649 return shouldContinue; 1650 }, 1651 1652 /** @private Updates according to parent did cancel build out. */ 1653 _parentDidCancelBuildOut: function () { 1654 var state = this.get('viewState'); 1655 1656 // If the view was building out because its parent was building out, attempt to reverse or 1657 // revert the transition. 1658 if (state === SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT) { 1659 var transitionIn = this.get('transitionIn'); 1660 1661 // Cancel the building out transition (in place if we are going to switch to transitioning back in). 1662 this.cancelAnimation(transitionIn ? SC.LayoutState.CURRENT : undefined); 1663 1664 // Set the proper state. 1665 this._gotoAttachedShownState(); 1666 1667 if (transitionIn) { 1668 this._transitionIn(true); 1669 } 1670 1671 // If the view was building out on its own or is hidden we can ignore it. 1672 } else if (state === SC.CoreView.ATTACHED_BUILDING_OUT || state & 1673 SC.CoreView.IS_HIDDEN) { 1674 // There's no need to continue to further child views. 1675 return false; 1676 } 1677 }, 1678 1679 /** @private Update child view states when the parent hides. Top-down! */ 1680 _parentDidHideInDocument: function () { // notifyStack 1681 var state = this.get('viewState'), 1682 shouldContinue = false; 1683 1684 // Handle all 12 possible view states. 1685 switch (state) { 1686 1687 // Scenario: The child view was shown. 1688 // Result: Go to hidden by parent state. 1689 case SC.CoreView.ATTACHED_SHOWN: 1690 // Go to the proper state. 1691 this._gotoAttachedHiddenByParentState(); 1692 1693 shouldContinue = true; 1694 break; 1695 1696 // Scenario: The child view was hidden or forced to unrendered or unattached state. 1697 // Result: Do nothing. 1698 case SC.CoreView.UNRENDERED: 1699 case SC.CoreView.UNATTACHED: 1700 case SC.CoreView.ATTACHED_HIDDEN: 1701 break; 1702 1703 // Invalid states. 1704 default: 1705 //@if(debug) 1706 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1707 // All animating states should have been canceled when parent will hide is called. 1708 // ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_SHOWING, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT, ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_SHOWN_ANIMATING 1709 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidHideInDocument".fmt(this)); 1710 //@endif 1711 } 1712 1713 // if (shouldContinue) { 1714 // // Allow children that have changed state to notify that they have been hidden. 1715 // notifyStack.push(this); 1716 // } 1717 1718 return shouldContinue; 1719 }, 1720 1721 /** @private Routes according to parent did detach. */ 1722 _parentDidDetach: function () { 1723 var state = this.get('viewState'); 1724 1725 if (state & SC.CoreView.IS_ATTACHED) { 1726 // Update states after *will* and before *did* notifications! 1727 this._gotoAttachedPartialState(); 1728 } else { 1729 // There's no need to continue to further child views. 1730 return false; 1731 } 1732 }, 1733 1734 /** @private Configure child views when parent did render. */ 1735 _parentDidRender: function (notifyStack) { 1736 var state = this.get('viewState'), 1737 shouldContinue = true; 1738 1739 // Handle all 12 possible view states. 1740 switch (state) { 1741 1742 // Scenario: The child view was unrendered and now is rendered. 1743 // Result: Add rendered state observers and move it to the proper rendered state. 1744 case SC.CoreView.UNRENDERED: 1745 this._sc_addRenderedStateObservers(); 1746 1747 // Go to the proper state. 1748 this._gotoAttachedPartialState(); 1749 break; 1750 1751 // Invalid states. 1752 default: 1753 //@if(debug) 1754 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1755 // All other states should be impossible if parent was UNRENDERED: 1756 // 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 1757 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidRender".fmt(this)); 1758 //@endif 1759 1760 // There's no need to continue to further child views. 1761 shouldContinue = false; 1762 } 1763 1764 if (shouldContinue) { 1765 // Allow children that have changed state to notify that they have been rendered. 1766 notifyStack.push(this); 1767 } 1768 1769 return shouldContinue; 1770 }, 1771 1772 /** @private Update child view states when the parent shows. Top-down! */ 1773 _parentDidShowInDocument: function () { // notifyStack 1774 var state = this.get('viewState'), 1775 shouldContinue = true; 1776 1777 // Handle all 12 possible view states. 1778 switch (state) { 1779 1780 // Scenario: The child view is only hidden because of the parent. 1781 // Result: Go to shown state. This will notify. 1782 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 1783 this._gotoAttachedShownState(); 1784 1785 break; 1786 1787 // Scenario: The child view is hidden on its own or has been forced to an unrendered or unattached state. 1788 // Result: Do nothing and don't notify. 1789 case SC.CoreView.UNRENDERED: 1790 case SC.CoreView.UNATTACHED: 1791 case SC.CoreView.ATTACHED_HIDDEN: 1792 // There's no need to continue to further child views. 1793 shouldContinue = false; 1794 break; 1795 1796 // Invalid states. 1797 default: 1798 //@if(debug) 1799 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1800 // These states should be impossible if the parent was HIDDEN. 1801 // ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_SHOWING, ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT 1802 // This state should be impossible if its parent was UNATTACHED (it should have been trimmed above): 1803 // ATTACHED_PARTIAL 1804 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentDidShowInDocument".fmt(this)); 1805 //@endif 1806 // There's no need to continue to further child views. 1807 shouldContinue = false; 1808 } 1809 1810 // if (shouldContinue) { 1811 // // Allow children that have changed state to notify that they have been made visible. 1812 // notifyStack.push(this); 1813 // } 1814 1815 return shouldContinue; 1816 }, 1817 1818 /** @private Starts building out view if appropriate. */ 1819 _parentWillBuildOutFromDocument: function (owningView) { 1820 var state = this.get('viewState'), 1821 transitionOut = this.get('transitionOut'), 1822 shouldContinue = true; 1823 1824 switch (state) { 1825 case SC.CoreView.UNRENDERED: 1826 case SC.CoreView.UNATTACHED: 1827 case SC.CoreView.ATTACHED_BUILDING_OUT: 1828 case SC.CoreView.ATTACHED_HIDDEN: 1829 // There's no need to continue to further child views. 1830 shouldContinue = false; 1831 break; 1832 1833 // Scenario: The child view is building in at the same time that its ancestor wants to detach. 1834 // 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. 1835 case SC.CoreView.ATTACHED_BUILDING_IN: 1836 1837 // Cancel the build in transition. 1838 if (transitionOut) { 1839 this.cancelAnimation(SC.LayoutState.CURRENT); 1840 } else { 1841 this.cancelAnimation(); 1842 } 1843 1844 // Set the proper state. 1845 this._gotoAttachedShownState(); 1846 1847 // Build out the view by parent. 1848 if (transitionOut) { 1849 this._transitionOut(true, owningView); 1850 } 1851 1852 break; 1853 1854 // Scenario: The view is shown and possibly transitioning. 1855 // Result: Allow any transitions to continue concurrent with build out transition (may be conflicts). 1856 case SC.CoreView.ATTACHED_HIDING: 1857 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1858 case SC.CoreView.ATTACHED_SHOWING: 1859 case SC.CoreView.ATTACHED_SHOWN: 1860 1861 // Build out the view by parent. 1862 if (transitionOut) { 1863 this._transitionOut(false, owningView); 1864 } 1865 break; 1866 1867 // Invalid states. 1868 default: 1869 //@if(debug) 1870 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1871 // These states should not be reachable here: ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_OUT_BY_PARENT 1872 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillBuildOutFromDocument".fmt(this)); 1873 //@endif 1874 // There's no need to continue to further child views. 1875 shouldContinue = false; 1876 } 1877 1878 return shouldContinue; 1879 }, 1880 1881 /** @private Prepares according to parent will hide. This is called before the parent view hides 1882 completely, which may be after a hide transition completes. */ 1883 _parentWillHideInDocument: function () { // notifyStack 1884 var state = this.get('viewState'), 1885 shouldContinue = true; 1886 1887 // Handle all 12 possible view states. 1888 switch (state) { 1889 1890 // Scenario: The child view is visible. 1891 // Result: Do nothing and continue. 1892 case SC.CoreView.ATTACHED_SHOWN: 1893 break; 1894 1895 // Scenario: The child view is animating. 1896 // Result: Complete its animation immediately and continue. 1897 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: 1898 case SC.CoreView.ATTACHED_SHOWING: 1899 case SC.CoreView.ATTACHED_BUILDING_IN: 1900 case SC.CoreView.ATTACHED_BUILDING_OUT: 1901 case SC.CoreView.ATTACHED_BUILDING_OUT_BY_PARENT: 1902 case SC.CoreView.ATTACHED_HIDING: 1903 this.cancelAnimation(); 1904 break; 1905 1906 // Scenario: The child view is hidden or has been forced to an unrendered or unattached state. 1907 // Result: Do nothing and don't notify. 1908 case SC.CoreView.UNRENDERED: 1909 case SC.CoreView.UNATTACHED: 1910 case SC.CoreView.ATTACHED_HIDDEN: 1911 // There's no need to continue to further child views. 1912 break; 1913 1914 // Invalid states. 1915 default: 1916 //@if(debug) 1917 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1918 // This state should be impossible if its parent was UNATTACHED or HIDDEN/HIDING (it should have been trimmed above): 1919 // ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT 1920 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillHideInDocument".fmt(this)); 1921 //@endif 1922 // There's no need to continue to further child views. 1923 shouldContinue = false; 1924 } 1925 1926 // if (shouldContinue) { 1927 // // Allow children that have changed state to notify that they will be shown. 1928 // notifyStack.push(this); 1929 // } 1930 1931 return shouldContinue; 1932 }, 1933 1934 /** @private Clean up before parent is detached. */ 1935 _parentWillDetach: function (notifyStack) { 1936 var state = this.get('viewState'), 1937 shouldContinue = true; 1938 1939 // Handle all 12 possible view states. 1940 switch (state) { 1941 1942 // Scenario: The child view is visible. 1943 // Result: Do nothing and continue. 1944 case SC.CoreView.ATTACHED_SHOWN: 1945 break; 1946 1947 // Scenario: The child view is animating. 1948 // Result: Complete its animation immediately and continue. 1949 case SC.CoreView.ATTACHED_SHOWN_ANIMATING: // TODO: We need concurrent states! 1950 case SC.CoreView.ATTACHED_SHOWING: 1951 case SC.CoreView.ATTACHED_BUILDING_IN: // Was building in and didn't have a build out. 1952 case SC.CoreView.ATTACHED_BUILDING_OUT: // Was building out on its own at the same time. 1953 case SC.CoreView.ATTACHED_HIDING: 1954 this.cancelAnimation(); 1955 break; 1956 1957 // Scenario: The child view has forced to unattached or unrendered state, or it's hidden. 1958 // Result: Don't continue. 1959 case SC.CoreView.UNRENDERED: 1960 case SC.CoreView.UNATTACHED: 1961 case SC.CoreView.ATTACHED_HIDDEN: 1962 shouldContinue = false; 1963 break; 1964 1965 // Invalid states. 1966 default: 1967 //@if(debug) 1968 // Add some debugging only warnings for if the view statechart is breaking assumptions. 1969 // These states should not be reachable here: ATTACHED_PARTIAL, ATTACHED_HIDDEN_BY_PARENT, ATTACHED_BUILDING_OUT_BY_PARENT 1970 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillDetach".fmt(this)); 1971 //@endif 1972 // There's no need to continue to further child views. 1973 shouldContinue = false; 1974 } 1975 1976 if (shouldContinue) { 1977 // Allow children that have changed state to notify that they will be shown. 1978 notifyStack.push(this); 1979 } 1980 1981 return shouldContinue; 1982 }, 1983 1984 /** @private Prepares according to parent will show. */ 1985 _parentWillShowInDocument: function (notifyStack) { 1986 var state = this.get('viewState'), 1987 shouldContinue = true; 1988 1989 // Handle all 12 possible view states. 1990 switch (state) { 1991 1992 // Scenario: The child view is only hidden because of the parent. 1993 // Result: Run queued updates. This will notify. 1994 case SC.CoreView.ATTACHED_HIDDEN_BY_PARENT: 1995 this._executeQueuedUpdates(); 1996 1997 break; 1998 1999 // Scenario: The child view is hidden on its own or has been forced to an unrendered or unattached state. 2000 // Result: Do nothing and don't notify. 2001 case SC.CoreView.UNRENDERED: 2002 case SC.CoreView.UNATTACHED: 2003 case SC.CoreView.ATTACHED_HIDDEN: 2004 // There's no need to continue to further child views. 2005 shouldContinue = false; 2006 break; 2007 2008 // Invalid states. 2009 default: 2010 //@if(debug) 2011 // Add some debugging only warnings for if the view statechart is breaking assumptions. 2012 // These states should be impossible if the parent was HIDDEN. 2013 // ATTACHED_SHOWN, ATTACHED_SHOWN_ANIMATING, ATTACHED_SHOWING, ATTACHED_HIDING, ATTACHED_BUILDING_IN, ATTACHED_BUILDING_OUT, ATTACHED_BUILDING_OUT_BY_PARENT 2014 // This state should be impossible if its parent was UNATTACHED (it should have been trimmed above): 2015 // ATTACHED_PARTIAL 2016 SC.warn("Core Developer Warning: Found invalid state for view %@ in _parentWillShowInDocument".fmt(this)); 2017 //@endif 2018 // There's no need to continue to further child views. 2019 shouldContinue = false; 2020 } 2021 2022 if (shouldContinue) { 2023 // Allow children that have changed state to notify that they will be shown. 2024 notifyStack.push(this); 2025 } 2026 2027 return shouldContinue; 2028 }, 2029 2030 /** @private */ 2031 _setupTransition: function (transition) { 2032 // Get a copy of the layout. 2033 var layout = SC.clone(this.get('layout')); 2034 // Prepare for a transition. 2035 this._preTransitionLayout = layout; 2036 this._preTransitionFrame = this.get('borderFrame'); 2037 // Cache appropriate layout values. 2038 var layoutProperties = SC.get(transition, 'layoutProperties'); 2039 // If the transition specifies any layouts, cache them. 2040 if (layoutProperties && layoutProperties.length) { 2041 this._transitionLayoutCache = {}; 2042 var i, prop, len = layoutProperties.length; 2043 for (i = 0; i < len; i++) { 2044 prop = layoutProperties[i]; 2045 this._transitionLayoutCache[prop] = layout[prop] === undefined ? null : layout[prop]; 2046 } 2047 } 2048 }, 2049 2050 /** @private */ 2051 _teardownTransition: function () { 2052 // Make sure this isn't being called twice for the same transition. For example, 2053 // some transition plugins will send a didTransitionIn/Out event even if the 2054 // transition was cancelled. 2055 2056 // If we have a hash of cached layout properties, adjust back to it. 2057 if (this._transitionLayoutCache) { 2058 this.adjust(this._transitionLayoutCache); 2059 } 2060 // Otherwise, just set the layout back to what it was. 2061 else if (this._preTransitionLayout) { 2062 this.set('layout', this._preTransitionLayout); 2063 } 2064 // Clean up. 2065 this._preTransitionLayout = null; 2066 this._preTransitionFrame = null; 2067 this._transitionLayoutCache = null; 2068 }, 2069 2070 /** @private Attempts to run a transition hide, ensuring any incoming transitions are stopped in place. */ 2071 _transitionHide: function (inPlace) { 2072 var transitionHide = this.get('transitionHide'), 2073 options = this.get('transitionHideOptions') || {}; 2074 2075 //@if (debug) 2076 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2077 SC.Logger.log('%c%@ — _transitionHide()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2078 } 2079 //@endif 2080 2081 // switch (state) { 2082 // case SC.CoreView.ATTACHED_SHOWING: 2083 // case SC.CoreView.ATTACHED_BUILDING_IN: 2084 // this.cancelAnimation(SC.LayoutState.CURRENT); 2085 // inPlace = true; 2086 // break; 2087 // default: 2088 if (!inPlace) { 2089 this._setupTransition(transitionHide); 2090 } 2091 // } 2092 2093 // Set up the hiding transition. 2094 if (transitionHide.setup) { 2095 transitionHide.setup(this, options, inPlace); 2096 } 2097 2098 // Execute the hiding transition. 2099 transitionHide.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2100 2101 // Set the proper state. 2102 this._gotoAttachedHidingState(); 2103 }, 2104 2105 /** @private Attempts to run a transition in, ensuring any outgoing transitions are stopped in place. */ 2106 _transitionIn: function (inPlace) { 2107 var transitionIn = this.get('transitionIn'), 2108 options = this.get('transitionInOptions') || {}; 2109 2110 //@if (debug) 2111 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2112 SC.Logger.log('%c%@ — _transitionIn()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2113 } 2114 //@endif 2115 2116 if (!inPlace) { 2117 this._setupTransition(transitionIn); 2118 } 2119 2120 // Set up the incoming transition. 2121 if (transitionIn.setup) { 2122 transitionIn.setup(this, options, inPlace); 2123 } 2124 2125 // Execute the incoming transition. 2126 transitionIn.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2127 2128 // Set the proper state. 2129 this._gotoAttachedBuildingInState(); 2130 }, 2131 2132 /** @private Attempts to run a transition out, ensuring any incoming transitions are stopped in place. */ 2133 _transitionOut: function (inPlace, owningView) { 2134 var transitionOut = this.get('transitionOut'), 2135 options = this.get('transitionOutOptions') || {}; 2136 2137 if (!inPlace) { 2138 this._setupTransition(transitionOut); 2139 } 2140 2141 // Increment the shared building out count. 2142 owningView._sc_buildOutCount++; 2143 2144 // Set up the outgoing transition. 2145 if (transitionOut.setup) { 2146 transitionOut.setup(this, options, inPlace); 2147 } 2148 2149 // Execute the outgoing transition. 2150 transitionOut.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2151 2152 // Set the proper state. 2153 if (owningView === this) { 2154 this._gotoAttachedBuildingOutState(); 2155 } else { 2156 this._gotoAttachedBuildingOutByParentState(); 2157 } 2158 }, 2159 2160 /** @private Attempts to run a transition show, ensuring any hiding transitions are stopped in place. */ 2161 _transitionShow: function (inPlace) { 2162 var transitionShow = this.get('transitionShow'), 2163 options = this.get('transitionShowOptions') || {}; 2164 2165 //@if (debug) 2166 if (SC.LOG_VIEW_STATES || this.SC_LOG_VIEW_STATE) { 2167 SC.Logger.log('%c%@ — _transitionShow()'.fmt(this), SC.LOG_VIEW_STATES_STYLE[this.get('viewState')]); 2168 } 2169 //@endif 2170 2171 if (!inPlace) { 2172 this._setupTransition(transitionShow); 2173 } 2174 2175 // Set up the showing transition. 2176 if (transitionShow.setup) { 2177 transitionShow.setup(this, options, inPlace); 2178 } 2179 2180 // Execute the showing transition. 2181 transitionShow.run(this, options, this._preTransitionLayout, this._preTransitionFrame); 2182 2183 // Set the proper state. 2184 this._gotoAttachedShowingState(); 2185 }, 2186 2187 /** @private Goes to the proper attached state depending on its parents state*/ 2188 _gotoSomeAttachedState: function () { 2189 var parentView = this.get('parentView'), 2190 isParentHidden = parentView ? parentView.get('viewState') & SC.CoreView.IS_HIDDEN : false, 2191 // Views without a parent are not limited by a parent's current state. 2192 isParentShown = parentView ? parentView.get('viewState') & SC.CoreView.IS_SHOWN : true; 2193 2194 // Set the proper state. 2195 if (isParentShown) { 2196 if (this.get('isVisible')) { 2197 this._gotoAttachedShownState(); 2198 } else { 2199 this._gotoAttachedHiddenState(); 2200 } 2201 } else if (isParentHidden) { 2202 this._gotoAttachedHiddenByParentState(); 2203 } else { 2204 this._gotoAttachedPartialState(); 2205 } 2206 } 2207 2208 }); 2209