]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/dom/ScriptLoader.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / dom / ScriptLoader.js
1 /**
2  * ScriptLoader.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         /**
13          * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks when various items gets loaded. This class is useful to load external JavaScript files. 
14          *
15          * @class tinymce.dom.ScriptLoader
16          * @example
17          * // Load a script from a specific URL using the global script loader
18          * tinymce.ScriptLoader.load('somescript.js');
19          * 
20          * // Load a script using a unique instance of the script loader
21          * var scriptLoader = new tinymce.dom.ScriptLoader();
22          * 
23          * scriptLoader.load('somescript.js');
24          * 
25          * // Load multiple scripts
26          * var scriptLoader = new tinymce.dom.ScriptLoader();
27          * 
28          * scriptLoader.add('somescript1.js');
29          * scriptLoader.add('somescript2.js');
30          * scriptLoader.add('somescript3.js');
31          * 
32          * scriptLoader.loadQueue(function() {
33          *    alert('All scripts are now loaded.');
34          * });
35          */
36         tinymce.dom.ScriptLoader = function(settings) {
37                 var QUEUED = 0,
38                         LOADING = 1,
39                         LOADED = 2,
40                         states = {},
41                         queue = [],
42                         scriptLoadedCallbacks = {},
43                         queueLoadedCallbacks = [],
44                         loading = 0,
45                         undefined;
46
47                 /**
48                  * Loads a specific script directly without adding it to the load queue.
49                  *
50                  * @method load
51                  * @param {String} url Absolute URL to script to add.
52                  * @param {function} callback Optional callback function to execute ones this script gets loaded.
53                  * @param {Object} scope Optional scope to execute callback in.
54                  */
55                 function loadScript(url, callback) {
56                         var t = this, dom = tinymce.DOM, elm, uri, loc, id;
57
58                         // Execute callback when script is loaded
59                         function done() {
60                                 dom.remove(id);
61
62                                 if (elm)
63                                         elm.onreadystatechange = elm.onload = elm = null;
64
65                                 callback();
66                         };
67                         
68                         function error() {
69                                 // Report the error so it's easier for people to spot loading errors
70                                 if (typeof(console) !== "undefined" && console.log)
71                                         console.log("Failed to load: " + url);
72
73                                 // We can't mark it as done if there is a load error since
74                                 // A) We don't want to produce 404 errors on the server and
75                                 // B) the onerror event won't fire on all browsers.
76                                 // done();
77                         };
78
79                         id = dom.uniqueId();
80
81                         if (tinymce.isIE6) {
82                                 uri = new tinymce.util.URI(url);
83                                 loc = location;
84
85                                 // If script is from same domain and we
86                                 // use IE 6 then use XHR since it's more reliable
87                                 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol && uri.protocol.toLowerCase() != 'file') {
88                                         tinymce.util.XHR.send({
89                                                 url : tinymce._addVer(uri.getURI()),
90                                                 success : function(content) {
91                                                         // Create new temp script element
92                                                         var script = dom.create('script', {
93                                                                 type : 'text/javascript'
94                                                         });
95
96                                                         // Evaluate script in global scope
97                                                         script.text = content;
98                                                         document.getElementsByTagName('head')[0].appendChild(script);
99                                                         dom.remove(script);
100
101                                                         done();
102                                                 },
103                                                 
104                                                 error : error
105                                         });
106
107                                         return;
108                                 }
109                         }
110
111                         // Create new script element
112                         elm = dom.create('script', {
113                                 id : id,
114                                 type : 'text/javascript',
115                                 src : tinymce._addVer(url)
116                         });
117
118                         // Add onload listener for non IE browsers since IE9
119                         // fires onload event before the script is parsed and executed
120                         if (!tinymce.isIE)
121                                 elm.onload = done;
122
123                         // Add onerror event will get fired on some browsers but not all of them
124                         elm.onerror = error;
125
126                         // Opera 9.60 doesn't seem to fire the onreadystate event at correctly
127                         if (!tinymce.isOpera) {
128                                 elm.onreadystatechange = function() {
129                                         var state = elm.readyState;
130
131                                         // Loaded state is passed on IE 6 however there
132                                         // are known issues with this method but we can't use
133                                         // XHR in a cross domain loading
134                                         if (state == 'complete' || state == 'loaded')
135                                                 done();
136                                 };
137                         }
138
139                         // Most browsers support this feature so we report errors
140                         // for those at least to help users track their missing plugins etc
141                         // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option
142                         /*elm.onerror = function() {
143                                 alert('Failed to load: ' + url);
144                         };*/
145
146                         // Add script to document
147                         (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
148                 };
149
150                 /**
151                  * Returns true/false if a script has been loaded or not.
152                  *
153                  * @method isDone
154                  * @param {String} url URL to check for.
155                  * @return [Boolean} true/false if the URL is loaded.
156                  */
157                 this.isDone = function(url) {
158                         return states[url] == LOADED;
159                 };
160
161                 /**
162                  * Marks a specific script to be loaded. This can be useful if a script got loaded outside
163                  * the script loader or to skip it from loading some script.
164                  *
165                  * @method markDone
166                  * @param {string} u Absolute URL to the script to mark as loaded.
167                  */
168                 this.markDone = function(url) {
169                         states[url] = LOADED;
170                 };
171
172                 /**
173                  * Adds a specific script to the load queue of the script loader.
174                  *
175                  * @method add
176                  * @param {String} url Absolute URL to script to add.
177                  * @param {function} callback Optional callback function to execute ones this script gets loaded.
178                  * @param {Object} scope Optional scope to execute callback in.
179                  */
180                 this.add = this.load = function(url, callback, scope) {
181                         var item, state = states[url];
182
183                         // Add url to load queue
184                         if (state == undefined) {
185                                 queue.push(url);
186                                 states[url] = QUEUED;
187                         }
188
189                         if (callback) {
190                                 // Store away callback for later execution
191                                 if (!scriptLoadedCallbacks[url])
192                                         scriptLoadedCallbacks[url] = [];
193
194                                 scriptLoadedCallbacks[url].push({
195                                         func : callback,
196                                         scope : scope || this
197                                 });
198                         }
199                 };
200
201                 /**
202                  * Starts the loading of the queue.
203                  *
204                  * @method loadQueue
205                  * @param {function} callback Optional callback to execute when all queued items are loaded.
206                  * @param {Object} scope Optional scope to execute the callback in.
207                  */
208                 this.loadQueue = function(callback, scope) {
209                         this.loadScripts(queue, callback, scope);
210                 };
211
212                 /**
213                  * Loads the specified queue of files and executes the callback ones they are loaded.
214                  * This method is generally not used outside this class but it might be useful in some scenarios. 
215                  *
216                  * @method loadScripts
217                  * @param {Array} scripts Array of queue items to load.
218                  * @param {function} callback Optional callback to execute ones all items are loaded.
219                  * @param {Object} scope Optional scope to execute callback in.
220                  */
221                 this.loadScripts = function(scripts, callback, scope) {
222                         var loadScripts;
223
224                         function execScriptLoadedCallbacks(url) {
225                                 // Execute URL callback functions
226                                 tinymce.each(scriptLoadedCallbacks[url], function(callback) {
227                                         callback.func.call(callback.scope);
228                                 });
229
230                                 scriptLoadedCallbacks[url] = undefined;
231                         };
232
233                         queueLoadedCallbacks.push({
234                                 func : callback,
235                                 scope : scope || this
236                         });
237
238                         loadScripts = function() {
239                                 var loadingScripts = tinymce.grep(scripts);
240
241                                 // Current scripts has been handled
242                                 scripts.length = 0;
243
244                                 // Load scripts that needs to be loaded
245                                 tinymce.each(loadingScripts, function(url) {
246                                         // Script is already loaded then execute script callbacks directly
247                                         if (states[url] == LOADED) {
248                                                 execScriptLoadedCallbacks(url);
249                                                 return;
250                                         }
251
252                                         // Is script not loading then start loading it
253                                         if (states[url] != LOADING) {
254                                                 states[url] = LOADING;
255                                                 loading++;
256
257                                                 loadScript(url, function() {
258                                                         states[url] = LOADED;
259                                                         loading--;
260
261                                                         execScriptLoadedCallbacks(url);
262
263                                                         // Load more scripts if they where added by the recently loaded script
264                                                         loadScripts();
265                                                 });
266                                         }
267                                 });
268
269                                 // No scripts are currently loading then execute all pending queue loaded callbacks
270                                 if (!loading) {
271                                         tinymce.each(queueLoadedCallbacks, function(callback) {
272                                                 callback.func.call(callback.scope);
273                                         });
274
275                                         queueLoadedCallbacks.length = 0;
276                                 }
277                         };
278
279                         loadScripts();
280                 };
281         };
282
283         // Global script loader
284         tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
285 })(tinymce);