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