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