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