4 * Copyright, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://www.tinymce.com/license
8 * Contributing: http://www.tinymce.com/contributing
14 * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
15 * when various items gets loaded. This class is useful to load external JavaScript files.
17 * @class tinymce.dom.ScriptLoader
19 * // Load a script from a specific URL using the global script loader
20 * tinymce.ScriptLoader.load('somescript.js');
22 * // Load a script using a unique instance of the script loader
23 * var scriptLoader = new tinymce.dom.ScriptLoader();
25 * scriptLoader.load('somescript.js');
27 * // Load multiple scripts
28 * var scriptLoader = new tinymce.dom.ScriptLoader();
30 * scriptLoader.add('somescript1.js');
31 * scriptLoader.add('somescript2.js');
32 * scriptLoader.add('somescript3.js');
34 * scriptLoader.loadQueue(function() {
35 * alert('All scripts are now loaded.');
38 define("tinymce/dom/ScriptLoader", [
39 "tinymce/dom/DOMUtils",
41 ], function(DOMUtils, Tools) {
42 var DOM = DOMUtils.DOM;
43 var each = Tools.each, grep = Tools.grep;
45 function ScriptLoader() {
51 scriptLoadedCallbacks = {},
52 queueLoadedCallbacks = [],
57 * Loads a specific script directly without adding it to the load queue.
60 * @param {String} url Absolute URL to script to add.
61 * @param {function} callback Optional callback function to execute ones this script gets loaded.
62 * @param {Object} scope Optional scope to execute callback in.
64 function loadScript(url, callback) {
65 var dom = DOM, elm, id;
67 // Execute callback when script is loaded
72 elm.onreadystatechange = elm.onload = elm = null;
79 // Report the error so it's easier for people to spot loading errors
80 if (typeof(console) !== "undefined" && console.log) {
81 console.log("Failed to load: " + url);
84 // We can't mark it as done if there is a load error since
85 // A) We don't want to produce 404 errors on the server and
86 // B) the onerror event won't fire on all browsers.
92 // Create new script element
93 elm = document.createElement('script');
95 elm.type = 'text/javascript';
98 // Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
99 if ("onreadystatechange" in elm) {
100 elm.onreadystatechange = function() {
101 if (/loaded|complete/.test(elm.readyState)) {
109 // Add onerror event will get fired on some browsers but not all of them
112 // Add script to document
113 (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
117 * Returns true/false if a script has been loaded or not.
120 * @param {String} url URL to check for.
121 * @return {Boolean} true/false if the URL is loaded.
123 this.isDone = function(url) {
124 return states[url] == LOADED;
128 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
129 * the script loader or to skip it from loading some script.
132 * @param {string} u Absolute URL to the script to mark as loaded.
134 this.markDone = function(url) {
135 states[url] = LOADED;
139 * Adds a specific script to the load queue of the script loader.
142 * @param {String} url Absolute URL to script to add.
143 * @param {function} callback Optional callback function to execute ones this script gets loaded.
144 * @param {Object} scope Optional scope to execute callback in.
146 this.add = this.load = function(url, callback, scope) {
147 var state = states[url];
149 // Add url to load queue
150 if (state == undef) {
152 states[url] = QUEUED;
156 // Store away callback for later execution
157 if (!scriptLoadedCallbacks[url]) {
158 scriptLoadedCallbacks[url] = [];
161 scriptLoadedCallbacks[url].push({
169 * Starts the loading of the queue.
172 * @param {function} callback Optional callback to execute when all queued items are loaded.
173 * @param {Object} scope Optional scope to execute the callback in.
175 this.loadQueue = function(callback, scope) {
176 this.loadScripts(queue, callback, scope);
180 * Loads the specified queue of files and executes the callback ones they are loaded.
181 * This method is generally not used outside this class but it might be useful in some scenarios.
183 * @method loadScripts
184 * @param {Array} scripts Array of queue items to load.
185 * @param {function} callback Optional callback to execute ones all items are loaded.
186 * @param {Object} scope Optional scope to execute callback in.
188 this.loadScripts = function(scripts, callback, scope) {
191 function execScriptLoadedCallbacks(url) {
192 // Execute URL callback functions
193 each(scriptLoadedCallbacks[url], function(callback) {
194 callback.func.call(callback.scope);
197 scriptLoadedCallbacks[url] = undef;
200 queueLoadedCallbacks.push({
205 loadScripts = function() {
206 var loadingScripts = grep(scripts);
208 // Current scripts has been handled
211 // Load scripts that needs to be loaded
212 each(loadingScripts, function(url) {
213 // Script is already loaded then execute script callbacks directly
214 if (states[url] == LOADED) {
215 execScriptLoadedCallbacks(url);
219 // Is script not loading then start loading it
220 if (states[url] != LOADING) {
221 states[url] = LOADING;
224 loadScript(url, function() {
225 states[url] = LOADED;
228 execScriptLoadedCallbacks(url);
230 // Load more scripts if they where added by the recently loaded script
236 // No scripts are currently loading then execute all pending queue loaded callbacks
238 each(queueLoadedCallbacks, function(callback) {
239 callback.func.call(callback.scope);
242 queueLoadedCallbacks.length = 0;
250 ScriptLoader.ScriptLoader = new ScriptLoader();