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.tools} object, which contains
  8  *		utility functions.
  9  */
 10 
 11 (function()
 12 {
 13 	var functions = [];
 14 
 15 	CKEDITOR.on( 'reset', function()
 16 		{
 17 			functions = [];
 18 		});
 19 
 20 	/**
 21 	 * Utility functions.
 22 	 * @namespace
 23 	 * @example
 24 	 */
 25 	CKEDITOR.tools =
 26 	{
 27 		/**
 28 		 * Compare the elements of two arrays.
 29 		 * @param {Array} arrayA An array to be compared.
 30 		 * @param {Array} arrayB The other array to be compared.
 31 		 * @returns {Boolean} "true" is the arrays have the same lenght and
 32 		 *		their elements match.
 33 		 * @example
 34 		 * var a = [ 1, 'a', 3 ];
 35 		 * var b = [ 1, 3, 'a' ];
 36 		 * var c = [ 1, 'a', 3 ];
 37 		 * var d = [ 1, 'a', 3, 4 ];
 38 		 *
 39 		 * alert( CKEDITOR.tools.arrayCompare( a, b ) );  // false
 40 		 * alert( CKEDITOR.tools.arrayCompare( a, c ) );  // true
 41 		 * alert( CKEDITOR.tools.arrayCompare( a, d ) );  // false
 42 		 */
 43 		arrayCompare : function( arrayA, arrayB )
 44 		{
 45 			if ( !arrayA && !arrayB )
 46 				return true;
 47 
 48 			if ( !arrayA || !arrayB || arrayA.length != arrayB.length )
 49 				return false;
 50 
 51 			for ( var i = 0 ; i < arrayA.length ; i++ )
 52 			{
 53 				if ( arrayA[ i ] != arrayB[ i ] )
 54 					return false;
 55 			}
 56 
 57 			return true;
 58 		},
 59 
 60 		/**
 61 		 * Creates a deep copy of an object.
 62 		 * Attention: there is no support for recursive references.
 63 		 * @param {Object} object The object to be cloned.
 64 		 * @returns {Object} The object clone.
 65 		 * @example
 66 		 * var obj =
 67 		 *     {
 68 		 *         name : 'John',
 69 		 *         cars :
 70 		 *             {
 71 		 *                 Mercedes : { color : 'blue' },
 72 		 *                 Porsche : { color : 'red' }
 73 		 *             }
 74 		 *     };
 75 		 * var clone = CKEDITOR.tools.clone( obj );
 76 		 * clone.name = 'Paul';
 77 		 * clone.cars.Porsche.color = 'silver';
 78 		 * alert( obj.name );	// John
 79 		 * alert( clone.name );	// Paul
 80 		 * alert( obj.cars.Porsche.color );	// red
 81 		 * alert( clone.cars.Porsche.color );	// silver
 82 		 */
 83 		clone : function( obj )
 84 		{
 85 			var clone;
 86 
 87 			// Array.
 88 			if ( obj && ( obj instanceof Array ) )
 89 			{
 90 				clone = [];
 91 
 92 				for ( var i = 0 ; i < obj.length ; i++ )
 93 					clone[ i ] = this.clone( obj[ i ] );
 94 
 95 				return clone;
 96 			}
 97 
 98 			// "Static" types.
 99 			if ( obj === null
100 				|| ( typeof( obj ) != 'object' )
101 				|| ( obj instanceof String )
102 				|| ( obj instanceof Number )
103 				|| ( obj instanceof Boolean )
104 				|| ( obj instanceof Date )
105 				|| ( obj instanceof RegExp) )
106 			{
107 				return obj;
108 			}
109 
110 			// Objects.
111 			clone = new obj.constructor();
112 
113 			for ( var propertyName in obj )
114 			{
115 				var property = obj[ propertyName ];
116 				clone[ propertyName ] = this.clone( property );
117 			}
118 
119 			return clone;
120 		},
121 
122 		/**
123 		 * Turn the first letter of string to upper-case.
124 		 * @param {String} str
125 		 */
126 		capitalize: function( str )
127 		{
128 			return str.charAt( 0 ).toUpperCase() + str.substring( 1 ).toLowerCase();
129 		},
130 
131 		/**
132 		 * Copy the properties from one object to another. By default, properties
133 		 * already present in the target object <strong>are not</strong> overwritten.
134 		 * @param {Object} target The object to be extended.
135 		 * @param {Object} source[,souce(n)] The objects from which copy
136 		 *		properties. Any number of objects can be passed to this function.
137 		 * @param {Boolean} [overwrite] If 'true' is specified it indicates that
138 		 *            properties already present in the target object could be
139 		 *            overwritten by subsequent objects.
140 		 * @param {Object} [properties] Only properties within the specified names
141 		 *            list will be received from the source object.
142 		 * @returns {Object} the extended object (target).
143 		 * @example
144 		 * // Create the sample object.
145 		 * var myObject =
146 		 * {
147 		 *     prop1 : true
148 		 * };
149 		 *
150 		 * // Extend the above object with two properties.
151 		 * CKEDITOR.tools.extend( myObject,
152 		 *     {
153 		 *         prop2 : true,
154 		 *         prop3 : true
155 		 *     } );
156 		 *
157 		 * // Alert "prop1", "prop2" and "prop3".
158 		 * for ( var p in myObject )
159 		 *     alert( p );
160 		 */
161 		extend : function( target )
162 		{
163 			var argsLength = arguments.length,
164 				overwrite, propertiesList;
165 
166 			if ( typeof ( overwrite = arguments[ argsLength - 1 ] ) == 'boolean')
167 				argsLength--;
168 			else if ( typeof ( overwrite = arguments[ argsLength - 2 ] ) == 'boolean' )
169 			{
170 				propertiesList = arguments [ argsLength -1 ];
171 				argsLength-=2;
172 			}
173 			for ( var i = 1 ; i < argsLength ; i++ )
174 			{
175 				var source = arguments[ i ];
176 				for ( var propertyName in source )
177 				{
178 					// Only copy existed fields if in overwrite mode.
179 					if ( overwrite === true || target[ propertyName ] == undefined )
180 					{
181 						// Only copy  specified fields if list is provided.
182 						if ( !propertiesList || ( propertyName in propertiesList ) )
183 							target[ propertyName ] = source[ propertyName ];
184 
185 					}
186 				}
187 			}
188 
189 			return target;
190 		},
191 
192 		/**
193 		 * Creates an object which is an instance of a class which prototype is a
194 		 * predefined object. All properties defined in the source object are
195 		 * automatically inherited by the resulting object, including future
196 		 * changes to it.
197 		 * @param {Object} source The source object to be used as the prototype for
198 		 *		the final object.
199 		 * @returns {Object} The resulting copy.
200 		 */
201 		prototypedCopy : function( source )
202 		{
203 			var copy = function()
204 			{};
205 			copy.prototype = source;
206 			return new copy();
207 		},
208 
209 		/**
210 		 * Checks if an object is an Array.
211 		 * @param {Object} object The object to be checked.
212 		 * @type Boolean
213 		 * @returns <i>true</i> if the object is an Array, otherwise <i>false</i>.
214 		 * @example
215 		 * alert( CKEDITOR.tools.isArray( [] ) );      // "true"
216 		 * alert( CKEDITOR.tools.isArray( 'Test' ) );  // "false"
217 		 */
218 		isArray : function( object )
219 		{
220 			return ( !!object && object instanceof Array );
221 		},
222 
223 		/**
224 		 * Whether the object contains no properties of it's own.
225  		 * @param object
226 		 */
227 		isEmpty : function ( object )
228 		{
229 			for ( var i in object )
230 			{
231 				if ( object.hasOwnProperty( i ) )
232 					return false;
233 			}
234 			return true;
235 		},
236 
237 		/**
238 		 * Transforms a CSS property name to its relative DOM style name.
239 		 * @param {String} cssName The CSS property name.
240 		 * @returns {String} The transformed name.
241 		 * @example
242 		 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) );  // "backgroundColor"
243 		 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'float' ) );             // "cssFloat"
244 		 */
245 		cssStyleToDomStyle : ( function()
246 		{
247 			var test = document.createElement( 'div' ).style;
248 
249 			var cssFloat = ( typeof test.cssFloat != 'undefined' ) ? 'cssFloat'
250 				: ( typeof test.styleFloat != 'undefined' ) ? 'styleFloat'
251 				: 'float';
252 
253 			return function( cssName )
254 			{
255 				if ( cssName == 'float' )
256 					return cssFloat;
257 				else
258 				{
259 					return cssName.replace( /-./g, function( match )
260 						{
261 							return match.substr( 1 ).toUpperCase();
262 						});
263 				}
264 			};
265 		} )(),
266 
267 		/**
268 		 * Build the HTML snippet of a set of <style>/<link>.
269 		 * @param css {String|Array} Each of which are url (absolute) of a CSS file or
270 		 * a trunk of style text.
271 		 */
272 		buildStyleHtml : function ( css )
273 		{
274 			css = [].concat( css );
275 			var item, retval = [];
276 			for ( var i = 0; i < css.length; i++ )
277 			{
278 				item = css[ i ];
279 				// Is CSS style text ?
280 				if ( /@import|[{}]/.test(item) )
281 					retval.push('<style>' + item + '</style>');
282 				else
283 					retval.push('<link type="text/css" rel=stylesheet href="' + item + '">');
284 			}
285 			return retval.join( '' );
286 		},
287 
288 		/**
289 		 * Replace special HTML characters in a string with their relative HTML
290 		 * entity values.
291 		 * @param {String} text The string to be encoded.
292 		 * @returns {String} The encode string.
293 		 * @example
294 		 * alert( CKEDITOR.tools.htmlEncode( 'A > B & C < D' ) );  // "A &gt; B &amp; C &lt; D"
295 		 */
296 		htmlEncode : function( text )
297 		{
298 			var standard = function( text )
299 			{
300 				var span = new CKEDITOR.dom.element( 'span' );
301 				span.setText( text );
302 				return span.getHtml();
303 			};
304 
305 			var fix1 = ( standard( '\n' ).toLowerCase() == '<br>' ) ?
306 				function( text )
307 				{
308 					// #3874 IE and Safari encode line-break into <br>
309 					return standard( text ).replace( /<br>/gi, '\n' );
310 				} :
311 				standard;
312 
313 			var fix2 = ( standard( '>' ) == '>' ) ?
314 				function( text )
315 				{
316 					// WebKit does't encode the ">" character, which makes sense, but
317 					// it's different than other browsers.
318 					return fix1( text ).replace( />/g, '>' );
319 				} :
320 				fix1;
321 
322 			var fix3 = ( standard( '  ' ) == '  ' ) ?
323 				function( text )
324 				{
325 					// #3785 IE8 changes spaces (>= 2) to  
326 					return fix2( text ).replace( / /g, ' ' );
327 				} :
328 				fix2;
329 
330 			this.htmlEncode = fix3;
331 
332 			return this.htmlEncode( text );
333 		},
334 
335 		/**
336 		 * Replace special HTML characters in HTMLElement's attribute with their relative HTML entity values.
337 		 * @param {String} The attribute's value to be encoded.
338 		 * @returns {String} The encode value.
339 		 * @example
340 		 * element.setAttribute( 'title', '<a " b >' );
341 		 * alert( CKEDITOR.tools.htmlEncodeAttr( element.getAttribute( 'title' ) );  // ">a " b <"
342 		 */
343 		htmlEncodeAttr : function( text )
344 		{
345 			return text.replace( /"/g, '"' ).replace( /</g, '<' ).replace( />/g, '>' );
346 		},
347 
348 		/**
349 		 * Gets a unique number for this CKEDITOR execution session. It returns
350 		 * progressive numbers starting at 1.
351 		 * @function
352 		 * @returns {Number} A unique number.
353 		 * @example
354 		 * alert( CKEDITOR.tools.<b>getNextNumber()</b> );  // "1" (e.g.)
355 		 * alert( CKEDITOR.tools.<b>getNextNumber()</b> );  // "2"
356 		 */
357 		getNextNumber : (function()
358 		{
359 			var last = 0;
360 			return function()
361 			{
362 				return ++last;
363 			};
364 		})(),
365 
366 		/**
367 		 * Gets a unique ID for CKEditor's interface elements. It returns a
368 		 * string with the "cke_" prefix and a progressive number.
369 		 * @function
370 		 * @returns {String} A unique ID.
371 		 * @example
372 		 * alert( CKEDITOR.tools.<b>getNextId()</b> );  // "cke_1" (e.g.)
373 		 * alert( CKEDITOR.tools.<b>getNextId()</b> );  // "cke_2"
374 		 */
375 		getNextId : function()
376 		{
377 			return 'cke_' + this.getNextNumber();
378 		},
379 
380 		/**
381 		 * Creates a function override.
382 		 * @param {Function} originalFunction The function to be overridden.
383 		 * @param {Function} functionBuilder A function that returns the new
384 		 *		function. The original function reference will be passed to this
385 		 *		function.
386 		 * @returns {Function} The new function.
387 		 * @example
388 		 * var example =
389 		 * {
390 		 *     myFunction : function( name )
391 		 *     {
392 		 *         alert( 'Name: ' + name );
393 		 *     }
394 		 * };
395 		 *
396 		 * example.myFunction = CKEDITOR.tools.override( example.myFunction, function( myFunctionOriginal )
397 		 *     {
398 		 *         return function( name )
399 		 *             {
400 		 *                 alert( 'Override Name: ' + name );
401 		 *                 myFunctionOriginal.call( this, name );
402 		 *             };
403 		 *     });
404 		 */
405 		override : function( originalFunction, functionBuilder )
406 		{
407 			return functionBuilder( originalFunction );
408 		},
409 
410 		/**
411 		 * Executes a function after specified delay.
412 		 * @param {Function} func The function to be executed.
413 		 * @param {Number} [milliseconds] The amount of time (millisecods) to wait
414 		 *		to fire the function execution. Defaults to zero.
415 		 * @param {Object} [scope] The object to hold the function execution scope
416 		 *		(the "this" object). By default the "window" object.
417 		 * @param {Object|Array} [args] A single object, or an array of objects, to
418 		 *		pass as arguments to the function.
419 		 * @param {Object} [ownerWindow] The window that will be used to set the
420 		 *		timeout. By default the current "window".
421 		 * @returns {Object} A value that can be used to cancel the function execution.
422 		 * @example
423 		 * CKEDITOR.tools.<b>setTimeout(
424 		 *     function()
425 		 *     {
426 		 *         alert( 'Executed after 2 seconds' );
427 		 *     },
428 		 *     2000 )</b>;
429 		 */
430 		setTimeout : function( func, milliseconds, scope, args, ownerWindow )
431 		{
432 			if ( !ownerWindow )
433 				ownerWindow = window;
434 
435 			if ( !scope )
436 				scope = ownerWindow;
437 
438 			return ownerWindow.setTimeout(
439 				function()
440 				{
441 					if ( args )
442 						func.apply( scope, [].concat( args ) ) ;
443 					else
444 						func.apply( scope ) ;
445 				},
446 				milliseconds || 0 );
447 		},
448 
449 		/**
450 		 * Remove spaces from the start and the end of a string. The following
451 		 * characters are removed: space, tab, line break, line feed.
452 		 * @function
453 		 * @param {String} str The text from which remove the spaces.
454 		 * @returns {String} The modified string without the boundary spaces.
455 		 * @example
456 		 * alert( CKEDITOR.tools.trim( '  example ' );  // "example"
457 		 */
458 		trim : (function()
459 		{
460 			// We are not using \s because we don't want "non-breaking spaces" to be caught.
461 			var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;
462 			return function( str )
463 			{
464 				return str.replace( trimRegex, '' ) ;
465 			};
466 		})(),
467 
468 		/**
469 		 * Remove spaces from the start (left) of a string. The following
470 		 * characters are removed: space, tab, line break, line feed.
471 		 * @function
472 		 * @param {String} str The text from which remove the spaces.
473 		 * @returns {String} The modified string excluding the removed spaces.
474 		 * @example
475 		 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "example "
476 		 */
477 		ltrim : (function()
478 		{
479 			// We are not using \s because we don't want "non-breaking spaces" to be caught.
480 			var trimRegex = /^[ \t\n\r]+/g;
481 			return function( str )
482 			{
483 				return str.replace( trimRegex, '' ) ;
484 			};
485 		})(),
486 
487 		/**
488 		 * Remove spaces from the end (right) of a string. The following
489 		 * characters are removed: space, tab, line break, line feed.
490 		 * @function
491 		 * @param {String} str The text from which remove the spaces.
492 		 * @returns {String} The modified string excluding the removed spaces.
493 		 * @example
494 		 * alert( CKEDITOR.tools.ltrim( '  example ' );  // "  example"
495 		 */
496 		rtrim : (function()
497 		{
498 			// We are not using \s because we don't want "non-breaking spaces" to be caught.
499 			var trimRegex = /[ \t\n\r]+$/g;
500 			return function( str )
501 			{
502 				return str.replace( trimRegex, '' ) ;
503 			};
504 		})(),
505 
506 		/**
507 		 * Returns the index of an element in an array.
508 		 * @param {Array} array The array to be searched.
509 		 * @param {Object} entry The element to be found.
510 		 * @returns {Number} The (zero based) index of the first entry that matches
511 		 *		the entry, or -1 if not found.
512 		 * @example
513 		 * var letters = [ 'a', 'b', 0, 'c', false ];
514 		 * alert( CKEDITOR.tools.indexOf( letters, '0' ) );  "-1" because 0 !== '0'
515 		 * alert( CKEDITOR.tools.indexOf( letters, false ) );  "4" because 0 !== false
516 		 */
517 		indexOf :
518 			// #2514: We should try to use Array.indexOf if it does exist.
519 			( Array.prototype.indexOf ) ?
520 				function( array, entry )
521 					{
522 						return array.indexOf( entry );
523 					}
524 			:
525 				function( array, entry )
526 				{
527 					for ( var i = 0, len = array.length ; i < len ; i++ )
528 					{
529 						if ( array[ i ] === entry )
530 							return i;
531 					}
532 					return -1;
533 				},
534 
535 		/**
536 		 * Creates a function that will always execute in the context of a
537 		 * specified object.
538 		 * @param {Function} func The function to be executed.
539 		 * @param {Object} obj The object to which bind the execution context.
540 		 * @returns {Function} The function that can be used to execute the
541 		 *		"func" function in the context of "obj".
542 		 * @example
543 		 * var obj = { text : 'My Object' };
544 		 *
545 		 * function alertText()
546 		 * {
547 		 *     alert( this.text );
548 		 * }
549 		 *
550 		 * var newFunc = <b>CKEDITOR.tools.bind( alertText, obj )</b>;
551 		 * newFunc();  // Alerts "My Object".
552 		 */
553 		bind : function( func, obj )
554 		{
555 			return function() { return func.apply( obj, arguments ); };
556 		},
557 
558 		/**
559 		 * Class creation based on prototype inheritance, with supports of the
560 		 * following features:
561 		 * <ul>
562 		 * <li> Static fields </li>
563 		 * <li> Private fields </li>
564 		 * <li> Public (prototype) fields </li>
565 		 * <li> Chainable base class constructor </li>
566 		 * </ul>
567 		 * @param {Object} definition The class definition object.
568 		 * @returns {Function} A class-like JavaScript function.
569 		 */
570 		createClass : function( definition )
571 		{
572 			var $ = definition.$,
573 				baseClass = definition.base,
574 				privates = definition.privates || definition._,
575 				proto = definition.proto,
576 				statics = definition.statics;
577 
578 			if ( privates )
579 			{
580 				var originalConstructor = $;
581 				$ = function()
582 				{
583 					// Create (and get) the private namespace.
584 					var _ = this._ || ( this._ = {} );
585 
586 					// Make some magic so "this" will refer to the main
587 					// instance when coding private functions.
588 					for ( var privateName in privates )
589 					{
590 						var priv = privates[ privateName ];
591 
592 						_[ privateName ] =
593 							( typeof priv == 'function' ) ? CKEDITOR.tools.bind( priv, this ) : priv;
594 					}
595 
596 					originalConstructor.apply( this, arguments );
597 				};
598 			}
599 
600 			if ( baseClass )
601 			{
602 				$.prototype = this.prototypedCopy( baseClass.prototype );
603 				$.prototype.constructor = $;
604 				$.prototype.base = function()
605 				{
606 					this.base = baseClass.prototype.base;
607 					baseClass.apply( this, arguments );
608 					this.base = arguments.callee;
609 				};
610 			}
611 
612 			if ( proto )
613 				this.extend( $.prototype, proto, true );
614 
615 			if ( statics )
616 				this.extend( $, statics, true );
617 
618 			return $;
619 		},
620 
621 		/**
622 		 * Creates a function reference that can be called later using
623 		 * CKEDITOR.tools.callFunction. This approach is specially useful to
624 		 * make DOM attribute function calls to JavaScript defined functions.
625 		 * @param {Function} fn The function to be executed on call.
626 		 * @param {Object} [scope] The object to have the context on "fn" execution.
627 		 * @returns {Number} A unique reference to be used in conjuction with
628 		 *		CKEDITOR.tools.callFunction.
629 		 * @example
630 		 * var ref = <b>CKEDITOR.tools.addFunction</b>(
631 		 *     function()
632 		 *     {
633 		 *         alert( 'Hello!');
634 		 *     });
635 		 * CKEDITOR.tools.callFunction( ref );  // Hello!
636 		 */
637 		addFunction : function( fn, scope )
638 		{
639 			return functions.push( function()
640 				{
641 					return fn.apply( scope || this, arguments );
642 				}) - 1;
643 		},
644 
645 		/**
646 		 * Removes the function reference created with {@see CKEDITOR.tools.addFunction}.
647 		 * @param {Number} ref The function reference created with
648 		 *		CKEDITOR.tools.addFunction.
649 		 */
650 		removeFunction : function( ref )
651 		{
652 			functions[ ref ] = null;
653 		},
654 
655 		/**
656 		 * Executes a function based on the reference created with
657 		 * CKEDITOR.tools.addFunction.
658 		 * @param {Number} ref The function reference created with
659 		 *		CKEDITOR.tools.addFunction.
660 		 * @param {[Any,[Any,...]} params Any number of parameters to be passed
661 		 *		to the executed function.
662 		 * @returns {Any} The return value of the function.
663 		 * @example
664 		 * var ref = CKEDITOR.tools.addFunction(
665 		 *     function()
666 		 *     {
667 		 *         alert( 'Hello!');
668 		 *     });
669 		 * <b>CKEDITOR.tools.callFunction( ref )</b>;  // Hello!
670 		 */
671 		callFunction : function( ref )
672 		{
673 			var fn = functions[ ref ];
674 			return fn && fn.apply( window, Array.prototype.slice.call( arguments, 1 ) );
675 		},
676 
677 		/**
678 		 * Append the 'px' length unit to the size if it's missing.
679 		 * @param length
680 		 */
681 		cssLength : (function()
682 		{
683 			return function( length )
684 			{
685 				return length + ( !length || isNaN( Number( length ) ) ? '' : 'px' );
686 			};
687 		})(),
688 
689 		/**
690 		 * Convert the specified CSS length value to the calculated pixel length inside this page.
691 		 * <strong>Note:</strong> Percentage based value is left intact.
692 		 * @param {String} cssLength CSS length value.
693 		 */
694 		convertToPx : ( function ()
695 			{
696 				var calculator;
697 
698 				return function( cssLength )
699 				{
700 					if ( !calculator )
701 					{
702 						calculator = CKEDITOR.dom.element.createFromHtml(
703 								'<div style="position:absolute;left:-9999px;' +
704 								'top:-9999px;margin:0px;padding:0px;border:0px;"' +
705 								'></div>', CKEDITOR.document );
706 						CKEDITOR.document.getBody().append( calculator );
707 					}
708 
709 					if ( !(/%$/).test( cssLength ) )
710 					{
711 						calculator.setStyle( 'width', cssLength );
712 						return calculator.$.clientWidth;
713 					}
714 
715 					return cssLength;
716 				};
717 			} )(),
718 
719 		/**
720 		 * String specified by {@param str} repeats {@param times} times.
721 		 * @param str
722 		 * @param times
723 		 */
724 		repeat : function( str, times )
725 		{
726 			return new Array( times + 1 ).join( str );
727 		},
728 
729 		/**
730 		 * Return the first successfully executed function's return value that
731 		 * doesn't throw any exception.
732 		 */
733 		tryThese : function()
734 		{
735 			var returnValue;
736 			for ( var i = 0, length = arguments.length; i < length; i++ )
737 			{
738 				var lambda = arguments[i];
739 				try
740 				{
741 					returnValue = lambda();
742 					break;
743 				}
744 				catch (e) {}
745 			}
746 			return returnValue;
747 		},
748 
749 		/**
750 		 * Generate a combined key from a series of params.
751 		 * @param {String} subKey One or more string used as sub keys.
752 		 * @example
753 		 * var key = CKEDITOR.tools.genKey( 'key1', 'key2', 'key3' );
754 		 * alert( key );		// "key1-key2-key3".
755 		 */
756 		genKey : function()
757 		{
758 			return Array.prototype.slice.call( arguments ).join( '-' );
759 		},
760 
761 		/**
762 		 * Try to avoid differences in the style attribute.
763 		 *
764 		 * @param {String} styleText The style data to be normalized.
765 		 * @param {Boolean} [nativeNormalize=false] Parse the data using the browser.
766 		 * @returns {String} The normalized value.
767 		 */
768 		normalizeCssText: function( styleText, nativeNormalize ) {
769 			var props = [],
770 				name,
771 				parsedProps = CKEDITOR.tools.parseCssText( styleText, true, nativeNormalize );
772 
773 			for ( name in parsedProps )
774 				props.push( name + ':' + parsedProps[ name ] );
775 
776 			props.sort();
777 
778 			return props.length ? ( props.join( ';' ) + ';' ) : '';
779 		},
780 
781 		/**
782 		 * Find and convert <code>rgb(x,x,x)</code> colors definition to hexadecimal notation.
783 		 * @param {String} styleText The style data (or just a string containing rgb colors) to be converted.
784 		 * @returns {String} The style data with rgb colors converted to hexadecimal equivalents.
785 		 */
786 		convertRgbToHex: function( styleText ) {
787 			return styleText.replace( /(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi, function( match, red, green, blue ) {
788 				var color = [ red, green, blue ];
789 				// Add padding zeros if the hex value is less than 0x10.
790 				for ( var i = 0; i < 3; i++ )
791 					color[ i ] = ( '0' + parseInt( color[ i ], 10 ).toString( 16 ) ).slice( -2 );
792 				return '#' + color.join( '' );
793 			});
794 		},
795 
796 		/**
797 		 * Turn inline style text properties into one hash.
798 		 *
799 		 * @param {String} styleText The style data to be parsed.
800 		 * @param {Boolean} [normalize=false] Normalize properties and values
801 		 * (e.g. trim spaces, convert to lower case).
802 		 * @param {Boolean} [nativeNormalize=false] Parse the data using the browser.
803 		 * @returns {String} The object containing parsed properties.
804 		 */
805 		parseCssText: function( styleText, normalize, nativeNormalize ) {
806 			var retval = {};
807 
808 			if ( nativeNormalize ) {
809 				// Injects the style in a temporary span object, so the browser parses it,
810 				// retrieving its final format.
811 				var temp = new CKEDITOR.dom.element( 'span' );
812 				temp.setAttribute( 'style', styleText );
813 				styleText = CKEDITOR.tools.convertRgbToHex( temp.getAttribute( 'style' ) || '' );
814 			}
815 
816 			// IE will leave a single semicolon when failed to parse the style text. (#3891)
817 			if ( !styleText || styleText == ';' )
818 				return retval;
819 
820 			styleText.replace( /"/g, '"' ).replace( /\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) {
821 				if ( normalize ) {
822 					name = name.toLowerCase();
823 					// Normalize font-family property, ignore quotes and being case insensitive. (#7322)
824 					// http://www.w3.org/TR/css3-fonts/#font-family-the-font-family-property
825 					if ( name == 'font-family' )
826 						value = value.toLowerCase().replace( /["']/g, '' ).replace( /\s*,\s*/g, ',' );
827 					value = CKEDITOR.tools.trim( value );
828 				}
829 
830 				retval[ name ] = value;
831 			});
832 			return retval;
833 		}
834 
835 	};
836 })();
837 
838 // PACKAGER_RENAME( CKEDITOR.tools )
839