JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
df6bcdbafd83000310b60be01c4165dfdae3760a
[ckeditor.git] / _source / core / loader.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.loader} objects, which is used to\r
8  *              load core scripts and their dependencies from _source.\r
9  */\r
10 \r
11 if ( typeof CKEDITOR == 'undefined' )\r
12         CKEDITOR = {};\r
13 \r
14 if ( !CKEDITOR.loader )\r
15 {\r
16         /**\r
17          * Load core scripts and their dependencies from _source.\r
18          * @namespace\r
19          * @example\r
20          */\r
21         CKEDITOR.loader = (function()\r
22         {\r
23                 // Table of script names and their dependencies.\r
24                 var scripts =\r
25                 {\r
26                         'core/_bootstrap'               : [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/comment', 'core/dom/elementpath', 'core/dom/text', 'core/dom/rangelist' ],\r
27                         'core/ajax'                             : [ 'core/xml' ],\r
28                         'core/ckeditor'                 : [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/htmlparser/filter', 'core/htmlparser/basicwriter', 'core/tools' ],\r
29                         'core/ckeditor_base'    : [],\r
30                         'core/ckeditor_basic'   : [ 'core/editor_basic', 'core/env', 'core/event' ],\r
31                         'core/command'                  : [],\r
32                         'core/config'                   : [ 'core/ckeditor_base' ],\r
33                         'core/dom'                              : [],\r
34                         'core/dom/comment'              : [ 'core/dom/node' ],\r
35                         'core/dom/document'             : [ 'core/dom', 'core/dom/domobject', 'core/dom/window' ],\r
36                         'core/dom/documentfragment'     : [ 'core/dom/element' ],\r
37                         'core/dom/element'              : [ 'core/dom', 'core/dom/document', 'core/dom/domobject', 'core/dom/node', 'core/dom/nodelist', 'core/tools' ],\r
38                         'core/dom/elementpath'  : [ 'core/dom/element' ],\r
39                         'core/dom/event'                : [],\r
40                         'core/dom/node'                 : [ 'core/dom/domobject', 'core/tools' ],\r
41                         'core/dom/nodelist'             : [ 'core/dom/node' ],\r
42                         'core/dom/domobject'    : [ 'core/dom/event' ],\r
43                         'core/dom/range'                : [ 'core/dom/document', 'core/dom/documentfragment', 'core/dom/element', 'core/dom/walker' ],\r
44                         'core/dom/rangelist'    : [ 'core/dom/range' ],\r
45                         'core/dom/text'                 : [ 'core/dom/node', 'core/dom/domobject' ],\r
46                         'core/dom/walker'               : [ 'core/dom/node' ],\r
47                         'core/dom/window'               : [ 'core/dom/domobject' ],\r
48                         'core/dtd'                              : [ 'core/tools' ],\r
49                         'core/editor'                   : [ 'core/command', 'core/config', 'core/editor_basic', 'core/focusmanager', 'core/lang', 'core/plugins', 'core/skins', 'core/themes', 'core/tools', 'core/ui' ],\r
50                         'core/editor_basic'             : [ 'core/event' ],\r
51                         'core/env'                              : [],\r
52                         'core/event'                    : [],\r
53                         'core/focusmanager'             : [],\r
54                         'core/htmlparser'               : [],\r
55                         'core/htmlparser/comment'       : [ 'core/htmlparser' ],\r
56                         'core/htmlparser/element'       : [ 'core/htmlparser', 'core/htmlparser/fragment' ],\r
57                         'core/htmlparser/fragment'      : [ 'core/htmlparser', 'core/htmlparser/comment', 'core/htmlparser/text', 'core/htmlparser/cdata' ],\r
58                         'core/htmlparser/text'          : [ 'core/htmlparser' ],\r
59                         'core/htmlparser/cdata'         : [ 'core/htmlparser' ],\r
60                         'core/htmlparser/filter'        : [ 'core/htmlparser' ],\r
61                         'core/htmlparser/basicwriter': [ 'core/htmlparser' ],\r
62                         'core/imagecacher'              : [ 'core/dom/element' ],\r
63                         'core/lang'                             : [],\r
64                         'core/plugins'                  : [ 'core/resourcemanager' ],\r
65                         'core/resourcemanager'  : [ 'core/scriptloader', 'core/tools' ],\r
66                         'core/scriptloader'             : [ 'core/dom/element', 'core/env' ],\r
67                         'core/skins'                    : [ 'core/imagecacher', 'core/scriptloader' ],\r
68                         'core/themes'                   : [ 'core/resourcemanager' ],\r
69                         'core/tools'                    : [ 'core/env' ],\r
70                         'core/ui'                               : [],\r
71                         'core/xml'                              : [ 'core/env' ]\r
72                 };\r
73 \r
74                 var basePath = (function()\r
75                 {\r
76                         // This is a copy of CKEDITOR.basePath, but requires the script having\r
77                         // "_source/core/loader.js".\r
78                         if ( CKEDITOR && CKEDITOR.basePath )\r
79                                 return CKEDITOR.basePath;\r
80 \r
81                         // Find out the editor directory path, based on its <script> tag.\r
82                         var path = '';\r
83                         var scripts = document.getElementsByTagName( 'script' );\r
84 \r
85                         for ( var i = 0 ; i < scripts.length ; i++ )\r
86                         {\r
87                                 var match = scripts[i].src.match( /(^|.*[\\\/])core\/loader.js(?:\?.*)?$/i );\r
88 \r
89                                 if ( match )\r
90                                 {\r
91                                         path = match[1];\r
92                                         break;\r
93                                 }\r
94                         }\r
95 \r
96                         // In IE (only) the script.src string is the raw valued entered in the\r
97                         // HTML. Other browsers return the full resolved URL instead.\r
98                         if ( path.indexOf('://') == -1 )\r
99                         {\r
100                                 // Absolute path.\r
101                                 if ( path.indexOf( '/' ) === 0 )\r
102                                         path = location.href.match( /^.*?:\/\/[^\/]*/ )[0] + path;\r
103                                 // Relative path.\r
104                                 else\r
105                                         path = location.href.match( /^[^\?]*\// )[0] + path;\r
106                         }\r
107 \r
108                         return path;\r
109                 })();\r
110 \r
111                 var timestamp = 'A73H4HC';\r
112 \r
113                 var getUrl = function( resource )\r
114                 {\r
115                         if ( CKEDITOR && CKEDITOR.getUrl )\r
116                                 return CKEDITOR.getUrl( resource );\r
117 \r
118                         return basePath + resource +\r
119                                 ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) +\r
120                                 't=' + timestamp;\r
121                 };\r
122 \r
123                 var pendingLoad = [];\r
124 \r
125                 /** @lends CKEDITOR.loader */\r
126                 return {\r
127                         /**\r
128                          * The list of loaded scripts in their loading order.\r
129                          * @type Array\r
130                          * @example\r
131                          * // Alert the loaded script names.\r
132                          * alert( <b>CKEDITOR.loader.loadedScripts</b> );\r
133                          */\r
134                         loadedScripts : [],\r
135 \r
136                         loadPending : function()\r
137                         {\r
138                                 var scriptName = pendingLoad.shift();\r
139 \r
140                                 if ( !scriptName )\r
141                                         return;\r
142 \r
143                                 var scriptSrc = getUrl( '_source/' + scriptName + '.js' );\r
144 \r
145                                 var script = document.createElement( 'script' );\r
146                                 script.type = 'text/javascript';\r
147                                 script.src = scriptSrc;\r
148 \r
149                                 function onScriptLoaded()\r
150                                 {\r
151                                         // Append this script to the list of loaded scripts.\r
152                                         CKEDITOR.loader.loadedScripts.push( scriptName );\r
153 \r
154                                         // Load the next.\r
155                                         CKEDITOR.loader.loadPending();\r
156                                 }\r
157 \r
158                                 // We must guarantee the execution order of the scripts, so we\r
159                                 // need to load them one by one. (#4145)\r
160                                 // The following if/else block has been taken from the scriptloader core code.\r
161                                 if ( typeof(script.onreadystatechange) !== "undefined" )\r
162                                 {\r
163                                         /** @ignore */\r
164                                         script.onreadystatechange = function()\r
165                                         {\r
166                                                 if ( script.readyState == 'loaded' || script.readyState == 'complete' )\r
167                                                 {\r
168                                                         script.onreadystatechange = null;\r
169                                                         onScriptLoaded();\r
170                                                 }\r
171                                         };\r
172                                 }\r
173                                 else\r
174                                 {\r
175                                         /** @ignore */\r
176                                         script.onload = function()\r
177                                         {\r
178                                                 // Some browsers, such as Safari, may call the onLoad function\r
179                                                 // immediately. Which will break the loading sequence. (#3661)\r
180                                                 setTimeout( function() { onScriptLoaded( scriptName ); }, 0 );\r
181                                         };\r
182                                 }\r
183 \r
184                                 document.body.appendChild( script );\r
185                         },\r
186 \r
187                         /**\r
188                          * Loads a specific script, including its dependencies. This is not a\r
189                          * synchronous loading, which means that the code to be loaded will\r
190                          * not necessarily be available after this call.\r
191                          * @example\r
192                          * CKEDITOR.loader.load( 'core/dom/element' );\r
193                          */\r
194                         load : function( scriptName, defer )\r
195                         {\r
196                                 // Check if the script has already been loaded.\r
197                                 if ( scriptName in this.loadedScripts )\r
198                                         return;\r
199 \r
200                                 // Get the script dependencies list.\r
201                                 var dependencies = scripts[ scriptName ];\r
202                                 if ( !dependencies )\r
203                                         throw 'The script name"' + scriptName + '" is not defined.';\r
204 \r
205                                 // Mark the script as loaded, even before really loading it, to\r
206                                 // avoid cross references recursion.\r
207                                 this.loadedScripts[ scriptName ] = true;\r
208 \r
209                                 // Load all dependencies first.\r
210                                 for ( var i = 0 ; i < dependencies.length ; i++ )\r
211                                         this.load( dependencies[ i ], true );\r
212 \r
213                                 var scriptSrc = getUrl( '_source/' + scriptName + '.js' );\r
214 \r
215                                 // Append the <script> element to the DOM.\r
216                                 // If the page is fully loaded, we can't use document.write\r
217                                 // but if the script is run while the body is loading then it's safe to use it\r
218                                 // Unfortunately, Firefox <3.6 doesn't support document.readyState, so it won't get this improvement\r
219                                 if ( document.body && (!document.readyState || document.readyState == 'complete') )\r
220                                 {\r
221                                         pendingLoad.push( scriptName );\r
222 \r
223                                         if ( !defer )\r
224                                                 this.loadPending();\r
225                                 }\r
226                                 else\r
227                                 {\r
228                                         // Append this script to the list of loaded scripts.\r
229                                         this.loadedScripts.push( scriptName );\r
230 \r
231                                         document.write( '<script src="' + scriptSrc + '" type="text/javascript"><\/script>' );\r
232                                 }\r
233                         }\r
234                 };\r
235         })();\r
236 }\r
237 \r
238 // Check if any script has been defined for autoload.\r
239 if ( CKEDITOR._autoLoad )\r
240 {\r
241         CKEDITOR.loader.load( CKEDITOR._autoLoad );\r
242         delete CKEDITOR._autoLoad;\r
243 }\r