X-Git-Url: https://scm.cri.ensmp.fr/git/ckeditor.git/blobdiff_plain/256592bf803e851aa7fc953e08a6e9e58d970f8c..871bad8291b6dbc29d489d95d185458caab25158:/skins/ckeditor/_source/core/tools.js diff --git a/skins/ckeditor/_source/core/tools.js b/skins/ckeditor/_source/core/tools.js new file mode 100644 index 0000000..5d0573c --- /dev/null +++ b/skins/ckeditor/_source/core/tools.js @@ -0,0 +1,763 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview Defines the {@link CKEDITOR.tools} object, which contains + * utility functions. + */ + +(function() +{ + var functions = []; + + CKEDITOR.on( 'reset', function() + { + functions = []; + }); + + /** + * Utility functions. + * @namespace + * @example + */ + CKEDITOR.tools = + { + /** + * Compare the elements of two arrays. + * @param {Array} arrayA An array to be compared. + * @param {Array} arrayB The other array to be compared. + * @returns {Boolean} "true" is the arrays have the same lenght and + * their elements match. + * @example + * var a = [ 1, 'a', 3 ]; + * var b = [ 1, 3, 'a' ]; + * var c = [ 1, 'a', 3 ]; + * var d = [ 1, 'a', 3, 4 ]; + * + * alert( CKEDITOR.tools.arrayCompare( a, b ) ); // false + * alert( CKEDITOR.tools.arrayCompare( a, c ) ); // true + * alert( CKEDITOR.tools.arrayCompare( a, d ) ); // false + */ + arrayCompare : function( arrayA, arrayB ) + { + if ( !arrayA && !arrayB ) + return true; + + if ( !arrayA || !arrayB || arrayA.length != arrayB.length ) + return false; + + for ( var i = 0 ; i < arrayA.length ; i++ ) + { + if ( arrayA[ i ] != arrayB[ i ] ) + return false; + } + + return true; + }, + + /** + * Creates a deep copy of an object. + * Attention: there is no support for recursive references. + * @param {Object} object The object to be cloned. + * @returns {Object} The object clone. + * @example + * var obj = + * { + * name : 'John', + * cars : + * { + * Mercedes : { color : 'blue' }, + * Porsche : { color : 'red' } + * } + * }; + * var clone = CKEDITOR.tools.clone( obj ); + * clone.name = 'Paul'; + * clone.cars.Porsche.color = 'silver'; + * alert( obj.name ); // John + * alert( clone.name ); // Paul + * alert( obj.cars.Porsche.color ); // red + * alert( clone.cars.Porsche.color ); // silver + */ + clone : function( obj ) + { + var clone; + + // Array. + if ( obj && ( obj instanceof Array ) ) + { + clone = []; + + for ( var i = 0 ; i < obj.length ; i++ ) + clone[ i ] = this.clone( obj[ i ] ); + + return clone; + } + + // "Static" types. + if ( obj === null + || ( typeof( obj ) != 'object' ) + || ( obj instanceof String ) + || ( obj instanceof Number ) + || ( obj instanceof Boolean ) + || ( obj instanceof Date ) + || ( obj instanceof RegExp) ) + { + return obj; + } + + // Objects. + clone = new obj.constructor(); + + for ( var propertyName in obj ) + { + var property = obj[ propertyName ]; + clone[ propertyName ] = this.clone( property ); + } + + return clone; + }, + + /** + * Turn the first letter of string to upper-case. + * @param {String} str + */ + capitalize: function( str ) + { + return str.charAt( 0 ).toUpperCase() + str.substring( 1 ).toLowerCase(); + }, + + /** + * Copy the properties from one object to another. By default, properties + * already present in the target object are not overwritten. + * @param {Object} target The object to be extended. + * @param {Object} source[,souce(n)] The objects from which copy + * properties. Any number of objects can be passed to this function. + * @param {Boolean} [overwrite] If 'true' is specified it indicates that + * properties already present in the target object could be + * overwritten by subsequent objects. + * @param {Object} [properties] Only properties within the specified names + * list will be received from the source object. + * @returns {Object} the extended object (target). + * @example + * // Create the sample object. + * var myObject = + * { + * prop1 : true + * }; + * + * // Extend the above object with two properties. + * CKEDITOR.tools.extend( myObject, + * { + * prop2 : true, + * prop3 : true + * } ); + * + * // Alert "prop1", "prop2" and "prop3". + * for ( var p in myObject ) + * alert( p ); + */ + extend : function( target ) + { + var argsLength = arguments.length, + overwrite, propertiesList; + + if ( typeof ( overwrite = arguments[ argsLength - 1 ] ) == 'boolean') + argsLength--; + else if ( typeof ( overwrite = arguments[ argsLength - 2 ] ) == 'boolean' ) + { + propertiesList = arguments [ argsLength -1 ]; + argsLength-=2; + } + for ( var i = 1 ; i < argsLength ; i++ ) + { + var source = arguments[ i ]; + for ( var propertyName in source ) + { + // Only copy existed fields if in overwrite mode. + if ( overwrite === true || target[ propertyName ] == undefined ) + { + // Only copy specified fields if list is provided. + if ( !propertiesList || ( propertyName in propertiesList ) ) + target[ propertyName ] = source[ propertyName ]; + + } + } + } + + return target; + }, + + /** + * Creates an object which is an instance of a class which prototype is a + * predefined object. All properties defined in the source object are + * automatically inherited by the resulting object, including future + * changes to it. + * @param {Object} source The source object to be used as the prototype for + * the final object. + * @returns {Object} The resulting copy. + */ + prototypedCopy : function( source ) + { + var copy = function() + {}; + copy.prototype = source; + return new copy(); + }, + + /** + * Checks if an object is an Array. + * @param {Object} object The object to be checked. + * @type Boolean + * @returns true if the object is an Array, otherwise false. + * @example + * alert( CKEDITOR.tools.isArray( [] ) ); // "true" + * alert( CKEDITOR.tools.isArray( 'Test' ) ); // "false" + */ + isArray : function( object ) + { + return ( !!object && object instanceof Array ); + }, + + /** + * Whether the object contains no properties of it's own. + * @param object + */ + isEmpty : function ( object ) + { + for ( var i in object ) + { + if ( object.hasOwnProperty( i ) ) + return false; + } + return true; + }, + + /** + * Transforms a CSS property name to its relative DOM style name. + * @param {String} cssName The CSS property name. + * @returns {String} The transformed name. + * @example + * alert( CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) ); // "backgroundColor" + * alert( CKEDITOR.tools.cssStyleToDomStyle( 'float' ) ); // "cssFloat" + */ + cssStyleToDomStyle : ( function() + { + var test = document.createElement( 'div' ).style; + + var cssFloat = ( typeof test.cssFloat != 'undefined' ) ? 'cssFloat' + : ( typeof test.styleFloat != 'undefined' ) ? 'styleFloat' + : 'float'; + + return function( cssName ) + { + if ( cssName == 'float' ) + return cssFloat; + else + { + return cssName.replace( /-./g, function( match ) + { + return match.substr( 1 ).toUpperCase(); + }); + } + }; + } )(), + + /** + * Build the HTML snippet of a set of <style>/<link>. + * @param css {String|Array} Each of which are url (absolute) of a CSS file or + * a trunk of style text. + */ + buildStyleHtml : function ( css ) + { + css = [].concat( css ); + var item, retval = []; + for ( var i = 0; i < css.length; i++ ) + { + item = css[ i ]; + // Is CSS style text ? + if ( /@import|[{}]/.test(item) ) + retval.push(''); + else + retval.push(''); + } + return retval.join( '' ); + }, + + /** + * Replace special HTML characters in a string with their relative HTML + * entity values. + * @param {String} text The string to be encoded. + * @returns {String} The encode string. + * @example + * alert( CKEDITOR.tools.htmlEncode( 'A > B & C < D' ) ); // "A &gt; B &amp; C &lt; D" + */ + htmlEncode : function( text ) + { + var standard = function( text ) + { + var span = new CKEDITOR.dom.element( 'span' ); + span.setText( text ); + return span.getHtml(); + }; + + var fix1 = ( standard( '\n' ).toLowerCase() == '
' ) ? + function( text ) + { + // #3874 IE and Safari encode line-break into
+ return standard( text ).replace( /
/gi, '\n' ); + } : + standard; + + var fix2 = ( standard( '>' ) == '>' ) ? + function( text ) + { + // WebKit does't encode the ">" character, which makes sense, but + // it's different than other browsers. + return fix1( text ).replace( />/g, '>' ); + } : + fix1; + + var fix3 = ( standard( ' ' ) == '  ' ) ? + function( text ) + { + // #3785 IE8 changes spaces (>= 2) to   + return fix2( text ).replace( / /g, ' ' ); + } : + fix2; + + this.htmlEncode = fix3; + + return this.htmlEncode( text ); + }, + + /** + * Replace special HTML characters in HTMLElement's attribute with their relative HTML entity values. + * @param {String} The attribute's value to be encoded. + * @returns {String} The encode value. + * @example + * element.setAttribute( 'title', '' ); + * alert( CKEDITOR.tools.htmlEncodeAttr( element.getAttribute( 'title' ) ); // ">a " b <" + */ + htmlEncodeAttr : function( text ) + { + return text.replace( /"/g, '"' ).replace( //g, '>' ); + }, + + /** + * Gets a unique number for this CKEDITOR execution session. It returns + * progressive numbers starting at 1. + * @function + * @returns {Number} A unique number. + * @example + * alert( CKEDITOR.tools.getNextNumber() ); // "1" (e.g.) + * alert( CKEDITOR.tools.getNextNumber() ); // "2" + */ + getNextNumber : (function() + { + var last = 0; + return function() + { + return ++last; + }; + })(), + + /** + * Gets a unique ID for CKEditor's interface elements. It returns a + * string with the "cke_" prefix and a progressive number. + * @function + * @returns {String} A unique ID. + * @example + * alert( CKEDITOR.tools.getNextId() ); // "cke_1" (e.g.) + * alert( CKEDITOR.tools.getNextId() ); // "cke_2" + */ + getNextId : function() + { + return 'cke_' + this.getNextNumber(); + }, + + /** + * Creates a function override. + * @param {Function} originalFunction The function to be overridden. + * @param {Function} functionBuilder A function that returns the new + * function. The original function reference will be passed to this + * function. + * @returns {Function} The new function. + * @example + * var example = + * { + * myFunction : function( name ) + * { + * alert( 'Name: ' + name ); + * } + * }; + * + * example.myFunction = CKEDITOR.tools.override( example.myFunction, function( myFunctionOriginal ) + * { + * return function( name ) + * { + * alert( 'Override Name: ' + name ); + * myFunctionOriginal.call( this, name ); + * }; + * }); + */ + override : function( originalFunction, functionBuilder ) + { + return functionBuilder( originalFunction ); + }, + + /** + * Executes a function after specified delay. + * @param {Function} func The function to be executed. + * @param {Number} [milliseconds] The amount of time (millisecods) to wait + * to fire the function execution. Defaults to zero. + * @param {Object} [scope] The object to hold the function execution scope + * (the "this" object). By default the "window" object. + * @param {Object|Array} [args] A single object, or an array of objects, to + * pass as arguments to the function. + * @param {Object} [ownerWindow] The window that will be used to set the + * timeout. By default the current "window". + * @returns {Object} A value that can be used to cancel the function execution. + * @example + * CKEDITOR.tools.setTimeout( + * function() + * { + * alert( 'Executed after 2 seconds' ); + * }, + * 2000 ); + */ + setTimeout : function( func, milliseconds, scope, args, ownerWindow ) + { + if ( !ownerWindow ) + ownerWindow = window; + + if ( !scope ) + scope = ownerWindow; + + return ownerWindow.setTimeout( + function() + { + if ( args ) + func.apply( scope, [].concat( args ) ) ; + else + func.apply( scope ) ; + }, + milliseconds || 0 ); + }, + + /** + * Remove spaces from the start and the end of a string. The following + * characters are removed: space, tab, line break, line feed. + * @function + * @param {String} str The text from which remove the spaces. + * @returns {String} The modified string without the boundary spaces. + * @example + * alert( CKEDITOR.tools.trim( ' example ' ); // "example" + */ + trim : (function() + { + // We are not using \s because we don't want "non-breaking spaces" to be caught. + var trimRegex = /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g; + return function( str ) + { + return str.replace( trimRegex, '' ) ; + }; + })(), + + /** + * Remove spaces from the start (left) of a string. The following + * characters are removed: space, tab, line break, line feed. + * @function + * @param {String} str The text from which remove the spaces. + * @returns {String} The modified string excluding the removed spaces. + * @example + * alert( CKEDITOR.tools.ltrim( ' example ' ); // "example " + */ + ltrim : (function() + { + // We are not using \s because we don't want "non-breaking spaces" to be caught. + var trimRegex = /^[ \t\n\r]+/g; + return function( str ) + { + return str.replace( trimRegex, '' ) ; + }; + })(), + + /** + * Remove spaces from the end (right) of a string. The following + * characters are removed: space, tab, line break, line feed. + * @function + * @param {String} str The text from which remove the spaces. + * @returns {String} The modified string excluding the removed spaces. + * @example + * alert( CKEDITOR.tools.ltrim( ' example ' ); // " example" + */ + rtrim : (function() + { + // We are not using \s because we don't want "non-breaking spaces" to be caught. + var trimRegex = /[ \t\n\r]+$/g; + return function( str ) + { + return str.replace( trimRegex, '' ) ; + }; + })(), + + /** + * Returns the index of an element in an array. + * @param {Array} array The array to be searched. + * @param {Object} entry The element to be found. + * @returns {Number} The (zero based) index of the first entry that matches + * the entry, or -1 if not found. + * @example + * var letters = [ 'a', 'b', 0, 'c', false ]; + * alert( CKEDITOR.tools.indexOf( letters, '0' ) ); "-1" because 0 !== '0' + * alert( CKEDITOR.tools.indexOf( letters, false ) ); "4" because 0 !== false + */ + indexOf : + // #2514: We should try to use Array.indexOf if it does exist. + ( Array.prototype.indexOf ) ? + function( array, entry ) + { + return array.indexOf( entry ); + } + : + function( array, entry ) + { + for ( var i = 0, len = array.length ; i < len ; i++ ) + { + if ( array[ i ] === entry ) + return i; + } + return -1; + }, + + /** + * Creates a function that will always execute in the context of a + * specified object. + * @param {Function} func The function to be executed. + * @param {Object} obj The object to which bind the execution context. + * @returns {Function} The function that can be used to execute the + * "func" function in the context of "obj". + * @example + * var obj = { text : 'My Object' }; + * + * function alertText() + * { + * alert( this.text ); + * } + * + * var newFunc = CKEDITOR.tools.bind( alertText, obj ); + * newFunc(); // Alerts "My Object". + */ + bind : function( func, obj ) + { + return function() { return func.apply( obj, arguments ); }; + }, + + /** + * Class creation based on prototype inheritance, with supports of the + * following features: + * + * @param {Object} definition The class definition object. + * @returns {Function} A class-like JavaScript function. + */ + createClass : function( definition ) + { + var $ = definition.$, + baseClass = definition.base, + privates = definition.privates || definition._, + proto = definition.proto, + statics = definition.statics; + + if ( privates ) + { + var originalConstructor = $; + $ = function() + { + // Create (and get) the private namespace. + var _ = this._ || ( this._ = {} ); + + // Make some magic so "this" will refer to the main + // instance when coding private functions. + for ( var privateName in privates ) + { + var priv = privates[ privateName ]; + + _[ privateName ] = + ( typeof priv == 'function' ) ? CKEDITOR.tools.bind( priv, this ) : priv; + } + + originalConstructor.apply( this, arguments ); + }; + } + + if ( baseClass ) + { + $.prototype = this.prototypedCopy( baseClass.prototype ); + $.prototype.constructor = $; + $.prototype.base = function() + { + this.base = baseClass.prototype.base; + baseClass.apply( this, arguments ); + this.base = arguments.callee; + }; + } + + if ( proto ) + this.extend( $.prototype, proto, true ); + + if ( statics ) + this.extend( $, statics, true ); + + return $; + }, + + /** + * Creates a function reference that can be called later using + * CKEDITOR.tools.callFunction. This approach is specially useful to + * make DOM attribute function calls to JavaScript defined functions. + * @param {Function} fn The function to be executed on call. + * @param {Object} [scope] The object to have the context on "fn" execution. + * @returns {Number} A unique reference to be used in conjuction with + * CKEDITOR.tools.callFunction. + * @example + * var ref = CKEDITOR.tools.addFunction( + * function() + * { + * alert( 'Hello!'); + * }); + * CKEDITOR.tools.callFunction( ref ); // Hello! + */ + addFunction : function( fn, scope ) + { + return functions.push( function() + { + return fn.apply( scope || this, arguments ); + }) - 1; + }, + + /** + * Removes the function reference created with {@see CKEDITOR.tools.addFunction}. + * @param {Number} ref The function reference created with + * CKEDITOR.tools.addFunction. + */ + removeFunction : function( ref ) + { + functions[ ref ] = null; + }, + + /** + * Executes a function based on the reference created with + * CKEDITOR.tools.addFunction. + * @param {Number} ref The function reference created with + * CKEDITOR.tools.addFunction. + * @param {[Any,[Any,...]} params Any number of parameters to be passed + * to the executed function. + * @returns {Any} The return value of the function. + * @example + * var ref = CKEDITOR.tools.addFunction( + * function() + * { + * alert( 'Hello!'); + * }); + * CKEDITOR.tools.callFunction( ref ); // Hello! + */ + callFunction : function( ref ) + { + var fn = functions[ ref ]; + return fn && fn.apply( window, Array.prototype.slice.call( arguments, 1 ) ); + }, + + /** + * Append the 'px' length unit to the size if it's missing. + * @param length + */ + cssLength : (function() + { + return function( length ) + { + return length + ( !length || isNaN( Number( length ) ) ? '' : 'px' ); + }; + })(), + + /** + * Convert the specified CSS length value to the calculated pixel length inside this page. + * Note: Percentage based value is left intact. + * @param {String} cssLength CSS length value. + */ + convertToPx : ( function () + { + var calculator; + + return function( cssLength ) + { + if ( !calculator ) + { + calculator = CKEDITOR.dom.element.createFromHtml( + '
', CKEDITOR.document ); + CKEDITOR.document.getBody().append( calculator ); + } + + if ( !(/%$/).test( cssLength ) ) + { + calculator.setStyle( 'width', cssLength ); + return calculator.$.clientWidth; + } + + return cssLength; + }; + } )(), + + /** + * String specified by {@param str} repeats {@param times} times. + * @param str + * @param times + */ + repeat : function( str, times ) + { + return new Array( times + 1 ).join( str ); + }, + + /** + * Return the first successfully executed function's return value that + * doesn't throw any exception. + */ + tryThese : function() + { + var returnValue; + for ( var i = 0, length = arguments.length; i < length; i++ ) + { + var lambda = arguments[i]; + try + { + returnValue = lambda(); + break; + } + catch (e) {} + } + return returnValue; + }, + + /** + * Generate a combined key from a series of params. + * @param {String} subKey One or more string used as sub keys. + * @example + * var key = CKEDITOR.tools.genKey( 'key1', 'key2', 'key3' ); + * alert( key ); // "key1-key2-key3". + */ + genKey : function() + { + return Array.prototype.slice.call( arguments ).join( '-' ); + } + }; +})(); + +// PACKAGER_RENAME( CKEDITOR.tools )