1 sc_require("views/view");
  2 sc_require('views/view/layout_style');
  3 
  4 /** Select a horizontal layout for various views.*/
  5 SC.LAYOUT_HORIZONTAL = 'sc-layout-horizontal';
  6 
  7 /** Select a vertical layout for various views.*/
  8 SC.LAYOUT_VERTICAL = 'sc-layout-vertical';
  9 
 10 /**
 11   Layout properties to take up the full width of a parent view.
 12 */
 13 SC.FULL_WIDTH = { left: 0, right: 0 };
 14 
 15 /**
 16   Layout properties to take up the full height of a parent view.
 17 */
 18 SC.FULL_HEIGHT = { top: 0, bottom: 0 };
 19 
 20 /**
 21   Layout properties to center.  Note that you must also specify a width and
 22   height for this to work.
 23 */
 24 SC.ANCHOR_CENTER = { centerX: 0, centerY: 0 };
 25 
 26 /**
 27   Layout property for width, height
 28 */
 29 
 30 SC.LAYOUT_AUTO = 'auto';
 31 
 32 // Regexes representating valid values for rotation and scale layout properties
 33 SC._ROTATION_VALUE_REGEX = /^\-?\d+(\.\d*)?(rad|deg)$/;
 34 SC._SCALE_VALUE_REGEX = /^\d+(,\d+){0,2}$/;
 35 
 36 SC.View.reopen(
 37   /** @scope SC.View.prototype */ {
 38 
 39   // ------------------------------------------------------------------------
 40   // Properties
 41   //
 42 
 43   /* @private Internal variable used to check for layout changes that resize. */
 44   _sc_previousLayout: null,
 45 
 46   /**
 47     The view's background color. Only recommended for use during prototyping and in views
 48     where the background color may change arbitrarily, for example in connection with an
 49     instance of `SC.Color`. Otherwise you should use CSS and `classNames` or
 50     `classNameBindings`.
 51 
 52     If set at create time, will be added to the view's layer. For dynamic background colors,
 53     you must add `backgroundColor` to the view's `displayProperties`.
 54 
 55     @type String
 56     @default null
 57   */
 58   backgroundColor: null,
 59 
 60   /**
 61     The frame of the view including the borders and scale
 62   */
 63   borderFrame: function () {
 64     var frame = this.get('frame'),
 65         ret = null;
 66 
 67     if (frame) {
 68       /*jshint eqnull:true */
 69       var layout = this.get('layout'),
 70         defaultValue = layout.border == null ? 0 : layout.border,
 71         borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
 72         borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue),
 73         borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
 74         borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue);
 75 
 76       ret = {
 77           x: frame.x,
 78           y: frame.y,
 79           width: frame.width,
 80           height: frame.height
 81         };
 82 
 83       var scale = frame.scale;
 84       /*jshint eqnull:true*/
 85       if (scale != null) {
 86         var scaledBorderTop = borderTop * scale,
 87             scaledBorderRight = borderRight * scale,
 88             scaledBorderBottom = borderBottom * scale,
 89             scaledBorderLeft = borderLeft * scale;
 90 
 91         ret.scale = scale;
 92         ret.x -= scaledBorderLeft;
 93         ret.y -= scaledBorderTop;
 94         ret.width += scaledBorderLeft + scaledBorderRight;
 95         ret.height += scaledBorderTop + scaledBorderBottom;
 96       } else {
 97         ret.x -= borderLeft;
 98         ret.y -= borderTop;
 99         ret.width += borderLeft + borderRight;
100         ret.height += borderTop + borderBottom;
101       }
102 
103       if (frame.transformOriginX != null) {
104         ret.transformOriginX = frame.transformOriginX;
105       }
106 
107       if (frame.transformOriginY != null) {
108         ret.transformOriginY = frame.transformOriginY;
109       }
110     }
111 
112     return ret;
113   }.property('frame').cacheable(),
114 
115 
116   /**
117     Set this property to YES whenever the view needs to layout its child
118     views.  Normally this property is set automatically whenever the layout
119     property for a child view changes.
120 
121     @type Boolean
122   */
123   childViewsNeedLayout: NO,
124 
125   /**
126     The child view layout plugin to use when laying out child views.
127 
128     You can set this property to a child layout plugin object to
129     automatically set and adjust the layouts of this view's child views
130     according to some specific layout style.  For instance, SproutCore includes
131     two such plugins, SC.View.VERTICAL_STACK and SC.View.HORIZONTAL_STACK.
132 
133     SC.View.VERTICAL_STACK will arrange child views in order in a vertical
134     stack, which only requires that the height of each child view be specified.
135     Likewise, SC.View.HORIZONTAL_STACK does the same in the horizontal
136     direction, which requires that the width of each child view be specified.
137 
138     Where child layout plugins are extremely useful, besides simplifying
139     the amount of layout code you need to write, is that they can update the
140     layouts automatically as things change.  For more details and examples,
141     please see the documentation for SC.View.VERTICAL_STACK and
142     SC.View.HORIZONTAL_STACK.
143 
144     To define your own child view layout plugin, simply create an object that
145     conforms to the SC.ChildViewLayoutProtocol protocol.
146 
147     **Note** This should only be set once and is not bindable.
148 
149     @type Object
150     @default null
151    */
152   childViewLayout: null,
153 
154   /**
155     The options for the given child view layout plugin.
156 
157     These options are specific to the current child layout plugin being used and
158     are used to modify the applied layouts.  For example, SC.View.VERTICAL_STACK
159     accepts options like:
160 
161         childViewLayoutOptions: {
162           paddingAfter: 20,
163           paddingBefore: 20,
164           spacing: 10
165         }
166 
167     To determine what options may be used for a given plugin and to see what the
168     default options are, please refer to the documentation for the child layout
169     plugin being used.
170 
171     @type Object
172     @default null
173   */
174   childViewLayoutOptions: null,
175 
176   /** @private The explicit layout of the view, computed from the layout using the explicit position. */
177   explicitLayout: function () {
178     var layout = this.get('layout'),
179         ret = null;
180 
181     if (layout) {
182       ret = this._sc_computeExplicitLayout(layout);
183     }
184 
185     return ret;
186   }.property('layout').cacheable(),
187 
188   /**
189     Walks like a duck. Is `true` to indicate that a view has layout support.
190   */
191   hasLayout: true,
192 
193   /**
194     Whether the view and its child views should be monitored for changes that
195     affect the current child view layout.
196 
197     When `true` and using a childViewLayout plugin, the view and its child views
198     will be observed for any changes that would affect the layout of all the
199     child views.  For example, if `isChildViewLayout` is true and using
200     SC.View.VERTICAL_STACK, if any child view's height or visibility changes
201     all of the child views will be re-adjusted.
202 
203     If you only want to automatically layout the child views once, you can
204     set this to `false` to improve performance.
205 
206     @type Boolean
207     @default true
208   */
209   isChildViewLayoutLive: true,
210 
211   /**
212     Returns whether the height is 'fixed' or not. A fixed height is defined on the layout
213     as an integer number of pixels.  Fixed widths are therefore unaffected by changes
214     to their parent view's height.
215 
216     @field
217     @returns {Boolean} YES if fixed, NO otherwise
218     @test in layout
219   */
220   isFixedHeight: function() {
221     var layout = this.get('layout');
222 
223     // Height is fixed if it has a height and it isn't SC.LAYOUT_AUTO or a percent.
224     return (layout.height !== undefined) &&
225       !SC.isPercentage(layout.height) &&
226       (layout.height !== SC.LAYOUT_AUTO);
227   }.property('layout').cacheable(),
228 
229   /**
230     Returns whether the layout is 'fixed' or not.  A fixed layout means a
231     fixed left & top position and fixed width & height.  Fixed layouts are
232     therefore unaffected by changes to their parent view's layout.
233 
234     @returns {Boolean} YES if fixed, NO otherwise
235     @test in layoutStyle
236   */
237   isFixedLayout: function () {
238     return this.get('isFixedPosition') && this.get('isFixedSize');
239   }.property('isFixedPosition', 'isFixedSize').cacheable(),
240 
241   /**
242     Returns whether the position is 'fixed' or not.  A fixed position means a
243     fixed left & top position within its parent's frame.  Fixed positions are
244     therefore unaffected by changes to their parent view's size.
245 
246     @field
247     @returns {Boolean} YES if fixed, NO otherwise
248     @test in layoutStyle
249   */
250   isFixedPosition: function () {
251     var explicitLayout = this.get('explicitLayout'),
252       left = explicitLayout.left,
253       top = explicitLayout.top,
254       hasFixedLeft,
255       hasFixedTop;
256 
257     // Position is fixed if it has left + top, but not as percentages and not as SC.LAYOUT_AUTO.
258     hasFixedLeft = left !== undefined && !SC.isPercentage(left) && left !== SC.LAYOUT_AUTO;
259     hasFixedTop = top !== undefined && !SC.isPercentage(top) && top !== SC.LAYOUT_AUTO;
260 
261     return hasFixedLeft && hasFixedTop;
262   }.property('explicitLayout').cacheable(),
263 
264   /**
265     Returns whether the size is 'fixed' or not.  A fixed size means a fixed
266     width and height.  Fixed sizes are therefore unaffected by changes to their
267     parent view's size.
268 
269     @field
270     @returns {Boolean} YES if fixed, NO otherwise
271     @test in layout
272   */
273   isFixedSize: function () {
274     return this.get('isFixedHeight') && this.get('isFixedWidth');
275   }.property('isFixedWidth', 'isFixedHeight').cacheable(),
276 
277   /**
278     Returns whether the width is 'fixed' or not. A fixed width is defined on the layout
279     as an integer number of pixels.  Fixed widths are therefore unaffected by changes
280     to their parent view's width.
281 
282     @field
283     @returns {Boolean} YES if fixed, NO otherwise
284     @test in layout
285   */
286   isFixedWidth: function() {
287     var layout = this.get('layout');
288 
289     // Width is fixed if it has a width and it isn't SC.LAYOUT_AUTO or a percent.
290     return (layout.width !== undefined) &&
291       !SC.isPercentage(layout.width) &&
292       (layout.width !== SC.LAYOUT_AUTO);
293   }.property('layout').cacheable(),
294 
295   /**
296     Set the layout to a hash of layout properties to describe in detail how your view
297     should be positioned on screen. Like most application development environments,
298     your views are laid out absolutely, relative to their parent view.
299 
300     You can define your layout using combinations of the following positional properties:
301 
302      - left
303      - top
304      - right
305      - bottom
306      - height
307      - width
308      - centerX: offset from center, horizontally
309      - centerY: offset from center, vertically
310      - minWidth
311      - minHeight
312      - maxWidth
313      - maxHeight
314      - scale: once positioned, scales the view in place.
315      - transformOriginX, transformOriginY: defines the point (as a decimal percentage) around which
316        your view will scale. (Also impacts rotation; see below.)
317 
318     They are processed by SproutCore's layout engine and used to position the view's element onscreen. They are
319     also reliably and speedily processed into a scaled rectangle (with x, y, height, width, scale and origin
320     values) available on the frame property. See documentation on it and the clippingFrame property for more.
321 
322     Most of these properties take integer numbers of pixels, for example { left: 10 }, or fractional
323     percentages like { left 0.25 }. Exceptions include scale, which takes a scale factor (e.g. { scale:
324     2 } doubles the view's size), and transformOriginX/Y which take a decimal percent, and default to 0.5
325     (the center of the view).
326 
327     It's possible to define very sophisticated layouts with these properties alone. For example, you
328     can define a view which takes up the full screen until it reaches a certain width, and aligns to
329     the left thereafter, with { left: 0, right: 0, maxWidth: 400 }. (If you need the flexibility to
330     assign entirely different layouts at different screen or window sizes, see the Design Modes
331     documentation under SC.Application.)
332 
333     Certain layout combinations are nonsensical and of course should be avoided. For example, you
334     can use left + right or left + width, but not left + right + width.
335 
336     If your view has a CSS border, it's important that you specify its thickness in the layout hash,
337     using one or more of the following border properties, as well as in your CSS. This is an unfortunate
338     bit of repetition, but it's necessary to allow SproutCore to adjust the layout to compensate. (HTML
339     positions borders outside of the body of an element; SproutCore positions them inside their rectangles.)
340 
341      - border: border thickness on all sides
342      - borderTop: top border thickness
343      - borderRight: right border thickness
344      - borderBottom: bottom border thickness
345      - borderLeft: bottom left thickness
346 
347     You can also use the following layout properties, which don't impact your view's frame.
348 
349      - opacity: the opacity of the view
350      - rotate: once positioned, rotates the view in place.
351      - zIndex: position above or below other views (Not recommended. Control sibling view
352        overlay with childView order (later views draw above earlier views) where possible.)
353 
354     To change a layout property, you should use the adjust method, which handles some particulars for you.
355 
356     @type Object
357     @default { top: 0, left: 0, bottom: 0, right: 0 }
358     @test in layoutStyle
359   */
360   layout: { top: 0, left: 0, bottom: 0, right: 0 },
361 
362   /**
363     The view responsible for laying out this view.  The default version
364     returns the current parent view.
365   */
366   layoutView: function () {
367     return this.get('parentView');
368   }.property('parentView').cacheable(),
369 
370   /**
371     The transition plugin to use when this view is moved or resized by adjusting
372     its layout.
373 
374     SC.CoreView uses a pluggable transition architecture where the transition
375     setup, execution and cleanup can be handled by a plugin.  This allows you
376     to create complex transition animations and share them across all your views
377     with only a single line of code.
378 
379     There are a number of pre-built transition adjust plugins available in
380     the SproutCore foundation framework:
381 
382       SC.View.SMOOTH_ADJUST
383       SC.View.BOUNCE_ADJUST
384       SC.View.SPRING_ADJUST
385 
386     To create a custom transition plugin simply create a regular JavaScript
387     object that conforms to the SC.ViewTransitionProtocol protocol.
388 
389     NOTE: When creating custom transition adjust plugins, be aware that SC.View
390     will not call the `setup` method of the plugin, only the `run` method.
391 
392     @type Object (SC.ViewTransitionProtocol)
393     @default null
394     @since Version 1.10
395   */
396   transitionAdjust: null,
397 
398   /**
399     The options for the given `transitionAdjust` plugin.
400 
401     These options are specific to the current transition plugin used and are
402     used to modify the transition animation.  To determine what options
403     may be used for a given plugin and to see what the default options are,
404     see the documentation for the transition plugin being used.
405 
406     Most transitions will accept a duration and timing option, but may
407     also use other options.  For example, SC.View.BOUNCE_ADJUST accepts options
408     like:
409 
410         transitionAdjustOptions: {
411           bounciness: 0.5, // how much the adjustment should bounce back each time
412           bounces: 4, // the number of bounces
413           duration: 0.25,
414           delay: 1
415         }
416 
417     @type Object
418     @default null
419     @since Version 1.10
420   */
421   transitionAdjustOptions: null,
422 
423   /**
424     Activates use of brower's static layout. To activate, set this property to YES.
425 
426     @type Boolean
427     @default NO
428   */
429   useStaticLayout: NO,
430 
431   // ------------------------------------------------------------------------
432   // Methods
433   //
434 
435   /** @private */
436   _sc_adjustForBorder: function (frame, layout) {
437     /*jshint eqnull:true */
438     var defaultValue = layout.border == null ? 0 : layout.border,
439         borderTop = this._sc_explicitValueFor(layout.borderTop, defaultValue),
440         borderLeft = this._sc_explicitValueFor(layout.borderLeft, defaultValue),
441         borderBottom = this._sc_explicitValueFor(layout.borderBottom, defaultValue),
442         borderRight = this._sc_explicitValueFor(layout.borderRight, defaultValue);
443 
444     frame.x += borderLeft; // The border on the left pushes the frame to the right
445     frame.y += borderTop; // The border on the top pushes the frame down
446     frame.width -= (borderLeft + borderRight); // Border takes up space
447     frame.height -= (borderTop + borderBottom); // Border takes up space
448 
449     return frame;
450   },
451 
452   /** @private */
453   _sc_adjustForScale: function (frame, layout) {
454 
455     // Scale not supported on this platform, ignore the layout values.
456     if (!SC.platform.supportsCSSTransforms) {
457       frame.scale = 1;
458       frame.transformOriginX = frame.transformOriginY = 0.5;
459 
460     // Use scale.
461     } else {
462 
463       // Get the scale and transform origins, if not provided. (Note inlining of SC.none for performance)
464       /*jshint eqnull:true*/
465       var scale = layout.scale,
466           oX = layout.transformOriginX,
467           oY = layout.transformOriginY;
468 
469       // If the scale is set and isn't 1, do some calculations.
470       if (scale != null && scale !== 1) {
471         // Scale the rect.
472         frame = SC.scaleRect(frame, scale, oX, oY);
473 
474         // Add the scale and original unscaled height and width.
475         frame.scale = scale;
476       }
477 
478       // If the origin is set and isn't 0.5, include it.
479       if (oX != null && oX !== 0.5) {
480         frame.transformOriginX = oX;
481       }
482 
483       // If the origin is set and isn't 0.5, include it.
484       if (oY != null && oY !== 0.5) {
485         frame.transformOriginY = oY;
486       }
487     }
488 
489     // Make sure width/height are never < 0.
490     if (frame.height < 0) frame.height = 0;
491     if (frame.width < 0) frame.width = 0;
492 
493     return frame;
494   },
495 
496   /** @private Apply the adjustment to a clone of the layout (cloned unless newLayout is passed in) */
497   _sc_applyAdjustment: function (key, newValue, layout, newLayout) {
498     var animateLayout = this._animateLayout;
499 
500     // If a call to animate occurs in the same run loop, the animation layout
501     // would still be applied in the next run loop, potentially overriding this
502     // adjustment. So we need to cancel the animation layout.
503     if (animateLayout) {
504       if (newValue === null) {
505         delete animateLayout[key];
506       } else {
507         animateLayout[key] = newValue;
508       }
509 
510       if (this._pendingAnimations && this._pendingAnimations[key]) {
511         // Adjusting a value that was about to be animated cancels the animation.
512         delete this._pendingAnimations[key];
513       }
514 
515     }
516 
517     // Ignore undefined values or values equal to the current value.
518     /*jshint eqeqeq:false*/
519     if (newValue !== undefined && layout[key] != newValue) { // coerced so '100' == 100
520       // Only clone the layout if it is not given.
521       if (!newLayout) newLayout = SC.clone(this.get('layout'));
522 
523       if (newValue === null) {
524         delete newLayout[key];
525       } else {
526         newLayout[key] = newValue;
527       }
528     }
529 
530     return newLayout;
531   },
532 
533   /** @private */
534   _sc_checkForResize: function (previousLayout, currentLayout) {
535     // Did our layout change in a way that could cause us to have changed size?  If
536     // not, then there's no need to invalidate the frames of our child views.
537     var didResizeHeight = true,
538         didResizeWidth = true,
539         didResize = true;
540 
541     // We test the new layout to see if we believe it will affect the view's frame.
542     // Since all the child view frames may depend on the parent's frame, it's
543     // best only to notify a frame change when it actually happens.
544     /*jshint eqnull:true*/
545 
546     // Simple test: Width is defined and hasn't changed.
547     // Complex test: No defined width, left or right haven't changed.
548     if (previousLayout != null &&
549         ((previousLayout.width != null &&
550           previousLayout.width === currentLayout.width) ||
551          (previousLayout.width == null &&
552            currentLayout.width == null &&
553            previousLayout.left === currentLayout.left &&
554            previousLayout.right === currentLayout.right))) {
555       didResizeWidth = false;
556     }
557 
558     // Simple test: Height is defined and hasn't changed.
559     // Complex test: No defined height, top or bottom haven't changed.
560     if (!didResizeWidth &&
561         ((previousLayout.height != null &&
562           previousLayout.height === currentLayout.height) ||
563          (previousLayout.height == null &&
564            currentLayout.height == null &&
565            previousLayout.top === currentLayout.top &&
566            previousLayout.bottom === currentLayout.bottom))) {
567       didResizeHeight = false;
568     }
569 
570     // Border test: Even if the width & height haven't changed, a change in a border would be a resize.
571     if (!didResizeHeight && !didResizeWidth) {
572       didResize = !(previousLayout.border === currentLayout.border &&
573               previousLayout.borderTop === currentLayout.borderTop &&
574               previousLayout.borderLeft === currentLayout.borderLeft &&
575               previousLayout.borderBottom === currentLayout.borderBottom &&
576               previousLayout.borderRight === currentLayout.borderRight);
577     }
578 
579     return didResize;
580   },
581 
582   /** @private Called when the child view layout plugin or options change. */
583   _cvl_childViewLayoutDidChange: function () {
584     this.set('childViewsNeedLayout', true);
585 
586     // Filter the input channel.
587     this.invokeOnce(this.layoutChildViewsIfNeeded);
588   },
589 
590   /** @private Called when the child views change. */
591   _cvl_childViewsDidChange: function () {
592     this._cvl_teardownChildViewsLiveLayout();
593     this._cvl_setupChildViewsLiveLayout();
594 
595     this.set('childViewsNeedLayout', true);
596 
597     // Filter the input channel.
598     this.invokeOnce(this.layoutChildViewsIfNeeded);
599   },
600 
601   /** @private Add observers to the child views for automatic child view layout. */
602   _cvl_setupChildViewsLiveLayout: function () {
603     var childViewLayout = this.childViewLayout,
604       childViews,
605       childLayoutProperties = childViewLayout.childLayoutProperties || [];
606 
607     // Create a reference to the current child views so that we can clean them if they change.
608     childViews = this._cvl_childViews = this.get('childViews');
609     for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
610       var observedProperty = childLayoutProperties[i];
611 
612       for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
613         var childView = childViews.objectAt(j);
614         if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
615           childView.addObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
616         }
617       }
618     }
619   },
620 
621   /** @private Remove observers from the child views for automatic child view layout. */
622   _cvl_teardownChildViewsLiveLayout: function () {
623     var childViewLayout = this.childViewLayout,
624       childViews = this._cvl_childViews || [],
625       childLayoutProperties = childViewLayout.childLayoutProperties || [];
626 
627     for (var i = 0, len = childLayoutProperties.length; i < len; i++) {
628       var observedProperty = childLayoutProperties[i];
629 
630       for (var j = 0, jlen = childViews.get('length'); j < jlen; j++) {
631         var childView = childViews.objectAt(j);
632         if (!childView.get('useAbsoluteLayout') && !childView.get('useStaticLayout')) {
633           childView.removeObserver(observedProperty, this, this._cvl_childViewLayoutDidChange);
634         }
635       }
636     }
637   },
638 
639   /** @private Computes the explicit layout. */
640   _sc_computeExplicitLayout: function (layout) {
641     var ret = SC.copy(layout);
642 
643     /* jshint eqnull:true */
644     var hasBottom = (layout.bottom != null);
645     var hasRight = (layout.right != null);
646     var hasLeft = (layout.left != null);
647     var hasTop = (layout.top != null);
648     var hasCenterX = (layout.centerX != null);
649     var hasCenterY = (layout.centerY != null);
650     var hasHeight = (layout.height != null); //  || (layout.maxHeight != null)
651     var hasWidth = (layout.width != null); // || (layout.maxWidth != null)
652 
653     /*jshint eqnull:true */
654     // Left + Top take precedence (left & right & width becomes left & width).
655     delete ret.right; // Right will be set if needed below.
656     delete ret.bottom; // Bottom will be set if needed below.
657 
658     if (hasLeft) {
659       ret.left = layout.left;
660     } else if (!hasCenterX && !(hasWidth && hasRight)) {
661       ret.left = 0;
662     }
663 
664     if (hasRight && !(hasLeft && hasWidth)) {
665       ret.right = layout.right;
666     } else if (!hasCenterX && !hasWidth) {
667       ret.right = 0;
668     }
669 
670     //@if(debug)
671     // Debug-only warning when layout isn't valid.
672     // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
673     // if (hasRight && hasLeft && hasWidth) {
674     //   SC.warn("Developer Warning: When setting `width` in the layout, you must only set `left` or `right`, but not both: %@".fmt(this));
675     // }
676     //@endif
677 
678     if (hasTop) {
679       ret.top = layout.top;
680     } else if (!hasCenterY && !(hasHeight && hasBottom)) {
681       ret.top = 0;
682     }
683 
684     if (hasBottom && !(hasTop && hasHeight)) {
685       ret.bottom = layout.bottom;
686     } else if (!hasCenterY && !hasHeight) {
687       ret.bottom = 0;
688     }
689 
690     //@if(debug)
691     // Debug-only warning when layout isn't valid.
692     // UNUSED: This is too noisy for certain views that adjust their own layouts based on top of the default layout.
693     // if (hasBottom && hasTop && hasHeight) {
694     //   SC.warn("Developer Warning: When setting `height` in the layout, you must only set `top` or `bottom`, but not both: %@".fmt(this));
695     // }
696     //@endif
697 
698     // CENTERS
699     if (hasCenterX) {
700       ret.centerX = layout.centerX;
701 
702       //@if(debug)
703       // Debug-only warning when layout isn't valid.
704       if (hasCenterX && !hasWidth) {
705         SC.warn("Developer Warning: When setting `centerX` in the layout, you must also define the `width`: %@".fmt(this));
706       }
707       //@endif
708     }
709 
710     if (hasCenterY) {
711       ret.centerY = layout.centerY;
712 
713       //@if(debug)
714       // Debug-only warning when layout isn't valid.
715       if (hasCenterY && !hasHeight) {
716         SC.warn("Developer Warning: When setting `centerY` in the layout, you must also define the `height`: %@".fmt(this));
717       }
718       //@endif
719     }
720 
721     // BORDERS
722     // Apply border first, so that the more specific borderX values will override it next.
723     var border = layout.border;
724     if (border != null) {
725       ret.borderTop = border;
726       ret.borderRight = border;
727       ret.borderBottom = border;
728       ret.borderLeft = border;
729       delete ret.border;
730     }
731 
732     // Override generic border with more specific borderX.
733     if (layout.borderTop != null) {
734       ret.borderTop = layout.borderTop;
735     }
736     if (layout.borderRight != null) {
737       ret.borderRight = layout.borderRight;
738     }
739     if (layout.borderBottom != null) {
740       ret.borderBottom = layout.borderBottom;
741     }
742     if (layout.borderLeft != null) {
743       ret.borderLeft = layout.borderLeft;
744     }
745 
746     return ret;
747   },
748 
749   /** @private */
750   _sc_convertFrameFromViewHelper: function (frame, fromView, targetView) {
751     var myX = frame.x, myY = frame.y, myWidth = frame.width, myHeight = frame.height, view, f;
752 
753     // first, walk up from the view of the frame, up to the top level
754     if (fromView) {
755       view = fromView;
756       //Note: Intentional assignment of variable f
757       while (view && (f = view.get('frame'))) {
758 
759         // if scale != 1, then multiple by the scale (going from view to parent)
760         if (f.scale && f.scale !== 1) {
761           myX *= f.scale;
762           myY *= f.scale;
763           myWidth *= f.scale;
764           myHeight *= f.scale;
765         }
766 
767         myX += f.x;
768         myY += f.y;
769 
770         view = view.get('layoutView');
771       }
772     }
773 
774     // now, we'll walk down from the top level to the target view
775 
776     // construct an array of view ancestry, from
777     // the top level view down to the target view
778     if (targetView) {
779       var viewAncestors = [];
780       view = targetView;
781 
782       while (view && view.get('frame')) {
783         viewAncestors.unshift(view);
784         view = view.get('layoutView');
785       }
786 
787       // now walk the frame from
788       for (var i = 0; i < viewAncestors.length; i++ ) {
789         view = viewAncestors[i];
790         f = view.get('frame');
791 
792         myX -= f.x;
793         myY -= f.y;
794 
795         if (f.scale && f.scale !== 1) {
796           myX /= f.scale;
797           myY /= f.scale;
798           myWidth /= f.scale;
799           myHeight /= f.scale;
800         }
801       }
802     }
803 
804     return { x: myX, y: myY, width: myWidth, height: myHeight };
805   },
806 
807   /** @private */
808   _sc_explicitValueFor: function (givenValue, impliedValue) {
809     return givenValue === undefined ? impliedValue : givenValue;
810   },
811 
812   /** @private Attempts to run a transition adjust, ensuring any showing transitions are stopped in place. */
813   _sc_transitionAdjust: function (layout) {
814     var transitionAdjust = this.get('transitionAdjust'),
815       options = this.get('transitionAdjustOptions') || {};
816 
817     // Execute the adjusting transition.
818     transitionAdjust.run(this, options, layout);
819   },
820 
821   /** @private
822     Invoked by other views to notify this view that its frame has changed.
823 
824     This notifies the view that its frame property has changed,
825     then notifies its child views that their clipping frames may have changed.
826   */
827   _sc_viewFrameDidChange: function () {
828     this.notifyPropertyChange('frame');
829 
830     // Notify the children that their clipping frame may have changed. Top-down, because a child's
831     // clippingFrame is dependent on its parent's frame.
832     this._callOnChildViews('_sc_clippingFrameDidChange');
833   },
834 
835   /**
836     This convenience method will take the current layout, apply any changes
837     you pass and set it again.  It is more convenient than having to do this
838     yourself sometimes.
839 
840     You can pass just a key/value pair or a hash with several pairs.  You can
841     also pass a null value to delete a property.
842 
843     This method will avoid actually setting the layout if the value you pass
844     does not edit the layout.
845 
846     @param {String|Hash} key
847     @param {Object} value
848     @returns {SC.View} receiver
849   */
850   adjust: function (key, value) {
851     if (key === undefined) { return this; } // FAST PATH! Nothing to do.
852 
853     var layout = this.get('layout'),
854       newLayout;
855 
856     // Normalize arguments.
857     if (SC.typeOf(key) === SC.T_STRING) {
858       newLayout = this._sc_applyAdjustment(key, value, layout);
859     } else {
860       for (var aKey in key) {
861         if (!key.hasOwnProperty(aKey)) { continue; }
862 
863         newLayout = this._sc_applyAdjustment(aKey, key[aKey], layout, newLayout);
864       }
865     }
866 
867     // now set adjusted layout
868     if (newLayout) {
869       var transitionAdjust = this.get('transitionAdjust');
870 
871       if (this.get('viewState') & SC.CoreView.IS_SHOWN && transitionAdjust) {
872         // Run the adjust transition.
873         this._sc_transitionAdjust(newLayout);
874       } else {
875         this.set('layout', newLayout);
876       }
877     }
878 
879     return this;
880   },
881 
882   /** */
883   computeParentDimensions: function (frame) {
884     var parentView = this.get('parentView'),
885         parentFrame = (parentView) ? parentView.get('frame') : null,
886         ret;
887 
888     if (parentFrame) {
889       ret = {
890         width: parentFrame.width,
891         height: parentFrame.height
892       };
893     } else if (frame) {
894       ret = {
895         width: (frame.left || 0) + (frame.width || 0) + (frame.right || 0),
896         height: (frame.top || 0) + (frame.height || 0) + (frame.bottom || 0)
897       };
898     } else {
899       ret = {
900         width: 0,
901         height: 0
902       };
903     }
904 
905     return ret;
906   },
907 
908   /**
909     Converts a frame from the receiver's offset to the target offset.  Both
910     the receiver and the target must belong to the same pane.  If you pass
911     null, the conversion will be to the pane level.
912 
913     Note that the context of a view's frame is the view's parent frame.  In
914     other words, if you want to convert the frame of your view to the global
915     frame, then you should do:
916 
917         var pv = this.get('parentView'), frame = this.get('frame');
918         var newFrame = pv ? pv.convertFrameToView(frame, null) : frame;
919 
920     @param {Rect} frame the source frame
921     @param {SC.View} targetView the target view to convert to
922     @returns {Rect} converted frame
923     @test in convertFrames
924   */
925   convertFrameToView: function (frame, targetView) {
926     return this._sc_convertFrameFromViewHelper(frame, this, targetView);
927   },
928 
929   /**
930     Converts a frame offset in the coordinates of another view system to the
931     receiver's view.
932 
933     Note that the convext of a view's frame is relative to the view's
934     parentFrame.  For example, if you want to convert the frame of view that
935     belongs to another view to the receiver's frame you would do:
936 
937         var frame = view.get('frame');
938         var newFrame = this.convertFrameFromView(frame, view.get('parentView'));
939 
940     @param {Rect} frame the source frame
941     @param {SC.View} targetView the target view to convert to
942     @returns {Rect} converted frame
943     @test in converFrames
944   */
945   convertFrameFromView: function (frame, targetView) {
946     return this._sc_convertFrameFromViewHelper(frame, targetView, this);
947   },
948 
949   /** @private */
950   didTransitionAdjust: function () {},
951 
952   /**
953     This method is called whenever a property changes that invalidates the
954     layout of the view.  Changing the layout will do this automatically, but
955     you can add others if you want.
956 
957     Implementation Note:  In a traditional setup, we would simply observe
958     'layout' here, but as described above in the documentation for our custom
959     implementation of propertyDidChange(), this method must always run
960     immediately after 'layout' is updated to avoid the potential for stale
961     (incorrect) cached 'frame' values.
962 
963     @returns {SC.View} receiver
964   */
965   layoutDidChange: function () {
966     var currentLayout = this.get('layout');
967 
968     // Handle old style rotation.
969     if (!SC.none(currentLayout.rotate)) {
970       if (SC.none(currentLayout.rotateZ) && SC.platform.get('supportsCSS3DTransforms')) {
971         currentLayout.rotateZ = currentLayout.rotate;
972         delete currentLayout.rotate;
973       }
974     }
975 
976     // Optimize notifications depending on if we resized or just moved.
977     var didResize = this._sc_checkForResize(this._sc_previousLayout, currentLayout);
978 
979     // Cache the last layout to fine-tune notifications when the layout changes.
980     // NOTE: Do this before continuing so that any adjustments that occur in viewDidResize or from
981     //  _sc_viewFrameDidChange (say to the position after a resize), don't result in _sc_checkForResize
982     //  running against the old _sc_previousLayout.
983     this._sc_previousLayout = currentLayout;
984 
985     if (didResize) {
986       this.viewDidResize();
987     } else {
988       // Even if we didn't resize, our frame sould have changed.
989       // TODO: consider checking for position changes by testing the resulting frame against the cached frame. This is difficult to do.
990       this._sc_viewFrameDidChange();
991     }
992 
993     // Notify layoutView/parentView, unless we are transitioning.
994     var layoutView = this.get('layoutView');
995     if (layoutView) {
996       layoutView.set('childViewsNeedLayout', YES);
997       layoutView.layoutDidChangeFor(this);
998 
999       // Check if childViewsNeedLayout is still true.
1000       if (layoutView.get('childViewsNeedLayout')) {
1001         layoutView.invokeOnce(layoutView.layoutChildViewsIfNeeded);
1002       }
1003     } else {
1004       this.invokeOnce(this.updateLayout);
1005     }
1006 
1007     return this;
1008   },
1009 
1010   /**
1011     One of two methods that are invoked whenever one of your childViews
1012     layout changes.  This method is invoked every time a child view's layout
1013     changes to give you a chance to record the information about the view.
1014 
1015     Since this method may be called many times during a single run loop, you
1016     should keep this method pretty short.  The other method called when layout
1017     changes, layoutChildViews(), is invoked only once at the end of
1018     the run loop.  You should do any expensive operations (including changing
1019     a childView's actual layer) in this other method.
1020 
1021     Note that if as a result of running this method you decide that you do not
1022     need your layoutChildViews() method run later, you can set the
1023     childViewsNeedsLayout property to NO from this method and the layout
1024     method will not be called layer.
1025 
1026     @param {SC.View} childView the view whose layout has changed.
1027     @returns {void}
1028   */
1029   layoutDidChangeFor: function (childView) {
1030     var set = this._needLayoutViews;
1031 
1032     // Track this view.
1033     if (!set) set = this._needLayoutViews = SC.CoreSet.create();
1034     set.add(childView);
1035   },
1036 
1037   /**
1038     Called your layout method if the view currently needs to layout some
1039     child views.
1040 
1041     @param {Boolean} force if true assume view is visible even if it is not.
1042     @returns {SC.View} receiver
1043     @test in layoutChildViews
1044   */
1045   layoutChildViewsIfNeeded: function (force) {
1046     if (this.get('childViewsNeedLayout')) {
1047       this.layoutChildViews(force);
1048 
1049       this.set('childViewsNeedLayout', NO);
1050     }
1051 
1052     return this;
1053   },
1054 
1055   /**
1056     Applies the current layout to the layer.  This method is usually only
1057     called once per runloop.  You can override this method to provide your
1058     own layout updating method if you want, though usually the better option
1059     is to override the layout method from the parent view.
1060 
1061     The default implementation of this method simply calls the updateLayout()
1062     method on the views that need layout.
1063 
1064     @param {Boolean} force Force the update to the layer's layout style immediately even if the view is not in a shown state.  Otherwise the style will be updated when the view returns to a shown state.
1065     @returns {void}
1066   */
1067   layoutChildViews: function (force) {
1068     var childViewLayout = this.childViewLayout,
1069       set, len, i;
1070 
1071     // Allow the child view layout plugin to layout all child views.
1072     if (childViewLayout) {
1073       // Adjust all other child views right now.
1074       // Note: this will add the affected child views to the set so they will be updated only once in this run loop
1075       childViewLayout.layoutChildViews(this);
1076     }
1077 
1078     // Retreive these values after they may have been updated by adjustments by
1079     // the childViewLayout plugin.
1080     set = this._needLayoutViews;
1081     if (set) {
1082       for (i = 0, len = set.length; i < len; ++i) {
1083         set[i].updateLayout(force);
1084       }
1085 
1086       set.clear(); // reset & reuse
1087     }
1088   },
1089 
1090   /**
1091     This method may be called on your view whenever the parent view resizes.
1092 
1093     The default version of this method will reset the frame and then call
1094     viewDidResize() if its size may have changed.  You will not usually override
1095     this method, but you may override the viewDidResize() method.
1096 
1097     @param {Frame} parentFrame the parent view's current frame.
1098     @returns {void}
1099     @test in viewDidResize
1100   */
1101   parentViewDidResize: function (parentFrame) {
1102     // Determine if our position may have changed.
1103     var positionMayHaveChanged = !this.get('isFixedPosition');
1104 
1105     // Figure out if our size may have changed.
1106     var isStatic = this.get('useStaticLayout'),
1107         // Figure out whether our height may have changed.
1108         parentHeight = parentFrame ? parentFrame.height : 0,
1109         parentHeightDidChange = parentHeight !== this._scv_parentHeight,
1110         isFixedHeight = this.get('isFixedHeight'),
1111         heightMayHaveChanged = isStatic || (parentHeightDidChange && !isFixedHeight),
1112         // Figure out whether our width may have changed.
1113         parentWidth = parentFrame ? parentFrame.width : 0,
1114         parentWidthDidChange = parentWidth !== this._scv_parentWidth,
1115         isFixedWidth = this.get('isFixedWidth'),
1116         widthMayHaveChanged = isStatic || (parentWidthDidChange && !isFixedWidth);
1117 
1118     // Update the cached parent frame.
1119     this._scv_parentHeight = parentHeight;
1120     this._scv_parentWidth = parentWidth;
1121 
1122     // If our height or width changed, our resulting frame change may impact our child views.
1123     if (heightMayHaveChanged || widthMayHaveChanged) {
1124       this.viewDidResize();
1125     }
1126     // If our size didn't change but our position did, our frame will change, but it won't impact our child
1127     // views' frames. (Note that the _sc_viewFrameDidChange call is made by viewDidResize above.)
1128     else if (positionMayHaveChanged) {
1129       this._sc_viewFrameDidChange();
1130     }
1131   },
1132 
1133   /**
1134     The 'frame' property depends on the 'layout' property as well as the
1135     parent view's frame.  In order to properly invalidate any cached values,
1136     we need to invalidate the cache whenever 'layout' changes.  However,
1137     observing 'layout' does not guarantee that; the observer might not be run
1138     before all other observers.
1139 
1140     In order to avoid any window of opportunity where the cached frame could
1141     be invalid, we need to force layoutDidChange() to immediately run
1142     whenever 'layout' is set.
1143   */
1144   propertyDidChange: function (key, value, _keepCache) {
1145     //@if(debug)
1146     // Debug mode only property validation.
1147     if (key === 'layout') {
1148       // If a layout value is accidentally set to NaN, this can result in infinite loops. Help the
1149       // developer out by failing early so that they can follow the stack trace to the problem.
1150       for (var property in value) {
1151         if (!value.hasOwnProperty(property)) { continue; }
1152 
1153         var layoutValue = value[property];
1154         if (isNaN(layoutValue) && (layoutValue !== SC.LAYOUT_AUTO) &&
1155             !SC._ROTATION_VALUE_REGEX.exec(layoutValue) && !SC._SCALE_VALUE_REGEX.exec(layoutValue)) {
1156           throw new Error("SC.View layout property set to invalid value, %@: %@.".fmt(property, layoutValue));
1157         }
1158       }
1159     }
1160     //@endif
1161 
1162     // To allow layout to be a computed property, we check if any property has
1163     // changed and if layout is dependent on the property.
1164     var layoutChange = false;
1165     if (typeof this.layout === "function" && this._kvo_dependents) {
1166       var dependents = this._kvo_dependents[key];
1167       if (dependents && dependents.indexOf('layout') !== -1) { layoutChange = true; }
1168     }
1169 
1170     // If the key is 'layout', we need to call layoutDidChange() immediately
1171     // so that if the frame has changed any cached values (for both this view
1172     // and any child views) can be appropriately invalidated.
1173     if (key === 'layout' || layoutChange) {
1174       this.layoutDidChange();
1175     }
1176 
1177     // Resume notification as usual.
1178     return sc_super();
1179   },
1180 
1181   /**
1182   */
1183   // propertyWillChange: function (key) {
1184   //   // To allow layout to be a computed property, we check if any property has
1185   //   // changed and if layout is dependent on the property.
1186   //   var layoutChange = false;
1187   //   if (typeof this.layout === "function" && this._kvo_dependents) {
1188   //     var dependents = this._kvo_dependents[key];
1189   //     if (dependents && dependents.indexOf('layout') !== -1) { layoutChange = true; }
1190   //   }
1191 
1192   //   if (key === 'layout' || layoutChange) {
1193   //     this._sc_previousLayout = this.get('layout');
1194   //   }
1195 
1196   //   return sc_super();
1197   // },
1198 
1199   /**
1200     Attempt to scroll the view to visible.  This will walk up the parent
1201     view hierarchy looking looking for a scrollable view.  It will then
1202     call scrollToVisible() on it.
1203 
1204     Returns YES if an actual scroll took place, no otherwise.
1205 
1206     @returns {Boolean}
1207   */
1208   scrollToVisible: function () {
1209     var pv = this.get('parentView');
1210     while (pv && !pv.get('isScrollable')) { pv = pv.get('parentView'); }
1211 
1212     // found view, first make it scroll itself visible then scroll this.
1213     if (pv) {
1214       pv.scrollToVisible();
1215       return pv.scrollToVisible(this);
1216     } else {
1217       return NO;
1218     }
1219   },
1220 
1221   /**
1222     This method is invoked on your view when the view resizes due to a layout
1223     change or potentially due to the parent view resizing (if your view’s size
1224     depends on the size of your parent view).  You can override this method
1225     to implement your own layout if you like, such as performing a grid
1226     layout.
1227 
1228     The default implementation simply notifies about the change to 'frame' and
1229     then calls parentViewDidResize on all of your children.
1230 
1231     @returns {void}
1232   */
1233   viewDidResize: function () {
1234     this._sc_viewFrameDidChange();
1235 
1236     // Also notify our children.
1237     var cv = this.childViews,
1238         frame = this.get('frame'),
1239         len, idx, view;
1240     for (idx = 0; idx < (len = cv.length); ++idx) {
1241       view = cv[idx];
1242       view.tryToPerform('parentViewDidResize', frame);
1243     }
1244   },
1245 
1246   // Implementation note: As a general rule, paired method calls, such as
1247   // beginLiveResize/endLiveResize that are called recursively on the tree
1248   // should reverse the order when doing the final half of the call. This
1249   // ensures that the calls are propertly nested for any cleanup routines.
1250   //
1251   // -> View A.beginXXX()
1252   //   -> View B.beginXXX()
1253   //     -> View C.beginXXX()
1254   //   -> View D.beginXXX()
1255   //
1256   // ...later on, endXXX methods are called in reverse order of beginXXX...
1257   //
1258   //   <- View D.endXXX()
1259   //     <- View C.endXXX()
1260   //   <- View B.endXXX()
1261   // <- View A.endXXX()
1262   //
1263   // See the two methods below for an example implementation.
1264 
1265   /**
1266     Call this method when you plan to begin a live resize.  This will
1267     notify the receiver view and any of its children that are interested
1268     that the resize is about to begin.
1269 
1270     @returns {SC.View} receiver
1271     @test in viewDidResize
1272   */
1273   beginLiveResize: function () {
1274     // call before children have been notified...
1275     if (this.willBeginLiveResize) this.willBeginLiveResize();
1276 
1277     // notify children in order
1278     var ary = this.get('childViews'), len = ary.length, idx, view;
1279     for (idx = 0; idx < len; ++idx) {
1280       view = ary[idx];
1281       if (view.beginLiveResize) view.beginLiveResize();
1282     }
1283     return this;
1284   },
1285 
1286   /**
1287     Call this method when you are finished with a live resize.  This will
1288     notify the receiver view and any of its children that are interested
1289     that the live resize has ended.
1290 
1291     @returns {SC.View} receiver
1292     @test in viewDidResize
1293   */
1294   endLiveResize: function () {
1295     // notify children in *reverse* order
1296     var ary = this.get('childViews'), len = ary.length, idx, view;
1297     for (idx = len - 1; idx >= 0; --idx) { // loop backwards
1298       view = ary[idx];
1299       if (view.endLiveResize) view.endLiveResize();
1300     }
1301 
1302     // call *after* all children have been notified...
1303     if (this.didEndLiveResize) this.didEndLiveResize();
1304     return this;
1305   },
1306 
1307   /**
1308     Invoked by the layoutChildViews method to update the layout on a
1309     particular view.  This method creates a render context and calls the
1310     renderLayout() method, which is probably what you want to override instead
1311     of this.
1312 
1313     You will not usually override this method, but you may call it if you
1314     implement layoutChildViews() in a view yourself.
1315 
1316     @param {Boolean} force Force the update to the layer's layout style immediately even if the view is not in a shown state.  Otherwise the style will be updated when the view returns to a shown state.
1317     @returns {SC.View} receiver
1318     @test in layoutChildViews
1319   */
1320   updateLayout: function (force) {
1321     this._doUpdateLayout(force);
1322 
1323     return this;
1324   },
1325 
1326   /**
1327     Default method called by the layout view to actually apply the current
1328     layout to the layer.  The default implementation simply assigns the
1329     current layoutStyle to the layer.  This method is also called whenever
1330     the layer is first created.
1331 
1332     @param {SC.RenderContext} the render context
1333     @returns {void}
1334     @test in layoutChildViews
1335   */
1336   renderLayout: function (context) {
1337     context.setStyle(this.get('layoutStyle'));
1338   },
1339 
1340   // ------------------------------------------------------------------------
1341   // Statechart
1342   //
1343 
1344   /** @private Update this view's layout action. */
1345   _doUpdateLayout: function (force) {
1346     var isRendered = this.get('_isRendered'),
1347       isVisibleInWindow = this.get('isVisibleInWindow'),
1348       handled = true;
1349 
1350     if (isRendered) {
1351       if (isVisibleInWindow || force) {
1352         // Only in the visible states do we allow updates without being forced.
1353         this._doUpdateLayoutStyle();
1354       } else {
1355         // Otherwise mark the view as needing an update when we enter a shown state again.
1356         this._layoutStyleNeedsUpdate = true;
1357       }
1358     } else {
1359       handled = false;
1360     }
1361 
1362     return handled;
1363   },
1364 
1365   /** @private */
1366   _doUpdateLayoutStyle: function () {
1367     var layer = this.get('layer'),
1368       layoutStyle = this.get('layoutStyle');
1369 
1370     for (var styleName in layoutStyle) {
1371       layer.style[styleName] = layoutStyle[styleName];
1372     }
1373 
1374     // Reset that an update is required.
1375     this._layoutStyleNeedsUpdate = false;
1376 
1377     // Notify updated.
1378     this._updatedLayout();
1379   },
1380 
1381   /** @private Override: Notify on attached (avoids notify of frame changed). */
1382   _notifyDidAttach: function () {
1383     // If we are using static layout then we don't know the frame until appended to the document.
1384     if (this.get('useStaticLayout')) {
1385       // We call viewDidResize so that it calls parentViewDidResize on all child views.
1386       this.viewDidResize();
1387     }
1388 
1389     // Notify.
1390     if (this.didAppendToDocument) { this.didAppendToDocument(); }
1391   },
1392 
1393   /** @private Override: The 'adopted' event (uses isFixedSize so our childViews are notified if our frame changes). */
1394   _adopted: function (beforeView) {
1395     // If our size depends on our parent, it will have changed on adoption.
1396     var isFixedSize = this.get('isFixedSize');
1397     if (isFixedSize) {
1398       // Even if our size is fixed, our frame may have changed (in particular if the anchor is not top/left)
1399       this._sc_viewFrameDidChange();
1400     } else {
1401       this.viewDidResize();
1402     }
1403 
1404     sc_super();
1405   },
1406 
1407   /** @private Extension: The 'orphaned' event (uses isFixedSize so our childViews are notified if our frame changes). */
1408   _orphaned: function () {
1409     sc_super();
1410 
1411     if (!this.isDestroyed) {
1412       // If our size depends on our parent, it will have changed on orphaning.
1413       var isFixedSize = this.get('isFixedSize');
1414       if (isFixedSize) {
1415       // Even if our size is fixed, our frame may have changed (in particular if the anchor is not top/left)
1416       this._sc_viewFrameDidChange();
1417       } else {
1418         this.viewDidResize();
1419       }
1420     }
1421   },
1422 
1423   /** @private Extension: The 'updatedContent' event. */
1424   _updatedContent: function () {
1425     sc_super();
1426 
1427     // If this view uses static layout, then notify that the frame (likely)
1428     // changed.
1429     if (this.get('useStaticLayout')) { this.viewDidResize(); }
1430   },
1431 
1432   /** @private The 'updatedLayout' event. */
1433   _updatedLayout: function () {
1434     // Notify.
1435     this.didRenderAnimations();
1436   }
1437 
1438 });
1439 
1440 SC.View.mixin(
1441   /** @scope SC.View */ {
1442 
1443   /**
1444     Convert any layout to a Top, Left, Width, Height layout
1445   */
1446   convertLayoutToAnchoredLayout: function (layout, parentFrame) {
1447     var ret = {top: 0, left: 0, width: parentFrame.width, height: parentFrame.height},
1448         pFW = parentFrame.width, pFH = parentFrame.height, //shortHand for parentDimensions
1449         lR = layout.right,
1450         lL = layout.left,
1451         lT = layout.top,
1452         lB = layout.bottom,
1453         lW = layout.width,
1454         lH = layout.height,
1455         lcX = layout.centerX,
1456         lcY = layout.centerY;
1457 
1458     // X Conversion
1459     // handle left aligned and left/right
1460     if (!SC.none(lL)) {
1461       if (SC.isPercentage(lL)) ret.left = lL * pFW;
1462       else ret.left = lL;
1463       if (lW !== undefined) {
1464         if (lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO;
1465         else if (SC.isPercentage(lW)) ret.width = lW * pFW;
1466         else ret.width = lW;
1467       } else {
1468         if (lR && SC.isPercentage(lR)) ret.width = pFW - ret.left - (lR * pFW);
1469         else ret.width = pFW - ret.left - (lR || 0);
1470       }
1471 
1472     // handle right aligned
1473     } else if (!SC.none(lR)) {
1474 
1475       // if no width, calculate it from the parent frame
1476       if (SC.none(lW)) {
1477         ret.left = 0;
1478         if (lR && SC.isPercentage(lR)) ret.width = pFW - (lR * pFW);
1479         else ret.width = pFW - (lR || 0);
1480 
1481       // If has width, calculate the left anchor from the width and right and parent frame
1482       } else {
1483         if (lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO;
1484         else {
1485           if (SC.isPercentage(lW)) ret.width = lW * pFW;
1486           else ret.width = lW;
1487           if (SC.isPercentage(lR)) ret.left = pFW - (ret.width + lR);
1488           else ret.left = pFW - (ret.width + lR);
1489         }
1490       }
1491 
1492     // handle centered
1493     } else if (!SC.none(lcX)) {
1494       if (lW && SC.isPercentage(lW)) ret.width = (lW * pFW);
1495       else ret.width = (lW || 0);
1496       ret.left = ((pFW - ret.width) / 2);
1497       if (SC.isPercentage(lcX)) ret.left = ret.left + lcX * pFW;
1498       else ret.left = ret.left + lcX;
1499 
1500     // if width defined, assume left of zero
1501     } else if (!SC.none(lW)) {
1502       ret.left =  0;
1503       if (lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO;
1504       else {
1505         if (SC.isPercentage(lW)) ret.width = lW * pFW;
1506         else ret.width = lW;
1507       }
1508 
1509     // fallback, full width.
1510     } else {
1511       ret.left = 0;
1512       ret.width = 0;
1513     }
1514 
1515     // handle min/max
1516     if (layout.minWidth !== undefined) ret.minWidth = layout.minWidth;
1517     if (layout.maxWidth !== undefined) ret.maxWidth = layout.maxWidth;
1518 
1519     // Y Conversion
1520     // handle left aligned and top/bottom
1521     if (!SC.none(lT)) {
1522       if (SC.isPercentage(lT)) ret.top = lT * pFH;
1523       else ret.top = lT;
1524       if (lH !== undefined) {
1525         if (lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO;
1526         else if (SC.isPercentage(lH)) ret.height = lH * pFH;
1527         else ret.height = lH;
1528       } else {
1529         ret.height = pFH - ret.top;
1530         if (lB && SC.isPercentage(lB)) ret.height = ret.height - (lB * pFH);
1531         else ret.height = ret.height - (lB || 0);
1532       }
1533 
1534     // handle bottom aligned
1535     } else if (!SC.none(lB)) {
1536 
1537       // if no height, calculate it from the parent frame
1538       if (SC.none(lH)) {
1539         ret.top = 0;
1540         if (lB && SC.isPercentage(lB)) ret.height = pFH - (lB * pFH);
1541         else ret.height = pFH - (lB || 0);
1542 
1543       // If has height, calculate the top anchor from the height and bottom and parent frame
1544       } else {
1545         if (lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO;
1546         else {
1547           if (SC.isPercentage(lH)) ret.height = lH * pFH;
1548           else ret.height = lH;
1549           ret.top = pFH - ret.height;
1550           if (SC.isPercentage(lB)) ret.top = ret.top - (lB * pFH);
1551           else ret.top = ret.top - lB;
1552         }
1553       }
1554 
1555     // handle centered
1556     } else if (!SC.none(lcY)) {
1557       if (lH && SC.isPercentage(lH)) ret.height = (lH * pFH);
1558       else ret.height = (lH || 0);
1559       ret.top = ((pFH - ret.height) / 2);
1560       if (SC.isPercentage(lcY)) ret.top = ret.top + lcY * pFH;
1561       else ret.top = ret.top + lcY;
1562 
1563     // if height defined, assume top of zero
1564     } else if (!SC.none(lH)) {
1565       ret.top =  0;
1566       if (lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO;
1567       else if (SC.isPercentage(lH)) ret.height = lH * pFH;
1568       else ret.height = lH;
1569 
1570     // fallback, full height.
1571     } else {
1572       ret.top = 0;
1573       ret.height = 0;
1574     }
1575 
1576     if (ret.top) ret.top = Math.floor(ret.top);
1577     if (ret.bottom) ret.bottom = Math.floor(ret.bottom);
1578     if (ret.left) ret.left = Math.floor(ret.left);
1579     if (ret.right) ret.right = Math.floor(ret.right);
1580     if (ret.width !== SC.LAYOUT_AUTO) ret.width = Math.floor(ret.width);
1581     if (ret.height !== SC.LAYOUT_AUTO) ret.height = Math.floor(ret.height);
1582 
1583     // handle min/max
1584     if (layout.minHeight !== undefined) ret.minHeight = layout.minHeight;
1585     if (layout.maxHeight !== undefined) ret.maxHeight = layout.maxHeight;
1586 
1587     return ret;
1588   },
1589 
1590   /**
1591     For now can only convert Top/Left/Width/Height to a Custom Layout
1592   */
1593   convertLayoutToCustomLayout: function (layout, layoutParams, parentFrame) {
1594     // TODO: [EG] Create Top/Left/Width/Height to a Custom Layout conversion
1595   }
1596 
1597 });
1598