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,1920 @@
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 webpackUniversalModuleDefinition(root, factory) {
13
+ if(typeof exports === 'object' && typeof module === 'object')
14
+ module.exports = factory();
15
+ else if(typeof define === 'function' && define.amd)
16
+ define(factory);
17
+ else if(typeof exports === 'object')
18
+ exports["Holder"] = factory();
19
+ else
20
+ root["Holder"] = factory();
21
+ })(this, function() {
22
+ return /******/ (function(modules) { // webpackBootstrap
23
+ /******/ // The module cache
24
+ /******/ var installedModules = {};
25
+
26
+ /******/ // The require function
27
+ /******/ function __webpack_require__(moduleId) {
28
+
29
+ /******/ // Check if module is in cache
30
+ /******/ if(installedModules[moduleId])
31
+ /******/ return installedModules[moduleId].exports;
32
+
33
+ /******/ // Create a new module (and put it into the cache)
34
+ /******/ var module = installedModules[moduleId] = {
35
+ /******/ exports: {},
36
+ /******/ id: moduleId,
37
+ /******/ loaded: false
38
+ /******/ };
39
+
40
+ /******/ // Execute the module function
41
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
42
+
43
+ /******/ // Flag the module as loaded
44
+ /******/ module.loaded = true;
45
+
46
+ /******/ // Return the exports of the module
47
+ /******/ return module.exports;
48
+ /******/ }
49
+
50
+
51
+ /******/ // expose the modules object (__webpack_modules__)
52
+ /******/ __webpack_require__.m = modules;
53
+
54
+ /******/ // expose the module cache
55
+ /******/ __webpack_require__.c = installedModules;
56
+
57
+ /******/ // __webpack_public_path__
58
+ /******/ __webpack_require__.p = "";
59
+
60
+ /******/ // Load entry module and return exports
61
+ /******/ return __webpack_require__(0);
62
+ /******/ })
63
+ /************************************************************************/
64
+ /******/ ([
65
+ /* 0 */
66
+ /***/ function(module, exports, __webpack_require__) {
67
+
68
+ /* WEBPACK VAR INJECTION */(function(global) {/*
69
+ Holder.js - client side image placeholders
70
+ © 2012-2015 Ivan Malopinsky - http://imsky.co
71
+ */
72
+
73
+ //Libraries and functions
74
+ var onDomReady = __webpack_require__(1);
75
+ var SceneGraph = __webpack_require__(2);
76
+ var utils = __webpack_require__(3);
77
+
78
+ var extend = utils.extend;
79
+ var cssProps = utils.cssProps;
80
+ var encodeHtmlEntity = utils.encodeHtmlEntity;
81
+ var decodeHtmlEntity = utils.decodeHtmlEntity;
82
+ var imageExists = utils.imageExists;
83
+ var getNodeArray = utils.getNodeArray;
84
+ var dimensionCheck = utils.dimensionCheck;
85
+
86
+ //Constants and definitions
87
+ var SVG_NS = 'http://www.w3.org/2000/svg';
88
+ var NODE_TYPE_COMMENT = 8;
89
+ var version = '2.6.0';
90
+ var generatorComment = '\n' +
91
+ 'Created with Holder.js ' + version + '.\n' +
92
+ 'Learn more at http://holderjs.com\n' +
93
+ '(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n';
94
+
95
+ var Holder = {
96
+ version: version,
97
+
98
+ /**
99
+ * Adds a theme to default settings
100
+ *
101
+ * @param {string} name Theme name
102
+ * @param {Object} theme Theme object, with foreground, background, size, font, and fontweight properties.
103
+ */
104
+ addTheme: function(name, theme) {
105
+ name != null && theme != null && (App.settings.themes[name] = theme);
106
+ delete App.vars.cache.themeKeys;
107
+ return this;
108
+ },
109
+
110
+ /**
111
+ * Appends a placeholder to an element
112
+ *
113
+ * @param {string} src Placeholder URL string
114
+ * @param {string} el Selector of target element(s)
115
+ */
116
+ addImage: function(src, el) {
117
+ var node = document.querySelectorAll(el);
118
+ if (node.length) {
119
+ for (var i = 0, l = node.length; i < l; i++) {
120
+ var img = newEl('img');
121
+ var domProps = {};
122
+ domProps[App.vars.dataAttr] = src;
123
+ setAttr(img, domProps);
124
+ node[i].appendChild(img);
125
+ }
126
+ }
127
+ return this;
128
+ },
129
+
130
+ /**
131
+ * Sets whether or not an image is updated on resize.
132
+ * If an image is set to be updated, it is immediately rendered.
133
+ *
134
+ * @param {Object} el Image DOM element
135
+ * @param {Boolean} value Resizable update flag value
136
+ */
137
+ setResizeUpdate: function(el, value) {
138
+ if (el.holderData) {
139
+ el.holderData.resizeUpdate = !!value;
140
+ if (el.holderData.resizeUpdate) {
141
+ updateResizableElements(el);
142
+ }
143
+ }
144
+ },
145
+
146
+ /**
147
+ * Runs Holder with options. By default runs Holder on all images with "holder.js" in their source attributes.
148
+ *
149
+ * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties
150
+ */
151
+ run: function(userOptions) {
152
+ userOptions = userOptions || {};
153
+ var engineSettings = {};
154
+ var options = extend(App.settings, userOptions);
155
+
156
+ App.vars.preempted = true;
157
+ App.vars.dataAttr = options.dataAttr || App.vars.dataAttr;
158
+
159
+ engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
160
+ if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) {
161
+ engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
162
+ }
163
+
164
+ var images = getNodeArray(options.images);
165
+ var bgnodes = getNodeArray(options.bgnodes);
166
+ var stylenodes = getNodeArray(options.stylenodes);
167
+ var objects = getNodeArray(options.objects);
168
+
169
+ engineSettings.stylesheets = [];
170
+ engineSettings.svgXMLStylesheet = true;
171
+ engineSettings.noFontFallback = options.noFontFallback ? options.noFontFallback : false;
172
+
173
+ for (var i = 0; i < stylenodes.length; i++) {
174
+ var styleNode = stylenodes[i];
175
+ if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
176
+ var href = styleNode.attributes.href.value;
177
+ //todo: write isomorphic relative-to-absolute URL function
178
+ var proxyLink = newEl('a');
179
+ proxyLink.href = href;
180
+ var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
181
+ engineSettings.stylesheets.push(stylesheetURL);
182
+ }
183
+ }
184
+
185
+ for (i = 0; i < bgnodes.length; i++) {
186
+ //Skip processing background nodes if getComputedStyle is unavailable, since only modern browsers would be able to use canvas or SVG to render to background
187
+ if (!global.getComputedStyle) continue;
188
+ var backgroundImage = global.getComputedStyle(bgnodes[i], null).getPropertyValue('background-image');
189
+ var dataBackgroundImage = bgnodes[i].getAttribute('data-background-src');
190
+ var rawURL = null;
191
+
192
+ if (dataBackgroundImage == null) {
193
+ rawURL = backgroundImage;
194
+ } else {
195
+ rawURL = dataBackgroundImage;
196
+ }
197
+
198
+ var holderURL = null;
199
+ var holderString = '?' + options.domain + '/';
200
+
201
+ if (rawURL.indexOf(holderString) === 0) {
202
+ holderURL = rawURL.slice(1);
203
+ } else if (rawURL.indexOf(holderString) != -1) {
204
+ var fragment = rawURL.substr(rawURL.indexOf(holderString)).slice(1);
205
+ var fragmentMatch = fragment.match(/([^\"]*)"?\)/);
206
+
207
+ if (fragmentMatch != null) {
208
+ holderURL = fragmentMatch[1];
209
+ }
210
+ }
211
+
212
+ if (holderURL != null) {
213
+ var holderFlags = parseURL(holderURL, options);
214
+ if (holderFlags) {
215
+ prepareDOMElement({
216
+ mode: 'background',
217
+ el: bgnodes[i],
218
+ flags: holderFlags,
219
+ engineSettings: engineSettings
220
+ });
221
+ }
222
+ }
223
+ }
224
+
225
+ for (i = 0; i < objects.length; i++) {
226
+ var object = objects[i];
227
+ var objectAttr = {};
228
+
229
+ try {
230
+ objectAttr.data = object.getAttribute('data');
231
+ objectAttr.dataSrc = object.getAttribute(App.vars.dataAttr);
232
+ } catch (e) {}
233
+
234
+ var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0;
235
+ var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0;
236
+
237
+ if (objectHasSrcURL) {
238
+ prepareImageElement(options, engineSettings, objectAttr.data, object);
239
+ } else if (objectHasDataSrcURL) {
240
+ prepareImageElement(options, engineSettings, objectAttr.dataSrc, object);
241
+ }
242
+ }
243
+
244
+ for (i = 0; i < images.length; i++) {
245
+ var image = images[i];
246
+ var imageAttr = {};
247
+
248
+ try {
249
+ imageAttr.src = image.getAttribute('src');
250
+ imageAttr.dataSrc = image.getAttribute(App.vars.dataAttr);
251
+ imageAttr.rendered = image.getAttribute('data-holder-rendered');
252
+ } catch (e) {}
253
+
254
+ var imageHasSrc = imageAttr.src != null;
255
+ var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0;
256
+ var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true';
257
+
258
+ if (imageHasSrc) {
259
+ if (imageAttr.src.indexOf(options.domain) === 0) {
260
+ prepareImageElement(options, engineSettings, imageAttr.src, image);
261
+ } else if (imageHasDataSrcURL) {
262
+ //Image has a valid data-src and an invalid src
263
+ if (imageRendered) {
264
+ //If the placeholder has already been render, re-render it
265
+ prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
266
+ } else {
267
+ //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't
268
+ (function(src, options, engineSettings, dataSrc, image) {
269
+ imageExists(src, function(exists) {
270
+ if (!exists) {
271
+ prepareImageElement(options, engineSettings, dataSrc, image);
272
+ }
273
+ });
274
+ })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image);
275
+ }
276
+ }
277
+ } else if (imageHasDataSrcURL) {
278
+ prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
279
+ }
280
+ }
281
+
282
+ return this;
283
+ }
284
+ };
285
+
286
+ var App = {
287
+ settings: {
288
+ domain: 'holder.js',
289
+ images: 'img',
290
+ objects: 'object',
291
+ bgnodes: 'body .holderjs',
292
+ stylenodes: 'head link.holderjs',
293
+ stylesheets: [],
294
+ themes: {
295
+ 'gray': {
296
+ background: '#EEEEEE',
297
+ foreground: '#AAAAAA'
298
+ },
299
+ 'social': {
300
+ background: '#3a5a97',
301
+ foreground: '#FFFFFF'
302
+ },
303
+ 'industrial': {
304
+ background: '#434A52',
305
+ foreground: '#C2F200'
306
+ },
307
+ 'sky': {
308
+ background: '#0D8FDB',
309
+ foreground: '#FFFFFF'
310
+ },
311
+ 'vine': {
312
+ background: '#39DBAC',
313
+ foreground: '#1E292C'
314
+ },
315
+ 'lava': {
316
+ background: '#F8591A',
317
+ foreground: '#1C2846'
318
+ }
319
+ }
320
+ },
321
+ defaults: {
322
+ size: 10,
323
+ units: 'pt',
324
+ scale: 1 / 16
325
+ },
326
+ flags: {
327
+ dimensions: {
328
+ regex: /^(\d+)x(\d+)$/,
329
+ output: function(val) {
330
+ var exec = this.regex.exec(val);
331
+ return {
332
+ width: +exec[1],
333
+ height: +exec[2]
334
+ };
335
+ }
336
+ },
337
+ fluid: {
338
+ regex: /^([0-9]+%?)x([0-9]+%?)$/,
339
+ output: function(val) {
340
+ var exec = this.regex.exec(val);
341
+ return {
342
+ width: exec[1],
343
+ height: exec[2]
344
+ };
345
+ }
346
+ },
347
+ colors: {
348
+ regex: /(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,
349
+ output: function(val) {
350
+ var exec = this.regex.exec(val);
351
+ return {
352
+ foreground: '#' + exec[2],
353
+ background: '#' + exec[1]
354
+ };
355
+ }
356
+ },
357
+ text: {
358
+ regex: /text\:(.*)/,
359
+ output: function(val) {
360
+ return this.regex.exec(val)[1].replace('\\/', '/');
361
+ }
362
+ },
363
+ font: {
364
+ regex: /font\:(.*)/,
365
+ output: function(val) {
366
+ return this.regex.exec(val)[1];
367
+ }
368
+ },
369
+ auto: {
370
+ regex: /^auto$/
371
+ },
372
+ textmode: {
373
+ regex: /textmode\:(.*)/,
374
+ output: function(val) {
375
+ return this.regex.exec(val)[1];
376
+ }
377
+ },
378
+ random: {
379
+ regex: /^random$/
380
+ },
381
+ size: {
382
+ regex: /size\:(\d+)/,
383
+ output: function(val) {
384
+ return this.regex.exec(val)[1];
385
+ }
386
+ }
387
+ }
388
+ };
389
+
390
+ /**
391
+ * Processes provided source attribute and sets up the appropriate rendering workflow
392
+ *
393
+ * @private
394
+ * @param options Instance options from Holder.run
395
+ * @param renderSettings Instance configuration
396
+ * @param src Image URL
397
+ * @param el Image DOM element
398
+ */
399
+ function prepareImageElement(options, engineSettings, src, el) {
400
+ var holderFlags = parseURL(src.substr(src.lastIndexOf(options.domain)), options);
401
+ if (holderFlags) {
402
+ prepareDOMElement({
403
+ mode: null,
404
+ el: el,
405
+ flags: holderFlags,
406
+ engineSettings: engineSettings
407
+ });
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Processes a Holder URL and extracts flags
413
+ *
414
+ * @private
415
+ * @param url URL
416
+ * @param options Instance options from Holder.run
417
+ */
418
+ function parseURL(url, options) {
419
+ var ret = {
420
+ theme: extend(App.settings.themes.gray, null),
421
+ stylesheets: options.stylesheets,
422
+ holderURL: []
423
+ };
424
+ var render = false;
425
+ var vtab = String.fromCharCode(11);
426
+ var flags = url.replace(/([^\\])\//g, '$1' + vtab).split(vtab);
427
+ var uriRegex = /%[0-9a-f]{2}/gi;
428
+ for (var fl = flags.length, j = 0; j < fl; j++) {
429
+ var flag = flags[j];
430
+ if (flag.match(uriRegex)) {
431
+ try {
432
+ flag = decodeURIComponent(flag);
433
+ } catch (e) {
434
+ flag = flags[j];
435
+ }
436
+ }
437
+
438
+ var push = false;
439
+
440
+ if (App.flags.dimensions.match(flag)) {
441
+ render = true;
442
+ ret.dimensions = App.flags.dimensions.output(flag);
443
+ push = true;
444
+ } else if (App.flags.fluid.match(flag)) {
445
+ render = true;
446
+ ret.dimensions = App.flags.fluid.output(flag);
447
+ ret.fluid = true;
448
+ push = true;
449
+ } else if (App.flags.textmode.match(flag)) {
450
+ ret.textmode = App.flags.textmode.output(flag);
451
+ push = true;
452
+ } else if (App.flags.colors.match(flag)) {
453
+ var colors = App.flags.colors.output(flag);
454
+ ret.theme = extend(ret.theme, colors);
455
+ //todo: convert implicit theme use to a theme: flag
456
+ push = true;
457
+ } else if (options.themes[flag]) {
458
+ //If a theme is specified, it will override custom colors
459
+ if (options.themes.hasOwnProperty(flag)) {
460
+ ret.theme = extend(options.themes[flag], null);
461
+ }
462
+ push = true;
463
+ } else if (App.flags.font.match(flag)) {
464
+ ret.font = App.flags.font.output(flag);
465
+ push = true;
466
+ } else if (App.flags.auto.match(flag)) {
467
+ ret.auto = true;
468
+ push = true;
469
+ } else if (App.flags.text.match(flag)) {
470
+ ret.text = App.flags.text.output(flag);
471
+ push = true;
472
+ } else if (App.flags.size.match(flag)) {
473
+ ret.size = App.flags.size.output(flag);
474
+ push = true;
475
+ } else if (App.flags.random.match(flag)) {
476
+ if (App.vars.cache.themeKeys == null) {
477
+ App.vars.cache.themeKeys = Object.keys(options.themes);
478
+ }
479
+ var theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
480
+ ret.theme = extend(options.themes[theme], null);
481
+ push = true;
482
+ }
483
+
484
+ if (push) {
485
+ ret.holderURL.push(flag);
486
+ }
487
+ }
488
+ ret.holderURL.unshift(options.domain);
489
+ ret.holderURL = ret.holderURL.join('/');
490
+ return render ? ret : false;
491
+ }
492
+
493
+ /**
494
+ * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders)
495
+ *
496
+ * @private
497
+ * @param settings DOM prep settings
498
+ */
499
+ function prepareDOMElement(prepSettings) {
500
+ var mode = prepSettings.mode;
501
+ var el = prepSettings.el;
502
+ var flags = prepSettings.flags;
503
+ var _engineSettings = prepSettings.engineSettings;
504
+ var dimensions = flags.dimensions,
505
+ theme = flags.theme;
506
+ var dimensionsCaption = dimensions.width + 'x' + dimensions.height;
507
+ mode = mode == null ? (flags.fluid ? 'fluid' : 'image') : mode;
508
+
509
+ if (flags.text != null) {
510
+ theme.text = flags.text;
511
+
512
+ //<object> SVG embedding doesn't parse Unicode properly
513
+ if (el.nodeName.toLowerCase() === 'object') {
514
+ var textLines = theme.text.split('\\n');
515
+ for (var k = 0; k < textLines.length; k++) {
516
+ textLines[k] = encodeHtmlEntity(textLines[k]);
517
+ }
518
+ theme.text = textLines.join('\\n');
519
+ }
520
+ }
521
+
522
+ var holderURL = flags.holderURL;
523
+ var engineSettings = extend(_engineSettings, null);
524
+
525
+ if (flags.font) {
526
+ theme.font = flags.font;
527
+ //Only run the <canvas> webfont fallback if noFontFallback is false, if the node is not an image, and if canvas is supported
528
+ if (!engineSettings.noFontFallback && el.nodeName.toLowerCase() === 'img' && App.setup.supportsCanvas && engineSettings.renderer === 'svg') {
529
+ engineSettings = extend(engineSettings, {
530
+ renderer: 'canvas'
531
+ });
532
+ }
533
+ }
534
+
535
+ //Chrome and Opera require a quick 10ms re-render if web fonts are used with canvas
536
+ if (flags.font && engineSettings.renderer == 'canvas') {
537
+ engineSettings.reRender = true;
538
+ }
539
+
540
+ if (mode == 'background') {
541
+ if (el.getAttribute('data-background-src') == null) {
542
+ setAttr(el, {
543
+ 'data-background-src': holderURL
544
+ });
545
+ }
546
+ } else {
547
+ var domProps = {};
548
+ domProps[App.vars.dataAttr] = holderURL;
549
+ setAttr(el, domProps);
550
+ }
551
+
552
+ flags.theme = theme;
553
+
554
+ //todo consider using all renderSettings in holderData
555
+ el.holderData = {
556
+ flags: flags,
557
+ engineSettings: engineSettings
558
+ };
559
+
560
+ if (mode == 'image' || mode == 'fluid') {
561
+ setAttr(el, {
562
+ 'alt': (theme.text ? theme.text + ' [' + dimensionsCaption + ']' : dimensionsCaption)
563
+ });
564
+ }
565
+
566
+ var renderSettings = {
567
+ mode: mode,
568
+ el: el,
569
+ holderSettings: {
570
+ dimensions: dimensions,
571
+ theme: theme,
572
+ flags: flags
573
+ },
574
+ engineSettings: engineSettings
575
+ };
576
+
577
+ if (mode == 'image') {
578
+ if (engineSettings.renderer == 'html' || !flags.auto) {
579
+ el.style.width = dimensions.width + 'px';
580
+ el.style.height = dimensions.height + 'px';
581
+ }
582
+ if (engineSettings.renderer == 'html') {
583
+ el.style.backgroundColor = theme.background;
584
+ } else {
585
+ render(renderSettings);
586
+
587
+ if (flags.textmode == 'exact') {
588
+ el.holderData.resizeUpdate = true;
589
+ App.vars.resizableImages.push(el);
590
+ updateResizableElements(el);
591
+ }
592
+ }
593
+ } else if (mode == 'background' && engineSettings.renderer != 'html') {
594
+ render(renderSettings);
595
+ } else if (mode == 'fluid') {
596
+ el.holderData.resizeUpdate = true;
597
+
598
+ if (dimensions.height.slice(-1) == '%') {
599
+ el.style.height = dimensions.height;
600
+ } else if (flags.auto == null || !flags.auto) {
601
+ el.style.height = dimensions.height + 'px';
602
+ }
603
+ if (dimensions.width.slice(-1) == '%') {
604
+ el.style.width = dimensions.width;
605
+ } else if (flags.auto == null || !flags.auto) {
606
+ el.style.width = dimensions.width + 'px';
607
+ }
608
+ if (el.style.display == 'inline' || el.style.display === '' || el.style.display == 'none') {
609
+ el.style.display = 'block';
610
+ }
611
+
612
+ setInitialDimensions(el);
613
+
614
+ if (engineSettings.renderer == 'html') {
615
+ el.style.backgroundColor = theme.background;
616
+ } else {
617
+ App.vars.resizableImages.push(el);
618
+ updateResizableElements(el);
619
+ }
620
+ }
621
+ }
622
+
623
+ /**
624
+ * Core function that takes output from renderers and sets it as the source or background-image of the target element
625
+ *
626
+ * @private
627
+ * @param renderSettings Renderer settings
628
+ */
629
+ function render(renderSettings) {
630
+ var image = null;
631
+ var mode = renderSettings.mode;
632
+ var holderSettings = renderSettings.holderSettings;
633
+ var el = renderSettings.el;
634
+ var engineSettings = renderSettings.engineSettings;
635
+
636
+ switch (engineSettings.renderer) {
637
+ case 'svg':
638
+ if (!App.setup.supportsSVG) return;
639
+ break;
640
+ case 'canvas':
641
+ if (!App.setup.supportsCanvas) return;
642
+ break;
643
+ default:
644
+ return;
645
+ }
646
+
647
+ //todo: move generation of scene up to flag generation to reduce extra object creation
648
+ var scene = {
649
+ width: holderSettings.dimensions.width,
650
+ height: holderSettings.dimensions.height,
651
+ theme: holderSettings.theme,
652
+ flags: holderSettings.flags
653
+ };
654
+
655
+ var sceneGraph = buildSceneGraph(scene);
656
+
657
+ function getRenderedImage() {
658
+ var image = null;
659
+ switch (engineSettings.renderer) {
660
+ case 'canvas':
661
+ image = sgCanvasRenderer(sceneGraph, renderSettings);
662
+ break;
663
+ case 'svg':
664
+ image = sgSVGRenderer(sceneGraph, renderSettings);
665
+ break;
666
+ default:
667
+ throw 'Holder: invalid renderer: ' + engineSettings.renderer;
668
+ }
669
+ return image;
670
+ }
671
+
672
+ image = getRenderedImage();
673
+
674
+ if (image == null) {
675
+ throw 'Holder: couldn\'t render placeholder';
676
+ }
677
+
678
+ //todo: add <object> canvas rendering
679
+ if (mode == 'background') {
680
+ el.style.backgroundImage = 'url(' + image + ')';
681
+ el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px';
682
+ } else {
683
+ if (el.nodeName.toLowerCase() === 'img') {
684
+ setAttr(el, {
685
+ 'src': image
686
+ });
687
+ } else if (el.nodeName.toLowerCase() === 'object') {
688
+ setAttr(el, {
689
+ 'data': image
690
+ });
691
+ setAttr(el, {
692
+ 'type': 'image/svg+xml'
693
+ });
694
+ }
695
+ if (engineSettings.reRender) {
696
+ global.setTimeout(function() {
697
+ var image = getRenderedImage();
698
+ if (image == null) {
699
+ throw 'Holder: couldn\'t render placeholder';
700
+ }
701
+ //todo: refactor this code into a function
702
+ if (el.nodeName.toLowerCase() === 'img') {
703
+ setAttr(el, {
704
+ 'src': image
705
+ });
706
+ } else if (el.nodeName.toLowerCase() === 'object') {
707
+ setAttr(el, {
708
+ 'data': image
709
+ });
710
+ setAttr(el, {
711
+ 'type': 'image/svg+xml'
712
+ });
713
+ }
714
+ }, 100);
715
+ }
716
+ }
717
+ //todo: account for re-rendering
718
+ setAttr(el, {
719
+ 'data-holder-rendered': true
720
+ });
721
+ }
722
+
723
+ /**
724
+ * Core function that takes a Holder scene description and builds a scene graph
725
+ *
726
+ * @private
727
+ * @param scene Holder scene object
728
+ */
729
+ function buildSceneGraph(scene) {
730
+ var fontSize = App.defaults.size;
731
+ if (parseFloat(scene.theme.size)) {
732
+ fontSize = scene.theme.size;
733
+ } else if (parseFloat(scene.flags.size)) {
734
+ fontSize = scene.flags.size;
735
+ }
736
+
737
+ scene.font = {
738
+ family: scene.theme.font ? scene.theme.font : 'Arial, Helvetica, Open Sans, sans-serif',
739
+ size: textSize(scene.width, scene.height, fontSize),
740
+ units: scene.theme.units ? scene.theme.units : App.defaults.units,
741
+ weight: scene.theme.fontweight ? scene.theme.fontweight : 'bold'
742
+ };
743
+ scene.text = scene.theme.text ? scene.theme.text : Math.floor(scene.width) + 'x' + Math.floor(scene.height);
744
+
745
+ switch (scene.flags.textmode) {
746
+ case 'literal':
747
+ scene.text = scene.flags.dimensions.width + 'x' + scene.flags.dimensions.height;
748
+ break;
749
+ case 'exact':
750
+ if (!scene.flags.exactDimensions) break;
751
+ scene.text = Math.floor(scene.flags.exactDimensions.width) + 'x' + Math.floor(scene.flags.exactDimensions.height);
752
+ break;
753
+ }
754
+
755
+ var sceneGraph = new SceneGraph({
756
+ width: scene.width,
757
+ height: scene.height
758
+ });
759
+
760
+ var Shape = sceneGraph.Shape;
761
+
762
+ var holderBg = new Shape.Rect('holderBg', {
763
+ fill: scene.theme.background
764
+ });
765
+
766
+ holderBg.resize(scene.width, scene.height);
767
+ sceneGraph.root.add(holderBg);
768
+
769
+ var holderTextGroup = new Shape.Group('holderTextGroup', {
770
+ text: scene.text,
771
+ align: 'center',
772
+ font: scene.font,
773
+ fill: scene.theme.foreground
774
+ });
775
+
776
+ holderTextGroup.moveTo(null, null, 1);
777
+ sceneGraph.root.add(holderTextGroup);
778
+
779
+ var tpdata = holderTextGroup.textPositionData = stagingRenderer(sceneGraph);
780
+ if (!tpdata) {
781
+ throw 'Holder: staging fallback not supported yet.';
782
+ }
783
+ holderTextGroup.properties.leading = tpdata.boundingBox.height;
784
+
785
+ //todo: alignment: TL, TC, TR, CL, CR, BL, BC, BR
786
+ var textNode = null;
787
+ var line = null;
788
+
789
+ function finalizeLine(parent, line, width, height) {
790
+ line.width = width;
791
+ line.height = height;
792
+ parent.width = Math.max(parent.width, line.width);
793
+ parent.height += line.height;
794
+ parent.add(line);
795
+ }
796
+
797
+ if (tpdata.lineCount > 1) {
798
+ var offsetX = 0;
799
+ var offsetY = 0;
800
+ var maxLineWidth = scene.width * App.setup.lineWrapRatio;
801
+ var lineIndex = 0;
802
+ line = new Shape.Group('line' + lineIndex);
803
+
804
+ for (var i = 0; i < tpdata.words.length; i++) {
805
+ var word = tpdata.words[i];
806
+ textNode = new Shape.Text(word.text);
807
+ var newline = word.text == '\\n';
808
+ if (offsetX + word.width >= maxLineWidth || newline === true) {
809
+ finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
810
+ offsetX = 0;
811
+ offsetY += holderTextGroup.properties.leading;
812
+ lineIndex += 1;
813
+ line = new Shape.Group('line' + lineIndex);
814
+ line.y = offsetY;
815
+ }
816
+ if (newline === true) {
817
+ continue;
818
+ }
819
+ textNode.moveTo(offsetX, 0);
820
+ offsetX += tpdata.spaceWidth + word.width;
821
+ line.add(textNode);
822
+ }
823
+
824
+ finalizeLine(holderTextGroup, line, offsetX, holderTextGroup.properties.leading);
825
+
826
+ for (var lineKey in holderTextGroup.children) {
827
+ line = holderTextGroup.children[lineKey];
828
+ line.moveTo(
829
+ (holderTextGroup.width - line.width) / 2,
830
+ null,
831
+ null);
832
+ }
833
+
834
+ holderTextGroup.moveTo(
835
+ (scene.width - holderTextGroup.width) / 2, (scene.height - holderTextGroup.height) / 2,
836
+ null);
837
+
838
+ //If the text exceeds vertical space, move it down so the first line is visible
839
+ if ((scene.height - holderTextGroup.height) / 2 < 0) {
840
+ holderTextGroup.moveTo(null, 0, null);
841
+ }
842
+ } else {
843
+ textNode = new Shape.Text(scene.text);
844
+ line = new Shape.Group('line0');
845
+ line.add(textNode);
846
+ holderTextGroup.add(line);
847
+
848
+ holderTextGroup.moveTo(
849
+ (scene.width - tpdata.boundingBox.width) / 2, (scene.height - tpdata.boundingBox.height) / 2,
850
+ null);
851
+ }
852
+
853
+ //todo: renderlist
854
+
855
+ return sceneGraph;
856
+ }
857
+
858
+ /**
859
+ * Adaptive text sizing function
860
+ *
861
+ * @private
862
+ * @param width Parent width
863
+ * @param height Parent height
864
+ * @param fontSize Requested text size
865
+ */
866
+ function textSize(width, height, fontSize) {
867
+ var stageWidth = parseInt(width, 10);
868
+ var stageHeight = parseInt(height, 10);
869
+
870
+ var bigSide = Math.max(stageWidth, stageHeight);
871
+ var smallSide = Math.min(stageWidth, stageHeight);
872
+
873
+ var newHeight = 0.8 * Math.min(smallSide, bigSide * App.defaults.scale);
874
+ return Math.round(Math.max(fontSize, newHeight));
875
+ }
876
+
877
+ /**
878
+ * Iterates over resizable (fluid or auto) placeholders and renders them
879
+ *
880
+ * @private
881
+ * @param element Optional element selector, specified only if a specific element needs to be re-rendered
882
+ */
883
+ function updateResizableElements(element) {
884
+ var images;
885
+ if (element == null || element.nodeType == null) {
886
+ images = App.vars.resizableImages;
887
+ } else {
888
+ images = [element];
889
+ }
890
+ for (var i = 0, l = images.length; i < l; i++) {
891
+ var el = images[i];
892
+ if (el.holderData) {
893
+ var flags = el.holderData.flags;
894
+ var dimensions = dimensionCheck(el);
895
+ if (dimensions) {
896
+ if (!el.holderData.resizeUpdate) {
897
+ continue;
898
+ }
899
+
900
+ if (flags.fluid && flags.auto) {
901
+ var fluidConfig = el.holderData.fluidConfig;
902
+ switch (fluidConfig.mode) {
903
+ case 'width':
904
+ dimensions.height = dimensions.width / fluidConfig.ratio;
905
+ break;
906
+ case 'height':
907
+ dimensions.width = dimensions.height * fluidConfig.ratio;
908
+ break;
909
+ }
910
+ }
911
+
912
+ var settings = {
913
+ mode: 'image',
914
+ holderSettings: {
915
+ dimensions: dimensions,
916
+ theme: flags.theme,
917
+ flags: flags
918
+ },
919
+ el: el,
920
+ engineSettings: el.holderData.engineSettings
921
+ };
922
+
923
+ if (flags.textmode == 'exact') {
924
+ flags.exactDimensions = dimensions;
925
+ settings.holderSettings.dimensions = flags.dimensions;
926
+ }
927
+
928
+ render(settings);
929
+ } else {
930
+ setInvisible(el);
931
+ }
932
+ }
933
+ }
934
+ }
935
+
936
+ /**
937
+ * Sets up aspect ratio metadata for fluid placeholders, in order to preserve proportions when resizing
938
+ *
939
+ * @private
940
+ * @param el Image DOM element
941
+ */
942
+ function setInitialDimensions(el) {
943
+ if (el.holderData) {
944
+ var dimensions = dimensionCheck(el);
945
+ if (dimensions) {
946
+ var flags = el.holderData.flags;
947
+
948
+ var fluidConfig = {
949
+ fluidHeight: flags.dimensions.height.slice(-1) == '%',
950
+ fluidWidth: flags.dimensions.width.slice(-1) == '%',
951
+ mode: null,
952
+ initialDimensions: dimensions
953
+ };
954
+
955
+ if (fluidConfig.fluidWidth && !fluidConfig.fluidHeight) {
956
+ fluidConfig.mode = 'width';
957
+ fluidConfig.ratio = fluidConfig.initialDimensions.width / parseFloat(flags.dimensions.height);
958
+ } else if (!fluidConfig.fluidWidth && fluidConfig.fluidHeight) {
959
+ fluidConfig.mode = 'height';
960
+ fluidConfig.ratio = parseFloat(flags.dimensions.width) / fluidConfig.initialDimensions.height;
961
+ }
962
+
963
+ el.holderData.fluidConfig = fluidConfig;
964
+ } else {
965
+ setInvisible(el);
966
+ }
967
+ }
968
+ }
969
+
970
+ /**
971
+ * Iterates through all current invisible images, and if they're visible, renders them and removes them from further checks. Runs every animation frame.
972
+ *
973
+ * @private
974
+ */
975
+ function visibilityCheck() {
976
+ var renderableImages = [];
977
+ var keys = Object.keys(App.vars.invisibleImages);
978
+ var el;
979
+ for (var i = 0, l = keys.length; i < l; i++) {
980
+ el = App.vars.invisibleImages[keys[i]];
981
+ if (dimensionCheck(el) && el.nodeName.toLowerCase() == 'img') {
982
+ renderableImages.push(el);
983
+ delete App.vars.invisibleImages[keys[i]];
984
+ }
985
+ }
986
+
987
+ if (renderableImages.length) {
988
+ Holder.run({
989
+ images: renderableImages
990
+ });
991
+ }
992
+
993
+ global.requestAnimationFrame(visibilityCheck);
994
+ }
995
+
996
+ /**
997
+ * Starts checking for invisible placeholders if not doing so yet. Does nothing otherwise.
998
+ *
999
+ * @private
1000
+ */
1001
+ function startVisibilityCheck() {
1002
+ if (!App.vars.visibilityCheckStarted) {
1003
+ global.requestAnimationFrame(visibilityCheck);
1004
+ App.vars.visibilityCheckStarted = true;
1005
+ }
1006
+ }
1007
+
1008
+ /**
1009
+ * Sets a unique ID for an image detected to be invisible and adds it to the map of invisible images checked by visibilityCheck
1010
+ *
1011
+ * @private
1012
+ * @param el Invisible DOM element
1013
+ */
1014
+ function setInvisible(el) {
1015
+ if (!el.holderData.invisibleId) {
1016
+ App.vars.invisibleId += 1;
1017
+ App.vars.invisibleImages['i' + App.vars.invisibleId] = el;
1018
+ el.holderData.invisibleId = App.vars.invisibleId;
1019
+ }
1020
+ }
1021
+
1022
+ //todo: see if possible to convert stagingRenderer to use HTML only
1023
+ var stagingRenderer = (function() {
1024
+ var svg = null,
1025
+ stagingText = null,
1026
+ stagingTextNode = null;
1027
+ return function(graph) {
1028
+ var rootNode = graph.root;
1029
+ if (App.setup.supportsSVG) {
1030
+ var firstTimeSetup = false;
1031
+ var tnode = function(text) {
1032
+ return document.createTextNode(text);
1033
+ };
1034
+ if (svg == null || svg.parentNode !== document.body) {
1035
+ firstTimeSetup = true;
1036
+ }
1037
+
1038
+ svg = initSVG(svg, rootNode.properties.width, rootNode.properties.height);
1039
+ //Show staging element before staging
1040
+ svg.style.display = 'block';
1041
+
1042
+ if (firstTimeSetup) {
1043
+ stagingText = newEl('text', SVG_NS);
1044
+ stagingTextNode = tnode(null);
1045
+ setAttr(stagingText, {
1046
+ x: 0
1047
+ });
1048
+ stagingText.appendChild(stagingTextNode);
1049
+ svg.appendChild(stagingText);
1050
+ document.body.appendChild(svg);
1051
+ svg.style.visibility = 'hidden';
1052
+ svg.style.position = 'absolute';
1053
+ svg.style.top = '-100%';
1054
+ svg.style.left = '-100%';
1055
+ //todo: workaround for zero-dimension <svg> tag in Opera 12
1056
+ //svg.setAttribute('width', 0);
1057
+ //svg.setAttribute('height', 0);
1058
+ }
1059
+
1060
+ var holderTextGroup = rootNode.children.holderTextGroup;
1061
+ var htgProps = holderTextGroup.properties;
1062
+ setAttr(stagingText, {
1063
+ 'y': htgProps.font.size,
1064
+ 'style': cssProps({
1065
+ 'font-weight': htgProps.font.weight,
1066
+ 'font-size': htgProps.font.size + htgProps.font.units,
1067
+ 'font-family': htgProps.font.family
1068
+ })
1069
+ });
1070
+
1071
+ //Get bounding box for the whole string (total width and height)
1072
+ stagingTextNode.nodeValue = htgProps.text;
1073
+ var stagingTextBBox = stagingText.getBBox();
1074
+
1075
+ //Get line count and split the string into words
1076
+ var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.setup.lineWrapRatio));
1077
+ var words = htgProps.text.split(' ');
1078
+ var newlines = htgProps.text.match(/\\n/g);
1079
+ lineCount += newlines == null ? 0 : newlines.length;
1080
+
1081
+ //Get bounding box for the string with spaces removed
1082
+ stagingTextNode.nodeValue = htgProps.text.replace(/[ ]+/g, '');
1083
+ var computedNoSpaceLength = stagingText.getComputedTextLength();
1084
+
1085
+ //Compute average space width
1086
+ var diffLength = stagingTextBBox.width - computedNoSpaceLength;
1087
+ var spaceWidth = Math.round(diffLength / Math.max(1, words.length - 1));
1088
+
1089
+ //Get widths for every word with space only if there is more than one line
1090
+ var wordWidths = [];
1091
+ if (lineCount > 1) {
1092
+ stagingTextNode.nodeValue = '';
1093
+ for (var i = 0; i < words.length; i++) {
1094
+ if (words[i].length === 0) continue;
1095
+ stagingTextNode.nodeValue = decodeHtmlEntity(words[i]);
1096
+ var bbox = stagingText.getBBox();
1097
+ wordWidths.push({
1098
+ text: words[i],
1099
+ width: bbox.width
1100
+ });
1101
+ }
1102
+ }
1103
+
1104
+ //Hide staging element after staging
1105
+ svg.style.display = 'none';
1106
+
1107
+ return {
1108
+ spaceWidth: spaceWidth,
1109
+ lineCount: lineCount,
1110
+ boundingBox: stagingTextBBox,
1111
+ words: wordWidths
1112
+ };
1113
+ } else {
1114
+ //todo: canvas fallback for measuring text on android 2.3
1115
+ return false;
1116
+ }
1117
+ };
1118
+ })();
1119
+
1120
+ var sgCanvasRenderer = (function() {
1121
+ var canvas = newEl('canvas');
1122
+ var ctx = null;
1123
+
1124
+ return function(sceneGraph) {
1125
+ if (ctx == null) {
1126
+ ctx = canvas.getContext('2d');
1127
+ }
1128
+ var root = sceneGraph.root;
1129
+ canvas.width = App.dpr(root.properties.width);
1130
+ canvas.height = App.dpr(root.properties.height);
1131
+ ctx.textBaseline = 'middle';
1132
+
1133
+ ctx.fillStyle = root.children.holderBg.properties.fill;
1134
+ ctx.fillRect(0, 0, App.dpr(root.children.holderBg.width), App.dpr(root.children.holderBg.height));
1135
+
1136
+ var textGroup = root.children.holderTextGroup;
1137
+ var tgProps = textGroup.properties;
1138
+ ctx.font = textGroup.properties.font.weight + ' ' + App.dpr(textGroup.properties.font.size) + textGroup.properties.font.units + ' ' + textGroup.properties.font.family + ', monospace';
1139
+ ctx.fillStyle = textGroup.properties.fill;
1140
+
1141
+ for (var lineKey in textGroup.children) {
1142
+ var line = textGroup.children[lineKey];
1143
+ for (var wordKey in line.children) {
1144
+ var word = line.children[wordKey];
1145
+ var x = App.dpr(textGroup.x + line.x + word.x);
1146
+ var y = App.dpr(textGroup.y + line.y + word.y + (textGroup.properties.leading / 2));
1147
+
1148
+ ctx.fillText(word.properties.text, x, y);
1149
+ }
1150
+ }
1151
+
1152
+ return canvas.toDataURL('image/png');
1153
+ };
1154
+ })();
1155
+
1156
+ var sgSVGRenderer = (function() {
1157
+ //Prevent IE <9 from initializing SVG renderer
1158
+ if (!global.XMLSerializer) return;
1159
+ var xml = createXML();
1160
+ var svg = initSVG(null, 0, 0);
1161
+ var bgEl = newEl('rect', SVG_NS);
1162
+ svg.appendChild(bgEl);
1163
+
1164
+ //todo: create a reusable pool for textNodes, resize if more words present
1165
+
1166
+ return function(sceneGraph, renderSettings) {
1167
+ var root = sceneGraph.root;
1168
+
1169
+ initSVG(svg, root.properties.width, root.properties.height);
1170
+
1171
+ var groups = svg.querySelectorAll('g');
1172
+
1173
+ for (var i = 0; i < groups.length; i++) {
1174
+ groups[i].parentNode.removeChild(groups[i]);
1175
+ }
1176
+
1177
+ var holderURL = renderSettings.holderSettings.flags.holderURL;
1178
+ var holderId = 'holder_' + (Number(new Date()) + 32768 + (0 | Math.random() * 32768)).toString(16);
1179
+ var sceneGroupEl = newEl('g', SVG_NS);
1180
+ var textGroup = root.children.holderTextGroup;
1181
+ var tgProps = textGroup.properties;
1182
+ var textGroupEl = newEl('g', SVG_NS);
1183
+ var tpdata = textGroup.textPositionData;
1184
+ var textCSSRule = '#' + holderId + ' text { ' +
1185
+ cssProps({
1186
+ 'fill': tgProps.fill,
1187
+ 'font-weight': tgProps.font.weight,
1188
+ 'font-family': tgProps.font.family + ', monospace',
1189
+ 'font-size': tgProps.font.size + tgProps.font.units
1190
+ }) + ' } ';
1191
+ var commentNode = xml.createComment('\n' + 'Source URL: ' + holderURL + generatorComment);
1192
+ var holderCSS = xml.createCDATASection(textCSSRule);
1193
+ var styleEl = svg.querySelector('style');
1194
+
1195
+ setAttr(sceneGroupEl, {
1196
+ id: holderId
1197
+ });
1198
+
1199
+ svg.insertBefore(commentNode, svg.firstChild);
1200
+ styleEl.appendChild(holderCSS);
1201
+
1202
+ sceneGroupEl.appendChild(bgEl);
1203
+ sceneGroupEl.appendChild(textGroupEl);
1204
+ svg.appendChild(sceneGroupEl);
1205
+
1206
+ setAttr(bgEl, {
1207
+ 'width': root.children.holderBg.width,
1208
+ 'height': root.children.holderBg.height,
1209
+ 'fill': root.children.holderBg.properties.fill
1210
+ });
1211
+
1212
+ textGroup.y += tpdata.boundingBox.height * 0.8;
1213
+
1214
+ for (var lineKey in textGroup.children) {
1215
+ var line = textGroup.children[lineKey];
1216
+ for (var wordKey in line.children) {
1217
+ var word = line.children[wordKey];
1218
+ var x = textGroup.x + line.x + word.x;
1219
+ var y = textGroup.y + line.y + word.y;
1220
+
1221
+ var textEl = newEl('text', SVG_NS);
1222
+ var textNode = document.createTextNode(null);
1223
+
1224
+ setAttr(textEl, {
1225
+ 'x': x,
1226
+ 'y': y
1227
+ });
1228
+
1229
+ textNode.nodeValue = word.properties.text;
1230
+ textEl.appendChild(textNode);
1231
+ textGroupEl.appendChild(textEl);
1232
+ }
1233
+ }
1234
+
1235
+ var svgString = 'data:image/svg+xml;base64,' +
1236
+ btoa(unescape(encodeURIComponent(serializeSVG(svg, renderSettings.engineSettings))));
1237
+ return svgString;
1238
+ };
1239
+ })();
1240
+
1241
+ //Helpers
1242
+
1243
+ /**
1244
+ * Generic new DOM element function
1245
+ *
1246
+ * @private
1247
+ * @param tag Tag to create
1248
+ * @param namespace Optional namespace value
1249
+ */
1250
+ function newEl(tag, namespace) {
1251
+ if (namespace == null) {
1252
+ return document.createElement(tag);
1253
+ } else {
1254
+ return document.createElementNS(namespace, tag);
1255
+ }
1256
+ }
1257
+
1258
+ /**
1259
+ * Generic setAttribute function
1260
+ *
1261
+ * @private
1262
+ * @param el Reference to DOM element
1263
+ * @param attrs Object with attribute keys and values
1264
+ */
1265
+ function setAttr(el, attrs) {
1266
+ for (var a in attrs) {
1267
+ el.setAttribute(a, attrs[a]);
1268
+ }
1269
+ }
1270
+
1271
+ /**
1272
+ * Generic SVG element creation function
1273
+ *
1274
+ * @private
1275
+ * @param svg SVG context, set to null if new
1276
+ * @param width Document width
1277
+ * @param height Document height
1278
+ */
1279
+ function initSVG(svg, width, height) {
1280
+ var defs, style;
1281
+
1282
+ if (svg == null) {
1283
+ svg = newEl('svg', SVG_NS);
1284
+ defs = newEl('defs', SVG_NS);
1285
+ style = newEl('style', SVG_NS);
1286
+ setAttr(style, {
1287
+ 'type': 'text/css'
1288
+ });
1289
+ defs.appendChild(style);
1290
+ svg.appendChild(defs);
1291
+ } else {
1292
+ style = svg.querySelector('style');
1293
+ }
1294
+
1295
+ //IE throws an exception if this is set and Chrome requires it to be set
1296
+ if (svg.webkitMatchesSelector) {
1297
+ svg.setAttribute('xmlns', SVG_NS);
1298
+ }
1299
+ //Remove comment nodes
1300
+ for (var i = 0; i < svg.childNodes.length; i++) {
1301
+ if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) {
1302
+ svg.removeChild(svg.childNodes[i]);
1303
+ }
1304
+ }
1305
+
1306
+ //Remove CSS
1307
+ while (style.childNodes.length) {
1308
+ style.removeChild(style.childNodes[0]);
1309
+ }
1310
+
1311
+ setAttr(svg, {
1312
+ 'width': width,
1313
+ 'height': height,
1314
+ 'viewBox': '0 0 ' + width + ' ' + height,
1315
+ 'preserveAspectRatio': 'none'
1316
+ });
1317
+
1318
+ return svg;
1319
+ }
1320
+
1321
+ /**
1322
+ * Returns XML processing instructions
1323
+ *
1324
+ * @private
1325
+ * @param svg SVG context
1326
+ * @param stylesheets CSS stylesheets to include
1327
+ */
1328
+ function serializeSVG(svg, engineSettings) {
1329
+ if (!global.XMLSerializer) return;
1330
+ var serializer = new XMLSerializer();
1331
+ var svgCSS = '';
1332
+ var stylesheets = engineSettings.stylesheets;
1333
+
1334
+ //External stylesheets: Processing Instruction method
1335
+ if (engineSettings.svgXMLStylesheet) {
1336
+ var xml = createXML();
1337
+ //Add <?xml-stylesheet ?> directives
1338
+ for (var i = stylesheets.length - 1; i >= 0; i--) {
1339
+ var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"');
1340
+ xml.insertBefore(csspi, xml.firstChild);
1341
+ }
1342
+
1343
+ //Add <?xml ... ?> UTF-8 directive
1344
+ var xmlpi = xml.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8" standalone="yes"');
1345
+ xml.insertBefore(xmlpi, xml.firstChild);
1346
+ xml.removeChild(xml.documentElement);
1347
+ svgCSS = serializer.serializeToString(xml);
1348
+ }
1349
+
1350
+ var svgText = serializer.serializeToString(svg);
1351
+ svgText = svgText.replace(/\&amp;(\#[0-9]{2,}\;)/g, '&$1');
1352
+ return svgCSS + svgText;
1353
+ }
1354
+
1355
+ /**
1356
+ * Creates a XML document
1357
+ * @private
1358
+ */
1359
+ function createXML() {
1360
+ if (!global.DOMParser) return;
1361
+ return new DOMParser().parseFromString('<xml />', 'application/xml');
1362
+ }
1363
+
1364
+ /**
1365
+ * Prevents a function from being called too often, waits until a timer elapses to call it again
1366
+ *
1367
+ * @param fn Function to call
1368
+ */
1369
+ function debounce(fn) {
1370
+ if (!App.vars.debounceTimer) fn.call(this);
1371
+ if (App.vars.debounceTimer) global.clearTimeout(App.vars.debounceTimer);
1372
+ App.vars.debounceTimer = global.setTimeout(function() {
1373
+ App.vars.debounceTimer = null;
1374
+ fn.call(this);
1375
+ }, App.setup.debounce);
1376
+ }
1377
+
1378
+ /**
1379
+ * Holder-specific resize/orientation change callback, debounced to prevent excessive execution
1380
+ */
1381
+ function resizeEvent() {
1382
+ debounce(function() {
1383
+ updateResizableElements(null);
1384
+ });
1385
+ }
1386
+
1387
+ //Set up flags
1388
+
1389
+ for (var flag in App.flags) {
1390
+ if (!App.flags.hasOwnProperty(flag)) continue;
1391
+ App.flags[flag].match = function(val) {
1392
+ return val.match(this.regex);
1393
+ };
1394
+ }
1395
+
1396
+ //Properties set once on setup
1397
+
1398
+ App.setup = {
1399
+ renderer: 'html',
1400
+ debounce: 100,
1401
+ ratio: 1,
1402
+ supportsCanvas: false,
1403
+ supportsSVG: false,
1404
+ lineWrapRatio: 0.9,
1405
+ renderers: ['html', 'canvas', 'svg']
1406
+ };
1407
+
1408
+ App.dpr = function(val) {
1409
+ return val * App.setup.ratio;
1410
+ };
1411
+
1412
+ //Properties modified during runtime
1413
+
1414
+ App.vars = {
1415
+ preempted: false,
1416
+ resizableImages: [],
1417
+ invisibleImages: {},
1418
+ invisibleId: 0,
1419
+ visibilityCheckStarted: false,
1420
+ debounceTimer: null,
1421
+ cache: {},
1422
+ dataAttr: 'data-src'
1423
+ };
1424
+
1425
+ //Pre-flight
1426
+
1427
+ (function() {
1428
+ var devicePixelRatio = 1,
1429
+ backingStoreRatio = 1;
1430
+
1431
+ var canvas = newEl('canvas');
1432
+ var ctx = null;
1433
+
1434
+ if (canvas.getContext) {
1435
+ if (canvas.toDataURL('image/png').indexOf('data:image/png') != -1) {
1436
+ App.setup.renderer = 'canvas';
1437
+ ctx = canvas.getContext('2d');
1438
+ App.setup.supportsCanvas = true;
1439
+ }
1440
+ }
1441
+
1442
+ if (App.setup.supportsCanvas) {
1443
+ devicePixelRatio = global.devicePixelRatio || 1;
1444
+ backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
1445
+ }
1446
+
1447
+ App.setup.ratio = devicePixelRatio / backingStoreRatio;
1448
+
1449
+ if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) {
1450
+ App.setup.renderer = 'svg';
1451
+ App.setup.supportsSVG = true;
1452
+ }
1453
+ })();
1454
+
1455
+ //Starts checking for invisible placeholders
1456
+ startVisibilityCheck();
1457
+
1458
+ if (onDomReady) {
1459
+ onDomReady(function() {
1460
+ if (!App.vars.preempted) {
1461
+ Holder.run();
1462
+ }
1463
+ if (global.addEventListener) {
1464
+ global.addEventListener('resize', resizeEvent, false);
1465
+ global.addEventListener('orientationchange', resizeEvent, false);
1466
+ } else {
1467
+ global.attachEvent('onresize', resizeEvent);
1468
+ }
1469
+
1470
+ if (typeof global.Turbolinks == 'object') {
1471
+ global.document.addEventListener('page:change', function() {
1472
+ Holder.run();
1473
+ });
1474
+ }
1475
+ });
1476
+ }
1477
+
1478
+ module.exports = Holder;
1479
+
1480
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
1481
+
1482
+ /***/ },
1483
+ /* 1 */
1484
+ /***/ function(module, exports, __webpack_require__) {
1485
+
1486
+ /*!
1487
+ * onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license
1488
+ *
1489
+ * Specially modified to work with Holder.js
1490
+ */
1491
+
1492
+ function _onDomReady(win) {
1493
+ //Lazy loading fix for Firefox < 3.6
1494
+ //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
1495
+ if (document.readyState == null && document.addEventListener) {
1496
+ document.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
1497
+ document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
1498
+ document.readyState = "complete";
1499
+ }, false);
1500
+ document.readyState = "loading";
1501
+ }
1502
+
1503
+ var doc = win.document,
1504
+ docElem = doc.documentElement,
1505
+
1506
+ LOAD = "load",
1507
+ FALSE = false,
1508
+ ONLOAD = "on"+LOAD,
1509
+ COMPLETE = "complete",
1510
+ READYSTATE = "readyState",
1511
+ ATTACHEVENT = "attachEvent",
1512
+ DETACHEVENT = "detachEvent",
1513
+ ADDEVENTLISTENER = "addEventListener",
1514
+ DOMCONTENTLOADED = "DOMContentLoaded",
1515
+ ONREADYSTATECHANGE = "onreadystatechange",
1516
+ REMOVEEVENTLISTENER = "removeEventListener",
1517
+
1518
+ // W3C Event model
1519
+ w3c = ADDEVENTLISTENER in doc,
1520
+ _top = FALSE,
1521
+
1522
+ // isReady: Is the DOM ready to be used? Set to true once it occurs.
1523
+ isReady = FALSE,
1524
+
1525
+ // Callbacks pending execution until DOM is ready
1526
+ callbacks = [];
1527
+
1528
+ // Handle when the DOM is ready
1529
+ function ready( fn ) {
1530
+ if ( !isReady ) {
1531
+
1532
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1533
+ if ( !doc.body ) {
1534
+ return defer( ready );
1535
+ }
1536
+
1537
+ // Remember that the DOM is ready
1538
+ isReady = true;
1539
+
1540
+ // Execute all callbacks
1541
+ while ( fn = callbacks.shift() ) {
1542
+ defer( fn );
1543
+ }
1544
+ }
1545
+ }
1546
+
1547
+ // The ready event handler
1548
+ function completed( event ) {
1549
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
1550
+ if ( w3c || event.type === LOAD || doc[READYSTATE] === COMPLETE ) {
1551
+ detach();
1552
+ ready();
1553
+ }
1554
+ }
1555
+
1556
+ // Clean-up method for dom ready events
1557
+ function detach() {
1558
+ if ( w3c ) {
1559
+ doc[REMOVEEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE );
1560
+ win[REMOVEEVENTLISTENER]( LOAD, completed, FALSE );
1561
+ } else {
1562
+ doc[DETACHEVENT]( ONREADYSTATECHANGE, completed );
1563
+ win[DETACHEVENT]( ONLOAD, completed );
1564
+ }
1565
+ }
1566
+
1567
+ // Defers a function, scheduling it to run after the current call stack has cleared.
1568
+ function defer( fn, wait ) {
1569
+ // Allow 0 to be passed
1570
+ setTimeout( fn, +wait >= 0 ? wait : 1 );
1571
+ }
1572
+
1573
+ // Attach the listeners:
1574
+
1575
+ // Catch cases where onDomReady is called after the browser event has already occurred.
1576
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
1577
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
1578
+ if ( doc[READYSTATE] === COMPLETE ) {
1579
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
1580
+ defer( ready );
1581
+
1582
+ // Standards-based browsers support DOMContentLoaded
1583
+ } else if ( w3c ) {
1584
+ // Use the handy event callback
1585
+ doc[ADDEVENTLISTENER]( DOMCONTENTLOADED, completed, FALSE );
1586
+
1587
+ // A fallback to window.onload, that will always work
1588
+ win[ADDEVENTLISTENER]( LOAD, completed, FALSE );
1589
+
1590
+ // If IE event model is used
1591
+ } else {
1592
+ // Ensure firing before onload, maybe late but safe also for iframes
1593
+ doc[ATTACHEVENT]( ONREADYSTATECHANGE, completed );
1594
+
1595
+ // A fallback to window.onload, that will always work
1596
+ win[ATTACHEVENT]( ONLOAD, completed );
1597
+
1598
+ // If IE and not a frame
1599
+ // continually check to see if the document is ready
1600
+ try {
1601
+ _top = win.frameElement == null && docElem;
1602
+ } catch(e) {}
1603
+
1604
+ if ( _top && _top.doScroll ) {
1605
+ (function doScrollCheck() {
1606
+ if ( !isReady ) {
1607
+ try {
1608
+ // Use the trick by Diego Perini
1609
+ // http://javascript.nwbox.com/IEContentLoaded/
1610
+ _top.doScroll("left");
1611
+ } catch(e) {
1612
+ return defer( doScrollCheck, 50 );
1613
+ }
1614
+
1615
+ // detach all dom ready events
1616
+ detach();
1617
+
1618
+ // and execute any waiting functions
1619
+ ready();
1620
+ }
1621
+ })();
1622
+ }
1623
+ }
1624
+
1625
+ function onDomReady( fn ) {
1626
+ // If DOM is ready, execute the function (async), otherwise wait
1627
+ isReady ? defer( fn ) : callbacks.push( fn );
1628
+ }
1629
+
1630
+ // Add version
1631
+ onDomReady.version = "1.4.0";
1632
+ // Add method to check if DOM is ready
1633
+ onDomReady.isReady = function(){
1634
+ return isReady;
1635
+ };
1636
+
1637
+ return onDomReady;
1638
+ }
1639
+
1640
+ module.exports = typeof window !== "undefined" && _onDomReady(window);
1641
+
1642
+ /***/ },
1643
+ /* 2 */
1644
+ /***/ function(module, exports, __webpack_require__) {
1645
+
1646
+ var augment = __webpack_require__(4);
1647
+
1648
+ var SceneGraph = function(sceneProperties) {
1649
+ var nodeCount = 1;
1650
+
1651
+ //todo: move merge to helpers section
1652
+ function merge(parent, child) {
1653
+ for (var prop in child) {
1654
+ parent[prop] = child[prop];
1655
+ }
1656
+ return parent;
1657
+ }
1658
+
1659
+ var SceneNode = augment.defclass({
1660
+ constructor: function(name) {
1661
+ nodeCount++;
1662
+ this.parent = null;
1663
+ this.children = {};
1664
+ this.id = nodeCount;
1665
+ this.name = 'n' + nodeCount;
1666
+ if (name != null) {
1667
+ this.name = name;
1668
+ }
1669
+ this.x = 0;
1670
+ this.y = 0;
1671
+ this.z = 0;
1672
+ this.width = 0;
1673
+ this.height = 0;
1674
+ },
1675
+ resize: function(width, height) {
1676
+ if (width != null) {
1677
+ this.width = width;
1678
+ }
1679
+ if (height != null) {
1680
+ this.height = height;
1681
+ }
1682
+ },
1683
+ moveTo: function(x, y, z) {
1684
+ this.x = x != null ? x : this.x;
1685
+ this.y = y != null ? y : this.y;
1686
+ this.z = z != null ? z : this.z;
1687
+ },
1688
+ add: function(child) {
1689
+ var name = child.name;
1690
+ if (this.children[name] == null) {
1691
+ this.children[name] = child;
1692
+ child.parent = this;
1693
+ } else {
1694
+ throw 'SceneGraph: child with that name already exists: ' + name;
1695
+ }
1696
+ }
1697
+ });
1698
+
1699
+ var RootNode = augment(SceneNode, function(uber) {
1700
+ this.constructor = function() {
1701
+ uber.constructor.call(this, 'root');
1702
+ this.properties = sceneProperties;
1703
+ };
1704
+ });
1705
+
1706
+ var Shape = augment(SceneNode, function(uber) {
1707
+ function constructor(name, props) {
1708
+ uber.constructor.call(this, name);
1709
+ this.properties = {
1710
+ fill: '#000'
1711
+ };
1712
+ if (props != null) {
1713
+ merge(this.properties, props);
1714
+ } else if (name != null && typeof name !== 'string') {
1715
+ throw 'SceneGraph: invalid node name';
1716
+ }
1717
+ }
1718
+
1719
+ this.Group = augment.extend(this, {
1720
+ constructor: constructor,
1721
+ type: 'group'
1722
+ });
1723
+
1724
+ this.Rect = augment.extend(this, {
1725
+ constructor: constructor,
1726
+ type: 'rect'
1727
+ });
1728
+
1729
+ this.Text = augment.extend(this, {
1730
+ constructor: function(text) {
1731
+ constructor.call(this);
1732
+ this.properties.text = text;
1733
+ },
1734
+ type: 'text'
1735
+ });
1736
+ });
1737
+
1738
+ var root = new RootNode();
1739
+
1740
+ this.Shape = Shape;
1741
+ this.root = root;
1742
+
1743
+ return this;
1744
+ };
1745
+
1746
+ module.exports = SceneGraph;
1747
+
1748
+
1749
+ /***/ },
1750
+ /* 3 */
1751
+ /***/ function(module, exports, __webpack_require__) {
1752
+
1753
+ /* WEBPACK VAR INJECTION */(function(global) {/**
1754
+ * Shallow object clone and merge
1755
+ *
1756
+ * @param a Object A
1757
+ * @param b Object B
1758
+ * @returns {Object} New object with all of A's properties, and all of B's properties, overwriting A's properties
1759
+ */
1760
+ exports.extend = function(a, b) {
1761
+ var c = {};
1762
+ for (var x in a) {
1763
+ if (a.hasOwnProperty(x)) {
1764
+ c[x] = a[x];
1765
+ }
1766
+ }
1767
+ if (b != null) {
1768
+ for (var y in b) {
1769
+ if (b.hasOwnProperty(y)) {
1770
+ c[y] = b[y];
1771
+ }
1772
+ }
1773
+ }
1774
+ return c;
1775
+ };
1776
+
1777
+ /**
1778
+ * Takes a k/v list of CSS properties and returns a rule
1779
+ *
1780
+ * @param props CSS properties object
1781
+ */
1782
+ exports.cssProps = function(props) {
1783
+ var ret = [];
1784
+ for (var p in props) {
1785
+ if (props.hasOwnProperty(p)) {
1786
+ ret.push(p + ':' + props[p]);
1787
+ }
1788
+ }
1789
+ return ret.join(';');
1790
+ };
1791
+
1792
+ /**
1793
+ * Encodes HTML entities in a string
1794
+ *
1795
+ * @param str Input string
1796
+ */
1797
+ exports.encodeHtmlEntity = function(str) {
1798
+ var buf = [];
1799
+ var charCode = 0;
1800
+ for (var i = str.length - 1; i >= 0; i--) {
1801
+ charCode = str.charCodeAt(i);
1802
+ if (charCode > 128) {
1803
+ buf.unshift(['&#', charCode, ';'].join(''));
1804
+ } else {
1805
+ buf.unshift(str[i]);
1806
+ }
1807
+ }
1808
+ return buf.join('');
1809
+ };
1810
+
1811
+
1812
+ /**
1813
+ * Converts a value into an array of DOM nodes
1814
+ *
1815
+ * @param val A string, a NodeList, a Node, or an HTMLCollection
1816
+ */
1817
+ exports.getNodeArray = function(val) {
1818
+ var retval = null;
1819
+ if (typeof(val) == 'string') {
1820
+ retval = document.querySelectorAll(val);
1821
+ } else if (global.NodeList && val instanceof global.NodeList) {
1822
+ retval = val;
1823
+ } else if (global.Node && val instanceof global.Node) {
1824
+ retval = [val];
1825
+ } else if (global.HTMLCollection && val instanceof global.HTMLCollection) {
1826
+ retval = val;
1827
+ } else if (val instanceof Array) {
1828
+ retval = val;
1829
+ } else if (val === null) {
1830
+ retval = [];
1831
+ }
1832
+ return retval;
1833
+ };
1834
+
1835
+ /**
1836
+ * Checks if an image exists
1837
+ *
1838
+ * @param src URL of image
1839
+ * @param callback Callback to call once image status has been found
1840
+ */
1841
+ exports.imageExists = function(src, callback) {
1842
+ var image = new Image();
1843
+ image.onerror = function() {
1844
+ callback.call(this, false);
1845
+ };
1846
+ image.onload = function() {
1847
+ callback.call(this, true);
1848
+ };
1849
+ image.src = src;
1850
+ };
1851
+
1852
+ /**
1853
+ * Decodes HTML entities in a string
1854
+ *
1855
+ * @param str Input string
1856
+ */
1857
+ exports.decodeHtmlEntity = function(str) {
1858
+ return str.replace(/&#(\d+);/g, function(match, dec) {
1859
+ return String.fromCharCode(dec);
1860
+ });
1861
+ };
1862
+
1863
+
1864
+ /**
1865
+ * Returns an element's dimensions if it's visible, `false` otherwise.
1866
+ *
1867
+ * @private
1868
+ * @param el DOM element
1869
+ */
1870
+ exports.dimensionCheck = function(el) {
1871
+ var dimensions = {
1872
+ height: el.clientHeight,
1873
+ width: el.clientWidth
1874
+ };
1875
+
1876
+ if (dimensions.height && dimensions.width) {
1877
+ return dimensions;
1878
+ } else {
1879
+ return false;
1880
+ }
1881
+ };
1882
+
1883
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
1884
+
1885
+ /***/ },
1886
+ /* 4 */
1887
+ /***/ function(module, exports, __webpack_require__) {
1888
+
1889
+ var Factory = function () {};
1890
+ var slice = Array.prototype.slice;
1891
+
1892
+ var augment = function (base, body) {
1893
+ var uber = Factory.prototype = typeof base === "function" ? base.prototype : base;
1894
+ var prototype = new Factory(), properties = body.apply(prototype, slice.call(arguments, 2).concat(uber));
1895
+ if (typeof properties === "object") for (var key in properties) prototype[key] = properties[key];
1896
+ if (!prototype.hasOwnProperty("constructor")) return prototype;
1897
+ var constructor = prototype.constructor;
1898
+ constructor.prototype = prototype;
1899
+ return constructor;
1900
+ };
1901
+
1902
+ augment.defclass = function (prototype) {
1903
+ var constructor = prototype.constructor;
1904
+ constructor.prototype = prototype;
1905
+ return constructor;
1906
+ };
1907
+
1908
+ augment.extend = function (base, body) {
1909
+ return augment(base, function (uber) {
1910
+ this.uber = uber;
1911
+ return body;
1912
+ });
1913
+ };
1914
+
1915
+ module.exports = augment;
1916
+
1917
+ /***/ }
1918
+ /******/ ])
1919
+ });
1920
+ ;