PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/sources/IE9BorderImageRenderer.js

https://gitlab.com/oytunistrator/PIE
JavaScript | 198 lines | 147 code | 26 blank | 25 comment | 16 complexity | c84a6a209be1d4d3cb006b4541e9481a MD5 | raw file
  1. /**
  2. * Renderer for border-image
  3. * @constructor
  4. * @param {Element} el The target element
  5. * @param {Object} styleInfos The StyleInfo objects
  6. * @param {PIE.RootRenderer} parent
  7. */
  8. PIE.IE9BorderImageRenderer = PIE.RendererBase.newRenderer( {
  9. REPEAT: 'repeat',
  10. STRETCH: 'stretch',
  11. ROUND: 'round',
  12. bgLayerZIndex: 0,
  13. needsUpdate: function() {
  14. return this.styleInfos.borderImageInfo.changed();
  15. },
  16. isActive: function() {
  17. return this.styleInfos.borderImageInfo.isActive();
  18. },
  19. draw: function() {
  20. var me = this,
  21. props = me.styleInfos.borderImageInfo.getProps(),
  22. borderProps = me.styleInfos.borderInfo.getProps(),
  23. bounds = me.boundsInfo.getBounds(),
  24. repeat = props.repeat,
  25. repeatH = repeat.h,
  26. repeatV = repeat.v,
  27. el = me.targetElement,
  28. isAsync = 0;
  29. PIE.Util.withImageSize( props.src, function( imgSize ) {
  30. var elW = bounds.w,
  31. elH = bounds.h,
  32. imgW = imgSize.w,
  33. imgH = imgSize.h,
  34. // The image cannot be referenced as a URL directly in the SVG because IE9 throws a strange
  35. // security exception (perhaps due to cross-origin policy within data URIs?) Therefore we
  36. // work around this by converting the image data into a data URI itself using a transient
  37. // canvas. This unfortunately requires the border-image src to be within the same domain,
  38. // which isn't a limitation in true border-image, so we need to try and find a better fix.
  39. imgSrc = me.imageToDataURI( props.src, imgW, imgH ),
  40. REPEAT = me.REPEAT,
  41. STRETCH = me.STRETCH,
  42. ROUND = me.ROUND,
  43. ceil = Math.ceil,
  44. zero = PIE.getLength( '0' ),
  45. widths = props.widths || ( borderProps ? borderProps.widths : { 't': zero, 'r': zero, 'b': zero, 'l': zero } ),
  46. widthT = widths['t'].pixels( el ),
  47. widthR = widths['r'].pixels( el ),
  48. widthB = widths['b'].pixels( el ),
  49. widthL = widths['l'].pixels( el ),
  50. slices = props.slice,
  51. sliceT = slices['t'].pixels( el ),
  52. sliceR = slices['r'].pixels( el ),
  53. sliceB = slices['b'].pixels( el ),
  54. sliceL = slices['l'].pixels( el ),
  55. centerW = elW - widthL - widthR,
  56. middleH = elH - widthT - widthB,
  57. imgCenterW = imgW - sliceL - sliceR,
  58. imgMiddleH = imgH - sliceT - sliceB,
  59. // Determine the size of each tile - 'round' is handled below
  60. tileSizeT = repeatH === STRETCH ? centerW : imgCenterW * widthT / sliceT,
  61. tileSizeR = repeatV === STRETCH ? middleH : imgMiddleH * widthR / sliceR,
  62. tileSizeB = repeatH === STRETCH ? centerW : imgCenterW * widthB / sliceB,
  63. tileSizeL = repeatV === STRETCH ? middleH : imgMiddleH * widthL / sliceL,
  64. svg,
  65. patterns = [],
  66. rects = [],
  67. i = 0;
  68. // For 'round', subtract from each tile's size enough so that they fill the space a whole number of times
  69. if (repeatH === ROUND) {
  70. tileSizeT -= (tileSizeT - (centerW % tileSizeT || tileSizeT)) / ceil(centerW / tileSizeT);
  71. tileSizeB -= (tileSizeB - (centerW % tileSizeB || tileSizeB)) / ceil(centerW / tileSizeB);
  72. }
  73. if (repeatV === ROUND) {
  74. tileSizeR -= (tileSizeR - (middleH % tileSizeR || tileSizeR)) / ceil(middleH / tileSizeR);
  75. tileSizeL -= (tileSizeL - (middleH % tileSizeL || tileSizeL)) / ceil(middleH / tileSizeL);
  76. }
  77. // Build the SVG for the border-image rendering. Add each piece as a pattern, which is then stretched
  78. // or repeated as the fill of a rect of appropriate size.
  79. svg = [
  80. '<svg width="' + elW + '" height="' + elH + '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'
  81. ];
  82. function addImage( x, y, w, h, cropX, cropY, cropW, cropH, tileW, tileH ) {
  83. patterns.push(
  84. '<pattern patternUnits="userSpaceOnUse" id="pattern' + i + '" ' +
  85. 'x="' + (repeatH === REPEAT ? x + w / 2 - tileW / 2 : x) + '" ' +
  86. 'y="' + (repeatV === REPEAT ? y + h / 2 - tileH / 2 : y) + '" ' +
  87. 'width="' + tileW + '" height="' + tileH + '">' +
  88. '<svg width="' + tileW + '" height="' + tileH + '" viewBox="' + cropX + ' ' + cropY + ' ' + cropW + ' ' + cropH + '" preserveAspectRatio="none">' +
  89. '<image xlink:href="' + imgSrc + '" x="0" y="0" width="' + imgW + '" height="' + imgH + '" />' +
  90. '</svg>' +
  91. '</pattern>'
  92. );
  93. rects.push(
  94. '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" fill="url(#pattern' + i + ')" />'
  95. );
  96. i++;
  97. }
  98. addImage( 0, 0, widthL, widthT, 0, 0, sliceL, sliceT, widthL, widthT ); // top left
  99. addImage( widthL, 0, centerW, widthT, sliceL, 0, imgCenterW, sliceT, tileSizeT, widthT ); // top center
  100. addImage( elW - widthR, 0, widthR, widthT, imgW - sliceR, 0, sliceR, sliceT, widthR, widthT ); // top right
  101. addImage( 0, widthT, widthL, middleH, 0, sliceT, sliceL, imgMiddleH, widthL, tileSizeL ); // middle left
  102. if ( props.fill ) { // center fill
  103. addImage( widthL, widthT, centerW, middleH, sliceL, sliceT, imgCenterW, imgMiddleH,
  104. tileSizeT || tileSizeB || imgCenterW, tileSizeL || tileSizeR || imgMiddleH );
  105. }
  106. addImage( elW - widthR, widthT, widthR, middleH, imgW - sliceR, sliceT, sliceR, imgMiddleH, widthR, tileSizeR ); // middle right
  107. addImage( 0, elH - widthB, widthL, widthB, 0, imgH - sliceB, sliceL, sliceB, widthL, widthB ); // bottom left
  108. addImage( widthL, elH - widthB, centerW, widthB, sliceL, imgH - sliceB, imgCenterW, sliceB, tileSizeB, widthB ); // bottom center
  109. addImage( elW - widthR, elH - widthB, widthR, widthB, imgW - sliceR, imgH - sliceB, sliceR, sliceB, widthR, widthB ); // bottom right
  110. svg.push(
  111. '<defs>' +
  112. patterns.join('\n') +
  113. '</defs>' +
  114. rects.join('\n') +
  115. '</svg>'
  116. );
  117. me.parent.setBackgroundLayer( me.bgLayerZIndex, 'url(data:image/svg+xml,' + escape( svg.join( '' ) ) + ') no-repeat border-box border-box' );
  118. // If the border-image's src wasn't immediately available, the SVG for its background layer
  119. // will have been created asynchronously after the main element's update has finished; we'll
  120. // therefore need to force the root renderer to sync to the final background once finished.
  121. if( isAsync ) {
  122. me.parent.updateRendering();
  123. }
  124. }, me );
  125. isAsync = 1;
  126. },
  127. /**
  128. * Convert a given image to a data URI
  129. */
  130. imageToDataURI: (function() {
  131. var uris = {};
  132. return function( src, width, height ) {
  133. var uri = uris[ src ],
  134. image, canvas;
  135. if ( !uri ) {
  136. image = new Image();
  137. canvas = doc.createElement( 'canvas' );
  138. image.src = src;
  139. canvas.width = width;
  140. canvas.height = height;
  141. canvas.getContext( '2d' ).drawImage( image, 0, 0 );
  142. uri = uris[ src ] = canvas.toDataURL();
  143. }
  144. return uri;
  145. }
  146. })(),
  147. prepareUpdate: function() {
  148. if (this.isActive()) {
  149. var me = this,
  150. el = me.targetElement,
  151. rs = el.runtimeStyle,
  152. widths = me.styleInfos.borderImageInfo.getProps().widths;
  153. // Force border-style to solid so it doesn't collapse
  154. rs.borderStyle = 'solid';
  155. // If widths specified in border-image shorthand, override border-width
  156. if ( widths ) {
  157. rs.borderTopWidth = widths['t'].pixels( el ) + 'px';
  158. rs.borderRightWidth = widths['r'].pixels( el ) + 'px';
  159. rs.borderBottomWidth = widths['b'].pixels( el ) + 'px';
  160. rs.borderLeftWidth = widths['l'].pixels( el ) + 'px';
  161. }
  162. // Make the border transparent
  163. me.hideBorder();
  164. }
  165. },
  166. destroy: function() {
  167. var me = this,
  168. rs = me.targetElement.runtimeStyle;
  169. me.parent.setBackgroundLayer( me.bgLayerZIndex );
  170. rs.borderColor = rs.borderStyle = rs.borderWidth = '';
  171. }
  172. } );