3
Copyright 2012 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('get', function(Y) {
9
/*jslint boss:true, expr:true, laxbreak: true */
12
Provides dynamic loading of remote JavaScript and CSS resources.
21
CUSTOM_ATTRS, // defined lazily in Y.Get.Transaction._createNode()
26
// -- Public Properties ----------------------------------------------------
29
Default options for CSS requests. Options specified here will override
30
global defaults for CSS requests.
32
See the `options` property for all available options.
44
doc : Y.config.linkDoc || Y.config.doc,
49
Default options for JS requests. Options specified here will override global
50
defaults for JS requests.
52
See the `options` property for all available options.
61
doc : Y.config.scriptDoc || Y.config.doc
65
Default options to use for all requests.
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
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.
78
@property {Object} options
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.
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.
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.
95
@property {Object} [options.context] `this` object to use when calling
96
callback functions. Defaults to the transaction object.
98
@property {Mixed} [options.data] Arbitrary data object to pass to "on*"
101
@property {Document} [options.doc] Document into which nodes should be
102
inserted. By default, the current document is used.
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.
109
@property {Function} [options.onEnd] Callback to execute after a transaction
110
is complete, regardless of whether it succeeded or failed.
112
@property {Function} [options.onFailure] Callback to execute after a
113
transaction fails, times out, or is aborted.
115
@property {Function} [options.onProgress] Callback to execute after each
116
individual request in a transaction either succeeds or fails.
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.
125
@property {Function} [options.onTimeout] Callback to execute after a
126
transaction times out.
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
133
@property {Number} [options.purgethreshold=20] Number of nodes to insert
134
before triggering an automatic purge when `autopurge` is `true`.
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,
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.
155
// -- Protected Properties -------------------------------------------------
158
Regex that matches a CSS URL. Used to guess the file type when it's not
168
REGEX_CSS: /\.css(?:[?;].*)?$/i,
171
Regex that matches a JS URL. Used to guess the file type when it's not
181
REGEX_JS : /\.js(?:[?;].*)?$/i,
184
Contains information about the current environment, such as what script and
185
link injection features it supports.
187
This object is created and populated the first time the `_getEnv()` method
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.
201
@property _insertCache
210
Information about the currently pending transaction, if any.
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.
225
HTML nodes eligible to be purged next time autopurge is triggered.
227
@property _purgeNodes
236
Queued transactions and associated callbacks.
246
// -- Public Methods -------------------------------------------------------
249
Aborts the specified transaction.
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).
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.
260
@param {Get.Transaction} transaction Transaction to abort.
261
@deprecated Use the `abort()` method on the transaction instead.
264
abort: function (transaction) {
265
var i, id, item, len, pending;
267
Y.log('`Y.Get.abort()` is deprecated as of 3.5.0. Use the `abort()` method on the transaction instead.', 'warn', 'get');
269
if (!transaction.abort) {
271
pending = this._pending;
274
if (pending && pending.transaction.id === id) {
275
transaction = pending.transaction;
276
this._pending = null;
278
for (i = 0, len = this._queue.length; i < len; ++i) {
279
item = this._queue[i].transaction;
281
if (item.id === id) {
283
this._queue.splice(i, 1);
290
transaction && transaction.abort();
294
Loads one or more CSS files.
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.
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
304
URLs may be relative or absolute, and do not have to have the same origin
307
The `options` parameter may be omitted completely and a callback passed in
308
its place, if desired.
312
// Load a single CSS file and log a message on completion.
313
Y.Get.css('foo.css', function (err) {
315
Y.log('foo.css failed to load!');
317
Y.log('foo.css was loaded successfully');
321
// Load multiple CSS files and log a message when all have finished
323
var urls = ['foo.css', 'http://example.com/bar.css', 'baz/quux.css'];
325
Y.Get.css(urls, function (err) {
327
Y.log('one or more files failed to load!');
329
Y.log('all files loaded successfully');
333
// Specify transaction-level options, which will apply to all requests
334
// within the transaction.
336
attributes: {'class': 'my-css'},
340
// Specify per-request options, which override transaction-level and
343
{url: 'foo.css', attributes: {id: 'foo'}},
344
{url: 'bar.css', attributes: {id: 'bar', charset: 'iso-8859-1'}}
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`
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.
361
@return {Get.Transaction} Transaction object.
364
css: function (urls, options, callback) {
365
return this._load('css', urls, options, callback);
369
Loads one or more JavaScript resources.
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.
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
379
URLs may be relative or absolute, and do not have to have the same origin
382
The `options` parameter may be omitted completely and a callback passed in
383
its place, if desired.
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.
391
// Load a single JS file and log a message on completion.
392
Y.Get.js('foo.js', function (err) {
394
Y.log('foo.js failed to load!');
396
Y.log('foo.js was loaded successfully');
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'];
404
Y.Get.js(urls, function (err) {
406
Y.log('one or more files failed to load!');
408
Y.log('all files loaded successfully');
412
// Specify transaction-level options, which will apply to all requests
413
// within the transaction.
415
attributes: {'class': 'my-js'},
419
// Specify per-request options, which override transaction-level and
422
{url: 'foo.js', attributes: {id: 'foo'}},
423
{url: 'bar.js', attributes: {id: 'bar', charset: 'iso-8859-1'}}
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`
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.
440
@return {Get.Transaction} Transaction object.
444
js: function (urls, options, callback) {
445
return this._load('js', urls, options, callback);
449
Loads one or more CSS and/or JavaScript resources in the same transaction.
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,
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.
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) {
468
Y.log('one or more files failed to load!');
470
Y.log('all files loaded successfully');
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`
484
@param {Array|null} err Array of errors that occurred during the
485
transaction, or `null` on success.
486
@param {Get.Transaction} Transaction object.
488
@return {Get.Transaction} Transaction object.
492
load: function (urls, options, callback) {
493
return this._load(null, urls, options, callback);
496
// -- Protected Methods ----------------------------------------------------
499
Triggers an automatic purge if the purge threshold has been reached.
502
@param {Number} threshold Purge threshold to use, in milliseconds.
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);
515
Populates the `_env` property with information about the current
519
@return {Object} Environment information.
524
_getEnv: function () {
525
var doc = Y.config.doc,
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,
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,
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
547
// davglass: Seems that Chrome on Android needs this to be false.
549
(!ua.gecko && !ua.webkit) || ua.gecko >= 9 ||
550
ua.compareVersions(ua.webkit, 535.24) >= 0
551
) && !(ua.chrome && ua.chrome <= 18),
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)
560
_getTransaction: function (urls, options) {
564
if (!Lang.isArray(urls)) {
568
options = Y.merge(this.options, options);
570
// Clone the attributes object so we don't end up modifying it by ref.
571
options.attributes = Y.merge(this.options.attributes,
574
for (i = 0, len = urls.length; i < len; ++i) {
576
req = {attributes: {}};
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') {
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.
590
Y.log('URL must be a string or an object with a `url` property.', 'error', 'get');
594
Y.mix(req, options, false, null, 0, true);
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.
600
if (this.REGEX_CSS.test(url)) {
603
if (!this.REGEX_JS.test(url)) {
604
Y.log("Can't guess file type from URL. Assuming JS: " + url, 'warn', 'get');
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);
616
// Give the node an id attribute if it doesn't already have one.
617
req.attributes.id || (req.attributes.id = Y.guid());
619
// Backcompat for <3.5.0 behavior.
621
Y.log('The `win` option is deprecated as of 3.5.0. Use `doc` instead.', 'warn', 'get');
622
req.doc = req.win.document;
624
req.win = req.doc.defaultView || req.doc.parentWindow;
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;
635
return new Transaction(requests, options);
638
_load: function (type, urls, options, callback) {
641
// Allow callback as third param.
642
if (typeof options === 'function') {
647
options || (options = {});
654
transaction = this._getTransaction(urls, options);
658
transaction: transaction
673
item = this._queue.shift();
676
this._pending = item;
678
item.transaction.execute(function () {
679
item.callback && item.callback.apply(this, arguments);
687
_purge: function (nodes) {
688
var purgeNodes = this._purgeNodes,
689
isTransaction = nodes !== purgeNodes,
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) {
699
node.parentNode && node.parentNode.removeChild(node);
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).
708
index = Y.Array.indexOf(purgeNodes, node);
711
purgeNodes.splice(index, 1);
727
Represents a Get transaction, which may contain requests for one or more JS or
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.
733
@class Get.Transaction
737
Get.Transaction = Transaction = function (requests, options) {
740
self.id = Transaction._lastId += 1;
741
self.data = options.data;
744
self.options = options;
745
self.requests = requests;
747
self._callbacks = []; // callbacks to call after execution finishes
751
// Deprecated pre-3.5.0 properties.
752
self.tId = self.id; // Use `id` instead.
753
self.win = options.win || Y.config.win;
757
Arbitrary data object associated with this transaction.
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.
762
@property {Object} data
766
Array of errors that have occurred during this transaction, if any.
769
@property {Object[]} errors
770
@property {String} errors.error Error message.
771
@property {Object} errors.request Request object related to the error.
775
Numeric id for this transaction, unique among all transactions within the same
776
YUI sandbox in the current pageview.
778
@property {Number} id
783
HTMLElement nodes (native ones, not YUI Node instances) that have been inserted
784
during the current transaction.
786
@property {HTMLElement[]} nodes
790
Options associated with this transaction.
792
See `Get.options` for the full list of available options.
794
@property {Object} options
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.
802
@property {Object} requests
807
Id of the most recent transaction.
814
Transaction._lastId = 0;
816
Transaction.prototype = {
817
// -- Public Properties ----------------------------------------------------
820
Current state of this transaction. One of "new", "executing", or "done".
826
_state: 'new', // "new", "executing", or "done"
828
// -- Public Methods -------------------------------------------------------
831
Aborts this transaction.
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).
839
@param {String} [msg="Aborted."] Optional message to use in the `errors`
840
array describing why the transaction was aborted.
842
abort: function (msg) {
843
this._pending = null;
844
this._pendingCSS = null;
845
this._pollTimer = clearTimeout(this._pollTimer);
849
this.errors.push({error: msg || 'Aborted'});
854
Begins execting the transaction.
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.
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).
868
@param {Function} callback Callback function to execute after all requests
869
in the transaction are complete, or after the transaction is aborted.
871
execute: function (callback) {
873
requests = self.requests,
877
if (state === 'done') {
878
callback && callback(self.errors.length ? self.errors : null, self);
881
callback && self._callbacks.push(callback);
883
if (state === 'executing') {
888
self._state = 'executing';
889
self._queue = queue = [];
891
if (self.options.timeout) {
892
self._timeout = setTimeout(function () {
893
self.abort('Timeout');
894
}, self.options.timeout);
897
for (i = 0, len = requests.length; i < len; ++i) {
898
req = self.requests[i];
900
if (req.async || req.type === 'css') {
901
// No need to queue CSS or fully async JS.
912
Manually purges any `<script>` or `<link>` nodes this transaction has
915
Be careful when purging a transaction that contains CSS requests, since
916
removing `<link>` nodes will also remove any styles they applied.
921
Get._purge(this.nodes);
924
// -- Protected Methods ----------------------------------------------------
925
_createNode: function (name, attrs, doc) {
926
var node = doc.createElement(name),
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');
937
CUSTOM_ATTRS = testEl.className === 'a' ? {} : {
943
for (attr in attrs) {
944
if (attrs.hasOwnProperty(attr)) {
945
node.setAttribute(CUSTOM_ATTRS[attr] || attr, attrs[attr]);
952
_finish: function () {
953
var errors = this.errors.length ? this.errors : null,
954
options = this.options,
955
thisObj = options.context || this,
958
if (this._state === 'done') {
962
this._state = 'done';
964
for (i = 0, len = this._callbacks.length; i < len; ++i) {
965
this._callbacks[i].call(thisObj, errors, this);
968
data = this._getEventData();
971
if (options.onTimeout && errors[errors.length - 1].error === 'Timeout') {
972
options.onTimeout.call(thisObj, data);
975
if (options.onFailure) {
976
options.onFailure.call(thisObj, data);
978
} else if (options.onSuccess) {
979
options.onSuccess.call(thisObj, data);
983
options.onEnd.call(thisObj, data);
987
_getEventData: function (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
1002
_getInsertBefore: function (req) {
1004
el = req.insertBefore,
1005
cache, cachedNode, docStamp;
1008
return typeof el === 'string' ? doc.getElementById(el) : el;
1011
cache = Get._insertCache;
1012
docStamp = Y.stamp(doc);
1014
if ((el = cache[docStamp])) { // assignment
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);
1025
// Look for a <head> element.
1026
el = doc.head || doc.getElementsByTagName('head')[0];
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);
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]);
1041
_insert: function (req) {
1043
insertBefore = this._getInsertBefore(req),
1044
isScript = req.type === 'js',
1048
cssTimeout, nodeType;
1052
nodeType = 'script';
1053
} else if (!env.cssLoad && ua.gecko) {
1059
node = req.node = this._createNode(nodeType, req.attributes,
1063
function onError() {
1064
self._progress('Failed to load ' + req.url, req);
1069
clearTimeout(cssTimeout);
1072
self._progress(null, req);
1076
// Deal with script asynchronicity.
1078
node.setAttribute('src', req.url);
1081
// Explicitly indicate that we want the browser to execute this
1082
// script asynchronously. This is necessary for older browsers
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
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;
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.
1108
// Props to Zach Leatherman for calling my attention to this
1110
node.innerHTML = (req.attributes.charset ?
1111
'@charset "' + req.attributes.charset + '";' : '') +
1112
'@import "' + req.url + '";';
1114
node.setAttribute('href', req.url);
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;
1127
} else if (!isScript && !env.cssLoad) {
1128
// CSS on Firefox <9 or WebKit.
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;
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);
1145
this.nodes.push(node);
1146
insertBefore.parentNode.insertBefore(node, insertBefore);
1149
_next: function () {
1150
if (this._pending) {
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) {
1165
_poll: function (newReq) {
1167
pendingCSS = self._pendingCSS,
1168
isWebKit = Y.UA.webkit,
1169
i, hasRules, j, nodeHref, req, sheets;
1172
pendingCSS || (pendingCSS = self._pendingCSS = []);
1173
pendingCSS.push(newReq);
1175
if (self._pollTimer) {
1176
// A poll timeout is already pending, so no need to create a
1182
self._pollTimer = null;
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
1188
for (i = 0; i < pendingCSS.length; ++i) {
1189
req = pendingCSS[i];
1192
// Look for a stylesheet matching the pending URL.
1193
sheets = req.doc.styleSheets;
1195
nodeHref = req.node.href;
1198
if (sheets[j].href === nodeHref) {
1199
pendingCSS.splice(i, 1);
1201
self._progress(null, req);
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.
1210
// See Zach's blog for more details:
1211
// http://www.zachleat.com/web/2010/07/29/load-css-dynamically/
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;
1218
// If we get here, the stylesheet has loaded.
1219
pendingCSS.splice(i, 1);
1221
self._progress(null, req);
1223
// An exception means the stylesheet is still loading.
1228
if (pendingCSS.length) {
1229
self._pollTimer = setTimeout(function () {
1230
self._poll.call(self);
1231
}, self.options.pollInterval);
1235
_progress: function (err, req) {
1236
var options = this.options;
1246
Y.log(err, 'error', 'get');
1249
req.node._yuiget_finished = req.finished = true;
1251
if (options.onProgress) {
1252
options.onProgress.call(options.context || this,
1253
this._getEventData(req));
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);
1264
if (this._pending === req) {
1265
this._pending = null;
1274
}, '3.5.1' ,{requires:['yui-base']});