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