Вход Регистрация
Файл: contao-3.5.8/assets/css3pie/1.0.0/PIE-uncompressed.js
Строк: 5167
<?php
/*
PIE: CSS3 rendering for IE
Version 1.0.0
http://css3pie.com
Dual-licensed for use under the Apache License Version 2.0 or the General Public License (GPL) Version 2.
*/
(function(){
var 
doc document;var PIE window['PIE'];

if( !
PIE ) {
    
PIE window['PIE'] = {
        
CSS_PREFIX'-pie-',
        
STYLE_PREFIX'Pie',
        
CLASS_PREFIX'pie_',
        
tableCellTags: {
            
'TD'1,
            
'TH'1
        
},

        
/**
         * Lookup table of elements which cannot take custom children.
         */
        
childlessElements: {
            
'TABLE':1,
            
'THEAD':1,
            
'TBODY':1,
            
'TFOOT':1,
            
'TR':1,
            
'INPUT':1,
            
'TEXTAREA':1,
            
'SELECT':1,
            
'OPTION':1,
            
'IMG':1,
            
'HR':1
        
},

        
/**
         * Elements that can receive user focus
         */
        
focusableElements: {
            
'A':1,
            
'INPUT':1,
            
'TEXTAREA':1,
            
'SELECT':1,
            
'BUTTON':1
        
},

        
/**
         * Values of the type attribute for input elements displayed as buttons
         */
        
inputButtonTypes: {
            
'submit':1,
            
'button':1,
            
'reset':1
        
},

        
emptyFn: function() {}
    };

    
// Force the background cache to be used. No reason it shouldn't be.
    
try {
        
doc.execCommand'BackgroundImageCache'falsetrue );
    } catch(
e) {}

    (function() {
        
/*
         * IE version detection approach by James Padolsey, with modifications -- from
         * http://james.padolsey.com/javascript/detect-ie-in-js-using-conditional-comments/
         */
        
var ieVersion 4,
            
div doc.createElement('div'),
            
all div.getElementsByTagName('i'),
            
shape;
        while (
            
div.innerHTML '<!--[if gt IE ' + (++ieVersion) + ']><i></i><![endif]-->',
            
all[0]
        ) {}
        
PIE.ieVersion ieVersion;

        
// Detect IE6
        
if( ieVersion === ) {
            
// IE6 can't access properties with leading dash, but can without it.
            
PIE.CSS_PREFIX PIE.CSS_PREFIX.replace( /^-/, '' );
        }

        
PIE.ieDocMode doc.documentMode || PIE.ieVersion;

        
// Detect VML support (a small number of IE installs don't have a working VML engine)
        
div.innerHTML '<v:shape adj="1"/>';
        
shape div.firstChild;
        
shape.style['behavior'] = 'url(#default#VML)';
        
PIE.supportsVML = (typeof shape['adj'] === "object");
    }());
/**
 * Utility functions
 */
(function() {
    var 
vmlCreatorDoc,
        
idNum 0,
        
imageSizes = {};


    
PIE.Util = {

        
/**
         * To create a VML element, it must be created by a Document which has the VML
         * namespace set. Unfortunately, if you try to add the namespace programatically
         * into the main document, you will get an "Unspecified error" when trying to
         * access document.namespaces before the document is finished loading. To get
         * around this, we create a DocumentFragment, which in IE land is apparently a
         * full-fledged Document. It allows adding namespaces immediately, so we add the
         * namespace there and then have it create the VML element.
         * @param {string} tag The tag name for the VML element
         * @return {Element} The new VML element
         */
        
createVmlElement: function( tag ) {
            var 
vmlPrefix 'css3vml';
            if( !
vmlCreatorDoc ) {
                
vmlCreatorDoc doc.createDocumentFragment();
                
vmlCreatorDoc.namespaces.addvmlPrefix'urn:schemas-microsoft-com:vml' );
            }
            return 
vmlCreatorDoc.createElementvmlPrefix ':' tag );
        },


        
/**
         * Generate and return a unique ID for a given object. The generated ID is stored
         * as a property of the object for future reuse.
         * @param {Object} obj
         */
        
getUID: function( obj ) {
            return 
obj && obj'_pieId' ] || ( obj'_pieId' ] = '_' + ++idNum );
        },


        
/**
         * Simple utility for merging objects
         * @param {Object} obj1 The main object into which all others will be merged
         * @param {...Object} var_args Other objects which will be merged into the first, in order
         */
        
merge: function( obj1 ) {
            var 
ilenpobjNargs arguments;
            for( 
1len args.lengthleni++ ) {
                
objN args[i];
                for( 
p in objN ) {
                    if( 
objN.hasOwnProperty) ) {
                        
obj1] = objN];
                    }
                }
            }
            return 
obj1;
        },


        
/**
         * Execute a callback function, passing it the dimensions of a given image once
         * they are known.
         * @param {string} src The source URL of the image
         * @param {function({w:number, h:number})} func The callback function to be called once the image dimensions are known
         * @param {Object} ctx A context object which will be used as the 'this' value within the executed callback function
         */
        
withImageSize: function( srcfuncctx ) {
            var 
size imageSizessrc ], imgqueue;
            if( 
size ) {
                
// If we have a queue, add to it
                
if( Object.prototype.toString.callsize ) === '[object Array]' ) {
                    
size.push( [ funcctx ] );
                }
                
// Already have the size cached, call func right away
                
else {
                    
func.callctxsize );
                }
            } else {
                
queue imageSizessrc ] = [ [ funcctx ] ]; //create queue
                
img = new Image();
                
img.onload = function() {
                    
size imageSizessrc ] = { wimg.widthhimg.height };
                    for( var 
0len queue.lengthleni++ ) {
                        
queue][ ].callqueue][ ], size );
                    }
                    
img.onload null;
                };
                
img.src src;
            }
        }
    };
})();
/**
 * Utility functions for handling gradients
 */
PIE.GradientUtil = {

    
getGradientMetrics: function( elwidthheightgradientInfo ) {
        var 
angle gradientInfo.angle,
            
startPos gradientInfo.gradientStart,
            
startXstartY,
            
endXendY,
            
startCornerXstartCornerY,
            
endCornerXendCornerY,
            
deltaXdeltaY,
            
pUNDEF;

        
// 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 ) ? width 0;
            
startCornerY angle 180 height 0;
            
endCornerX width startCornerX;
            
endCornerY height startCornerY;
        }

        
// Normalize the angle to a value between [0, 360)
        
function normalizeAngle() {
            while( 
angle ) {
                
angle += 360;
            }
            
angle angle 360;
        }

        
// Find the start and end points of the gradient
        
if( startPos ) {
            
startPos startPos.coordselwidthheight );
            
startX startPos.x;
            
startY startPos.y;
        }
        if( 
angle ) {
            
angle angle.degrees();

            
normalizeAngle();
            
findCorners();

            
// 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.
            
PIE.GradientUtil.perpendicularIntersectstartXstartYangleendCornerXendCornerY );
            
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 width startX;
            
endY height startY;
        }
        else {
            
// Neither position nor angle specified; create vertical gradient from top to bottom
            
startX startY endX 0;
            
endY height;
        }
        
deltaX endX startX;
        
deltaY endY startY;

        if( 
angle === UNDEF ) {
            
// Get the angle based on the change in x/y from start to end point. Checks first for horizontal
            // or vertical angles so they get exact whole numbers rather than what atan2 gives.
            
angle = ( !deltaX ? ( deltaY 90 270 ) :
                        ( !
deltaY ? ( deltaX 180 ) :
                            -
Math.atan2deltaYdeltaX ) / Math.PI 180
                        
)
                    );
            
normalizeAngle();
            
findCorners();
        }

        return {
            
angleangle,
            
startXstartX,
            
startYstartY,
            
endXendX,
            
endYendY,
            
startCornerXstartCornerX,
            
startCornerYstartCornerY,
            
endCornerXendCornerX,
            
endCornerYendCornerY,
            
deltaXdeltaX,
            
deltaYdeltaY,
            
lineLengthPIE.GradientUtil.distancestartXstartYendXendY )
        }
    },

    
/**
     * 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 ]
     */
    
perpendicularIntersect: function( x1y1anglex2y2 ) {
        
// Handle straight vertical and horizontal angles, for performance and to avoid
        // divide-by-zero errors.
        
if( angle === || angle === 180 ) {
            return [ 
x2y1 ];
        }
        else if( 
angle === 90 || angle === 270 ) {
            return [ 
x1y2 ];
        }
        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 Math.PI 180 ),
                
c1 a1 x1 y1,
                
a2 = -a1,
                
c2 a2 x2 y2,
                
a2 a1,
                
endX = ( c2 c1 ) / d,
                
endY = ( a1 c2 a2 c1 ) / d;
            return [ 
endXendY ];
        }
    },

    
/**
     * Find the distance between two points
     * @param {Number} p1x
     * @param {Number} p1y
     * @param {Number} p2x
     * @param {Number} p2y
     * @return {Number} the distance
     */
    
distance: function( p1xp1yp2xp2y ) {
        var 
dx p2x p1x,
            
dy p2y p1y;
        return 
Math.abs(
            
dx === dy :
            
dy === dx :
            
Math.sqrtdx dx dy dy )
        );
    }

};
/**
 * 
 */
PIE.Observable = function() {
    
/**
     * List of registered observer functions
     */
    
this.observers = [];

    
/**
     * Hash of function ids to their position in the observers list, for fast lookup
     */
    
this.indexes = {};
};
PIE.Observable.prototype = {

    
observe: function( fn ) {
        var 
id PIE.Util.getUIDfn ),
            
indexes this.indexes,
            
observers this.observers;
        if( !( 
id in indexes ) ) {
            
indexesid ] = observers.length;
            
observers.pushfn );
        }
    },

    
unobserve: function( fn ) {
        var 
id PIE.Util.getUIDfn ),
            
indexes this.indexes;
        if( 
id && id in indexes ) {
            
delete this.observersindexesid ] ];
            
delete indexesid ];
        }
    },

    
fire: function() {
        var 
this.observers,
            
o.length;
        while( 
i-- ) {
            
o] && o]();
        }
    }

};
/*
 * Simple heartbeat timer - this is a brute-force workaround for syncing issues caused by IE not
 * always firing the onmove and onresize events when elements are moved or resized. We check a few
 * times every second to make sure the elements have the correct position and size. See Element.js
 * which adds heartbeat listeners based on the custom -pie-poll flag, which defaults to true in IE8-9
 * and false elsewhere.
 */

PIE.Heartbeat = new PIE.Observable();
PIE.Heartbeat.run = function() {
    var 
me this,
        
interval;
    if( !
me.running ) {
        
interval doc.documentElement.currentStyle.getAttributePIE.CSS_PREFIX 'poll-interval' ) || 250;
        (function 
beat() {
            
me.fire();
            
setTimeout(beatinterval);
        })();
        
me.running 1;
    }
};
/**
 * Create an observable listener for the onunload event
 */
(function() {
    
PIE.OnUnload = new PIE.Observable();

    function 
handleUnload() {
        
PIE.OnUnload.fire();
        
window.detachEvent'onunload'handleUnload );
        
window'PIE' ] = null;
    }

    
window.attachEvent'onunload'handleUnload );

    
/**
     * Attach an event which automatically gets detached onunload
     */
    
PIE.OnUnload.attachManagedEvent = function( targetnamehandler ) {
        
target.attachEventnamehandler );
        
this.observe( function() {
            
target.detachEventnamehandler );
        } );
    };
})()
/**
 * Create a single observable listener for window resize events.
 */
PIE.OnResize = new PIE.Observable();

PIE.OnUnload.attachManagedEventwindow'onresize', function() { PIE.OnResize.fire(); } );
/**
 * Create a single observable listener for scroll events. Used for lazy loading based
 * on the viewport, and for fixed position backgrounds.
 */
(function() {
    
PIE.OnScroll = new PIE.Observable();

    function 
scrolled() {
        
PIE.OnScroll.fire();
    }

    
PIE.OnUnload.attachManagedEventwindow'onscroll'scrolled );

    
PIE.OnResize.observescrolled );
})();
/**
 * Listen for printing events, destroy all active PIE instances when printing, and
 * restore them afterward.
 */
(function() {

    var 
elements;

    function 
beforePrint() {
        
elements PIE.Element.destroyAll();
    }

    function 
afterPrint() {
        if( 
elements ) {
            for( var 
0len elements.lengthleni++ ) {
                
PIE'attach' ]( elements[i] );
            }
            
elements 0;
        }
    }

    if( 
PIE.ieDocMode ) {
        
PIE.OnUnload.attachManagedEventwindow'onbeforeprint'beforePrint );
        
PIE.OnUnload.attachManagedEventwindow'onafterprint'afterPrint );
    }

})();
/**
 * Create a single observable listener for document mouseup events.
 */
PIE.OnMouseup = new PIE.Observable();

PIE.OnUnload.attachManagedEventdoc'onmouseup', function() { PIE.OnMouseup.fire(); } );
/**
 * Wrapper for length and percentage style values. The value is immutable. A singleton instance per unique
 * value is returned from PIE.getLength() - always use that instead of instantiating directly.
 * @constructor
 * @param {string} val The CSS string representing the length. It is assumed that this will already have
 *                 been validated as a valid length or percentage syntax.
 */
PIE.Length = (function() {
    var 
lengthCalcEl doc.createElement'length-calc' ),
        
parent doc.body || doc.documentElement,
        
lengthCalcEl.style,
        
conversions = {},
        
units = [ 'mm''cm''in''pt''pc' ],
        
units.length,
        
instances = {};

    
s.position 'absolute';
    
s.top s.left '-9999px';

    
parent.appendChildlengthCalcEl );
    while( 
i-- ) {
        
s.width '100' units[i];
        
conversionsunits[i] ] = lengthCalcEl.offsetWidth 100;
    }
    
parent.removeChildlengthCalcEl );

    
// All calcs from here on will use 1em
    
s.width '1em';


    function 
Lengthval ) {
        
this.val val;
    }
    
Length.prototype = {
        
/**
         * Regular expression for matching the length unit
         * @private
         */
        
unitRE: /(px|em|ex|mm|cm|in|pt|pc|%)$/,

        
/**
         * Get the numeric value of the length
         * @return {number} The value
         */
        
getNumber: function() {
            var 
num this.num,
                
UNDEF;
            if( 
num === UNDEF ) {
                
num this.num parseFloatthis.val );
            }
            return 
num;
        },

        
/**
         * Get the unit of the length
         * @return {string} The unit
         */
        
getUnit: function() {
            var 
unit this.unit,
                
m;
            if( !
unit ) {
                
this.val.matchthis.unitRE );
                
unit this.unit = ( && m[0] ) || 'px';
            }
            return 
unit;
        },

        
/**
         * Determine whether this is a percentage length value
         * @return {boolean}
         */
        
isPercentage: function() {
            return 
this.getUnit() === '%';
        },

        
/**
         * Resolve this length into a number of pixels.
         * @param {Element} el - the context element, used to resolve font-relative values
         * @param {(function():number|number)=} pct100 - the number of pixels that equal a 100% percentage. This can be either a number or a
         *                  function which will be called to return the number.
         */
        
pixels: function( elpct100 ) {
            var 
num this.getNumber(),
                
unit this.getUnit();
            switch( 
unit ) {
                case 
"px":
                    return 
num;
                case 
"%":
                    return 
num * ( typeof pct100 === 'function' pct100() : pct100 ) / 100;
                case 
"em":
                    return 
num this.getEmPixelsel );
                case 
"ex":
                    return 
num this.getEmPixelsel ) / 2;
                default:
                    return 
num conversionsunit ];
            }
        },

        
/**
         * The em and ex units are relative to the font-size of the current element,
         * however if the font-size is set using non-pixel units then we get that value
         * rather than a pixel conversion. To get around this, we keep a floating element
         * with width:1em which we insert into the target element and then read its offsetWidth.
         * For elements that won't accept a child we insert into the parent node and perform
         * additional calculation. If the font-size *is* specified in pixels, then we use that
         * directly to avoid the expensive DOM manipulation.
         * @param {Element} el
         * @return {number}
         */
        
getEmPixels: function( el ) {
            var 
fs el.currentStyle.fontSize,
                
pxparentme;

            if( 
fs.indexOf'px' ) > ) {
                return 
parseFloatfs );
            }
            else if( 
el.tagName in PIE.childlessElements ) {
                
me this;
                
parent el.parentNode;
                return 
PIE.getLengthfs ).pixelsparent, function() {
                    return 
me.getEmPixelsparent );
                } );
            }
            else {
                
el.appendChildlengthCalcEl );
                
px lengthCalcEl.offsetWidth;
                if( 
lengthCalcEl.parentNode === el ) { //not sure how this could be false but it sometimes is
                    
el.removeChildlengthCalcEl );
                }
                return 
px;
            }
        }
    };


    
/**
     * Retrieve a PIE.Length instance for the given value. A shared singleton instance is returned for each unique value.
     * @param {string} val The CSS string representing the length. It is assumed that this will already have
     *                 been validated as a valid length or percentage syntax.
     */
    
PIE.getLength = function( val ) {
        return 
instancesval ] || ( instancesval ] = new Lengthval ) );
    };

    return 
Length;
})();
/**
 * Wrapper for a CSS3 bg-position value. Takes up to 2 position keywords and 2 lengths/percentages.
 * @constructor
 * @param {Array.<PIE.Tokenizer.Token>} tokens The tokens making up the background position value.
 */
PIE.BgPosition = (function() {

    var 
length_fifty PIE.getLength'50%' ),
        
vert_idents = { 'top'1'center'1'bottom'},
        
horiz_idents = { 'left'1'center'1'right'};


    function 
BgPositiontokens ) {
        
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,
                    
Tokenizer PIE.Tokenizer,
                    
identType Tokenizer.Type,
                    
length_zero PIE.getLength'0' ),
                    
type_ident identType.IDENT,
                    
type_length identType.LENGTH,
                    
type_percent identType.PERCENT,
                    
typevalue,
                    
vals = [ 'left'length_zero'top'length_zero ];

                
// If only one value, the second is assumed to be 'center'
                
if( len === ) {
                    
tokens.push( new Tokenizer.Tokentype_ident'center' ) );
                    
len++;
                }

                
// Two values - CSS2
                
if( len === ) {
                    
// If both idents, they can appear in either order, so switch them if needed
                    
if( type_ident & ( tokens[0].tokenType tokens[1].tokenType ) &&
                        
tokens[0].tokenValue in vert_idents && tokens[1].tokenValue in horiz_idents ) {
                        
tokens.pushtokens.shift() );
                    }
                    if( 
tokens[0].tokenType type_ident ) {
                        if( 
tokens[0].tokenValue === 'center' ) {
                            
vals[1] = length_fifty;
                        } else {
                            
vals[0] = tokens[0].tokenValue;
                        }
                    }
                    else if( 
tokens[0].isLengthOrPercent() ) {
                        
vals[1] = PIE.getLengthtokens[0].tokenValue );
                    }
                    if( 
tokens[1].tokenType type_ident ) {
                        if( 
tokens[1].tokenValue === 'center' ) {
                            
vals[3] = length_fifty;
                        } else {
                            
vals[2] = tokens[1].tokenValue;
                        }
                    }
                    else if( 
tokens[1].isLengthOrPercent() ) {
                        
vals[3] = PIE.getLengthtokens[1].tokenValue );
                    }
                }

                
// Three or four values - CSS3
                
else {
                    
// TODO
                
}

                
this._values vals;
            }
            return 
this._values;
        },

        
/**
         * Find the coordinates of the background image from the upper-left corner of the background area.
         * Note that these coordinate values are not rounded.
         * @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( elwidthheight ) {
            var 
vals this.getValues(),
                
pxX vals[1].pixelselwidth ),
                
pxY vals[3].pixelselheight );

            return {
                
xvals[0] === 'right' width pxX pxX,
                
yvals[2] === 'bottom' height pxY pxY
            
};
        }
    };

    return 
BgPosition;
})();
/**
 * Wrapper for a CSS3 background-size value.
 * @constructor
 * @param {String|PIE.Length} w The width parameter
 * @param {String|PIE.Length} h The height parameter, if any
 */
PIE.BgSize = (function() {

    var 
CONTAIN 'contain',
        
COVER 'cover',
        
AUTO 'auto';


    function 
BgSizew) {
        
this.w;
        
this.h;
    }
    
BgSize.prototype = {

        
pixels: function( elareaWareaHimgWimgH ) {
            var 
me this,
                
me.w,
                
me.h,
                
areaRatio areaW areaH,
                
imgRatio imgW imgH;

            if ( 
=== CONTAIN ) {
                
imgRatio areaRatio areaW areaH imgRatio;
                
imgRatio areaRatio areaW imgRatio areaH;
            }
            else if ( 
=== COVER ) {
                
imgRatio areaRatio areaW areaH imgRatio;
                
imgRatio areaRatio areaW imgRatio areaH;
            }
            else if ( 
=== AUTO ) {
                
= ( === AUTO imgH h.pixelselareaH ) );
                
imgRatio;
            }
            else {
                
w.pixelselareaW );
                
= ( === AUTO imgRatio h.pixelselareaH ) );
            }

            return { 
wwh};
        }

    };

    
BgSize.DEFAULT = new BgSizeAUTOAUTO );

    return 
BgSize;
})();
/**
 * Wrapper for angle values; handles conversion to degrees from all allowed angle units
 * @constructor
 * @param {string} val The raw CSS value for the angle. It is assumed it has been pre-validated.
 */
PIE.Angle = (function() {
    function 
Angleval ) {
        
this.val val;
    }
    
Angle.prototype = {
        
unitRE: /[a-z]+$/i,

        
/**
         * @return {string} The unit of the angle value
         */
        
getUnit: function() {
            return 
this._unit || ( this._unit this.val.matchthis.unitRE )[0].toLowerCase() );
        },

        
/**
         * Get the numeric value of the angle in degrees.
         * @return {number} The degrees value
         */
        
degrees: function() {
            var 
deg this._degun;
            if( 
deg === undefined ) {
                
this.getUnit();
                
parseFloatthis.val10 );
                
deg this._deg = ( === 'deg' === 'rad' Math.PI 180 === 'grad' 400 360 === 'turn' 360 );
            }
            return 
deg;
        }
    };

    return 
Angle;
})();
/**
 * Abstraction for colors values. Allows detection of rgba values. A singleton instance per unique
 * value is returned from PIE.getColor() - always use that instead of instantiating directly.
 * @constructor
 * @param {string} val The raw CSS string value for the color
 */
PIE.Color = (function() {
    var 
instances = {};

    function 
Colorval ) {
        
this.val val;
    }

    
/**
     * Regular expression for matching rgba colors and extracting their components
     * @type {RegExp}
     */
    
Color.rgbaRE = /s*rgba(s*(d{1,3})s*,s*(d{1,3})s*,s*(d{1,3})s*,s*(d+|d*.d+)s*)s*/;

    
Color.names = {
        
"aliceblue":"F0F8FF""antiquewhite":"FAEBD7""aqua":"0FF",
        
"aquamarine":"7FFFD4""azure":"F0FFFF""beige":"F5F5DC",
        
"bisque":"FFE4C4""black":"000""blanchedalmond":"FFEBCD",
        
"blue":"00F""blueviolet":"8A2BE2""brown":"A52A2A",
        
"burlywood":"DEB887""cadetblue":"5F9EA0""chartreuse":"7FFF00",
        
"chocolate":"D2691E""coral":"FF7F50""cornflowerblue":"6495ED",
        
"cornsilk":"FFF8DC""crimson":"DC143C""cyan":"0FF",
        
"darkblue":"00008B""darkcyan":"008B8B""darkgoldenrod":"B8860B",
        
"darkgray":"A9A9A9""darkgreen":"006400""darkkhaki":"BDB76B",
        
"darkmagenta":"8B008B""darkolivegreen":"556B2F""darkorange":"FF8C00",
        
"darkorchid":"9932CC""darkred":"8B0000""darksalmon":"E9967A",
        
"darkseagreen":"8FBC8F""darkslateblue":"483D8B""darkslategray":"2F4F4F",
        
"darkturquoise":"00CED1""darkviolet":"9400D3""deeppink":"FF1493",
        
"deepskyblue":"00BFFF""dimgray":"696969""dodgerblue":"1E90FF",
        
"firebrick":"B22222""floralwhite":"FFFAF0""forestgreen":"228B22",
        
"fuchsia":"F0F""gainsboro":"DCDCDC""ghostwhite":"F8F8FF",
        
"gold":"FFD700""goldenrod":"DAA520""gray":"808080",
        
"green":"008000""greenyellow":"ADFF2F""honeydew":"F0FFF0",
        
"hotpink":"FF69B4""indianred":"CD5C5C""indigo":"4B0082",
        
"ivory":"FFFFF0""khaki":"F0E68C""lavender":"E6E6FA",
        
"lavenderblush":"FFF0F5""lawngreen":"7CFC00""lemonchiffon":"FFFACD",
        
"lightblue":"ADD8E6""lightcoral":"F08080""lightcyan":"E0FFFF",
        
"lightgoldenrodyellow":"FAFAD2""lightgreen":"90EE90""lightgrey":"D3D3D3",
        
"lightpink":"FFB6C1""lightsalmon":"FFA07A""lightseagreen":"20B2AA",
        
"lightskyblue":"87CEFA""lightslategray":"789""lightsteelblue":"B0C4DE",
        
"lightyellow":"FFFFE0""lime":"0F0""limegreen":"32CD32",
        
"linen":"FAF0E6""magenta":"F0F""maroon":"800000",
        
"mediumauqamarine":"66CDAA""mediumblue":"0000CD""mediumorchid":"BA55D3",
        
"mediumpurple":"9370D8""mediumseagreen":"3CB371""mediumslateblue":"7B68EE",
        
"mediumspringgreen":"00FA9A""mediumturquoise":"48D1CC""mediumvioletred":"C71585",
        
"midnightblue":"191970""mintcream":"F5FFFA""mistyrose":"FFE4E1",
        
"moccasin":"FFE4B5""navajowhite":"FFDEAD""navy":"000080",
        
"oldlace":"FDF5E6""olive":"808000""olivedrab":"688E23",
        
"orange":"FFA500""orangered":"FF4500""orchid":"DA70D6",
        
"palegoldenrod":"EEE8AA""palegreen":"98FB98""paleturquoise":"AFEEEE",
        
"palevioletred":"D87093""papayawhip":"FFEFD5""peachpuff":"FFDAB9",
        
"peru":"CD853F""pink":"FFC0CB""plum":"DDA0DD",
        
"powderblue":"B0E0E6""purple":"800080""red":"F00",
        
"rosybrown":"BC8F8F""royalblue":"4169E1""saddlebrown":"8B4513",
        
"salmon":"FA8072""sandybrown":"F4A460""seagreen":"2E8B57",
        
"seashell":"FFF5EE""sienna":"A0522D""silver":"C0C0C0",
        
"skyblue":"87CEEB""slateblue":"6A5ACD""slategray":"708090",
        
"snow":"FFFAFA""springgreen":"00FF7F""steelblue":"4682B4",
        
"tan":"D2B48C""teal":"008080""thistle":"D8BFD8",
        
"tomato":"FF6347""turquoise":"40E0D0""violet":"EE82EE",
        
"wheat":"F5DEB3""white":"FFF""whitesmoke":"F5F5F5",
        
"yellow":"FF0""yellowgreen":"9ACD32"
    
};

    
Color.prototype = {
        
/**
         * @private
         */
        
parse: function() {
            if( !
this._color ) {
                var 
me this,
                    
me.val,
                    
vLower,
                    
v.matchColor.rgbaRE );
                if( 
) {
                    
me._color 'rgb(' m[1] + ',' m[2] + ',' m[3] + ')';
                    
me._alpha parseFloatm[4] );
                }
                else {
                    if( ( 
vLower v.toLowerCase() ) in Color.names ) {
                        
'#' Color.names[vLower];
                    }
                    
me._color v;
                    
me._alpha = ( === 'transparent' );
                }
            }
        },

        
/**
         * Retrieve the value of the color in a format usable by IE natively. This will be the same as
         * the raw input value, except for rgba values which will be converted to an rgb value.
         * @param {Element} el The context element, used to get 'currentColor' keyword value.
         * @return {string} Color value
         */
        
colorValue: function( el ) {
            
this.parse();
            return 
this._color === 'currentColor' el.currentStyle.color this._color;
        },

        
/**
         * Retrieve the alpha value of the color. Will be 1 for all values except for rgba values
         * with an alpha component.
         * @return {number} The alpha value, from 0 to 1.
         */
        
alpha: function() {
            
this.parse();
            return 
this._alpha;
        }
    };


    
/**
     * Retrieve a PIE.Color instance for the given value. A shared singleton instance is returned for each unique value.
     * @param {string} val The CSS string representing the color. It is assumed that this will already have
     *                 been validated as a valid color syntax.
     */
    
PIE.getColor = function(val) {
        return 
instancesval ] || ( instancesval ] = new Colorval ) );
    };

    return 
Color;
})();
/**
 * A tokenizer for CSS value strings.
 * @constructor
 * @param {string} css The CSS value string
 */
PIE.Tokenizer = (function() {
    function 
Tokenizercss ) {
        
this.css css;
        
this.ch 0;
        
this.tokens = [];
        
this.tokenIndex 0;
    }

    
/**
     * Enumeration of token type constants.
     * @enum {number}
     */
    
var Type Tokenizer.Type = {
        
ANGLE1,
        
CHARACTER2,
        
COLOR4,
        
DIMEN8,
        FUNCTION: 
16,
        
IDENT32,
        
LENGTH64,
        
NUMBER128,
        
OPERATOR256,
        
PERCENT512,
        
STRING1024,
        
URL2048
    
};

    
/**
     * A single token
     * @constructor
     * @param {number} type The type of the token - see PIE.Tokenizer.Type
     * @param {string} value The value of the token
     */
    
Tokenizer.Token = function( typevalue ) {
        
this.tokenType type;
        
this.tokenValue value;
    };
    
Tokenizer.Token.prototype = {
        
isLength: function() {
            return 
this.tokenType Type.LENGTH || ( this.tokenType Type.NUMBER && this.tokenValue === '0' );
        },
        
isLengthOrPercent: function() {
            return 
this.isLength() || this.tokenType Type.PERCENT;
        }
    };

    
Tokenizer.prototype = {
        
whitespace: /s/,
        
number: /^[+-]?(d*.)?d+/,
        
url: /^url(s*("([^"]*)"|'([^']*)'|([!#$%&*-~]*))s*)/i,
        ident: /^-?[_a-z][w-]*/i,
        string: /^("
([^"]*)"|'([^']*)')/,
        operator: /^[/,]/,
        hash: /^#[w]+/,
        hashColor: /^#([da-f]{6}|[da-f]{3})/i,

        unitTypes: {
            '
px': Type.LENGTH, 'em': Type.LENGTH, 'ex': Type.LENGTH,
            '
mm': Type.LENGTH, 'cm': Type.LENGTH, 'in': Type.LENGTH,
            '
pt': Type.LENGTH, 'pc': Type.LENGTH,
            '
deg': Type.ANGLE, 'rad': Type.ANGLE, 'grad': Type.ANGLE
        },

        colorFunctions: {
            '
rgb': 1, 'rgba': 1, 'hsl': 1, 'hsla': 1
        },


        /**
         * Advance to and return the next token in the CSS string. If the end of the CSS string has
         * been reached, null will be returned.
         * @param {boolean} forget - if true, the token will not be stored for the purposes of backtracking with prev().
         * @return {PIE.Tokenizer.Token}
         */
        next: function( forget ) {
            var css, ch, firstChar, match, val,
                me = this;

            function newToken( type, value ) {
                var tok = new Tokenizer.Token( type, value );
                if( !forget ) {
                    me.tokens.push( tok );
                    me.tokenIndex++;
                }
                return tok;
            }
            function failure() {
                me.tokenIndex++;
                return null;
            }

            // In case we previously backed up, return the stored token in the next slot
            if( this.tokenIndex < this.tokens.length ) {
                return this.tokens[ this.tokenIndex++ ];
            }

            // Move past leading whitespace characters
            while( this.whitespace.test( this.css.charAt( this.ch ) ) ) {
                this.ch++;
            }
            if( this.ch >= this.css.length ) {
                return failure();
            }

            ch = this.ch;
            css = this.css.substring( this.ch );
            firstChar = css.charAt( 0 );
            switch( firstChar ) {
                case '
#':
                    
if( match css.matchthis.hashColor ) ) {
                        
this.ch += match[0].length;
                        return 
newTokenType.COLORmatch[0] );
                    }
                    break;

                case 
'"':
                case 
"'":
                    if( 
match css.matchthis.string ) ) {
                        
this.ch += match[0].length;
                        return 
newTokenType.STRINGmatch[2] || match[3] || '' );
                    }
                    break;

                case 
"/":
                case 
",":
                    
this.ch++;
                    return 
newTokenType.OPERATORfirstChar );

                case 
'u':
                    if( 
match css.matchthis.url ) ) {
                        
this.ch += match[0].length;
                        return 
newTokenType.URLmatch[2] || match[3] || match[4] || '' );
                    }
            }

            
// Numbers and values starting with numbers
            
if( match css.matchthis.number ) ) {
                
val match[0];
                
this.ch += val.length;

                
// Check if it is followed by a unit
                
if( css.charAtval.length ) === '%' ) {
                    
this.ch++;
                    return 
newTokenType.PERCENTval '%' );
                }
                if( 
match css.substringval.length ).matchthis.ident ) ) {
                    
val += match[0];
                    
this.ch += match[0].length;
                    return 
newTokenthis.unitTypesmatch[0].toLowerCase() ] || Type.DIMENval );
                }

                
// Plain ol' number
                
return newTokenType.NUMBERval );
            }

            
// Identifiers
            
if( match css.matchthis.ident ) ) {
                
val match[0];
                
this.ch += val.length;

                
// Named colors
                
if( val.toLowerCase() in PIE.Color.names || val === 'currentColor' || val === 'transparent' ) {
                    return 
newTokenType.COLORval );
                }

                
// Functions
                
if( css.charAtval.length ) === '(' ) {
                    
this.ch++;

                    
// Color values in function format: rgb, rgba, hsl, hsla
                    
if( val.toLowerCase() in this.colorFunctions ) {
                        function 
isNumtok ) {
                            return 
tok && tok.tokenType Type.NUMBER;
                        }
                        function 
isNumOrPcttok ) {
                            return 
tok && ( tok.tokenType & ( Type.NUMBER Type.PERCENT ) );
                        }
                        function 
isValuetokval ) {
                            return 
tok && tok.tokenValue === val;
                        }
                        function 
next() {
                            return 
me.next);
                        }

                        if( ( 
val.charAt(0) === 'r' isNumOrPctnext() ) : isNumnext() ) ) &&
                            
isValuenext(), ',' ) &&
                            
isNumOrPctnext() ) &&
                            
isValuenext(), ',' ) &&
                            
isNumOrPctnext() ) &&
                            ( 
val === 'rgb' || val === 'hsa' || (
                                
isValuenext(), ',' ) &&
                                
isNumnext() )
                            ) ) &&
                            
isValuenext(), ')' ) ) {
                            return 
newTokenType.COLORthis.css.substringchthis.ch ) );
                        }
                        return 
failure();
                    }

                    return 
newTokenType.FUNCTION, val );
                }

                
// Other identifier
                
return newTokenType.IDENTval );
            }

            
// Standalone character
            
this.ch++;
            return 
newTokenType.CHARACTERfirstChar );
        },

        
/**
         * Determine whether there is another token
         * @return {boolean}
         */
        
hasNext: function() {
            var 
next this.next();
            
this.prev();
            return !!
next;
        },

        
/**
         * Back up and return the previous token
         * @return {PIE.Tokenizer.Token}
         */
        
prev: function() {
            return 
this.tokensthis.tokenIndex-- - ];
        },

        
/**
         * Retrieve all the tokens in the CSS string
         * @return {Array.<PIE.Tokenizer.Token>}
         */
        
all: function() {
            while( 
this.next() ) {}
            return 
this.tokens;
        },

        
/**
         * Return a list of tokens from the current position until the given function returns
         * true. The final token will not be included in the list.
         * @param {function():boolean} func - test function
         * @param {boolean} require - if true, then if the end of the CSS string is reached
         *        before the test function returns true, null will be returned instead of the
         *        tokens that have been found so far.
         * @return {Array.<PIE.Tokenizer.Token>}
         */
        
until: function( func, require ) {
            var list = [], 
thit;
            while( 
this.next() ) {
                if( 
func) ) {
                    
hit true;
                    
this.prev();
                    break;
                }
                list.
push);
            }
            return require && !
hit null : list;
        }
    };

    return 
Tokenizer;
})();
/**
 * Handles calculating, caching, and detecting changes to size and position of the element.
 * @constructor
 * @param {Element} el the target element
 */
PIE.BoundsInfo = function( el ) {
    
this.targetElement el;
};
PIE.BoundsInfo.prototype = {

    
_locked0,

    
positionChanged: function() {
        var 
last this._lastBounds,
            
bounds;
        return !
last || ( ( bounds this.getBounds() ) && ( last.!== bounds.|| last.!== bounds.) );
    },

    
sizeChanged: function() {
        var 
last this._lastBounds,
            
bounds;
        return !
last || ( ( bounds this.getBounds() ) && ( last.!== bounds.|| last.!== bounds.) );
    },

    
getLiveBounds: function() {
        var 
el this.targetElement,
            
rect el.getBoundingClientRect(),
            
isIE9 PIE.ieDocMode === 9,
            
isIE7 PIE.ieVersion === 7,
            
width rect.right rect.left;
        return {
            
xrect.left,
            
yrect.top,
            
// In some cases scrolling the page will cause IE9 to report incorrect dimensions
            // in the rect returned by getBoundingClientRect, so we must query offsetWidth/Height
            // instead. Also IE7 is inconsistent in using logical vs. device pixels in measurements
            // so we must calculate the ratio and use it in certain places as a position adjustment.
            
wisIE9 || isIE7 el.offsetWidth width,
            
hisIE9 || isIE7 el.offsetHeight rect.bottom rect.top,
            
logicalZoomRatio: ( isIE7 && width ) ? el.offsetWidth width 1
        
};
    },

    
getBounds: function() {
        return 
this._locked 
                ( 
this._lockedBounds || ( this._lockedBounds this.getLiveBounds() ) ) :
                
this.getLiveBounds();
    },

    
hasBeenQueried: function() {
        return !!
this._lastBounds;
    },

    
lock: function() {
        ++
this._locked;
    },

    
unlock: function() {
        if( !--
this._locked ) {
            if( 
this._lockedBounds this._lastBounds this._lockedBounds;
            
this._lockedBounds null;
        }
    }

};
(function() {

function 
cacheWhenLockedfn ) {
    var 
uid PIE.Util.getUIDfn );
    return function() {
        if( 
this._locked ) {
            var 
cache this._lockedValues || ( this._lockedValues = {} );
            return ( 
uid in cache ) ? cacheuid ] : ( cacheuid ] = fn.callthis ) );
        } else {
            return 
fn.callthis );
        }
    }
}


PIE.StyleInfoBase = {

    
_locked0,

    
/**
     * Create a new StyleInfo class, with the standard constructor, and augmented by
     * the StyleInfoBase's members.
     * @param proto
     */
    
newStyleInfo: function( proto ) {
        function 
StyleInfoel ) {
            
this.targetElement el;
            
this._lastCss this.getCss();
        }
        
PIE.Util.mergeStyleInfo.prototypePIE.StyleInfoBaseproto );
        
StyleInfo._propsCache = {};
        return 
StyleInfo;
    },

    
/**
     * Get an object representation of the target CSS style, caching it for each unique
     * CSS value string.
     * @return {Object}
     */
    
getProps: function() {
        var 
css this.getCss(),
            
cache this.constructor._propsCache;
        return 
css ? ( css in cache cachecss ] : ( cachecss ] = this.parseCsscss ) ) ) : null;
    },

    
/**
     * Get the raw CSS value for the target style
     * @return {string}
     */
    
getCsscacheWhenLocked( function() {
        var 
el this.targetElement,
            
ctor this.constructor,
            
el.style,
            
cs el.currentStyle,
            
cssProp this.cssProperty,
            
styleProp this.styleProperty,
            
prefixedCssProp ctor._prefixedCssProp || ( ctor._prefixedCssProp PIE.CSS_PREFIX cssProp ),
            
prefixedStyleProp ctor._prefixedStyleProp || ( ctor._prefixedStyleProp PIE.STYLE_PREFIX styleProp.charAt(0).toUpperCase() + styleProp.substring(1) );
        return 
sprefixedStyleProp ] || cs.getAttributeprefixedCssProp ) || sstyleProp ] || cs.getAttributecssProp );
    } ),

    
/**
     * Determine whether the target CSS style is active.
     * @return {boolean}
     */
    
isActivecacheWhenLocked( function() {
        return !!
this.getProps();
    } ),

    
/**
     * Determine whether the target CSS style has changed since the last time it was used.
     * @return {boolean}
     */
    
changedcacheWhenLocked( function() {
        var 
currentCss this.getCss(),
            
changed currentCss !== this._lastCss;
        
this._lastCss currentCss;
        return 
changed;
    } ),

    
cacheWhenLockedcacheWhenLocked,

    
lock: function() {
        ++
this._locked;
    },

    
unlock: function() {
        if( !--
this._locked ) {
            
delete this._lockedValues;
        }
    }
};

})();
/**
 * Handles parsing, caching, and detecting changes to background (and -pie-background) CSS
 * @constructor
 * @param {Element} el the target element
 */
PIE.BackgroundStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
cssPropertyPIE.CSS_PREFIX 'background',
    
stylePropertyPIE.STYLE_PREFIX 'Background',

    
attachIdents: { 'scroll':1'fixed':1'local':},
    
repeatIdents: { 'repeat-x':1'repeat-y':1'repeat':1'no-repeat':},
    
originAndClipIdents: { 'padding-box':1'border-box':1'content-box':},
    
positionIdents: { 'top':1'right':1'bottom':1'left':1'center':},
    
sizeIdents: { 'contain':1'cover':},
    
propertyNames: {
        
CLIP'backgroundClip',
        
COLOR'backgroundColor',
        
IMAGE'backgroundImage',
        
ORIGIN'backgroundOrigin',
        
POSITION'backgroundPosition',
        
REPEAT'backgroundRepeat',
        
SIZE'backgroundSize'
    
},

    
/**
     * 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>,
     *     bgImages: [
     *         {
     *             imgType: 'image',
     *             imgUrl: 'image.png',
     *             imgRepeat: <'no-repeat' | 'repeat-x' | 'repeat-y' | 'repeat'>,
     *             bgPosition: <PIE.BgPosition>,
     *             bgAttachment: <'scroll' | 'fixed' | 'local'>,
     *             bgOrigin: <'border-box' | 'padding-box' | 'content-box'>,
     *             bgClip: <'border-box' | 'padding-box'>,
     *             bgSize: <PIE.BgSize>,
     *             origString: 'url(img.png) no-repeat top left'
     *         },
     *         {
     *             imgType: '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.targetElement,
            
cs el.currentStyle,
            
tokenizertokenimage,
            
tok_type PIE.Tokenizer.Type,
            
type_operator tok_type.OPERATOR,
            
type_ident tok_type.IDENT,
            
type_color tok_type.COLOR,
            
tokTypetokVal,
            
beginCharIndex 0,
            
positionIdents this.positionIdents,
            
gradientstopwidthheight,
            
props = { bgImages: [] };

        function 
isBgPosTokentoken ) {
            return 
token && token.isLengthOrPercent() || ( token.tokenType type_ident && token.tokenValue in positionIdents );
        }

        function 
sizeTokentoken ) {
            return 
token && ( ( token.isLengthOrPercent() && PIE.getLengthtoken.tokenValue ) ) || ( token.tokenValue === 'auto' && 'auto' ) );
        }

        
// If the CSS3-specific -pie-background property is present, parse it
        
if( this.getCss3() ) {
            
tokenizer = new PIE.Tokenizercss );
            
image = {};

            while( 
token tokenizer.next() ) {
                
tokType token.tokenType;
                
tokVal token.tokenValue;

                if( !
image.imgType && tokType tok_type.FUNCTION && tokVal === 'linear-gradient' ) {
                    
gradient = { stops: [], imgTypetokVal };
                    
stop = {};
                    while( 
token tokenizer.next() ) {
                        
tokType token.tokenType;
                        
tokVal token.tokenValue;

                        
// 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.pushstop );
                            }
                            if( 
gradient.stops.length ) {
                                
PIE.Util.mergeimagegradient );
                            }
                            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.tokenType !== type_operator ) {
                                    break; 
//fail
                                
}
                                
tokenizer.next();
                            }

                            
stop = {
                                
colorPIE.getColortokVal )
                            };
                            
// check for offset following color
                            
token tokenizer.next();
                            if( 
token.isLengthOrPercent() ) {
                                
stop.offset PIE.getLengthtoken.tokenValue );
                            } 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.Angletoken.tokenValue );
                        }
                        else if( 
isBgPosTokentoken ) && !gradient.gradientStart && !stop.color && !gradient.stops.length ) {
                            
tokenizer.prev();
                            
gradient.gradientStart = new PIE.BgPosition(
                                
tokenizer.until( function( ) {
                                    return !
isBgPosToken);
                                }, 
false )
                            );
                        }
                        else if( 
tokType type_operator && tokVal === ',' ) {
                            if( 
stop.color ) {
                                
gradient.stops.pushstop );
                                
stop = {};
                            }
                        }
                        else {
                            
// Found something we didn't recognize; fail without adding image
                            
break;
                        }
                    }
                }
                else if( !
image.imgType && tokType tok_type.URL ) {
                    
image.imgUrl tokVal;
                    
image.imgType 'image';
                }
                else if( 
isBgPosTokentoken ) && !image.bgPosition ) {
                    
tokenizer.prev();
                    
image.bgPosition = new PIE.BgPosition(
                        
tokenizer.until( function( ) {
                            return !
isBgPosToken);
                        }, 
false )
                    );
                }
                else if( 
tokType type_ident ) {
                    if( 
tokVal in this.repeatIdents && !image.imgRepeat ) {
                        
image.imgRepeat tokVal;
                    }
                    else if( 
tokVal in this.originAndClipIdents && !image.bgOrigin ) {
                        
image.bgOrigin tokVal;
                        if( ( 
token tokenizer.next() ) && ( token.tokenType type_ident ) &&
                            
token.tokenValue in this.originAndClipIdents ) {
                            
image.bgClip token.tokenValue;
                        } else {
                            
image.bgClip tokVal;
                            
tokenizer.prev();
                        }
                    }
                    else if( 
tokVal in this.attachIdents && !image.bgAttachment ) {
                        
image.bgAttachment tokVal;
                    }
                    else {
                        return 
null;
                    }
                }
                else if( 
tokType type_color && !props.color ) {
                    
props.color PIE.getColortokVal );
                }
                else if( 
tokType type_operator && tokVal === '/' && !image.bgSize && image.bgPosition ) {
                    
// background size
                    
token tokenizer.next();
                    if( 
token.tokenType type_ident && token.tokenValue in this.sizeIdents ) {
                        
image.bgSize = new PIE.BgSizetoken.tokenValue );
                    }
                    else if( 
width sizeTokentoken ) ) {
                        
height sizeTokentokenizer.next() );
                        if ( !
height ) {
                            
height width;
                            
tokenizer.prev();
                        }
                        
image.bgSize = new PIE.BgSizewidthheight );
                    }
                    else {
                        return 
null;
                    }
                }
                
// new layer
                
else if( tokType type_operator && tokVal === ',' && image.imgType ) {
                    
image.origString css.substringbeginCharIndextokenizer.ch );
                    
beginCharIndex tokenizer.ch;
                    
props.bgImages.pushimage );
                    
image = {};
                }
                else {
                    
// Found something unrecognized; chuck everything
                    
return null;
                }
            }

            
// leftovers
            
if( image.imgType ) {
                
image.origString css.substringbeginCharIndex );
                
props.bgImages.pushimage );
            }
        }

        
// Otherwise, use the standard background properties; let IE give us the values rather than parsing them
        
else {
            
this.withActualBgPIE.ieDocMode ?
                function() {
                    var 
propNames this.propertyNames,
                        
posX cs[propNames.POSITION 'X'],
                        
posY cs[propNames.POSITION 'Y'],
                        
img cs[propNames.IMAGE],
                        
color cs[propNames.COLOR];

                    if( 
color !== 'transparent' ) {
                        
props.color PIE.getColorcolor )
                    }
                    if( 
img !== 'none' ) {
                        
props.bgImages = [ {
                            
imgType'image',
                            
imgUrl: new PIE.Tokenizerimg ).next().tokenValue,
                            
imgRepeatcs[propNames.REPEAT],
                            
bgPosition: new PIE.BgPosition( new PIE.TokenizerposX ' ' posY ).all() )
                        } ];
                    }
                } :
                function() {
                    var 
propNames this.propertyNames,
                        
splitter = /s*,s*/,
                        
images cs[propNames.IMAGE].splitsplitter ),
                        
color cs[propNames.COLOR],
                        
repeatspositionsoriginsclipssizesilenimagesizeParts;

                    if( 
color !== 'transparent' ) {
                        
props.color PIE.getColorcolor )
                    }

                    
len images.length;
                    if( 
len && images[0] !== 'none' ) {
                        
repeats cs[propNames.REPEAT].splitsplitter );
                        
positions cs[propNames.POSITION].splitsplitter );
                        
origins cs[propNames.ORIGIN].splitsplitter );
                        
clips cs[propNames.CLIP].splitsplitter );
                        
sizes cs[propNames.SIZE].splitsplitter );

                        
props.bgImages = [];
                        for( 
0leni++ ) {
                            
image images];
                            if( 
image && image !== 'none' ) {
                                
sizeParts sizes[i].split' ' );
                                
props.bgImages.push( {
                                    
origStringimage ' ' repeats] + ' ' positions] + ' / ' sizes] + ' ' +
                                                
origins] + ' ' clips],
                                    
imgType'image',
                                    
imgUrl: new PIE.Tokenizerimage ).next().tokenValue,
                                    
imgRepeatrepeats],
                                    
bgPosition: new PIE.BgPosition( new PIE.Tokenizerpositions] ).all() ),
                                    
bgOriginorigins],
                                    
bgClipclips],
                                    
bgSize: new PIE.BgSizesizeParts], sizeParts] )
                                } );
                            }
                        }
                    }
                }
            );
        }

        return ( 
props.color || props.bgImages[0] ) ? props null;
    },

    
/**
     * 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 
isIE9 PIE.ieDocMode 8,
            
propNames this.propertyNames,
            
rs this.targetElement.runtimeStyle,
            
rsImage rs[propNames.IMAGE],
            
rsColor rs[propNames.COLOR],
            
rsRepeat rs[propNames.REPEAT],
            
rsCliprsOriginrsSizersPositionret;

        if( 
rsImage rs[propNames.IMAGE] = '';
        if( 
rsColor rs[propNames.COLOR] = '';
        if( 
rsRepeat rs[propNames.REPEAT] = '';
        if( 
isIE9 ) {
            
rsClip rs[propNames.CLIP];
            
rsOrigin rs[propNames.ORIGIN];
            
rsPosition rs[propNames.POSITION];
            
rsSize rs[propNames.SIZE];
            if( 
rsClip rs[propNames.CLIP] = '';
            if( 
rsOrigin rs[propNames.ORIGIN] = '';
            if( 
rsPosition rs[propNames.POSITION] = '';
            if( 
rsSize rs[propNames.SIZE] = '';
        }

        
ret fn.callthis );

        if( 
rsImage rs[propNames.IMAGE] = rsImage;
        if( 
rsColor rs[propNames.COLOR] = rsColor;
        if( 
rsRepeat rs[propNames.REPEAT] = rsRepeat;
        if( 
isIE9 ) {
            if( 
rsClip rs[propNames.CLIP] = rsClip;
            if( 
rsOrigin rs[propNames.ORIGIN] = rsOrigin;
            if( 
rsPosition rs[propNames.POSITION] = rsPosition;
            if( 
rsSize rs[propNames.SIZE] = rsSize;
        }

        return 
ret;
    },

    
getCssPIE.StyleInfoBase.cacheWhenLocked( function() {
        return 
this.getCss3() ||
               
this.withActualBg( function() {
                   var 
cs this.targetElement.currentStyle,
                       
propNames this.propertyNames;
                   return 
cs[propNames.COLOR] + ' ' cs[propNames.IMAGE] + ' ' cs[propNames.REPEAT] + ' ' +
                   
cs[propNames.POSITION 'X'] + ' ' cs[propNames.POSITION 'Y'];
               } );
    } ),

    
getCss3PIE.StyleInfoBase.cacheWhenLocked( function() {
        var 
el this.targetElement;
        return 
el.stylethis.styleProperty ] || el.currentStyle.getAttributethis.cssProperty );
    } ),

    
/**
     * Tests if style.PiePngFix or the -pie-png-fix property is set to true in IE6.
     */
    
isPngFix: function() {
        var 
val 0el;
        if( 
PIE.ieVersion ) {
            
el this.targetElement;
            
val = ( '' + ( el.stylePIE.STYLE_PREFIX 'PngFix' ] || el.currentStyle.getAttributePIE.CSS_PREFIX 'png-fix' ) ) === 'true' );
        }
        return 
val;
    },
    
    
/**
     * 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 either the -pie-background override property is present
     * and parses successfully or '-pie-png-fix' is set to true in IE6.
     */
    
isActivePIE.StyleInfoBase.cacheWhenLocked( function() {
        return (
this.getCss3() || this.isPngFix()) && !!this.getProps();
    } )

} );
/**
 * Handles parsing, caching, and detecting changes to border CSS
 * @constructor
 * @param {Element} el the target element
 */
PIE.BorderStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
sides: [ 'Top''Right''Bottom''Left' ],
    
namedWidths: {
        
'thin''1px',
        
'medium''3px',
        
'thick''5px'
    
},

    
parseCss: function( css ) {
        var 
= {},
            
= {},
            
= {},
            
active false,
            
colorsSame true,
            
stylesSame true,
            
widthsSame true;

        
this.withActualBorder( function() {
            var 
el this.targetElement,
                
cs el.currentStyle,
                
0,
                
stylecolorwidthlastStylelastColorlastWidthsideltr;
            for( ; 
4i++ ) {
                
side this.sides];

                
ltr side.charAt(0).toLowerCase();
                
style sltr ] = cs'border' side 'Style' ];
                
color cs'border' side 'Color' ];
                
width cs'border' side 'Width' ];

                if( 
) {
                    if( 
style !== lastStyle ) { stylesSame false; }
                    if( 
color !== lastColor ) { colorsSame false; }
                    if( 
width !== lastWidth ) { widthsSame false; }
                }
                
lastStyle style;
                
lastColor color;
                
lastWidth width;

                
cltr ] = PIE.getColorcolor );

                
width wltr ] = PIE.getLengthsltr ] === 'none' '0' : ( this.namedWidthswidth ] || width ) );
                if( 
width.pixelsthis.targetElement ) > ) {
                    
active true;
                }
            }
        } );

        return 
active ? {
            
widthsw,
            
styless,
            
colorsc,
            
widthsSamewidthsSame,
            
colorsSamecolorsSame,
            
stylesSamestylesSame
        
} : null;
    },

    
getCssPIE.StyleInfoBase.cacheWhenLocked( function() {
        var 
el this.targetElement,
            
cs el.currentStyle,
            
css;

        
// Don't redraw or hide borders for cells in border-collapse:collapse tables
        
if( !( el.tagName in PIE.tableCellTags && el.offsetParent.currentStyle.borderCollapse === 'collapse' ) ) {
            
this.withActualBorder( function() {
                
css cs.borderWidth '|' cs.borderStyle '|' cs.borderColor;
            } );
        }
        return 
css;
    } ),

    
/**
     * Execute a function with the actual border styles (not overridden with runtimeStyle
     * properties set by the renderers) available via currentStyle.
     * @param fn
     */
    
withActualBorder: function( fn ) {
        var 
rs this.targetElement.runtimeStyle,
            
rsWidth rs.borderWidth,
            
rsColor rs.borderColor,
            
ret;

        if( 
rsWidth rs.borderWidth '';
        if( 
rsColor rs.borderColor '';

        
ret fn.callthis );

        if( 
rsWidth rs.borderWidth rsWidth;
        if( 
rsColor rs.borderColor rsColor;

        return 
ret;
    }

} );
/**
 * Handles parsing, caching, and detecting changes to border-radius CSS
 * @constructor
 * @param {Element} el the target element
 */
(function() {

PIE.BorderRadiusStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
cssProperty'border-radius',
    
styleProperty'borderRadius',

    
parseCss: function( css ) {
        var 
nullxy,
            
tokenizertokenlength,
            
hasNonZero false;

        if( 
css ) {
            
tokenizer = new PIE.Tokenizercss );

            function 
collectLengths() {
                var 
arr = [], num;
                while( ( 
token tokenizer.next() ) && token.isLengthOrPercent() ) {
                    
length PIE.getLengthtoken.tokenValue );
                    
num length.getNumber();
                    if( 
num ) {
                        return 
null;
                    }
                    if( 
num ) {
                        
hasNonZero true;
                    }
                    
arr.pushlength );
                }
                return 
arr.length && arr.length ? {
                        
'tl'arr[0],
                        
'tr'arr[1] || arr[0],
                        
'br'arr[2] || arr[0],
                        
'bl'arr[3] || arr[1] || arr[0]
                    } : 
null;
            }

            
// Grab the initial sequence of lengths
            
if( collectLengths() ) {
                
// See if there is a slash followed by more lengths, for the y-axis radii
                
if( token ) {
                    if( 
token.tokenType PIE.Tokenizer.Type.OPERATOR && token.tokenValue === '/' ) {
                        
collectLengths();
                    }
                } else {
                    
x;
                }

                
// Treat all-zero values the same as no value
                
if( hasNonZero && && ) {
                    
= { xx};
                }
            }
        }

        return 
p;
    }
} );

var 
zero PIE.getLength'0' ),
    
zeros = { 'tl'zero'tr'zero'br'zero'bl'zero };
PIE.BorderRadiusStyleInfo.ALL_ZERO = { xzerosyzeros };

})();
/**
 * Handles parsing, caching, and detecting changes to border-image CSS
 * @constructor
 * @param {Element} el the target element
 */
PIE.BorderImageStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
cssProperty'border-image',
    
styleProperty'borderImage',

    
repeatIdents: { 'stretch':1'round':1'repeat':1'space':},

    
parseCss: function( css ) {
        var 
nulltokenizertokentypevalue,
            
sliceswidthsoutsets,
            
slashCount 0,
            
Type PIE.Tokenizer.Type,
            
IDENT Type.IDENT,
            
NUMBER Type.NUMBER,
            
PERCENT Type.PERCENT;

        if( 
css ) {
            
tokenizer = new PIE.Tokenizercss );
            
= {};

            function 
isSlashtoken ) {
                return 
token && ( token.tokenType Type.OPERATOR ) && ( token.tokenValue === '/' );
            }

            function 
isFillIdenttoken ) {
                return 
token && ( token.tokenType IDENT ) && ( token.tokenValue === 'fill' );
            }

            function 
collectSlicesEtc() {
                
slices tokenizer.until( function( tok ) {
                    return !( 
tok.tokenType & ( NUMBER PERCENT ) );
                } );

                if( 
isFillIdenttokenizer.next() ) && !p.fill ) {
                    
p.fill true;
                } else {
                    
tokenizer.prev();
                }

                if( 
isSlashtokenizer.next() ) ) {
                    
slashCount++;
                    
widths tokenizer.until( function( token ) {
                        return !
token.isLengthOrPercent() && !( ( token.tokenType IDENT ) && token.tokenValue === 'auto' );
                    } );

                    if( 
isSlashtokenizer.next() ) ) {
                        
slashCount++;
                        
outsets tokenizer.until( function( token ) {
                            return !
token.isLength();
                        } );
                    }
                } else {
                    
tokenizer.prev();
                }
            }

            while( 
token tokenizer.next() ) {
                
type token.tokenType;
                
value token.tokenValue;

                
// Numbers and/or 'fill' keyword: slice values. May be followed optionally by width values, followed optionally by outset values
                
if( type & ( NUMBER PERCENT ) && !slices ) {
                    
tokenizer.prev();
                    
collectSlicesEtc();
                }
                else if( 
isFillIdenttoken ) && !p.fill ) {
                    
p.fill true;
                    
collectSlicesEtc();
                }

                
// Idents: one or values for 'repeat'
                
else if( ( type IDENT ) && this.repeatIdents[value] && !p.repeat ) {
                    
p.repeat = { hvalue };
                    if( 
token tokenizer.next() ) {
                        if( ( 
token.tokenType IDENT ) && this.repeatIdents[token.tokenValue] ) {
                            
p.repeat.token.tokenValue;
                        } else {
                            
tokenizer.prev();
                        }
                    }
                }

                
// URL of the image
                
else if( ( type Type.URL ) && !p.src ) {
                    
p.src =  value;
                }

                
// Found something unrecognized; exit.
                
else {
                    return 
null;
                }
            }

            
// Validate what we collected
            
if( !p.src || !slices || slices.length || slices.length ||
                ( 
widths && widths.length ) || ( slashCount === && widths.length ) ||
                ( 
outsets && outsets.length ) || ( slashCount === && outsets.length ) ) {
                return 
null;
            }

            
// Fill in missing values
            
if( !p.repeat ) {
                
p.repeat = { h'stretch' };
            }
            if( !
p.repeat.) {
                
p.repeat.p.repeat.h;
            }

            function 
distributeSidestokensconvertFn ) {
                return {
                    
't'convertFntokens[0] ),
                    
'r'convertFntokens[1] || tokens[0] ),
                    
'b'convertFntokens[2] || tokens[0] ),
                    
'l'convertFntokens[3] || tokens[1] || tokens[0] )
                };
            }

            
p.slice distributeSidesslices, function( tok ) {
                return 
PIE.getLength( ( tok.tokenType NUMBER ) ? tok.tokenValue 'px' tok.tokenValue );
            } );

            if( 
widths && widths[0] ) {
                
p.widths distributeSideswidths, function( tok ) {
                    return 
tok.isLengthOrPercent() ? PIE.getLengthtok.tokenValue ) : tok.tokenValue;
                } );
            }

            if( 
outsets && outsets[0] ) {
                
p.outset distributeSidesoutsets, function( tok ) {
                    return 
tok.isLength() ? PIE.getLengthtok.tokenValue ) : tok.tokenValue;
                } );
            }
        }

        return 
p;
    }

} );
/**
 * Handles parsing, caching, and detecting changes to box-shadow CSS
 * @constructor
 * @param {Element} el the target element
 */
PIE.BoxShadowStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
cssProperty'box-shadow',
    
styleProperty'boxShadow',

    
parseCss: function( css ) {
        var 
props,
            
getLength PIE.getLength,
            
Type PIE.Tokenizer.Type,
            
tokenizer;

        if( 
css ) {
            
tokenizer = new PIE.Tokenizercss );
            
props = { outset: [], inset: [] };

            function 
parseItem() {
                var 
tokentypevaluecolorlengthsinsetlen;

                while( 
token tokenizer.next() ) {
                    
value token.tokenValue;
                    
type token.tokenType;

                    if( 
type Type.OPERATOR && value === ',' ) {
                        break;
                    }
                    else if( 
token.isLength() && !lengths ) {
                        
tokenizer.prev();
                        
lengths tokenizer.until( function( token ) {
                            return !
token.isLength();
                        } );
                    }
                    else if( 
type Type.COLOR && !color ) {
                        
color value;
                    }
                    else if( 
type Type.IDENT && value === 'inset' && !inset ) {
                        
inset true;
                    }
                    else { 
//encountered an unrecognized token; fail.
                        
return false;
                    }
                }

                
len lengths && lengths.length;
                if( 
len && len ) {
                    ( 
inset props.inset props.outset ).push( {
                        
xOffsetgetLengthlengths[0].tokenValue ),
                        
yOffsetgetLengthlengths[1].tokenValue ),
                        
blurgetLengthlengths[2] ? lengths[2].tokenValue '0' ),
                        
spreadgetLengthlengths[3] ? lengths[3].tokenValue '0' ),
                        
colorPIE.getColorcolor || 'currentColor' )
                    } );
                    return 
true;
                }
                return 
false;
            }

            while( 
parseItem() ) {}
        }

        return 
props && ( props.inset.length || props.outset.length ) ? props null;
    }
} );
/**
 * Retrieves the state of the element's visibility and display
 * @constructor
 * @param {Element} el the target element
 */
PIE.VisibilityStyleInfo PIE.StyleInfoBase.newStyleInfo( {

    
getCssPIE.StyleInfoBase.cacheWhenLocked( function() {
        var 
cs this.targetElement.currentStyle;
        return 
cs.visibility '|' cs.display;
    } ),

    
parseCss: function() {
        var 
el this.targetElement,
            
rs el.runtimeStyle,
            
cs el.currentStyle,
            
rsVis rs.visibility,
            
csVis;

        
rs.visibility '';
        
csVis cs.visibility;
        
rs.visibility rsVis;

        return {
            
visiblecsVis !== 'hidden',
            
displayedcs.display !== 'none'
        
}
    },

    
/**
     * Always return false for isActive, since this property alone will not trigger
     * a renderer to do anything.
     */
    
isActive: function() {
        return 
false;
    }

} );
PIE.RendererBase = {

    
/**
     * Create a new Renderer class, with the standard constructor, and augmented by
     * the RendererBase's members.
     * @param proto
     */
    
newRenderer: function( proto ) {
        function 
RendererelboundsInfostyleInfosparent ) {
            
this.targetElement el;
            
this.boundsInfo boundsInfo;
            
this.styleInfos styleInfos;
            
this.parent parent;
        }
        
PIE.Util.mergeRenderer.prototypePIE.RendererBaseproto );
        return 
Renderer;
    },

    
/**
     * Flag indicating the element has already been positioned at least once.
     * @type {boolean}
     */
    
isPositionedfalse,

    
/**
     * Determine if the renderer needs to be updated
     * @return {boolean}
     */
    
needsUpdate: function() {
        return 
false;
    },

    
/**
     * Run any preparation logic that would affect the main update logic of this
     * renderer or any of the other renderers, e.g. things that might affect the
     * element's size or style properties.
     */
    
prepareUpdatePIE.emptyFn,

    
/**
     * Tell the renderer to update based on modified properties
     */
    
updateProps: function() {
        
this.destroy();
        if( 
this.isActive() ) {
            
this.draw();
        }
    },

    
/**
     * Tell the renderer to update based on modified element position
     */
    
updatePos: function() {
        
this.isPositioned true;
    },

    
/**
     * Tell the renderer to update based on modified element dimensions
     */
    
updateSize: function() {
        if( 
this.isActive() ) {
            
this.draw();
        } else {
            
this.destroy();
        }
    },


    
/**
     * Add a layer element, with the given z-order index, to the renderer's main box element. We can't use
     * z-index because that breaks when the root rendering box's z-index is 'auto' in IE8+ standards mode.
     * So instead we make sure they are inserted into the DOM in the correct order.
     * @param {number} index
     * @param {Element} el
     */
    
addLayer: function( indexel ) {
        
this.removeLayerindex );
        for( var 
layers this._layers || ( this._layers = [] ), index 1len layers.lengthlayerleni++ ) {
            
layer layers[i];
            if( 
layer ) {
                break;
            }
        }
        
layers[index] = el;
        
this.getBox().insertBeforeellayer || null );
    },

    
/**
     * Retrieve a layer element by its index, or null if not present
     * @param {number} index
     * @return {Element}
     */
    
getLayer: function( index ) {
        var 
layers this._layers;
        return 
layers && layers[index] || null;
    },

    
/**
     * Remove a layer element by its index
     * @param {number} index
     */
    
removeLayer: function( index ) {
        var 
layer this.getLayerindex ),
            
box this._box;
        if( 
layer && box ) {
            
box.removeChildlayer );
            
this._layers[index] = null;
        }
    },


    
/**
     * Get a VML shape by name, creating it if necessary.
     * @param {string} name A name identifying the element
     * @param {string=} subElName If specified a subelement of the shape will be created with this tag name
     * @param {Element} parent The parent element for the shape; will be ignored if 'group' is specified
     * @param {number=} group If specified, an ordinal group for the shape. 1 or greater. Groups are rendered
     *                  using container elements in the correct order, to get correct z stacking without z-index.
     */
    
getShape: function( namesubElNameparentgroup ) {
        var 
shapes this._shapes || ( this._shapes = {} ),
            
shape shapesname ],
            
s;

        if( !
shape ) {
            
shape shapesname ] = PIE.Util.createVmlElement'shape' );
            if( 
subElName ) {
                
shape.appendChildshapesubElName ] = PIE.Util.createVmlElementsubElName ) );
            }

            if( 
group ) {
                
parent this.getLayergroup );
                if( !
parent ) {
                    
this.addLayergroupdoc.createElement'group' group ) );
                    
parent this.getLayergroup );
                }
            }

            
parent.appendChildshape );

            
shape.style;
            
s.position 'absolute';
            
s.left s.top 0;
            
s['behavior'] = 'url(#default#VML)';
        }
        return 
shape;
    },

    
/**
     * Delete a named shape which was created by getShape(). Returns true if a shape with the
     * given name was found and deleted, or false if there was no shape of that name.
     * @param {string} name
     * @return {boolean}
     */
    
deleteShape: function( name ) {
        var 
shapes this._shapes,
            
shape shapes && shapesname ];
        if( 
shape ) {
            
shape.parentNode.removeChildshape );
            
delete shapesname ];
        }
        return !!
shape;
    },


    
/**
     * For a given set of border radius length/percentage values, convert them to concrete pixel
     * values based on the current size of the target element.
     * @param {Object} radii
     * @return {Object}
     */
    
getRadiiPixels: function( radii ) {
        var 
el this.targetElement,
            
bounds this.boundsInfo.getBounds(),
            
bounds.w,
            
bounds.h,
            
tlXtlYtrX, trY, brXbrYblXblYf;

        
tlX radii.x['tl'].pixelsel);
        
tlY radii.y['tl'].pixelsel);
        
trX radii.x['tr'].pixelsel);
        trY = 
radii.y['tr'].pixelsel);
        
brX radii.x['br'].pixelsel);
        
brY radii.y['br'].pixelsel);
        
blX radii.x['bl'].pixelsel);
        
blY radii.y['bl'].pixelsel);

        
// If any corner ellipses overlap, reduce them all by the appropriate factor. This formula
        // is taken straight from the CSS3 Backgrounds and Borders spec.
        
Math.min(
            
/ ( tlX trX ),
            
/ ( trY + brY ),
            
/ ( blX brX ),
            
/ ( tlY blY )
        );
        if( 
) {
            
tlX *= f;
            
tlY *= f;
            
trX *= f;
            trY *= 
f;
            
brX *= f;
            
brY *= f;
            
blX *= f;
            
blY *= f;
        }

        return {
            
x: {
                
'tl'tlX,
                
'tr'trX,
                
'br'brX,
                
'bl'blX
            
},
            
y: {
                
'tl'tlY,
                
'tr': trY,
                
'br'brY,
                
'bl'blY
            
}
        }
    },

    
/**
     * Return the VML path string for the element's background box, with corners rounded.
     * @param {Object.<{t:number, r:number, b:number, l:number}>} shrink - if present, specifies number of
     *        pixels to shrink the box path inward from the element's four sides.
     * @param {number=} mult If specified, all coordinates will be multiplied by this number
     * @param {Object=} radii If specified, this will be used for the corner radii instead of the properties
     *        from this renderer's borderRadiusInfo object.
     * @return {string} the VML path
     */
    
getBoxPath: function( shrinkmultradii ) {
        
mult mult || 1;

        var 
rstr,
            
bounds this.boundsInfo.getBounds(),
            
bounds.mult,
            
bounds.mult,
            
radInfo this.styleInfos.borderRadiusInfo,
            
floor Math.floorceil Math.ceil,
            
shrinkT shrink shrink.mult 0,
            
shrinkR shrink shrink.mult 0,
            
shrinkB shrink shrink.mult 0,
            
shrinkL shrink shrink.mult 0,
            
tlXtlYtrX, trY, brXbrYblXblY;

        if( 
radii || radInfo.isActive() ) {
            
this.getRadiiPixelsradii || radInfo.getProps() );

            
tlX r.x['tl'] * mult;
            
tlY r.y['tl'] * mult;
            
trX r.x['tr'] * mult;
            trY = 
r.y['tr'] * mult;
            
brX r.x['br'] * mult;
            
brY r.y['br'] * mult;
            
blX r.x['bl'] * mult;
            
blY r.y['bl'] * mult;

            
str 'm' floorshrinkL ) + ',' floortlY ) +
                
'qy' floortlX ) + ',' floorshrinkT ) +
                
'l' ceiltrX ) + ',' floorshrinkT ) +
                
'qx' ceilshrinkR ) + ',' floor( trY ) +
                
'l' ceilshrinkR ) + ',' ceilbrY ) +
                
'qy' ceilbrX ) + ',' ceilshrinkB ) +
                
'l' floorblX ) + ',' ceilshrinkB ) +
                
'qx' floorshrinkL ) + ',' ceilblY ) + ' x e';
        } else {
            
// simplified path for non-rounded box
            
str 'm' floorshrinkL ) + ',' floorshrinkT ) +
                  
'l' ceilshrinkR ) + ',' floorshrinkT ) +
                  
'l' ceilshrinkR ) + ',' ceilshrinkB ) +
                  
'l' floorshrinkL ) + ',' ceilshrinkB ) +
                  
'xe';
        }
        return 
str;
    },


    
/**
     * Get the container element for the shapes, creating it if necessary.
     */
    
getBox: function() {
        var 
box this.parent.getLayerthis.boxZIndex ), s;

        if( !
box ) {
            
box doc.createElementthis.boxName );
            
box.style;
            
s.position 'absolute';
            
s.top s.left 0;
            
this.parent.addLayerthis.boxZIndexbox );
        }

        return 
box;
    },


    
/**
     * 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. Also, some elements
     * like form buttons require removing the border width altogether, so for those we increase the padding
     * by the border size.
     */
    
hideBorder: function() {
        var 
el this.targetElement,
            
cs el.currentStyle,
            
rs el.runtimeStyle,
            
tag el.tagName,
            
isIE6 PIE.ieVersion === 6,
            
sidessidei;

        if( ( 
isIE6 && ( tag in PIE.childlessElements || tag === 'FIELDSET' ) ) ||
                
tag === 'BUTTON' || ( tag === 'INPUT' && el.type in PIE.inputButtonTypes ) ) {
            
rs.borderWidth '';
            
sides this.styleInfos.borderInfo.sides;
            for( 
sides.lengthi--; ) {
                
side sides];
                
rs'padding' side ] = '';
                
rs'padding' side ] = ( PIE.getLengthcs'padding' side ] ) ).pixelsel ) +
                                         ( 
PIE.getLengthcs'border' side 'Width' ] ) ).pixelsel ) +
                                         ( 
PIE.ieVersion !== && ); //needs an extra horizontal pixel to counteract the extra "inner border" going away
            
}
            
rs.borderWidth 0;
        }
        else if( 
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 !== || el.firstChild.tagName !== 'ie6-mask' ) {
                var 
cont doc.createElement'ie6-mask' ),
                    
cont.stylechild;
                
s.visibility 'visible';
                
s.zoom 1;
                while( 
child el.firstChild ) {
                    
cont.appendChildchild );
                }
                
el.appendChildcont );
                
rs.visibility 'hidden';
            }
        }
        else {
            
rs.borderColor 'transparent';
        }
    },

    
unhideBorder: function() {
        
    },


    
/**
     * Destroy the rendered objects. This is a base implementation which handles common renderer
     * structures, but individual renderers may override as necessary.
     */
    
destroy: function() {
        
this.parent.removeLayerthis.boxZIndex );
        
delete this._shapes;
        
delete this._layers;
    }
};
/**
 * Root renderer; creates the outermost container element and handles keeping it aligned
 * with the target element's size and position.
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 */
PIE.RootRenderer PIE.RendererBase.newRenderer( {

    
isActive: function() {
        var 
children this.childRenderers;
        for( var 
i in children ) {
            if( 
children.hasOwnProperty) && children].isActive() ) {
                return 
true;
            }
        }
        return 
false;
    },

    
needsUpdate: function() {
        return 
this.styleInfos.visibilityInfo.changed();
    },

    
updatePos: function() {
        if( 
this.isActive() ) {
            var 
el this.getPositioningElement(),
                
par el,
                
docEl,
                
parRect,
                
tgtCS el.currentStyle,
                
tgtPos tgtCS.position,
                
boxPos,
                
this.getBox().stylecs,
                
00,
                
elBounds this.boundsInfo.getBounds(),
                
logicalZoomRatio elBounds.logicalZoomRatio;

            if( 
tgtPos === 'fixed' && PIE.ieVersion ) {
                
elBounds.logicalZoomRatio;
                
elBounds.logicalZoomRatio;
                
boxPos tgtPos;
            } else {
                
// Get the element's offsets from its nearest positioned ancestor. Uses
                // getBoundingClientRect for accuracy and speed.
                
do {
                    
par par.offsetParent;
                } while( 
par && ( par.currentStyle.position === 'static' ) );
                if( 
par ) {
                    
parRect par.getBoundingClientRect();
                    
cs par.currentStyle;
                    
= ( elBounds.parRect.left ) * logicalZoomRatio - ( parseFloat(cs.borderLeftWidth) || );
                    
= ( elBounds.parRect.top ) * logicalZoomRatio - ( parseFloat(cs.borderTopWidth) || );
                } else {
                    
docEl doc.documentElement;
                    
= ( elBounds.docEl.scrollLeft docEl.clientLeft ) * logicalZoomRatio;
                    
= ( elBounds.docEl.scrollTop docEl.clientTop ) * logicalZoomRatio;
                }
                
boxPos 'absolute';
            }

            
s.position boxPos;
            
s.left x;
            
s.top y;
            
s.zIndex tgtPos === 'static' ? -tgtCS.zIndex;
            
this.isPositioned true;
        }
    },

    
updateSizePIE.emptyFn,

    
updateVisibility: function() {
        var 
vis this.styleInfos.visibilityInfo.getProps();
        
this.getBox().style.display = ( vis.visible && vis.displayed ) ? '' 'none';
    },

    
updateProps: function() {
        if( 
this.isActive() ) {
            
this.updateVisibility();
        } else {
            
this.destroy();
        }
    },

    
getPositioningElement: function() {
        var 
el this.targetElement;
        return 
el.tagName in PIE.tableCellTags el.offsetParent el;
    },

    
getBox: function() {
        var 
box this._boxel;
        if( !
box ) {
            
el this.getPositioningElement();
            
box this._box doc.createElement'css3-container' );
            
box.style['direction'] = 'ltr'//fix positioning bug in rtl environments

            
this.updateVisibility();

            
el.parentNode.insertBeforeboxel );
        }
        return 
box;
    },

    
finishUpdatePIE.emptyFn,

    
destroy: function() {
        var 
box this._boxpar;
        if( 
box && ( par box.parentNode ) ) {
            
par.removeChildbox );
        }
        
delete this._box;
        
delete this._layers;
    }

} );
/**
 * Renderer for element backgrounds.
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.BackgroundRenderer PIE.RendererBase.newRenderer( {

    
boxZIndex2,
    
boxName'background',

    
needsUpdate: function() {
        var 
si this.styleInfos;
        return 
si.backgroundInfo.changed() || si.borderRadiusInfo.changed();
    },

    
isActive: function() {
        var 
si this.styleInfos;
        return 
si.borderImageInfo.isActive() ||
               
si.borderRadiusInfo.isActive() ||
               
si.backgroundInfo.isActive() ||
               ( 
si.boxShadowInfo.isActive() && si.boxShadowInfo.getProps().inset );
    },

    
/**
     * Draw the shapes
     */
    
draw: function() {
        var 
bounds this.boundsInfo.getBounds();
        if( 
bounds.&& bounds.) {
            
this.drawBgColor();
            
this.drawBgImages();
        }
    },

    
/**
     * Draw the background color shape
     */
    
drawBgColor: function() {
        var 
props this.styleInfos.backgroundInfo.getProps(),
            
bounds this.boundsInfo.getBounds(),
            
el this.targetElement,
            
color props && props.color,
            
shapewhsalpha;

        if( 
color && color.alpha() > ) {
            
this.hideBackground();

            
shape this.getShape'bgColor''fill'this.getBox(), );
            
bounds.w;
            
bounds.h;
            
shape.stroked false;
            
shape.coordsize ',' 2;
            
shape.coordorigin '1,1';
            
shape.path this.getBoxPathnull);
            
shape.style;
            
s.width w;
            
s.height h;
            
shape.fill.color color.colorValueel );

            
alpha color.alpha();
            if( 
alpha ) {
                
shape.fill.opacity alpha;
            }
        } else {
            
this.deleteShape'bgColor' );
        }
    },

    
/**
     * Draw all the background image layers
     */
    
drawBgImages: function() {
        var 
props this.styleInfos.backgroundInfo.getProps(),
            
bounds this.boundsInfo.getBounds(),
            
images props && props.bgImages,
            
imgshapewhsi;

        if( 
images ) {
            
this.hideBackground();

            
bounds.w;
            
bounds.h;

            
images.length;
            while( 
i-- ) {
                
img images[i];
                
shape this.getShape'bgImage' i'fill'this.getBox(), );

                
shape.stroked false;
                
shape.fill.type 'tile';
                
shape.fillcolor 'none';
                
shape.coordsize ',' 2;
                
shape.coordorigin '1,1';
                
shape.path this.getBoxPath0);
                
shape.style;
                
s.width w;
                
s.height h;

                if( 
img.imgType === 'linear-gradient' ) {
                    
this.addLinearGradientshapeimg );
                }
                else {
                    
shape.fill.src img.imgUrl;
                    
this.positionBgImageshape);
                }
            }
        }

        
// Delete any bgImage shapes previously created which weren't used above
        
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( shapeindex ) {
        var 
me this;
        
PIE.Util.withImageSizeshape.fill.src, function( size ) {
            var 
el me.targetElement,
                
bounds me.boundsInfo.getBounds(),
                
elW bounds.w,
                
elH bounds.h;

            
// It's possible that the element dimensions are zero now but weren't when the original
            // update executed, make sure that's not the case to avoid divide-by-zero error
            
if( elW && elH ) {
                var 
fill shape.fill,
                    
si me.styleInfos,
                    
border si.borderInfo.getProps(),
                    
bw border && border.widths,
                    
bwT bw bw['t'].pixelsel ) : 0,
                    
bwR bw bw['r'].pixelsel ) : 0,
                    
bwB bw bw['b'].pixelsel ) : 0,
                    
bwL bw bw['l'].pixelsel ) : 0,
                    
bg si.backgroundInfo.getProps().bgImagesindex ],
                    
bgPos bg.bgPosition bg.bgPosition.coordselelW size.bwL bwRelH size.bwT bwB ) : { x:0y:},
                    
repeat bg.imgRepeat,
                    
pxXpxY,
                    
clipT 0clipL 0,
                    
clipR elW 1clipB elH 1//make sure the default clip region is not inside the box (by a subpixel)
                    
clipAdjust PIE.ieVersion === 1//prior to IE8 requires 1 extra pixel in the image clip region

                // Positioning - find the pixel offset from the top/left and convert to a ratio
                // The position is shifted by half a pixel, to adjust for the half-pixel coordorigin shift which is
                // needed to fix antialiasing but makes the bg image fuzzy.
                
pxX Math.roundbgPos.) + bwL 0.5;
                
pxY Math.roundbgPos.) + bwT 0.5;
                
fill.position = ( pxX elW ) + ',' + ( pxY elH );

                
// Set the size of the image. We have to actually set it to px values otherwise it will not honor
                // the user's browser zoom level and always display at its natural screen size.
                
fill['size']['x'] = 1//Can be any value, just has to be set to "prime" it so the next line works. Weird!
                
fill['size'] = size.'px,' size.'px';

                
// Repeating - clip the image shape
                
if( repeat && repeat !== 'repeat' ) {
                    if( 
repeat === 'repeat-x' || repeat === 'no-repeat' ) {
                        
clipT pxY 1;
                        
clipB pxY size.clipAdjust;
                    }
                    if( 
repeat === 'repeat-y' || repeat === 'no-repeat' ) {
                        
clipL pxX 1;
                        
clipR pxX size.clipAdjust;
                    }
                    
shape.style.clip 'rect(' clipT 'px,' clipR 'px,' clipB 'px,' clipL 'px)';
                }
            }
        } );
    },


    
/**
     * Draw the linear gradient for a gradient layer
     * @param {Element} shape
     * @param {Object} info The object holding the information about the gradient
     */
    
addLinearGradient: function( shapeinfo ) {
        var 
el this.targetElement,
            
bounds this.boundsInfo.getBounds(),
            
bounds.w,
            
bounds.h,
            
fill shape.fill,
            
stops info.stops,
            
stopCount stops.length,
            
PI Math.PI,
            
GradientUtil PIE.GradientUtil,
            
perpendicularIntersect GradientUtil.perpendicularIntersect,
            
distance GradientUtil.distance,
            
metrics GradientUtil.getGradientMetricselwhinfo ),
            
angle metrics.angle,
            
startX metrics.startX,
            
startY metrics.startY,
            
startCornerX metrics.startCornerX,
            
startCornerY metrics.startCornerY,
            
endCornerX metrics.endCornerX,
            
endCornerY metrics.endCornerY,
            
deltaX metrics.deltaX,
            
deltaY metrics.deltaY,
            
lineLength metrics.lineLength,
            
vmlAnglevmlGradientLengthvmlColors,
            
stopPxvmlOffsetPct,
            
pijbeforeafter;

        
// 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. If the angle is perfectly
        // horizontal or vertical then we don't need to do this conversion.
        
vmlAngle = ( angle 90 ) ? Math.atan2deltaX hdeltaY ) / PI 180 : ( angle 90 );

        
// 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.
        
perpendicularIntersectstartCornerXstartCornerYangleendCornerXendCornerY );
        
vmlGradientLength distancestartCornerXstartCornerYp[0], p[1] );
        
vmlColors = [];
        
perpendicularIntersectstartXstartYanglestartCornerXstartCornerY );
        
vmlOffsetPct distancestartXstartYp[0], p[1] ) / vmlGradientLength 100;

        
// Find the pixel offsets along the CSS3 gradient-line for each stop.
        
stopPx = [];
        for( 
0stopCounti++ ) {
            
stopPx.pushstops[i].offset stops[i].offset.pixelsellineLength ) :
                         
=== === stopCount lineLength null );
        }
        
// Fill in gaps with evenly-spaced offsets
        
for( 1stopCounti++ ) {
            if( 
stopPx] === null ) {
                
before stopPx];
                
i;
                do {
                    
after stopPx[ ++];
                } while( 
after === null );
                
stopPx] = before + ( after before ) / ( );
            }
            
// Make sure each stop's offset is no less than the one before it
            
stopPx] = Math.maxstopPx], stopPx] );
        }

        
// Convert to percentage along the VML gradient line and add to the VML 'colors' value
        
for( 0stopCounti++ ) {
            
vmlColors.push(
                ( 
vmlOffsetPct + ( stopPx] / vmlGradientLength 100 ) ) + '% ' stops[i].color.colorValueel )
            );
        }

        
// 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.colorValueel );
        
fill['color2'] = stops[stopCount 1].color.colorValueel );
        if( 
fill['colors'] ) { //sometimes the colors object isn't initialized so we have to assign it directly (?)
            
fill['colors'].value vmlColors.join',' );
        } else {
            
fill['colors'] = vmlColors.join',' );
        }
    },


    
/**
     * Hide the actual background image and color of the element.
     */
    
hideBackground: function() {
        var 
rs this.targetElement.runtimeStyle;
        
rs.backgroundImage 'url(about:blank)'//ensures the background area reacts to mouse events
        
rs.backgroundColor 'transparent';
    },

    
destroy: function() {
        
PIE.RendererBase.destroy.callthis );
        var 
rs this.targetElement.runtimeStyle;
        
rs.backgroundImage rs.backgroundColor '';
    }

} );
/**
 * Renderer for element borders.
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.BorderRenderer PIE.RendererBase.newRenderer( {

    
boxZIndex4,
    
boxName'border',

    
needsUpdate: function() {
        var 
si this.styleInfos;
        return 
si.borderInfo.changed() || si.borderRadiusInfo.changed();
    },

    
isActive: function() {
        var 
si this.styleInfos;
        return 
si.borderRadiusInfo.isActive() &&
               !
si.borderImageInfo.isActive() &&
               
si.borderInfo.isActive(); //check BorderStyleInfo last because it's the most expensive
    
},

    
/**
     * Draw the border shape(s)
     */
    
draw: function() {
        var 
el this.targetElement,
            
props this.styleInfos.borderInfo.getProps(),
            
bounds this.boundsInfo.getBounds(),
            
bounds.w,
            
bounds.h,
            
shapestrokes,
            
segmentssegilen;

        if( 
props ) {
            
this.hideBorder();

            
segments this.getBorderSegments);
            for( 
0len segments.lengthleni++) {
                
seg segments[i];
                
shape this.getShape'borderPiece' iseg.stroke 'stroke' 'fill'this.getBox() );
                
shape.coordsize ',' 2;
                
shape.coordorigin '1,1';
                
shape.path seg.path;
                
shape.style;
                
s.width w;
                
s.height h;

                
shape.filled = !!seg.fill;
                
shape.stroked = !!seg.stroke;
                if( 
seg.stroke ) {
                    
stroke shape.stroke;
                    
stroke['weight'] = seg.weight 'px';
                    
stroke.color seg.color.colorValueel );
                    
stroke['dashstyle'] = seg.stroke === 'dashed' '2 2' seg.stroke === 'dotted' '1 1' 'solid';
                    
stroke['linestyle'] = seg.stroke === 'double' && seg.weight 'ThinThin' 'Single';
                } else {
                    
shape.fill.color seg.fill.colorValueel );
                }
            }

            
// remove any previously-created border shapes which didn't get used above
            
while( this.deleteShape'borderPiece' i++ ) ) {}
        }
    },


    
/**
     * Get the VML path definitions for the border segment(s).
     * @param {number=} mult If specified, all coordinates will be multiplied by this number
     * @return {Array.<string>}
     */
    
getBorderSegments: function( mult ) {
        var 
el this.targetElement,
            
boundselWelH,
            
borderInfo this.styleInfos.borderInfo,
            
segments = [],
            
floorceilwTwRwBwL,
            
round Math.round,
            
borderPropsradiusInforadiiwidthsstylescolors;

        if( 
borderInfo.isActive() ) {
            
borderProps borderInfo.getProps();

            
widths borderProps.widths;
            
styles borderProps.styles;
            
colors borderProps.colors;

            if( 
borderProps.widthsSame && borderProps.stylesSame && borderProps.colorsSame ) {
                if( 
colors['t'].alpha() > ) {
                    
// shortcut for identical border on all sides - only need 1 stroked shape
                    
wT widths['t'].pixelsel ); //thickness
                    
wR wT 2//shrink
                    
segments.push( {
                        
paththis.getBoxPath( { twRrwRbwRlwR }, mult ),
                        
strokestyles['t'],
                        
colorcolors['t'],
                        
weightwT
                    
} );
                }
            }
            else {
                
mult mult || 1;
                
bounds this.boundsInfo.getBounds();
                
elW bounds.w;
                
elH bounds.h;

                
wT roundwidths['t'].pixelsel ) );
                
wR roundwidths['r'].pixelsel ) );
                
wB roundwidths['b'].pixelsel ) );
                
wL roundwidths['l'].pixelsel ) );
                var 
pxWidths = {
                    
't'wT,
                    
'r'wR,
                    
'b'wB,
                    
'l'wL
                
};

                
radiusInfo this.styleInfos.borderRadiusInfo;
                if( 
radiusInfo.isActive() ) {
                    
radii this.getRadiiPixelsradiusInfo.getProps() );
                }

                
floor Math.floor;
                
ceil Math.ceil;

                function 
radiusxycorner ) {
                    return 
radii radiixy ][ corner ] : 0;
                }

                function 
curvecornershrinkXshrinkYstartAngleccwdoMove ) {
                    var 
rx radius'x'corner),
                        
ry radius'y'corner),
                        
deg 65535,
                        
isRight corner.charAt) === 'r',
                        
isBottom corner.charAt) === 'b';
                    return ( 
rx && ry ) ?
                                ( 
doMove 'al' 'ae' ) +
                                ( 
isRight ceilelW rx ) : floorrx ) ) * mult ',' // center x
                                
isBottom ceilelH ry ) : floorry ) ) * mult ',' // center y
                                
floorrx ) - shrinkX ) * mult ',' // width
                                
floorry ) - shrinkY ) * mult ',' // height
                                
startAngle deg ) + ',' // start angle
                                
45 deg * ( ccw : -// angle change
                            
) : (
                                ( 
doMove 'm' 'l' ) +
                                ( 
isRight elW shrinkX shrinkX ) * mult ',' +
                                ( 
isBottom elH shrinkY shrinkY ) * mult
                            
);
                }

                function 
linesideshrinkccwdoMove ) {
                    var
                        
start = (
                            
side === 't' ?
                                
floorradius'x''tl') ) * mult ',' ceilshrink ) * mult :
                            
side === 'r' ?
                                
ceilelW shrink ) * mult ',' floorradius'y''tr') ) * mult :
                            
side === 'b' ?
                                
ceilelW radius'x''br') ) * mult ',' floorelH shrink ) * mult :
                            
// side === 'l' ?
                                
floorshrink ) * mult ',' ceilelH radius'y''bl') ) * mult
                        
),
                        
end = (
                            
side === 't' ?
                                
ceilelW radius'x''tr') ) * mult ',' ceilshrink ) * mult :
                            
side === 'r' ?
                                
ceilelW shrink ) * mult ',' ceilelH radius'y''br') ) * mult :
                            
side === 'b' ?
                                
floorradius'x''bl') ) * mult ',' floorelH shrink ) * mult :
                            
// side === 'l' ?
                                
floorshrink ) * mult ',' floorradius'y''tl') ) * mult
                        
);
                    return 
ccw ? ( doMove 'm' end '' ) + 'l' start :
                                 ( 
doMove 'm' start '' ) + 'l' end;
                }


                function 
addSidesidesideBeforesideAftercornerBeforecornerAfterbaseAngle ) {
                    var 
vert side === 'l' || side === 'r',
                        
sideW pxWidthsside ],
                        
beforeXbeforeYafterXafterY;

                    if( 
sideW && stylesside ] !== 'none' && colorsside ].alpha() > ) {
                        
beforeX pxWidthsvert side sideBefore ];
                        
beforeY pxWidthsvert sideBefore side ];
                        
afterX pxWidthsvert side sideAfter ];
                        
afterY pxWidthsvert sideAfter side ];

                        if( 
stylesside ] === 'dashed' || stylesside ] === 'dotted' ) {
                            
segments.push( {
                                
pathcurvecornerBeforebeforeXbeforeYbaseAngle 450) +
                                      
curvecornerBefore00baseAngle1),
                                
fillcolorsside ]
                            } );
                            
segments.push( {
                                
pathlinesidesideW 20),
                                
strokestylesside ],
                                
weightsideW,
                                
colorcolorsside ]
                            } );
                            
segments.push( {
                                
pathcurvecornerAfterafterXafterYbaseAngle0) +
                                      
curvecornerAfter00baseAngle 451),
                                
fillcolorsside ]
                            } );
                        }
                        else {
                            
segments.push( {
                                
pathcurvecornerBeforebeforeXbeforeYbaseAngle 450) +
                                      
linesidesideW0) +
                                      
curvecornerAfterafterXafterYbaseAngle0) +

                                      ( 
stylesside ] === 'double' && sideW ?
                                              
curvecornerAfterafterX floorafterX ), afterY floorafterY ), baseAngle 451) +
                                              
linesideceilsideW ), 1) +
                                              
curvecornerBeforebeforeX floorbeforeX ), beforeY floorbeforeY ), baseAngle1) +
                                              
'x ' +
                                              
curvecornerBeforefloorbeforeX ), floorbeforeY ), baseAngle 450) +
                                              
linesidefloorsideW ), 1) +
                                              
curvecornerAfterfloorafterX ), floorafterY ), baseAngle0)
                                          : 
'' ) +

                                      
curvecornerAfter00baseAngle 451) +
                                      
lineside01) +
                                      
curvecornerBefore00baseAngle1),
                                
fillcolorsside ]
                            } );
                        }
                    }
                }

                
addSide't''l''r''tl''tr'90 );
                
addSide'r''t''b''tr''br');
                
addSide'b''r''l''br''bl', -90 );
                
addSide'l''b''t''bl''tl', -180 );
            }
        }

        return 
segments;
    },

    
destroy: function() {
        var 
me this;
        if (
me.finalized || !me.styleInfos.borderImageInfo.isActive()) {
            
me.targetElement.runtimeStyle.borderColor '';
        }
        
PIE.RendererBase.destroy.callme );
    }


} );
/**
 * Renderer for border-image
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.BorderImageRenderer PIE.RendererBase.newRenderer( {

    
boxZIndex5,
    
pieceNames: [ 't''tr''r''br''b''bl''l''tl''c' ],

    
needsUpdate: function() {
        return 
this.styleInfos.borderImageInfo.changed();
    },

    
isActive: function() {
        return 
this.styleInfos.borderImageInfo.isActive();
    },

    
draw: function() {
        
this.getBox(); //make sure pieces are created

        
var props this.styleInfos.borderImageInfo.getProps(),
            
borderProps this.styleInfos.borderInfo.getProps(),
            
bounds this.boundsInfo.getBounds(),
            
el this.targetElement,
            
pieces this.pieces;

        
PIE.Util.withImageSizeprops.src, function( imgSize ) {
            var 
elW bounds.w,
                
elH bounds.h,
                
zero PIE.getLength'0' ),
                
widths props.widths || ( borderProps borderProps.widths : { 't'zero'r'zero'b'zero'l'zero } ),
                
widthT widths['t'].pixelsel ),
                
widthR widths['r'].pixelsel ),
                
widthB widths['b'].pixelsel ),
                
widthL widths['l'].pixelsel ),
                
slices props.slice,
                
sliceT slices['t'].pixelsel ),
                
sliceR slices['r'].pixelsel ),
                
sliceB slices['b'].pixelsel ),
                
sliceL slices['l'].pixelsel );

            
// Piece positions and sizes
            
function setSizeAndPospiecewhx) {
                var 
pieces[piece].style,
                    
max Math.max;
                
s.width max(w0);
                
s.height max(h0);
                
s.left x;
                
s.top y;
            }
            
setSizeAndPos'tl'widthLwidthT0);
            
setSizeAndPos't'elW widthL widthRwidthTwidthL);
            
setSizeAndPos'tr'widthRwidthTelW widthR);
            
setSizeAndPos'r'widthRelH widthT widthBelW widthRwidthT );
            
setSizeAndPos'br'widthRwidthBelW widthRelH widthB );
            
setSizeAndPos'b'elW widthL widthRwidthBwidthLelH widthB );
            
setSizeAndPos'bl'widthLwidthB0elH widthB );
            
setSizeAndPos'l'widthLelH widthT widthB0widthT );
            
setSizeAndPos'c'elW widthL widthRelH widthT widthBwidthLwidthT );


            
// image croppings
            
function setCropssidescropval ) {
                for( var 
i=0len=sides.lengthleni++ ) {
                    
piecessides[i] ]['imagedata'][ crop ] = val;
                }
            }

            
// corners
            
setCrops( [ 'tl''t''tr' ], 'cropBottom', ( imgSize.sliceT ) / imgSize.);
            
setCrops( [ 'tl''l''bl' ], 'cropRight', ( imgSize.sliceL ) / imgSize.);
            
setCrops( [ 'bl''b''br' ], 'cropTop', ( imgSize.sliceB ) / imgSize.);
            
setCrops( [ 'tr''r''br' ], 'cropLeft', ( imgSize.sliceR ) / imgSize.);

            
// edges and center
            // TODO right now this treats everything like 'stretch', need to support other schemes
            //if( props.repeat.v === 'stretch' ) {
                
setCrops( [ 'l''r''c' ], 'cropTop'sliceT imgSize.);
                
setCrops( [ 'l''r''c' ], 'cropBottom'sliceB imgSize.);
            
//}
            //if( props.repeat.h === 'stretch' ) {
                
setCrops( [ 't''b''c' ], 'cropLeft'sliceL imgSize.);
                
setCrops( [ 't''b''c' ], 'cropRight'sliceR imgSize.);
            
//}

            // center fill
            
pieces['c'].style.display props.fill '' 'none';
        }, 
this );
    },

    
getBox: function() {
        var 
box this.parent.getLayerthis.boxZIndex ),
            
spiecei,
            
pieceNames this.pieceNames,
            
len pieceNames.length;

        if( !
box ) {
            
box doc.createElement'border-image' );
            
box.style;
            
s.position 'absolute';

            
this.pieces = {};

            for( 
0leni++ ) {
                
piece this.piecespieceNames[i] ] = PIE.Util.createVmlElement'rect' );
                
piece.appendChildPIE.Util.createVmlElement'imagedata' ) );
                
piece.style;
                
s['behavior'] = 'url(#default#VML)';
                
s.position "absolute";
                
s.top s.left 0;
                
piece['imagedata'].src this.styleInfos.borderImageInfo.getProps().src;
                
piece.stroked false;
                
piece.filled false;
                
box.appendChildpiece );
            }

            
this.parent.addLayerthis.boxZIndexbox );
        }

        return 
box;
    },

    
prepareUpdate: function() {
        if (
this.isActive()) {
            var 
me this,
                
el me.targetElement,
                
rs el.runtimeStyle,
                
widths me.styleInfos.borderImageInfo.getProps().widths;

            
// Force border-style to solid so it doesn't collapse
            
rs.borderStyle 'solid';

            
// If widths specified in border-image shorthand, override border-width
            // NOTE px units needed here as this gets used by the IE9 renderer too
            
if ( widths ) {
                
rs.borderTopWidth widths['t'].pixelsel ) + 'px';
                
rs.borderRightWidth widths['r'].pixelsel ) + 'px';
                
rs.borderBottomWidth widths['b'].pixelsel ) + 'px';
                
rs.borderLeftWidth widths['l'].pixelsel ) + 'px';
            }

            
// Make the border transparent
            
me.hideBorder();
        }
    },

    
destroy: function() {
        var 
me this,
            
rs me.targetElement.runtimeStyle;
        
rs.borderStyle '';
        if (
me.finalized || !me.styleInfos.borderInfo.isActive()) {
            
rs.borderColor rs.borderWidth '';
        }
        
PIE.RendererBase.destroy.callthis );
    }

} );
/**
 * Renderer for outset box-shadows
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.BoxShadowOutsetRenderer PIE.RendererBase.newRenderer( {

    
boxZIndex1,
    
boxName'outset-box-shadow',

    
needsUpdate: function() {
        var 
si this.styleInfos;
        return 
si.boxShadowInfo.changed() || si.borderRadiusInfo.changed();
    },

    
isActive: function() {
        var 
boxShadowInfo this.styleInfos.boxShadowInfo;
        return 
boxShadowInfo.isActive() && boxShadowInfo.getProps().outset[0];
    },

    
draw: function() {
        var 
me this,
            
el this.targetElement,
            
box this.getBox(),
            
styleInfos this.styleInfos,
            
shadowInfos styleInfos.boxShadowInfo.getProps().outset,
            
radii styleInfos.borderRadiusInfo.getProps(),
            
len shadowInfos.length,
            
lenj,
            
bounds this.boundsInfo.getBounds(),
            
bounds.w,
            
bounds.h,
            
clipAdjust PIE.ieVersion === 0//workaround for IE8 bug where VML leaks out top/left of clip region by 1px
            
corners = [ 'tl''tr''br''bl' ], corner,
            
shadowInfoshapefillssxOffyOffspreadblurshrinkcoloralphapath,
            
totalWtotalHfocusXfocusYisBottomisRight;


        function 
getShadowShapeindexcornerxOffyOffcolorblurpath ) {
            var 
shape me.getShape'shadow' index corner'fill'boxlen index ),
                
fill shape.fill;

            
// Position and size
            
shape['coordsize'] = ',' 2;
            
shape['coordorigin'] = '1,1';

            
// Color and opacity
            
shape['stroked'] = false;
            
shape['filled'] = true;
            
fill.color color.colorValueel );
            if( 
blur ) {
                
fill['type'] = 'gradienttitle'//makes the VML gradient follow the shape's outline - hooray for undocumented features?!?!
                
fill['color2'] = fill.color;
                
fill['opacity'] = 0;
            }

            
// Path
            
shape.path path;

            
// This needs to go last for some reason, to prevent rendering at incorrect size
            
ss shape.style;
            
ss.left xOff;
            
ss.top yOff;
            
ss.width w;
            
ss.height h;

            return 
shape;
        }


        while( 
i-- ) {
            
shadowInfo shadowInfos];
            
xOff shadowInfo.xOffset.pixelsel );
            
yOff shadowInfo.yOffset.pixelsel );
            
spread shadowInfo.spread.pixelsel );
            
blur shadowInfo.blur.pixelsel );
            
color shadowInfo.color;
            
// Shape path
            
shrink = -spread blur;
            if( !
radii && blur ) {
                
// If blurring, use a non-null border radius info object so that getBoxPath will
                // round the corners of the expanded shadow shape rather than squaring them off.
                
radii PIE.BorderRadiusStyleInfo.ALL_ZERO;
            }
            
path this.getBoxPath( { tshrinkrshrinkbshrinklshrink }, 2radii );

            if( 
blur ) {
                
totalW = ( spread blur ) * w;
                
totalH = ( spread blur ) * h;
                
focusX totalW blur totalW 0;
                
focusY totalH blur totalH 0;
                if( 
blur spread || blur spread ) {
                    
// If the blur is larger than half the element's narrowest dimension, we cannot do
                    // this with a single shape gradient, because its focussize would have to be less than
                    // zero which results in ugly artifacts. Instead we create four shapes, each with its
                    // gradient focus past center, and then clip them so each only shows the quadrant
                    // opposite the focus.
                    
for( 4j--; ) {
                        
corner corners[j];
                        
isBottom corner.charAt) === 'b';
                        
isRight corner.charAt) === 'r';
                        
shape getShadowShapeicornerxOffyOffcolorblurpath );
                        
fill shape.fill;
                        
fill['focusposition'] = ( isRight focusX focusX ) + ',' +
                                                ( 
isBottom focusY focusY );
                        
fill['focussize'] = '0,0';

                        
// Clip to show only the appropriate quadrant. Add 1px to the top/left clip values
                        // in IE8 to prevent a bug where IE8 displays one pixel outside the clip region.
                        
shape.style.clip 'rect(' + ( ( isBottom totalH ) + clipAdjust ) + 'px,' +
                                                     ( 
isRight totalW totalW ) + 'px,' +
                                                     ( 
isBottom totalH totalH ) + 'px,' +
                                                     ( ( 
isRight totalW ) + clipAdjust ) + 'px)';
                    }
                } else {
                    
// TODO delete old quadrant shapes if resizing expands past the barrier
                    
shape getShadowShapei''xOffyOffcolorblurpath );
                    
fill shape.fill;
                    
fill['focusposition'] = focusX ',' focusY;
                    
fill['focussize'] = ( focusX ) + ',' + ( focusY );
                }
            } else {
                
shape getShadowShapei''xOffyOffcolorblurpath );
                
alpha color.alpha();
                if( 
alpha ) {
                    
// shape.style.filter = 'alpha(opacity=' + ( alpha * 100 ) + ')';
                    // ss.filter = 'progid:DXImageTransform.Microsoft.BasicImage(opacity=' + ( alpha  ) + ')';
                    
shape.fill.opacity alpha;
                }
            }
        }
    }

} );
/**
 * Renderer for re-rendering img elements using VML. Kicks in if the img has
 * a border-radius applied, or if the -pie-png-fix flag is set.
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.ImgRenderer PIE.RendererBase.newRenderer( {

    
boxZIndex6,
    
boxName'imgEl',

    
needsUpdate: function() {
        var 
si this.styleInfos;
        return 
this.targetElement.src !== this._lastSrc || si.borderRadiusInfo.changed();
    },

    
isActive: function() {
        var 
si this.styleInfos;
        return 
si.borderRadiusInfo.isActive() || si.backgroundInfo.isPngFix();
    },

    
draw: function() {
        
this._lastSrc src;
        
this.hideActualImg();

        var 
shape this.getShape'img''fill'this.getBox() ),
            
fill shape.fill,
            
bounds this.boundsInfo.getBounds(),
            
bounds.w,
            
bounds.h,
            
borderProps this.styleInfos.borderInfo.getProps(),
            
borderWidths borderProps && borderProps.widths,
            
el this.targetElement,
            
src el.src,
            
round Math.round,
            
cs el.currentStyle,
            
getLength PIE.getLength,
            
szero;

        
// In IE6, the BorderRenderer will have hidden the border by moving the border-width to
        // the padding; therefore we want to pretend the borders have no width so they aren't doubled
        // when adding in the current padding value below.
        
if( !borderWidths || PIE.ieVersion ) {
            
zero PIE.getLength'0' );
            
borderWidths = { 't'zero'r'zero'b'zero'l'zero };
        }

        
shape.stroked false;
        
fill.type 'frame';
        
fill.src src;
        
fill.position = (0.5 0) + ',' + (0.5 0);
        
shape.coordsize ',' 2;
        
shape.coordorigin '1,1';
        
shape.path this.getBoxPath( {
            
troundborderWidths['t'].pixelsel ) + getLengthcs.paddingTop ).pixelsel ) ),
            
rroundborderWidths['r'].pixelsel ) + getLengthcs.paddingRight ).pixelsel ) ),
            
broundborderWidths['b'].pixelsel ) + getLengthcs.paddingBottom ).pixelsel ) ),
            
lroundborderWidths['l'].pixelsel ) + getLengthcs.paddingLeft ).pixelsel ) )
        }, 
);
        
shape.style;
        
s.width w;
        
s.height h;
    },

    
hideActualImg: function() {
        
this.targetElement.runtimeStyle.filter 'alpha(opacity=0)';
    },

    
destroy: function() {
        
PIE.RendererBase.destroy.callthis );
        
this.targetElement.runtimeStyle.filter '';
    }

} );
/**
 * Root renderer for IE9; manages the rendering layers in the element's background
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 */
PIE.IE9RootRenderer PIE.RendererBase.newRenderer( {

    
updatePosPIE.emptyFn,
    
updateSizePIE.emptyFn,
    
updateVisibilityPIE.emptyFn,
    
updatePropsPIE.emptyFn,

    
outerCommasRE: /^,+|,+$/g,
    
innerCommasRE: /,+/g,

    
setBackgroundLayer: function(zIndexbg) {
        var 
me this,
            
bgLayers me._bgLayers || ( me._bgLayers = [] ),
            
undef;
        
bgLayers[zIndex] = bg || undef;
    },

    
finishUpdate: function() {
        var 
me this,
            
bgLayers me._bgLayers,
            
bg;
        if( 
bgLayers && ( bg bgLayers.join',' ).replaceme.outerCommasRE'' ).replaceme.innerCommasRE',' ) ) !== me._lastBg ) {
            
me._lastBg me.targetElement.runtimeStyle.background bg;
        }
    },

    
destroy: function() {
        
this.targetElement.runtimeStyle.background '';
        
delete this._bgLayers;
    }

} );
/**
 * Renderer for element backgrounds, specific for IE9. Only handles translating CSS3 gradients
 * to an equivalent SVG data URI.
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 */
PIE.IE9BackgroundRenderer PIE.RendererBase.newRenderer( {

    
bgLayerZIndex1,

    
needsUpdate: function() {
        var 
si this.styleInfos;
        return 
si.backgroundInfo.changed();
    },

    
isActive: function() {
        var 
si this.styleInfos;
        return 
si.backgroundInfo.isActive() || si.borderImageInfo.isActive();
    },

    
draw: function() {
        var 
me this,
            
props me.styleInfos.backgroundInfo.getProps(),
            
bgimages0imgbgAreaSizebgSize;

        if ( 
props ) {
            
bg = [];

            
images props.bgImages;
            if ( 
images ) {
                while( 
img imagesi++ ] ) {
                    if (
img.imgType === 'linear-gradient' ) {
                        
bgAreaSize me.getBgAreaSizeimg.bgOrigin );
                        
bgSize = ( img.bgSize || PIE.BgSize.DEFAULT ).pixels(
                            
me.targetElementbgAreaSize.wbgAreaSize.hbgAreaSize.wbgAreaSize.h
                        
),
                        
bg.push(
                            
'url(data:image/svg+xml,' escapeme.getGradientSvgimgbgSize.wbgSize.) ) + ') ' +
                            
me.bgPositionToStringimg.bgPosition ) + ' / ' bgSize.'px ' bgSize.'px ' +
                            ( 
img.bgAttachment || '' ) + ' ' + ( img.bgOrigin || '' ) + ' ' + ( img.bgClip || '' )
                        );
                    } else {
                        
bg.pushimg.origString );
                    }
                }
            }

            if ( 
props.color ) {
                
bg.pushprops.color.val );
            }

            
me.parent.setBackgroundLayer(me.bgLayerZIndexbg.join(','));
        }
    },

    
bgPositionToString: function( bgPosition ) {
        return 
bgPosition bgPosition.tokens.map(function(token) {
            return 
token.tokenValue;
        }).
join(' ') : '0 0';
    },

    
getBgAreaSize: function( bgOrigin ) {
        var 
me this,
            
el me.targetElement,
            
bounds me.boundsInfo.getBounds(),
            
elW bounds.w,
            
elH bounds.h,
            
elW,
            
elH,
            
bordersgetLengthcs;

        if( 
bgOrigin !== 'border-box' ) {
            
borders me.styleInfos.borderInfo.getProps();
            if( 
borders && ( borders borders.widths ) ) {
                
-= borders'l' ].pixelsel ) + borders'l' ].pixelsel );
                
-= borders't' ].pixelsel ) + borders'b' ].pixelsel );
            }
        }

        if ( 
bgOrigin === 'content-box' ) {
            
getLength PIE.getLength;
            
cs el.currentStyle;
            
-= getLengthcs.paddingLeft ).pixelsel ) + getLengthcs.paddingRight ).pixelsel );
            
-= getLengthcs.paddingTop ).pixelsel ) + getLengthcs.paddingBottom ).pixelsel );
        }

        return { 
wwh};
    },

    
getGradientSvg: function( infobgWidthbgHeight ) {
        var 
el this.targetElement,
            
stopsInfo info.stops,
            
stopCount stopsInfo.length,
            
metrics PIE.GradientUtil.getGradientMetricselbgWidthbgHeightinfo ),
            
startX metrics.startX,
            
startY metrics.startY,
            
endX metrics.endX,
            
endY metrics.endY,
            
lineLength metrics.lineLength,
            
stopPx,
            
ijbeforeafter,
            
svg;

        
// Find the pixel offsets along the CSS3 gradient-line for each stop.
        
stopPx = [];
        for( 
0stopCounti++ ) {
            
stopPx.pushstopsInfo[i].offset stopsInfo[i].offset.pixelsellineLength ) :
                         
=== === stopCount lineLength null );
        }
        
// Fill in gaps with evenly-spaced offsets
        
for( 1stopCounti++ ) {
            if( 
stopPx] === null ) {
                
before stopPx];
                
i;
                do {
                    
after stopPx[ ++];
                } while( 
after === null );
                
stopPx] = before + ( after before ) / ( );
            }
        }

        
svg = [
            
'<svg width="' bgWidth '" height="' bgHeight '" xmlns="http://www.w3.org/2000/svg">' +
                
'<defs>' +
                    
'<linearGradient id="g" gradientUnits="userSpaceOnUse"' +
                    
' x1="' + ( startX bgWidth 100 ) + '%" y1="' + ( startY bgHeight 100 ) + '%" x2="' + ( endX bgWidth 100 ) + '%" y2="' + ( endY bgHeight 100 ) + '%">'
        
];

        
// Convert to percentage along the SVG gradient line and add to the stops list
        
for( 0stopCounti++ ) {
            
svg.push(
                
'<stop offset="' + ( stopPx] / lineLength ) +
                    
'" stop-color="' stopsInfo[i].color.colorValueel ) +
                    
'" stop-opacity="' stopsInfo[i].color.alpha() + '"/>'
            
);
        }

        
svg.push(
                    
'</linearGradient>' +
                
'</defs>' +
                
'<rect width="100%" height="100%" fill="url(#g)"/>' +
            
'</svg>'
        
);

        return 
svg.join'' );
    },

    
destroy: function() {
        
this.parent.setBackgroundLayerthis.bgLayerZIndex );
    }

} );
/**
 * Renderer for border-image
 * @constructor
 * @param {Element} el The target element
 * @param {Object} styleInfos The StyleInfo objects
 * @param {PIE.RootRenderer} parent
 */
PIE.IE9BorderImageRenderer PIE.RendererBase.newRenderer( {

    
REPEAT'repeat',
    
STRETCH'stretch',
    
ROUND'round',

    
bgLayerZIndex0,

    
needsUpdate: function() {
        return 
this.styleInfos.borderImageInfo.changed();
    },

    
isActive: function() {
        return 
this.styleInfos.borderImageInfo.isActive();
    },

    
draw: function() {
        var 
me this,
            
props me.styleInfos.borderImageInfo.getProps(),
            
borderProps me.styleInfos.borderInfo.getProps(),
            
bounds me.boundsInfo.getBounds(),
            
repeat props.repeat,
            
repeatH repeat.h,
            
repeatV repeat.v,
            
el me.targetElement,
            
isAsync 0;

        
PIE.Util.withImageSizeprops.src, function( imgSize ) {
            var 
elW bounds.w,
                
elH bounds.h,
                
imgW imgSize.w,
                
imgH imgSize.h,

                
// The image cannot be referenced as a URL directly in the SVG because IE9 throws a strange
                // security exception (perhaps due to cross-origin policy within data URIs?) Therefore we
                // work around this by converting the image data into a data URI itself using a transient
                // canvas. This unfortunately requires the border-image src to be within the same domain,
                // which isn't a limitation in true border-image, so we need to try and find a better fix.
                
imgSrc me.imageToDataURIprops.srcimgWimgH ),

                
REPEAT me.REPEAT,
                
STRETCH me.STRETCH,
                
ROUND me.ROUND,
                
ceil Math.ceil,

                
zero PIE.getLength'0' ),
                
widths props.widths || ( borderProps borderProps.widths : { 't'zero'r'zero'b'zero'l'zero } ),
                
widthT widths['t'].pixelsel ),
                
widthR widths['r'].pixelsel ),
                
widthB widths['b'].pixelsel ),
                
widthL widths['l'].pixelsel ),
                
slices props.slice,
                
sliceT slices['t'].pixelsel ),
                
sliceR slices['r'].pixelsel ),
                
sliceB slices['b'].pixelsel ),
                
sliceL slices['l'].pixelsel ),
                
centerW elW widthL widthR,
                
middleH elH widthT widthB,
                
imgCenterW imgW sliceL sliceR,
                
imgMiddleH imgH sliceT sliceB,

                
// Determine the size of each tile - 'round' is handled below
                
tileSizeT repeatH === STRETCH centerW imgCenterW widthT sliceT,
                
tileSizeR repeatV === STRETCH middleH imgMiddleH widthR sliceR,
                
tileSizeB repeatH === STRETCH centerW imgCenterW widthB sliceB,
                
tileSizeL repeatV === STRETCH middleH imgMiddleH widthL sliceL,

                
svg,
                
patterns = [],
                
rects = [],
                
0;

            
// For 'round', subtract from each tile's size enough so that they fill the space a whole number of times
            
if (repeatH === ROUND) {
                
tileSizeT -= (tileSizeT - (centerW tileSizeT || tileSizeT)) / ceil(centerW tileSizeT);
                
tileSizeB -= (tileSizeB - (centerW tileSizeB || tileSizeB)) / ceil(centerW tileSizeB);
            }
            if (
repeatV === ROUND) {
                
tileSizeR -= (tileSizeR - (middleH tileSizeR || tileSizeR)) / ceil(middleH tileSizeR);
                
tileSizeL -= (tileSizeL - (middleH tileSizeL || tileSizeL)) / ceil(middleH tileSizeL);
            }


            
// Build the SVG for the border-image rendering. Add each piece as a pattern, which is then stretched
            // or repeated as the fill of a rect of appropriate size.
            
svg = [
                
'<svg width="' elW '" height="' elH '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
            
];

            function 
addImagexywhcropXcropYcropWcropHtileWtileH ) {
                
patterns.push(
                    
'<pattern patternUnits="userSpaceOnUse" id="pattern' '" ' +
                            
'x="' + (repeatH === REPEAT tileW x) + '" ' +
                            
'y="' + (repeatV === REPEAT tileH y) + '" ' +
                            
'width="' tileW '" height="' tileH '">' +
                        
'<svg width="' tileW '" height="' tileH '" viewBox="' cropX ' ' cropY ' ' cropW ' ' cropH '" preserveAspectRatio="none">' +
                            
'<image xlink:href="' imgSrc '" x="0" y="0" width="' imgW '" height="' imgH '" />' +
                        
'</svg>' +
                    
'</pattern>'
                
);
                
rects.push(
                    
'<rect x="' '" y="' '" width="' '" height="' '" fill="url(#pattern' ')" />'
                
);
                
i++;
            }
            
addImage00widthLwidthT00sliceLsliceTwidthLwidthT ); // top left
            
addImagewidthL0centerWwidthTsliceL0imgCenterWsliceTtileSizeTwidthT ); // top center
            
addImageelW widthR0widthRwidthTimgW sliceR0sliceRsliceTwidthRwidthT ); // top right
            
addImage0widthTwidthLmiddleH0sliceTsliceLimgMiddleHwidthLtileSizeL ); // middle left
            
if ( props.fill ) { // center fill
                
addImagewidthLwidthTcenterWmiddleHsliceLsliceTimgCenterWimgMiddleH
                          
tileSizeT || tileSizeB || imgCenterWtileSizeL || tileSizeR || imgMiddleH );
            }
            
addImageelW widthRwidthTwidthRmiddleHimgW sliceRsliceTsliceRimgMiddleHwidthRtileSizeR ); // middle right
            
addImage0elH widthBwidthLwidthB0imgH sliceBsliceLsliceBwidthLwidthB ); // bottom left
            
addImagewidthLelH widthBcenterWwidthBsliceLimgH sliceBimgCenterWsliceBtileSizeBwidthB ); // bottom center
            
addImageelW widthRelH widthBwidthRwidthBimgW sliceRimgH sliceBsliceRsliceBwidthRwidthB ); // bottom right

            
svg.push(
                    
'<defs>' +
                        
patterns.join('n') +
                    
'</defs>' +
                    
rects.join('n') +
                
'</svg>'
            
);

            
me.parent.setBackgroundLayerme.bgLayerZIndex'url(data:image/svg+xml,' escapesvg.join'' ) ) + ') no-repeat border-box border-box' );

            
// If the border-image's src wasn't immediately available, the SVG for its background layer
            // will have been created asynchronously after the main element's update has finished; we'll
            // therefore need to force the root renderer to sync to the final background once finished.
            
if( isAsync ) {
                
me.parent.finishUpdate();
            }
        }, 
me );

        
isAsync 1;
    },

    
/**
     * Convert a given image to a data URI
     */
    
imageToDataURI: (function() {
        var 
uris = {};
        return function( 
srcwidthheight ) {
            var 
uri urissrc ],
                
imagecanvas;
            if ( !
uri ) {
                
image = new Image();
                
canvas doc.createElement'canvas' );
                
image.src src;
                
canvas.width width;
                
canvas.height height;
                
canvas.getContext'2d' ).drawImageimage0);
                
uri urissrc ] = canvas.toDataURL();
            }
            return 
uri;
        }
    })(),

    
prepareUpdatePIE.BorderImageRenderer.prototype.prepareUpdate,

    
destroy: function() {
        var 
me this,
            
rs me.targetElement.runtimeStyle;
        
me.parent.setBackgroundLayerme.bgLayerZIndex );
        
rs.borderColor rs.borderStyle rs.borderWidth '';
    }

} );

PIE.Element = (function() {

    var 
wrappers = {},
        
lazyInitCssProp PIE.CSS_PREFIX 'lazy-init',
        
pollCssProp PIE.CSS_PREFIX 'poll',
        
trackActiveCssProp PIE.CSS_PREFIX 'track-active',
        
trackHoverCssProp PIE.CSS_PREFIX 'track-hover',
        
hoverClass PIE.CLASS_PREFIX 'hover',
        
activeClass PIE.CLASS_PREFIX 'active',
        
focusClass PIE.CLASS_PREFIX 'focus',
        
firstChildClass PIE.CLASS_PREFIX 'first-child',
        
ignorePropertyNames = { 'background':1'bgColor':1'display'},
        
classNameRegExes = {},
        
dummyArray = [];


    function 
addClasselclassName ) {
        
el.className += ' ' className;
    }

    function 
removeClasselclassName ) {
        var 
re classNameRegExesclassName ] ||
            ( 
classNameRegExesclassName ] = new RegExp'\b' className '\b''g' ) );
        
el.className el.className.replacere'' );
    }

    function 
delayAddClasselclassName /*, className2*/ ) {
        var 
classes dummyArray.slice.callarguments),
            
classes.length;
        
setTimeout( function() {
            if( 
el ) {
                while( 
i-- ) {
                    
addClasselclasses] );
                }
            }
        }, 
);
    }

    function 
delayRemoveClasselclassName /*, className2*/ ) {
        var 
classes dummyArray.slice.callarguments),
            
classes.length;
        
setTimeout( function() {
            if( 
el ) {
                while( 
i-- ) {
                    
removeClasselclasses] );
                }
            }
        }, 
);
    }



    function 
Elementel ) {
        var 
renderers,
            
rootRenderer,
            
boundsInfo = new PIE.BoundsInfoel ),
            
styleInfos,
            
styleInfosArr,
            
initializing,
            
initialized,
            
eventsAttached,
            
eventListeners = [],
            
delayed,
            
destroyed,
            
poll;

        
/**
         * Initialize PIE for this element.
         */
        
function init() {
            if( !
initialized ) {
                var 
docEl,
                    
bounds,
                    
ieDocMode PIE.ieDocMode,
                    
cs el.currentStyle,
                    
lazy cs.getAttributelazyInitCssProp ) === 'true',
                    
trackActive cs.getAttributetrackActiveCssProp ) !== 'false',
                    
trackHover cs.getAttributetrackHoverCssProp ) !== 'false',
                    
childRenderers;

                
// Polling for size/position changes: default to on in IE8, off otherwise, overridable by -pie-poll
                
poll cs.getAttributepollCssProp );
                
poll ieDocMode poll !== 'false' poll === 'true';

                
// Force layout so move/resize events will fire. Set this as soon as possible to avoid layout changes
                // after load, but make sure it only gets called the first time through to avoid recursive calls to init().
                
if( !initializing ) {
                    
initializing 1;
                    
el.runtimeStyle.zoom 1;
                    
initFirstChildPseudoClass();
                }

                
boundsInfo.lock();

                
// If the -pie-lazy-init:true flag is set, check if the element is outside the viewport and if so, delay initialization
                
if( lazy && ( bounds boundsInfo.getBounds() ) && ( docEl doc.documentElement || doc.body ) &&
                        ( 
bounds.docEl.clientHeight || bounds.docEl.clientWidth || bounds.bounds.|| bounds.bounds.) ) {
                    if( !
delayed ) {
                        
delayed 1;
                        
PIE.OnScroll.observeinit );
                    }
                } else {
                    
initialized 1;
                    
delayed initializing 0;
                    
PIE.OnScroll.unobserveinit );

                    
// Create the style infos and renderers
                    
if ( ieDocMode === ) {
                        
styleInfos = {
                            
backgroundInfo: new PIE.BackgroundStyleInfoel ),
                            
borderImageInfo: new PIE.BorderImageStyleInfoel ),
                            
borderInfo: new PIE.BorderStyleInfoel )
                        };
                        
styleInfosArr = [
                            
styleInfos.backgroundInfo,
                            
styleInfos.borderImageInfo
                        
];
                        
rootRenderer = new PIE.IE9RootRendererelboundsInfostyleInfos );
                        
childRenderers = [
                            new 
PIE.IE9BackgroundRendererelboundsInfostyleInfosrootRenderer ),
                            new 
PIE.IE9BorderImageRendererelboundsInfostyleInfosrootRenderer )
                        ];
                    } else {

                        
styleInfos = {
                            
backgroundInfo: new PIE.BackgroundStyleInfoel ),
                            
borderInfo: new PIE.BorderStyleInfoel ),
                            
borderImageInfo: new PIE.BorderImageStyleInfoel ),
                            
borderRadiusInfo: new PIE.BorderRadiusStyleInfoel ),
                            
boxShadowInfo: new PIE.BoxShadowStyleInfoel ),
                            
visibilityInfo: new PIE.VisibilityStyleInfoel )
                        };
                        
styleInfosArr = [
                            
styleInfos.backgroundInfo,
                            
styleInfos.borderInfo,
                            
styleInfos.borderImageInfo,
                            
styleInfos.borderRadiusInfo,
                            
styleInfos.boxShadowInfo,
                            
styleInfos.visibilityInfo
                        
];
                        
rootRenderer = new PIE.RootRendererelboundsInfostyleInfos );
                        
childRenderers = [
                            new 
PIE.BoxShadowOutsetRendererelboundsInfostyleInfosrootRenderer ),
                            new 
PIE.BackgroundRendererelboundsInfostyleInfosrootRenderer ),
                            
//new PIE.BoxShadowInsetRenderer( el, boundsInfo, styleInfos, rootRenderer ),
                            
new PIE.BorderRendererelboundsInfostyleInfosrootRenderer ),
                            new 
PIE.BorderImageRendererelboundsInfostyleInfosrootRenderer )
                        ];
                        if( 
el.tagName === 'IMG' ) {
                            
childRenderers.push( new PIE.ImgRendererelboundsInfostyleInfosrootRenderer ) );
                        }
                        
rootRenderer.childRenderers childRenderers// circular reference, can't pass in constructor; TODO is there a cleaner way?
                    
}
                    
renderers = [ rootRenderer ].concatchildRenderers );

                    
// Add property change listeners to ancestors if requested
                    
initAncestorEventListeners();

                    
// Add to list of polled elements in IE8
                    
if( poll ) {
                        
PIE.Heartbeat.observeupdate );
                        
PIE.Heartbeat.run();
                    }

                    
// Trigger rendering
                    
update);
                }

                if( !
eventsAttached ) {
                    
eventsAttached 1;
                    if( 
ieDocMode ) {
                        
addListenerel'onmove'handleMoveOrResize );
                    }
                    
addListenerel'onresize'handleMoveOrResize );
                    
addListenerel'onpropertychange'propChanged );
                    if( 
trackHover ) {
                        
addListenerel'onmouseenter'mouseEntered );
                    }
                    if( 
trackHover || trackActive ) {
                        
addListenerel'onmouseleave'mouseLeft );
                    }
                    if( 
trackActive ) {
                        
addListenerel'onmousedown'mousePressed );
                    }
                    if( 
el.tagName in PIE.focusableElements ) {
                        
addListenerel'onfocus'focused );
                        
addListenerel'onblur'blurred );
                    }
                    
PIE.OnResize.observehandleMoveOrResize );

                    
PIE.OnUnload.observeremoveEventListeners );
                }

                
boundsInfo.unlock();
            }
        }




        
/**
         * Event handler for onmove and onresize events. Invokes update() only if the element's
         * bounds have previously been calculated, to prevent multiple runs during page load when
         * the element has no initial CSS3 properties.
         */
        
function handleMoveOrResize() {
            if( 
boundsInfo && boundsInfo.hasBeenQueried() ) {
                
update();
            }
        }


        
/**
         * Update position and/or size as necessary. Both move and resize events call
         * this rather than the updatePos/Size functions because sometimes, particularly
         * during page load, one will fire but the other won't.
         */
        
function updateforce ) {
            if( !
destroyed ) {
                if( 
initialized ) {
                    var 
ilen renderers.length;

                    
lockAll();
                    for( 
0leni++ ) {
                        
renderers[i].prepareUpdate();
                    }
                    if( 
force || boundsInfo.positionChanged() ) {
                        
/* TODO just using getBoundingClientRect (used internally by BoundsInfo) for detecting
                           position changes may not always be accurate; it's possible that
                           an element will actually move relative to its positioning parent, but its position
                           relative to the viewport will stay the same. Need to come up with a better way to
                           track movement. The most accurate would be the same logic used in RootRenderer.updatePos()
                           but that is a more expensive operation since it does some DOM walking, and we want this
                           check to be as fast as possible. */
                        
for( 0leni++ ) {
                            
renderers[i].updatePos();
                        }
                    }
                    if( 
force || boundsInfo.sizeChanged() ) {
                        for( 
0leni++ ) {
                            
renderers[i].updateSize();
                        }
                    }
                    
rootRenderer.finishUpdate();
                    
unlockAll();
                }
                else if( !
initializing ) {
                    
init();
                }
            }
        }

        
/**
         * Handle property changes to trigger update when appropriate.
         */
        
function propChanged() {
            var 
ilen renderers.length,
                
renderer,
                
event;

            
// Some elements like <table> fire onpropertychange events for old-school background properties
            // ('background', 'bgColor') when runtimeStyle background properties are changed, which
            // results in an infinite loop; therefore we filter out those property names. Also, 'display'
            // is ignored because size calculations don't work correctly immediately when its onpropertychange
            // event fires, and because it will trigger an onresize event anyway.
            
if( !destroyed && !( && e.propertyName in ignorePropertyNames ) ) {
                if( 
initialized ) {
                    
lockAll();
                    for( 
0leni++ ) {
                        
renderers[i].prepareUpdate();
                    }
                    for( 
0leni++ ) {
                        
renderer renderers[i];
                        
// Make sure position is synced if the element hasn't already been rendered.
                        // TODO this feels sloppy - look into merging propChanged and update functions
                        
if( !renderer.isPositioned ) {
                            
renderer.updatePos();
                        }
                        if( 
renderer.needsUpdate() ) {
                            
renderer.updateProps();
                        }
                    }
                    
rootRenderer.finishUpdate();
                    
unlockAll();
                }
                else if( !
initializing ) {
                    
init();
                }
            }
        }


        
/**
         * Handle mouseenter events. Adds a custom class to the element to allow IE6 to add
         * hover styles to non-link elements, and to trigger a propertychange update.
         */
        
function mouseEntered() {
            
//must delay this because the mouseenter event fires before the :hover styles are added.
            
delayAddClasselhoverClass );
        }

        
/**
         * Handle mouseleave events
         */
        
function mouseLeft() {
            
//must delay this because the mouseleave event fires before the :hover styles are removed.
            
delayRemoveClasselhoverClassactiveClass );
        }

        
/**
         * Handle mousedown events. Adds a custom class to the element to allow IE6 to add
         * active styles to non-link elements, and to trigger a propertychange update.
         */
        
function mousePressed() {
            
//must delay this because the mousedown event fires before the :active styles are added.
            
delayAddClasselactiveClass );

            
// listen for mouseups on the document; can't just be on the element because the user might
            // have dragged out of the element while the mouse button was held down
            
PIE.OnMouseup.observemouseReleased );
        }

        
/**
         * Handle mouseup events
         */
        
function mouseReleased() {
            
//must delay this because the mouseup event fires before the :active styles are removed.
            
delayRemoveClasselactiveClass );

            
PIE.OnMouseup.unobservemouseReleased );
        }

        
/**
         * Handle focus events. Adds a custom class to the element to trigger a propertychange update.
         */
        
function focused() {
            
//must delay this because the focus event fires before the :focus styles are added.
            
delayAddClasselfocusClass );
        }

        
/**
         * Handle blur events
         */
        
function blurred() {
            
//must delay this because the blur event fires before the :focus styles are removed.
            
delayRemoveClasselfocusClass );
        }


        
/**
         * Handle property changes on ancestors of the element; see initAncestorEventListeners()
         * which adds these listeners as requested with the -pie-watch-ancestors CSS property.
         */
        
function ancestorPropChanged() {
            var 
name event.propertyName;
            if( 
name === 'className' || name === 'id' ) {
                
propChanged();
            }
        }

        function 
lockAll() {
            
boundsInfo.lock();
            for( var 
styleInfosArr.lengthi--; ) {
                
styleInfosArr[i].lock();
            }
        }

        function 
unlockAll() {
            for( var 
styleInfosArr.lengthi--; ) {
                
styleInfosArr[i].unlock();
            }
            
boundsInfo.unlock();
        }


        function 
addListenertargetEltypehandler ) {
            
targetEl.attachEventtypehandler );
            
eventListeners.push( [ targetEltypehandler ] );
        }

        
/**
         * Remove all event listeners from the element and any monitored ancestors.
         */
        
function removeEventListeners() {
            if (
eventsAttached) {
                var 
eventListeners.length,
                    
listener;

                while( 
i-- ) {
                    
listener eventListeners];
                    
listener].detachEventlistener], listener] );
                }

                
PIE.OnUnload.unobserveremoveEventListeners );
                
eventsAttached 0;
                
eventListeners = [];
            }
        }


        
/**
         * Clean everything up when the behavior is removed from the element, or the element
         * is manually destroyed.
         */
        
function destroy() {
            if( !
destroyed ) {
                var 
ilen;

                
removeEventListeners();

                
destroyed 1;

                
// destroy any active renderers
                
if( renderers ) {
                    for( 
0len renderers.lengthleni++ ) {
                        
renderers[i].finalized 1;
                        
renderers[i].destroy();
                    }
                }

                
// Remove from list of polled elements in IE8
                
if( poll ) {
                    
PIE.Heartbeat.unobserveupdate );
                }
                
// Stop onresize listening
                
PIE.OnResize.unobserveupdate );

                
// Kill references
                
renderers boundsInfo styleInfos styleInfosArr el null;
            }
        }


        
/**
         * If requested via the custom -pie-watch-ancestors CSS property, add onpropertychange and
         * other event listeners to ancestor(s) of the element so we can pick up style changes
         * based on CSS rules using descendant selectors.
         */
        
function initAncestorEventListeners() {
            var 
watch el.currentStyle.getAttributePIE.CSS_PREFIX 'watch-ancestors' ),
                
ia;
            if( 
watch ) {
                
watch parseIntwatch10 );
                
0;
                
el.parentNode;
                while( 
&& ( watch === 'NaN' || i++ < watch ) ) {
                    
addListenera'onpropertychange'ancestorPropChanged );
                    
addListenera'onmouseenter'mouseEntered );
                    
addListenera'onmouseleave'mouseLeft );
                    
addListenera'onmousedown'mousePressed );
                    if( 
a.tagName in PIE.focusableElements ) {
                        
addListenera'onfocus'focused );
                        
addListenera'onblur'blurred );
                    }
                    
a.parentNode;
                }
            }
        }


        
/**
         * If the target element is a first child, add a pie_first-child class to it. This allows using
         * the added class as a workaround for the fact that PIE's rendering element breaks the :first-child
         * pseudo-class selector.
         */
        
function initFirstChildPseudoClass() {
            var 
tmpEl el,
                
isFirst 1;
            while( 
tmpEl tmpEl.previousSibling ) {
                if( 
tmpEl.nodeType === ) {
                    
isFirst 0;
                    break;
                }
            }
            if( 
isFirst ) {
                
addClasselfirstChildClass );
            }
        }


        
// These methods are all already bound to this instance so there's no need to wrap them
        // in a closure to maintain the 'this' scope object when calling them.
        
this.init init;
        
this.update update;
        
this.destroy destroy;
        
this.el el;
    }

    
Element.getInstance = function( el ) {
        var 
id PIE.Util.getUIDel );
        return 
wrappersid ] || ( wrappersid ] = new Elementel ) );
    };

    
Element.destroy = function( el ) {
        var 
id PIE.Util.getUIDel ),
            
wrapper wrappersid ];
        if( 
wrapper ) {
            
wrapper.destroy();
            
delete wrappersid ];
        }
    };

    
Element.destroyAll = function() {
        var 
els = [], wrapper;
        if( 
wrappers ) {
            for( var 
w in wrappers ) {
                if( 
wrappers.hasOwnProperty) ) {
                    
wrapper wrappers];
                    
els.pushwrapper.el );
                    
wrapper.destroy();
                }
            }
            
wrappers = {};
        }
        return 
els;
    };

    return 
Element;
})();

/*
 * This file exposes the public API for invoking PIE.
 */


/**
 * @property supportsVML
 * True if the current IE browser environment has a functioning VML engine. Should be true
 * in most IEs, but in rare cases may be false. If false, PIE will exit immediately when
 * attached to an element; this property may be used for debugging or by external scripts
 * to perform some special action when VML support is absent.
 * @type {boolean}
 */
PIE'supportsVML' ] = PIE.supportsVML;


/**
 * Programatically attach PIE to a single element.
 * @param {Element} el
 */
PIE'attach' ] = function( el ) {
    if (
PIE.ieDocMode 10 && PIE.supportsVML) {
        
PIE.Element.getInstanceel ).init();
    }
};


/**
 * Programatically detach PIE from a single element.
 * @param {Element} el
 */
PIE'detach' ] = function( el ) {
    
PIE.Element.destroyel );
};


// if( !PIE )
})();
?>
Онлайн: 0
Реклама