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 > B & C < 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