h2ocube_rails_assets 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,391 @@
1
+ /*jslint browser: true, eqeqeq: true, bitwise: true, newcap: true, immed: true, regexp: false */
2
+
3
+ /**
4
+ LazyLoad makes it easy and painless to lazily load one or more external
5
+ JavaScript or CSS files on demand either during or after the rendering of a web
6
+ page.
7
+
8
+ Supported browsers include Firefox 2+, IE6+, Safari 3+ (including Mobile
9
+ Safari), Google Chrome, and Opera 9+. Other browsers may or may not work and
10
+ are not officially supported.
11
+
12
+ Visit https://github.com/rgrove/lazyload/ for more info.
13
+
14
+ Copyright (c) 2011 Ryan Grove <ryan@wonko.com>
15
+ All rights reserved.
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
18
+ this software and associated documentation files (the 'Software'), to deal in
19
+ the Software without restriction, including without limitation the rights to
20
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
21
+ the Software, and to permit persons to whom the Software is furnished to do so,
22
+ subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
29
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
30
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
31
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
+
34
+ @module lazyload
35
+ @class LazyLoad
36
+ @static
37
+ @version 2.0.3 (git)
38
+ */
39
+
40
+ LazyLoad = (function (doc) {
41
+ // -- Private Variables ------------------------------------------------------
42
+
43
+ // User agent and feature test information.
44
+ var env,
45
+
46
+ // Reference to the <head> element (populated lazily).
47
+ head,
48
+
49
+ // Requests currently in progress, if any.
50
+ pending = {},
51
+
52
+ // Number of times we've polled to check whether a pending stylesheet has
53
+ // finished loading. If this gets too high, we're probably stalled.
54
+ pollCount = 0,
55
+
56
+ // Queued requests.
57
+ queue = {css: [], js: []},
58
+
59
+ // Reference to the browser's list of stylesheets.
60
+ styleSheets = doc.styleSheets;
61
+
62
+ // -- Private Methods --------------------------------------------------------
63
+
64
+ /**
65
+ Creates and returns an HTML element with the specified name and attributes.
66
+
67
+ @method createNode
68
+ @param {String} name element name
69
+ @param {Object} attrs name/value mapping of element attributes
70
+ @return {HTMLElement}
71
+ @private
72
+ */
73
+ function createNode(name, attrs) {
74
+ var node = doc.createElement(name), attr;
75
+
76
+ for (attr in attrs) {
77
+ if (attrs.hasOwnProperty(attr)) {
78
+ node.setAttribute(attr, attrs[attr]);
79
+ }
80
+ }
81
+
82
+ return node;
83
+ }
84
+
85
+ /**
86
+ Called when the current pending resource of the specified type has finished
87
+ loading. Executes the associated callback (if any) and loads the next
88
+ resource in the queue.
89
+
90
+ @method finish
91
+ @param {String} type resource type ('css' or 'js')
92
+ @private
93
+ */
94
+ function finish(type) {
95
+ var p = pending[type],
96
+ callback,
97
+ urls;
98
+
99
+ if (p) {
100
+ callback = p.callback;
101
+ urls = p.urls;
102
+
103
+ urls.shift();
104
+ pollCount = 0;
105
+
106
+ // If this is the last of the pending URLs, execute the callback and
107
+ // start the next request in the queue (if any).
108
+ if (!urls.length) {
109
+ callback && callback.call(p.context, p.obj);
110
+ pending[type] = null;
111
+ queue[type].length && load(type);
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ Populates the <code>env</code> variable with user agent and feature test
118
+ information.
119
+
120
+ @method getEnv
121
+ @private
122
+ */
123
+ function getEnv() {
124
+ var ua = navigator.userAgent;
125
+
126
+ env = {
127
+ // True if this browser supports disabling async mode on dynamically
128
+ // created script nodes. See
129
+ // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
130
+ async: doc.createElement('script').async === true
131
+ };
132
+
133
+ (env.webkit = /AppleWebKit\//.test(ua))
134
+ || (env.ie = /MSIE/.test(ua))
135
+ || (env.opera = /Opera/.test(ua))
136
+ || (env.gecko = /Gecko\//.test(ua))
137
+ || (env.unknown = true);
138
+ }
139
+
140
+ /**
141
+ Loads the specified resources, or the next resource of the specified type
142
+ in the queue if no resources are specified. If a resource of the specified
143
+ type is already being loaded, the new request will be queued until the
144
+ first request has been finished.
145
+
146
+ When an array of resource URLs is specified, those URLs will be loaded in
147
+ parallel if it is possible to do so while preserving execution order. All
148
+ browsers support parallel loading of CSS, but only Firefox and Opera
149
+ support parallel loading of scripts. In other browsers, scripts will be
150
+ queued and loaded one at a time to ensure correct execution order.
151
+
152
+ @method load
153
+ @param {String} type resource type ('css' or 'js')
154
+ @param {String|Array} urls (optional) URL or array of URLs to load
155
+ @param {Function} callback (optional) callback function to execute when the
156
+ resource is loaded
157
+ @param {Object} obj (optional) object to pass to the callback function
158
+ @param {Object} context (optional) if provided, the callback function will
159
+ be executed in this object's context
160
+ @private
161
+ */
162
+ function load(type, urls, callback, obj, context) {
163
+ var _finish = function () { finish(type); },
164
+ isCSS = type === 'css',
165
+ nodes = [],
166
+ i, len, node, p, pendingUrls, url;
167
+
168
+ env || getEnv();
169
+
170
+ if (urls) {
171
+ // If urls is a string, wrap it in an array. Otherwise assume it's an
172
+ // array and create a copy of it so modifications won't be made to the
173
+ // original.
174
+ urls = typeof urls === 'string' ? [urls] : urls.concat();
175
+
176
+ // Create a request object for each URL. If multiple URLs are specified,
177
+ // the callback will only be executed after all URLs have been loaded.
178
+ //
179
+ // Sadly, Firefox and Opera are the only browsers capable of loading
180
+ // scripts in parallel while preserving execution order. In all other
181
+ // browsers, scripts must be loaded sequentially.
182
+ //
183
+ // All browsers respect CSS specificity based on the order of the link
184
+ // elements in the DOM, regardless of the order in which the stylesheets
185
+ // are actually downloaded.
186
+ if (isCSS || env.async || env.gecko || env.opera) {
187
+ // Load in parallel.
188
+ queue[type].push({
189
+ urls : urls,
190
+ callback: callback,
191
+ obj : obj,
192
+ context : context
193
+ });
194
+ } else {
195
+ // Load sequentially.
196
+ for (i = 0, len = urls.length; i < len; ++i) {
197
+ queue[type].push({
198
+ urls : [urls[i]],
199
+ callback: i === len - 1 ? callback : null, // callback is only added to the last URL
200
+ obj : obj,
201
+ context : context
202
+ });
203
+ }
204
+ }
205
+ }
206
+
207
+ // If a previous load request of this type is currently in progress, we'll
208
+ // wait our turn. Otherwise, grab the next item in the queue.
209
+ if (pending[type] || !(p = pending[type] = queue[type].shift())) {
210
+ return;
211
+ }
212
+
213
+ head || (head = doc.head || doc.getElementsByTagName('head')[0]);
214
+ pendingUrls = p.urls;
215
+
216
+ for (i = 0, len = pendingUrls.length; i < len; ++i) {
217
+ url = pendingUrls[i];
218
+
219
+ if (isCSS) {
220
+ node = env.gecko ? createNode('style') : createNode('link', {
221
+ href: url,
222
+ rel : 'stylesheet'
223
+ });
224
+ } else {
225
+ node = createNode('script', {src: url});
226
+ node.async = false;
227
+ }
228
+
229
+ node.className = 'lazyload';
230
+ node.setAttribute('charset', 'utf-8');
231
+
232
+ if (env.ie && !isCSS) {
233
+ node.onreadystatechange = function () {
234
+ if (/loaded|complete/.test(node.readyState)) {
235
+ node.onreadystatechange = null;
236
+ _finish();
237
+ }
238
+ };
239
+ } else if (isCSS && (env.gecko || env.webkit)) {
240
+ // Gecko and WebKit don't support the onload event on link nodes.
241
+ if (env.webkit) {
242
+ // In WebKit, we can poll for changes to document.styleSheets to
243
+ // figure out when stylesheets have loaded.
244
+ p.urls[i] = node.href; // resolve relative URLs (or polling won't work)
245
+ pollWebKit();
246
+ } else {
247
+ // In Gecko, we can import the requested URL into a <style> node and
248
+ // poll for the existence of node.sheet.cssRules. Props to Zach
249
+ // Leatherman for calling my attention to this technique.
250
+ node.innerHTML = '@import "' + url + '";';
251
+ pollGecko(node);
252
+ }
253
+ } else {
254
+ node.onload = node.onerror = _finish;
255
+ }
256
+
257
+ nodes.push(node);
258
+ }
259
+
260
+ for (i = 0, len = nodes.length; i < len; ++i) {
261
+ head.appendChild(nodes[i]);
262
+ }
263
+ }
264
+
265
+ /**
266
+ Begins polling to determine when the specified stylesheet has finished loading
267
+ in Gecko. Polling stops when all pending stylesheets have loaded or after 10
268
+ seconds (to prevent stalls).
269
+
270
+ Thanks to Zach Leatherman for calling my attention to the @import-based
271
+ cross-domain technique used here, and to Oleg Slobodskoi for an earlier
272
+ same-domain implementation. See Zach's blog for more details:
273
+ http://www.zachleat.com/web/2010/07/29/load-css-dynamically/
274
+
275
+ @method pollGecko
276
+ @param {HTMLElement} node Style node to poll.
277
+ @private
278
+ */
279
+ function pollGecko(node) {
280
+ var hasRules;
281
+
282
+ try {
283
+ // We don't really need to store this value or ever refer to it again, but
284
+ // if we don't store it, Closure Compiler assumes the code is useless and
285
+ // removes it.
286
+ hasRules = !!node.sheet.cssRules;
287
+ } catch (ex) {
288
+ // An exception means the stylesheet is still loading.
289
+ pollCount += 1;
290
+
291
+ if (pollCount < 200) {
292
+ setTimeout(function () { pollGecko(node); }, 50);
293
+ } else {
294
+ // We've been polling for 10 seconds and nothing's happened. Stop
295
+ // polling and finish the pending requests to avoid blocking further
296
+ // requests.
297
+ hasRules && finish('css');
298
+ }
299
+
300
+ return;
301
+ }
302
+
303
+ // If we get here, the stylesheet has loaded.
304
+ finish('css');
305
+ }
306
+
307
+ /**
308
+ Begins polling to determine when pending stylesheets have finished loading
309
+ in WebKit. Polling stops when all pending stylesheets have loaded or after 10
310
+ seconds (to prevent stalls).
311
+
312
+ @method pollWebKit
313
+ @private
314
+ */
315
+ function pollWebKit() {
316
+ var css = pending.css, i;
317
+
318
+ if (css) {
319
+ i = styleSheets.length;
320
+
321
+ // Look for a stylesheet matching the pending URL.
322
+ while (--i >= 0) {
323
+ if (styleSheets[i].href === css.urls[0]) {
324
+ finish('css');
325
+ break;
326
+ }
327
+ }
328
+
329
+ pollCount += 1;
330
+
331
+ if (css) {
332
+ if (pollCount < 200) {
333
+ setTimeout(pollWebKit, 50);
334
+ } else {
335
+ // We've been polling for 10 seconds and nothing's happened, which may
336
+ // indicate that the stylesheet has been removed from the document
337
+ // before it had a chance to load. Stop polling and finish the pending
338
+ // request to prevent blocking further requests.
339
+ finish('css');
340
+ }
341
+ }
342
+ }
343
+ }
344
+
345
+ return {
346
+
347
+ /**
348
+ Requests the specified CSS URL or URLs and executes the specified
349
+ callback (if any) when they have finished loading. If an array of URLs is
350
+ specified, the stylesheets will be loaded in parallel and the callback
351
+ will be executed after all stylesheets have finished loading.
352
+
353
+ @method css
354
+ @param {String|Array} urls CSS URL or array of CSS URLs to load
355
+ @param {Function} callback (optional) callback function to execute when
356
+ the specified stylesheets are loaded
357
+ @param {Object} obj (optional) object to pass to the callback function
358
+ @param {Object} context (optional) if provided, the callback function
359
+ will be executed in this object's context
360
+ @static
361
+ */
362
+ css: function (urls, callback, obj, context) {
363
+ load('css', urls, callback, obj, context);
364
+ },
365
+
366
+ /**
367
+ Requests the specified JavaScript URL or URLs and executes the specified
368
+ callback (if any) when they have finished loading. If an array of URLs is
369
+ specified and the browser supports it, the scripts will be loaded in
370
+ parallel and the callback will be executed after all scripts have
371
+ finished loading.
372
+
373
+ Currently, only Firefox and Opera support parallel loading of scripts while
374
+ preserving execution order. In other browsers, scripts will be
375
+ queued and loaded one at a time to ensure correct execution order.
376
+
377
+ @method js
378
+ @param {String|Array} urls JS URL or array of JS URLs to load
379
+ @param {Function} callback (optional) callback function to execute when
380
+ the specified scripts are loaded
381
+ @param {Object} obj (optional) object to pass to the callback function
382
+ @param {Object} context (optional) if provided, the callback function
383
+ will be executed in this object's context
384
+ @static
385
+ */
386
+ js: function (urls, callback, obj, context) {
387
+ load('js', urls, callback, obj, context);
388
+ }
389
+
390
+ };
391
+ })(this.document);