tennpipes-init 3.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +163 -0
  4. data/Rakefile +1 -0
  5. data/bin/tennpipes-init +16 -0
  6. data/lib/tennpipes-init.rb +73 -0
  7. data/lib/tennpipes-init/command.rb +18 -0
  8. data/lib/tennpipes-init/generators/actions.rb +630 -0
  9. data/lib/tennpipes-init/generators/app.rb +75 -0
  10. data/lib/tennpipes-init/generators/app/app.rb.tt +72 -0
  11. data/lib/tennpipes-init/generators/app/app.rb.tt~ +72 -0
  12. data/lib/tennpipes-init/generators/cli.rb +57 -0
  13. data/lib/tennpipes-init/generators/component.rb +73 -0
  14. data/lib/tennpipes-init/generators/components/actions.rb +208 -0
  15. data/lib/tennpipes-init/generators/components/mocks/mocha.rb +10 -0
  16. data/lib/tennpipes-init/generators/components/mocks/rr.rb +13 -0
  17. data/lib/tennpipes-init/generators/components/orms/activerecord.rb +201 -0
  18. data/lib/tennpipes-init/generators/components/orms/couchrest.rb +55 -0
  19. data/lib/tennpipes-init/generators/components/orms/datamapper.rb +140 -0
  20. data/lib/tennpipes-init/generators/components/orms/dynamoid.rb +67 -0
  21. data/lib/tennpipes-init/generators/components/orms/minirecord.rb +165 -0
  22. data/lib/tennpipes-init/generators/components/orms/mongoid.rb +113 -0
  23. data/lib/tennpipes-init/generators/components/orms/mongomapper.rb +43 -0
  24. data/lib/tennpipes-init/generators/components/orms/mongomatic.rb +84 -0
  25. data/lib/tennpipes-init/generators/components/orms/ohm.rb +65 -0
  26. data/lib/tennpipes-init/generators/components/orms/ripple.rb +75 -0
  27. data/lib/tennpipes-init/generators/components/orms/sequel.rb +99 -0
  28. data/lib/tennpipes-init/generators/components/renderers/erb.rb +3 -0
  29. data/lib/tennpipes-init/generators/components/renderers/haml.rb +3 -0
  30. data/lib/tennpipes-init/generators/components/renderers/liquid.rb +4 -0
  31. data/lib/tennpipes-init/generators/components/renderers/slim.rb +3 -0
  32. data/lib/tennpipes-init/generators/components/scripts/dojo.rb +10 -0
  33. data/lib/tennpipes-init/generators/components/scripts/extcore.rb +10 -0
  34. data/lib/tennpipes-init/generators/components/scripts/jquery.rb +10 -0
  35. data/lib/tennpipes-init/generators/components/scripts/mootools.rb +10 -0
  36. data/lib/tennpipes-init/generators/components/scripts/prototype.rb +12 -0
  37. data/lib/tennpipes-init/generators/components/scripts/rightjs.rb +10 -0
  38. data/lib/tennpipes-init/generators/components/stylesheets/compass.rb +39 -0
  39. data/lib/tennpipes-init/generators/components/stylesheets/compass/application.scss +43 -0
  40. data/lib/tennpipes-init/generators/components/stylesheets/compass/partials/_base.scss +12 -0
  41. data/lib/tennpipes-init/generators/components/stylesheets/less.rb +25 -0
  42. data/lib/tennpipes-init/generators/components/stylesheets/sass.rb +15 -0
  43. data/lib/tennpipes-init/generators/components/stylesheets/scss.rb +16 -0
  44. data/lib/tennpipes-init/generators/components/tests/bacon.rb +103 -0
  45. data/lib/tennpipes-init/generators/components/tests/cucumber.rb +86 -0
  46. data/lib/tennpipes-init/generators/components/tests/minitest.rb +110 -0
  47. data/lib/tennpipes-init/generators/components/tests/riot.rb +117 -0
  48. data/lib/tennpipes-init/generators/components/tests/rspec.rb +111 -0
  49. data/lib/tennpipes-init/generators/components/tests/shoulda.rb +114 -0
  50. data/lib/tennpipes-init/generators/components/tests/steak.rb +114 -0
  51. data/lib/tennpipes-init/generators/controller.rb +77 -0
  52. data/lib/tennpipes-init/generators/helper.rb +50 -0
  53. data/lib/tennpipes-init/generators/mailer.rb +52 -0
  54. data/lib/tennpipes-init/generators/migration.rb +43 -0
  55. data/lib/tennpipes-init/generators/model.rb +113 -0
  56. data/lib/tennpipes-init/generators/plugin.rb +67 -0
  57. data/lib/tennpipes-init/generators/project.rb +160 -0
  58. data/lib/tennpipes-init/generators/project/config.ru +9 -0
  59. data/lib/tennpipes-init/generators/project/config/apps.rb.tt +35 -0
  60. data/lib/tennpipes-init/generators/project/config/boot.rb +49 -0
  61. data/lib/tennpipes-init/generators/project/public/favicon.ico +0 -0
  62. data/lib/tennpipes-init/generators/project/public/images/booking.com.png +0 -0
  63. data/lib/tennpipes-init/generators/project/public/images/causes.png +0 -0
  64. data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bg.jpg +0 -0
  65. data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bluelogo.png +0 -0
  66. data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bluelogo.svg +68 -0
  67. data/lib/tennpipes-init/generators/project/public/images/ennkeypee_whitelogo.png +0 -0
  68. data/lib/tennpipes-init/generators/project/public/images/ennkeypee_whitelogo.svg +65 -0
  69. data/lib/tennpipes-init/generators/project/public/images/forever21.png +0 -0
  70. data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.eot +0 -0
  71. data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.svg +970 -0
  72. data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.ttf +0 -0
  73. data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.woff +0 -0
  74. data/lib/tennpipes-init/generators/project/public/images/icons/tennpipes-icons.css +598 -0
  75. data/lib/tennpipes-init/generators/project/public/images/image1.jpg +0 -0
  76. data/lib/tennpipes-init/generators/project/public/images/image11.jpg +0 -0
  77. data/lib/tennpipes-init/generators/project/public/images/image2.jpg +0 -0
  78. data/lib/tennpipes-init/generators/project/public/images/image3.jpg +0 -0
  79. data/lib/tennpipes-init/generators/project/public/images/intuit.png +0 -0
  80. data/lib/tennpipes-init/generators/project/public/images/krispykreme.png +0 -0
  81. data/lib/tennpipes-init/generators/project/public/images/placeholders/README.md +302 -0
  82. data/lib/tennpipes-init/generators/project/public/images/placeholders/bower.json +4 -0
  83. data/lib/tennpipes-init/generators/project/public/images/placeholders/composer.json +34 -0
  84. data/lib/tennpipes-init/generators/project/public/images/placeholders/gulpfile.js +92 -0
  85. data/lib/tennpipes-init/generators/project/public/images/placeholders/holder.js +1920 -0
  86. data/lib/tennpipes-init/generators/project/public/images/placeholders/holder.min.js +12 -0
  87. data/lib/tennpipes-init/generators/project/public/images/placeholders/package.json +46 -0
  88. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/holder.js +1411 -0
  89. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/augment.js +27 -0
  90. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/ondomready.js +155 -0
  91. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/polyfills.js +177 -0
  92. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/scenegraph.js +101 -0
  93. data/lib/tennpipes-init/generators/project/public/images/placeholders/src/utils.js +129 -0
  94. data/lib/tennpipes-init/generators/project/public/images/priceline.com.png +0 -0
  95. data/lib/tennpipes-init/generators/project/public/images/stripe.png +0 -0
  96. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes.min.js +6081 -0
  97. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.abide.js +340 -0
  98. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.accordion.js +67 -0
  99. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.alert.js +43 -0
  100. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.clearing.js +556 -0
  101. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.dropdown.js +448 -0
  102. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.equalizer.js +77 -0
  103. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.interchange.js +354 -0
  104. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.joyride.js +932 -0
  105. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.js +703 -0
  106. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.magellan.js +203 -0
  107. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.offcanvas.js +152 -0
  108. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.orbit.js +476 -0
  109. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.reveal.js +471 -0
  110. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.slider.js +263 -0
  111. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.tab.js +237 -0
  112. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.tooltip.js +307 -0
  113. data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.topbar.js +452 -0
  114. data/lib/tennpipes-init/generators/project/public/javascripts/vendor/fastclick.js +8 -0
  115. data/lib/tennpipes-init/generators/project/public/javascripts/vendor/jquery.cookie.js +8 -0
  116. data/lib/tennpipes-init/generators/project/public/javascripts/vendor/jquery.js +26 -0
  117. data/lib/tennpipes-init/generators/project/public/javascripts/vendor/modernizr.js +8 -0
  118. data/lib/tennpipes-init/generators/project/public/javascripts/vendor/placeholder.js +2 -0
  119. data/lib/tennpipes-init/generators/project/public/stylesheets/app.css~ +178 -0
  120. data/lib/tennpipes-init/generators/project/public/stylesheets/app1.css~ +177 -0
  121. data/lib/tennpipes-init/generators/project/public/stylesheets/ennkeypee.css +214 -0
  122. data/lib/tennpipes-init/generators/project/public/stylesheets/ennkeypee.css~ +214 -0
  123. data/lib/tennpipes-init/generators/project/public/stylesheets/normalize.css +427 -0
  124. data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.css +6201 -0
  125. data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.css~ +6201 -0
  126. data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.min.css +1 -0
  127. data/lib/tennpipes-init/generators/runner.rb +139 -0
  128. data/lib/tennpipes-init/generators/task.rb +45 -0
  129. data/lib/tennpipes-init/generators/templates/Gemfile.tt +32 -0
  130. data/lib/tennpipes-init/generators/templates/Rakefile.tt +8 -0
  131. data/lib/tennpipes-init/generators/templates/controller.rb.tt +22 -0
  132. data/lib/tennpipes-init/generators/templates/gem/README.md.tt +29 -0
  133. data/lib/tennpipes-init/generators/templates/gem/gemspec.tt +19 -0
  134. data/lib/tennpipes-init/generators/templates/gem/lib/libname.tt +6 -0
  135. data/lib/tennpipes-init/generators/templates/gem/lib/libname/version.tt +3 -0
  136. data/lib/tennpipes-init/generators/templates/helper.rb.tt +13 -0
  137. data/lib/tennpipes-init/generators/templates/initializer.rb.tt +5 -0
  138. data/lib/tennpipes-init/generators/templates/mailer.rb.tt +54 -0
  139. data/lib/tennpipes-init/generators/templates/project_bin.tt +14 -0
  140. data/lib/tennpipes-init/generators/templates/task.rb.tt +7 -0
  141. data/lib/tennpipes-init/tennpipes-tasks/activerecord.rb +377 -0
  142. data/lib/tennpipes-init/tennpipes-tasks/database.rb +12 -0
  143. data/lib/tennpipes-init/tennpipes-tasks/datamapper.rb +94 -0
  144. data/lib/tennpipes-init/tennpipes-tasks/minirecord.rb +19 -0
  145. data/lib/tennpipes-init/tennpipes-tasks/mongoid.rb +215 -0
  146. data/lib/tennpipes-init/tennpipes-tasks/mongomapper.rb +55 -0
  147. data/lib/tennpipes-init/tennpipes-tasks/sequel.rb +85 -0
  148. data/lib/tennpipes-init/tennpipes-tasks/sql-helpers.rb +72 -0
  149. data/test/fixtures/admin_template.rb +7 -0
  150. data/test/fixtures/example_template.rb +15 -0
  151. data/test/fixtures/git_template.rb +4 -0
  152. data/test/fixtures/plugin_template.rb +13 -0
  153. data/test/fixtures/rake_template.rb +9 -0
  154. data/test/helper.rb +103 -0
  155. data/test/test_app_generator.rb +142 -0
  156. data/test/test_cli.rb +27 -0
  157. data/test/test_component_generator.rb +98 -0
  158. data/test/test_controller_generator.rb +272 -0
  159. data/test/test_generator.rb +13 -0
  160. data/test/test_helper_generator.rb +133 -0
  161. data/test/test_mailer_generator.rb +69 -0
  162. data/test/test_migration_generator.rb +222 -0
  163. data/test/test_model_generator.rb +553 -0
  164. data/test/test_plugin_generator.rb +152 -0
  165. data/test/test_project_generator.rb +757 -0
  166. data/test/test_task_generator.rb +53 -0
  167. metadata +285 -0
@@ -0,0 +1,12 @@
1
+ /*!
2
+
3
+ Holder - client side image placeholders
4
+ Version 2.6.0+51ebp
5
+ © 2015 Ivan Malopinsky - http://imsky.co
6
+
7
+ Site: http://holderjs.com
8
+ Issues: https://github.com/imsky/holder/issues
9
+ License: http://opensource.org/licenses/MIT
10
+
11
+ */
12
+ !function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Holder=b():a.Holder=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){(function(b){function d(a,b,c,d){var g=e(c.substr(c.lastIndexOf(a.domain)),a);g&&f({mode:null,el:d,flags:g,engineSettings:b})}function e(a,b){for(var c={theme:y(K.settings.themes.gray,null),stylesheets:b.stylesheets,holderURL:[]},d=!1,e=String.fromCharCode(11),f=a.replace(/([^\\])\//g,"$1"+e).split(e),g=/%[0-9a-f]{2}/gi,h=f.length,i=0;h>i;i++){var j=f[i];if(j.match(g))try{j=decodeURIComponent(j)}catch(k){j=f[i]}var l=!1;if(K.flags.dimensions.match(j))d=!0,c.dimensions=K.flags.dimensions.output(j),l=!0;else if(K.flags.fluid.match(j))d=!0,c.dimensions=K.flags.fluid.output(j),c.fluid=!0,l=!0;else if(K.flags.textmode.match(j))c.textmode=K.flags.textmode.output(j),l=!0;else if(K.flags.colors.match(j)){var m=K.flags.colors.output(j);c.theme=y(c.theme,m),l=!0}else if(b.themes[j])b.themes.hasOwnProperty(j)&&(c.theme=y(b.themes[j],null)),l=!0;else if(K.flags.font.match(j))c.font=K.flags.font.output(j),l=!0;else if(K.flags.auto.match(j))c.auto=!0,l=!0;else if(K.flags.text.match(j))c.text=K.flags.text.output(j),l=!0;else if(K.flags.size.match(j))c.size=K.flags.size.output(j),l=!0;else if(K.flags.random.match(j)){null==K.vars.cache.themeKeys&&(K.vars.cache.themeKeys=Object.keys(b.themes));var n=K.vars.cache.themeKeys[0|Math.random()*K.vars.cache.themeKeys.length];c.theme=y(b.themes[n],null),l=!0}l&&c.holderURL.push(j)}return c.holderURL.unshift(b.domain),c.holderURL=c.holderURL.join("/"),d?c:!1}function f(a){var b=a.mode,c=a.el,d=a.flags,e=a.engineSettings,f=d.dimensions,h=d.theme,i=f.width+"x"+f.height;if(b=null==b?d.fluid?"fluid":"image":b,null!=d.text&&(h.text=d.text,"object"===c.nodeName.toLowerCase())){for(var l=h.text.split("\\n"),m=0;m<l.length;m++)l[m]=A(l[m]);h.text=l.join("\\n")}var n=d.holderURL,o=y(e,null);if(d.font&&(h.font=d.font,!o.noFontFallback&&"img"===c.nodeName.toLowerCase()&&K.setup.supportsCanvas&&"svg"===o.renderer&&(o=y(o,{renderer:"canvas"}))),d.font&&"canvas"==o.renderer&&(o.reRender=!0),"background"==b)null==c.getAttribute("data-background-src")&&p(c,{"data-background-src":n});else{var q={};q[K.vars.dataAttr]=n,p(c,q)}d.theme=h,c.holderData={flags:d,engineSettings:o},("image"==b||"fluid"==b)&&p(c,{alt:h.text?h.text+" ["+i+"]":i});var r={mode:b,el:c,holderSettings:{dimensions:f,theme:h,flags:d},engineSettings:o};"image"==b?("html"!=o.renderer&&d.auto||(c.style.width=f.width+"px",c.style.height=f.height+"px"),"html"==o.renderer?c.style.backgroundColor=h.background:(g(r),"exact"==d.textmode&&(c.holderData.resizeUpdate=!0,K.vars.resizableImages.push(c),j(c)))):"background"==b&&"html"!=o.renderer?g(r):"fluid"==b&&(c.holderData.resizeUpdate=!0,"%"==f.height.slice(-1)?c.style.height=f.height:null!=d.auto&&d.auto||(c.style.height=f.height+"px"),"%"==f.width.slice(-1)?c.style.width=f.width:null!=d.auto&&d.auto||(c.style.width=f.width+"px"),("inline"==c.style.display||""===c.style.display||"none"==c.style.display)&&(c.style.display="block"),k(c),"html"==o.renderer?c.style.backgroundColor=h.background:(K.vars.resizableImages.push(c),j(c)))}function g(a){function c(){var b=null;switch(i.renderer){case"canvas":b=M(k,a);break;case"svg":b=N(k,a);break;default:throw"Holder: invalid renderer: "+i.renderer}return b}var d=null,e=a.mode,f=a.holderSettings,g=a.el,i=a.engineSettings;switch(i.renderer){case"svg":if(!K.setup.supportsSVG)return;break;case"canvas":if(!K.setup.supportsCanvas)return;break;default:return}var j={width:f.dimensions.width,height:f.dimensions.height,theme:f.theme,flags:f.flags},k=h(j);if(d=c(),null==d)throw"Holder: couldn't render placeholder";"background"==e?(g.style.backgroundImage="url("+d+")",g.style.backgroundSize=j.width+"px "+j.height+"px"):("img"===g.nodeName.toLowerCase()?p(g,{src:d}):"object"===g.nodeName.toLowerCase()&&(p(g,{data:d}),p(g,{type:"image/svg+xml"})),i.reRender&&b.setTimeout(function(){var a=c();if(null==a)throw"Holder: couldn't render placeholder";"img"===g.nodeName.toLowerCase()?p(g,{src:a}):"object"===g.nodeName.toLowerCase()&&(p(g,{data:a}),p(g,{type:"image/svg+xml"}))},100)),p(g,{"data-holder-rendered":!0})}function h(a){function b(a,b,c,d){b.width=c,b.height=d,a.width=Math.max(a.width,b.width),a.height+=b.height,a.add(b)}var c=K.defaults.size;switch(parseFloat(a.theme.size)?c=a.theme.size:parseFloat(a.flags.size)&&(c=a.flags.size),a.font={family:a.theme.font?a.theme.font:"Arial, Helvetica, Open Sans, sans-serif",size:i(a.width,a.height,c),units:a.theme.units?a.theme.units:K.defaults.units,weight:a.theme.fontweight?a.theme.fontweight:"bold"},a.text=a.theme.text?a.theme.text:Math.floor(a.width)+"x"+Math.floor(a.height),a.flags.textmode){case"literal":a.text=a.flags.dimensions.width+"x"+a.flags.dimensions.height;break;case"exact":if(!a.flags.exactDimensions)break;a.text=Math.floor(a.flags.exactDimensions.width)+"x"+Math.floor(a.flags.exactDimensions.height)}var d=new w({width:a.width,height:a.height}),e=d.Shape,f=new e.Rect("holderBg",{fill:a.theme.background});f.resize(a.width,a.height),d.root.add(f);var g=new e.Group("holderTextGroup",{text:a.text,align:"center",font:a.font,fill:a.theme.foreground});g.moveTo(null,null,1),d.root.add(g);var h=g.textPositionData=L(d);if(!h)throw"Holder: staging fallback not supported yet.";g.properties.leading=h.boundingBox.height;var j=null,k=null;if(h.lineCount>1){var l=0,m=0,n=a.width*K.setup.lineWrapRatio,o=0;k=new e.Group("line"+o);for(var p=0;p<h.words.length;p++){var q=h.words[p];j=new e.Text(q.text);var r="\\n"==q.text;(l+q.width>=n||r===!0)&&(b(g,k,l,g.properties.leading),l=0,m+=g.properties.leading,o+=1,k=new e.Group("line"+o),k.y=m),r!==!0&&(j.moveTo(l,0),l+=h.spaceWidth+q.width,k.add(j))}b(g,k,l,g.properties.leading);for(var s in g.children)k=g.children[s],k.moveTo((g.width-k.width)/2,null,null);g.moveTo((a.width-g.width)/2,(a.height-g.height)/2,null),(a.height-g.height)/2<0&&g.moveTo(null,0,null)}else j=new e.Text(a.text),k=new e.Group("line0"),k.add(j),g.add(k),g.moveTo((a.width-h.boundingBox.width)/2,(a.height-h.boundingBox.height)/2,null);return d}function i(a,b,c){var d=parseInt(a,10),e=parseInt(b,10),f=Math.max(d,e),g=Math.min(d,e),h=.8*Math.min(g,f*K.defaults.scale);return Math.round(Math.max(c,h))}function j(a){var b;b=null==a||null==a.nodeType?K.vars.resizableImages:[a];for(var c=0,d=b.length;d>c;c++){var e=b[c];if(e.holderData){var f=e.holderData.flags,h=E(e);if(h){if(!e.holderData.resizeUpdate)continue;if(f.fluid&&f.auto){var i=e.holderData.fluidConfig;switch(i.mode){case"width":h.height=h.width/i.ratio;break;case"height":h.width=h.height*i.ratio}}var j={mode:"image",holderSettings:{dimensions:h,theme:f.theme,flags:f},el:e,engineSettings:e.holderData.engineSettings};"exact"==f.textmode&&(f.exactDimensions=h,j.holderSettings.dimensions=f.dimensions),g(j)}else n(e)}}}function k(a){if(a.holderData){var b=E(a);if(b){var c=a.holderData.flags,d={fluidHeight:"%"==c.dimensions.height.slice(-1),fluidWidth:"%"==c.dimensions.width.slice(-1),mode:null,initialDimensions:b};d.fluidWidth&&!d.fluidHeight?(d.mode="width",d.ratio=d.initialDimensions.width/parseFloat(c.dimensions.height)):!d.fluidWidth&&d.fluidHeight&&(d.mode="height",d.ratio=parseFloat(c.dimensions.width)/d.initialDimensions.height),a.holderData.fluidConfig=d}else n(a)}}function l(){for(var a,c=[],d=Object.keys(K.vars.invisibleImages),e=0,f=d.length;f>e;e++)a=K.vars.invisibleImages[d[e]],E(a)&&"img"==a.nodeName.toLowerCase()&&(c.push(a),delete K.vars.invisibleImages[d[e]]);c.length&&J.run({images:c}),b.requestAnimationFrame(l)}function m(){K.vars.visibilityCheckStarted||(b.requestAnimationFrame(l),K.vars.visibilityCheckStarted=!0)}function n(a){a.holderData.invisibleId||(K.vars.invisibleId+=1,K.vars.invisibleImages["i"+K.vars.invisibleId]=a,a.holderData.invisibleId=K.vars.invisibleId)}function o(a,b){return null==b?document.createElement(a):document.createElementNS(b,a)}function p(a,b){for(var c in b)a.setAttribute(c,b[c])}function q(a,b,c){var d,e;null==a?(a=o("svg",F),d=o("defs",F),e=o("style",F),p(e,{type:"text/css"}),d.appendChild(e),a.appendChild(d)):e=a.querySelector("style"),a.webkitMatchesSelector&&a.setAttribute("xmlns",F);for(var f=0;f<a.childNodes.length;f++)a.childNodes[f].nodeType===G&&a.removeChild(a.childNodes[f]);for(;e.childNodes.length;)e.removeChild(e.childNodes[0]);return p(a,{width:b,height:c,viewBox:"0 0 "+b+" "+c,preserveAspectRatio:"none"}),a}function r(a,c){if(b.XMLSerializer){var d=new XMLSerializer,e="",f=c.stylesheets;if(c.svgXMLStylesheet){for(var g=s(),h=f.length-1;h>=0;h--){var i=g.createProcessingInstruction("xml-stylesheet",'href="'+f[h]+'" rel="stylesheet"');g.insertBefore(i,g.firstChild)}var j=g.createProcessingInstruction("xml",'version="1.0" encoding="UTF-8" standalone="yes"');g.insertBefore(j,g.firstChild),g.removeChild(g.documentElement),e=d.serializeToString(g)}var k=d.serializeToString(a);return k=k.replace(/\&amp;(\#[0-9]{2,}\;)/g,"&$1"),e+k}}function s(){return b.DOMParser?(new DOMParser).parseFromString("<xml />","application/xml"):void 0}function t(a){K.vars.debounceTimer||a.call(this),K.vars.debounceTimer&&b.clearTimeout(K.vars.debounceTimer),K.vars.debounceTimer=b.setTimeout(function(){K.vars.debounceTimer=null,a.call(this)},K.setup.debounce)}function u(){t(function(){j(null)})}var v=c(1),w=c(2),x=c(3),y=x.extend,z=x.cssProps,A=x.encodeHtmlEntity,B=x.decodeHtmlEntity,C=x.imageExists,D=x.getNodeArray,E=x.dimensionCheck,F="http://www.w3.org/2000/svg",G=8,H="2.6.0",I="\nCreated with Holder.js "+H+".\nLearn more at http://holderjs.com\n(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n",J={version:H,addTheme:function(a,b){return null!=a&&null!=b&&(K.settings.themes[a]=b),delete K.vars.cache.themeKeys,this},addImage:function(a,b){var c=document.querySelectorAll(b);if(c.length)for(var d=0,e=c.length;e>d;d++){var f=o("img"),g={};g[K.vars.dataAttr]=a,p(f,g),c[d].appendChild(f)}return this},setResizeUpdate:function(a,b){a.holderData&&(a.holderData.resizeUpdate=!!b,a.holderData.resizeUpdate&&j(a))},run:function(a){a=a||{};var c={},g=y(K.settings,a);K.vars.preempted=!0,K.vars.dataAttr=g.dataAttr||K.vars.dataAttr,c.renderer=g.renderer?g.renderer:K.setup.renderer,-1===K.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=K.setup.supportsSVG?"svg":K.setup.supportsCanvas?"canvas":"html");var h=D(g.images),i=D(g.bgnodes),j=D(g.stylenodes),k=D(g.objects);c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=g.noFontFallback?g.noFontFallback:!1;for(var l=0;l<j.length;l++){var m=j[l];if(m.attributes.rel&&m.attributes.href&&"stylesheet"==m.attributes.rel.value){var n=m.attributes.href.value,p=o("a");p.href=n;var q=p.protocol+"//"+p.host+p.pathname+p.search;c.stylesheets.push(q)}}for(l=0;l<i.length;l++)if(b.getComputedStyle){var r=b.getComputedStyle(i[l],null).getPropertyValue("background-image"),s=i[l].getAttribute("data-background-src"),t=null;t=null==s?r:s;var u=null,v="?"+g.domain+"/";if(0===t.indexOf(v))u=t.slice(1);else if(-1!=t.indexOf(v)){var w=t.substr(t.indexOf(v)).slice(1),x=w.match(/([^\"]*)"?\)/);null!=x&&(u=x[1])}if(null!=u){var z=e(u,g);z&&f({mode:"background",el:i[l],flags:z,engineSettings:c})}}for(l=0;l<k.length;l++){var A=k[l],B={};try{B.data=A.getAttribute("data"),B.dataSrc=A.getAttribute(K.vars.dataAttr)}catch(E){}var F=null!=B.data&&0===B.data.indexOf(g.domain),G=null!=B.dataSrc&&0===B.dataSrc.indexOf(g.domain);F?d(g,c,B.data,A):G&&d(g,c,B.dataSrc,A)}for(l=0;l<h.length;l++){var H=h[l],I={};try{I.src=H.getAttribute("src"),I.dataSrc=H.getAttribute(K.vars.dataAttr),I.rendered=H.getAttribute("data-holder-rendered")}catch(E){}var J=null!=I.src,L=null!=I.dataSrc&&0===I.dataSrc.indexOf(g.domain),M=null!=I.rendered&&"true"==I.rendered;J?0===I.src.indexOf(g.domain)?d(g,c,I.src,H):L&&(M?d(g,c,I.dataSrc,H):!function(a,b,c,e,f){C(a,function(a){a||d(b,c,e,f)})}(I.src,g,c,I.dataSrc,H)):L&&d(g,c,I.dataSrc,H)}return this}},K={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",stylesheets:[],themes:{gray:{background:"#EEEEEE",foreground:"#AAAAAA"},social:{background:"#3a5a97",foreground:"#FFFFFF"},industrial:{background:"#434A52",foreground:"#C2F200"},sky:{background:"#0D8FDB",foreground:"#FFFFFF"},vine:{background:"#39DBAC",foreground:"#1E292C"},lava:{background:"#F8591A",foreground:"#1C2846"}}},defaults:{size:10,units:"pt",scale:1/16},flags:{dimensions:{regex:/^(\d+)x(\d+)$/,output:function(a){var b=this.regex.exec(a);return{width:+b[1],height:+b[2]}}},fluid:{regex:/^([0-9]+%?)x([0-9]+%?)$/,output:function(a){var b=this.regex.exec(a);return{width:b[1],height:b[2]}}},colors:{regex:/(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,output:function(a){var b=this.regex.exec(a);return{foreground:"#"+b[2],background:"#"+b[1]}}},text:{regex:/text\:(.*)/,output:function(a){return this.regex.exec(a)[1].replace("\\/","/")}},font:{regex:/font\:(.*)/,output:function(a){return this.regex.exec(a)[1]}},auto:{regex:/^auto$/},textmode:{regex:/textmode\:(.*)/,output:function(a){return this.regex.exec(a)[1]}},random:{regex:/^random$/},size:{regex:/size\:(\d+)/,output:function(a){return this.regex.exec(a)[1]}}}},L=function(){var a=null,b=null,c=null;return function(d){var e=d.root;if(K.setup.supportsSVG){var f=!1,g=function(a){return document.createTextNode(a)};(null==a||a.parentNode!==document.body)&&(f=!0),a=q(a,e.properties.width,e.properties.height),a.style.display="block",f&&(b=o("text",F),c=g(null),p(b,{x:0}),b.appendChild(c),a.appendChild(b),document.body.appendChild(a),a.style.visibility="hidden",a.style.position="absolute",a.style.top="-100%",a.style.left="-100%");var h=e.children.holderTextGroup,i=h.properties;p(b,{y:i.font.size,style:z({"font-weight":i.font.weight,"font-size":i.font.size+i.font.units,"font-family":i.font.family})}),c.nodeValue=i.text;var j=b.getBBox(),k=Math.ceil(j.width/(e.properties.width*K.setup.lineWrapRatio)),l=i.text.split(" "),m=i.text.match(/\\n/g);k+=null==m?0:m.length,c.nodeValue=i.text.replace(/[ ]+/g,"");var n=b.getComputedTextLength(),r=j.width-n,s=Math.round(r/Math.max(1,l.length-1)),t=[];if(k>1){c.nodeValue="";for(var u=0;u<l.length;u++)if(0!==l[u].length){c.nodeValue=B(l[u]);var v=b.getBBox();t.push({text:l[u],width:v.width})}}return a.style.display="none",{spaceWidth:s,lineCount:k,boundingBox:j,words:t}}return!1}}(),M=function(){var a=o("canvas"),b=null;return function(c){null==b&&(b=a.getContext("2d"));var d=c.root;a.width=K.dpr(d.properties.width),a.height=K.dpr(d.properties.height),b.textBaseline="middle",b.fillStyle=d.children.holderBg.properties.fill,b.fillRect(0,0,K.dpr(d.children.holderBg.width),K.dpr(d.children.holderBg.height));{var e=d.children.holderTextGroup;e.properties}b.font=e.properties.font.weight+" "+K.dpr(e.properties.font.size)+e.properties.font.units+" "+e.properties.font.family+", monospace",b.fillStyle=e.properties.fill;for(var f in e.children){var g=e.children[f];for(var h in g.children){var i=g.children[h],j=K.dpr(e.x+g.x+i.x),k=K.dpr(e.y+g.y+i.y+e.properties.leading/2);b.fillText(i.properties.text,j,k)}}return a.toDataURL("image/png")}}(),N=function(){if(b.XMLSerializer){var a=s(),c=q(null,0,0),d=o("rect",F);return c.appendChild(d),function(b,e){var f=b.root;q(c,f.properties.width,f.properties.height);for(var g=c.querySelectorAll("g"),h=0;h<g.length;h++)g[h].parentNode.removeChild(g[h]);var i=e.holderSettings.flags.holderURL,j="holder_"+(Number(new Date)+32768+(0|32768*Math.random())).toString(16),k=o("g",F),l=f.children.holderTextGroup,m=l.properties,n=o("g",F),s=l.textPositionData,t="#"+j+" text { "+z({fill:m.fill,"font-weight":m.font.weight,"font-family":m.font.family+", monospace","font-size":m.font.size+m.font.units})+" } ",u=a.createComment("\nSource URL: "+i+I),v=a.createCDATASection(t),w=c.querySelector("style");p(k,{id:j}),c.insertBefore(u,c.firstChild),w.appendChild(v),k.appendChild(d),k.appendChild(n),c.appendChild(k),p(d,{width:f.children.holderBg.width,height:f.children.holderBg.height,fill:f.children.holderBg.properties.fill}),l.y+=.8*s.boundingBox.height;for(var x in l.children){var y=l.children[x];for(var A in y.children){var B=y.children[A],C=l.x+y.x+B.x,D=l.y+y.y+B.y,E=o("text",F),G=document.createTextNode(null);p(E,{x:C,y:D}),G.nodeValue=B.properties.text,E.appendChild(G),n.appendChild(E)}}var H="data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(r(c,e.engineSettings))));return H}}}();for(var O in K.flags)K.flags.hasOwnProperty(O)&&(K.flags[O].match=function(a){return a.match(this.regex)});K.setup={renderer:"html",debounce:100,ratio:1,supportsCanvas:!1,supportsSVG:!1,lineWrapRatio:.9,renderers:["html","canvas","svg"]},K.dpr=function(a){return a*K.setup.ratio},K.vars={preempted:!1,resizableImages:[],invisibleImages:{},invisibleId:0,visibilityCheckStarted:!1,debounceTimer:null,cache:{},dataAttr:"data-src"},function(){var a=1,c=1,d=o("canvas"),e=null;d.getContext&&-1!=d.toDataURL("image/png").indexOf("data:image/png")&&(K.setup.renderer="canvas",e=d.getContext("2d"),K.setup.supportsCanvas=!0),K.setup.supportsCanvas&&(a=b.devicePixelRatio||1,c=e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1),K.setup.ratio=a/c,document.createElementNS&&document.createElementNS(F,"svg").createSVGRect&&(K.setup.renderer="svg",K.setup.supportsSVG=!0)}(),m(),v&&v(function(){K.vars.preempted||J.run(),b.addEventListener?(b.addEventListener("resize",u,!1),b.addEventListener("orientationchange",u,!1)):b.attachEvent("onresize",u),"object"==typeof b.Turbolinks&&b.document.addEventListener("page:change",function(){J.run()})}),a.exports=J}).call(b,function(){return this}())},function(a){function b(a){function b(a){if(!v){if(!g.body)return e(b);for(v=!0;a=w.shift();)e(a)}}function c(a){(t||a.type===i||g[m]===l)&&(d(),b())}function d(){t?(g[s](q,c,j),a[s](i,c,j)):(g[o](r,c),a[o](k,c))}function e(a,b){setTimeout(a,+b>=0?b:1)}function f(a){v?e(a):w.push(a)}null==document.readyState&&document.addEventListener&&(document.addEventListener("DOMContentLoaded",function y(){document.removeEventListener("DOMContentLoaded",y,!1),document.readyState="complete"},!1),document.readyState="loading");var g=a.document,h=g.documentElement,i="load",j=!1,k="on"+i,l="complete",m="readyState",n="attachEvent",o="detachEvent",p="addEventListener",q="DOMContentLoaded",r="onreadystatechange",s="removeEventListener",t=p in g,u=j,v=j,w=[];if(g[m]===l)e(b);else if(t)g[p](q,c,j),a[p](i,c,j);else{g[n](r,c),a[n](k,c);try{u=null==a.frameElement&&h}catch(x){}u&&u.doScroll&&!function z(){if(!v){try{u.doScroll("left")}catch(a){return e(z,50)}d(),b()}}()}return f.version="1.4.0",f.isReady=function(){return v},f}a.exports="undefined"!=typeof window&&b(window)},function(a,b,c){var d=c(4),e=function(a){function b(a,b){for(var c in b)a[c]=b[c];return a}var c=1,e=d.defclass({constructor:function(a){c++,this.parent=null,this.children={},this.id=c,this.name="n"+c,null!=a&&(this.name=a),this.x=0,this.y=0,this.z=0,this.width=0,this.height=0},resize:function(a,b){null!=a&&(this.width=a),null!=b&&(this.height=b)},moveTo:function(a,b,c){this.x=null!=a?a:this.x,this.y=null!=b?b:this.y,this.z=null!=c?c:this.z},add:function(a){var b=a.name;if(null!=this.children[b])throw"SceneGraph: child with that name already exists: "+b;this.children[b]=a,a.parent=this}}),f=d(e,function(b){this.constructor=function(){b.constructor.call(this,"root"),this.properties=a}}),g=d(e,function(a){function c(c,d){if(a.constructor.call(this,c),this.properties={fill:"#000"},null!=d)b(this.properties,d);else if(null!=c&&"string"!=typeof c)throw"SceneGraph: invalid node name"}this.Group=d.extend(this,{constructor:c,type:"group"}),this.Rect=d.extend(this,{constructor:c,type:"rect"}),this.Text=d.extend(this,{constructor:function(a){c.call(this),this.properties.text=a},type:"text"})}),h=new f;return this.Shape=g,this.root=h,this};a.exports=e},function(a,b){(function(a){b.extend=function(a,b){var c={};for(var d in a)a.hasOwnProperty(d)&&(c[d]=a[d]);if(null!=b)for(var e in b)b.hasOwnProperty(e)&&(c[e]=b[e]);return c},b.cssProps=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);return b.join(";")},b.encodeHtmlEntity=function(a){for(var b=[],c=0,d=a.length-1;d>=0;d--)c=a.charCodeAt(d),b.unshift(c>128?["&#",c,";"].join(""):a[d]);return b.join("")},b.getNodeArray=function(b){var c=null;return"string"==typeof b?c=document.querySelectorAll(b):a.NodeList&&b instanceof a.NodeList?c=b:a.Node&&b instanceof a.Node?c=[b]:a.HTMLCollection&&b instanceof a.HTMLCollection?c=b:b instanceof Array?c=b:null===b&&(c=[]),c},b.imageExists=function(a,b){var c=new Image;c.onerror=function(){b.call(this,!1)},c.onload=function(){b.call(this,!0)},c.src=a},b.decodeHtmlEntity=function(a){return a.replace(/&#(\d+);/g,function(a,b){return String.fromCharCode(b)})},b.dimensionCheck=function(a){var b={height:a.clientHeight,width:a.clientWidth};return b.height&&b.width?b:!1}}).call(b,function(){return this}())},function(a){var b=function(){},c=Array.prototype.slice,d=function(a,d){var e=b.prototype="function"==typeof a?a.prototype:a,f=new b,g=d.apply(f,c.call(arguments,2).concat(e));if("object"==typeof g)for(var h in g)f[h]=g[h];if(!f.hasOwnProperty("constructor"))return f;var i=f.constructor;return i.prototype=f,i};d.defclass=function(a){var b=a.constructor;return b.prototype=a,b},d.extend=function(a,b){return d(a,function(a){return this.uber=a,b})},a.exports=d}])});
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "holderjs",
3
+ "officialName": "Holder",
4
+ "version": "2.6.0",
5
+ "summary": "client side image placeholders",
6
+ "description": "Holder uses SVG to render image placeholders entirely in browser.",
7
+ "author": {
8
+ "name": "Ivan Malopinsky",
9
+ "url": "http://imsky.co"
10
+ },
11
+ "homepage": "http://holderjs.com",
12
+ "license": {
13
+ "type": "MIT",
14
+ "url": "http://opensource.org/licenses/MIT"
15
+ },
16
+ "keywords": [
17
+ "images",
18
+ "placeholders",
19
+ "client-side",
20
+ "canvas",
21
+ "generation",
22
+ "development",
23
+ "svg"
24
+ ],
25
+ "main": "holder.js",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git://github.com/imsky/holder.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/imsky/holder/issues"
32
+ },
33
+ "devDependencies": {
34
+ "gulp": "~3",
35
+ "gulp-concat": "^2.2.0",
36
+ "gulp-header": "^1.0.2",
37
+ "gulp-jsbeautifier": "0.0.5",
38
+ "gulp-jshint": "^1.6.1",
39
+ "gulp-replace": "^0.5.3",
40
+ "gulp-todo": "^0.3.8",
41
+ "gulp-uglifyjs": "^0.4.4",
42
+ "gulp-util": "~3",
43
+ "gulp-webpack": "^1.3.0",
44
+ "moment": "^2.6.0"
45
+ }
46
+ }
@@ -0,0 +1,1411 @@
1
+ /*
2
+ Holder.js - client side image placeholders
3
+ © 2012-2015 Ivan Malopinsky - http://imsky.co
4
+ */
5
+
6
+ //Libraries and functions
7
+ var onDomReady = require('./lib/ondomready');
8
+ var SceneGraph = require('./scenegraph');
9
+ var utils = require('./utils');
10
+
11
+ var extend = utils.extend;
12
+ var cssProps = utils.cssProps;
13
+ var encodeHtmlEntity = utils.encodeHtmlEntity;
14
+ var decodeHtmlEntity = utils.decodeHtmlEntity;
15
+ var imageExists = utils.imageExists;
16
+ var getNodeArray = utils.getNodeArray;
17
+ var dimensionCheck = utils.dimensionCheck;
18
+
19
+ //Constants and definitions
20
+ var SVG_NS = 'http://www.w3.org/2000/svg';
21
+ var NODE_TYPE_COMMENT = 8;
22
+ var version = '%version%';
23
+ var generatorComment = '\n' +
24
+ 'Created with Holder.js ' + version + '.\n' +
25
+ 'Learn more at http://holderjs.com\n' +
26
+ '(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n';
27
+
28
+ var Holder = {
29
+ version: version,
30
+
31
+ /**
32
+ * Adds a theme to default settings
33
+ *
34
+ * @param {string} name Theme name
35
+ * @param {Object} theme Theme object, with foreground, background, size, font, and fontweight properties.
36
+ */
37
+ addTheme: function(name, theme) {
38
+ name != null && theme != null && (App.settings.themes[name] = theme);
39
+ delete App.vars.cache.themeKeys;
40
+ return this;
41
+ },
42
+
43
+ /**
44
+ * Appends a placeholder to an element
45
+ *
46
+ * @param {string} src Placeholder URL string
47
+ * @param {string} el Selector of target element(s)
48
+ */
49
+ addImage: function(src, el) {
50
+ var node = document.querySelectorAll(el);
51
+ if (node.length) {
52
+ for (var i = 0, l = node.length; i < l; i++) {
53
+ var img = newEl('img');
54
+ var domProps = {};
55
+ domProps[App.vars.dataAttr] = src;
56
+ setAttr(img, domProps);
57
+ node[i].appendChild(img);
58
+ }
59
+ }
60
+ return this;
61
+ },
62
+
63
+ /**
64
+ * Sets whether or not an image is updated on resize.
65
+ * If an image is set to be updated, it is immediately rendered.
66
+ *
67
+ * @param {Object} el Image DOM element
68
+ * @param {Boolean} value Resizable update flag value
69
+ */
70
+ setResizeUpdate: function(el, value) {
71
+ if (el.holderData) {
72
+ el.holderData.resizeUpdate = !!value;
73
+ if (el.holderData.resizeUpdate) {
74
+ updateResizableElements(el);
75
+ }
76
+ }
77
+ },
78
+
79
+ /**
80
+ * Runs Holder with options. By default runs Holder on all images with "holder.js" in their source attributes.
81
+ *
82
+ * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties
83
+ */
84
+ run: function(userOptions) {
85
+ userOptions = userOptions || {};
86
+ var engineSettings = {};
87
+ var options = extend(App.settings, userOptions);
88
+
89
+ App.vars.preempted = true;
90
+ App.vars.dataAttr = options.dataAttr || App.vars.dataAttr;
91
+
92
+ engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
93
+ if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) {
94
+ engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
95
+ }
96
+
97
+ var images = getNodeArray(options.images);
98
+ var bgnodes = getNodeArray(options.bgnodes);
99
+ var stylenodes = getNodeArray(options.stylenodes);
100
+ var objects = getNodeArray(options.objects);
101
+
102
+ engineSettings.stylesheets = [];
103
+ engineSettings.svgXMLStylesheet = true;
104
+ engineSettings.noFontFallback = options.noFontFallback ? options.noFontFallback : false;
105
+
106
+ for (var i = 0; i < stylenodes.length; i++) {
107
+ var styleNode = stylenodes[i];
108
+ if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
109
+ var href = styleNode.attributes.href.value;
110
+ //todo: write isomorphic relative-to-absolute URL function
111
+ var proxyLink = newEl('a');
112
+ proxyLink.href = href;
113
+ var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
114
+ engineSettings.stylesheets.push(stylesheetURL);
115
+ }
116
+ }
117
+
118
+ for (i = 0; i < bgnodes.length; i++) {
119
+ //Skip processing background nodes if getComputedStyle is unavailable, since only modern browsers would be able to use canvas or SVG to render to background
120
+ if (!global.getComputedStyle) continue;
121
+ var backgroundImage = global.getComputedStyle(bgnodes[i], null).getPropertyValue('background-image');
122
+ var dataBackgroundImage = bgnodes[i].getAttribute('data-background-src');
123
+ var rawURL = null;
124
+
125
+ if (dataBackgroundImage == null) {
126
+ rawURL = backgroundImage;
127
+ } else {
128
+ rawURL = dataBackgroundImage;
129
+ }
130
+
131
+ var holderURL = null;
132
+ var holderString = '?' + options.domain + '/';
133
+
134
+ if (rawURL.indexOf(holderString) === 0) {
135
+ holderURL = rawURL.slice(1);
136
+ } else if (rawURL.indexOf(holderString) != -1) {
137
+ var fragment = rawURL.substr(rawURL.indexOf(holderString)).slice(1);
138
+ var fragmentMatch = fragment.match(/([^\"]*)"?\)/);
139
+
140
+ if (fragmentMatch != null) {
141
+ holderURL = fragmentMatch[1];
142
+ }
143
+ }
144
+
145
+ if (holderURL != null) {
146
+ var holderFlags = parseURL(holderURL, options);
147
+ if (holderFlags) {
148
+ prepareDOMElement({
149
+ mode: 'background',
150
+ el: bgnodes[i],
151
+ flags: holderFlags,
152
+ engineSettings: engineSettings
153
+ });
154
+ }
155
+ }
156
+ }
157
+
158
+ for (i = 0; i < objects.length; i++) {
159
+ var object = objects[i];
160
+ var objectAttr = {};
161
+
162
+ try {
163
+ objectAttr.data = object.getAttribute('data');
164
+ objectAttr.dataSrc = object.getAttribute(App.vars.dataAttr);
165
+ } catch (e) {}
166
+
167
+ var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0;
168
+ var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0;
169
+
170
+ if (objectHasSrcURL) {
171
+ prepareImageElement(options, engineSettings, objectAttr.data, object);
172
+ } else if (objectHasDataSrcURL) {
173
+ prepareImageElement(options, engineSettings, objectAttr.dataSrc, object);
174
+ }
175
+ }
176
+
177
+ for (i = 0; i < images.length; i++) {
178
+ var image = images[i];
179
+ var imageAttr = {};
180
+
181
+ try {
182
+ imageAttr.src = image.getAttribute('src');
183
+ imageAttr.dataSrc = image.getAttribute(App.vars.dataAttr);
184
+ imageAttr.rendered = image.getAttribute('data-holder-rendered');
185
+ } catch (e) {}
186
+
187
+ var imageHasSrc = imageAttr.src != null;
188
+ var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0;
189
+ var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true';
190
+
191
+ if (imageHasSrc) {
192
+ if (imageAttr.src.indexOf(options.domain) === 0) {
193
+ prepareImageElement(options, engineSettings, imageAttr.src, image);
194
+ } else if (imageHasDataSrcURL) {
195
+ //Image has a valid data-src and an invalid src
196
+ if (imageRendered) {
197
+ //If the placeholder has already been render, re-render it
198
+ prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
199
+ } else {
200
+ //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't
201
+ (function(src, options, engineSettings, dataSrc, image) {
202
+ imageExists(src, function(exists) {
203
+ if (!exists) {
204
+ prepareImageElement(options, engineSettings, dataSrc, image);
205
+ }
206
+ });
207
+ })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image);
208
+ }
209
+ }
210
+ } else if (imageHasDataSrcURL) {
211
+ prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
212
+ }
213
+ }
214
+
215
+ return this;
216
+ }
217
+ };
218
+
219
+ var App = {
220
+ settings: {
221
+ domain: 'holder.js',
222
+ images: 'img',
223
+ objects: 'object',
224
+ bgnodes: 'body .holderjs',
225
+ stylenodes: 'head link.holderjs',
226
+ stylesheets: [],
227
+ themes: {
228
+ 'gray': {
229
+ background: '#EEEEEE',
230
+ foreground: '#AAAAAA'
231
+ },
232
+ 'social': {
233
+ background: '#3a5a97',
234
+ foreground: '#FFFFFF'
235
+ },
236
+ 'industrial': {
237
+ background: '#434A52',
238
+ foreground: '#C2F200'
239
+ },
240
+ 'sky': {
241
+ background: '#0D8FDB',
242
+ foreground: '#FFFFFF'
243
+ },
244
+ 'vine': {
245
+ background: '#39DBAC',
246
+ foreground: '#1E292C'
247
+ },
248
+ 'lava': {
249
+ background: '#F8591A',
250
+ foreground: '#1C2846'
251
+ }
252
+ }
253
+ },
254
+ defaults: {
255
+ size: 10,
256
+ units: 'pt',
257
+ scale: 1 / 16
258
+ },
259
+ flags: {
260
+ dimensions: {
261
+ regex: /^(\d+)x(\d+)$/,
262
+ output: function(val) {
263
+ var exec = this.regex.exec(val);
264
+ return {
265
+ width: +exec[1],
266
+ height: +exec[2]
267
+ };
268
+ }
269
+ },
270
+ fluid: {
271
+ regex: /^([0-9]+%?)x([0-9]+%?)$/,
272
+ output: function(val) {
273
+ var exec = this.regex.exec(val);
274
+ return {
275
+ width: exec[1],
276
+ height: exec[2]
277
+ };
278
+ }
279
+ },
280
+ colors: {
281
+ regex: /(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,
282
+ output: function(val) {
283
+ var exec = this.regex.exec(val);
284
+ return {
285
+ foreground: '#' + exec[2],
286
+ background: '#' + exec[1]
287
+ };
288
+ }
289
+ },
290
+ text: {
291
+ regex: /text\:(.*)/,
292
+ output: function(val) {
293
+ return this.regex.exec(val)[1].replace('\\/', '/');
294
+ }
295
+ },
296
+ font: {
297
+ regex: /font\:(.*)/,
298
+ output: function(val) {
299
+ return this.regex.exec(val)[1];
300
+ }
301
+ },
302
+ auto: {
303
+ regex: /^auto$/
304
+ },
305
+ textmode: {
306
+ regex: /textmode\:(.*)/,
307
+ output: function(val) {
308
+ return this.regex.exec(val)[1];
309
+ }
310
+ },
311
+ random: {
312
+ regex: /^random$/
313
+ },
314
+ size: {
315
+ regex: /size\:(\d+)/,
316
+ output: function(val) {
317
+ return this.regex.exec(val)[1];
318
+ }
319
+ }
320
+ }
321
+ };
322
+
323
+ /**
324
+ * Processes provided source attribute and sets up the appropriate rendering workflow
325
+ *
326
+ * @private
327
+ * @param options Instance options from Holder.run
328
+ * @param renderSettings Instance configuration
329
+ * @param src Image URL
330
+ * @param el Image DOM element
331
+ */
332
+ function prepareImageElement(options, engineSettings, src, el) {
333
+ var holderFlags = parseURL(src.substr(src.lastIndexOf(options.domain)), options);
334
+ if (holderFlags) {
335
+ prepareDOMElement({
336
+ mode: null,
337
+ el: el,
338
+ flags: holderFlags,
339
+ engineSettings: engineSettings
340
+ });
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Processes a Holder URL and extracts flags
346
+ *
347
+ * @private
348
+ * @param url URL
349
+ * @param options Instance options from Holder.run
350
+ */
351
+ function parseURL(url, options) {
352
+ var ret = {
353
+ theme: extend(App.settings.themes.gray, null),
354
+ stylesheets: options.stylesheets,
355
+ holderURL: []
356
+ };
357
+ var render = false;
358
+ var vtab = String.fromCharCode(11);
359
+ var flags = url.replace(/([^\\])\//g, '$1' + vtab).split(vtab);
360
+ var uriRegex = /%[0-9a-f]{2}/gi;
361
+ for (var fl = flags.length, j = 0; j < fl; j++) {
362
+ var flag = flags[j];
363
+ if (flag.match(uriRegex)) {
364
+ try {
365
+ flag = decodeURIComponent(flag);
366
+ } catch (e) {
367
+ flag = flags[j];
368
+ }
369
+ }
370
+
371
+ var push = false;
372
+
373
+ if (App.flags.dimensions.match(flag)) {
374
+ render = true;
375
+ ret.dimensions = App.flags.dimensions.output(flag);
376
+ push = true;
377
+ } else if (App.flags.fluid.match(flag)) {
378
+ render = true;
379
+ ret.dimensions = App.flags.fluid.output(flag);
380
+ ret.fluid = true;
381
+ push = true;
382
+ } else if (App.flags.textmode.match(flag)) {
383
+ ret.textmode = App.flags.textmode.output(flag);
384
+ push = true;
385
+ } else if (App.flags.colors.match(flag)) {
386
+ var colors = App.flags.colors.output(flag);
387
+ ret.theme = extend(ret.theme, colors);
388
+ //todo: convert implicit theme use to a theme: flag
389
+ push = true;
390
+ } else if (options.themes[flag]) {
391
+ //If a theme is specified, it will override custom colors
392
+ if (options.themes.hasOwnProperty(flag)) {
393
+ ret.theme = extend(options.themes[flag], null);
394
+ }
395
+ push = true;
396
+ } else if (App.flags.font.match(flag)) {
397
+ ret.font = App.flags.font.output(flag);
398
+ push = true;
399
+ } else if (App.flags.auto.match(flag)) {
400
+ ret.auto = true;
401
+ push = true;
402
+ } else if (App.flags.text.match(flag)) {
403
+ ret.text = App.flags.text.output(flag);
404
+ push = true;
405
+ } else if (App.flags.size.match(flag)) {
406
+ ret.size = App.flags.size.output(flag);
407
+ push = true;
408
+ } else if (App.flags.random.match(flag)) {
409
+ if (App.vars.cache.themeKeys == null) {
410
+ App.vars.cache.themeKeys = Object.keys(options.themes);
411
+ }
412
+ var theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
413
+ ret.theme = extend(options.themes[theme], null);
414
+ push = true;
415
+ }
416
+
417
+ if (push) {
418
+ ret.holderURL.push(flag);
419
+ }
420
+ }
421
+ ret.holderURL.unshift(options.domain);
422
+ ret.holderURL = ret.holderURL.join('/');
423
+ return render ? ret : false;
424
+ }
425
+
426
+ /**
427
+ * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders)
428
+ *
429
+ * @private
430
+ * @param settings DOM prep settings
431
+ */
432
+ function prepareDOMElement(prepSettings) {
433
+ var mode = prepSettings.mode;
434
+ var el = prepSettings.el;
435
+ var flags = prepSettings.flags;
436
+ var _engineSettings = prepSettings.engineSettings;
437
+ var dimensions = flags.dimensions,
438
+ theme = flags.theme;
439
+ var dimensionsCaption = dimensions.width + 'x' + dimensions.height;
440
+ mode = mode == null ? (flags.fluid ? 'fluid' : 'image') : mode;
441
+
442
+ if (flags.text != null) {
443
+ theme.text = flags.text;
444
+
445
+ //<object> SVG embedding doesn't parse Unicode properly
446
+ if (el.nodeName.toLowerCase() === 'object') {
447
+ var textLines = theme.text.split('\\n');
448
+ for (var k = 0; k < textLines.length; k++) {
449
+ textLines[k] = encodeHtmlEntity(textLines[k]);
450
+ }
451
+ theme.text = textLines.join('\\n');
452
+ }
453
+ }
454
+
455
+ var holderURL = flags.holderURL;
456
+ var engineSettings = extend(_engineSettings, null);
457
+
458
+ if (flags.font) {
459
+ theme.font = flags.font;
460
+ //Only run the <canvas> webfont fallback if noFontFallback is false, if the node is not an image, and if canvas is supported
461
+ if (!engineSettings.noFontFallback && el.nodeName.toLowerCase() === 'img' && App.setup.supportsCanvas && engineSettings.renderer === 'svg') {
462
+ engineSettings = extend(engineSettings, {
463
+ renderer: 'canvas'
464
+ });
465
+ }
466
+ }
467
+
468
+ //Chrome and Opera require a quick 10ms re-render if web fonts are used with canvas
469
+ if (flags.font && engineSettings.renderer == 'canvas') {
470
+ engineSettings.reRender = true;
471
+ }
472
+
473
+ if (mode == 'background') {
474
+ if (el.getAttribute('data-background-src') == null) {
475
+ setAttr(el, {
476
+ 'data-background-src': holderURL
477
+ });
478
+ }
479
+ } else {
480
+ var domProps = {};
481
+ domProps[App.vars.dataAttr] = holderURL;
482
+ setAttr(el, domProps);
483
+ }
484
+
485
+ flags.theme = theme;
486
+
487
+ //todo consider using all renderSettings in holderData
488
+ el.holderData = {
489
+ flags: flags,
490
+ engineSettings: engineSettings
491
+ };
492
+
493
+ if (mode == 'image' || mode == 'fluid') {
494
+ setAttr(el, {
495
+ 'alt': (theme.text ? theme.text + ' [' + dimensionsCaption + ']' : dimensionsCaption)
496
+ });
497
+ }
498
+
499
+ var renderSettings = {
500
+ mode: mode,
501
+ el: el,
502
+ holderSettings: {
503
+ dimensions: dimensions,
504
+ theme: theme,
505
+ flags: flags
506
+ },
507
+ engineSettings: engineSettings
508
+ };
509
+
510
+ if (mode == 'image') {
511
+ if (engineSettings.renderer == 'html' || !flags.auto) {
512
+ el.style.width = dimensions.width + 'px';
513
+ el.style.height = dimensions.height + 'px';
514
+ }
515
+ if (engineSettings.renderer == 'html') {
516
+ el.style.backgroundColor = theme.background;
517
+ } else {
518
+ render(renderSettings);
519
+
520
+ if (flags.textmode == 'exact') {
521
+ el.holderData.resizeUpdate = true;
522
+ App.vars.resizableImages.push(el);
523
+ updateResizableElements(el);
524
+ }
525
+ }
526
+ } else if (mode == 'background' && engineSettings.renderer != 'html') {
527
+ render(renderSettings);
528
+ } else if (mode == 'fluid') {
529
+ el.holderData.resizeUpdate = true;
530
+
531
+ if (dimensions.height.slice(-1) == '%') {
532
+ el.style.height = dimensions.height;
533
+ } else if (flags.auto == null || !flags.auto) {
534
+ el.style.height = dimensions.height + 'px';
535
+ }
536
+ if (dimensions.width.slice(-1) == '%') {
537
+ el.style.width = dimensions.width;
538
+ } else if (flags.auto == null || !flags.auto) {
539
+ el.style.width = dimensions.width + 'px';
540
+ }
541
+ if (el.style.display == 'inline' || el.style.display === '' || el.style.display == 'none') {
542
+ el.style.display = 'block';
543
+ }
544
+
545
+ setInitialDimensions(el);
546
+
547
+ if (engineSettings.renderer == 'html') {
548
+ el.style.backgroundColor = theme.background;
549
+ } else {
550
+ App.vars.resizableImages.push(el);
551
+ updateResizableElements(el);
552
+ }
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Core function that takes output from renderers and sets it as the source or background-image of the target element
558
+ *
559
+ * @private
560
+ * @param renderSettings Renderer settings
561
+ */
562
+ function render(renderSettings) {
563
+ var image = null;
564
+ var mode = renderSettings.mode;
565
+ var holderSettings = renderSettings.holderSettings;
566
+ var el = renderSettings.el;
567
+ var engineSettings = renderSettings.engineSettings;
568
+
569
+ switch (engineSettings.renderer) {
570
+ case 'svg':
571
+ if (!App.setup.supportsSVG) return;
572
+ break;
573
+ case 'canvas':
574
+ if (!App.setup.supportsCanvas) return;
575
+ break;
576
+ default:
577
+ return;
578
+ }
579
+
580
+ //todo: move generation of scene up to flag generation to reduce extra object creation
581
+ var scene = {
582
+ width: holderSettings.dimensions.width,
583
+ height: holderSettings.dimensions.height,
584
+ theme: holderSettings.theme,
585
+ flags: holderSettings.flags
586
+ };
587
+
588
+ var sceneGraph = buildSceneGraph(scene);
589
+
590
+ function getRenderedImage() {
591
+ var image = null;
592
+ switch (engineSettings.renderer) {
593
+ case 'canvas':
594
+ image = sgCanvasRenderer(sceneGraph, renderSettings);
595
+ break;
596
+ case 'svg':
597
+ image = sgSVGRenderer(sceneGraph, renderSettings);
598
+ break;
599
+ default:
600
+ throw 'Holder: invalid renderer: ' + engineSettings.renderer;
601
+ }
602
+ return image;
603
+ }
604
+
605
+ image = getRenderedImage();
606
+
607
+ if (image == null) {
608
+ throw 'Holder: couldn\'t render placeholder';
609
+ }
610
+
611
+ //todo: add <object> canvas rendering
612
+ if (mode == 'background') {
613
+ el.style.backgroundImage = 'url(' + image + ')';
614
+ el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px';
615
+ } else {
616
+ if (el.nodeName.toLowerCase() === 'img') {
617
+ setAttr(el, {
618
+ 'src': image
619
+ });
620
+ } else if (el.nodeName.toLowerCase() === 'object') {
621
+ setAttr(el, {
622
+ 'data': image
623
+ });
624
+ setAttr(el, {
625
+ 'type': 'image/svg+xml'
626
+ });
627
+ }
628
+ if (engineSettings.reRender) {
629
+ global.setTimeout(function() {
630
+ var image = getRenderedImage();
631
+ if (image == null) {
632
+ throw 'Holder: couldn\'t render placeholder';
633
+ }
634
+ //todo: refactor this code into a function
635
+ if (el.nodeName.toLowerCase() === 'img') {
636
+ setAttr(el, {
637
+ 'src': image
638
+ });
639
+ } else if (el.nodeName.toLowerCase() === 'object') {
640
+ setAttr(el, {
641
+ 'data': image
642
+ });
643
+ setAttr(el, {
644
+ 'type': 'image/svg+xml'
645
+ });
646
+ }
647
+ }, 100);
648
+ }
649
+ }
650
+ //todo: account for re-rendering
651
+ setAttr(el, {
652
+ 'data-holder-rendered': true
653
+ });
654
+ }
655
+
656
+ /**
657
+ * Core function that takes a Holder scene description and builds a scene graph
658
+ *
659
+ * @private
660
+ * @param scene Holder scene object
661
+ */
662
+ function buildSceneGraph(scene) {
663
+ var fontSize = App.defaults.size;
664
+ if (parseFloat(scene.theme.size)) {
665
+ fontSize = scene.theme.size;
666
+ } else if (parseFloat(scene.flags.size)) {
667
+ fontSize = scene.flags.size;
668
+ }
669
+
670
+ scene.font = {
671
+ family: scene.theme.font ? scene.theme.font : 'Arial, Helvetica, Open Sans, sans-serif',
672
+ size: textSize(scene.width, scene.height, fontSize),
673
+ units: scene.theme.units ? scene.theme.units : App.defaults.units,
674
+ weight: scene.theme.fontweight ? scene.theme.fontweight : 'bold'
675
+ };
676
+ scene.text = scene.theme.text ? scene.theme.text : Math.floor(scene.width) + 'x' + Math.floor(scene.height);
677
+
678
+ switch (scene.flags.textmode) {
679
+ case 'literal':
680
+ scene.text = scene.flags.dimensions.width + 'x' + scene.flags.dimensions.height;
681
+ break;
682
+ case 'exact':
683
+ if (!scene.flags.exactDimensions) break;
684
+ scene.text = Math.floor(scene.flags.exactDimensions.width) + 'x' + Math.floor(scene.flags.exactDimensions.height);
685
+ break;
686
+ }
687
+
688
+ var sceneGraph = new SceneGraph({
689
+ width: scene.width,
690
+ height: scene.height
691
+ });
692
+
693
+ var Shape = sceneGraph.Shape;
694
+
695
+ var holderBg = new Shape.Rect('holderBg', {
696
+ fill: scene.theme.background
697
+ });
698
+
699
+ holderBg.resize(scene.width, scene.height);
700
+ sceneGraph.root.add(holderBg);
701
+
702
+ var holderTextGroup = new Shape.Group('holderTextGroup', {
703
+ text: scene.text,
704
+ align: 'center',
705
+ font: scene.font,
706
+ fill: scene.theme.foreground
707
+ });
708
+
709
+ holderTextGroup.moveTo(null, null, 1);
710
+ sceneGraph.root.add(holderTextGroup);
711
+
712
+ var tpdata = holderTextGroup.textPositionData = stagingRenderer(sceneGraph);
713
+ if (!tpdata) {
714
+ throw 'Holder: staging fallback not supported yet.';
715
+ }
716
+ holderTextGroup.properties.leading = tpdata.boundingBox.height;
717
+
718
+ //todo: alignment: TL, TC, TR, CL, CR, BL, BC, BR
719
+ var textNode = null;
720
+ var line = null;
721
+
722
+ function finalizeLine(parent, line, width, height) {
723
+ line.width = width;
724
+ line.height = height;
725
+ parent.width = Math.max(parent.width, line.width);
726
+ parent.height += line.height;
727
+ parent.add(line);
728
+ }
729
+
730
+ if (tpdata.lineCount > 1) {
731
+ var offsetX = 0;
732
+ var offsetY = 0;
733
+ var maxLineWidth = scene.width * App.setup.lineWrapRatio;
734
+ var lineIndex = 0;
735
+ line = new Shape.Group('line' + lineIndex);
736
+
737
+ for (var i = 0; i < tpdata.words.length; i++) {
738
+ var word = tpdata.words[i];
739
+ textNode = new Shape.Text(word.text);
740
+ var newline = word.text == '\\n';
741
+ if (offsetX + word.width >= maxLineWidth || newline === true) {
742
+ finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
743
+ offsetX = 0;
744
+ offsetY += holderTextGroup.properties.leading;
745
+ lineIndex += 1;
746
+ line = new Shape.Group('line' + lineIndex);
747
+ line.y = offsetY;
748
+ }
749
+ if (newline === true) {
750
+ continue;
751
+ }
752
+ textNode.moveTo(offsetX, 0);
753
+ offsetX += tpdata.spaceWidth + word.width;
754
+ line.add(textNode);
755
+ }
756
+
757
+ finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
758
+
759
+ for (var lineKey in holderTextGroup.children) {
760
+ line = holderTextGroup.children[lineKey];
761
+ line.moveTo(
762
+ (holderTextGroup.width - line.width) / 2,
763
+ null,
764
+ null);
765
+ }
766
+
767
+ holderTextGroup.moveTo(
768
+ (scene.width - holderTextGroup.width) / 2, (scene.height - holderTextGroup.height) / 2,
769
+ null);
770
+
771
+ //If the text exceeds vertical space, move it down so the first line is visible
772
+ if ((scene.height - holderTextGroup.height) / 2 < 0) {
773
+ holderTextGroup.moveTo(null, 0, null);
774
+ }
775
+ } else {
776
+ textNode = new Shape.Text(scene.text);
777
+ line = new Shape.Group('line0');
778
+ line.add(textNode);
779
+ holderTextGroup.add(line);
780
+
781
+ holderTextGroup.moveTo(
782
+ (scene.width - tpdata.boundingBox.width) / 2, (scene.height - tpdata.boundingBox.height) / 2,
783
+ null);
784
+ }
785
+
786
+ //todo: renderlist
787
+
788
+ return sceneGraph;
789
+ }
790
+
791
+ /**
792
+ * Adaptive text sizing function
793
+ *
794
+ * @private
795
+ * @param width Parent width
796
+ * @param height Parent height
797
+ * @param fontSize Requested text size
798
+ */
799
+ function textSize(width, height, fontSize) {
800
+ var stageWidth = parseInt(width, 10);
801
+ var stageHeight = parseInt(height, 10);
802
+
803
+ var bigSide = Math.max(stageWidth, stageHeight);
804
+ var smallSide = Math.min(stageWidth, stageHeight);
805
+
806
+ var newHeight = 0.8 * Math.min(smallSide, bigSide * App.defaults.scale);
807
+ return Math.round(Math.max(fontSize, newHeight));
808
+ }
809
+
810
+ /**
811
+ * Iterates over resizable (fluid or auto) placeholders and renders them
812
+ *
813
+ * @private
814
+ * @param element Optional element selector, specified only if a specific element needs to be re-rendered
815
+ */
816
+ function updateResizableElements(element) {
817
+ var images;
818
+ if (element == null || element.nodeType == null) {
819
+ images = App.vars.resizableImages;
820
+ } else {
821
+ images = [element];
822
+ }
823
+ for (var i = 0, l = images.length; i < l; i++) {
824
+ var el = images[i];
825
+ if (el.holderData) {
826
+ var flags = el.holderData.flags;
827
+ var dimensions = dimensionCheck(el);
828
+ if (dimensions) {
829
+ if (!el.holderData.resizeUpdate) {
830
+ continue;
831
+ }
832
+
833
+ if (flags.fluid && flags.auto) {
834
+ var fluidConfig = el.holderData.fluidConfig;
835
+ switch (fluidConfig.mode) {
836
+ case 'width':
837
+ dimensions.height = dimensions.width / fluidConfig.ratio;
838
+ break;
839
+ case 'height':
840
+ dimensions.width = dimensions.height * fluidConfig.ratio;
841
+ break;
842
+ }
843
+ }
844
+
845
+ var settings = {
846
+ mode: 'image',
847
+ holderSettings: {
848
+ dimensions: dimensions,
849
+ theme: flags.theme,
850
+ flags: flags
851
+ },
852
+ el: el,
853
+ engineSettings: el.holderData.engineSettings
854
+ };
855
+
856
+ if (flags.textmode == 'exact') {
857
+ flags.exactDimensions = dimensions;
858
+ settings.holderSettings.dimensions = flags.dimensions;
859
+ }
860
+
861
+ render(settings);
862
+ } else {
863
+ setInvisible(el);
864
+ }
865
+ }
866
+ }
867
+ }
868
+
869
+ /**
870
+ * Sets up aspect ratio metadata for fluid placeholders, in order to preserve proportions when resizing
871
+ *
872
+ * @private
873
+ * @param el Image DOM element
874
+ */
875
+ function setInitialDimensions(el) {
876
+ if (el.holderData) {
877
+ var dimensions = dimensionCheck(el);
878
+ if (dimensions) {
879
+ var flags = el.holderData.flags;
880
+
881
+ var fluidConfig = {
882
+ fluidHeight: flags.dimensions.height.slice(-1) == '%',
883
+ fluidWidth: flags.dimensions.width.slice(-1) == '%',
884
+ mode: null,
885
+ initialDimensions: dimensions
886
+ };
887
+
888
+ if (fluidConfig.fluidWidth && !fluidConfig.fluidHeight) {
889
+ fluidConfig.mode = 'width';
890
+ fluidConfig.ratio = fluidConfig.initialDimensions.width / parseFloat(flags.dimensions.height);
891
+ } else if (!fluidConfig.fluidWidth && fluidConfig.fluidHeight) {
892
+ fluidConfig.mode = 'height';
893
+ fluidConfig.ratio = parseFloat(flags.dimensions.width) / fluidConfig.initialDimensions.height;
894
+ }
895
+
896
+ el.holderData.fluidConfig = fluidConfig;
897
+ } else {
898
+ setInvisible(el);
899
+ }
900
+ }
901
+ }
902
+
903
+ /**
904
+ * Iterates through all current invisible images, and if they're visible, renders them and removes them from further checks. Runs every animation frame.
905
+ *
906
+ * @private
907
+ */
908
+ function visibilityCheck() {
909
+ var renderableImages = [];
910
+ var keys = Object.keys(App.vars.invisibleImages);
911
+ var el;
912
+ for (var i = 0, l = keys.length; i < l; i++) {
913
+ el = App.vars.invisibleImages[keys[i]];
914
+ if (dimensionCheck(el) && el.nodeName.toLowerCase() == 'img') {
915
+ renderableImages.push(el);
916
+ delete App.vars.invisibleImages[keys[i]];
917
+ }
918
+ }
919
+
920
+ if (renderableImages.length) {
921
+ Holder.run({
922
+ images: renderableImages
923
+ });
924
+ }
925
+
926
+ global.requestAnimationFrame(visibilityCheck);
927
+ }
928
+
929
+ /**
930
+ * Starts checking for invisible placeholders if not doing so yet. Does nothing otherwise.
931
+ *
932
+ * @private
933
+ */
934
+ function startVisibilityCheck() {
935
+ if (!App.vars.visibilityCheckStarted) {
936
+ global.requestAnimationFrame(visibilityCheck);
937
+ App.vars.visibilityCheckStarted = true;
938
+ }
939
+ }
940
+
941
+ /**
942
+ * Sets a unique ID for an image detected to be invisible and adds it to the map of invisible images checked by visibilityCheck
943
+ *
944
+ * @private
945
+ * @param el Invisible DOM element
946
+ */
947
+ function setInvisible(el) {
948
+ if (!el.holderData.invisibleId) {
949
+ App.vars.invisibleId += 1;
950
+ App.vars.invisibleImages['i' + App.vars.invisibleId] = el;
951
+ el.holderData.invisibleId = App.vars.invisibleId;
952
+ }
953
+ }
954
+
955
+ //todo: see if possible to convert stagingRenderer to use HTML only
956
+ var stagingRenderer = (function() {
957
+ var svg = null,
958
+ stagingText = null,
959
+ stagingTextNode = null;
960
+ return function(graph) {
961
+ var rootNode = graph.root;
962
+ if (App.setup.supportsSVG) {
963
+ var firstTimeSetup = false;
964
+ var tnode = function(text) {
965
+ return document.createTextNode(text);
966
+ };
967
+ if (svg == null || svg.parentNode !== document.body) {
968
+ firstTimeSetup = true;
969
+ }
970
+
971
+ svg = initSVG(svg, rootNode.properties.width, rootNode.properties.height);
972
+ //Show staging element before staging
973
+ svg.style.display = 'block';
974
+
975
+ if (firstTimeSetup) {
976
+ stagingText = newEl('text', SVG_NS);
977
+ stagingTextNode = tnode(null);
978
+ setAttr(stagingText, {
979
+ x: 0
980
+ });
981
+ stagingText.appendChild(stagingTextNode);
982
+ svg.appendChild(stagingText);
983
+ document.body.appendChild(svg);
984
+ svg.style.visibility = 'hidden';
985
+ svg.style.position = 'absolute';
986
+ svg.style.top = '-100%';
987
+ svg.style.left = '-100%';
988
+ //todo: workaround for zero-dimension <svg> tag in Opera 12
989
+ //svg.setAttribute('width', 0);
990
+ //svg.setAttribute('height', 0);
991
+ }
992
+
993
+ var holderTextGroup = rootNode.children.holderTextGroup;
994
+ var htgProps = holderTextGroup.properties;
995
+ setAttr(stagingText, {
996
+ 'y': htgProps.font.size,
997
+ 'style': cssProps({
998
+ 'font-weight': htgProps.font.weight,
999
+ 'font-size': htgProps.font.size + htgProps.font.units,
1000
+ 'font-family': htgProps.font.family
1001
+ })
1002
+ });
1003
+
1004
+ //Get bounding box for the whole string (total width and height)
1005
+ stagingTextNode.nodeValue = htgProps.text;
1006
+ var stagingTextBBox = stagingText.getBBox();
1007
+
1008
+ //Get line count and split the string into words
1009
+ var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.setup.lineWrapRatio));
1010
+ var words = htgProps.text.split(' ');
1011
+ var newlines = htgProps.text.match(/\\n/g);
1012
+ lineCount += newlines == null ? 0 : newlines.length;
1013
+
1014
+ //Get bounding box for the string with spaces removed
1015
+ stagingTextNode.nodeValue = htgProps.text.replace(/[ ]+/g, '');
1016
+ var computedNoSpaceLength = stagingText.getComputedTextLength();
1017
+
1018
+ //Compute average space width
1019
+ var diffLength = stagingTextBBox.width - computedNoSpaceLength;
1020
+ var spaceWidth = Math.round(diffLength / Math.max(1, words.length - 1));
1021
+
1022
+ //Get widths for every word with space only if there is more than one line
1023
+ var wordWidths = [];
1024
+ if (lineCount > 1) {
1025
+ stagingTextNode.nodeValue = '';
1026
+ for (var i = 0; i < words.length; i++) {
1027
+ if (words[i].length === 0) continue;
1028
+ stagingTextNode.nodeValue = decodeHtmlEntity(words[i]);
1029
+ var bbox = stagingText.getBBox();
1030
+ wordWidths.push({
1031
+ text: words[i],
1032
+ width: bbox.width
1033
+ });
1034
+ }
1035
+ }
1036
+
1037
+ //Hide staging element after staging
1038
+ svg.style.display = 'none';
1039
+
1040
+ return {
1041
+ spaceWidth: spaceWidth,
1042
+ lineCount: lineCount,
1043
+ boundingBox: stagingTextBBox,
1044
+ words: wordWidths
1045
+ };
1046
+ } else {
1047
+ //todo: canvas fallback for measuring text on android 2.3
1048
+ return false;
1049
+ }
1050
+ };
1051
+ })();
1052
+
1053
+ var sgCanvasRenderer = (function() {
1054
+ var canvas = newEl('canvas');
1055
+ var ctx = null;
1056
+
1057
+ return function(sceneGraph) {
1058
+ if (ctx == null) {
1059
+ ctx = canvas.getContext('2d');
1060
+ }
1061
+ var root = sceneGraph.root;
1062
+ canvas.width = App.dpr(root.properties.width);
1063
+ canvas.height = App.dpr(root.properties.height);
1064
+ ctx.textBaseline = 'middle';
1065
+
1066
+ ctx.fillStyle = root.children.holderBg.properties.fill;
1067
+ ctx.fillRect(0, 0, App.dpr(root.children.holderBg.width), App.dpr(root.children.holderBg.height));
1068
+
1069
+ var textGroup = root.children.holderTextGroup;
1070
+ var tgProps = textGroup.properties;
1071
+ ctx.font = textGroup.properties.font.weight + ' ' + App.dpr(textGroup.properties.font.size) + textGroup.properties.font.units + ' ' + textGroup.properties.font.family + ', monospace';
1072
+ ctx.fillStyle = textGroup.properties.fill;
1073
+
1074
+ for (var lineKey in textGroup.children) {
1075
+ var line = textGroup.children[lineKey];
1076
+ for (var wordKey in line.children) {
1077
+ var word = line.children[wordKey];
1078
+ var x = App.dpr(textGroup.x + line.x + word.x);
1079
+ var y = App.dpr(textGroup.y + line.y + word.y + (textGroup.properties.leading / 2));
1080
+
1081
+ ctx.fillText(word.properties.text, x, y);
1082
+ }
1083
+ }
1084
+
1085
+ return canvas.toDataURL('image/png');
1086
+ };
1087
+ })();
1088
+
1089
+ var sgSVGRenderer = (function() {
1090
+ //Prevent IE <9 from initializing SVG renderer
1091
+ if (!global.XMLSerializer) return;
1092
+ var xml = createXML();
1093
+ var svg = initSVG(null, 0, 0);
1094
+ var bgEl = newEl('rect', SVG_NS);
1095
+ svg.appendChild(bgEl);
1096
+
1097
+ //todo: create a reusable pool for textNodes, resize if more words present
1098
+
1099
+ return function(sceneGraph, renderSettings) {
1100
+ var root = sceneGraph.root;
1101
+
1102
+ initSVG(svg, root.properties.width, root.properties.height);
1103
+
1104
+ var groups = svg.querySelectorAll('g');
1105
+
1106
+ for (var i = 0; i < groups.length; i++) {
1107
+ groups[i].parentNode.removeChild(groups[i]);
1108
+ }
1109
+
1110
+ var holderURL = renderSettings.holderSettings.flags.holderURL;
1111
+ var holderId = 'holder_' + (Number(new Date()) + 32768 + (0 | Math.random() * 32768)).toString(16);
1112
+ var sceneGroupEl = newEl('g', SVG_NS);
1113
+ var textGroup = root.children.holderTextGroup;
1114
+ var tgProps = textGroup.properties;
1115
+ var textGroupEl = newEl('g', SVG_NS);
1116
+ var tpdata = textGroup.textPositionData;
1117
+ var textCSSRule = '#' + holderId + ' text { ' +
1118
+ cssProps({
1119
+ 'fill': tgProps.fill,
1120
+ 'font-weight': tgProps.font.weight,
1121
+ 'font-family': tgProps.font.family + ', monospace',
1122
+ 'font-size': tgProps.font.size + tgProps.font.units
1123
+ }) + ' } ';
1124
+ var commentNode = xml.createComment('\n' + 'Source URL: ' + holderURL + generatorComment);
1125
+ var holderCSS = xml.createCDATASection(textCSSRule);
1126
+ var styleEl = svg.querySelector('style');
1127
+
1128
+ setAttr(sceneGroupEl, {
1129
+ id: holderId
1130
+ });
1131
+
1132
+ svg.insertBefore(commentNode, svg.firstChild);
1133
+ styleEl.appendChild(holderCSS);
1134
+
1135
+ sceneGroupEl.appendChild(bgEl);
1136
+ sceneGroupEl.appendChild(textGroupEl);
1137
+ svg.appendChild(sceneGroupEl);
1138
+
1139
+ setAttr(bgEl, {
1140
+ 'width': root.children.holderBg.width,
1141
+ 'height': root.children.holderBg.height,
1142
+ 'fill': root.children.holderBg.properties.fill
1143
+ });
1144
+
1145
+ textGroup.y += tpdata.boundingBox.height * 0.8;
1146
+
1147
+ for (var lineKey in textGroup.children) {
1148
+ var line = textGroup.children[lineKey];
1149
+ for (var wordKey in line.children) {
1150
+ var word = line.children[wordKey];
1151
+ var x = textGroup.x + line.x + word.x;
1152
+ var y = textGroup.y + line.y + word.y;
1153
+
1154
+ var textEl = newEl('text', SVG_NS);
1155
+ var textNode = document.createTextNode(null);
1156
+
1157
+ setAttr(textEl, {
1158
+ 'x': x,
1159
+ 'y': y
1160
+ });
1161
+
1162
+ textNode.nodeValue = word.properties.text;
1163
+ textEl.appendChild(textNode);
1164
+ textGroupEl.appendChild(textEl);
1165
+ }
1166
+ }
1167
+
1168
+ var svgString = 'data:image/svg+xml;base64,' +
1169
+ btoa(unescape(encodeURIComponent(serializeSVG(svg, renderSettings.engineSettings))));
1170
+ return svgString;
1171
+ };
1172
+ })();
1173
+
1174
+ //Helpers
1175
+
1176
+ /**
1177
+ * Generic new DOM element function
1178
+ *
1179
+ * @private
1180
+ * @param tag Tag to create
1181
+ * @param namespace Optional namespace value
1182
+ */
1183
+ function newEl(tag, namespace) {
1184
+ if (namespace == null) {
1185
+ return document.createElement(tag);
1186
+ } else {
1187
+ return document.createElementNS(namespace, tag);
1188
+ }
1189
+ }
1190
+
1191
+ /**
1192
+ * Generic setAttribute function
1193
+ *
1194
+ * @private
1195
+ * @param el Reference to DOM element
1196
+ * @param attrs Object with attribute keys and values
1197
+ */
1198
+ function setAttr(el, attrs) {
1199
+ for (var a in attrs) {
1200
+ el.setAttribute(a, attrs[a]);
1201
+ }
1202
+ }
1203
+
1204
+ /**
1205
+ * Generic SVG element creation function
1206
+ *
1207
+ * @private
1208
+ * @param svg SVG context, set to null if new
1209
+ * @param width Document width
1210
+ * @param height Document height
1211
+ */
1212
+ function initSVG(svg, width, height) {
1213
+ var defs, style;
1214
+
1215
+ if (svg == null) {
1216
+ svg = newEl('svg', SVG_NS);
1217
+ defs = newEl('defs', SVG_NS);
1218
+ style = newEl('style', SVG_NS);
1219
+ setAttr(style, {
1220
+ 'type': 'text/css'
1221
+ });
1222
+ defs.appendChild(style);
1223
+ svg.appendChild(defs);
1224
+ } else {
1225
+ style = svg.querySelector('style');
1226
+ }
1227
+
1228
+ //IE throws an exception if this is set and Chrome requires it to be set
1229
+ if (svg.webkitMatchesSelector) {
1230
+ svg.setAttribute('xmlns', SVG_NS);
1231
+ }
1232
+ //Remove comment nodes
1233
+ for (var i = 0; i < svg.childNodes.length; i++) {
1234
+ if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) {
1235
+ svg.removeChild(svg.childNodes[i]);
1236
+ }
1237
+ }
1238
+
1239
+ //Remove CSS
1240
+ while (style.childNodes.length) {
1241
+ style.removeChild(style.childNodes[0]);
1242
+ }
1243
+
1244
+ setAttr(svg, {
1245
+ 'width': width,
1246
+ 'height': height,
1247
+ 'viewBox': '0 0 ' + width + ' ' + height,
1248
+ 'preserveAspectRatio': 'none'
1249
+ });
1250
+
1251
+ return svg;
1252
+ }
1253
+
1254
+ /**
1255
+ * Returns XML processing instructions
1256
+ *
1257
+ * @private
1258
+ * @param svg SVG context
1259
+ * @param stylesheets CSS stylesheets to include
1260
+ */
1261
+ function serializeSVG(svg, engineSettings) {
1262
+ if (!global.XMLSerializer) return;
1263
+ var serializer = new XMLSerializer();
1264
+ var svgCSS = '';
1265
+ var stylesheets = engineSettings.stylesheets;
1266
+
1267
+ //External stylesheets: Processing Instruction method
1268
+ if (engineSettings.svgXMLStylesheet) {
1269
+ var xml = createXML();
1270
+ //Add <?xml-stylesheet ?> directives
1271
+ for (var i = stylesheets.length - 1; i >= 0; i--) {
1272
+ var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"');
1273
+ xml.insertBefore(csspi, xml.firstChild);
1274
+ }
1275
+
1276
+ //Add <?xml ... ?> UTF-8 directive
1277
+ var xmlpi = xml.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8" standalone="yes"');
1278
+ xml.insertBefore(xmlpi, xml.firstChild);
1279
+ xml.removeChild(xml.documentElement);
1280
+ svgCSS = serializer.serializeToString(xml);
1281
+ }
1282
+
1283
+ var svgText = serializer.serializeToString(svg);
1284
+ svgText = svgText.replace(/\&amp;(\#[0-9]{2,}\;)/g, '&$1');
1285
+ return svgCSS + svgText;
1286
+ }
1287
+
1288
+ /**
1289
+ * Creates a XML document
1290
+ * @private
1291
+ */
1292
+ function createXML() {
1293
+ if (!global.DOMParser) return;
1294
+ return new DOMParser().parseFromString('<xml />', 'application/xml');
1295
+ }
1296
+
1297
+ /**
1298
+ * Prevents a function from being called too often, waits until a timer elapses to call it again
1299
+ *
1300
+ * @param fn Function to call
1301
+ */
1302
+ function debounce(fn) {
1303
+ if (!App.vars.debounceTimer) fn.call(this);
1304
+ if (App.vars.debounceTimer) global.clearTimeout(App.vars.debounceTimer);
1305
+ App.vars.debounceTimer = global.setTimeout(function() {
1306
+ App.vars.debounceTimer = null;
1307
+ fn.call(this);
1308
+ }, App.setup.debounce);
1309
+ }
1310
+
1311
+ /**
1312
+ * Holder-specific resize/orientation change callback, debounced to prevent excessive execution
1313
+ */
1314
+ function resizeEvent() {
1315
+ debounce(function() {
1316
+ updateResizableElements(null);
1317
+ });
1318
+ }
1319
+
1320
+ //Set up flags
1321
+
1322
+ for (var flag in App.flags) {
1323
+ if (!App.flags.hasOwnProperty(flag)) continue;
1324
+ App.flags[flag].match = function(val) {
1325
+ return val.match(this.regex);
1326
+ };
1327
+ }
1328
+
1329
+ //Properties set once on setup
1330
+
1331
+ App.setup = {
1332
+ renderer: 'html',
1333
+ debounce: 100,
1334
+ ratio: 1,
1335
+ supportsCanvas: false,
1336
+ supportsSVG: false,
1337
+ lineWrapRatio: 0.9,
1338
+ renderers: ['html', 'canvas', 'svg']
1339
+ };
1340
+
1341
+ App.dpr = function(val) {
1342
+ return val * App.setup.ratio;
1343
+ };
1344
+
1345
+ //Properties modified during runtime
1346
+
1347
+ App.vars = {
1348
+ preempted: false,
1349
+ resizableImages: [],
1350
+ invisibleImages: {},
1351
+ invisibleId: 0,
1352
+ visibilityCheckStarted: false,
1353
+ debounceTimer: null,
1354
+ cache: {},
1355
+ dataAttr: 'data-src'
1356
+ };
1357
+
1358
+ //Pre-flight
1359
+
1360
+ (function() {
1361
+ var devicePixelRatio = 1,
1362
+ backingStoreRatio = 1;
1363
+
1364
+ var canvas = newEl('canvas');
1365
+ var ctx = null;
1366
+
1367
+ if (canvas.getContext) {
1368
+ if (canvas.toDataURL('image/png').indexOf('data:image/png') != -1) {
1369
+ App.setup.renderer = 'canvas';
1370
+ ctx = canvas.getContext('2d');
1371
+ App.setup.supportsCanvas = true;
1372
+ }
1373
+ }
1374
+
1375
+ if (App.setup.supportsCanvas) {
1376
+ devicePixelRatio = global.devicePixelRatio || 1;
1377
+ backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
1378
+ }
1379
+
1380
+ App.setup.ratio = devicePixelRatio / backingStoreRatio;
1381
+
1382
+ if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) {
1383
+ App.setup.renderer = 'svg';
1384
+ App.setup.supportsSVG = true;
1385
+ }
1386
+ })();
1387
+
1388
+ //Starts checking for invisible placeholders
1389
+ startVisibilityCheck();
1390
+
1391
+ if (onDomReady) {
1392
+ onDomReady(function() {
1393
+ if (!App.vars.preempted) {
1394
+ Holder.run();
1395
+ }
1396
+ if (global.addEventListener) {
1397
+ global.addEventListener('resize', resizeEvent, false);
1398
+ global.addEventListener('orientationchange', resizeEvent, false);
1399
+ } else {
1400
+ global.attachEvent('onresize', resizeEvent);
1401
+ }
1402
+
1403
+ if (typeof global.Turbolinks == 'object') {
1404
+ global.document.addEventListener('page:change', function() {
1405
+ Holder.run();
1406
+ });
1407
+ }
1408
+ });
1409
+ }
1410
+
1411
+ module.exports = Holder;