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