Browse files

Switch to Closure Compiler, using advanced optimizations mode. Lots o…

…f source format tweaks to allow this while preventing things from breaking due to property renaming, object flattening, etc.
  • Loading branch information...
1 parent beaaae3 commit f83e0b19f382e17b0c850b987fce4b54cb177243 Jason Johnston committed Apr 17, 2010
View
14 build.xml
@@ -38,11 +38,23 @@
</target>
<target name="package-compressed" depends="package-uncompressed">
- <!--<copy file="${build_dir}/script_uncompressed.js" tofile="${build_dir}/script_compressed.js" overwrite="true" />-->
+ <exec executable="java">
+ <arg line='-jar tools/compiler.jar
+ --js ${build_dir}/script_uncompressed.js
+ --js ${src_dir}/closure_preservefunctions.js
+ --module "script_compressed:1"
+ --module "temp:1"
+ --module_output_path_prefix ${build_dir}/
+ --compilation_level ADVANCED_OPTIMIZATIONS
+ --externs ${src_dir}/closure_externs.js' />
+ </exec>
+ <delete file="${build_dir}/temp.js" />
+ <!--
<exec executable="yuicompressor">
<arg line="${build_dir}/script_uncompressed.js -o ${build_dir}/script_compressed.js" />
</exec>
+ -->
<concat destfile="${build_dir}/PIE.htc">
<fileset file="${src_dir}/htc_open.txt" />
View
52 sources/Angle.js
@@ -3,30 +3,34 @@
* @constructor
* @param {string} val The raw CSS value for the angle. It is assumed it has been pre-validated.
*/
-PIE.Angle = function( val ) {
- this.val = val;
-};
-PIE.Angle.prototype = {
- unitRE: /(deg|rad|grad|turn)$/,
+PIE.Angle = (function() {
+ function Angle( val ) {
+ this.val = val;
+ }
+ Angle.prototype = {
+ unitRE: /(deg|rad|grad|turn)$/,
- /**
- * @return {string} The unit of the angle value
- */
- getUnit: function() {
- return this._unit || ( this._unit = this.val.match( this.unitRE )[1] );
- },
+ /**
+ * @return {string} The unit of the angle value
+ */
+ getUnit: function() {
+ return this._unit || ( this._unit = this.val.match( this.unitRE )[1] );
+ },
- /**
- * Get the numeric value of the angle in degrees.
- * @return {number} The degrees value
- */
- degrees: function() {
- var deg = this._deg, u, n;
- if( !deg ) {
- u = this.getUnit();
- n = parseFloat( this.val, 10 );
- deg = this._deg = ( u === 'deg' ? n : u === 'rad' ? n / Math.PI * 180 : u === 'grad' ? n / 400 * 360 : u === 'turn' ? n * 360 : 0 );
+ /**
+ * Get the numeric value of the angle in degrees.
+ * @return {number} The degrees value
+ */
+ degrees: function() {
+ var deg = this._deg, u, n;
+ if( !deg ) {
+ u = this.getUnit();
+ n = parseFloat( this.val, 10 );
+ deg = this._deg = ( u === 'deg' ? n : u === 'rad' ? n / Math.PI * 180 : u === 'grad' ? n / 400 * 360 : u === 'turn' ? n * 360 : 0 );
+ }
+ return deg;
}
- return deg;
- }
-};
+ };
+
+ return Angle;
+})();
View
1,127 sources/BackgroundAndBorderRenderer.js
@@ -5,626 +5,625 @@
* @param {Object} styleInfos The StyleInfo objects
* @param {PIE.RootRenderer} parent
*/
-PIE.BackgroundAndBorderRenderer = function( el, styleInfos, parent ) {
- this.element = el;
- this.styleInfos = styleInfos;
- this.parent = parent;
-};
-PIE.Util.merge( PIE.BackgroundAndBorderRenderer.prototype, PIE.RendererBase, {
-
- zIndex: 200,
-
- needsUpdate: function() {
- var si = this.styleInfos;
- return si.border.changed() || si.background.changed();
- },
-
- isActive: function() {
- var si = this.styleInfos;
- return si.borderImage.isActive() ||
- si.borderRadius.isActive() ||
- si.background.isActive() ||
- ( si.boxShadow.isActive() && si.boxShadow.getProps().inset );
- },
-
- updateSize: function() {
- if( this.isActive() ) {
- this.draw();
- }
- },
-
- updateProps: function() {
- this.destroy();
- if( this.isActive() ) {
- this.draw();
- }
- },
-
- /**
- * Draw the shapes
- */
- draw: function() {
- this.drawBgColor();
- this.drawBgImages();
- this.drawBorder();
- },
-
- /**
- * Draw the background color shape
- */
- drawBgColor: function() {
- var props = this.styleInfos.background.getProps(),
- color = props && props.color && props.color.value(),
- cont, el, shape, w, h, s, alpha;
-
- if( color && color !== 'transparent' ) {
- this.hideBackground();
-
- cont = this.getBox();
- el = this.element;
- shape = this.getShape( 'bgColor', 'fill' );
- w = el.offsetWidth;
- h = el.offsetHeight;
- shape.stroked = false;
- shape.coordsize = w + ',' + h;
- shape.path = this.getBoxPath();
- s = shape.style;
- s.width = w;
- s.height = h;
- s.zIndex = 1;
- shape.fill.color = color;
-
- alpha = props.color.alpha();
- if( alpha < 1 ) {
- shape.fill.opacity = alpha;
+PIE.BackgroundAndBorderRenderer = (function() {
+ function BackgroundAndBorderRenderer( el, styleInfos, parent ) {
+ this.element = el;
+ this.styleInfos = styleInfos;
+ this.parent = parent;
+ }
+ PIE.Util.merge( BackgroundAndBorderRenderer.prototype, PIE.RendererBase, {
+
+ zIndex: 200,
+
+ needsUpdate: function() {
+ var si = this.styleInfos;
+ return si.border.changed() || si.background.changed();
+ },
+
+ isActive: function() {
+ var si = this.styleInfos;
+ return si.borderImage.isActive() ||
+ si.borderRadius.isActive() ||
+ si.background.isActive() ||
+ ( si.boxShadow.isActive() && si.boxShadow.getProps().inset );
+ },
+
+ updateSize: function() {
+ if( this.isActive() ) {
+ this.draw();
}
- } else {
- this.deleteShape( 'bgColor' );
- }
- },
-
- /**
- * Draw all the background image layers
- */
- drawBgImages: function() {
- var props = this.styleInfos.background.getProps(),
- images = props && props.images,
- img, cont, el, shape, w, h, s, i;
-
- if( images ) {
- this.hideBackground();
+ },
- el = this.element;
- w = el.offsetWidth,
- h = el.offsetHeight,
+ updateProps: function() {
+ this.destroy();
+ if( this.isActive() ) {
+ this.draw();
+ }
+ },
- i = images.length;
- while( i-- ) {
- img = images[i];
- shape = this.getShape( 'bgImage' + i, 'fill' );
+ /**
+ * Draw the shapes
+ */
+ draw: function() {
+ this.drawBgColor();
+ this.drawBgImages();
+ this.drawBorder();
+ },
+ /**
+ * Draw the background color shape
+ */
+ drawBgColor: function() {
+ var props = this.styleInfos.background.getProps(),
+ color = props && props.color && props.color.value(),
+ cont, el, shape, w, h, s, alpha;
+
+ if( color && color !== 'transparent' ) {
+ this.hideBackground();
+
+ cont = this.getBox();
+ el = this.element;
+ shape = this.getShape( 'bgColor', 'fill' );
+ w = el.offsetWidth;
+ h = el.offsetHeight;
shape.stroked = false;
- shape.fill.type = 'tile';
- shape.fillcolor = 'none';
shape.coordsize = w + ',' + h;
shape.path = this.getBoxPath();
s = shape.style;
s.width = w;
s.height = h;
- s.zIndex = 2;
+ s.zIndex = 1;
+ shape.fill.color = color;
- if( img.type === 'linear-gradient' ) {
- this.addLinearGradient( shape, img );
+ alpha = props.color.alpha();
+ if( alpha < 1 ) {
+ shape.fill.opacity = alpha;
}
- else {
- shape.fill.src = img.url;
- this.positionBgImage( shape, i );
+ } else {
+ this.deleteShape( 'bgColor' );
+ }
+ },
+
+ /**
+ * Draw all the background image layers
+ */
+ drawBgImages: function() {
+ var props = this.styleInfos.background.getProps(),
+ images = props && props.images,
+ img, cont, el, shape, w, h, s, i;
+
+ if( images ) {
+ this.hideBackground();
+
+ el = this.element;
+ w = el.offsetWidth,
+ h = el.offsetHeight,
+
+ i = images.length;
+ while( i-- ) {
+ img = images[i];
+ shape = this.getShape( 'bgImage' + i, 'fill' );
+
+ shape.stroked = false;
+ shape.fill.type = 'tile';
+ shape.fillcolor = 'none';
+ shape.coordsize = w + ',' + h;
+ shape.path = this.getBoxPath();
+ s = shape.style;
+ s.width = w;
+ s.height = h;
+ s.zIndex = 2;
+
+ if( img.type === 'linear-gradient' ) {
+ this.addLinearGradient( shape, img );
+ }
+ else {
+ shape.fill.src = img.url;
+ this.positionBgImage( shape, i );
+ }
}
}
- }
- // Delete any bgImage shapes previously created which weren't used above
- i = images ? images.length : 0;
- while( this.deleteShape( 'bgImage' + i++ ) ) {}
- },
+ // Delete any bgImage shapes previously created which weren't used above
+ i = images ? images.length : 0;
+ while( this.deleteShape( 'bgImage' + i++ ) ) {}
+ },
- /**
- * Set the position and clipping of the background image for a layer
- * @param {Element} shape
- * @param {number} index
- */
- positionBgImage: function( shape, index ) {
- PIE.Util.withImageSize( shape.fill.src, function( size ) {
- var fill = shape.fill,
- el = this.element,
- elW = el.offsetWidth,
- elH = el.offsetHeight,
- cs = el.currentStyle,
- si = this.styleInfos,
- border = si.border.getProps(),
- bw = border && border.widths,
- bwT = bw ? bw.t.pixels( el ) : 0,
- bwR = bw ? bw.r.pixels( el ) : 0,
- bwB = bw ? bw.b.pixels( el ) : 0,
- bwL = bw ? bw.l.pixels( el ) : 0,
- bg = si.background.getProps().images[ index ],
- bgPos = bg.position.coords( el, elW - size.w - bwL - bwR, elH - size.h - bwT - bwB ),
- repeat = bg.repeat,
- pxX, pxY,
- clipT = 0, clipR = elW, clipB = elH, clipL = 0;
-
- // Positioning - find the pixel offset from the top/left and convert to a ratio
- pxX = bgPos.x + bwL;
- pxY = bgPos.y + bwT;
- fill.position = ( pxX / elW ) + ',' + ( pxY / elH );
-
- // Repeating - clip the image shape
- if( repeat !== 'repeat' ) {
- if( repeat === 'repeat-x' || repeat === 'no-repeat' ) {
- clipT = pxY;
- clipB = pxY + size.h;
- }
- if( repeat === 'repeat-y' || repeat === 'no-repeat' ) {
- clipL = pxX;
- clipR = pxX + size.w;
+ /**
+ * Set the position and clipping of the background image for a layer
+ * @param {Element} shape
+ * @param {number} index
+ */
+ positionBgImage: function( shape, index ) {
+ PIE.Util.withImageSize( shape.fill.src, function( size ) {
+ var fill = shape.fill,
+ el = this.element,
+ elW = el.offsetWidth,
+ elH = el.offsetHeight,
+ cs = el.currentStyle,
+ si = this.styleInfos,
+ border = si.border.getProps(),
+ bw = border && border.widths,
+ bwT = bw ? bw['t'].pixels( el ) : 0,
+ bwR = bw ? bw['r'].pixels( el ) : 0,
+ bwB = bw ? bw['b'].pixels( el ) : 0,
+ bwL = bw ? bw['l'].pixels( el ) : 0,
+ bg = si.background.getProps().images[ index ],
+ bgPos = bg.position.coords( el, elW - size.w - bwL - bwR, elH - size.h - bwT - bwB ),
+ repeat = bg.repeat,
+ pxX, pxY,
+ clipT = 0, clipR = elW, clipB = elH, clipL = 0;
+
+ // Positioning - find the pixel offset from the top/left and convert to a ratio
+ pxX = bgPos.x + bwL;
+ pxY = bgPos.y + bwT;
+ fill.position = ( pxX / elW ) + ',' + ( pxY / elH );
+
+ // Repeating - clip the image shape
+ if( repeat !== 'repeat' ) {
+ if( repeat === 'repeat-x' || repeat === 'no-repeat' ) {
+ clipT = pxY;
+ clipB = pxY + size.h;
+ }
+ if( repeat === 'repeat-y' || repeat === 'no-repeat' ) {
+ clipL = pxX;
+ clipR = pxX + size.w;
+ }
+ shape.style.clip = 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)';
}
- shape.style.clip = 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)';
- }
- }, this );
- },
-
-
- /**
- * Draw the linear gradient for a gradient layer
- * @param {Element} shape
- * @param {Object} info The object holding the information about the gradient
- */
- addLinearGradient: function( shape, info ) {
- var el = this.element,
- w = el.offsetWidth,
- h = el.offsetHeight,
- fill = shape.fill,
- angle = info.angle,
- startPos = info.gradientStart,
- stops = info.stops,
- stopCount = stops.length,
- PI = Math.PI,
- startX, startY,
- endX, endY,
- startCornerX, startCornerY,
- endCornerX, endCornerY,
- vmlAngle, vmlGradientLength, vmlColors,
- deltaX, deltaY, lineLength,
- stopPx, vmlOffsetPct,
- p, i, j, before, after;
+ }, this );
+ },
+
/**
- * Find the point along a given line (defined by a starting point and an angle), at which
- * that line is intersected by a perpendicular line extending through another point.
- * @param x1 - x coord of the starting point
- * @param y1 - y coord of the starting point
- * @param angle - angle of the line extending from the starting point (in degrees)
- * @param x2 - x coord of point along the perpendicular line
- * @param y2 - y coord of point along the perpendicular line
- * @return [ x, y ]
+ * Draw the linear gradient for a gradient layer
+ * @param {Element} shape
+ * @param {Object} info The object holding the information about the gradient
*/
- function perpendicularIntersect( x1, y1, angle, x2, y2 ) {
- // Handle straight vertical and horizontal angles, for performance and to avoid
- // divide-by-zero errors.
- if( angle === 0 || angle === 180 ) {
- return [ x2, y1 ];
- }
- else if( angle === 90 || angle === 270 ) {
- return [ x1, y2 ];
+ addLinearGradient: function( shape, info ) {
+ var el = this.element,
+ w = el.offsetWidth,
+ h = el.offsetHeight,
+ fill = shape.fill,
+ angle = info.angle,
+ startPos = info.gradientStart,
+ stops = info.stops,
+ stopCount = stops.length,
+ PI = Math.PI,
+ startX, startY,
+ endX, endY,
+ startCornerX, startCornerY,
+ endCornerX, endCornerY,
+ vmlAngle, vmlGradientLength, vmlColors,
+ deltaX, deltaY, lineLength,
+ stopPx, vmlOffsetPct,
+ p, i, j, before, after;
+
+ /**
+ * Find the point along a given line (defined by a starting point and an angle), at which
+ * that line is intersected by a perpendicular line extending through another point.
+ * @param x1 - x coord of the starting point
+ * @param y1 - y coord of the starting point
+ * @param angle - angle of the line extending from the starting point (in degrees)
+ * @param x2 - x coord of point along the perpendicular line
+ * @param y2 - y coord of point along the perpendicular line
+ * @return [ x, y ]
+ */
+ function perpendicularIntersect( x1, y1, angle, x2, y2 ) {
+ // Handle straight vertical and horizontal angles, for performance and to avoid
+ // divide-by-zero errors.
+ if( angle === 0 || angle === 180 ) {
+ return [ x2, y1 ];
+ }
+ else if( angle === 90 || angle === 270 ) {
+ return [ x1, y2 ];
+ }
+ else {
+ // General approach: determine the Ax+By=C formula for each line (the slope of the second
+ // line is the negative inverse of the first) and then solve for where both formulas have
+ // the same x/y values.
+ var a1 = Math.tan( -angle * PI / 180 ),
+ c1 = a1 * x1 - y1,
+ a2 = -1 / a1,
+ c2 = a2 * x2 - y2,
+ d = a2 - a1,
+ endX = ( c2 - c1 ) / d,
+ endY = ( a1 * c2 - a2 * c1 ) / d;
+ return [ endX, endY ];
+ }
}
- else {
- // General approach: determine the Ax+By=C formula for each line (the slope of the second
- // line is the negative inverse of the first) and then solve for where both formulas have
- // the same x/y values.
- var a1 = Math.tan( -angle * PI / 180 ),
- c1 = a1 * x1 - y1,
- a2 = -1 / a1,
- c2 = a2 * x2 - y2,
- d = a2 - a1,
- endX = ( c2 - c1 ) / d,
- endY = ( a1 * c2 - a2 * c1 ) / d;
- return [ endX, endY ];
+
+ // Find the "start" and "end" corners; these are the corners furthest along the gradient line.
+ // This is used below to find the start/end positions of the CSS3 gradient-line, and also in finding
+ // the total length of the VML rendered gradient-line corner to corner.
+ function findCorners() {
+ startCornerX = ( angle >= 90 && angle < 270 ) ? w : 0;
+ startCornerY = angle < 180 ? h : 0;
+ endCornerX = w - startCornerX;
+ endCornerY = h - startCornerY;
}
- }
- // Find the "start" and "end" corners; these are the corners furthest along the gradient line.
- // This is used below to find the start/end positions of the CSS3 gradient-line, and also in finding
- // the total length of the VML rendered gradient-line corner to corner.
- function findCorners() {
- startCornerX = ( angle >= 90 && angle < 270 ) ? w : 0;
- startCornerY = angle < 180 ? h : 0;
- endCornerX = w - startCornerX;
- endCornerY = h - startCornerY;
- }
+ // Normalize the angle to a value between [0, 360)
+ function normalizeAngle() {
+ if( angle < 0 ) {
+ angle += 360;
+ }
+ angle = angle % 360;
+ }
- // Normalize the angle to a value between [0, 360)
- function normalizeAngle() {
- if( angle < 0 ) {
- angle += 360;
+ // Find the distance between two points
+ function distance( p1, p2 ) {
+ var dx = p2[0] - p1[0],
+ dy = p2[1] - p1[1];
+ return Math.abs(
+ dx === 0 ? dy :
+ dy === 0 ? dx :
+ Math.sqrt( dx * dx + dy * dy )
+ );
}
- angle = angle % 360;
- }
- // Find the distance between two points
- function distance( p1, p2 ) {
- var dx = p2[0] - p1[0],
- dy = p2[1] - p1[1];
- return Math.abs(
- dx === 0 ? dy :
- dy === 0 ? dx :
- Math.sqrt( dx * dx + dy * dy )
- );
- }
+ // Find the start and end points of the gradient
+ if( startPos ) {
+ startPos = startPos.coords( el, w, h );
+ startX = startPos.x;
+ startY = startPos.y;
+ }
+ if( angle ) {
+ angle = angle.degrees();
- // Find the start and end points of the gradient
- if( startPos ) {
- startPos = startPos.coords( el, w, h );
- startX = startPos.x;
- startY = startPos.y;
- }
- if( angle ) {
- angle = angle.degrees();
+ normalizeAngle();
+ findCorners();
- normalizeAngle();
- findCorners();
+ // If no start position was specified, then choose a corner as the starting point.
+ if( !startPos ) {
+ startX = startCornerX;
+ startY = startCornerY;
+ }
- // If no start position was specified, then choose a corner as the starting point.
- if( !startPos ) {
- startX = startCornerX;
- startY = startCornerY;
+ // Find the end position by extending a perpendicular line from the gradient-line which
+ // intersects the corner opposite from the starting corner.
+ p = perpendicularIntersect( startX, startY, angle, endCornerX, endCornerY );
+ endX = p[0];
+ endY = p[1];
}
+ else if( startPos ) {
+ // Start position but no angle specified: find the end point by rotating 180deg around the center
+ endX = w - startX;
+ endY = h - startY;
+ }
+ else {
+ // Neither position nor angle specified; create vertical gradient from top to bottom
+ startX = startY = endX = 0;
+ endY = h;
+ }
+ deltaX = endX - startX;
+ deltaY = endY - startY;
- // Find the end position by extending a perpendicular line from the gradient-line which
- // intersects the corner opposite from the starting corner.
- p = perpendicularIntersect( startX, startY, angle, endCornerX, endCornerY );
- endX = p[0];
- endY = p[1];
- }
- else if( startPos ) {
- // Start position but no angle specified: find the end point by rotating 180deg around the center
- endX = w - startX;
- endY = h - startY;
- }
- else {
- // Neither position nor angle specified; create vertical gradient from top to bottom
- startX = startY = endX = 0;
- endY = h;
- }
- deltaX = endX - startX;
- deltaY = endY - startY;
+ if( angle === undefined ) {
+ angle = -Math.atan2( deltaY, deltaX ) / PI * 180;
+ normalizeAngle();
+ findCorners();
+ }
- if( angle === undefined ) {
- angle = -Math.atan2( deltaY, deltaX ) / PI * 180;
- normalizeAngle();
- findCorners();
- }
+ // In VML land, the angle of the rendered gradient depends on the aspect ratio of the shape's
+ // bounding box; for example specifying a 45 deg angle actually results in a gradient
+ // drawn diagonally from one corner to its opposite corner, which will only appear to the
+ // viewer as 45 degrees if the shape is equilateral. We adjust for this by taking the x/y deltas
+ // between the start and end points, multiply one of them by the shape's aspect ratio,
+ // and get their arctangent, resulting in an appropriate VML angle.
+ vmlAngle = Math.atan2( deltaX * w / h, deltaY ) / PI * 180;
+
+ // VML angles are 180 degrees offset from CSS angles
+ vmlAngle += 180;
+ vmlAngle = vmlAngle % 360;
+
+ // Add all the stops to the VML 'colors' list, including the first and last stops.
+ // For each, we find its pixel offset along the gradient-line; if the offset of a stop is less
+ // than that of its predecessor we increase it to be equal. We then map that pixel offset to a
+ // percentage along the VML gradient-line, which runs from shape corner to corner.
+ lineLength = distance( [ startX, startY ], [ endX, endY ] );
+ vmlGradientLength = distance( [ startCornerX, startCornerY ], perpendicularIntersect( startCornerX, startCornerY, angle, endCornerX, endCornerY ) );
+ vmlColors = [];
+ vmlOffsetPct = distance( [ startX, startY ], perpendicularIntersect( startX, startY, angle, startCornerX, startCornerY ) ) / vmlGradientLength * 100;
+
+ // Find the pixel offsets along the CSS3 gradient-line for each stop.
+ stopPx = [];
+ for( i = 0; i < stopCount; i++ ) {
+ stopPx.push( stops[i].offset ? stops[i].offset.pixels( el, lineLength ) :
+ i === 0 ? 0 : i === stopCount - 1 ? lineLength : null );
+ }
+ // Fill in gaps with evenly-spaced offsets
+ for( i = 1; i < stopCount; i++ ) {
+ if( stopPx[ i ] === null ) {
+ before = stopPx[ i - 1 ];
+ j = i;
+ do {
+ after = stopPx[ ++j ];
+ } while( after === null );
+ stopPx[ i ] = before + ( after - before ) / ( j - i + 1 );
+ }
+ // Make sure each stop's offset is no less than the one before it
+ stopPx[ i ] = Math.max( stopPx[ i ], stopPx[ i - 1 ] );
+ }
- // In VML land, the angle of the rendered gradient depends on the aspect ratio of the shape's
- // bounding box; for example specifying a 45 deg angle actually results in a gradient
- // drawn diagonally from one corner to its opposite corner, which will only appear to the
- // viewer as 45 degrees if the shape is equilateral. We adjust for this by taking the x/y deltas
- // between the start and end points, multiply one of them by the shape's aspect ratio,
- // and get their arctangent, resulting in an appropriate VML angle.
- vmlAngle = Math.atan2( deltaX * w / h, deltaY ) / PI * 180;
-
- // VML angles are 180 degrees offset from CSS angles
- vmlAngle += 180;
- vmlAngle = vmlAngle % 360;
-
- // Add all the stops to the VML 'colors' list, including the first and last stops.
- // For each, we find its pixel offset along the gradient-line; if the offset of a stop is less
- // than that of its predecessor we increase it to be equal. We then map that pixel offset to a
- // percentage along the VML gradient-line, which runs from shape corner to corner.
- lineLength = distance( [ startX, startY ], [ endX, endY ] );
- vmlGradientLength = distance( [ startCornerX, startCornerY ], perpendicularIntersect( startCornerX, startCornerY, angle, endCornerX, endCornerY ) );
- vmlColors = [];
- vmlOffsetPct = distance( [ startX, startY ], perpendicularIntersect( startX, startY, angle, startCornerX, startCornerY ) ) / vmlGradientLength * 100;
-
- // Find the pixel offsets along the CSS3 gradient-line for each stop.
- stopPx = [];
- for( i = 0; i < stopCount; i++ ) {
- stopPx.push( stops[i].offset ? stops[i].offset.pixels( el, lineLength ) :
- i === 0 ? 0 : i === stopCount - 1 ? lineLength : null );
- }
- // Fill in gaps with evenly-spaced offsets
- for( i = 1; i < stopCount; i++ ) {
- if( stopPx[ i ] === null ) {
- before = stopPx[ i - 1 ];
- j = i;
- do {
- after = stopPx[ ++j ];
- } while( after === null );
- stopPx[ i ] = before + ( after - before ) / ( j - i + 1 );
+ // Convert to percentage along the VML gradient line and add to the VML 'colors' value
+ for( i = 0; i < stopCount; i++ ) {
+ vmlColors.push(
+ ( vmlOffsetPct + ( stopPx[ i ] / vmlGradientLength * 100 ) ) + '% ' + stops[i].color.value()
+ );
}
- // Make sure each stop's offset is no less than the one before it
- stopPx[ i ] = Math.max( stopPx[ i ], stopPx[ i - 1 ] );
- }
- // Convert to percentage along the VML gradient line and add to the VML 'colors' value
- for( i = 0; i < stopCount; i++ ) {
- vmlColors.push(
- ( vmlOffsetPct + ( stopPx[ i ] / vmlGradientLength * 100 ) ) + '% ' + stops[i].color.value()
- );
- }
+ // Now, finally, we're ready to render the gradient fill. Set the start and end colors to
+ // the first and last stop colors; this just sets outer bounds for the gradient.
+ fill['angle'] = vmlAngle;
+ fill['type'] = 'gradient';
+ fill['method'] = 'sigma';
+ fill['color'] = stops[0].color.value();
+ fill['color2'] = stops[stopCount - 1].color.value();
+ fill['colors'].value = vmlColors.join( ',' );
+ },
- // Now, finally, we're ready to render the gradient fill. Set the start and end colors to
- // the first and last stop colors; this just sets outer bounds for the gradient.
- fill.angle = vmlAngle;
- fill.type = 'gradient';
- fill.method = 'sigma';
- fill.color = stops[0].color.value();
- fill.color2 = stops[stopCount - 1].color.value();
- fill.colors.value = vmlColors.join( ',' );
- },
-
-
- /**
- * Draw the border shape(s)
- */
- drawBorder: function() {
- var cont = this.getBox(),
- el = this.element,
- cs = el.currentStyle,
- w = el.offsetWidth,
- h = el.offsetHeight,
- props = this.styleInfos.border.getProps(),
- styles, colors, widths,
- side, shape, stroke, bColor, bWidth, bStyle, s;
-
- if( props ) {
- styles = props.styles;
- colors = props.colors;
- widths = props.widths;
-
- this.hideBorder();
-
- var segments = this.getBorderSegments();
- for( var i=0; i<segments.length; i++) {
- var seg = segments[i];
- shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill' );
- shape.coordsize = w + ',' + h;
- shape.path = seg.path;
- s = shape.style;
- s.width = w;
- s.height = h;
- s.zIndex = 3;
-
- shape.filled = !!seg.fill;
- shape.stroked = !!seg.stroke;
- if( seg.stroke ) {
- stroke = shape.stroke;
- stroke.weight = seg.weight + 'px';
- stroke.color = seg.color.value();
- stroke.dashstyle = seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid';
- stroke.linestyle = seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single';
- } else {
- shape.fill.color = seg.fill.value();
+
+ /**
+ * Draw the border shape(s)
+ */
+ drawBorder: function() {
+ var cont = this.getBox(),
+ el = this.element,
+ cs = el.currentStyle,
+ w = el.offsetWidth,
+ h = el.offsetHeight,
+ props = this.styleInfos.border.getProps(),
+ side, shape, stroke, bColor, bWidth, bStyle, s;
+
+ if( props ) {
+ this.hideBorder();
+
+ var segments = this.getBorderSegments();
+ for( var i=0; i<segments.length; i++) {
+ var seg = segments[i];
+ shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill' );
+ shape.coordsize = w + ',' + h;
+ shape.path = seg.path;
+ s = shape.style;
+ s.width = w;
+ s.height = h;
+ s.zIndex = 3;
+
+ shape.filled = !!seg.fill;
+ shape.stroked = !!seg.stroke;
+ if( seg.stroke ) {
+ stroke = shape.stroke;
+ stroke['weight'] = seg.weight + 'px';
+ stroke.color = seg.color.value();
+ stroke['dashstyle'] = seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid';
+ stroke['linestyle'] = seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single';
+ } else {
+ shape.fill.color = seg.fill.value();
+ }
}
+
+ // remove any previously-created border shapes which didn't get used above
+ while( this.deleteShape( 'borderPiece' + i++ ) ) {}
}
+ },
- // remove any previously-created border shapes which didn't get used above
- while( this.deleteShape( 'borderPiece' + i++ ) ) {}
- }
- },
-
- /**
- * Hide the actual background image and color of the element.
- */
- hideBackground: function() {
- var rs = this.element.runtimeStyle;
- rs.backgroundImage = 'none';
- rs.backgroundColor = 'transparent';
- },
-
- /**
- * Hide the actual border of the element. In IE7 and up we can just set its color to transparent;
- * however IE6 does not support transparent borders so we have to get tricky with it.
- */
- hideBorder: function() {
- var el = this.element;
- if( PIE.isIE6 ) {
- // Wrap all the element's children in a custom element, set the element to visiblity:hidden,
- // and set the wrapper element to visiblity:visible. This hides the outer element's decorations
- // (background and border) but displays all the contents.
- // TODO find a better way to do this that doesn't mess up the DOM parent-child relationship,
- // as this can interfere with other author scripts which add/modify/delete children. Also, this
- // won't work for elements which cannot take children, e.g. input/button/textarea/img/etc. Look into
- // using a compositor filter or some other filter which masks the border.
- if( el.childNodes.length !== 1 || el.firstChild.tagName !== 'ie6-mask' ) {
- var cont = this.element.document.createElement( 'ie6-mask' );
- cont.style.visibility = 'visible';
- cont.style.zoom = 1;
- while( el.firstChild ) {
- cont.appendChild( el.firstChild );
+ /**
+ * Hide the actual background image and color of the element.
+ */
+ hideBackground: function() {
+ var rs = this.element.runtimeStyle;
+ rs.backgroundImage = 'none';
+ rs.backgroundColor = 'transparent';
+ },
+
+ /**
+ * Hide the actual border of the element. In IE7 and up we can just set its color to transparent;
+ * however IE6 does not support transparent borders so we have to get tricky with it.
+ */
+ hideBorder: function() {
+ var el = this.element;
+ if( PIE.isIE6 ) {
+ // Wrap all the element's children in a custom element, set the element to visiblity:hidden,
+ // and set the wrapper element to visiblity:visible. This hides the outer element's decorations
+ // (background and border) but displays all the contents.
+ // TODO find a better way to do this that doesn't mess up the DOM parent-child relationship,
+ // as this can interfere with other author scripts which add/modify/delete children. Also, this
+ // won't work for elements which cannot take children, e.g. input/button/textarea/img/etc. Look into
+ // using a compositor filter or some other filter which masks the border.
+ if( el.childNodes.length !== 1 || el.firstChild.tagName !== 'ie6-mask' ) {
+ var cont = this.element.document.createElement( 'ie6-mask' );
+ cont.style.visibility = 'visible';
+ cont.style.zoom = 1;
+ while( el.firstChild ) {
+ cont.appendChild( el.firstChild );
+ }
+ el.appendChild( cont );
+ el.runtimeStyle.visibility = 'hidden';
}
- el.appendChild( cont );
- el.runtimeStyle.visibility = 'hidden';
+ } else {
+ el.runtimeStyle.borderColor = 'transparent';
}
- } else {
- el.runtimeStyle.borderColor = 'transparent';
- }
- },
-
-
- /**
- * Get the VML path definitions for the border segment(s).
- * @return {Array<{string}>}
- */
- getBorderSegments: function() {
- var el = this.element,
- elW, elH,
- borderInfo = this.styleInfos.border,
- segments = [],
- floor, ceil, wT, wR, wB, wL,
- borderProps, radiusInfo, radii, widths, styles, colors;
-
- if( borderInfo.isActive() ) {
- borderProps = borderInfo.getProps();
-
- widths = borderProps.widths;
- styles = borderProps.styles;
- colors = borderProps.colors;
-
- if( borderProps.widthsSame && borderProps.stylesSame && borderProps.colorsSame ) {
- // shortcut for identical border on all sides - only need 1 stroked shape
- wT = widths.t.pixels( el );
- wR = Math.floor( wT / 2 );
- segments.push( {
- path: this.getBoxPath( { t: wR, r: wR, b: wR, l: wR } ),
- stroke: styles.t,
- color: colors.t,
- weight: wT
- } );
- }
- else {
- elW = el.offsetWidth - 1;
- elH = el.offsetHeight - 1;
-
- wT = widths.t.pixels( el );
- wR = widths.r.pixels( el );
- wB = widths.b.pixels( el );
- wL = widths.l.pixels( el );
- var pxWidths = {
- t: wT,
- r: wR,
- b: wB,
- l: wL
- };
-
- radiusInfo = this.styleInfos.borderRadius;
- if( radiusInfo.isActive() ) {
- radii = this.getRadiiPixels( radiusInfo.getProps() );
- }
+ },
- floor = Math.floor;
- ceil = Math.ceil;
-
- function curve( corner, shrinkX, shrinkY, startAngle, ccw ) {
- var rx = radii.x[ corner ],
- ry = radii.y[ corner ],
- deg = 65535,
- isRight = corner.charAt( 1 ) === 'r',
- isBottom = corner.charAt( 0 ) === 'b';
- return ( rx > 0 && ry > 0 ) ?
- ( isRight ? ceil( elW - rx ) : floor( rx ) ) + ',' + // center x
- ( isBottom ? ceil( elH - ry ) : floor( ry ) ) + ',' + // center y
- ( floor( rx ) - shrinkX ) + ',' + // width
- ( floor( ry ) - shrinkY ) + ',' + // height
- ( startAngle * deg ) + ',' + // start angle
- ( 45 * deg * ( ccw ? 1 : -1 ) ) // angle change
- : '';
+
+ /**
+ * Get the VML path definitions for the border segment(s).
+ * @return {Array<{string}>}
+ */
+ getBorderSegments: function() {
+ var el = this.element,
+ elW, elH,
+ borderInfo = this.styleInfos.border,
+ segments = [],
+ floor, ceil, wT, wR, wB, wL,
+ borderProps, radiusInfo, radii, widths, styles, colors;
+
+ if( borderInfo.isActive() ) {
+ borderProps = borderInfo.getProps();
+
+ widths = borderProps.widths;
+ styles = borderProps.styles;
+ colors = borderProps.colors;
+
+ if( borderProps.widthsSame && borderProps.stylesSame && borderProps.colorsSame ) {
+ // shortcut for identical border on all sides - only need 1 stroked shape
+ wT = widths['t'].pixels( el );
+ wR = Math.floor( wT / 2 );
+ segments.push( {
+ path: this.getBoxPath( { t: wR, r: wR, b: wR, l: wR } ),
+ stroke: styles['t'],
+ color: colors['t'],
+ weight: wT
+ } );
}
+ else {
+ elW = el.offsetWidth - 1;
+ elH = el.offsetHeight - 1;
+
+ wT = widths['t'].pixels( el );
+ wR = widths['r'].pixels( el );
+ wB = widths['b'].pixels( el );
+ wL = widths['l'].pixels( el );
+ var pxWidths = {
+ 't': wT,
+ 'r': wR,
+ 'b': wB,
+ 'l': wL
+ };
+
+ radiusInfo = this.styleInfos.borderRadius;
+ if( radiusInfo.isActive() ) {
+ radii = this.getRadiiPixels( radiusInfo.getProps() );
+ }
+
+ floor = Math.floor;
+ ceil = Math.ceil;
+
+ function curve( corner, shrinkX, shrinkY, startAngle, ccw ) {
+ var rx = radii.x[ corner ],
+ ry = radii.y[ corner ],
+ deg = 65535,
+ isRight = corner.charAt( 1 ) === 'r',
+ isBottom = corner.charAt( 0 ) === 'b';
+ return ( rx > 0 && ry > 0 ) ?
+ ( isRight ? ceil( elW - rx ) : floor( rx ) ) + ',' + // center x
+ ( isBottom ? ceil( elH - ry ) : floor( ry ) ) + ',' + // center y
+ ( floor( rx ) - shrinkX ) + ',' + // width
+ ( floor( ry ) - shrinkY ) + ',' + // height
+ ( startAngle * deg ) + ',' + // start angle
+ ( 45 * deg * ( ccw ? 1 : -1 ) ) // angle change
+ : '';
+ }
- function addSide( side, sideBefore, sideAfter, cornerBefore, cornerAfter, baseAngle ) {
- var vert = side === 'l' || side === 'r',
- beforeX, beforeY, afterX, afterY;
-
- if( pxWidths[ side ] > 0 && styles[ side ] !== 'none' ) {
- beforeX = pxWidths[ vert ? side : sideBefore ];
- beforeY = pxWidths[ vert ? sideBefore : side ];
- afterX = pxWidths[ vert ? side : sideAfter ];
- afterY = pxWidths[ vert ? sideAfter : side ];
-
- if( styles[ side ] === 'dashed' || styles[ side ] === 'dotted' ) {
- segments.push( {
- path: 'al' + curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0 ) +
- 'ae' + curve( cornerBefore, 0, 0, baseAngle, 1 ),
- fill: colors[ side ]
- } );
- segments.push( {
- path: (
- side === 't' ?
- 'm' + floor( radii.x.tl ) + ',' + ceil( wT/2 ) +
- 'l' + ceil( elW - radii.x.tr ) + ',' + ceil( wT/2 ) :
- side === 'r' ?
- 'm' + ceil( elW - wR/2 ) + ',' + floor( radii.y.tr ) +
- 'l' + ceil( elW - wR/2 ) + ',' + ceil( elH - radii.y.br ) :
- side === 'b' ?
- 'm' + ceil( elW - radii.x.br ) + ',' + floor( elH - wB/2 ) +
- 'l' + floor( radii.x.bl ) + ',' + floor( elH - wB/2 ) :
- // side === 'l'
- 'm' + floor( wL/2 ) + ',' + ceil( elH - radii.y.bl ) +
- 'l' + floor( wL/2 ) + ',' + floor( radii.y.tl )
- ),
- stroke: styles[ side ],
- weight: pxWidths[ side ],
- color: colors[ side ]
- } );
- segments.push( {
- path: 'al' + curve( cornerAfter, afterX, afterY, baseAngle, 0 ) +
- 'ae' + curve( cornerAfter, 0, 0, baseAngle - 45, 1 ),
- fill: colors[ side ]
- } );
- }
- else {
- segments.push( {
- path: 'al' + curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0 ) +
- 'ae' + curve( cornerAfter, afterX, afterY, baseAngle, 0 ) +
-
- ( styles[ side ] === 'double' && pxWidths[ side ] > 2 ?
- 'ae' + curve( cornerAfter, afterX - floor( afterX / 3 ), afterY - floor( afterY / 3 ), baseAngle - 45, 1 ) +
- 'ae' + curve( cornerBefore, beforeX - floor( beforeX / 3 ), beforeY - floor( beforeY / 3 ), baseAngle, 1 ) +
- 'x al' + curve( cornerBefore, floor( beforeX / 3 ), floor( beforeY / 3 ), baseAngle + 45, 0 ) +
- 'ae' + curve( cornerAfter, floor( afterX / 3 ), floor( afterY / 3 ), baseAngle, 0 )
- : '' ) +
-
- 'ae' + curve( cornerAfter, 0, 0, baseAngle - 45, 1 ) +
- 'ae' + curve( cornerBefore, 0, 0, baseAngle, 1 ),
- fill: colors[ side ]
- } );
+ function addSide( side, sideBefore, sideAfter, cornerBefore, cornerAfter, baseAngle ) {
+ var vert = side === 'l' || side === 'r',
+ beforeX, beforeY, afterX, afterY;
+
+ if( pxWidths[ side ] > 0 && styles[ side ] !== 'none' ) {
+ beforeX = pxWidths[ vert ? side : sideBefore ];
+ beforeY = pxWidths[ vert ? sideBefore : side ];
+ afterX = pxWidths[ vert ? side : sideAfter ];
+ afterY = pxWidths[ vert ? sideAfter : side ];
+
+ if( styles[ side ] === 'dashed' || styles[ side ] === 'dotted' ) {
+ segments.push( {
+ path: 'al' + curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0 ) +
+ 'ae' + curve( cornerBefore, 0, 0, baseAngle, 1 ),
+ fill: colors[ side ]
+ } );
+ segments.push( {
+ path: (
+ side === 't' ?
+ 'm' + floor( radii.x['tl'] ) + ',' + ceil( wT/2 ) +
+ 'l' + ceil( elW - radii.x['tr'] ) + ',' + ceil( wT/2 ) :
+ side === 'r' ?
+ 'm' + ceil( elW - wR/2 ) + ',' + floor( radii.y['tr'] ) +
+ 'l' + ceil( elW - wR/2 ) + ',' + ceil( elH - radii.y['br'] ) :
+ side === 'b' ?
+ 'm' + ceil( elW - radii.x['br'] ) + ',' + floor( elH - wB/2 ) +
+ 'l' + floor( radii.x['bl'] ) + ',' + floor( elH - wB/2 ) :
+ // side === 'l'
+ 'm' + floor( wL/2 ) + ',' + ceil( elH - radii.y['bl'] ) +
+ 'l' + floor( wL/2 ) + ',' + floor( radii.y['tl'] )
+ ),
+ stroke: styles[ side ],
+ weight: pxWidths[ side ],
+ color: colors[ side ]
+ } );
+ segments.push( {
+ path: 'al' + curve( cornerAfter, afterX, afterY, baseAngle, 0 ) +
+ 'ae' + curve( cornerAfter, 0, 0, baseAngle - 45, 1 ),
+ fill: colors[ side ]
+ } );
+ }
+ else {
+ segments.push( {
+ path: 'al' + curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0 ) +
+ 'ae' + curve( cornerAfter, afterX, afterY, baseAngle, 0 ) +
+
+ ( styles[ side ] === 'double' && pxWidths[ side ] > 2 ?
+ 'ae' + curve( cornerAfter, afterX - floor( afterX / 3 ), afterY - floor( afterY / 3 ), baseAngle - 45, 1 ) +
+ 'ae' + curve( cornerBefore, beforeX - floor( beforeX / 3 ), beforeY - floor( beforeY / 3 ), baseAngle, 1 ) +
+ 'x al' + curve( cornerBefore, floor( beforeX / 3 ), floor( beforeY / 3 ), baseAngle + 45, 0 ) +
+ 'ae' + curve( cornerAfter, floor( afterX / 3 ), floor( afterY / 3 ), baseAngle, 0 )
+ : '' ) +
+
+ 'ae' + curve( cornerAfter, 0, 0, baseAngle - 45, 1 ) +
+ 'ae' + curve( cornerBefore, 0, 0, baseAngle, 1 ),
+ fill: colors[ side ]
+ } );
+ }
}
}
- }
- addSide( 't', 'l', 'r', 'tl', 'tr', 90 );
- addSide( 'r', 't', 'b', 'tr', 'br', 0 );
- addSide( 'b', 'r', 'l', 'br', 'bl', -90 );
- addSide( 'l', 'b', 't', 'bl', 'tl', -180 );
+ addSide( 't', 'l', 'r', 'tl', 'tr', 90 );
+ addSide( 'r', 't', 'b', 'tr', 'br', 0 );
+ addSide( 'b', 'r', 'l', 'br', 'bl', -90 );
+ addSide( 'l', 'b', 't', 'bl', 'tl', -180 );
+ }
}
- }
-
- return segments;
- },
+ return segments;
+ },
- /**
- * Get the container element for the shapes, creating it if necessary
- */
- getBox: function() {
- var box = this._box,
- infos = this.styleInfos,
- s;
- if( !box ) {
- box = this._box = this.element.document.createElement( 'bg-and-border' );
- s = box.style;
- s.position = 'absolute';
- s.zIndex = this.zIndex;
- this.parent.getBox().appendChild( box );
- }
+ /**
+ * Get the container element for the shapes, creating it if necessary
+ */
+ getBox: function() {
+ var box = this._box,
+ infos = this.styleInfos,
+ s;
+
+ if( !box ) {
+ box = this._box = this.element.document.createElement( 'bg-and-border' );
+ s = box.style;
+ s.position = 'absolute';
+ s.zIndex = this.zIndex;
+ this.parent.getBox().appendChild( box );
+ }
- return box;
- },
+ return box;
+ },
- /**
- * Destroy the rendered objects
- */
- destroy: function() {
- var box = this._box;
- if( box && box.parentNode ) {
- box.parentNode.removeChild( box );
+ /**
+ * Destroy the rendered objects
+ */
+ destroy: function() {
+ var box = this._box;
+ if( box && box.parentNode ) {
+ box.parentNode.removeChild( box );
+ }
+ delete this._box;
+ delete this._shapes;
}
- delete this._box;
- delete this._shapes;
- }
-} );
+ } );
+
+ return BackgroundAndBorderRenderer;
+})();
View
494 sources/BackgroundStyleInfo.js
@@ -3,291 +3,295 @@
* @constructor
* @param {Element} el the target element
*/
-PIE.BackgroundStyleInfo = function( el ) {
- this.element = el;
-};
-PIE.Util.merge( PIE.BackgroundStyleInfo.prototype, PIE.StyleBase, {
+PIE.BackgroundStyleInfo = (function() {
+ function BackgroundStyleInfo( el ) {
+ this.element = el;
+ }
+ PIE.Util.merge( BackgroundStyleInfo.prototype, PIE.StyleBase, {
- cssProperty: PIE.CSS_PREFIX + 'background',
- styleProperty: PIE.STYLE_PREFIX + 'Background',
+ cssProperty: PIE.CSS_PREFIX + 'background',
+ styleProperty: PIE.STYLE_PREFIX + 'Background',
- attachIdents: { scroll:1, fixed:1, local:1 },
- repeatIdents: { 'repeat-x':1, 'repeat-y':1, 'repeat':1, 'no-repeat':1 },
- originIdents: { 'padding-box':1, 'border-box':1, 'content-box':1 },
- clipIdents: { 'padding-box':1, 'border-box':1 },
- positionIdents: { top:1, right:1, bottom:1, left:1, center:1 },
- sizeIdents: { contain:1, cover:1 },
+ attachIdents: { scroll:1, fixed:1, local:1 },
+ repeatIdents: { 'repeat-x':1, 'repeat-y':1, 'repeat':1, 'no-repeat':1 },
+ originIdents: { 'padding-box':1, 'border-box':1, 'content-box':1 },
+ clipIdents: { 'padding-box':1, 'border-box':1 },
+ positionIdents: { top:1, right:1, bottom:1, left:1, center:1 },
+ sizeIdents: { contain:1, cover:1 },
- /**
- * For background styles, we support the -pie-background property but fall back to the standard
- * backround* properties. The reason we have to use the prefixed version is that IE natively
- * parses the standard properties and if it sees something it doesn't know how to parse, for example
- * multiple values or gradient definitions, it will throw that away and not make it available through
- * currentStyle.
- *
- * Format of return object:
- * {
- * color: <PIE.Color>,
- * images: [
- * {
- * type: 'image',
- * url: 'image.png',
- * repeat: <'no-repeat' | 'repeat-x' | 'repeat-y' | 'repeat'>,
- * position: <PIE.BgPosition>,
- * attachment: <'scroll' | 'fixed' | 'local'>,
- * origin: <'border-box' | 'padding-box' | 'content-box'>,
- * clip: <'border-box' | 'padding-box'>,
- * size: <'contain' | 'cover' | { w: <'auto' | PIE.Length>, h: <'auto' | PIE.Length> }>
- * },
- * {
- * type: 'linear-gradient',
- * gradientStart: <PIE.BgPosition>,
- * angle: <PIE.Angle>,
- * stops: [
- * { color: <PIE.Color>, offset: <PIE.Length> },
- * { color: <PIE.Color>, offset: <PIE.Length> }, ...
- * ]
- * }
- * ]
- * }
- * @param {String} css
- * @override
- */
- parseCss: function( css ) {
- var el = this.element,
- cs = el.currentStyle,
- rs = el.runtimeStyle,
- tokenizer, token, image,
- tok_type = PIE.Tokenizer.Type,
- type_length = tok_type.LENGTH,
- type_operator = tok_type.OPERATOR,
- type_ident = tok_type.IDENT,
- type_color = tok_type.COLOR,
- tokType, tokVal,
- positionIdents = this.positionIdents,
- gradient, stop,
- props = null;
+ /**
+ * For background styles, we support the -pie-background property but fall back to the standard
+ * backround* properties. The reason we have to use the prefixed version is that IE natively
+ * parses the standard properties and if it sees something it doesn't know how to parse, for example
+ * multiple values or gradient definitions, it will throw that away and not make it available through
+ * currentStyle.
+ *
+ * Format of return object:
+ * {
+ * color: <PIE.Color>,
+ * images: [
+ * {
+ * type: 'image',
+ * url: 'image.png',
+ * repeat: <'no-repeat' | 'repeat-x' | 'repeat-y' | 'repeat'>,
+ * position: <PIE.BgPosition>,
+ * attachment: <'scroll' | 'fixed' | 'local'>,
+ * origin: <'border-box' | 'padding-box' | 'content-box'>,
+ * clip: <'border-box' | 'padding-box'>,
+ * size: <'contain' | 'cover' | { w: <'auto' | PIE.Length>, h: <'auto' | PIE.Length> }>
+ * },
+ * {
+ * type: 'linear-gradient',
+ * gradientStart: <PIE.BgPosition>,
+ * angle: <PIE.Angle>,
+ * stops: [
+ * { color: <PIE.Color>, offset: <PIE.Length> },
+ * { color: <PIE.Color>, offset: <PIE.Length> }, ...
+ * ]
+ * }
+ * ]
+ * }
+ * @param {String} css
+ * @override
+ */
+ parseCss: function( css ) {
+ var el = this.element,
+ cs = el.currentStyle,
+ rs = el.runtimeStyle,
+ tokenizer, token, image,
+ tok_type = PIE.Tokenizer.Type,
+ type_length = tok_type.LENGTH,
+ type_operator = tok_type.OPERATOR,
+ type_ident = tok_type.IDENT,
+ type_color = tok_type.COLOR,
+ tokType, tokVal,
+ positionIdents = this.positionIdents,
+ gradient, stop,
+ props = null;
- function isLengthOrPercent( token ) {
- return token.type === type_length || token.type === tok_type.PERCENT || ( token.type === tok_type.NUMBER && token.value === '0' );
- }
+ function isLengthOrPercent( token ) {
+ return token.type === type_length || token.type === tok_type.PERCENT || ( token.type === tok_type.NUMBER && token.value === '0' );
+ }
- function isBgPosToken( token ) {
- return isLengthOrPercent( token ) || ( token.type === type_ident && token.value in positionIdents );
- }
+ function isBgPosToken( token ) {
+ return isLengthOrPercent( token ) || ( token.type === type_ident && token.value in positionIdents );
+ }
- function sizeToken( token ) {
- return ( isLengthOrPercent( token ) && new PIE.Length( token.value ) ) || ( token.value === 'auto' && 'auto' );
- }
+ function sizeToken( token ) {
+ return ( isLengthOrPercent( token ) && new PIE.Length( token.value ) ) || ( token.value === 'auto' && 'auto' );
+ }
- // If the CSS3-specific -pie-background property is present, parse it
- if( this.getCss3() ) {
- tokenizer = new PIE.Tokenizer( css );
- props = { images: [] };
- image = {};
+ // If the CSS3-specific -pie-background property is present, parse it
+ if( this.getCss3() ) {
+ tokenizer = new PIE.Tokenizer( css );
+ props = { images: [] };
+ image = {};
- while( token = tokenizer.next() ) {
- tokType = token.type;
- tokVal = token.value;
+ while( token = tokenizer.next() ) {
+ tokType = token.type;
+ tokVal = token.value;
- if( !image.type && tokType === tok_type.FUNCTION && tokVal === 'linear-gradient(' ) {
- gradient = { stops: [], type: 'linear-gradient' };
- stop = {};
- while( token = tokenizer.next() ) {
- tokType = token.type;
- tokVal = token.value;
+ if( !image.type && tokType === tok_type.FUNCTION && tokVal === 'linear-gradient(' ) {
+ gradient = { stops: [], type: 'linear-gradient' };
+ stop = {};
+ while( token = tokenizer.next() ) {
+ tokType = token.type;
+ tokVal = token.value;
- // If we reached the end of the function and had at least 2 stops, flush the info
- if( tokType === 'CHAR' && tokVal === ')' ) {
- if( stop.color ) {
- gradient.stops.push( stop );
- }
- if( gradient.stops.length > 1 ) {
- PIE.Util.merge( image, gradient );
+ // If we reached the end of the function and had at least 2 stops, flush the info
+ if( tokType === tok_type.CHARACTER && tokVal === ')' ) {
+ if( stop.color ) {
+ gradient.stops.push( stop );
+ }
+ if( gradient.stops.length > 1 ) {
+ PIE.Util.merge( image, gradient );
+ }
+ break;
}
- break;
- }
- // Color stop - must start with color
- if( tokType === type_color ) {
- // if we already have an angle/position, make sure that the previous token was a comma
- if( gradient.angle || gradient.gradientStart ) {
- token = tokenizer.prev();
- if( token.type !== type_operator ) {
- break; //fail
+ // Color stop - must start with color
+ if( tokType === type_color ) {
+ // if we already have an angle/position, make sure that the previous token was a comma
+ if( gradient.angle || gradient.gradientStart ) {
+ token = tokenizer.prev();
+ if( token.type !== type_operator ) {
+ break; //fail
+ }
+ tokenizer.next();
}
- tokenizer.next();
- }
- stop = {
- color: new PIE.Color( tokVal )
- };
- // check for offset following color
- token = tokenizer.next();
- if( isLengthOrPercent( token ) ) {
- stop.offset = new PIE.Length( token.value );
- } else {
+ stop = {
+ color: new PIE.Color( tokVal )
+ };
+ // check for offset following color
+ token = tokenizer.next();
+ if( isLengthOrPercent( token ) ) {
+ stop.offset = new PIE.Length( token.value );
+ } else {
+ tokenizer.prev();
+ }
+ }
+ // Angle - can only appear in first spot
+ else if( tokType === tok_type.ANGLE && !gradient.angle && !stop.color && !gradient.stops.length ) {
+ gradient.angle = new PIE.Angle( token.value );
+ }
+ else if( isBgPosToken( token ) && !gradient.gradientStart && !stop.color && !gradient.stops.length ) {
+ tokenizer.prev();
+ gradient.gradientStart = new PIE.BgPosition(
+ tokenizer.until( function( t ) {
+ return !isBgPosToken( t );
+ }, false ).slice( 0, -1 )
+ );
tokenizer.prev();
}
- }
- // Angle - can only appear in first spot
- else if( tokType === tok_type.ANGLE && !gradient.angle && !stop.color && !gradient.stops.length ) {
- gradient.angle = new PIE.Angle( token.value );
- }
- else if( isBgPosToken( token ) && !gradient.gradientStart && !stop.color && !gradient.stops.length ) {
- tokenizer.prev();
- gradient.gradientStart = new PIE.BgPosition(
- tokenizer.until( function( t ) {
- return !isBgPosToken( t );
- }, false ).slice( 0, -1 )
- );
- tokenizer.prev();
- }
- else if( tokType === type_operator && tokVal === ',' ) {
- if( stop.color ) {
- gradient.stops.push( stop );
- stop = {};
+ else if( tokType === type_operator && tokVal === ',' ) {
+ if( stop.color ) {
+ gradient.stops.push( stop );
+ stop = {};
+ }
+ }
+ else {
+ // Found something we didn't recognize; fail without adding image
+ break;
}
- }
- else {
- // Found something we didn't recognize; fail without adding image
- break;
}
}
- }
- else if( !image.type && tokType === tok_type.URL ) {
- image.url = tokVal;
- image.type = 'image';
- }
- else if( isBgPosToken( token ) && !image.size ) {
- tokenizer.prev();
- image.position = new PIE.BgPosition(
- tokenizer.until( function( t ) {
- return !isBgPosToken( t );
- }, false ).slice( 0, -1 )
- );
- tokenizer.prev();
- }
- else if( tokType === type_ident ) {
- if( tokVal in this.repeatIdents ) {
- image.repeat = tokVal;
+ else if( !image.type && tokType === tok_type.URL ) {
+ image.url = tokVal;
+ image.type = 'image';
}
- else if( tokVal in this.originIdents ) {
- image.origin = tokVal;
- if( tokVal in this.clipIdents ) {
- image.clip = tokVal;
+ else if( isBgPosToken( token ) && !image.size ) {
+ tokenizer.prev();
+ image.position = new PIE.BgPosition(
+ tokenizer.until( function( t ) {
+ return !isBgPosToken( t );
+ }, false ).slice( 0, -1 )
+ );
+ tokenizer.prev();
+ }
+ else if( tokType === type_ident ) {
+ if( tokVal in this.repeatIdents ) {
+ image.repeat = tokVal;
+ }
+ else if( tokVal in this.originIdents ) {
+ image.origin = tokVal;
+ if( tokVal in this.clipIdents ) {
+ image.clip = tokVal;
+ }
+ }
+ else if( tokVal in this.attachIdents ) {
+ image.attachment = tokVal;
}
}
- else if( tokVal in this.attachIdents ) {
- image.attachment = tokVal;
+ else if( tokType === type_color && !props.color ) {
+ props.color = new PIE.Color( tokVal );
}
- }
- else if( tokType === type_color && !props.color ) {
- props.color = new PIE.Color( tokVal );
- }
- else if( tokType === type_operator ) {
- // background size
- if( tokVal === '/' ) {
- token = tokenizer.next();
- tokType = token.type;
- tokVal = token.value;
- if( tokType === type_ident && tokVal in this.sizeIdents ) {
- image.size = tokVal;
+ else if( tokType === type_operator ) {
+ // background size
+ if( tokVal === '/' ) {
+ token = tokenizer.next();
+ tokType = token.type;
+ tokVal = token.value;
+ if( tokType === type_ident && tokVal in this.sizeIdents ) {
+ image.size = tokVal;
+ }
+ else if( tokVal = sizeToken( token ) ) {
+ image.size = {
+ w: tokVal,
+ h: sizeToken( tokenizer.next() ) || ( tokenizer.prev() && tokVal )
+ };
+ }
}
- else if( tokVal = sizeToken( token ) ) {
- image.size = {
- w: tokVal,
- h: sizeToken( tokenizer.next() ) || ( tokenizer.prev() && tokVal )
- };
+ // new layer
+ else if( tokVal === ',' && image.type ) {
+ props.images.push( image );
+ image = {};
}
}
- // new layer
- else if( tokVal === ',' && image.type ) {
- props.images.push( image );
- image = {};
+ else {
+ // Found something unrecognized; chuck everything
+ return null;
}
}
- else {
- // Found something unrecognized; chuck everything
- return null;
+
+ // leftovers
+ if( image.type ) {
+ props.images.push( image );
}
}
- // leftovers
- if( image.type ) {
- props.images.push( image );
+ // Otherwise, use the standard background properties; let IE give us the values rather than parsing them
+ else {
+ this.withActualBg( function() {
+ var posX = cs.backgroundPositionX,
+ posY = cs.backgroundPositionY,
+ img = cs.backgroundImage,
+ color = cs.backgroundColor;
+
+ props = {};
+ if( color !== 'transparent' ) {
+ props.color = new PIE.Color( color )
+ }
+ if( img !== 'none' ) {
+ props.images = [ {
+ type: 'image',
+ url: img.replace( this.urlRE, "$1" ),
+ repeat: cs.backgroundRepeat,
+ position: new PIE.BgPosition( new PIE.Tokenizer( posX + ' ' + posY ).all() )
+ } ];
+ }
+ } );
}
- }
- // Otherwise, use the standard background properties; let IE give us the values rather than parsing them
- else {
- this.withActualBg( function() {
- var posX = cs.backgroundPositionX,
- posY = cs.backgroundPositionY,
- img = cs.backgroundImage,
- color = cs.backgroundColor;
+ return props;
+ },
- props = {};
- if( color !== 'transparent' ) {
- props.color = new PIE.Color( color )
- }
- if( img !== 'none' ) {
- props.images = [ {
- type: 'image',
- url: img.replace( this.urlRE, "$1" ),
- repeat: cs.backgroundRepeat,
- position: new PIE.BgPosition( new PIE.Tokenizer( posX + ' ' + posY ).all() )
- } ];
- }
- } );
- }
-
- return props;
- },
+ /**
+ * Execute a function with the actual background styles (not overridden with runtimeStyle
+ * properties set by the renderers) available via currentStyle.
+ * @param fn
+ */
+ withActualBg: function( fn ) {
+ var rs = this.element.runtimeStyle,
+ rsImage = rs.backgroundImage,
+ rsColor = rs.backgroundColor,
+ ret;
- /**
- * Execute a function with the actual background styles (not overridden with runtimeStyle
- * properties set by the renderers) available via currentStyle.
- * @param fn
- */
- withActualBg: function( fn ) {
- var rs = this.element.runtimeStyle,
- rsImage = rs.backgroundImage,
- rsColor = rs.backgroundColor,
- ret;
+ rs.backgroundImage = rs.backgroundColor = '';
- rs.backgroundImage = rs.backgroundColor = '';
+ ret = fn.call( this );
- ret = fn.call( this );
+ rs.backgroundImage = rsImage;
+ rs.backgroundColor = rsColor;
- rs.backgroundImage = rsImage;
- rs.backgroundColor = rsColor;
+ return ret;
+ },
- return ret;
- },
+ getCss: function() {
+ var cs = this.element.currentStyle;
+ return this.getCss3() ||
+ this.withActualBg( function() {
+ return cs.backgroundColor + ' ' + cs.backgroundImage + ' ' + cs.backgroundRepeat + ' ' +
+ cs.backgroundPositionX + ' ' + cs.backgroundPositionY;
+ } );
+ },
- getCss: function() {
- var cs = this.element.currentStyle;
- return this.getCss3() ||
- this.withActualBg( function() {
- return cs.backgroundColor + ' ' + cs.backgroundImage + ' ' + cs.backgroundRepeat + ' ' +
- cs.backgroundPositionX + ' ' + cs.backgroundPositionY;
- } );
- },
+ getCss3: function() {
+ var el = this.element;
+ return el.style[ this.styleProperty ] || el.currentStyle.getAttribute( this.cssProperty );
+ },
- getCss3: function() {
- var el = this.element;
- return el.style[ this.styleProperty ] || el.currentStyle.getAttribute( this.cssProperty );
- },
+ /**
+ * The isActive logic is slightly different, because getProps() always returns an object
+ * even if it is just falling back to the native background properties. But we only want
+ * to report is as being "active" if the -pie-background override property is present and
+ * parses successfully.
+ */
+ isActive: function() {
+ return this.getCss3() && !!this.getProps();
+ }
- /**
- * The isActive logic is slightly different, because getProps() always returns an object
- * even if it is just falling back to the native background properties. But we only want
- * to report is as being "active" if the -pie-background override property is present and
- * parses successfully.
- */
- isActive: function() {
- return this.getCss3() && !!this.getProps();
- }
+ } );
-} );
+ return BackgroundStyleInfo;
+})();
View
160 sources/BgPosition.js
@@ -3,92 +3,96 @@
* @constructor
* @param {Array<{PIE.Tokenizer.Token}>} tokens The tokens making up the background position value.
*/
-PIE.BgPosition = function( tokens ) {
- this.tokens = tokens;
-};
-PIE.BgPosition.prototype = {
- /**
- * Normalize the values into the form:
- * [ xOffsetSide, xOffsetLength, yOffsetSide, yOffsetLength ]
- * where: xOffsetSide is either 'left' or 'right',
- * yOffsetSide is either 'top' or 'bottom',
- * and x/yOffsetLength are both PIE.Length objects.
- * @return {Array}
- */
- getValues: function() {
- if( !this._values ) {
- var tokens = this.tokens,
- len = tokens.length,
- length_zero = PIE.Length.ZERO,
- length_fifty = new PIE.Length( '50%' ),
- type_ident = PIE.Tokenizer.Type.IDENT,
- type_length = PIE.Tokenizer.Type.LENGTH,
- type_percent = PIE.Tokenizer.Type.PERCENT,
- type, value,
- vert_idents = { top: 1, center: 1, bottom: 1 },
- horiz_idents = { left: 1, center: 1, right: 1 },
- vals = [ 'left', length_zero, 'top', length_zero ];
-
- // If only one value, the second is assumed to be 'center'
- if( len === 1 ) {
- tokens.push( { type: type_ident, value: 'center' } );
- len++;
- }
+PIE.BgPosition = (function() {
+ function BgPosition( tokens ) {
+ this.tokens = tokens;
+ }
+ BgPosition.prototype = {
+ /**
+ * Normalize the values into the form:
+ * [ xOffsetSide, xOffsetLength, yOffsetSide, yOffsetLength ]
+ * where: xOffsetSide is either 'left' or 'right',
+ * yOffsetSide is either 'top' or 'bottom',
+ * and x/yOffsetLength are both PIE.Length objects.
+ * @return {Array}
+ */
+ getValues: function() {
+ if( !this._values ) {
+ var tokens = this.tokens,
+ len = tokens.length,
+ length_zero = PIE.Length.ZERO,
+ length_fifty = new PIE.Length( '50%' ),
+ type_ident = PIE.Tokenizer.Type.IDENT,
+ type_length = PIE.Tokenizer.Type.LENGTH,
+ type_percent = PIE.Tokenizer.Type.PERCENT,
+ type, value,
+ vert_idents = { top: 1, center: 1, bottom: 1 },
+ horiz_idents = { left: 1, center: 1, right: 1 },
+ vals = [ 'left', length_zero, 'top', length_zero ];
- // Two values - CSS2
- if( len === 2 ) {
- // If both idents, they can appear in either order, so switch them if needed
- if( tokens[0].type === type_ident && tokens[1].type === type_ident &&
- tokens[0].value in vert_idents && tokens[1].value in horiz_idents ) {
- tokens.push( tokens.shift() );
+ // If only one value, the second is assumed to be 'center'
+ if( len === 1 ) {
+ tokens.push( { type: type_ident, value: 'center' } );
+ len++;
}
- if( tokens[0].type === type_ident ) {
- if( tokens[0].value === 'center' ) {
- vals[1] = length_fifty;
- } else {
- vals[0] = tokens[0].value;
+
+ // Two values - CSS2
+ if( len === 2 ) {
+ // If both idents, they can appear in either order, so switch them if needed
+ if( tokens[0].type === type_ident && tokens[1].type === type_ident &&
+ tokens[0].value in vert_idents && tokens[1].value in horiz_idents ) {
+ tokens.push( tokens.shift() );
}
- }
- else if( tokens[0].type === type_length || tokens[0].type === type_percent ) {
- vals[1] = new PIE.Length( tokens[0].value );
- }
- if( tokens[1].type === type_ident ) {
- if( tokens[1].value === 'center' ) {
- vals[3] = length_fifty;
- } else {
- vals[2] = tokens[1].value;
+ if( tokens[0].type === type_ident ) {
+ if( tokens[0].value === 'center' ) {
+ vals[1] = length_fifty;
+ } else {
+ vals[0] = tokens[0].value;
+ }
+ }
+ else if( tokens[0].type === type_length || tokens[0].type === type_percent ) {
+ vals[1] = new PIE.Length( tokens[0].value );
+ }
+ if( tokens[1].type === type_ident ) {
+ if( tokens[1].value === 'center' ) {
+ vals[3] = length_fifty;
+ } else {
+ vals[2] = tokens[1].value;
+ }
+ }
+ else if( tokens[1].type === type_length || tokens[1].type === type_percent ) {
+ vals[3] = new PIE.Length( tokens[1].value );
}
}
- else if( tokens[1].type === type_length || tokens[1].type === type_percent ) {
- vals[3] = new PIE.Length( tokens[1].value );
+
+ // Three or four values - CSS3
+ else {
+ // TODO
}
- }
- // Three or four values - CSS3
- else {
- // TODO
+ this._values = vals;
}
+ return this._values;
+ },
- this._values = vals;
- }
- return this._values;
- },
+ /**
+ * Find the coordinates of the background image from the upper-left corner of the background area
+ * @param {Element} el
+ * @param {number} width - the width for percentages (background area width minus image width)
+ * @param {number} height - the height for percentages (background area height minus image height)
+ * @return {Object} { x: Number, y: Number }
+ */
+ coords: function( el, width, height ) {
+ var vals = this.getValues(),
+ pxX = vals[1].pixels( el, width ),
+ pxY = vals[3].pixels( el, height );
- /**
- * Find the coordinates of the background image from the upper-left corner of the background area
- * @param {Element} el
- * @param {number} width - the width for percentages (background area width minus image width)
- * @param {number} height - the height for percentages (background area height minus image height)
- * @return {Object} { x: Number, y: Number }
- */
- coords: function( el, width, height ) {
- var vals = this.getValues(),
- pxX = vals[1].pixels( el, width ),
- pxY = vals[3].pixels( el, height );
+ return {
+ x: Math.round( vals[0] === 'right' ? width - pxX : pxX ),
+ y: Math.round( vals[2] === 'bottom' ? height - pxY : pxY )
+ };
+ }
+ };
- return {
- x: Math.round( vals[0] === 'right' ? width - pxX : pxX ),
- y: Math.round( vals[2] === 'bottom' ? height - pxY : pxY )
- };
- }
-};
+ return BgPosition;
+})();
View
252 sources/BorderImageRenderer.js
@@ -5,134 +5,138 @@
* @param {Object} styleInfos The StyleInfo objects
* @param {PIE.RootRenderer} parent
*/
-PIE.BorderImageRenderer = function( el, styleInfos, parent ) {
- this.element = el;
- this.styleInfos = styleInfos;
- this.parent = parent;
-};
-PIE.Util.merge( PIE.BorderImageRenderer.prototype, PIE.RendererBase, {
-
- zIndex: 400,
- pieceNames: [ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl', 'c' ],
-
- needsUpdate: function() {
- var si = this.styleInfos;
- return si.borderImage.changed() || si.border.changed();
- },
-
- isActive: function() {
- return this.styleInfos.borderImage.isActive();
- },
-
- updateSize: function() {
- if( this.isActive() ) {
- var props = this.styleInfos.borderImage.getProps(),
- box = this.getBox(), //make sure pieces are created
- el = this.element,
- p = this.pieces;
-
- PIE.Util.withImageSize( props.src, function( imgSize ) {
- var w = el.offsetWidth,
- h = el.offsetHeight,
- z = el.currentStyle.zIndex,
-
- t = p.t.style,
- tr = p.tr.style,
- r = p.r.style,
- br = p.br.style,
- b = p.b.style,
- bl = p.bl.style,
- l = p.l.style,
- tl = p.tl.style,
- c = p.c.style,
-
- slices = props.slice,
- widths = props.width,
- widthT = widths.t.pixels( el ),
- widthR = widths.r.pixels( el ),
- widthB = widths.b.pixels( el ),
- widthL = widths.l.pixels( el );
-
- tl.height = t.height = tr.height = widthT;
- tl.width = l.width = bl.width = widthL;
- tr.left = r.left = br.left = w - widthR;
- tr.width = r.width = br.width = widthR;
- br.top = b.top = bl.top = h - widthB;
- br.height = b.height = bl.height = widthB;
- t.left = b.left = c.left = widthL;
- t.width = b.width = c.width = w - widthL - widthR;
- l.top = r.top = c.top = widthT;
- l.height = r.height = c.height = h - widthT - widthB;
-
-
- // image croppings
-
- // corners
- p.tl.imagedata.cropBottom = p.t.imagedata.cropBottom = p.tr.imagedata.cropBottom = ( imgSize.h - slices.t ) / imgSize.h;
- p.tl.imagedata.cropRight = p.l.imagedata.cropRight = p.bl.imagedata.cropRight = ( imgSize.w - slices.l ) / imgSize.w;
- p.bl.imagedata.cropTop = p.b.imagedata.cropTop = p.br.imagedata.cropTop = ( imgSize.h - slices.b ) / imgSize.h;
- p.tr.imagedata.cropLeft = p.r.imagedata.cropLeft = p.br.imagedata.cropLeft = ( imgSize.w - slices.r ) / imgSize.w;
-
- // edges and center
- if( props.repeat.v === 'stretch' ) {
- p.l.imagedata.cropTop = p.r.imagedata.cropTop = p.c.imagedata.cropTop = slices.t / imgSize.h;
- p.l.imagedata.cropBottom = p.r.imagedata.cropBottom = p.c.imagedata.cropBottom = slices.b / imgSize.h;
- }
- if( props.repeat.h === 'stretch' ) {
- p.t.imagedata.cropLeft = p.b.imagedata.cropLeft = p.c.imagedata.cropLeft = slices.l / imgSize.w;
- p.t.imagedata.cropRight = p.b.imagedata.cropRight = p.c.imagedata.cropRight = slices.r / imgSize.w;
- }
- }, this );
- } else {
- this.destroy();
- }
- },
+PIE.BorderImageRenderer = (function() {
+ function BorderImageRenderer( el, styleInfos, parent ) {
+ this.element = el;
+ this.styleInfos = styleInfos;
+ this.parent = parent;
+ }
+ PIE.Util.merge( BorderImageRenderer.prototype, PIE.RendererBase, {
+
+ zIndex: 400,
+ pieceNames: [ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl', 'c' ],
+
+ needsUpdate: function() {
+ var si = this.styleInfos;
+ return si.borderImage.changed() || si.border.changed();
+ },
+
+ isActive: function() {
+ return this.styleInfos.borderImage.isActive();
+ },
+
+ updateSize: function() {
+ if( this.isActive() ) {
+ var props = this.styleInfos.borderImage.getProps(),
+ box = this.getBox(), //make sure pieces are created
+ el = this.element,
+ p = this.pieces;
+
+ PIE.Util.withImageSize( props.src, function( imgSize ) {
+ var w = el.offsetWidth,
+ h = el.offsetHeight,
+ z = el.currentStyle.zIndex,
+
+ t = p['t'].style,
+ tr = p['tr'].style,
+ r = p['r'].style,
+ br = p['br'].style,
+ b = p['b'].style,
+ bl = p['bl'].style,
+ l = p['l'].style,
+ tl = p['tl'].style,
+ c = p['c'].style,
+
+ slices = props.slice,
+ widths = props.width,
+ widthT = widths.t.pixels( el ),
+ widthR = widths.r.pixels( el ),
+ widthB = widths.b.pixels( el ),
+ widthL = widths.