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.skins} object, which is used to
  8  *		manage skins loading.
  9  */
 10 
 11 /**
 12  * Manages skins loading.
 13  * @namespace
 14  * @example
 15  */
 16 CKEDITOR.skins = (function()
 17 {
 18 	// Holds the list of loaded skins.
 19 	var loaded = {},
 20 		paths = {};
 21 
 22 	var loadPart = function( editor, skinName, part, callback )
 23 	{
 24 		// Get the skin definition.
 25 		var skinDefinition = loaded[ skinName ];
 26 
 27 		if ( !editor.skin )
 28 		{
 29 			editor.skin = skinDefinition;
 30 
 31 			// Trigger init function if any.
 32 			if ( skinDefinition.init )
 33 				skinDefinition.init( editor );
 34 		}
 35 
 36 		var appendSkinPath = function( fileNames )
 37 		{
 38 			for ( var n = 0 ; n < fileNames.length ; n++ )
 39 			{
 40 				fileNames[ n ] = CKEDITOR.getUrl( paths[ skinName ] + fileNames[ n ] );
 41 			}
 42 		};
 43 
 44 		function fixCSSTextRelativePath( cssStyleText, baseUrl )
 45 		{
 46 			return cssStyleText.replace( /url\s*\(([\s'"]*)(.*?)([\s"']*)\)/g,
 47 					function( match, opener, path, closer )
 48 					{
 49 						if ( /^\/|^\w?:/.test( path ) )
 50 							return match;
 51 						else
 52 							return 'url(' + baseUrl + opener +  path + closer + ')';
 53 					} );
 54 		}
 55 
 56 		// Get the part definition.
 57 		part = skinDefinition[ part ];
 58 		var partIsLoaded = !part || !!part._isLoaded;
 59 
 60 		// Call the callback immediately if already loaded.
 61 		if ( partIsLoaded )
 62 			callback && callback();
 63 		else
 64 		{
 65 			// Put the callback in a queue.
 66 			var pending = part._pending || ( part._pending = [] );
 67 			pending.push( callback );
 68 
 69 			// We may have more than one skin part load request. Just the first
 70 			// one must do the loading job.
 71 			if ( pending.length > 1 )
 72 				return;
 73 
 74 			// Check whether the "css" and "js" properties have been defined
 75 			// for that part.
 76 			var cssIsLoaded = !part.css || !part.css.length,
 77 				jsIsLoaded = !part.js || !part.js.length;
 78 
 79 			// This is the function that will trigger the callback calls on
 80 			// load.
 81 			var checkIsLoaded = function()
 82 			{
 83 				if ( cssIsLoaded && jsIsLoaded )
 84 				{
 85 					// Mark the part as loaded.
 86 					part._isLoaded = 1;
 87 
 88 					// Call all pending callbacks.
 89 					for ( var i = 0 ; i < pending.length ; i++ )
 90 					{
 91 						if ( pending[ i ] )
 92 							pending[ i ]();
 93 					}
 94 				}
 95 			};
 96 
 97 			// Load the "css" pieces.
 98 			if ( !cssIsLoaded )
 99 			{
100 				var cssPart = part.css;
101 
102 				if ( CKEDITOR.tools.isArray( cssPart ) )
103 				{
104 					appendSkinPath( cssPart );
105 					for ( var c = 0 ; c < cssPart.length ; c++ )
106 						CKEDITOR.document.appendStyleSheet( cssPart[ c ] );
107 				}
108 				else
109 				{
110 					cssPart = fixCSSTextRelativePath(
111 								cssPart, CKEDITOR.getUrl( paths[ skinName ] ) );
112 					// Processing Inline CSS part.
113 					CKEDITOR.document.appendStyleText( cssPart );
114 				}
115 
116 				part.css = cssPart;
117 
118 				cssIsLoaded = 1;
119 			}
120 
121 			// Load the "js" pieces.
122 			if ( !jsIsLoaded )
123 			{
124 				appendSkinPath( part.js );
125 				CKEDITOR.scriptLoader.load( part.js, function()
126 					{
127 						jsIsLoaded = 1;
128 						checkIsLoaded();
129 					});
130 			}
131 
132 			// We may have nothing to load, so check it immediately.
133 			checkIsLoaded();
134 		}
135 	};
136 
137 	return /** @lends CKEDITOR.skins */ {
138 
139 		/**
140 		 * Registers a skin definition.
141 		 * @param {String} skinName The skin name.
142 		 * @param {Object} skinDefinition The skin definition.
143 		 * @example
144 		 */
145 		add : function( skinName, skinDefinition )
146 		{
147 			loaded[ skinName ] = skinDefinition;
148 
149 			skinDefinition.skinPath = paths[ skinName ]
150 				|| ( paths[ skinName ] =
151 						CKEDITOR.getUrl(
152 							'_source/' +	// @Packager.RemoveLine
153 							'skins/' + skinName + '/' ) );
154 		},
155 
156 		/**
157 		 * Loads a skin part. Skins are defined in parts, which are basically
158 		 * separated CSS files. This function is mainly used by the core code and
159 		 * should not have much use out of it.
160 		 * @param {String} skinName The name of the skin to be loaded.
161 		 * @param {String} skinPart The skin part to be loaded. Common skin parts
162 		 *		are "editor" and "dialog".
163 		 * @param {Function} [callback] A function to be called once the skin
164 		 *		part files are loaded.
165 		 * @example
166 		 */
167 		load : function( editor, skinPart, callback )
168 		{
169 			var skinName = editor.skinName,
170 				skinPath = editor.skinPath;
171 
172 			if ( loaded[ skinName ] )
173 				loadPart( editor, skinName, skinPart, callback );
174 			else
175 			{
176 				paths[ skinName ] = skinPath;
177 				CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( skinPath + 'skin.js' ), function()
178 						{
179 							 loadPart( editor, skinName, skinPart, callback );
180 						});
181 			}
182 		}
183 	};
184 })();
185