1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /**
  9   @type String
 10   @constant
 11 */
 12 SC.SCALE_NONE = "none";
 13 
 14 /**
 15   Stretch/shrink the shape to fill the frame
 16 
 17   @type String
 18   @constant
 19 */
 20 SC.FILL = "fill";
 21 
 22 /**
 23   Stretch/shrink the shape to fill the frame while maintaining aspect ratio, such
 24   that the shortest dimension will just fit within the frame and the longest dimension will
 25   overflow and be cropped.
 26 
 27   @type String
 28   @constant
 29 */
 30 SC.FILL_PROPORTIONALLY = SC.BEST_FILL = "best-fill";
 31 
 32 /**
 33   Stretch/shrink the shape to fit the frame while maintaining aspect ratio, such that the
 34   longest dimension will just fit within the frame
 35 
 36   @type String
 37   @constant
 38 */
 39 SC.BEST_FIT = "best-fit";
 40 
 41 /**
 42   Shrink the shape to fit the frame while maintaining aspect ratio, such that
 43   the longest dimension will just fit within the frame.  Do not stretch the shape if the shape's
 44   width is less than the frame's width.
 45 
 46   @type String
 47   @constant
 48 */
 49 SC.BEST_FIT_DOWN_ONLY = "best-fit-down";
 50 
 51 /**
 52   @namespace
 53 
 54   InnerFrame provides the innerFrameForSize function, which will return a frame for the given size adjusted
 55   to fit within the given outer size, according to the align and scale properties.
 56 
 57   View's that render images will find this mixin particularly useful for fitting their images.
 58  */
 59 SC.InnerFrame = {
 60 
 61   /**
 62     Align the shape within its frame. Possible values:
 63 
 64       - SC.ALIGN_TOP_LEFT
 65       - SC.ALIGN_TOP
 66       - SC.ALIGN_TOP_RIGHT
 67       - SC.ALIGN_LEFT
 68       - SC.ALIGN_CENTER
 69       - SC.ALIGN_RIGHT
 70       - SC.ALIGN_BOTTOM_LEFT
 71       - SC.ALIGN_BOTTOM
 72       - SC.ALIGN_BOTTOM_RIGHT
 73 
 74     @type String
 75     @default SC.ALIGN_CENTER
 76   */
 77   align: SC.ALIGN_CENTER,
 78 
 79   /**
 80     Returns a frame (x, y, width, height) fitting the source size (sourceWidth & sourceHeight) within the
 81     destination size (destWidth & destHeight) according to the align and scale properties.  This is essential to
 82     positioning child views or elements within parent views or elements in elegant ways.
 83 
 84     @param {Number} sourceWidth
 85     @param {Number} sourceHeight
 86     @param {Number} destWidth
 87     @param {Number} destHeight
 88     @returns {Object} the inner frame with properties: {x: value, y: value, width: value, height: value }
 89   */
 90   innerFrameForSize: function(sourceWidth, sourceHeight, destWidth, destHeight) {
 91     var align = this.get('align'),
 92         scale = this.get('scale'),
 93         scaleX,
 94         scaleY,
 95         result;
 96 
 97     // Fast path
 98     result = { x: 0, y: 0, width: destWidth, height: destHeight };
 99     if (scale === SC.FILL) return result;
100 
101     // Determine the appropriate scale
102     scaleX = destWidth / sourceWidth;
103     scaleY = destHeight / sourceHeight;
104 
105     switch (scale) {
106       case SC.BEST_FILL:
107         scale = scaleX > scaleY ? scaleX : scaleY;
108         break;
109       case SC.BEST_FIT:
110         scale = scaleX < scaleY ? scaleX : scaleY;
111         break;
112       case SC.BEST_FIT_DOWN_ONLY:
113         if ((sourceWidth > destWidth) || (sourceHeight > destHeight)) {
114           scale = scaleX < scaleY ? scaleX : scaleY;
115         } else {
116           scale = 1.0;
117         }
118         break;
119       case SC.SCALE_NONE:
120         scale = 1.0;
121         break;
122       default: // Number
123         if (isNaN(window.parseFloat(scale)) || (window.parseFloat(scale) <= 0)) {
124           SC.Logger.warn("SC.InnerFrame: The scale '%@' was not understood.  Scale must be one of SC.FILL, SC.BEST_FILL, SC.BEST_FIT, SC.BEST_FIT_DOWN_ONLY or a positive number greater than 0.00.".fmt(scale));
125 
126           // Don't attempt to scale or offset the image
127           return result;
128         }
129     }
130 
131     sourceWidth *= scale;
132     sourceHeight *= scale;
133     result.width = Math.round(sourceWidth);
134     result.height = Math.round(sourceHeight);
135 
136     // Align the image within its frame
137     switch (align) {
138       case SC.ALIGN_LEFT:
139         result.x = 0;
140         result.y = (destHeight / 2) - (sourceHeight / 2);
141         break;
142       case SC.ALIGN_RIGHT:
143         result.x = destWidth - sourceWidth;
144         result.y = (destHeight / 2) - (sourceHeight / 2);
145         break;
146       case SC.ALIGN_TOP:
147         result.x = (destWidth / 2) - (sourceWidth / 2);
148         result.y = 0;
149         break;
150       case SC.ALIGN_BOTTOM:
151         result.x = (destWidth / 2) - (sourceWidth / 2);
152         result.y = destHeight - sourceHeight;
153         break;
154       case SC.ALIGN_TOP_LEFT:
155         result.x = 0;
156         result.y = 0;
157         break;
158       case SC.ALIGN_TOP_RIGHT:
159         result.x = destWidth - sourceWidth;
160         result.y = 0;
161         break;
162       case SC.ALIGN_BOTTOM_LEFT:
163         result.x = 0;
164         result.y = destHeight - sourceHeight;
165         break;
166       case SC.ALIGN_BOTTOM_RIGHT:
167         result.x = destWidth - sourceWidth;
168         result.y = destHeight - sourceHeight;
169         break;
170       default: // SC.ALIGN_CENTER || SC.ALIGN_MIDDLE
171         //@if(debug)
172         if (align !== SC.ALIGN_CENTER && align !== SC.ALIGN_MIDDLE) {
173           SC.Logger.warn("SC.InnerFrame: The align '%@' was not understood.  Align must be one of SC.ALIGN_CENTER/SC.ALIGN_MIDDLE, SC.ALIGN_LEFT, SC.ALIGN_RIGHT, SC.ALIGN_TOP, SC.ALIGN_BOTTOM, SC.ALIGN_TOP_LEFT, SC.ALIGN_TOP_RIGHT, SC.ALIGN_BOTTOM_LEFT or SC.ALIGN_BOTTOM_RIGHT.".fmt(align));
174         }
175         //@endif
176         result.x = (destWidth / 2) - (sourceWidth / 2);
177         result.y = (destHeight / 2) - (sourceHeight / 2);
178     }
179 
180     return result;
181   },
182 
183   /**
184     Determines how the shape will scale to fit within its containing space. Possible values:
185 
186       - SC.SCALE_NONE
187       - SC.FILL
188       - SC.BEST_FILL
189       - SC.BEST_FIT
190       - SC.BEST_FIT_DOWN_ONLY
191 
192     @type String
193     @default SC.FILL
194   */
195   scale: SC.FILL
196 };
197