tennpipes-init 3.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;