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