1 /*
  2  * CKEditor - The text editor for Internet - http://ckeditor.com
  3  * Copyright (C) 2003-2008 Frederico Caldeira Knabben
  4  *
  5  * == BEGIN LICENSE ==
  6  *
  7  * Licensed under the terms of any of the following licenses at your
  8  * choice:
  9  *
 10  *  - GNU General Public License Version 2 or later (the "GPL")
 11  *    http://www.gnu.org/licenses/gpl.html
 12  *
 13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 14  *    http://www.gnu.org/licenses/lgpl.html
 15  *
 16  *  - Mozilla Public License Version 1.1 or later (the "MPL")
 17  *    http://www.mozilla.org/MPL/MPL-1.1.html
 18  *
 19  * == END LICENSE ==
 20  */
 21
 22 /**
 23  * @fileOverview Defines the {@link CKEDITOR.resourceManager} class, which is
 24  *		the base for resource managers, like plugins and themes.
 25  */
 26
 27  /**
 28  * Base class for resource managers, like plugins and themes. This class is not
 29  * intended to be used out of the CKEditor core code.
 30  * @param {String} basePath The path for the resources folder.
 31  * @param {String} fileName The name used for resource files.
 32  * @namespace
 33  * @example
 34  */
 35 CKEDITOR.resourceManager = function( basePath, fileName )
 36 {
 37 	/**
 38 	 * The base directory containing all resources.
 39 	 * @name CKEDITOR.resourceManager.prototype.basePath
 40 	 * @type String
 41 	 * @example
 42 	 */
 43 	this.basePath = basePath;
 44
 45 	/**
 46 	 * The name used for resource files.
 47 	 * @name CKEDITOR.resourceManager.prototype.fileName
 48 	 * @type String
 49 	 * @example
 50 	 */
 51 	this.fileName = fileName;
 52
 53 	/**
 54 	 * @private
 55 	 */
 56 	this._ =
 57 	{
 58 		// List of callbacks waiting for plugins to be loaded.
 59 		waitingList : {}
 60 	};
 61 };
 62
 63 CKEDITOR.resourceManager.prototype =
 64 {
 65 	/**
 66 	 * Contains references to all resources that have already been registered
 67 	 * with {@link #add}.
 68 	 * @type Object
 69 	 * @example
 70 	 */
 71 	registered : {},
 72
 73 	/**
 74 	 * Contains references to all resources that have already been loaded
 75 	 * with {@link #load}.
 76 	 * @type Object
 77 	 * @example
 78 	 */
 79 	loaded : {},
 80
 81 	/**
 82 	 * Contains references to all resources that have already been registered
 83 	 * with {@link #addExternal}.
 84 	 * @type Object
 85 	 * @example
 86 	 */
 87 	externals : {},
 88
 89 	/**
 90 	 * Registers a resource.
 91 	 * @param {String} name The resource name.
 92 	 * @param {Object} definition The resource definition.
 93 	 * @type undefined
 94 	 * @example
 95 	 * CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
 96 	 * @see CKEDITOR.pluginDefinition
 97 	 */
 98 	add : function( name, definition )
 99 	{
100 		if ( this.registered[ name ] )
101 			throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
102
103 		this.registered[ name ] = definition;
104 	},
105
106 	/**
107 	 * Gets the definition of a specific resource.
108 	 * @param {String} name The resource name.
109 	 * @type Object
110 	 * @example
111 	 * var definition = <b>CKEDITOR.plugins.get( 'sample' )</b>;
112 	 */
113 	get : function( name )
114 	{
115 		return this.registered[ name ] || null;
116 	},
117
118 	/**
119 	 * Get the full path for a specific loaded resource.
120 	 * @param {String} name The resource name.
121 	 * @type String
122 	 * @example
123 	 * alert( <b>CKEDITOR.plugins.getPath( 'sample' )</b> );  // "<editor path>/plugins/sample/"
124 	 */
125 	getPath : function( name )
126 	{
127 		return this.loaded[ name ] || null;
128 	},
129
130 	/**
131 	 * Registers a resource to be loaded from an external path instead of the core base path.
132 	 * @param {String} name The resource name.
133 	 * @param {String} path The resource external path.
134 	 * @type undefined
135 	 * @example
136 	 * // Loads a plugin from '/myplugin/samples/plugin.js'.
137 	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
138 	 */
139 	addExternal : function( name, path )
140 	{
141 		if ( this.registered[ name ] || this.externals[ name ] )
142 			throw '[CKEDITOR.resourceManager.import] The resource name "' + name + '" is already registered or imported.';
143
144 		this.externals[ name ] = path;
145 	},
146
147 	/**
148 	 * Loads one or more resources.
149 	 * @param {String|Array} name The name of the resource to load. It may be a
150 	 *		string with a single resource name, or an array with several names.
151 	 * @param {Function} callback A function to be called when all resources
152 	 *		are loaded. The callback will receive an array containing all
153 	 *		loaded names.
154 	 * @param {Object} [scope] The scope object to be used for the callback
155 	 *		call.
156 	 * @type undefined
157 	 * @example
158 	 * <b>CKEDITOR.plugins.load</b>( 'myplugin', function( plugins )
159 	 *     {
160 	 *         alert( plugins[0] );  // "myplugin"
161 	 *     });
162 	 */
163 	load : function( name, callback, scope )
164 	{
165 		// Ensure that we have an Array of names.
166 		var names = CKEDITOR.tools.isArray( name ) ? name : [ name ];
167
168 		var total = names.length;
169
170 		// Nothing to load, just call the callback.
171 		if ( !total )
172 		{
173 			callback.call( scope || window, names );
174 			return;
175 		}
176
177 		// This function is used to count the loaded plugins and call the
178 		// callback when finished loading.
179 		callback._loaded = 0;
180 		callback._total = total;
181 		var loadCheck = function( callback )
182 		{
183 			if ( ++callback._loaded == callback._total )
184 				callback.call( scope || window, names );
185 		};
186
187 		var loaded = this.loaded;
188 		var waitingList = this._waitingList;
189
190 		// Loop through all names.
191 		for ( var i = 0 ; i < names.length ; i++ )
192 		{
193 			name = names[ i ];
194
195 			// If not loaded already.
196 			if ( name && !loaded[ name ] && !this.registered[ name ] )
197 			{
198 				var waitingInfo = waitingList[ name ] || ( waitingList[ name ] = [] );
199 				waitingInfo.push( callback );
200
201 				// If this is the first call for it, go ahead loading.
202 				if ( waitingInfo.length == 1 )
203 				{
204 					// Calculate the plugin script path.
205 					var path = this.externals[ name ] || ( this.basePath + name + '/' );
206
207 					// Load the plugin script.
208 					CKEDITOR.scriptLoader.load( path + this.fileName + '.js', function( success )
209 						{
210 							if ( !success )
211 								throw '[CKEDITOR.resourceManager.load] Resource name "' + name + '" was not found at "' + path + this.fileName + '.js".';
212
213 							loaded[ name ] = path;
214
215 							// Check all callbacks that were waiting for this
216 							// resource.
217 							for ( var j = 0 ; j < waitingInfo.length ; j++ )
218 								loadCheck( waitingInfo[ j ] );
219
220 							delete waitingList[ name ];
221 						});
222 				}
223 			}
224 			else
225 				loadCheck( callback );
226 		}
227 	}
228 };
229