2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
8 YUI.add('get', function (Y, NAME) {
10 /*jslint boss:true, expr:true, laxbreak: true */
13 Provides dynamic loading of remote JavaScript and CSS resources.
22 CUSTOM_ATTRS, // defined lazily in Y.Get.Transaction._createNode()
27 // -- Public Properties ----------------------------------------------------
30 Default options for CSS requests. Options specified here will override
31 global defaults for CSS requests.
33 See the `options` property for all available options.
45 doc : Y.config.linkDoc || Y.config.doc,
50 Default options for JS requests. Options specified here will override global
51 defaults for JS requests.
53 See the `options` property for all available options.
62 doc : Y.config.scriptDoc || Y.config.doc
66 Default options to use for all requests.
68 Note that while all available options are documented here for ease of
69 discovery, some options (like callback functions) only make sense at the
72 Callback functions specified via the options object or the `options`
73 parameter of the `css()`, `js()`, or `load()` methods will receive the
74 transaction object as a parameter. See `Y.Get.Transaction` for details on
75 the properties and methods available on transactions.
79 @property {Object} options
81 @property {Boolean} [options.async=false] Whether or not to load scripts
82 asynchronously, meaning they're requested in parallel and execution
83 order is not guaranteed. Has no effect on CSS, since CSS is always
84 loaded asynchronously.
86 @property {Object} [options.attributes] HTML attribute name/value pairs that
87 should be added to inserted nodes. By default, the `charset` attribute
88 will be set to "utf-8" and nodes will be given an auto-generated `id`
89 attribute, but you can override these with your own values if desired.
91 @property {Boolean} [options.autopurge] Whether or not to automatically
92 purge inserted nodes after the purge threshold is reached. This is
93 `true` by default for JavaScript, but `false` for CSS since purging a
94 CSS node will also remove any styling applied by the referenced file.
96 @property {Object} [options.context] `this` object to use when calling
97 callback functions. Defaults to the transaction object.
99 @property {Mixed} [options.data] Arbitrary data object to pass to "on*"
102 @property {Document} [options.doc] Document into which nodes should be
103 inserted. By default, the current document is used.
105 @property {HTMLElement|String} [options.insertBefore] HTML element or id
106 string of an element before which all generated nodes should be
107 inserted. If not specified, Get will automatically determine the best
108 place to insert nodes for maximum compatibility.
110 @property {Function} [options.onEnd] Callback to execute after a transaction
111 is complete, regardless of whether it succeeded or failed.
113 @property {Function} [options.onFailure] Callback to execute after a
114 transaction fails, times out, or is aborted.
116 @property {Function} [options.onProgress] Callback to execute after each
117 individual request in a transaction either succeeds or fails.
119 @property {Function} [options.onSuccess] Callback to execute after a
120 transaction completes successfully with no errors. Note that in browsers
121 that don't support the `error` event on CSS `<link>` nodes, a failed CSS
122 request may still be reported as a success because in these browsers
123 it can be difficult or impossible to distinguish between success and
124 failure for CSS resources.
126 @property {Function} [options.onTimeout] Callback to execute after a
127 transaction times out.
129 @property {Number} [options.pollInterval=50] Polling interval (in
130 milliseconds) for detecting CSS load completion in browsers that don't
131 support the `load` event on `<link>` nodes. This isn't used for
134 @property {Number} [options.purgethreshold=20] Number of nodes to insert
135 before triggering an automatic purge when `autopurge` is `true`.
137 @property {Number} [options.timeout] Number of milliseconds to wait before
138 aborting a transaction. When a timeout occurs, the `onTimeout` callback
139 is called, followed by `onFailure` and finally `onEnd`. By default,
142 @property {String} [options.type] Resource type ("css" or "js"). This option
143 is set automatically by the `css()` and `js()` functions and will be
144 ignored there, but may be useful when using the `load()` function. If
145 not specified, the type will be inferred from the URL, defaulting to
146 "js" if the URL doesn't contain a recognizable file extension.
156 // -- Protected Properties -------------------------------------------------
159 Regex that matches a CSS URL. Used to guess the file type when it's not
169 REGEX_CSS: /\.css(?:[?;].*)?$/i,
172 Regex that matches a JS URL. Used to guess the file type when it's not
182 REGEX_JS : /\.js(?:[?;].*)?$/i,
185 Contains information about the current environment, such as what script and
186 link injection features it supports.
188 This object is created and populated the first time the `_getEnv()` method
199 Mapping of document _yuid strings to <head> or <base> node references so we
200 don't have to look the node up each time we want to insert a request node.
202 @property _insertCache
211 Information about the currently pending transaction, if any.
213 This is actually an object with two properties: `callback`, containing the
214 optional callback passed to `css()`, `load()`, or `js()`; and `transaction`,
215 containing the actual transaction instance.
226 HTML nodes eligible to be purged next time autopurge is triggered.
228 @property _purgeNodes
237 Queued transactions and associated callbacks.
247 // -- Public Methods -------------------------------------------------------
250 Aborts the specified transaction.
252 This will cause the transaction's `onFailure` callback to be called and
253 will prevent any new script and link nodes from being added to the document,
254 but any resources that have already been requested will continue loading
255 (there's no safe way to prevent this, unfortunately).
257 *Note:* This method is deprecated as of 3.5.0, and will be removed in a
258 future version of YUI. Use the transaction-level `abort()` method instead.
261 @param {Get.Transaction} transaction Transaction to abort.
262 @deprecated Use the `abort()` method on the transaction instead.
265 abort: function (transaction) {
266 var i, id, item, len, pending;
268 Y.log('`Y.Get.abort()` is deprecated as of 3.5.0. Use the `abort()` method on the transaction instead.', 'warn', 'get');
270 if (!transaction.abort) {
272 pending = this._pending;
275 if (pending && pending.transaction.id === id) {
276 transaction = pending.transaction;
277 this._pending = null;
279 for (i = 0, len = this._queue.length; i < len; ++i) {
280 item = this._queue[i].transaction;
282 if (item.id === id) {
284 this._queue.splice(i, 1);
291 transaction && transaction.abort();
295 Loads one or more CSS files.
297 The _urls_ parameter may be provided as a URL string, a request object,
298 or an array of URL strings and/or request objects.
300 A request object is just an object that contains a `url` property and zero
301 or more options that should apply specifically to that request.
302 Request-specific options take priority over transaction-level options and
305 URLs may be relative or absolute, and do not have to have the same origin
308 The `options` parameter may be omitted completely and a callback passed in
309 its place, if desired.
313 // Load a single CSS file and log a message on completion.
314 Y.Get.css('foo.css', function (err) {
316 Y.log('foo.css failed to load!');
318 Y.log('foo.css was loaded successfully');
322 // Load multiple CSS files and log a message when all have finished
324 var urls = ['foo.css', 'http://example.com/bar.css', 'baz/quux.css'];
326 Y.Get.css(urls, function (err) {
328 Y.log('one or more files failed to load!');
330 Y.log('all files loaded successfully');
334 // Specify transaction-level options, which will apply to all requests
335 // within the transaction.
337 attributes: {'class': 'my-css'},
341 // Specify per-request options, which override transaction-level and
344 {url: 'foo.css', attributes: {id: 'foo'}},
345 {url: 'bar.css', attributes: {id: 'bar', charset: 'iso-8859-1'}}
349 @param {String|Object|Array} urls URL string, request object, or array
350 of URLs and/or request objects to load.
351 @param {Object} [options] Options for this transaction. See the
352 `Y.Get.options` property for a complete list of available options.
353 @param {Function} [callback] Callback function to be called on completion.
354 This is a general callback and will be called before any more granular
355 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
358 @param {Array|null} callback.err Array of errors that occurred during
359 the transaction, or `null` on success.
360 @param {Get.Transaction} callback.transaction Transaction object.
362 @return {Get.Transaction} Transaction object.
365 css: function (urls, options, callback) {
366 return this._load('css', urls, options, callback);
370 Loads one or more JavaScript resources.
372 The _urls_ parameter may be provided as a URL string, a request object,
373 or an array of URL strings and/or request objects.
375 A request object is just an object that contains a `url` property and zero
376 or more options that should apply specifically to that request.
377 Request-specific options take priority over transaction-level options and
380 URLs may be relative or absolute, and do not have to have the same origin
383 The `options` parameter may be omitted completely and a callback passed in
384 its place, if desired.
386 Scripts will be executed in the order they're specified unless the `async`
387 option is `true`, in which case they'll be loaded in parallel and executed
388 in whatever order they finish loading.
392 // Load a single JS file and log a message on completion.
393 Y.Get.js('foo.js', function (err) {
395 Y.log('foo.js failed to load!');
397 Y.log('foo.js was loaded successfully');
401 // Load multiple JS files, execute them in order, and log a message when
402 // all have finished loading.
403 var urls = ['foo.js', 'http://example.com/bar.js', 'baz/quux.js'];
405 Y.Get.js(urls, function (err) {
407 Y.log('one or more files failed to load!');
409 Y.log('all files loaded successfully');
413 // Specify transaction-level options, which will apply to all requests
414 // within the transaction.
416 attributes: {'class': 'my-js'},
420 // Specify per-request options, which override transaction-level and
423 {url: 'foo.js', attributes: {id: 'foo'}},
424 {url: 'bar.js', attributes: {id: 'bar', charset: 'iso-8859-1'}}
428 @param {String|Object|Array} urls URL string, request object, or array
429 of URLs and/or request objects to load.
430 @param {Object} [options] Options for this transaction. See the
431 `Y.Get.options` property for a complete list of available options.
432 @param {Function} [callback] Callback function to be called on completion.
433 This is a general callback and will be called before any more granular
434 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
437 @param {Array|null} callback.err Array of errors that occurred during
438 the transaction, or `null` on success.
439 @param {Get.Transaction} callback.transaction Transaction object.
441 @return {Get.Transaction} Transaction object.
445 js: function (urls, options, callback) {
446 return this._load('js', urls, options, callback);
450 Loads one or more CSS and/or JavaScript resources in the same transaction.
452 Use this method when you want to load both CSS and JavaScript in a single
453 transaction and be notified when all requested URLs have finished loading,
456 Behavior and options are the same as for the `css()` and `js()` methods. If
457 a resource type isn't specified in per-request options or transaction-level
458 options, Get will guess the file type based on the URL's extension (`.css`
459 or `.js`, with or without a following query string). If the file type can't
460 be guessed from the URL, a warning will be logged and Get will assume the
461 URL is a JavaScript resource.
465 // Load both CSS and JS files in a single transaction, and log a message
466 // when all files have finished loading.
467 Y.Get.load(['foo.css', 'bar.js', 'baz.css'], function (err) {
469 Y.log('one or more files failed to load!');
471 Y.log('all files loaded successfully');
476 @param {String|Object|Array} urls URL string, request object, or array
477 of URLs and/or request objects to load.
478 @param {Object} [options] Options for this transaction. See the
479 `Y.Get.options` property for a complete list of available options.
480 @param {Function} [callback] Callback function to be called on completion.
481 This is a general callback and will be called before any more granular
482 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options`
485 @param {Array|null} err Array of errors that occurred during the
486 transaction, or `null` on success.
487 @param {Get.Transaction} Transaction object.
489 @return {Get.Transaction} Transaction object.
493 load: function (urls, options, callback) {
494 return this._load(null, urls, options, callback);
497 // -- Protected Methods ----------------------------------------------------
500 Triggers an automatic purge if the purge threshold has been reached.
503 @param {Number} threshold Purge threshold to use, in milliseconds.
508 _autoPurge: function (threshold) {
509 if (threshold && this._purgeNodes.length >= threshold) {
510 Y.log('autopurge triggered after ' + this._purgeNodes.length + ' nodes', 'info', 'get');
511 this._purge(this._purgeNodes);
516 Populates the `_env` property with information about the current
520 @return {Object} Environment information.
525 _getEnv: function () {
526 var doc = Y.config.doc,
529 // Note: some of these checks require browser sniffs since it's not
530 // feasible to load test files on every pageview just to perform a
531 // feature test. I'm sorry if this makes you sad.
532 return (this._env = {
534 // True if this is a browser that supports disabling async mode on
535 // dynamically created script nodes. See
536 // https://developer.mozilla.org/En/HTML/Element/Script#Attributes
538 // IE10 doesn't return true for the MDN feature test, so setting it explicitly,
539 // because it is async by default, and allows you to disable async by setting it to false
540 async: (doc && doc.createElement('script').async === true) || (ua.ie >= 10),
542 // True if this browser fires an event when a dynamically injected
543 // link node fails to load. This is currently true for Firefox 9+
544 // and WebKit 535.24+
545 cssFail: ua.gecko >= 9 || ua.compareVersions(ua.webkit, 535.24) >= 0,
547 // True if this browser fires an event when a dynamically injected
548 // link node finishes loading. This is currently true for IE, Opera,
549 // Firefox 9+, and WebKit 535.24+. Note that IE versions <9 fire the
550 // DOM 0 "onload" event, but not "load". All versions of IE fire
552 // davglass: Seems that Chrome on Android needs this to be false.
554 (!ua.gecko && !ua.webkit) || ua.gecko >= 9 ||
555 ua.compareVersions(ua.webkit, 535.24) >= 0
556 ) && !(ua.chrome && ua.chrome <= 18),
558 // True if this browser preserves script execution order while
559 // loading scripts in parallel as long as the script node's `async`
560 // attribute is set to false to explicitly disable async execution.
561 preservesScriptOrder: !!(ua.gecko || ua.opera || (ua.ie && ua.ie >= 10))
565 _getTransaction: function (urls, options) {
569 if (!Lang.isArray(urls)) {
573 options = Y.merge(this.options, options);
575 // Clone the attributes object so we don't end up modifying it by ref.
576 options.attributes = Y.merge(this.options.attributes,
579 for (i = 0, len = urls.length; i < len; ++i) {
581 req = {attributes: {}};
583 // If `url` is a string, we create a URL object for it, then mix in
584 // global options and request-specific options. If it's an object
585 // with a "url" property, we assume it's a request object containing
586 // URL-specific options.
587 if (typeof url === 'string') {
589 } else if (url.url) {
590 // URL-specific options override both global defaults and
591 // request-specific options.
592 Y.mix(req, url, false, null, 0, true);
593 url = url.url; // Make url a string so we can use it later.
595 Y.log('URL must be a string or an object with a `url` property.', 'error', 'get');
599 Y.mix(req, options, false, null, 0, true);
601 // If we didn't get an explicit type for this URL either in the
602 // request options or the URL-specific options, try to determine
603 // one from the file extension.
605 if (this.REGEX_CSS.test(url)) {
608 if (!this.REGEX_JS.test(url)) {
609 Y.log("Can't guess file type from URL. Assuming JS: " + url, 'warn', 'get');
616 // Mix in type-specific default options, but don't overwrite any
617 // options that have already been set.
618 Y.mix(req, req.type === 'js' ? this.jsOptions : this.cssOptions,
619 false, null, 0, true);
621 // Give the node an id attribute if it doesn't already have one.
622 req.attributes.id || (req.attributes.id = Y.guid());
624 // Backcompat for <3.5.0 behavior.
626 Y.log('The `win` option is deprecated as of 3.5.0. Use `doc` instead.', 'warn', 'get');
627 req.doc = req.win.document;
629 req.win = req.doc.defaultView || req.doc.parentWindow;
633 Y.log('The `charset` option is deprecated as of 3.5.0. Set `attributes.charset` instead.', 'warn', 'get');
634 req.attributes.charset = req.charset;
640 return new Transaction(requests, options);
643 _load: function (type, urls, options, callback) {
646 // Allow callback as third param.
647 if (typeof options === 'function') {
652 options || (options = {});
655 options._onFinish = Get._onTransactionFinish;
661 transaction = this._getTransaction(urls, options);
665 transaction: transaction
673 _onTransactionFinish : function() {
685 item = this._queue.shift();
688 this._pending = item;
689 item.transaction.execute(item.callback);
693 _purge: function (nodes) {
694 var purgeNodes = this._purgeNodes,
695 isTransaction = nodes !== purgeNodes,
698 while (node = nodes.pop()) { // assignment
699 // Don't purge nodes that haven't finished loading (or errored out),
700 // since this can hang the transaction.
701 if (!node._yuiget_finished) {
705 node.parentNode && node.parentNode.removeChild(node);
707 // If this is a transaction-level purge and this node also exists in
708 // the Get-level _purgeNodes array, we need to remove it from
709 // _purgeNodes to avoid creating a memory leak. The indexOf lookup
710 // sucks, but until we get WeakMaps, this is the least troublesome
711 // way to do this (we can't just hold onto node ids because they may
712 // not be in the same document).
714 index = Y.Array.indexOf(purgeNodes, node);
717 purgeNodes.splice(index, 1);
733 Represents a Get transaction, which may contain requests for one or more JS or
736 This class should not be instantiated manually. Instances will be created and
737 returned as needed by Y.Get's `css()`, `js()`, and `load()` methods.
739 @class Get.Transaction
743 Get.Transaction = Transaction = function (requests, options) {
746 self.id = Transaction._lastId += 1;
747 self.data = options.data;
750 self.options = options;
751 self.requests = requests;
753 self._callbacks = []; // callbacks to call after execution finishes
755 self._reqsWaiting = 0;
757 // Deprecated pre-3.5.0 properties.
758 self.tId = self.id; // Use `id` instead.
759 self.win = options.win || Y.config.win;
763 Arbitrary data object associated with this transaction.
765 This object comes from the options passed to `Get.css()`, `Get.js()`, or
766 `Get.load()`, and will be `undefined` if no data object was specified.
768 @property {Object} data
772 Array of errors that have occurred during this transaction, if any.
775 @property {Object[]} errors
776 @property {String} errors.error Error message.
777 @property {Object} errors.request Request object related to the error.
781 Numeric id for this transaction, unique among all transactions within the same
782 YUI sandbox in the current pageview.
784 @property {Number} id
789 HTMLElement nodes (native ones, not YUI Node instances) that have been inserted
790 during the current transaction.
792 @property {HTMLElement[]} nodes
796 Options associated with this transaction.
798 See `Get.options` for the full list of available options.
800 @property {Object} options
805 Request objects contained in this transaction. Each request object represents
806 one CSS or JS URL that will be (or has been) requested and loaded into the page.
808 @property {Object} requests
813 Id of the most recent transaction.
820 Transaction._lastId = 0;
822 Transaction.prototype = {
823 // -- Public Properties ----------------------------------------------------
826 Current state of this transaction. One of "new", "executing", or "done".
832 _state: 'new', // "new", "executing", or "done"
834 // -- Public Methods -------------------------------------------------------
837 Aborts this transaction.
839 This will cause the transaction's `onFailure` callback to be called and
840 will prevent any new script and link nodes from being added to the document,
841 but any resources that have already been requested will continue loading
842 (there's no safe way to prevent this, unfortunately).
845 @param {String} [msg="Aborted."] Optional message to use in the `errors`
846 array describing why the transaction was aborted.
848 abort: function (msg) {
849 this._pending = null;
850 this._pendingCSS = null;
851 this._pollTimer = clearTimeout(this._pollTimer);
853 this._reqsWaiting = 0;
855 this.errors.push({error: msg || 'Aborted'});
860 Begins execting the transaction.
862 There's usually no reason to call this manually, since Get will call it
863 automatically when other pending transactions have finished. If you really
864 want to execute your transaction before Get does, you can, but be aware that
865 this transaction's scripts may end up executing before the scripts in other
866 pending transactions.
868 If the transaction is already executing, the specified callback (if any)
869 will be queued and called after execution finishes. If the transaction has
870 already finished, the callback will be called immediately (the transaction
871 will not be executed again).
874 @param {Function} callback Callback function to execute after all requests
875 in the transaction are complete, or after the transaction is aborted.
877 execute: function (callback) {
879 requests = self.requests,
883 if (state === 'done') {
884 callback && callback(self.errors.length ? self.errors : null, self);
887 callback && self._callbacks.push(callback);
889 if (state === 'executing') {
894 self._state = 'executing';
895 self._queue = queue = [];
897 if (self.options.timeout) {
898 self._timeout = setTimeout(function () {
899 self.abort('Timeout');
900 }, self.options.timeout);
903 self._reqsWaiting = requests.length;
905 for (i = 0, len = requests.length; i < len; ++i) {
908 if (req.async || req.type === 'css') {
909 // No need to queue CSS or fully async JS.
920 Manually purges any `<script>` or `<link>` nodes this transaction has
923 Be careful when purging a transaction that contains CSS requests, since
924 removing `<link>` nodes will also remove any styles they applied.
929 Get._purge(this.nodes);
932 // -- Protected Methods ----------------------------------------------------
933 _createNode: function (name, attrs, doc) {
934 var node = doc.createElement(name),
938 // IE6 and IE7 expect property names rather than attribute names for
939 // certain attributes. Rather than sniffing, we do a quick feature
940 // test the first time _createNode() runs to determine whether we
941 // need to provide a workaround.
942 testEl = doc.createElement('div');
943 testEl.setAttribute('class', 'a');
945 CUSTOM_ATTRS = testEl.className === 'a' ? {} : {
951 for (attr in attrs) {
952 if (attrs.hasOwnProperty(attr)) {
953 node.setAttribute(CUSTOM_ATTRS[attr] || attr, attrs[attr]);
960 _finish: function () {
961 var errors = this.errors.length ? this.errors : null,
962 options = this.options,
963 thisObj = options.context || this,
966 if (this._state === 'done') {
970 this._state = 'done';
972 for (i = 0, len = this._callbacks.length; i < len; ++i) {
973 this._callbacks[i].call(thisObj, errors, this);
976 data = this._getEventData();
979 if (options.onTimeout && errors[errors.length - 1].error === 'Timeout') {
980 options.onTimeout.call(thisObj, data);
983 if (options.onFailure) {
984 options.onFailure.call(thisObj, data);
986 } else if (options.onSuccess) {
987 options.onSuccess.call(thisObj, data);
991 options.onEnd.call(thisObj, data);
994 if (options._onFinish) {
999 _getEventData: function (req) {
1001 // This merge is necessary for backcompat. I hate it.
1002 return Y.merge(this, {
1003 abort : this.abort, // have to copy these because the prototype isn't preserved
1014 _getInsertBefore: function (req) {
1016 el = req.insertBefore,
1020 return typeof el === 'string' ? doc.getElementById(el) : el;
1023 cache = Get._insertCache;
1024 docStamp = Y.stamp(doc);
1026 if ((el = cache[docStamp])) { // assignment
1030 // Inserting before a <base> tag apparently works around an IE bug
1031 // (according to a comment from pre-3.5.0 Y.Get), but I'm not sure what
1032 // bug that is, exactly. Better safe than sorry?
1033 if ((el = doc.getElementsByTagName('base')[0])) { // assignment
1034 return (cache[docStamp] = el);
1037 // Look for a <head> element.
1038 el = doc.head || doc.getElementsByTagName('head')[0];
1041 // Create a marker node at the end of <head> to use as an insertion
1042 // point. Inserting before this node will ensure that all our CSS
1043 // gets inserted in the correct order, to maintain style precedence.
1044 el.appendChild(doc.createTextNode(''));
1045 return (cache[docStamp] = el.lastChild);
1048 // If all else fails, just insert before the first script node on the
1049 // page, which is virtually guaranteed to exist.
1050 return (cache[docStamp] = doc.getElementsByTagName('script')[0]);
1053 _insert: function (req) {
1055 insertBefore = this._getInsertBefore(req),
1056 isScript = req.type === 'js',
1060 cssTimeout, nodeType;
1064 nodeType = 'script';
1065 } else if (!env.cssLoad && ua.gecko) {
1071 node = req.node = this._createNode(nodeType, req.attributes,
1075 function onError() {
1076 self._progress('Failed to load ' + req.url, req);
1081 clearTimeout(cssTimeout);
1084 self._progress(null, req);
1087 // Deal with script asynchronicity.
1089 node.setAttribute('src', req.url);
1092 // Explicitly indicate that we want the browser to execute this
1093 // script asynchronously. This is necessary for older browsers
1098 // This browser treats injected scripts as async by default
1099 // (standard HTML5 behavior) but asynchronous loading isn't
1100 // desired, so tell the browser not to mark this script as
1105 // If this browser doesn't preserve script execution order based
1106 // on insertion order, we'll need to avoid inserting other
1107 // scripts until this one finishes loading.
1108 if (!env.preservesScriptOrder) {
1109 this._pending = req;
1113 if (!env.cssLoad && ua.gecko) {
1114 // In Firefox <9, we can import the requested URL into a <style>
1115 // node and poll for the existence of node.sheet.cssRules. This
1116 // gives us a reliable way to determine CSS load completion that
1117 // also works for cross-domain stylesheets.
1119 // Props to Zach Leatherman for calling my attention to this
1121 node.innerHTML = (req.attributes.charset ?
1122 '@charset "' + req.attributes.charset + '";' : '') +
1123 '@import "' + req.url + '";';
1125 node.setAttribute('href', req.url);
1130 if (isScript && ua.ie && (ua.ie < 9 || (document.documentMode && document.documentMode < 9))) {
1131 // Script on IE < 9, and IE 9+ when in IE 8 or older modes, including quirks mode.
1132 node.onreadystatechange = function () {
1133 if (/loaded|complete/.test(node.readyState)) {
1134 node.onreadystatechange = null;
1138 } else if (!isScript && !env.cssLoad) {
1139 // CSS on Firefox <9 or WebKit.
1142 // Script or CSS on everything else. Using DOM 0 events because that
1143 // evens the playing field with older IEs.
1147 // We currently need to introduce a timeout for IE10, since it
1148 // calls onerror/onload synchronously for 304s - messing up existing
1151 // Remove this block if the following bug gets fixed by GA
1152 /*jshint maxlen: 1500 */
1153 // https://connect.microsoft.com/IE/feedback/details/763871/dynamically-loaded-scripts-with-304s-responses-interrupt-the-currently-executing-js-thread-onload
1154 node.onerror = function() { setTimeout(onError, 0); };
1155 node.onload = function() { setTimeout(onLoad, 0); };
1157 node.onerror = onError;
1158 node.onload = onLoad;
1161 // If this browser doesn't fire an event when CSS fails to load,
1162 // fail after a timeout to avoid blocking the transaction queue.
1163 if (!env.cssFail && !isScript) {
1164 cssTimeout = setTimeout(onError, req.timeout || 3000);
1168 this.nodes.push(node);
1169 insertBefore.parentNode.insertBefore(node, insertBefore);
1172 _next: function () {
1173 if (this._pending) {
1177 // If there are requests in the queue, insert the next queued request.
1178 // Otherwise, if we're waiting on already-inserted requests to finish,
1179 // wait longer. If there are no queued requests and we're not waiting
1180 // for anything to load, then we're done!
1181 if (this._queue.length) {
1182 this._insert(this._queue.shift());
1183 } else if (!this._reqsWaiting) {
1188 _poll: function (newReq) {
1190 pendingCSS = self._pendingCSS,
1191 isWebKit = Y.UA.webkit,
1192 i, hasRules, j, nodeHref, req, sheets;
1195 pendingCSS || (pendingCSS = self._pendingCSS = []);
1196 pendingCSS.push(newReq);
1198 if (self._pollTimer) {
1199 // A poll timeout is already pending, so no need to create a
1205 self._pollTimer = null;
1207 // Note: in both the WebKit and Gecko hacks below, a CSS URL that 404s
1208 // will still be treated as a success. There's no good workaround for
1211 for (i = 0; i < pendingCSS.length; ++i) {
1212 req = pendingCSS[i];
1215 // Look for a stylesheet matching the pending URL.
1216 sheets = req.doc.styleSheets;
1218 nodeHref = req.node.href;
1221 if (sheets[j].href === nodeHref) {
1222 pendingCSS.splice(i, 1);
1224 self._progress(null, req);
1229 // Many thanks to Zach Leatherman for calling my attention to
1230 // the @import-based cross-domain technique used here, and to
1231 // Oleg Slobodskoi for an earlier same-domain implementation.
1233 // See Zach's blog for more details:
1234 // http://www.zachleat.com/web/2010/07/29/load-css-dynamically/
1236 // We don't really need to store this value since we never
1237 // use it again, but if we don't store it, Closure Compiler
1238 // assumes the code is useless and removes it.
1239 hasRules = !!req.node.sheet.cssRules;
1241 // If we get here, the stylesheet has loaded.
1242 pendingCSS.splice(i, 1);
1244 self._progress(null, req);
1246 // An exception means the stylesheet is still loading.
1251 if (pendingCSS.length) {
1252 self._pollTimer = setTimeout(function () {
1253 self._poll.call(self);
1254 }, self.options.pollInterval);
1258 _progress: function (err, req) {
1259 var options = this.options;
1269 Y.log(err, 'error', 'get');
1272 req.node._yuiget_finished = req.finished = true;
1274 if (options.onProgress) {
1275 options.onProgress.call(options.context || this,
1276 this._getEventData(req));
1279 if (req.autopurge) {
1280 // Pre-3.5.0 Get always excludes the most recent node from an
1281 // autopurge. I find this odd, but I'm keeping that behavior for
1282 // the sake of backcompat.
1283 Get._autoPurge(this.options.purgethreshold);
1284 Get._purgeNodes.push(req.node);
1287 if (this._pending === req) {
1288 this._pending = null;
1291 this._reqsWaiting -= 1;
1298 }, '3.13.0', {"requires": ["yui-base"]});