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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +163 -0
- data/Rakefile +1 -0
- data/bin/tennpipes-init +16 -0
- data/lib/tennpipes-init.rb +73 -0
- data/lib/tennpipes-init/command.rb +18 -0
- data/lib/tennpipes-init/generators/actions.rb +630 -0
- data/lib/tennpipes-init/generators/app.rb +75 -0
- data/lib/tennpipes-init/generators/app/app.rb.tt +72 -0
- data/lib/tennpipes-init/generators/app/app.rb.tt~ +72 -0
- data/lib/tennpipes-init/generators/cli.rb +57 -0
- data/lib/tennpipes-init/generators/component.rb +73 -0
- data/lib/tennpipes-init/generators/components/actions.rb +208 -0
- data/lib/tennpipes-init/generators/components/mocks/mocha.rb +10 -0
- data/lib/tennpipes-init/generators/components/mocks/rr.rb +13 -0
- data/lib/tennpipes-init/generators/components/orms/activerecord.rb +201 -0
- data/lib/tennpipes-init/generators/components/orms/couchrest.rb +55 -0
- data/lib/tennpipes-init/generators/components/orms/datamapper.rb +140 -0
- data/lib/tennpipes-init/generators/components/orms/dynamoid.rb +67 -0
- data/lib/tennpipes-init/generators/components/orms/minirecord.rb +165 -0
- data/lib/tennpipes-init/generators/components/orms/mongoid.rb +113 -0
- data/lib/tennpipes-init/generators/components/orms/mongomapper.rb +43 -0
- data/lib/tennpipes-init/generators/components/orms/mongomatic.rb +84 -0
- data/lib/tennpipes-init/generators/components/orms/ohm.rb +65 -0
- data/lib/tennpipes-init/generators/components/orms/ripple.rb +75 -0
- data/lib/tennpipes-init/generators/components/orms/sequel.rb +99 -0
- data/lib/tennpipes-init/generators/components/renderers/erb.rb +3 -0
- data/lib/tennpipes-init/generators/components/renderers/haml.rb +3 -0
- data/lib/tennpipes-init/generators/components/renderers/liquid.rb +4 -0
- data/lib/tennpipes-init/generators/components/renderers/slim.rb +3 -0
- data/lib/tennpipes-init/generators/components/scripts/dojo.rb +10 -0
- data/lib/tennpipes-init/generators/components/scripts/extcore.rb +10 -0
- data/lib/tennpipes-init/generators/components/scripts/jquery.rb +10 -0
- data/lib/tennpipes-init/generators/components/scripts/mootools.rb +10 -0
- data/lib/tennpipes-init/generators/components/scripts/prototype.rb +12 -0
- data/lib/tennpipes-init/generators/components/scripts/rightjs.rb +10 -0
- data/lib/tennpipes-init/generators/components/stylesheets/compass.rb +39 -0
- data/lib/tennpipes-init/generators/components/stylesheets/compass/application.scss +43 -0
- data/lib/tennpipes-init/generators/components/stylesheets/compass/partials/_base.scss +12 -0
- data/lib/tennpipes-init/generators/components/stylesheets/less.rb +25 -0
- data/lib/tennpipes-init/generators/components/stylesheets/sass.rb +15 -0
- data/lib/tennpipes-init/generators/components/stylesheets/scss.rb +16 -0
- data/lib/tennpipes-init/generators/components/tests/bacon.rb +103 -0
- data/lib/tennpipes-init/generators/components/tests/cucumber.rb +86 -0
- data/lib/tennpipes-init/generators/components/tests/minitest.rb +110 -0
- data/lib/tennpipes-init/generators/components/tests/riot.rb +117 -0
- data/lib/tennpipes-init/generators/components/tests/rspec.rb +111 -0
- data/lib/tennpipes-init/generators/components/tests/shoulda.rb +114 -0
- data/lib/tennpipes-init/generators/components/tests/steak.rb +114 -0
- data/lib/tennpipes-init/generators/controller.rb +77 -0
- data/lib/tennpipes-init/generators/helper.rb +50 -0
- data/lib/tennpipes-init/generators/mailer.rb +52 -0
- data/lib/tennpipes-init/generators/migration.rb +43 -0
- data/lib/tennpipes-init/generators/model.rb +113 -0
- data/lib/tennpipes-init/generators/plugin.rb +67 -0
- data/lib/tennpipes-init/generators/project.rb +160 -0
- data/lib/tennpipes-init/generators/project/config.ru +9 -0
- data/lib/tennpipes-init/generators/project/config/apps.rb.tt +35 -0
- data/lib/tennpipes-init/generators/project/config/boot.rb +49 -0
- data/lib/tennpipes-init/generators/project/public/favicon.ico +0 -0
- data/lib/tennpipes-init/generators/project/public/images/booking.com.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/causes.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bg.jpg +0 -0
- data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bluelogo.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/ennkeypee_bluelogo.svg +68 -0
- data/lib/tennpipes-init/generators/project/public/images/ennkeypee_whitelogo.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/ennkeypee_whitelogo.svg +65 -0
- data/lib/tennpipes-init/generators/project/public/images/forever21.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.eot +0 -0
- data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.svg +970 -0
- data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.ttf +0 -0
- data/lib/tennpipes-init/generators/project/public/images/icons/foundation-icons.woff +0 -0
- data/lib/tennpipes-init/generators/project/public/images/icons/tennpipes-icons.css +598 -0
- data/lib/tennpipes-init/generators/project/public/images/image1.jpg +0 -0
- data/lib/tennpipes-init/generators/project/public/images/image11.jpg +0 -0
- data/lib/tennpipes-init/generators/project/public/images/image2.jpg +0 -0
- data/lib/tennpipes-init/generators/project/public/images/image3.jpg +0 -0
- data/lib/tennpipes-init/generators/project/public/images/intuit.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/krispykreme.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/README.md +302 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/bower.json +4 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/composer.json +34 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/gulpfile.js +92 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/holder.js +1920 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/holder.min.js +12 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/package.json +46 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/holder.js +1411 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/augment.js +27 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/ondomready.js +155 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/lib/polyfills.js +177 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/scenegraph.js +101 -0
- data/lib/tennpipes-init/generators/project/public/images/placeholders/src/utils.js +129 -0
- data/lib/tennpipes-init/generators/project/public/images/priceline.com.png +0 -0
- data/lib/tennpipes-init/generators/project/public/images/stripe.png +0 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes.min.js +6081 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.abide.js +340 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.accordion.js +67 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.alert.js +43 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.clearing.js +556 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.dropdown.js +448 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.equalizer.js +77 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.interchange.js +354 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.joyride.js +932 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.js +703 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.magellan.js +203 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.offcanvas.js +152 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.orbit.js +476 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.reveal.js +471 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.slider.js +263 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.tab.js +237 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.tooltip.js +307 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/tennpipes/tennpipes.topbar.js +452 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/vendor/fastclick.js +8 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/vendor/jquery.cookie.js +8 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/vendor/jquery.js +26 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/vendor/modernizr.js +8 -0
- data/lib/tennpipes-init/generators/project/public/javascripts/vendor/placeholder.js +2 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/app.css~ +178 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/app1.css~ +177 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/ennkeypee.css +214 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/ennkeypee.css~ +214 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/normalize.css +427 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.css +6201 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.css~ +6201 -0
- data/lib/tennpipes-init/generators/project/public/stylesheets/tennpipes.min.css +1 -0
- data/lib/tennpipes-init/generators/runner.rb +139 -0
- data/lib/tennpipes-init/generators/task.rb +45 -0
- data/lib/tennpipes-init/generators/templates/Gemfile.tt +32 -0
- data/lib/tennpipes-init/generators/templates/Rakefile.tt +8 -0
- data/lib/tennpipes-init/generators/templates/controller.rb.tt +22 -0
- data/lib/tennpipes-init/generators/templates/gem/README.md.tt +29 -0
- data/lib/tennpipes-init/generators/templates/gem/gemspec.tt +19 -0
- data/lib/tennpipes-init/generators/templates/gem/lib/libname.tt +6 -0
- data/lib/tennpipes-init/generators/templates/gem/lib/libname/version.tt +3 -0
- data/lib/tennpipes-init/generators/templates/helper.rb.tt +13 -0
- data/lib/tennpipes-init/generators/templates/initializer.rb.tt +5 -0
- data/lib/tennpipes-init/generators/templates/mailer.rb.tt +54 -0
- data/lib/tennpipes-init/generators/templates/project_bin.tt +14 -0
- data/lib/tennpipes-init/generators/templates/task.rb.tt +7 -0
- data/lib/tennpipes-init/tennpipes-tasks/activerecord.rb +377 -0
- data/lib/tennpipes-init/tennpipes-tasks/database.rb +12 -0
- data/lib/tennpipes-init/tennpipes-tasks/datamapper.rb +94 -0
- data/lib/tennpipes-init/tennpipes-tasks/minirecord.rb +19 -0
- data/lib/tennpipes-init/tennpipes-tasks/mongoid.rb +215 -0
- data/lib/tennpipes-init/tennpipes-tasks/mongomapper.rb +55 -0
- data/lib/tennpipes-init/tennpipes-tasks/sequel.rb +85 -0
- data/lib/tennpipes-init/tennpipes-tasks/sql-helpers.rb +72 -0
- data/test/fixtures/admin_template.rb +7 -0
- data/test/fixtures/example_template.rb +15 -0
- data/test/fixtures/git_template.rb +4 -0
- data/test/fixtures/plugin_template.rb +13 -0
- data/test/fixtures/rake_template.rb +9 -0
- data/test/helper.rb +103 -0
- data/test/test_app_generator.rb +142 -0
- data/test/test_cli.rb +27 -0
- data/test/test_component_generator.rb +98 -0
- data/test/test_controller_generator.rb +272 -0
- data/test/test_generator.rb +13 -0
- data/test/test_helper_generator.rb +133 -0
- data/test/test_mailer_generator.rb +69 -0
- data/test/test_migration_generator.rb +222 -0
- data/test/test_model_generator.rb +553 -0
- data/test/test_plugin_generator.rb +152 -0
- data/test/test_project_generator.rb +757 -0
- data/test/test_task_generator.rb +53 -0
- 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(/\&(\#[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(/\&(\#[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;
|