/sources/IE9BorderImageRenderer.js
JavaScript | 198 lines | 147 code | 26 blank | 25 comment | 16 complexity | c84a6a209be1d4d3cb006b4541e9481a MD5 | raw file
- /**
- * 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',
- bgLayerZIndex: 0,
- 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.withImageSize( props.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.imageToDataURI( props.src, imgW, imgH ),
- 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'].pixels( el ),
- widthR = widths['r'].pixels( el ),
- widthB = widths['b'].pixels( el ),
- widthL = widths['l'].pixels( el ),
- slices = props.slice,
- sliceT = slices['t'].pixels( el ),
- sliceR = slices['r'].pixels( el ),
- sliceB = slices['b'].pixels( el ),
- sliceL = slices['l'].pixels( el ),
- 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 = [],
- i = 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 addImage( x, y, w, h, cropX, cropY, cropW, cropH, tileW, tileH ) {
- patterns.push(
- '<pattern patternUnits="userSpaceOnUse" id="pattern' + i + '" ' +
- 'x="' + (repeatH === REPEAT ? x + w / 2 - tileW / 2 : x) + '" ' +
- 'y="' + (repeatV === REPEAT ? y + h / 2 - tileH / 2 : 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="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" fill="url(#pattern' + i + ')" />'
- );
- i++;
- }
- addImage( 0, 0, widthL, widthT, 0, 0, sliceL, sliceT, widthL, widthT ); // top left
- addImage( widthL, 0, centerW, widthT, sliceL, 0, imgCenterW, sliceT, tileSizeT, widthT ); // top center
- addImage( elW - widthR, 0, widthR, widthT, imgW - sliceR, 0, sliceR, sliceT, widthR, widthT ); // top right
- addImage( 0, widthT, widthL, middleH, 0, sliceT, sliceL, imgMiddleH, widthL, tileSizeL ); // middle left
- if ( props.fill ) { // center fill
- addImage( widthL, widthT, centerW, middleH, sliceL, sliceT, imgCenterW, imgMiddleH,
- tileSizeT || tileSizeB || imgCenterW, tileSizeL || tileSizeR || imgMiddleH );
- }
- addImage( elW - widthR, widthT, widthR, middleH, imgW - sliceR, sliceT, sliceR, imgMiddleH, widthR, tileSizeR ); // middle right
- addImage( 0, elH - widthB, widthL, widthB, 0, imgH - sliceB, sliceL, sliceB, widthL, widthB ); // bottom left
- addImage( widthL, elH - widthB, centerW, widthB, sliceL, imgH - sliceB, imgCenterW, sliceB, tileSizeB, widthB ); // bottom center
- addImage( elW - widthR, elH - widthB, widthR, widthB, imgW - sliceR, imgH - sliceB, sliceR, sliceB, widthR, widthB ); // bottom right
- svg.push(
- '<defs>' +
- patterns.join('\n') +
- '</defs>' +
- rects.join('\n') +
- '</svg>'
- );
- me.parent.setBackgroundLayer( me.bgLayerZIndex, 'url(data:image/svg+xml,' + escape( svg.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.updateRendering();
- }
- }, me );
- isAsync = 1;
- },
- /**
- * Convert a given image to a data URI
- */
- imageToDataURI: (function() {
- var uris = {};
- return function( src, width, height ) {
- var uri = uris[ src ],
- image, canvas;
- if ( !uri ) {
- image = new Image();
- canvas = doc.createElement( 'canvas' );
- image.src = src;
- canvas.width = width;
- canvas.height = height;
- canvas.getContext( '2d' ).drawImage( image, 0, 0 );
- uri = uris[ src ] = canvas.toDataURL();
- }
- return uri;
- }
- })(),
- 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
- if ( widths ) {
- rs.borderTopWidth = widths['t'].pixels( el ) + 'px';
- rs.borderRightWidth = widths['r'].pixels( el ) + 'px';
- rs.borderBottomWidth = widths['b'].pixels( el ) + 'px';
- rs.borderLeftWidth = widths['l'].pixels( el ) + 'px';
- }
- // Make the border transparent
- me.hideBorder();
- }
- },
- destroy: function() {
- var me = this,
- rs = me.targetElement.runtimeStyle;
- me.parent.setBackgroundLayer( me.bgLayerZIndex );
- rs.borderColor = rs.borderStyle = rs.borderWidth = '';
- }
- } );