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 	 * Contains references to all resources that have already been registered
 55 	 * with {@link #add}.
 56 	 * @name CKEDITOR.resourceManager.prototype.registered
 57 	 * @type Object
 58 	 * @example
 59 	 */
 60 	this.registered = {};
 61
 62 	/**
 63 	 * Contains references to all resources that have already been loaded
 64 	 * with {@link #load}.
 65 	 * @name CKEDITOR.resourceManager.prototype.loaded
 66 	 * @type Object
 67 	 * @example
 68 	 */
 69 	this.loaded = {};
 70
 71 	/**
 72 	 * Contains references to all resources that have already been registered
 73 	 * with {@link #addExternal}.
 74 	 * @name CKEDITOR.resourceManager.prototype.externals
 75 	 * @type Object
 76 	 * @example
 77 	 */
 78 	this.externals = {};
 79
 80 	/**
 81 	 * @private
 82 	 */
 83 	this._ =
 84 	{
 85 		// List of callbacks waiting for plugins to be loaded.
 86 		waitingList : {}
 87 	};
 88 };
 89
 90 CKEDITOR.resourceManager.prototype =
 91 {
 92 	/**
 93 	 * Registers a resource.
 94 	 * @param {String} name The resource name.
 95 	 * @param {Object} [definition] The resource definition.
 96 	 * @example
 97 	 * CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
 98 	 * @see CKEDITOR.pluginDefinition
 99 	 */
100 	add : function( name, definition )
101 	{
102 		if ( this.registered[ name ] )
103 			throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
104
105 		this.registered[ name ] = definition || {};
106 	},
107
108 	/**
109 	 * Gets the definition of a specific resource.
110 	 * @param {String} name The resource name.
111 	 * @type Object
112 	 * @example
113 	 * var definition = <b>CKEDITOR.plugins.get( 'sample' )</b>;
114 	 */
115 	get : function( name )
116 	{
117 		return this.registered[ name ] || null;
118 	},
119
120 	/**
121 	 * Get the full path for a specific loaded resource.
122 	 * @param {String} name The resource name.
123 	 * @type String
124 	 * @example
125 	 * alert( <b>CKEDITOR.plugins.getPath( 'sample' )</b> );  // "<editor path>/plugins/sample/"
126 	 */
127 	getPath : function( name )
128 	{
129 		return this.loaded[ name ] || null;
130 	},
131
132 	/**
133 	 * Registers a resource to be loaded from an external path instead of the core base path.
134 	 * @param {String} name The resource name.
135 	 * @param {String} path The resource external path.
136 	 * @example
137 	 * // Loads a plugin from '/myplugin/samples/plugin.js'.
138 	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
139 	 */
140 	addExternal : function( name, path )
141 	{
142 		if ( this.registered[ name ] || this.externals[ name ] )
143 			throw '[CKEDITOR.resourceManager.import] The resource name "' + name + '" is already registered or imported.';
144
145 		this.externals[ name ] = path;
146 	},
147
148 	/**
149 	 * Loads one or more resources.
150 	 * @param {String|Array} name The name of the resource to load. It may be a
151 	 *		string with a single resource name, or an array with several names.
152 	 * @param {Function} callback A function to be called when all resources
153 	 *		are loaded. The callback will receive an array containing all
154 	 *		loaded names.
155 	 * @param {Object} [scope] The scope object to be used for the callback
156 	 *		call.
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 		var total = names.length;
168
169 		// Nothing to load, just call the callback.
170 		if ( !total )
171 		{
172 			callback.call( scope || window, names );
173 			return;
174 		}
175
176 		// This function is used to count the loaded plugins and call the
177 		// callback when finished loading.
178 		callback._loaded = 0;
179 		callback._total = total;
180 		var loadCheck = function( callback )
181 		{
182 			if ( ++callback._loaded == callback._total )
183 				callback.call( scope || window, names );
184 		};
185
186 		var loaded = this.loaded,
187 			waitingList = this._.waitingList;
188
189 		var loadPlugin = function( name )
190 		{
191 			// Calculate the plugin script path.
192 			var path = this.externals[ name ] || ( this.basePath + name + '/' );
193 			var filePath = CKEDITOR.getUrl( path + this.fileName + '.js' );
194
195 			// Load the plugin script.
196 			CKEDITOR.scriptLoader.load( filePath, function( success )
197 				{
198 					if ( !success )
199 						throw '[CKEDITOR.resourceManager.load] Resource name "' + name + '" was not found at "' + filePath + '".';
200
201 					loaded[ name ] = path;
202
203 					// Check all callbacks that were waiting for this
204 					// resource.
205 					for ( var j = 0 ; j < waitingInfo.length ; j++ )
206 						loadCheck( waitingInfo[ j ] );
207
208 					delete waitingList[ name ];
209 				});
210 		};
211
212 		// Loop through all names.
213 		for ( var i = 0 ; i < names.length ; i++ )
214 		{
215 			name = names[ i ];
216
217 			// If not loaded already.
218 			if ( name && !loaded[ name ] && !this.registered[ name ] )
219 			{
220 				var waitingInfo = waitingList[ name ] || ( waitingList[ name ] = [] );
221 				waitingInfo.push( callback );
222
223 				// If this is the first call for it, go ahead loading.
224 				if ( waitingInfo.length == 1 )
225 					loadPlugin.call( this, name );
226 			}
227 			else
228 				loadCheck( callback );
229 		}
230 	}
231 };
232