]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | |
4 | */ | |
5 | ||
6 | /** | |
7 | * @fileOverview Defines the {@link CKEDITOR.scriptLoader} object, used to load scripts | |
8 | * asynchronously. | |
9 | */ | |
10 | ||
11 | /** | |
12 | * Load scripts asynchronously. | |
13 | * | |
14 | * @class | |
15 | * @singleton | |
16 | */ | |
17 | CKEDITOR.scriptLoader = ( function() { | |
18 | var uniqueScripts = {}, | |
19 | waitingList = {}; | |
20 | ||
21 | return { | |
22 | /** | |
23 | * Loads one or more external script checking if not already loaded | |
24 | * previously by this function. | |
25 | * | |
26 | * CKEDITOR.scriptLoader.load( '/myscript.js' ); | |
27 | * | |
28 | * CKEDITOR.scriptLoader.load( '/myscript.js', function( success ) { | |
29 | * // Alerts true if the script has been properly loaded. | |
30 | * // HTTP error 404 should return false. | |
31 | * alert( success ); | |
32 | * } ); | |
33 | * | |
34 | * CKEDITOR.scriptLoader.load( [ '/myscript1.js', '/myscript2.js' ], function( completed, failed ) { | |
35 | * alert( 'Number of scripts loaded: ' + completed.length ); | |
36 | * alert( 'Number of failures: ' + failed.length ); | |
37 | * } ); | |
38 | * | |
39 | * @param {String/Array} scriptUrl One or more URLs pointing to the | |
40 | * scripts to be loaded. | |
41 | * @param {Function} [callback] A function to be called when the script | |
42 | * is loaded and executed. If a string is passed to `scriptUrl`, a | |
43 | * boolean parameter is passed to the callback, indicating the | |
44 | * success of the load. If an array is passed instead, two arrays | |
45 | * parameters are passed to the callback - the first contains the | |
46 | * URLs that have been properly loaded and the second the failed ones. | |
47 | * @param {Object} [scope] The scope (`this` reference) to be used for | |
48 | * the callback call. Defaults to {@link CKEDITOR}. | |
49 | * @param {Boolean} [showBusy] Changes the cursor of the document while | |
50 | * the script is loaded. | |
51 | */ | |
52 | load: function( scriptUrl, callback, scope, showBusy ) { | |
53 | var isString = ( typeof scriptUrl == 'string' ); | |
54 | ||
55 | if ( isString ) | |
56 | scriptUrl = [ scriptUrl ]; | |
57 | ||
58 | if ( !scope ) | |
59 | scope = CKEDITOR; | |
60 | ||
61 | var scriptCount = scriptUrl.length, | |
62 | completed = [], | |
63 | failed = []; | |
64 | ||
65 | var doCallback = function( success ) { | |
66 | if ( callback ) { | |
67 | if ( isString ) | |
68 | callback.call( scope, success ); | |
69 | else | |
70 | callback.call( scope, completed, failed ); | |
71 | } | |
72 | }; | |
73 | ||
74 | if ( scriptCount === 0 ) { | |
75 | doCallback( true ); | |
76 | return; | |
77 | } | |
78 | ||
79 | var checkLoaded = function( url, success ) { | |
80 | ( success ? completed : failed ).push( url ); | |
81 | ||
82 | if ( --scriptCount <= 0 ) { | |
83 | showBusy && CKEDITOR.document.getDocumentElement().removeStyle( 'cursor' ); | |
84 | doCallback( success ); | |
85 | } | |
86 | }; | |
87 | ||
88 | var onLoad = function( url, success ) { | |
89 | // Mark this script as loaded. | |
90 | uniqueScripts[ url ] = 1; | |
91 | ||
92 | // Get the list of callback checks waiting for this file. | |
93 | var waitingInfo = waitingList[ url ]; | |
94 | delete waitingList[ url ]; | |
95 | ||
96 | // Check all callbacks waiting for this file. | |
97 | for ( var i = 0; i < waitingInfo.length; i++ ) | |
98 | waitingInfo[ i ]( url, success ); | |
99 | }; | |
100 | ||
101 | var loadScript = function( url ) { | |
102 | if ( uniqueScripts[ url ] ) { | |
103 | checkLoaded( url, true ); | |
104 | return; | |
105 | } | |
106 | ||
107 | var waitingInfo = waitingList[ url ] || ( waitingList[ url ] = [] ); | |
108 | waitingInfo.push( checkLoaded ); | |
109 | ||
110 | // Load it only for the first request. | |
111 | if ( waitingInfo.length > 1 ) | |
112 | return; | |
113 | ||
114 | // Create the <script> element. | |
115 | var script = new CKEDITOR.dom.element( 'script' ); | |
116 | script.setAttributes( { | |
117 | type: 'text/javascript', | |
118 | src: url | |
119 | } ); | |
120 | ||
121 | if ( callback ) { | |
122 | if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 ) { | |
123 | // FIXME: For IE, we are not able to return false on error (like 404). | |
124 | script.$.onreadystatechange = function() { | |
125 | if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' ) { | |
126 | script.$.onreadystatechange = null; | |
127 | onLoad( url, true ); | |
128 | } | |
129 | }; | |
130 | } else { | |
131 | script.$.onload = function() { | |
132 | // Some browsers, such as Safari, may call the onLoad function | |
133 | // immediately. Which will break the loading sequence. (#3661) | |
134 | setTimeout( function() { | |
135 | onLoad( url, true ); | |
136 | }, 0 ); | |
137 | }; | |
138 | ||
139 | // FIXME: Opera and Safari will not fire onerror. | |
140 | script.$.onerror = function() { | |
141 | onLoad( url, false ); | |
142 | }; | |
143 | } | |
144 | } | |
145 | ||
146 | // Append it to <head>. | |
147 | script.appendTo( CKEDITOR.document.getHead() ); | |
148 | ||
149 | CKEDITOR.fire( 'download', url ); // %REMOVE_LINE% | |
150 | }; | |
151 | ||
152 | showBusy && CKEDITOR.document.getDocumentElement().setStyle( 'cursor', 'wait' ); | |
153 | for ( var i = 0; i < scriptCount; i++ ) { | |
154 | loadScript( scriptUrl[ i ] ); | |
155 | } | |
156 | }, | |
157 | ||
158 | /** | |
159 | * Loads a script in a queue, so only one is loaded at the same time. | |
160 | * | |
161 | * @since 4.1.2 | |
162 | * @param {String} scriptUrl URL pointing to the script to be loaded. | |
163 | * @param {Function} [callback] A function to be called when the script | |
164 | * is loaded and executed. A boolean parameter is passed to the callback, | |
165 | * indicating the success of the load. | |
166 | * | |
167 | * @see CKEDITOR.scriptLoader#load | |
168 | */ | |
169 | queue: ( function() { | |
170 | var pending = []; | |
171 | ||
172 | // Loads the very first script from queue and removes it. | |
173 | function loadNext() { | |
174 | var script; | |
175 | ||
176 | if ( ( script = pending[ 0 ] ) ) | |
177 | this.load( script.scriptUrl, script.callback, CKEDITOR, 0 ); | |
178 | } | |
179 | ||
180 | return function( scriptUrl, callback ) { | |
181 | var that = this; | |
182 | ||
183 | // This callback calls the standard callback for the script | |
184 | // and loads the very next script from pending list. | |
185 | function callbackWrapper() { | |
186 | callback && callback.apply( this, arguments ); | |
187 | ||
188 | // Removed the just loaded script from the queue. | |
189 | pending.shift(); | |
190 | ||
191 | loadNext.call( that ); | |
192 | } | |
193 | ||
194 | // Let's add this script to the queue | |
195 | pending.push( { scriptUrl: scriptUrl, callback: callbackWrapper } ); | |
196 | ||
197 | // If the queue was empty, then start loading. | |
198 | if ( pending.length == 1 ) | |
199 | loadNext.call( this ); | |
200 | }; | |
201 | } )() | |
202 | }; | |
203 | } )(); |