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.resourceManager} class, which is
  8  *		the base for resource managers, like plugins and themes.
  9  */
 10 
 11  /**
 12  * Base class for resource managers, like plugins and themes. This class is not
 13  * intended to be used out of the CKEditor core code.
 14  * @param {String} basePath The path for the resources folder.
 15  * @param {String} fileName The name used for resource files.
 16  * @namespace
 17  * @example
 18  */
 19 CKEDITOR.resourceManager = function( basePath, fileName )
 20 {
 21 	/**
 22 	 * The base directory containing all resources.
 23 	 * @name CKEDITOR.resourceManager.prototype.basePath
 24 	 * @type String
 25 	 * @example
 26 	 */
 27 	this.basePath = basePath;
 28 
 29 	/**
 30 	 * The name used for resource files.
 31 	 * @name CKEDITOR.resourceManager.prototype.fileName
 32 	 * @type String
 33 	 * @example
 34 	 */
 35 	this.fileName = fileName;
 36 
 37 	/**
 38 	 * Contains references to all resources that have already been registered
 39 	 * with {@link #add}.
 40 	 * @name CKEDITOR.resourceManager.prototype.registered
 41 	 * @type Object
 42 	 * @example
 43 	 */
 44 	this.registered = {};
 45 
 46 	/**
 47 	 * Contains references to all resources that have already been loaded
 48 	 * with {@link #load}.
 49 	 * @name CKEDITOR.resourceManager.prototype.loaded
 50 	 * @type Object
 51 	 * @example
 52 	 */
 53 	this.loaded = {};
 54 
 55 	/**
 56 	 * Contains references to all resources that have already been registered
 57 	 * with {@link #addExternal}.
 58 	 * @name CKEDITOR.resourceManager.prototype.externals
 59 	 * @type Object
 60 	 * @example
 61 	 */
 62 	this.externals = {};
 63 
 64 	/**
 65 	 * @private
 66 	 */
 67 	this._ =
 68 	{
 69 		// List of callbacks waiting for plugins to be loaded.
 70 		waitingList : {}
 71 	};
 72 };
 73 
 74 CKEDITOR.resourceManager.prototype =
 75 {
 76 	/**
 77 	 * Registers a resource.
 78 	 * @param {String} name The resource name.
 79 	 * @param {Object} [definition] The resource definition.
 80 	 * @example
 81 	 * CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
 82 	 * @see CKEDITOR.pluginDefinition
 83 	 */
 84 	add : function( name, definition )
 85 	{
 86 		if ( this.registered[ name ] )
 87 			throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
 88 
 89 		CKEDITOR.fire( name + CKEDITOR.tools.capitalize( this.fileName ) + 'Ready',
 90 				this.registered[ name ] = definition || {} );
 91 	},
 92 
 93 	/**
 94 	 * Gets the definition of a specific resource.
 95 	 * @param {String} name The resource name.
 96 	 * @type Object
 97 	 * @example
 98 	 * var definition = <b>CKEDITOR.plugins.get( 'sample' )</b>;
 99 	 */
100 	get : function( name )
101 	{
102 		return this.registered[ name ] || null;
103 	},
104 
105 	/**
106 	 * Get the folder path for a specific loaded resource.
107 	 * @param {String} name The resource name.
108 	 * @type String
109 	 * @example
110 	 * alert( <b>CKEDITOR.plugins.getPath( 'sample' )</b> );  // "<editor path>/plugins/sample/"
111 	 */
112 	getPath : function( name )
113 	{
114 		var external = this.externals[ name ];
115 		return CKEDITOR.getUrl( ( external && external.dir ) || this.basePath + name + '/' );
116 	},
117 
118 	/**
119 	 * Get the file path for a specific loaded resource.
120 	 * @param {String} name The resource name.
121 	 * @type String
122 	 * @example
123 	 * alert( <b>CKEDITOR.plugins.getFilePath( 'sample' )</b> );  // "<editor path>/plugins/sample/plugin.js"
124 	 */
125 	getFilePath : function( name )
126 	{
127 		var external = this.externals[ name ];
128 		return CKEDITOR.getUrl(
129 				this.getPath( name ) +
130 				( ( external && ( typeof external.file == 'string' ) ) ? external.file : this.fileName + '.js' ) );
131 	},
132 
133 	/**
134 	 * Registers one or more resources to be loaded from an external path
135 	 * instead of the core base path.
136 	 * @param {String} names The resource names, separated by commas.
137 	 * @param {String} path The path of the folder containing the resource.
138 	 * @param {String} [fileName] The resource file name. If not provided, the
139 	 *		default name is used; If provided with a empty string, will implicitly indicates that {@param path}
140 	 * 		is already the full path.
141 	 * @example
142 	 * // Loads a plugin from '/myplugin/samples/plugin.js'.
143 	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
144 	 * @example
145 	 * // Loads a plugin from '/myplugin/samples/my_plugin.js'.
146 	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/', 'my_plugin.js' );
147 	 * @example
148 	 * // Loads a plugin from '/myplugin/samples/my_plugin.js'.
149 	 * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/my_plugin.js', '' );
150 	 */
151 	addExternal : function( names, path, fileName )
152 	{
153 		names = names.split( ',' );
154 		for ( var i = 0 ; i < names.length ; i++ )
155 		{
156 			var name = names[ i ];
157 
158 			this.externals[ name ] =
159 			{
160 				dir : path,
161 				file : fileName
162 			};
163 		}
164 	},
165 
166 	/**
167 	 * Loads one or more resources.
168 	 * @param {String|Array} name The name of the resource to load. It may be a
169 	 *		string with a single resource name, or an array with several names.
170 	 * @param {Function} callback A function to be called when all resources
171 	 *		are loaded. The callback will receive an array containing all
172 	 *		loaded names.
173 	 * @param {Object} [scope] The scope object to be used for the callback
174 	 *		call.
175 	 * @example
176 	 * <b>CKEDITOR.plugins.load</b>( 'myplugin', function( plugins )
177 	 *     {
178 	 *         alert( plugins['myplugin'] );  // "object"
179 	 *     });
180 	 */
181 	load : function( names, callback, scope )
182 	{
183 		// Ensure that we have an array of names.
184 		if ( !CKEDITOR.tools.isArray( names ) )
185 			names = names ? [ names ] : [];
186 
187 		var loaded = this.loaded,
188 			registered = this.registered,
189 			urls = [],
190 			urlsNames = {},
191 			resources = {};
192 
193 		// Loop through all names.
194 		for ( var i = 0 ; i < names.length ; i++ )
195 		{
196 			var name = names[ i ];
197 
198 			if ( !name )
199 				continue;
200 
201 			// If not available yet.
202 			if ( !loaded[ name ] && !registered[ name ] )
203 			{
204 				var url = this.getFilePath( name );
205 				urls.push( url );
206 				if ( !( url in urlsNames ) )
207 					urlsNames[ url ] = [];
208 				urlsNames[ url ].push( name );
209 			}
210 			else
211 				resources[ name ] = this.get( name );
212 		}
213 
214 		CKEDITOR.scriptLoader.load( urls, function( completed, failed )
215 			{
216 				if ( failed.length )
217 				{
218 					throw '[CKEDITOR.resourceManager.load] Resource name "' + urlsNames[ failed[ 0 ] ].join( ',' )
219 						+ '" was not found at "' + failed[ 0 ] + '".';
220 				}
221 
222 				for ( var i = 0 ; i < completed.length ; i++ )
223 				{
224 					var nameList = urlsNames[ completed[ i ] ];
225 					for ( var j = 0 ; j < nameList.length ; j++ )
226 					{
227 						var name = nameList[ j ];
228 						resources[ name ] = this.get( name );
229 
230 						loaded[ name ] = 1;
231 					}
232 				}
233 
234 				callback.call( scope, resources );
235 			}
236 			, this);
237 	}
238 };
239