1 /* 2 Copyright (c) 2003-2009, 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 /** 16 * Utility functions. 17 * @namespace 18 * @example 19 */ 20 CKEDITOR.tools = 21 { 22 arrayCompare : function( arrayA, arrayB ) 23 { 24 if ( !arrayA && !arrayB ) 25 return true; 26 27 if ( !arrayA || !arrayB || arrayA.length != arrayB.length ) 28 return false; 29 30 for ( var i = 0 ; i < arrayA.length ; i++ ) 31 { 32 if ( arrayA[ i ] != arrayB[ i ] ) 33 return false; 34 } 35 36 return true; 37 }, 38 39 /** 40 * Creates a deep copy of an object. 41 * Attention: there is no support for recursive references. 42 * @param {Object} object The object to be cloned. 43 * @returns {Object} The object clone. 44 * @example 45 * var obj = 46 * { 47 * name : 'John', 48 * cars : 49 * { 50 * Mercedes : { color : 'blue' }, 51 * Porsche : { color : 'red' } 52 * } 53 * }; 54 * var clone = CKEDITOR.tools.clone( obj ); 55 * clone.name = 'Paul'; 56 * clone.cars.Porsche.color = 'silver'; 57 * alert( obj.name ); // John 58 * alert( clone.name ); // Paul 59 * alert( obj.cars.Porsche.color ); // red 60 * alert( clone.cars.Porsche.color ); // silver 61 */ 62 clone : function( obj ) 63 { 64 var clone; 65 66 // Array. 67 if ( obj && ( obj instanceof Array ) ) 68 { 69 clone = []; 70 71 for ( var i = 0 ; i < obj.length ; i++ ) 72 clone[ i ] = this.clone( obj[ i ] ); 73 74 return clone; 75 } 76 77 // "Static" types. 78 if ( obj === null 79 || ( typeof( obj ) != 'object' ) 80 || ( obj instanceof String ) 81 || ( obj instanceof Number ) 82 || ( obj instanceof Boolean ) 83 || ( obj instanceof Date ) ) 84 { 85 return obj; 86 } 87 88 // Objects. 89 clone = new obj.constructor(); 90 91 for ( var propertyName in obj ) 92 { 93 var property = obj[ propertyName ]; 94 clone[ propertyName ] = this.clone( property ); 95 } 96 97 return clone; 98 }, 99 100 /** 101 * Copy the properties from one object to another. By default, properties 102 * already present in the target object <strong>are not</strong> overwritten. 103 * @param {Object} target The object to be extended. 104 * @param {Object} source[,souce(n)] The objects from which copy 105 * properties. Any number of objects can be passed to this function. 106 * @param {Boolean} [overwrite] If 'true' is specified it indicates that 107 * properties already present in the target object could be 108 * overwritten by subsequent objects. 109 * @param {Object} [properties] Only properties within the specified names 110 * list will be received from the source object. 111 * @returns {Object} the extended object (target). 112 * @example 113 * // Create the sample object. 114 * var myObject = 115 * { 116 * prop1 : true 117 * }; 118 * 119 * // Extend the above object with two properties. 120 * CKEDITOR.tools.extend( myObject, 121 * { 122 * prop2 : true, 123 * prop3 : true 124 * } ); 125 * 126 * // Alert "prop1", "prop2" and "prop3". 127 * for ( var p in myObject ) 128 * alert( p ); 129 */ 130 extend : function( target ) 131 { 132 var argsLength = arguments.length, 133 overwrite, propertiesList; 134 135 if ( typeof ( overwrite = arguments[ argsLength - 1 ] ) == 'boolean') 136 argsLength--; 137 else if ( typeof ( overwrite = arguments[ argsLength - 2 ] ) == 'boolean' ) 138 { 139 propertiesList = arguments [ argsLength -1 ]; 140 argsLength-=2; 141 } 142 for ( var i = 1 ; i < argsLength ; i++ ) 143 { 144 var source = arguments[ i ]; 145 for ( var propertyName in source ) 146 { 147 // Only copy existed fields if in overwrite mode. 148 if ( overwrite === true || target[ propertyName ] == undefined ) 149 { 150 // Only copy specified fields if list is provided. 151 if ( !propertiesList || ( propertyName in propertiesList ) ) 152 target[ propertyName ] = source[ propertyName ]; 153 154 } 155 } 156 } 157 158 return target; 159 }, 160 161 /** 162 * Creates an object which is an instance of a class which prototype is a 163 * predefined object. All properties defined in the source object are 164 * automatically inherited by the resulting object, including future 165 * changes to it. 166 * @param {Object} source The source object to be used as the prototype for 167 * the final object. 168 * @returns {Object} The resulting copy. 169 */ 170 prototypedCopy : function( source ) 171 { 172 var copy = function() 173 {}; 174 copy.prototype = source; 175 return new copy(); 176 }, 177 178 /** 179 * Checks if an object is an Array. 180 * @param {Object} object The object to be checked. 181 * @type Boolean 182 * @returns <i>true</i> if the object is an Array, otherwise <i>false</i>. 183 * @example 184 * alert( CKEDITOR.tools.isArray( [] ) ); // "true" 185 * alert( CKEDITOR.tools.isArray( 'Test' ) ); // "false" 186 */ 187 isArray : function( object ) 188 { 189 return ( !!object && object instanceof Array ); 190 }, 191 192 /** 193 * Transforms a CSS property name to its relative DOM style name. 194 * @param {String} cssName The CSS property name. 195 * @returns {String} The transformed name. 196 * @example 197 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) ); // "backgroundColor" 198 * alert( CKEDITOR.tools.cssStyleToDomStyle( 'float' ) ); // "cssFloat" 199 */ 200 cssStyleToDomStyle : function( cssName ) 201 { 202 if ( cssName == 'float' ) 203 return 'cssFloat'; 204 else 205 { 206 return cssName.replace( /-./g, function( match ) 207 { 208 return match.substr( 1 ).toUpperCase(); 209 }); 210 } 211 }, 212 213 /** 214 * Replace special HTML characters in a string with their relative HTML 215 * entity values. 216 * @param {String} text The string to be encoded. 217 * @returns {String} The encode string. 218 * @example 219 * alert( CKEDITOR.tools.htmlEncode( 'A > B & C < D' ) ); // "A > B & C < D" 220 */ 221 htmlEncode : function( text ) 222 { 223 var standard = function( text ) 224 { 225 var span = new CKEDITOR.dom.element( 'span' ); 226 span.setText( text ); 227 return span.getHtml(); 228 }; 229 230 this.htmlEncode = ( standard( '>' ) == '>' ) ? 231 function( text ) 232 { 233 // WebKit does't encode the ">" character, which makes sense, but 234 // it's different than other browsers. 235 return standard( text ).replace( />/g, '>' ); 236 } : 237 standard; 238 239 return this.htmlEncode( text ); 240 }, 241 242 /** 243 * Gets a unique number for this CKEDITOR execution session. It returns 244 * progressive numbers starting at 1. 245 * @function 246 * @returns {Number} A unique number. 247 * @example 248 * alert( CKEDITOR.tools.<b>getNextNumber()</b> ); // "1" (e.g.) 249 * alert( CKEDITOR.tools.<b>getNextNumber()</b> ); // "2" 250 */ 251 getNextNumber : (function() 252 { 253 var last = 0; 254 return function() 255 { 256 return ++last; 257 }; 258 })(), 259 260 /** 261 * Creates a function override. 262 * @param {Function} originalFunction The function to be overridden. 263 * @param {Function} functionBuilder A function that returns the new 264 * function. The original function reference will be passed to this 265 * function. 266 * @returns {Function} The new function. 267 * @example 268 * var example = 269 * { 270 * myFunction : function( name ) 271 * { 272 * alert( 'Name: ' + name ); 273 * } 274 * }; 275 * 276 * example.myFunction = CKEDITOR.tools.override( example.myFunction, function( myFunctionOriginal ) 277 * { 278 * return function( name ) 279 * { 280 * alert( 'Override Name: ' + name ); 281 * myFunctionOriginal.call( this, name ); 282 * }; 283 * }); 284 */ 285 override : function( originalFunction, functionBuilder ) 286 { 287 return functionBuilder( originalFunction ); 288 }, 289 290 /** 291 * Executes a function after specified delay. 292 * @param {Function} func The function to be executed. 293 * @param {Number} [milliseconds] The amount of time (millisecods) to wait 294 * to fire the function execution. Defaults to zero. 295 * @param {Object} [scope] The object to hold the function execution scope 296 * (the "this" object). By default the "window" object. 297 * @param {Object|Array} [args] A single object, or an array of objects, to 298 * pass as arguments to the function. 299 * @param {Object} [ownerWindow] The window that will be used to set the 300 * timeout. By default the current "window". 301 * @returns {Object} A value that can be used to cancel the function execution. 302 * @example 303 * CKEDITOR.tools.<b>setTimeout( 304 * function() 305 * { 306 * alert( 'Executed after 2 seconds' ); 307 * }, 308 * 2000 )</b>; 309 */ 310 setTimeout : function( func, milliseconds, scope, args, ownerWindow ) 311 { 312 if ( !ownerWindow ) 313 ownerWindow = window; 314 315 if ( !scope ) 316 scope = ownerWindow; 317 318 return ownerWindow.setTimeout( 319 function() 320 { 321 if ( args ) 322 func.apply( scope, [].concat( args ) ) ; 323 else 324 func.apply( scope ) ; 325 }, 326 milliseconds || 0 ); 327 }, 328 329 /** 330 * Remove spaces from the start and the end of a string. The following 331 * characters are removed: space, tab, line break, line feed. 332 * @function 333 * @param {String} str The text from which remove the spaces. 334 * @returns {String} The modified string without the boundary spaces. 335 * @example 336 * alert( CKEDITOR.tools.trim( ' example ' ); // "example" 337 */ 338 trim : (function() 339 { 340 // We are not using \s because we don't want "non-breaking spaces" to be caught. 341 var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g; 342 return function( str ) 343 { 344 return str.replace( trimRegex, '' ) ; 345 }; 346 })(), 347 348 /** 349 * Remove spaces from the start (left) of a string. The following 350 * characters are removed: space, tab, line break, line feed. 351 * @function 352 * @param {String} str The text from which remove the spaces. 353 * @returns {String} The modified string excluding the removed spaces. 354 * @example 355 * alert( CKEDITOR.tools.ltrim( ' example ' ); // "example " 356 */ 357 ltrim : (function() 358 { 359 // We are not using \s because we don't want "non-breaking spaces" to be caught. 360 var trimRegex = /^[ \t\n\r]+/g; 361 return function( str ) 362 { 363 return str.replace( trimRegex, '' ) ; 364 }; 365 })(), 366 367 /** 368 * Remove spaces from the end (right) of a string. The following 369 * characters are removed: space, tab, line break, line feed. 370 * @function 371 * @param {String} str The text from which remove the spaces. 372 * @returns {String} The modified string excluding the removed spaces. 373 * @example 374 * alert( CKEDITOR.tools.ltrim( ' example ' ); // " example" 375 */ 376 rtrim : (function() 377 { 378 // We are not using \s because we don't want "non-breaking spaces" to be caught. 379 var trimRegex = /[ \t\n\r]+$/g; 380 return function( str ) 381 { 382 return str.replace( trimRegex, '' ) ; 383 }; 384 })(), 385 386 /** 387 * Returns the index of an element in an array. 388 * @param {Array} array The array to be searched. 389 * @param {Object} entry The element to be found. 390 * @returns {Number} The (zero based) index of the first entry that matches 391 * the entry, or -1 if not found. 392 * @example 393 * var letters = [ 'a', 'b', 0, 'c', false ]; 394 * alert( CKEDITOR.tools.indexOf( letters, '0' ) ); "-1" because 0 !== '0' 395 * alert( CKEDITOR.tools.indexOf( letters, false ) ); "4" because 0 !== false 396 */ 397 indexOf : 398 // #2514: We should try to use Array.indexOf if it does exist. 399 ( Array.prototype.indexOf ) ? 400 function( array, entry ) 401 { 402 return array.indexOf( entry ); 403 } 404 : 405 function( array, entry ) 406 { 407 for ( var i = 0, len = array.length ; i < len ; i++ ) 408 { 409 if ( array[ i ] === entry ) 410 return i; 411 } 412 return -1; 413 }, 414 415 bind : function( func, obj ) 416 { 417 return function() { return func.apply( obj, arguments ); }; 418 }, 419 420 /** 421 * Class creation based on prototype inheritance, with supports of the 422 * following features: 423 * <ul> 424 * <li> Static fields </li> 425 * <li> Private fields </li> 426 * <li> Public(prototype) fields </li> 427 * <li> Chainable base class constructor </li> 428 * </ul> 429 * 430 * @param {Object} definiton (Optional)The class definiton object. 431 */ 432 createClass : function( definition ) 433 { 434 var $ = definition.$, 435 baseClass = definition.base, 436 privates = definition.privates || definition._, 437 proto = definition.proto, 438 statics = definition.statics; 439 440 if ( privates ) 441 { 442 var originalConstructor = $; 443 $ = function() 444 { 445 // Create (and get) the private namespace. 446 var _ = this._ || ( this._ = {} ); 447 448 // Make some magic so "this" will refer to the main 449 // instance when coding private functions. 450 for ( var privateName in privates ) 451 { 452 var priv = privates[ privateName ]; 453 454 _[ privateName ] = 455 ( typeof priv == 'function' ) ? CKEDITOR.tools.bind( priv, this ) : priv; 456 } 457 458 originalConstructor.apply( this, arguments ); 459 }; 460 } 461 462 if ( baseClass ) 463 { 464 $.prototype = this.prototypedCopy( baseClass.prototype ); 465 $.prototype.constructor = $; 466 $.prototype.base = function() 467 { 468 this.base = baseClass.prototype.base; 469 baseClass.apply( this, arguments ); 470 this.base = arguments.callee; 471 }; 472 } 473 474 if ( proto ) 475 this.extend( $.prototype, proto, true ); 476 477 if ( statics ) 478 this.extend( $, statics, true ); 479 480 return $; 481 }, 482 483 addFunction : function( fn, scope ) 484 { 485 return functions.push( function() 486 { 487 fn.apply( scope || this, arguments ); 488 }) - 1; 489 }, 490 491 callFunction : function( index ) 492 { 493 var fn = functions[ index ]; 494 return fn.apply( window, Array.prototype.slice.call( arguments, 1 ) ); 495 }, 496 497 cssLength : (function() 498 { 499 var decimalRegex = /^\d+(?:\.\d+)?$/; 500 return function( length ) 501 { 502 return length + ( decimalRegex.test( length ) ? 'px' : '' ); 503 }; 504 })() 505 }; 506 })(); 507 508 // PACKAGER_RENAME( CKEDITOR.tools ) 509