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