]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/yui/get.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / yui / get.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('get', function(Y) {
9
10
11 /**
12  * Provides a mechanism to fetch remote resources and
13  * insert them into a document.
14  * @module yui
15  * @submodule get
16  */
17
18 var ua = Y.UA,
19     L = Y.Lang,
20     TYPE_JS = 'text/javascript',
21     TYPE_CSS = 'text/css',
22     STYLESHEET = 'stylesheet';
23
24 /**
25  * Fetches and inserts one or more script or link nodes into the document
26  * @class Get
27  * @static
28  */
29 Y.Get = function() {
30
31     /**
32      * hash of queues to manage multiple requests
33      * @property queues
34      * @private
35      */
36     var _get, _purge, _track,
37
38     queues = {},
39
40     /**
41      * queue index used to generate transaction ids
42      * @property qidx
43      * @type int
44      * @private
45      */
46     qidx = 0,
47
48     /**
49      * interal property used to prevent multiple simultaneous purge
50      * processes
51      * @property purging
52      * @type boolean
53      * @private
54      */
55     purging,
56
57
58     /**
59      * Generates an HTML element, this is not appended to a document
60      * @method _node
61      * @param {string} type the type of element.
62      * @param {string} attr the attributes.
63      * @param {Window} win optional window to create the element in.
64      * @return {HTMLElement} the generated node.
65      * @private
66      */
67     _node = function(type, attr, win) {
68         var w = win || Y.config.win,
69             d = w.document,
70             n = d.createElement(type),
71             i;
72
73         for (i in attr) {
74             if (attr[i] && attr.hasOwnProperty(i)) {
75                 n.setAttribute(i, attr[i]);
76             }
77         }
78
79         return n;
80     },
81
82     /**
83      * Generates a link node
84      * @method _linkNode
85      * @param {string} url the url for the css file.
86      * @param {Window} win optional window to create the node in.
87      * @param {object} attributes optional attributes collection to apply to the
88      * new node.
89      * @return {HTMLElement} the generated node.
90      * @private
91      */
92     _linkNode = function(url, win, attributes) {
93         var o = {
94             id: Y.guid(),
95             type: TYPE_CSS,
96             rel: STYLESHEET,
97             href: url
98         };
99         if (attributes) {
100             Y.mix(o, attributes);
101         }
102         return _node('link', o, win);
103     },
104
105     /**
106      * Generates a script node
107      * @method _scriptNode
108      * @param {string} url the url for the script file.
109      * @param {Window} win optional window to create the node in.
110      * @param {object} attributes optional attributes collection to apply to the
111      * new node.
112      * @return {HTMLElement} the generated node.
113      * @private
114      */
115     _scriptNode = function(url, win, attributes) {
116         var o = {
117             id: Y.guid(),
118             type: TYPE_JS
119         };
120
121         if (attributes) {
122             Y.mix(o, attributes);
123         }
124
125         o.src = url;
126
127         return _node('script', o, win);
128     },
129
130     /**
131      * Returns the data payload for callback functions.
132      * @method _returnData
133      * @param {object} q the queue.
134      * @param {string} msg the result message.
135      * @param {string} result the status message from the request.
136      * @return {object} the state data from the request.
137      * @private
138      */
139     _returnData = function(q, msg, result) {
140         return {
141                 tId: q.tId,
142                 win: q.win,
143                 data: q.data,
144                 nodes: q.nodes,
145                 msg: msg,
146                 statusText: result,
147                 purge: function() {
148                     _purge(this.tId);
149                 }
150             };
151     },
152
153     /**
154      * The transaction is finished
155      * @method _end
156      * @param {string} id the id of the request.
157      * @param {string} msg the result message.
158      * @param {string} result the status message from the request.
159      * @private
160      */
161     _end = function(id, msg, result) {
162         var q = queues[id], sc;
163         if (q && q.onEnd) {
164             sc = q.context || q;
165             q.onEnd.call(sc, _returnData(q, msg, result));
166         }
167     },
168
169     /*
170      * The request failed, execute fail handler with whatever
171      * was accomplished.  There isn't a failure case at the
172      * moment unless you count aborted transactions
173      * @method _fail
174      * @param {string} id the id of the request
175      * @private
176      */
177     _fail = function(id, msg) {
178
179         var q = queues[id], sc;
180         if (q.timer) {
181             // q.timer.cancel();
182             clearTimeout(q.timer);
183         }
184
185         // execute failure callback
186         if (q.onFailure) {
187             sc = q.context || q;
188             q.onFailure.call(sc, _returnData(q, msg));
189         }
190
191         _end(id, msg, 'failure');
192     },
193
194     /**
195      * The request is complete, so executing the requester's callback
196      * @method _finish
197      * @param {string} id the id of the request.
198      * @private
199      */
200     _finish = function(id) {
201         var q = queues[id], msg, sc;
202         if (q.timer) {
203             // q.timer.cancel();
204             clearTimeout(q.timer);
205         }
206         q.finished = true;
207
208         if (q.aborted) {
209             msg = 'transaction ' + id + ' was aborted';
210             _fail(id, msg);
211             return;
212         }
213
214         // execute success callback
215         if (q.onSuccess) {
216             sc = q.context || q;
217             q.onSuccess.call(sc, _returnData(q));
218         }
219
220         _end(id, msg, 'OK');
221     },
222
223     /**
224      * Timeout detected
225      * @method _timeout
226      * @param {string} id the id of the request.
227      * @private
228      */
229     _timeout = function(id) {
230         var q = queues[id], sc;
231         if (q.onTimeout) {
232             sc = q.context || q;
233             q.onTimeout.call(sc, _returnData(q));
234         }
235
236         _end(id, 'timeout', 'timeout');
237     },
238
239
240     /**
241      * Loads the next item for a given request
242      * @method _next
243      * @param {string} id the id of the request.
244      * @param {string} loaded the url that was just loaded, if any.
245      * @return {string} the result.
246      * @private
247      */
248     _next = function(id, loaded) {
249         var q = queues[id], msg, w, d, h, n, url, s,
250             insertBefore;
251
252         if (q.timer) {
253             // q.timer.cancel();
254             clearTimeout(q.timer);
255         }
256
257         if (q.aborted) {
258             msg = 'transaction ' + id + ' was aborted';
259             _fail(id, msg);
260             return;
261         }
262
263         if (loaded) {
264             q.url.shift();
265             if (q.varName) {
266                 q.varName.shift();
267             }
268         } else {
269             // This is the first pass: make sure the url is an array
270             q.url = (L.isString(q.url)) ? [q.url] : q.url;
271             if (q.varName) {
272                 q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
273             }
274         }
275
276         w = q.win;
277         d = w.document;
278         h = d.getElementsByTagName('head')[0];
279
280         if (q.url.length === 0) {
281             _finish(id);
282             return;
283         }
284
285         url = q.url[0];
286
287         // if the url is undefined, this is probably a trailing comma
288         // problem in IE.
289         if (!url) {
290             q.url.shift();
291             return _next(id);
292         }
293
294
295         if (q.timeout) {
296             // q.timer = L.later(q.timeout, q, _timeout, id);
297             q.timer = setTimeout(function() {
298                 _timeout(id);
299             }, q.timeout);
300         }
301
302         if (q.type === 'script') {
303             n = _scriptNode(url, w, q.attributes);
304         } else {
305             n = _linkNode(url, w, q.attributes);
306         }
307
308         // track this node's load progress
309         _track(q.type, n, id, url, w, q.url.length);
310
311         // add the node to the queue so we can return it to the user supplied
312         // callback
313         q.nodes.push(n);
314
315         // add it to the head or insert it before 'insertBefore'.  Work around
316         // IE bug if there is a base tag.
317         insertBefore = q.insertBefore ||
318                        d.getElementsByTagName('base')[0];
319
320         if (insertBefore) {
321             s = _get(insertBefore, id);
322             if (s) {
323                 s.parentNode.insertBefore(n, s);
324             }
325         } else {
326             h.appendChild(n);
327         }
328
329
330         // FireFox does not support the onload event for link nodes, so
331         // there is no way to make the css requests synchronous. This means
332         // that the css rules in multiple files could be applied out of order
333         // in this browser if a later request returns before an earlier one.
334         // Safari too.
335         if ((ua.webkit || ua.gecko) && q.type === 'css') {
336             _next(id, url);
337         }
338     },
339
340     /**
341      * Removes processed queues and corresponding nodes
342      * @method _autoPurge
343      * @private
344      */
345     _autoPurge = function() {
346         if (purging) {
347             return;
348         }
349         purging = true;
350
351         var i, q;
352
353         for (i in queues) {
354             if (queues.hasOwnProperty(i)) {
355                 q = queues[i];
356                 if (q.autopurge && q.finished) {
357                     _purge(q.tId);
358                     delete queues[i];
359                 }
360             }
361         }
362
363         purging = false;
364     },
365
366     /**
367      * Saves the state for the request and begins loading
368      * the requested urls
369      * @method queue
370      * @param {string} type the type of node to insert.
371      * @param {string} url the url to load.
372      * @param {object} opts the hash of options for this request.
373      * @return {object} transaction object.
374      * @private
375      */
376     _queue = function(type, url, opts) {
377         opts = opts || {};
378
379         var id = 'q' + (qidx++), q,
380             thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
381
382         if (qidx % thresh === 0) {
383             _autoPurge();
384         }
385
386         queues[id] = Y.merge(opts, {
387             tId: id,
388             type: type,
389             url: url,
390             finished: false,
391             nodes: []
392         });
393
394         q = queues[id];
395         q.win = q.win || Y.config.win;
396         q.context = q.context || q;
397         q.autopurge = ('autopurge' in q) ? q.autopurge :
398                       (type === 'script') ? true : false;
399
400         q.attributes = q.attributes || {};
401         q.attributes.charset = opts.charset || q.attributes.charset || 'utf-8';
402
403         _next(id);
404
405         return {
406             tId: id
407         };
408     };
409
410     /**
411      * Detects when a node has been loaded.  In the case of
412      * script nodes, this does not guarantee that contained
413      * script is ready to use.
414      * @method _track
415      * @param {string} type the type of node to track.
416      * @param {HTMLElement} n the node to track.
417      * @param {string} id the id of the request.
418      * @param {string} url the url that is being loaded.
419      * @param {Window} win the targeted window.
420      * @param {int} qlength the number of remaining items in the queue,
421      * including this one.
422      * @param {Function} trackfn function to execute when finished
423      * the default is _next.
424      * @private
425      */
426     _track = function(type, n, id, url, win, qlength, trackfn) {
427         var f = trackfn || _next;
428
429         // IE supports the readystatechange event for script and css nodes
430         // Opera only for script nodes.  Opera support onload for script
431         // nodes, but this doesn't fire when there is a load failure.
432         // The onreadystatechange appears to be a better way to respond
433         // to both success and failure.
434         if (ua.ie) {
435             n.onreadystatechange = function() {
436                 var rs = this.readyState;
437                 if ('loaded' === rs || 'complete' === rs) {
438                     n.onreadystatechange = null;
439                     f(id, url);
440                 }
441             };
442
443         // webkit prior to 3.x is no longer supported
444         } else if (ua.webkit) {
445             if (type === 'script') {
446                 // Safari 3.x supports the load event for script nodes (DOM2)
447                 n.addEventListener('load', function() {
448                     f(id, url);
449                 });
450             }
451
452         // FireFox and Opera support onload (but not DOM2 in FF) handlers for
453         // script nodes.  Opera, but not FF, supports the onload event for link
454         // nodes.
455         } else {
456             n.onload = function() {
457                 f(id, url);
458             };
459
460             n.onerror = function(e) {
461                 _fail(id, e + ': ' + url);
462             };
463         }
464     };
465
466     _get = function(nId, tId) {
467         var q = queues[tId],
468             n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
469         if (!n) {
470             _fail(tId, 'target node not found: ' + nId);
471         }
472
473         return n;
474     };
475
476     /**
477      * Removes the nodes for the specified queue
478      * @method _purge
479      * @param {string} tId the transaction id.
480      * @private
481      */
482     _purge = function(tId) {
483         var n, l, d, h, s, i, node, attr, insertBefore,
484             q = queues[tId];
485
486         if (q) {
487             n = q.nodes;
488             l = n.length;
489             d = q.win.document;
490             h = d.getElementsByTagName('head')[0];
491
492             insertBefore = q.insertBefore ||
493                            d.getElementsByTagName('base')[0];
494
495             if (insertBefore) {
496                 s = _get(insertBefore, tId);
497                 if (s) {
498                     h = s.parentNode;
499                 }
500             }
501
502             for (i = 0; i < l; i = i + 1) {
503                 node = n[i];
504                 if (node.clearAttributes) {
505                     node.clearAttributes();
506                 } else {
507                     for (attr in node) {
508                         if (node.hasOwnProperty(attr)) {
509                             delete node[attr];
510                         }
511                     }
512                 }
513
514                 h.removeChild(node);
515             }
516         }
517         q.nodes = [];
518     };
519
520     return {
521
522         /**
523          * The number of request required before an automatic purge.
524          * Can be configured via the 'purgethreshold' config
525          * property PURGE_THRESH
526          * @static
527          * @type int
528          * @default 20
529          * @private
530          */
531         PURGE_THRESH: 20,
532
533         /**
534          * Called by the the helper for detecting script load in Safari
535          * @method _finalize
536          * @static
537          * @param {string} id the transaction id.
538          * @private
539          */
540         _finalize: function(id) {
541             setTimeout(function() {
542                 _finish(id);
543             }, 0);
544         },
545
546         /**
547          * Abort a transaction
548          * @method abort
549          * @static
550          * @param {string|object} o Either the tId or the object returned from
551          * script() or css().
552          */
553         abort: function(o) {
554             var id = (L.isString(o)) ? o : o.tId,
555                 q = queues[id];
556             if (q) {
557                 q.aborted = true;
558             }
559         },
560
561         /**
562          * Fetches and inserts one or more script nodes into the head
563          * of the current document or the document in a specified window.
564          *
565          * @method script
566          * @static
567          * @param {string|string[]} url the url or urls to the script(s).
568          * @param {object} opts Options:
569          * <dl>
570          * <dt>onSuccess</dt>
571          * <dd>
572          * callback to execute when the script(s) are finished loading
573          * The callback receives an object back with the following
574          * data:
575          * <dl>
576          * <dt>win</dt>
577          * <dd>the window the script(s) were inserted into</dd>
578          * <dt>data</dt>
579          * <dd>the data object passed in when the request was made</dd>
580          * <dt>nodes</dt>
581          * <dd>An array containing references to the nodes that were
582          * inserted</dd>
583          * <dt>purge</dt>
584          * <dd>A function that, when executed, will remove the nodes
585          * that were inserted</dd>
586          * <dt>
587          * </dl>
588          * </dd>
589          * <dt>onTimeout</dt>
590          * <dd>
591          * callback to execute when a timeout occurs.
592          * The callback receives an object back with the following
593          * data:
594          * <dl>
595          * <dt>win</dt>
596          * <dd>the window the script(s) were inserted into</dd>
597          * <dt>data</dt>
598          * <dd>the data object passed in when the request was made</dd>
599          * <dt>nodes</dt>
600          * <dd>An array containing references to the nodes that were
601          * inserted</dd>
602          * <dt>purge</dt>
603          * <dd>A function that, when executed, will remove the nodes
604          * that were inserted</dd>
605          * <dt>
606          * </dl>
607          * </dd>
608          * <dt>onEnd</dt>
609          * <dd>a function that executes when the transaction finishes,
610          * regardless of the exit path</dd>
611          * <dt>onFailure</dt>
612          * <dd>
613          * callback to execute when the script load operation fails
614          * The callback receives an object back with the following
615          * data:
616          * <dl>
617          * <dt>win</dt>
618          * <dd>the window the script(s) were inserted into</dd>
619          * <dt>data</dt>
620          * <dd>the data object passed in when the request was made</dd>
621          * <dt>nodes</dt>
622          * <dd>An array containing references to the nodes that were
623          * inserted successfully</dd>
624          * <dt>purge</dt>
625          * <dd>A function that, when executed, will remove any nodes
626          * that were inserted</dd>
627          * <dt>
628          * </dl>
629          * </dd>
630          * <dt>context</dt>
631          * <dd>the execution context for the callbacks</dd>
632          * <dt>win</dt>
633          * <dd>a window other than the one the utility occupies</dd>
634          * <dt>autopurge</dt>
635          * <dd>
636          * setting to true will let the utilities cleanup routine purge
637          * the script once loaded
638          * </dd>
639          * <dt>purgethreshold</dt>
640          * <dd>
641          * The number of transaction before autopurge should be initiated
642          * </dd>
643          * <dt>data</dt>
644          * <dd>
645          * data that is supplied to the callback when the script(s) are
646          * loaded.
647          * </dd>
648          * <dt>insertBefore</dt>
649          * <dd>node or node id that will become the new node's nextSibling.
650          * If this is not specified, nodes will be inserted before a base
651          * tag should it exist.  Otherwise, the nodes will be appended to the
652          * end of the document head.</dd>
653          * </dl>
654          * <dt>charset</dt>
655          * <dd>Node charset, default utf-8 (deprecated, use the attributes
656          * config)</dd>
657          * <dt>attributes</dt>
658          * <dd>An object literal containing additional attributes to add to
659          * the link tags</dd>
660          * <dt>timeout</dt>
661          * <dd>Number of milliseconds to wait before aborting and firing
662          * the timeout event</dd>
663          * <pre>
664          * &nbsp; Y.Get.script(
665          * &nbsp; ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
666          * &nbsp;  "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"],
667          * &nbsp; &#123;
668          * &nbsp;   onSuccess: function(o) &#123;
669          * &nbsp;     this.log("won't cause error because Y is the context");
670          * &nbsp;                   // immediately
671          * &nbsp;   &#125;,
672          * &nbsp;   onFailure: function(o) &#123;
673          * &nbsp;   &#125;,
674          * &nbsp;   onTimeout: function(o) &#123;
675          * &nbsp;   &#125;,
676          * &nbsp;   data: "foo",
677          * &nbsp;   timeout: 10000, // 10 second timeout
678          * &nbsp;   context: Y, // make the YUI instance
679          * &nbsp;   // win: otherframe // target another window/frame
680          * &nbsp;   autopurge: true // allow the utility to choose when to
681          * &nbsp;                   // remove the nodes
682          * &nbsp;   purgetheshold: 1 // purge previous transaction before
683          * &nbsp;                    // next transaction
684          * &nbsp; &#125;);.
685          * </pre>
686          * @return {tId: string} an object containing info about the
687          * transaction.
688          */
689         script: function(url, opts) {
690             return _queue('script', url, opts);
691         },
692
693         /**
694          * Fetches and inserts one or more css link nodes into the
695          * head of the current document or the document in a specified
696          * window.
697          * @method css
698          * @static
699          * @param {string} url the url or urls to the css file(s).
700          * @param {object} opts Options:
701          * <dl>
702          * <dt>onSuccess</dt>
703          * <dd>
704          * callback to execute when the css file(s) are finished loading
705          * The callback receives an object back with the following
706          * data:
707          * <dl>win</dl>
708          * <dd>the window the link nodes(s) were inserted into</dd>
709          * <dt>data</dt>
710          * <dd>the data object passed in when the request was made</dd>
711          * <dt>nodes</dt>
712          * <dd>An array containing references to the nodes that were
713          * inserted</dd>
714          * <dt>purge</dt>
715          * <dd>A function that, when executed, will remove the nodes
716          * that were inserted</dd>
717          * <dt>
718          * </dl>
719          * </dd>
720          * <dt>context</dt>
721          * <dd>the execution context for the callbacks</dd>
722          * <dt>win</dt>
723          * <dd>a window other than the one the utility occupies</dd>
724          * <dt>data</dt>
725          * <dd>
726          * data that is supplied to the callbacks when the nodes(s) are
727          * loaded.
728          * </dd>
729          * <dt>insertBefore</dt>
730          * <dd>node or node id that will become the new node's nextSibling</dd>
731          * <dt>charset</dt>
732          * <dd>Node charset, default utf-8 (deprecated, use the attributes
733          * config)</dd>
734          * <dt>attributes</dt>
735          * <dd>An object literal containing additional attributes to add to
736          * the link tags</dd>
737          * </dl>
738          * <pre>
739          * Y.Get.css("http://localhost/css/menu.css");
740          * </pre>
741          * <pre>
742          * &nbsp; Y.Get.css(
743          * &nbsp; ["http://localhost/css/menu.css",
744          * &nbsp;   insertBefore: 'custom-styles' // nodes will be inserted
745          * &nbsp;                                 // before the specified node
746          * &nbsp; &#125;);.
747          * </pre>
748          * @return {tId: string} an object containing info about the
749          * transaction.
750          */
751         css: function(url, opts) {
752             return _queue('css', url, opts);
753         }
754     };
755 }();
756
757
758
759 }, '3.3.0' ,{requires:['yui-base']});