X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fcore%2Ftools.js;h=a529c6a8b05e0fd7b9d183063bb489388e41f43b;hb=a272c66d841421f8bf933c16535bdcde1c4649fc;hp=29b34b4bfb27e1b5197664511b47e54a20a59b7d;hpb=ea7e3453c7b0f023b050aca6d9f83ab372860d91;p=ckeditor.git diff --git a/_source/core/tools.js b/_source/core/tools.js index 29b34b4..a529c6a 100644 --- a/_source/core/tools.js +++ b/_source/core/tools.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ @@ -12,6 +12,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license { var functions = []; + CKEDITOR.on( 'reset', function() + { + functions = []; + }); + /** * Utility functions. * @namespace @@ -19,6 +24,22 @@ For licensing, see LICENSE.html or http://ckeditor.com/license */ 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 ) @@ -80,7 +101,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license || ( obj instanceof String ) || ( obj instanceof Number ) || ( obj instanceof Boolean ) - || ( obj instanceof Date ) ) + || ( obj instanceof Date ) + || ( obj instanceof RegExp) ) { return obj; } @@ -98,6 +120,15 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** + * 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. @@ -190,6 +221,20 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** + * 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. @@ -197,17 +242,47 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * alert( CKEDITOR.tools.cssStyleToDomStyle( 'background-color' ) ); // "backgroundColor" * alert( CKEDITOR.tools.cssStyleToDomStyle( 'float' ) ); // "cssFloat" */ - cssStyleToDomStyle : function( cssName ) + cssStyleToDomStyle : ( function() { - if ( cssName == 'float' ) - return 'cssFloat'; - else + var test = document.createElement( 'div' ).style; + + var cssFloat = ( typeof test.cssFloat != 'undefined' ) ? 'cssFloat' + : ( typeof test.styleFloat != 'undefined' ) ? 'styleFloat' + : 'float'; + + return function( cssName ) { - return cssName.replace( /-./g, function( match ) - { - return match.substr( 1 ).toUpperCase(); - }); + 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( '' ); }, /** @@ -258,6 +333,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }, /** + * 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 @@ -276,6 +364,20 @@ For licensing, see LICENSE.html or http://ckeditor.com/license })(), /** + * 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 @@ -430,6 +532,24 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 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 ); }; @@ -441,11 +561,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * - * - * @param {Object} definiton (Optional)The class definiton object. + * @param {Object} definition The class definition object. + * @returns {Function} A class-like JavaScript function. */ createClass : function( definition ) { @@ -498,33 +618,220 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 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() { - fn.apply( scope || this, arguments ); + return fn.apply( scope || this, arguments ); }) - 1; }, - callFunction : function( index ) + /** + * Removes the function reference created with {@see CKEDITOR.tools.addFunction}. + * @param {Number} ref The function reference created with + * CKEDITOR.tools.addFunction. + */ + removeFunction : function( ref ) { - var fn = functions[ index ]; - return fn.apply( window, Array.prototype.slice.call( arguments, 1 ) ); + 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() { - var decimalRegex = /^\d+(?:\.\d+)?$/; return function( length ) { - return length + ( decimalRegex.test( length ) ? 'px' : '' ); + 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( '-' ); + }, + + /** + * Try to avoid differences in the style attribute. + * + * @param {String} styleText The style data to be normalized. + * @param {Boolean} [nativeNormalize=false] Parse the data using the browser. + * @returns {String} The normalized value. + */ + normalizeCssText: function( styleText, nativeNormalize ) { + var props = [], + name, + parsedProps = CKEDITOR.tools.parseCssText( styleText, true, nativeNormalize ); + + for ( name in parsedProps ) + props.push( name + ':' + parsedProps[ name ] ); + + props.sort(); + + return props.length ? ( props.join( ';' ) + ';' ) : ''; + }, + + /** + * Find and convert rgb(x,x,x) colors definition to hexadecimal notation. + * @param {String} styleText The style data (or just a string containing rgb colors) to be converted. + * @returns {String} The style data with rgb colors converted to hexadecimal equivalents. + */ + convertRgbToHex: function( styleText ) { + return styleText.replace( /(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi, function( match, red, green, blue ) { + var color = [ red, green, blue ]; + // Add padding zeros if the hex value is less than 0x10. + for ( var i = 0; i < 3; i++ ) + color[ i ] = ( '0' + parseInt( color[ i ], 10 ).toString( 16 ) ).slice( -2 ); + return '#' + color.join( '' ); + }); + }, + + /** + * Turn inline style text properties into one hash. + * + * @param {String} styleText The style data to be parsed. + * @param {Boolean} [normalize=false] Normalize properties and values + * (e.g. trim spaces, convert to lower case). + * @param {Boolean} [nativeNormalize=false] Parse the data using the browser. + * @returns {String} The object containing parsed properties. + */ + parseCssText: function( styleText, normalize, nativeNormalize ) { + var retval = {}; + + if ( nativeNormalize ) { + // Injects the style in a temporary span object, so the browser parses it, + // retrieving its final format. + var temp = new CKEDITOR.dom.element( 'span' ); + temp.setAttribute( 'style', styleText ); + styleText = CKEDITOR.tools.convertRgbToHex( temp.getAttribute( 'style' ) || '' ); + } + + // IE will leave a single semicolon when failed to parse the style text. (#3891) + if ( !styleText || styleText == ';' ) + return retval; + + styleText.replace( /"/g, '"' ).replace( /\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) { + if ( normalize ) { + name = name.toLowerCase(); + // Normalize font-family property, ignore quotes and being case insensitive. (#7322) + // http://www.w3.org/TR/css3-fonts/#font-family-the-font-family-property + if ( name == 'font-family' ) + value = value.toLowerCase().replace( /["']/g, '' ).replace( /\s*,\s*/g, ',' ); + value = CKEDITOR.tools.trim( value ); + } + + retval[ name ] = value; + }); + return retval; } + }; })();