JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
6e7884b514dcde1bbd67d23081f836f0d77f6662
[ckeditor.git] / _source / core / scriptloader.js
1 /*\r
2 Copyright (c) 2003-2009, 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 )\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                                         doCallback( success );\r
91                         };\r
92 \r
93                         var onLoad = function( url, success )\r
94                         {\r
95                                 // Mark this script as loaded.\r
96                                 uniqueScripts[ url ] = 1;\r
97 \r
98                                 // Get the list of callback checks waiting for this file.\r
99                                 var waitingInfo = waitingList[ url ];\r
100                                 delete waitingList[ url ];\r
101 \r
102                                 // Check all callbacks waiting for this file.\r
103                                 for ( var i = 0 ; i < waitingInfo.length ; i++ )\r
104                                         waitingInfo[ i ]( url, success );\r
105                         };\r
106 \r
107                         var loadScript = function( url )\r
108                         {\r
109                                 if ( noCheck !== true && uniqueScripts[ url ] )\r
110                                 {\r
111                                         checkLoaded( url, true );\r
112                                         return;\r
113                                 }\r
114 \r
115                                 var waitingInfo = waitingList[ url ] || ( waitingList[ url ] = [] );\r
116                                 waitingInfo.push( checkLoaded );\r
117 \r
118                                 // Load it only for the first request.\r
119                                 if ( waitingInfo.length > 1 )\r
120                                         return;\r
121 \r
122                                 // Create the <script> element.\r
123                                 var script = new CKEDITOR.dom.element( 'script' );\r
124                                 script.setAttributes( {\r
125                                         type : 'text/javascript',\r
126                                         src : url } );\r
127 \r
128                                 if ( callback )\r
129                                 {\r
130                                         if ( CKEDITOR.env.ie )\r
131                                         {\r
132                                                 // FIXME: For IE, we are not able to return false on error (like 404).\r
133 \r
134                                                 /** @ignore */\r
135                                                 script.$.onreadystatechange = function ()\r
136                                                 {\r
137                                                         if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' )\r
138                                                         {\r
139                                                                 script.$.onreadystatechange = null;\r
140                                                                 onLoad( url, true );\r
141                                                         }\r
142                                                 };\r
143                                         }\r
144                                         else\r
145                                         {\r
146                                                 /** @ignore */\r
147                                                 script.$.onload = function()\r
148                                                 {\r
149                                                         // Some browsers, such as Safari, may call the onLoad function\r
150                                                         // immediately. Which will break the loading sequence. (#3661)\r
151                                                         setTimeout( function() { onLoad( url, true ); }, 0 );\r
152                                                 };\r
153 \r
154                                                 // FIXME: Opera and Safari will not fire onerror.\r
155 \r
156                                                 /** @ignore */\r
157                                                 script.$.onerror = function()\r
158                                                 {\r
159                                                         onLoad( url, false );\r
160                                                 };\r
161                                         }\r
162                                 }\r
163 \r
164                                 // Append it to <head>.\r
165                                 script.appendTo( CKEDITOR.document.getHead() );\r
166 \r
167                                 CKEDITOR.fire( 'download', url );               // @Packager.RemoveLine\r
168                         };\r
169 \r
170                         for ( var i = 0 ; i < scriptCount ; i++ )\r
171                         {\r
172                                 loadScript( scriptUrl[ i ] );\r
173                         }\r
174                 },\r
175 \r
176                 /**\r
177                  * Executes a JavaScript code into the current document.\r
178                  * @param {String} code The code to be executed.\r
179                  * @example\r
180                  * CKEDITOR.scriptLoader.loadCode( 'var x = 10;' );\r
181                  * alert( x );  // "10"\r
182                  */\r
183                 loadCode : function( code )\r
184                 {\r
185                         // Create the <script> element.\r
186                         var script = new CKEDITOR.dom.element( 'script' );\r
187                         script.setAttribute( 'type', 'text/javascript' );\r
188                         script.appendText( code );\r
189 \r
190                         // Append it to <head>.\r
191                         script.appendTo( CKEDITOR.document.getHead() );\r
192                 }\r
193         };\r
194 })();\r