~andreserl/maas/packaging_precise_rebase

« back to all changes in this revision

Viewing changes to debian/extras/jslibs/yui/get/get-debug.js

  • Committer: Andres Rodriguez
  • Date: 2013-03-20 18:12:30 UTC
  • mfrom: (145.2.22 precise.sru)
  • Revision ID: andreserl@ubuntu.com-20130320181230-6l5guc0nhlv2z4p7
Re-base againts latest quantal released branch towards SRU

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.5.1 (build 22)
 
3
Copyright 2012 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
YUI.add('get', function(Y) {
 
8
 
 
9
/*jslint boss:true, expr:true, laxbreak: true */
 
10
 
 
11
/**
 
12
Provides dynamic loading of remote JavaScript and CSS resources.
 
13
 
 
14
@module get
 
15
@class Get
 
16
@static
 
17
**/
 
18
 
 
19
var Lang = Y.Lang,
 
20
 
 
21
    CUSTOM_ATTRS, // defined lazily in Y.Get.Transaction._createNode()
 
22
 
 
23
    Get, Transaction;
 
24
 
 
25
Y.Get = Get = {
 
26
    // -- Public Properties ----------------------------------------------------
 
27
 
 
28
    /**
 
29
    Default options for CSS requests. Options specified here will override
 
30
    global defaults for CSS requests.
 
31
 
 
32
    See the `options` property for all available options.
 
33
 
 
34
    @property cssOptions
 
35
    @type Object
 
36
    @static
 
37
    @since 3.5.0
 
38
    **/
 
39
    cssOptions: {
 
40
        attributes: {
 
41
            rel: 'stylesheet'
 
42
        },
 
43
 
 
44
        doc         : Y.config.linkDoc || Y.config.doc,
 
45
        pollInterval: 50
 
46
    },
 
47
 
 
48
    /**
 
49
    Default options for JS requests. Options specified here will override global
 
50
    defaults for JS requests.
 
51
 
 
52
    See the `options` property for all available options.
 
53
 
 
54
    @property jsOptions
 
55
    @type Object
 
56
    @static
 
57
    @since 3.5.0
 
58
    **/
 
59
    jsOptions: {
 
60
        autopurge: true,
 
61
        doc      : Y.config.scriptDoc || Y.config.doc
 
62
    },
 
63
 
 
64
    /**
 
65
    Default options to use for all requests.
 
66
 
 
67
    Note that while all available options are documented here for ease of
 
68
    discovery, some options (like callback functions) only make sense at the
 
69
    transaction level.
 
70
 
 
71
    Callback functions specified via the options object or the `options`
 
72
    parameter of the `css()`, `js()`, or `load()` methods will receive the
 
73
    transaction object as a parameter. See `Y.Get.Transaction` for details on
 
74
    the properties and methods available on transactions.
 
75
 
 
76
    @static
 
77
    @since 3.5.0
 
78
    @property {Object} options
 
79
 
 
80
    @property {Boolean} [options.async=false] Whether or not to load scripts
 
81
        asynchronously, meaning they're requested in parallel and execution
 
82
        order is not guaranteed. Has no effect on CSS, since CSS is always
 
83
        loaded asynchronously.
 
84
 
 
85
    @property {Object} [options.attributes] HTML attribute name/value pairs that
 
86
        should be added to inserted nodes. By default, the `charset` attribute
 
87
        will be set to "utf-8" and nodes will be given an auto-generated `id`
 
88
        attribute, but you can override these with your own values if desired.
 
89
 
 
90
    @property {Boolean} [options.autopurge] Whether or not to automatically
 
91
        purge inserted nodes after the purge threshold is reached. This is
 
92
        `true` by default for JavaScript, but `false` for CSS since purging a
 
93
        CSS node will also remove any styling applied by the referenced file.
 
94
 
 
95
    @property {Object} [options.context] `this` object to use when calling
 
96
        callback functions. Defaults to the transaction object.
 
97
 
 
98
    @property {Mixed} [options.data] Arbitrary data object to pass to "on*"
 
99
        callbacks.
 
100
 
 
101
    @property {Document} [options.doc] Document into which nodes should be
 
102
        inserted. By default, the current document is used.
 
103
 
 
104
    @property {HTMLElement|String} [options.insertBefore] HTML element or id
 
105
        string of an element before which all generated nodes should be
 
106
        inserted. If not specified, Get will automatically determine the best
 
107
        place to insert nodes for maximum compatibility.
 
108
 
 
109
    @property {Function} [options.onEnd] Callback to execute after a transaction
 
110
        is complete, regardless of whether it succeeded or failed.
 
111
 
 
112
    @property {Function} [options.onFailure] Callback to execute after a
 
113
        transaction fails, times out, or is aborted.
 
114
 
 
115
    @property {Function} [options.onProgress] Callback to execute after each
 
116
        individual request in a transaction either succeeds or fails.
 
117
 
 
118
    @property {Function} [options.onSuccess] Callback to execute after a
 
119
        transaction completes successfully with no errors. Note that in browsers
 
120
        that don't support the `error` event on CSS `<link>` nodes, a failed CSS
 
121
        request may still be reported as a success because in these browsers
 
122
        it can be difficult or impossible to distinguish between success and
 
123
        failure for CSS resources.
 
124
 
 
125
    @property {Function} [options.onTimeout] Callback to execute after a
 
126
        transaction times out.
 
127
 
 
128
    @property {Number} [options.pollInterval=50] Polling interval (in
 
129
        milliseconds) for detecting CSS load completion in browsers that don't
 
130
        support the `load` event on `<link>` nodes. This isn't used for
 
131
        JavaScript.
 
132
 
 
133
    @property {Number} [options.purgethreshold=20] Number of nodes to insert
 
134
        before triggering an automatic purge when `autopurge` is `true`.
 
135
 
 
136
    @property {Number} [options.timeout] Number of milliseconds to wait before
 
137
        aborting a transaction. When a timeout occurs, the `onTimeout` callback
 
138
        is called, followed by `onFailure` and finally `onEnd`. By default,
 
139
        there is no timeout.
 
140
 
 
141
    @property {String} [options.type] Resource type ("css" or "js"). This option
 
142
        is set automatically by the `css()` and `js()` functions and will be
 
143
        ignored there, but may be useful when using the `load()` function. If
 
144
        not specified, the type will be inferred from the URL, defaulting to
 
145
        "js" if the URL doesn't contain a recognizable file extension.
 
146
    **/
 
147
    options: {
 
148
        attributes: {
 
149
            charset: 'utf-8'
 
150
        },
 
151
 
 
152
        purgethreshold: 20
 
153
    },
 
154
 
 
155
    // -- Protected Properties -------------------------------------------------
 
156
 
 
157
    /**
 
158
    Regex that matches a CSS URL. Used to guess the file type when it's not
 
159
    specified.
 
160
 
 
161
    @property REGEX_CSS
 
162
    @type RegExp
 
163
    @final
 
164
    @protected
 
165
    @static
 
166
    @since 3.5.0
 
167
    **/
 
168
    REGEX_CSS: /\.css(?:[?;].*)?$/i,
 
169
 
 
170
    /**
 
171
    Regex that matches a JS URL. Used to guess the file type when it's not
 
172
    specified.
 
173
 
 
174
    @property REGEX_JS
 
175
    @type RegExp
 
176
    @final
 
177
    @protected
 
178
    @static
 
179
    @since 3.5.0
 
180
    **/
 
181
    REGEX_JS : /\.js(?:[?;].*)?$/i,
 
182
 
 
183
    /**
 
184
    Contains information about the current environment, such as what script and
 
185
    link injection features it supports.
 
186
 
 
187
    This object is created and populated the first time the `_getEnv()` method
 
188
    is called.
 
189
 
 
190
    @property _env
 
191
    @type Object
 
192
    @protected
 
193
    @static
 
194
    @since 3.5.0
 
195
    **/
 
196
 
 
197
    /**
 
198
    Mapping of document _yuid strings to <head> or <base> node references so we
 
199
    don't have to look the node up each time we want to insert a request node.
 
200
 
 
201
    @property _insertCache
 
202
    @type Object
 
203
    @protected
 
204
    @static
 
205
    @since 3.5.0
 
206
    **/
 
207
    _insertCache: {},
 
208
 
 
209
    /**
 
210
    Information about the currently pending transaction, if any.
 
211
 
 
212
    This is actually an object with two properties: `callback`, containing the
 
213
    optional callback passed to `css()`, `load()`, or `js()`; and `transaction`,
 
214
    containing the actual transaction instance.
 
215
 
 
216
    @property _pending
 
217
    @type Object
 
218
    @protected
 
219
    @static
 
220
    @since 3.5.0
 
221
    **/
 
222
    _pending: null,
 
223
 
 
224
    /**
 
225
    HTML nodes eligible to be purged next time autopurge is triggered.
 
226
 
 
227
    @property _purgeNodes
 
228
    @type HTMLElement[]
 
229
    @protected
 
230
    @static
 
231
    @since 3.5.0
 
232
    **/
 
233
    _purgeNodes: [],
 
234
 
 
235
    /**
 
236
    Queued transactions and associated callbacks.
 
237
 
 
238
    @property _queue
 
239
    @type Object[]
 
240
    @protected
 
241
    @static
 
242
    @since 3.5.0
 
243
    **/
 
244
    _queue: [],
 
245
 
 
246
    // -- Public Methods -------------------------------------------------------
 
247
 
 
248
    /**
 
249
    Aborts the specified transaction.
 
250
 
 
251
    This will cause the transaction's `onFailure` callback to be called and
 
252
    will prevent any new script and link nodes from being added to the document,
 
253
    but any resources that have already been requested will continue loading
 
254
    (there's no safe way to prevent this, unfortunately).
 
255
 
 
256
    *Note:* This method is deprecated as of 3.5.0, and will be removed in a
 
257
    future version of YUI. Use the transaction-level `abort()` method instead.
 
258
 
 
259
    @method abort
 
260
    @param {Get.Transaction} transaction Transaction to abort.
 
261
    @deprecated Use the `abort()` method on the transaction instead.
 
262
    @static
 
263
    **/
 
264
    abort: function (transaction) {
 
265
        var i, id, item, len, pending;
 
266
 
 
267
        Y.log('`Y.Get.abort()` is deprecated as of 3.5.0. Use the `abort()` method on the transaction instead.', 'warn', 'get');
 
268
 
 
269
        if (!transaction.abort) {
 
270
            id          = transaction;
 
271
            pending     = this._pending;
 
272
            transaction = null;
 
273
 
 
274
            if (pending && pending.transaction.id === id) {
 
275
                transaction   = pending.transaction;
 
276
                this._pending = null;
 
277
            } else {
 
278
                for (i = 0, len = this._queue.length; i < len; ++i) {
 
279
                    item = this._queue[i].transaction;
 
280
 
 
281
                    if (item.id === id) {
 
282
                        transaction = item;
 
283
                        this._queue.splice(i, 1);
 
284
                        break;
 
285
                    }
 
286
                }
 
287
            }
 
288
        }
 
289
 
 
290
        transaction && transaction.abort();
 
291
    },
 
292
 
 
293
    /**
 
294
    Loads one or more CSS files.
 
295
 
 
296
    The _urls_ parameter may be provided as a URL string, a request object,
 
297
    or an array of URL strings and/or request objects.
 
298
 
 
299
    A request object is just an object that contains a `url` property and zero
 
300
    or more options that should apply specifically to that request.
 
301
    Request-specific options take priority over transaction-level options and
 
302
    default options.
 
303
 
 
304
    URLs may be relative or absolute, and do not have to have the same origin
 
305
    as the current page.
 
306
 
 
307
    The `options` parameter may be omitted completely and a callback passed in
 
308
    its place, if desired.
 
309
 
 
310
    @example
 
311
 
 
312
        // Load a single CSS file and log a message on completion.
 
313
        Y.Get.css('foo.css', function (err) {
 
314
            if (err) {
 
315
                Y.log('foo.css failed to load!');
 
316
            } else {
 
317
                Y.log('foo.css was loaded successfully');
 
318
            }
 
319
        });
 
320
 
 
321
        // Load multiple CSS files and log a message when all have finished
 
322
        // loading.
 
323
        var urls = ['foo.css', 'http://example.com/bar.css', 'baz/quux.css'];
 
324
 
 
325
        Y.Get.css(urls, function (err) {
 
326
            if (err) {
 
327
                Y.log('one or more files failed to load!');
 
328
            } else {
 
329
                Y.log('all files loaded successfully');
 
330
            }
 
331
        });
 
332
 
 
333
        // Specify transaction-level options, which will apply to all requests
 
334
        // within the transaction.
 
335
        Y.Get.css(urls, {
 
336
            attributes: {'class': 'my-css'},
 
337
            timeout   : 5000
 
338
        });
 
339
 
 
340
        // Specify per-request options, which override transaction-level and
 
341
        // default options.
 
342
        Y.Get.css([
 
343
            {url: 'foo.css', attributes: {id: 'foo'}},
 
344
            {url: 'bar.css', attributes: {id: 'bar', charset: 'iso-8859-1'}}
 
345
        ]);
 
346
 
 
347
    @method css
 
348
    @param {String|Object|Array} urls URL string, request object, or array
 
349
        of URLs and/or request objects to load.
 
350
    @param {Object} [options] Options for this transaction. See the
 
351
        `Y.Get.options` property for a complete list of available options.
 
352
    @param {Function} [callback] Callback function to be called on completion.
 
353
        This is a general callback and will be called before any more granular
 
354
        callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
 
355
        object.
 
356
 
 
357
        @param {Array|null} callback.err Array of errors that occurred during
 
358
            the transaction, or `null` on success.
 
359
        @param {Get.Transaction} callback.transaction Transaction object.
 
360
 
 
361
    @return {Get.Transaction} Transaction object.
 
362
    @static
 
363
    **/
 
364
    css: function (urls, options, callback) {
 
365
        return this._load('css', urls, options, callback);
 
366
    },
 
367
 
 
368
    /**
 
369
    Loads one or more JavaScript resources.
 
370
 
 
371
    The _urls_ parameter may be provided as a URL string, a request object,
 
372
    or an array of URL strings and/or request objects.
 
373
 
 
374
    A request object is just an object that contains a `url` property and zero
 
375
    or more options that should apply specifically to that request.
 
376
    Request-specific options take priority over transaction-level options and
 
377
    default options.
 
378
 
 
379
    URLs may be relative or absolute, and do not have to have the same origin
 
380
    as the current page.
 
381
 
 
382
    The `options` parameter may be omitted completely and a callback passed in
 
383
    its place, if desired.
 
384
 
 
385
    Scripts will be executed in the order they're specified unless the `async`
 
386
    option is `true`, in which case they'll be loaded in parallel and executed
 
387
    in whatever order they finish loading.
 
388
 
 
389
    @example
 
390
 
 
391
        // Load a single JS file and log a message on completion.
 
392
        Y.Get.js('foo.js', function (err) {
 
393
            if (err) {
 
394
                Y.log('foo.js failed to load!');
 
395
            } else {
 
396
                Y.log('foo.js was loaded successfully');
 
397
            }
 
398
        });
 
399
 
 
400
        // Load multiple JS files, execute them in order, and log a message when
 
401
        // all have finished loading.
 
402
        var urls = ['foo.js', 'http://example.com/bar.js', 'baz/quux.js'];
 
403
 
 
404
        Y.Get.js(urls, function (err) {
 
405
            if (err) {
 
406
                Y.log('one or more files failed to load!');
 
407
            } else {
 
408
                Y.log('all files loaded successfully');
 
409
            }
 
410
        });
 
411
 
 
412
        // Specify transaction-level options, which will apply to all requests
 
413
        // within the transaction.
 
414
        Y.Get.js(urls, {
 
415
            attributes: {'class': 'my-js'},
 
416
            timeout   : 5000
 
417
        });
 
418
 
 
419
        // Specify per-request options, which override transaction-level and
 
420
        // default options.
 
421
        Y.Get.js([
 
422
            {url: 'foo.js', attributes: {id: 'foo'}},
 
423
            {url: 'bar.js', attributes: {id: 'bar', charset: 'iso-8859-1'}}
 
424
        ]);
 
425
 
 
426
    @method js
 
427
    @param {String|Object|Array} urls URL string, request object, or array
 
428
        of URLs and/or request objects to load.
 
429
    @param {Object} [options] Options for this transaction. See the
 
430
        `Y.Get.options` property for a complete list of available options.
 
431
    @param {Function} [callback] Callback function to be called on completion.
 
432
        This is a general callback and will be called before any more granular
 
433
        callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
 
434
        object.
 
435
 
 
436
        @param {Array|null} callback.err Array of errors that occurred during
 
437
            the transaction, or `null` on success.
 
438
        @param {Get.Transaction} callback.transaction Transaction object.
 
439
 
 
440
    @return {Get.Transaction} Transaction object.
 
441
    @since 3.5.0
 
442
    @static
 
443
    **/
 
444
    js: function (urls, options, callback) {
 
445
        return this._load('js', urls, options, callback);
 
446
    },
 
447
 
 
448
    /**
 
449
    Loads one or more CSS and/or JavaScript resources in the same transaction.
 
450
 
 
451
    Use this method when you want to load both CSS and JavaScript in a single
 
452
    transaction and be notified when all requested URLs have finished loading,
 
453
    regardless of type.
 
454
 
 
455
    Behavior and options are the same as for the `css()` and `js()` methods. If
 
456
    a resource type isn't specified in per-request options or transaction-level
 
457
    options, Get will guess the file type based on the URL's extension (`.css`
 
458
    or `.js`, with or without a following query string). If the file type can't
 
459
    be guessed from the URL, a warning will be logged and Get will assume the
 
460
    URL is a JavaScript resource.
 
461
 
 
462
    @example
 
463
 
 
464
        // Load both CSS and JS files in a single transaction, and log a message
 
465
        // when all files have finished loading.
 
466
        Y.Get.load(['foo.css', 'bar.js', 'baz.css'], function (err) {
 
467
            if (err) {
 
468
                Y.log('one or more files failed to load!');
 
469
            } else {
 
470
                Y.log('all files loaded successfully');
 
471
            }
 
472
        });
 
473
 
 
474
    @method load
 
475
    @param {String|Object|Array} urls URL string, request object, or array
 
476
        of URLs and/or request objects to load.
 
477
    @param {Object} [options] Options for this transaction. See the
 
478
        `Y.Get.options` property for a complete list of available options.
 
479
    @param {Function} [callback] Callback function to be called on completion.
 
480
        This is a general callback and will be called before any more granular
 
481
        callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
 
482
        object.
 
483
 
 
484
        @param {Array|null} err Array of errors that occurred during the
 
485
            transaction, or `null` on success.
 
486
        @param {Get.Transaction} Transaction object.
 
487
 
 
488
    @return {Get.Transaction} Transaction object.
 
489
    @since 3.5.0
 
490
    @static
 
491
    **/
 
492
    load: function (urls, options, callback) {
 
493
        return this._load(null, urls, options, callback);
 
494
    },
 
495
 
 
496
    // -- Protected Methods ----------------------------------------------------
 
497
 
 
498
    /**
 
499
    Triggers an automatic purge if the purge threshold has been reached.
 
500
 
 
501
    @method _autoPurge
 
502
    @param {Number} threshold Purge threshold to use, in milliseconds.
 
503
    @protected
 
504
    @since 3.5.0
 
505
    @static
 
506
    **/
 
507
    _autoPurge: function (threshold) {
 
508
        if (threshold && this._purgeNodes.length >= threshold) {
 
509
            Y.log('autopurge triggered after ' + this._purgeNodes.length + ' nodes', 'info', 'get');
 
510
            this._purge(this._purgeNodes);
 
511
        }
 
512
    },
 
513
 
 
514
    /**
 
515
    Populates the `_env` property with information about the current
 
516
    environment.
 
517
 
 
518
    @method _getEnv
 
519
    @return {Object} Environment information.
 
520
    @protected
 
521
    @since 3.5.0
 
522
    @static
 
523
    **/
 
524
    _getEnv: function () {
 
525
        var doc = Y.config.doc,
 
526
            ua  = Y.UA;
 
527
 
 
528
        // Note: some of these checks require browser sniffs since it's not
 
529
        // feasible to load test files on every pageview just to perform a
 
530
        // feature test. I'm sorry if this makes you sad.
 
531
        return (this._env = {
 
532
            // True if this is a browser that supports disabling async mode on
 
533
            // dynamically created script nodes. See
 
534
            // https://developer.mozilla.org/En/HTML/Element/Script#Attributes
 
535
            async: doc && doc.createElement('script').async === true,
 
536
 
 
537
            // True if this browser fires an event when a dynamically injected
 
538
            // link node fails to load. This is currently true for Firefox 9+
 
539
            // and WebKit 535.24+.
 
540
            cssFail: ua.gecko >= 9 || ua.compareVersions(ua.webkit, 535.24) >= 0,
 
541
 
 
542
            // True if this browser fires an event when a dynamically injected
 
543
            // link node finishes loading. This is currently true for IE, Opera,
 
544
            // Firefox 9+, and WebKit 535.24+. Note that IE versions <9 fire the
 
545
            // DOM 0 "onload" event, but not "load". All versions of IE fire
 
546
            // "onload".
 
547
            // davglass: Seems that Chrome on Android needs this to be false.
 
548
            cssLoad: (
 
549
                    (!ua.gecko && !ua.webkit) || ua.gecko >= 9 ||
 
550
                    ua.compareVersions(ua.webkit, 535.24) >= 0
 
551
                ) && !(ua.chrome && ua.chrome <= 18),
 
552
 
 
553
            // True if this browser preserves script execution order while
 
554
            // loading scripts in parallel as long as the script node's `async`
 
555
            // attribute is set to false to explicitly disable async execution.
 
556
            preservesScriptOrder: !!(ua.gecko || ua.opera)
 
557
        });
 
558
    },
 
559
 
 
560
    _getTransaction: function (urls, options) {
 
561
        var requests = [],
 
562
            i, len, req, url;
 
563
 
 
564
        if (!Lang.isArray(urls)) {
 
565
            urls = [urls];
 
566
        }
 
567
 
 
568
        options = Y.merge(this.options, options);
 
569
 
 
570
        // Clone the attributes object so we don't end up modifying it by ref.
 
571
        options.attributes = Y.merge(this.options.attributes,
 
572
                options.attributes);
 
573
 
 
574
        for (i = 0, len = urls.length; i < len; ++i) {
 
575
            url = urls[i];
 
576
            req = {attributes: {}};
 
577
 
 
578
            // If `url` is a string, we create a URL object for it, then mix in
 
579
            // global options and request-specific options. If it's an object
 
580
            // with a "url" property, we assume it's a request object containing
 
581
            // URL-specific options.
 
582
            if (typeof url === 'string') {
 
583
                req.url = url;
 
584
            } else if (url.url) {
 
585
                // URL-specific options override both global defaults and
 
586
                // request-specific options.
 
587
                Y.mix(req, url, false, null, 0, true);
 
588
                url = url.url; // Make url a string so we can use it later.
 
589
            } else {
 
590
                Y.log('URL must be a string or an object with a `url` property.', 'error', 'get');
 
591
                continue;
 
592
            }
 
593
 
 
594
            Y.mix(req, options, false, null, 0, true);
 
595
 
 
596
            // If we didn't get an explicit type for this URL either in the
 
597
            // request options or the URL-specific options, try to determine
 
598
            // one from the file extension.
 
599
            if (!req.type) {
 
600
                if (this.REGEX_CSS.test(url)) {
 
601
                    req.type = 'css';
 
602
                } else {
 
603
                    if (!this.REGEX_JS.test(url)) {
 
604
                        Y.log("Can't guess file type from URL. Assuming JS: " + url, 'warn', 'get');
 
605
                    }
 
606
 
 
607
                    req.type = 'js';
 
608
                }
 
609
            }
 
610
 
 
611
            // Mix in type-specific default options, but don't overwrite any
 
612
            // options that have already been set.
 
613
            Y.mix(req, req.type === 'js' ? this.jsOptions : this.cssOptions,
 
614
                false, null, 0, true);
 
615
 
 
616
            // Give the node an id attribute if it doesn't already have one.
 
617
            req.attributes.id || (req.attributes.id = Y.guid());
 
618
 
 
619
            // Backcompat for <3.5.0 behavior.
 
620
            if (req.win) {
 
621
                Y.log('The `win` option is deprecated as of 3.5.0. Use `doc` instead.', 'warn', 'get');
 
622
                req.doc = req.win.document;
 
623
            } else {
 
624
                req.win = req.doc.defaultView || req.doc.parentWindow;
 
625
            }
 
626
 
 
627
            if (req.charset) {
 
628
                Y.log('The `charset` option is deprecated as of 3.5.0. Set `attributes.charset` instead.', 'warn', 'get');
 
629
                req.attributes.charset = req.charset;
 
630
            }
 
631
 
 
632
            requests.push(req);
 
633
        }
 
634
 
 
635
        return new Transaction(requests, options);
 
636
    },
 
637
 
 
638
    _load: function (type, urls, options, callback) {
 
639
        var transaction;
 
640
 
 
641
        // Allow callback as third param.
 
642
        if (typeof options === 'function') {
 
643
            callback = options;
 
644
            options  = {};
 
645
        }
 
646
 
 
647
        options || (options = {});
 
648
        options.type = type;
 
649
 
 
650
        if (!this._env) {
 
651
            this._getEnv();
 
652
        }
 
653
 
 
654
        transaction = this._getTransaction(urls, options);
 
655
 
 
656
        this._queue.push({
 
657
            callback   : callback,
 
658
            transaction: transaction
 
659
        });
 
660
 
 
661
        this._next();
 
662
 
 
663
        return transaction;
 
664
    },
 
665
 
 
666
    _next: function () {
 
667
        var item;
 
668
 
 
669
        if (this._pending) {
 
670
            return;
 
671
        }
 
672
 
 
673
        item = this._queue.shift();
 
674
 
 
675
        if (item) {
 
676
            this._pending = item;
 
677
 
 
678
            item.transaction.execute(function () {
 
679
                item.callback && item.callback.apply(this, arguments);
 
680
 
 
681
                Get._pending = null;
 
682
                Get._next();
 
683
            });
 
684
        }
 
685
    },
 
686
 
 
687
    _purge: function (nodes) {
 
688
        var purgeNodes    = this._purgeNodes,
 
689
            isTransaction = nodes !== purgeNodes,
 
690
            index, node;
 
691
 
 
692
        while (node = nodes.pop()) { // assignment
 
693
            // Don't purge nodes that haven't finished loading (or errored out),
 
694
            // since this can hang the transaction.
 
695
            if (!node._yuiget_finished) {
 
696
                continue;
 
697
            }
 
698
 
 
699
            node.parentNode && node.parentNode.removeChild(node);
 
700
 
 
701
            // If this is a transaction-level purge and this node also exists in
 
702
            // the Get-level _purgeNodes array, we need to remove it from
 
703
            // _purgeNodes to avoid creating a memory leak. The indexOf lookup
 
704
            // sucks, but until we get WeakMaps, this is the least troublesome
 
705
            // way to do this (we can't just hold onto node ids because they may
 
706
            // not be in the same document).
 
707
            if (isTransaction) {
 
708
                index = Y.Array.indexOf(purgeNodes, node);
 
709
 
 
710
                if (index > -1) {
 
711
                    purgeNodes.splice(index, 1);
 
712
                }
 
713
            }
 
714
        }
 
715
    }
 
716
};
 
717
 
 
718
/**
 
719
Alias for `js()`.
 
720
 
 
721
@method script
 
722
@static
 
723
**/
 
724
Get.script = Get.js;
 
725
 
 
726
/**
 
727
Represents a Get transaction, which may contain requests for one or more JS or
 
728
CSS files.
 
729
 
 
730
This class should not be instantiated manually. Instances will be created and
 
731
returned as needed by Y.Get's `css()`, `js()`, and `load()` methods.
 
732
 
 
733
@class Get.Transaction
 
734
@constructor
 
735
@since 3.5.0
 
736
**/
 
737
Get.Transaction = Transaction = function (requests, options) {
 
738
    var self = this;
 
739
 
 
740
    self.id       = Transaction._lastId += 1;
 
741
    self.data     = options.data;
 
742
    self.errors   = [];
 
743
    self.nodes    = [];
 
744
    self.options  = options;
 
745
    self.requests = requests;
 
746
 
 
747
    self._callbacks = []; // callbacks to call after execution finishes
 
748
    self._queue     = [];
 
749
    self._waiting   = 0;
 
750
 
 
751
    // Deprecated pre-3.5.0 properties.
 
752
    self.tId = self.id; // Use `id` instead.
 
753
    self.win = options.win || Y.config.win;
 
754
};
 
755
 
 
756
/**
 
757
Arbitrary data object associated with this transaction.
 
758
 
 
759
This object comes from the options passed to `Get.css()`, `Get.js()`, or
 
760
`Get.load()`, and will be `undefined` if no data object was specified.
 
761
 
 
762
@property {Object} data
 
763
**/
 
764
 
 
765
/**
 
766
Array of errors that have occurred during this transaction, if any.
 
767
 
 
768
@since 3.5.0
 
769
@property {Object[]} errors
 
770
@property {String} errors.error Error message.
 
771
@property {Object} errors.request Request object related to the error.
 
772
**/
 
773
 
 
774
/**
 
775
Numeric id for this transaction, unique among all transactions within the same
 
776
YUI sandbox in the current pageview.
 
777
 
 
778
@property {Number} id
 
779
@since 3.5.0
 
780
**/
 
781
 
 
782
/**
 
783
HTMLElement nodes (native ones, not YUI Node instances) that have been inserted
 
784
during the current transaction.
 
785
 
 
786
@property {HTMLElement[]} nodes
 
787
**/
 
788
 
 
789
/**
 
790
Options associated with this transaction.
 
791
 
 
792
See `Get.options` for the full list of available options.
 
793
 
 
794
@property {Object} options
 
795
@since 3.5.0
 
796
**/
 
797
 
 
798
/**
 
799
Request objects contained in this transaction. Each request object represents
 
800
one CSS or JS URL that will be (or has been) requested and loaded into the page.
 
801
 
 
802
@property {Object} requests
 
803
@since 3.5.0
 
804
**/
 
805
 
 
806
/**
 
807
Id of the most recent transaction.
 
808
 
 
809
@property _lastId
 
810
@type Number
 
811
@protected
 
812
@static
 
813
**/
 
814
Transaction._lastId = 0;
 
815
 
 
816
Transaction.prototype = {
 
817
    // -- Public Properties ----------------------------------------------------
 
818
 
 
819
    /**
 
820
    Current state of this transaction. One of "new", "executing", or "done".
 
821
 
 
822
    @property _state
 
823
    @type String
 
824
    @protected
 
825
    **/
 
826
    _state: 'new', // "new", "executing", or "done"
 
827
 
 
828
    // -- Public Methods -------------------------------------------------------
 
829
 
 
830
    /**
 
831
    Aborts this transaction.
 
832
 
 
833
    This will cause the transaction's `onFailure` callback to be called and
 
834
    will prevent any new script and link nodes from being added to the document,
 
835
    but any resources that have already been requested will continue loading
 
836
    (there's no safe way to prevent this, unfortunately).
 
837
 
 
838
    @method abort
 
839
    @param {String} [msg="Aborted."] Optional message to use in the `errors`
 
840
        array describing why the transaction was aborted.
 
841
    **/
 
842
    abort: function (msg) {
 
843
        this._pending    = null;
 
844
        this._pendingCSS = null;
 
845
        this._pollTimer  = clearTimeout(this._pollTimer);
 
846
        this._queue      = [];
 
847
        this._waiting    = 0;
 
848
 
 
849
        this.errors.push({error: msg || 'Aborted'});
 
850
        this._finish();
 
851
    },
 
852
 
 
853
    /**
 
854
    Begins execting the transaction.
 
855
 
 
856
    There's usually no reason to call this manually, since Get will call it
 
857
    automatically when other pending transactions have finished. If you really
 
858
    want to execute your transaction before Get does, you can, but be aware that
 
859
    this transaction's scripts may end up executing before the scripts in other
 
860
    pending transactions.
 
861
 
 
862
    If the transaction is already executing, the specified callback (if any)
 
863
    will be queued and called after execution finishes. If the transaction has
 
864
    already finished, the callback will be called immediately (the transaction
 
865
    will not be executed again).
 
866
 
 
867
    @method execute
 
868
    @param {Function} callback Callback function to execute after all requests
 
869
        in the transaction are complete, or after the transaction is aborted.
 
870
    **/
 
871
    execute: function (callback) {
 
872
        var self     = this,
 
873
            requests = self.requests,
 
874
            state    = self._state,
 
875
            i, len, queue, req;
 
876
 
 
877
        if (state === 'done') {
 
878
            callback && callback(self.errors.length ? self.errors : null, self);
 
879
            return;
 
880
        } else {
 
881
            callback && self._callbacks.push(callback);
 
882
 
 
883
            if (state === 'executing') {
 
884
                return;
 
885
            }
 
886
        }
 
887
 
 
888
        self._state = 'executing';
 
889
        self._queue = queue = [];
 
890
 
 
891
        if (self.options.timeout) {
 
892
            self._timeout = setTimeout(function () {
 
893
                self.abort('Timeout');
 
894
            }, self.options.timeout);
 
895
        }
 
896
 
 
897
        for (i = 0, len = requests.length; i < len; ++i) {
 
898
            req = self.requests[i];
 
899
 
 
900
            if (req.async || req.type === 'css') {
 
901
                // No need to queue CSS or fully async JS.
 
902
                self._insert(req);
 
903
            } else {
 
904
                queue.push(req);
 
905
            }
 
906
        }
 
907
 
 
908
        self._next();
 
909
    },
 
910
 
 
911
    /**
 
912
    Manually purges any `<script>` or `<link>` nodes this transaction has
 
913
    created.
 
914
 
 
915
    Be careful when purging a transaction that contains CSS requests, since
 
916
    removing `<link>` nodes will also remove any styles they applied.
 
917
 
 
918
    @method purge
 
919
    **/
 
920
    purge: function () {
 
921
        Get._purge(this.nodes);
 
922
    },
 
923
 
 
924
    // -- Protected Methods ----------------------------------------------------
 
925
    _createNode: function (name, attrs, doc) {
 
926
        var node = doc.createElement(name),
 
927
            attr, testEl;
 
928
 
 
929
        if (!CUSTOM_ATTRS) {
 
930
            // IE6 and IE7 expect property names rather than attribute names for
 
931
            // certain attributes. Rather than sniffing, we do a quick feature
 
932
            // test the first time _createNode() runs to determine whether we
 
933
            // need to provide a workaround.
 
934
            testEl = doc.createElement('div');
 
935
            testEl.setAttribute('class', 'a');
 
936
 
 
937
            CUSTOM_ATTRS = testEl.className === 'a' ? {} : {
 
938
                'for'  : 'htmlFor',
 
939
                'class': 'className'
 
940
            };
 
941
        }
 
942
 
 
943
        for (attr in attrs) {
 
944
            if (attrs.hasOwnProperty(attr)) {
 
945
                node.setAttribute(CUSTOM_ATTRS[attr] || attr, attrs[attr]);
 
946
            }
 
947
        }
 
948
 
 
949
        return node;
 
950
    },
 
951
 
 
952
    _finish: function () {
 
953
        var errors  = this.errors.length ? this.errors : null,
 
954
            options = this.options,
 
955
            thisObj = options.context || this,
 
956
            data, i, len;
 
957
 
 
958
        if (this._state === 'done') {
 
959
            return;
 
960
        }
 
961
 
 
962
        this._state = 'done';
 
963
 
 
964
        for (i = 0, len = this._callbacks.length; i < len; ++i) {
 
965
            this._callbacks[i].call(thisObj, errors, this);
 
966
        }
 
967
 
 
968
        data = this._getEventData();
 
969
 
 
970
        if (errors) {
 
971
            if (options.onTimeout && errors[errors.length - 1].error === 'Timeout') {
 
972
                options.onTimeout.call(thisObj, data);
 
973
            }
 
974
 
 
975
            if (options.onFailure) {
 
976
                options.onFailure.call(thisObj, data);
 
977
            }
 
978
        } else if (options.onSuccess) {
 
979
            options.onSuccess.call(thisObj, data);
 
980
        }
 
981
 
 
982
        if (options.onEnd) {
 
983
            options.onEnd.call(thisObj, data);
 
984
        }
 
985
    },
 
986
 
 
987
    _getEventData: function (req) {
 
988
        if (req) {
 
989
            // This merge is necessary for backcompat. I hate it.
 
990
            return Y.merge(this, {
 
991
                abort  : this.abort, // have to copy these because the prototype isn't preserved
 
992
                purge  : this.purge,
 
993
                request: req,
 
994
                url    : req.url,
 
995
                win    : req.win
 
996
            });
 
997
        } else {
 
998
            return this;
 
999
        }
 
1000
    },
 
1001
 
 
1002
    _getInsertBefore: function (req) {
 
1003
        var doc = req.doc,
 
1004
            el  = req.insertBefore,
 
1005
            cache, cachedNode, docStamp;
 
1006
 
 
1007
        if (el) {
 
1008
            return typeof el === 'string' ? doc.getElementById(el) : el;
 
1009
        }
 
1010
 
 
1011
        cache    = Get._insertCache;
 
1012
        docStamp = Y.stamp(doc);
 
1013
 
 
1014
        if ((el = cache[docStamp])) { // assignment
 
1015
            return el;
 
1016
        }
 
1017
 
 
1018
        // Inserting before a <base> tag apparently works around an IE bug
 
1019
        // (according to a comment from pre-3.5.0 Y.Get), but I'm not sure what
 
1020
        // bug that is, exactly. Better safe than sorry?
 
1021
        if ((el = doc.getElementsByTagName('base')[0])) { // assignment
 
1022
            return (cache[docStamp] = el);
 
1023
        }
 
1024
 
 
1025
        // Look for a <head> element.
 
1026
        el = doc.head || doc.getElementsByTagName('head')[0];
 
1027
 
 
1028
        if (el) {
 
1029
            // Create a marker node at the end of <head> to use as an insertion
 
1030
            // point. Inserting before this node will ensure that all our CSS
 
1031
            // gets inserted in the correct order, to maintain style precedence.
 
1032
            el.appendChild(doc.createTextNode(''));
 
1033
            return (cache[docStamp] = el.lastChild);
 
1034
        }
 
1035
 
 
1036
        // If all else fails, just insert before the first script node on the
 
1037
        // page, which is virtually guaranteed to exist.
 
1038
        return (cache[docStamp] = doc.getElementsByTagName('script')[0]);
 
1039
    },
 
1040
 
 
1041
    _insert: function (req) {
 
1042
        var env          = Get._env,
 
1043
            insertBefore = this._getInsertBefore(req),
 
1044
            isScript     = req.type === 'js',
 
1045
            node         = req.node,
 
1046
            self         = this,
 
1047
            ua           = Y.UA,
 
1048
            cssTimeout, nodeType;
 
1049
 
 
1050
        if (!node) {
 
1051
            if (isScript) {
 
1052
                nodeType = 'script';
 
1053
            } else if (!env.cssLoad && ua.gecko) {
 
1054
                nodeType = 'style';
 
1055
            } else {
 
1056
                nodeType = 'link';
 
1057
            }
 
1058
 
 
1059
            node = req.node = this._createNode(nodeType, req.attributes,
 
1060
                req.doc);
 
1061
        }
 
1062
 
 
1063
        function onError() {
 
1064
            self._progress('Failed to load ' + req.url, req);
 
1065
        }
 
1066
 
 
1067
        function onLoad() {
 
1068
            if (cssTimeout) {
 
1069
                clearTimeout(cssTimeout);
 
1070
            }
 
1071
 
 
1072
            self._progress(null, req);
 
1073
        }
 
1074
 
 
1075
 
 
1076
        // Deal with script asynchronicity.
 
1077
        if (isScript) {
 
1078
            node.setAttribute('src', req.url);
 
1079
 
 
1080
            if (req.async) {
 
1081
                // Explicitly indicate that we want the browser to execute this
 
1082
                // script asynchronously. This is necessary for older browsers
 
1083
                // like Firefox <4.
 
1084
                node.async = true;
 
1085
            } else {
 
1086
                if (env.async) {
 
1087
                    // This browser treats injected scripts as async by default
 
1088
                    // (standard HTML5 behavior) but asynchronous loading isn't
 
1089
                    // desired, so tell the browser not to mark this script as
 
1090
                    // async.
 
1091
                    node.async = false;
 
1092
                }
 
1093
 
 
1094
                // If this browser doesn't preserve script execution order based
 
1095
                // on insertion order, we'll need to avoid inserting other
 
1096
                // scripts until this one finishes loading.
 
1097
                if (!env.preservesScriptOrder) {
 
1098
                    this._pending = req;
 
1099
                }
 
1100
            }
 
1101
        } else {
 
1102
            if (!env.cssLoad && ua.gecko) {
 
1103
                // In Firefox <9, we can import the requested URL into a <style>
 
1104
                // node and poll for the existence of node.sheet.cssRules. This
 
1105
                // gives us a reliable way to determine CSS load completion that
 
1106
                // also works for cross-domain stylesheets.
 
1107
                //
 
1108
                // Props to Zach Leatherman for calling my attention to this
 
1109
                // technique.
 
1110
                node.innerHTML = (req.attributes.charset ?
 
1111
                    '@charset "' + req.attributes.charset + '";' : '') +
 
1112
                    '@import "' + req.url + '";';
 
1113
            } else {
 
1114
                node.setAttribute('href', req.url);
 
1115
            }
 
1116
        }
 
1117
 
 
1118
        // Inject the node.
 
1119
        if (isScript && ua.ie && ua.ie < 9) {
 
1120
            // Script on IE6, 7, and 8.
 
1121
            node.onreadystatechange = function () {
 
1122
                if (/loaded|complete/.test(node.readyState)) {
 
1123
                    node.onreadystatechange = null;
 
1124
                    onLoad();
 
1125
                }
 
1126
            };
 
1127
        } else if (!isScript && !env.cssLoad) {
 
1128
            // CSS on Firefox <9 or WebKit.
 
1129
            this._poll(req);
 
1130
        } else {
 
1131
            // Script or CSS on everything else. Using DOM 0 events because that
 
1132
            // evens the playing field with older IEs.
 
1133
            node.onerror = onError;
 
1134
            node.onload  = onLoad;
 
1135
 
 
1136
            // If this browser doesn't fire an event when CSS fails to load,
 
1137
            // fail after a timeout to avoid blocking the transaction queue.
 
1138
            if (!env.cssFail && !isScript) {
 
1139
                cssTimeout = setTimeout(onError, req.timeout || 3000);
 
1140
            }
 
1141
        }
 
1142
 
 
1143
        this._waiting += 1;
 
1144
 
 
1145
        this.nodes.push(node);
 
1146
        insertBefore.parentNode.insertBefore(node, insertBefore);
 
1147
    },
 
1148
 
 
1149
    _next: function () {
 
1150
        if (this._pending) {
 
1151
            return;
 
1152
        }
 
1153
 
 
1154
        // If there are requests in the queue, insert the next queued request.
 
1155
        // Otherwise, if we're waiting on already-inserted requests to finish,
 
1156
        // wait longer. If there are no queued requests and we're not waiting
 
1157
        // for anything to load, then we're done!
 
1158
        if (this._queue.length) {
 
1159
            this._insert(this._queue.shift());
 
1160
        } else if (!this._waiting) {
 
1161
            this._finish();
 
1162
        }
 
1163
    },
 
1164
 
 
1165
    _poll: function (newReq) {
 
1166
        var self       = this,
 
1167
            pendingCSS = self._pendingCSS,
 
1168
            isWebKit   = Y.UA.webkit,
 
1169
            i, hasRules, j, nodeHref, req, sheets;
 
1170
 
 
1171
        if (newReq) {
 
1172
            pendingCSS || (pendingCSS = self._pendingCSS = []);
 
1173
            pendingCSS.push(newReq);
 
1174
 
 
1175
            if (self._pollTimer) {
 
1176
                // A poll timeout is already pending, so no need to create a
 
1177
                // new one.
 
1178
                return;
 
1179
            }
 
1180
        }
 
1181
 
 
1182
        self._pollTimer = null;
 
1183
 
 
1184
        // Note: in both the WebKit and Gecko hacks below, a CSS URL that 404s
 
1185
        // will still be treated as a success. There's no good workaround for
 
1186
        // this.
 
1187
 
 
1188
        for (i = 0; i < pendingCSS.length; ++i) {
 
1189
            req = pendingCSS[i];
 
1190
 
 
1191
            if (isWebKit) {
 
1192
                // Look for a stylesheet matching the pending URL.
 
1193
                sheets   = req.doc.styleSheets;
 
1194
                j        = sheets.length;
 
1195
                nodeHref = req.node.href;
 
1196
 
 
1197
                while (--j >= 0) {
 
1198
                    if (sheets[j].href === nodeHref) {
 
1199
                        pendingCSS.splice(i, 1);
 
1200
                        i -= 1;
 
1201
                        self._progress(null, req);
 
1202
                        break;
 
1203
                    }
 
1204
                }
 
1205
            } else {
 
1206
                // Many thanks to Zach Leatherman for calling my attention to
 
1207
                // the @import-based cross-domain technique used here, and to
 
1208
                // Oleg Slobodskoi for an earlier same-domain implementation.
 
1209
                //
 
1210
                // See Zach's blog for more details:
 
1211
                // http://www.zachleat.com/web/2010/07/29/load-css-dynamically/
 
1212
                try {
 
1213
                    // We don't really need to store this value since we never
 
1214
                    // use it again, but if we don't store it, Closure Compiler
 
1215
                    // assumes the code is useless and removes it.
 
1216
                    hasRules = !!req.node.sheet.cssRules;
 
1217
 
 
1218
                    // If we get here, the stylesheet has loaded.
 
1219
                    pendingCSS.splice(i, 1);
 
1220
                    i -= 1;
 
1221
                    self._progress(null, req);
 
1222
                } catch (ex) {
 
1223
                    // An exception means the stylesheet is still loading.
 
1224
                }
 
1225
            }
 
1226
        }
 
1227
 
 
1228
        if (pendingCSS.length) {
 
1229
            self._pollTimer = setTimeout(function () {
 
1230
                self._poll.call(self);
 
1231
            }, self.options.pollInterval);
 
1232
        }
 
1233
    },
 
1234
 
 
1235
    _progress: function (err, req) {
 
1236
        var options = this.options;
 
1237
 
 
1238
        if (err) {
 
1239
            req.error = err;
 
1240
 
 
1241
            this.errors.push({
 
1242
                error  : err,
 
1243
                request: req
 
1244
            });
 
1245
 
 
1246
            Y.log(err, 'error', 'get');
 
1247
        }
 
1248
 
 
1249
        req.node._yuiget_finished = req.finished = true;
 
1250
 
 
1251
        if (options.onProgress) {
 
1252
            options.onProgress.call(options.context || this,
 
1253
                this._getEventData(req));
 
1254
        }
 
1255
 
 
1256
        if (req.autopurge) {
 
1257
            // Pre-3.5.0 Get always excludes the most recent node from an
 
1258
            // autopurge. I find this odd, but I'm keeping that behavior for
 
1259
            // the sake of backcompat.
 
1260
            Get._autoPurge(this.options.purgethreshold);
 
1261
            Get._purgeNodes.push(req.node);
 
1262
        }
 
1263
 
 
1264
        if (this._pending === req) {
 
1265
            this._pending = null;
 
1266
        }
 
1267
 
 
1268
        this._waiting -= 1;
 
1269
        this._next();
 
1270
    }
 
1271
};
 
1272
 
 
1273
 
 
1274
}, '3.5.1' ,{requires:['yui-base']});