X-Git-Url: https://git.immae.eu/?p=perso%2FImmae%2FProjets%2Fpackagist%2Fpiedsjaloux-ckeditor-component.git;a=blobdiff_plain;f=sources%2Fcore%2Ftools.js;fp=sources%2Fcore%2Ftools.js;h=a4b736d647700bb6b59f3701a0f19b398f14dc9c;hp=ae5b4d0ff3a9c18144ab88dfbd73f000b7775657;hb=317f8f8f0651488f226b5280a8f036c7c135c639;hpb=1096cdefb1c9a3f3c4ca6807e272da6c92e5ed9c diff --git a/sources/core/tools.js b/sources/core/tools.js index ae5b4d0..a4b736d 100644 --- a/sources/core/tools.js +++ b/sources/core/tools.js @@ -1,5 +1,5 @@ /** - * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ @@ -980,6 +980,23 @@ } ); }, + /** + * Normalizes hexadecimal notation so that the color string is always 6 characters long and lowercase. + * + * @param {String} styleText The style data (or just a string containing hex colors) to be converted. + * @returns {String} The style data with hex colors normalized. + */ + normalizeHex: function( styleText ) { + return styleText.replace( /#(([0-9a-f]{3}){1,2})($|;|\s+)/gi, function( match, hexColor, hexColorPart, separator ) { + var normalizedHexColor = hexColor.toLowerCase(); + if ( normalizedHexColor.length == 3 ) { + var parts = normalizedHexColor.split( '' ); + normalizedHexColor = [ parts[ 0 ], parts[ 0 ], parts[ 1 ], parts[ 1 ], parts[ 2 ], parts[ 2 ] ].join( '' ); + } + return '#' + normalizedHexColor + separator; + } ); + }, + /** * Turns inline style text properties into one hash. * @@ -996,21 +1013,24 @@ // 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' ) || '' ); + styleText = temp.setAttribute( 'style', styleText ).getAttribute( 'style' ) || ''; + } + + // Normalize colors. + if ( styleText ) { + styleText = CKEDITOR.tools.normalizeHex( CKEDITOR.tools.convertRgbToHex( styleText ) ); } - // IE will leave a single semicolon when failed to parse the style text. (#3891) + // IE will leave a single semicolon when failed to parse the style text. (http://dev.ckeditor.com/ticket/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 + // Drop extra whitespacing from font-family. if ( name == 'font-family' ) - value = value.toLowerCase().replace( /["']/g, '' ).replace( /\s*,\s*/g, ',' ); + value = value.replace( /\s*,\s*/g, ',' ); value = CKEDITOR.tools.trim( value ); } @@ -1292,6 +1312,67 @@ return false; }, + /** + * Converts a keystroke to its string representation. Returns an object with two fields: + * + * * `display` – A string that should be used for visible labels. + * For Mac devices it uses `⌥` for `ALT`, `⇧` for `SHIFT` and `⌘` for `COMMAND`. + * * `aria` – A string that should be used for ARIA descriptions. + * It does not use special characters such as `⌥`, `⇧` or `⌘`. + * + * var lang = editor.lang.common.keyboard; + * var shortcut = CKEDITOR.tools.keystrokeToString( lang, CKEDITOR.CTRL + 88 ); + * console.log( shortcut.display ); // 'CTRL + X', on Mac '⌘ + X'. + * console.log( shortcut.aria ); // 'CTRL + X', on Mac 'COMMAND + X'. + * + * @since 4.6.0 + * @param {Object} lang A language object with the key name translation. + * @param {Number} keystroke The keystroke to convert. + * @returns {{display: String, aria: String}} + */ + keystrokeToString: function( lang, keystroke ) { + var special = keystroke & 0xFF0000, + key = keystroke & 0x00FFFF, + isMac = CKEDITOR.env.mac, + CTRL = 17, + CMD = 224, + ALT = 18, + SHIFT = 16, + display = [], + aria = []; + + + if ( special & CKEDITOR.CTRL ) { + display.push( isMac ? '⌘' : lang[ CTRL ] ); + aria.push( isMac ? lang[ CMD ] : lang[ CTRL ] ); + } + + if ( special & CKEDITOR.ALT ) { + display.push( isMac ? '⌥' : lang[ ALT ] ); + aria.push( lang[ ALT ] ); + } + + if ( special & CKEDITOR.SHIFT ) { + display.push( isMac ? '⇧' : lang[ SHIFT ] ); + aria.push( lang[ SHIFT ] ); + } + + if ( key ) { + if ( lang[ key ] ) { + display.push( lang[ key ] ); + aria.push( lang[ key ] ); + } else { + display.push( String.fromCharCode( key ) ); + aria.push( String.fromCharCode( key ) ); + } + } + + return { + display: display.join( '+' ), + aria: aria.join( '+' ) + }; + }, + /** * The data URI of a transparent image. May be used e.g. in HTML as an image source or in CSS in `url()`. * @@ -1353,6 +1434,540 @@ } return token; + }, + + /** + * Returns an escaped CSS selector. `CSS.escape()` is used if defined, leading digit is escaped otherwise. + * + * @since 4.5.10 + * @param {String} selector A CSS selector to escape. + * @returns {String} An escaped selector. + */ + escapeCss: function( selector ) { + // Invalid input. + if ( !selector ) { + return ''; + } + + // CSS.escape() can be used. + if ( window.CSS && CSS.escape ) { + return CSS.escape( selector ); + } + + // Simple leading digit escape. + if ( !isNaN( parseInt( selector.charAt( 0 ), 10 ) ) ) { + return '\\3' + selector.charAt( 0 ) + ' ' + selector.substring( 1, selector.length ); + } + + return selector; + }, + + /** + * Detects which mouse button generated a given DOM event. + * + * @since 4.7.3 + * @param {CKEDITOR.dom.event} evt DOM event. + * @returns {Number|Boolean} Returns a number indicating the mouse button or `false` + * if the mouse button cannot be determined. + */ + getMouseButton: function( evt ) { + var evtData = evt.data, + domEvent = evtData && evtData.$; + + if ( !( evtData && domEvent ) ) { + // Added in case when there's no data available. That's the case in some unit test in built version which + // mock event but doesn't put data object. + return false; + } + + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) { + if ( domEvent.button === 4 ) { + return CKEDITOR.MOUSE_BUTTON_MIDDLE; + } else if ( domEvent.button === 1 ) { + return CKEDITOR.MOUSE_BUTTON_LEFT; + } else { + return CKEDITOR.MOUSE_BUTTON_RIGHT; + } + } + + return domEvent.button; + }, + + /** + * A set of functions for operations on styles. + * + * @property {CKEDITOR.tools.style} + */ + style: { + /** + * Methods to parse miscellaneous CSS properties. + * + * @property {CKEDITOR.tools.style.parse} + * @member CKEDITOR.tools.style + */ + parse: { + // Color list based on https://www.w3.org/TR/css-color-4/#named-colors. + _colors: { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aqua: '#00FFFF', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blue: '#0000FF', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + fuchsia: '#FF00FF', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + gray: '#808080', + green: '#008000', + greenyellow: '#ADFF2F', + grey: '#808080', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgray: '#D3D3D3', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + lime: '#00FF00', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + maroon: '#800000', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + navy: '#000080', + oldlace: '#FDF5E6', + olive: '#808000', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#FF0000', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + silver: '#C0C0C0', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + teal: '#008080', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + white: '#FFFFFF', + whitesmoke: '#F5F5F5', + yellow: '#FFFF00', + yellowgreen: '#9ACD32' + }, + + _borderStyle: [ + 'none', + 'hidden', + 'dotted', + 'dashed', + 'solid', + 'double', + 'groove', + 'ridge', + 'inset', + 'outset' + ], + + _widthRegExp: /^(thin|medium|thick|[\+-]?\d+(\.\d+)?[a-z%]+|[\+-]?0+(\.0+)?|\.\d+[a-z%]+)$/, + + _rgbaRegExp: /rgba?\(\s*\d+%?\s*,\s*\d+%?\s*,\s*\d+%?\s*(?:,\s*[0-9.]+\s*)?\)/gi, + + _hslaRegExp: /hsla?\(\s*[0-9.]+\s*,\s*\d+%\s*,\s*\d+%\s*(?:,\s*[0-9.]+\s*)?\)/gi, + + /** + * Parses the `value` used as a `background` property shorthand and returns information as an object. + * + * **Note:** Currently only the `color` property is extracted. Any other parts will go into the `unprocessed` property. + * + * var background = CKEDITOR.tools.style.parse.background( '#0C0 url(foo.png)' ); + * console.log( background ); + * // Logs: { color: '#0C0', unprocessed: 'url(foo.png)' } + * + * @param {String} value The value of the `background` property. + * @returns {Object} An object with information extracted from the background. + * @returns {String} return.color The **first** color value found. The color format remains the same as in input. + * @returns {String} return.unprocessed The remaining part of the `value` that has not been processed. + * @member CKEDITOR.tools.style.parse + */ + background: function( value ) { + var ret = {}, + colors = this._findColor( value ); + + if ( colors.length ) { + ret.color = colors[ 0 ]; + + CKEDITOR.tools.array.forEach( colors, function( colorToken ) { + value = value.replace( colorToken, '' ); + } ); + } + + value = CKEDITOR.tools.trim( value ); + + if ( value ) { + // If anything was left unprocessed include it as unprocessed part. + ret.unprocessed = value; + } + + return ret; + }, + + /** + * Parses the `margin` CSS property shorthand format. + * + * console.log( CKEDITOR.tools.parse.margin( '3px 0 2' ) ); + * // Logs: { top: "3px", right: "0", bottom: "2", left: "0" } + * + * @param {String} value The `margin` property value. + * @returns {Object} + * @returns {Number} return.top Top margin. + * @returns {Number} return.right Right margin. + * @returns {Number} return.bottom Bottom margin. + * @returns {Number} return.left Left margin. + * @member CKEDITOR.tools.style.parse + */ + margin: function( value ) { + var ret = {}; + + var widths = value.match( /(?:\-?[\.\d]+(?:%|\w*)|auto|inherit|initial|unset)/g ) || [ '0px' ]; + + switch ( widths.length ) { + case 1: + mapStyles( [ 0, 0, 0, 0 ] ); + break; + case 2: + mapStyles( [ 0, 1, 0, 1 ] ); + break; + case 3: + mapStyles( [ 0, 1, 2, 1 ] ); + break; + case 4: + mapStyles( [ 0, 1, 2, 3 ] ); + break; + } + + function mapStyles( map ) { + ret.top = widths[ map[ 0 ] ]; + ret.right = widths[ map[ 1 ] ]; + ret.bottom = widths[ map[ 2 ] ]; + ret.left = widths[ map[ 3 ] ]; + } + + return ret; + }, + + /** + * Parses the `border` CSS property shorthand format. + * This CSS property does not support inheritance (https://www.w3.org/TR/css3-background/#the-border-shorthands). + * + * console.log( CKEDITOR.tools.style.parse.border( '3px solid #ffeedd' ) ); + * // Logs: { width: "3px", style: "solid", color: "#ffeedd" } + * + * @param {String} value The `border` property value. + * @returns {Object} + * @returns {String} return.width The border-width attribute. + * @returns {String} return.style The border-style attribute. + * @returns {String} return.color The border-color attribute. + * @member CKEDITOR.tools.style.parse + */ + border: function( value ) { + var ret = {}, + input = value.split( /\s+/ ); + + CKEDITOR.tools.array.forEach( input, function( val ) { + if ( !ret.color ) { + var parseColor = CKEDITOR.tools.style.parse._findColor( val ); + if ( parseColor.length ) { + ret.color = parseColor[ 0 ]; + return; + } + } + + if ( !ret.style ) { + if ( CKEDITOR.tools.indexOf( CKEDITOR.tools.style.parse._borderStyle, val ) !== -1 ) { + ret.style = val; + return; + } + } + + if ( !ret.width ) { + if ( CKEDITOR.tools.style.parse._widthRegExp.test( val ) ) { + ret.width = val; + return; + } + } + + } ); + return ret; + }, + + /** + * Searches the `value` for any CSS color occurrences and returns it. + * + * @private + * @param {String} value + * @returns {String[]} An array of matched results. + * @member CKEDITOR.tools.style.parse + */ + _findColor: function( value ) { + var ret = [], + arrayTools = CKEDITOR.tools.array; + + + // Check for rgb(a). + ret = ret.concat( value.match( this._rgbaRegExp ) || [] ); + + // Check for hsl(a). + ret = ret.concat( value.match( this._hslaRegExp ) || [] ); + + ret = ret.concat( arrayTools.filter( value.split( /\s+/ ), function( colorEntry ) { + // Check for hex format. + if ( colorEntry.match( /^\#[a-f0-9]{3}(?:[a-f0-9]{3})?$/gi ) ) { + return true; + } + + // Check for preset names. + return colorEntry.toLowerCase() in CKEDITOR.tools.style.parse._colors; + } ) ); + + return ret; + } + } + }, + + /** + * A set of array helpers. + * + * @property {CKEDITOR.tools.array} + * @member CKEDITOR.tools + */ + array: { + /** + * Returns a copy of `array` filtered using the `fn` function. Any elements that the `fn` will return `false` for + * will get removed from the returned array. + * + * var filtered = this.array.filter( [ 0, 1, 2, 3 ], function( value ) { + * // Leave only values equal or greater than 2. + * return value >= 2; + * } ); + * console.log( filtered ); + * // Logs: [ 2, 3 ] + * + * @param {Array} array + * @param {Function} fn A function that gets called with each `array` item. Any item that `fn` + * returned a `false`-alike value for will be filtered out of the `array`. + * @param {Mixed} fn.value The currently iterated array value. + * @param {Number} fn.index The index of the currently iterated value in an array. + * @param {Array} fn.array The original array passed as the `array` variable. + * @param {Mixed} [thisArg=undefined] A context object for `fn`. + * @returns {Array} The filtered array. + * @member CKEDITOR.tools.array + */ + filter: function( array, fn, thisArg ) { + var ret = []; + + this.forEach( array, function( val, i ) { + if ( fn.call( thisArg, val, i, array ) ) { + ret.push( val ); + } + } ); + + return ret; + }, + + /** + * Iterates over every element in the `array`. + * + * @param {Array} array An array to be iterated over. + * @param {Function} fn The function called for every `array` element. + * @param {Mixed} fn.value The currently iterated array value. + * @param {Number} fn.index The index of the currently iterated value in an array. + * @param {Array} fn.array The original array passed as an `array` variable. + * @param {Mixed} [thisArg=undefined] The context object for `fn`. + * @member CKEDITOR.tools.array + */ + forEach: function( array, fn, thisArg ) { + var len = array.length, + i; + + for ( i = 0; i < len; i++ ) { + fn.call( thisArg, array[ i ], i, array ); + } + }, + + /** + * Applies a function to each element of an array and returns the array of results in the same order. + * Note the order of the parameters. + * + * @param {Array} array An array of elements that `fn` is applied on. + * @param {Function} fn A function with the signature `a -> b`. + * @param {Mixed} [thisArg=undefined] The context object for `fn`. + * @returns {Array} An array of mapped elements. + * @member CKEDITOR.tools.array + * @since 4.6.2 + */ + map: function( array, fn, thisArg ) { + var result = []; + for ( var i = 0; i < array.length; i++ ) { + result.push( fn.call( thisArg, array[ i ], i, array ) ); + } + return result; + }, + + /** + * Applies a function against each value in an array storing the result in an accumulator passed to the next iteration. + * Note the order of the parameters. + * + * @param {Array} array An array of elements that `fn` is applied on. + * @param {Function} fn A function with the signature `(accumulator, a, index, array) -> b`. + * @param {Mixed} initial Initial value of the accumulator. + * @param {Mixed} [thisArg=undefined] The context object for `fn`. + * @returns {Mixed} The final value of the accumulator. + * @member CKEDITOR.tools.array + * @since 4.6.2 + */ + reduce: function( array, fn, initial, thisArg ) { + var acc = initial; + for ( var i = 0; i < array.length; i++ ) { + acc = fn.call( thisArg, acc, array[ i ], i, array ); + } + return acc; + } + }, + + /** + * A set of object helpers. + * + * @property {CKEDITOR.tools.object} + * @member CKEDITOR.tools + */ + object: { + /** + * Returns the first key from `obj` which has a given `value`. + * + * @param {Object} obj An object whose `key` is looked for. + * @param {Mixed} value An object's `value` to be looked for. + * @returns {String/null} Matched `key` or `null` if not found. + * @member CKEDITOR.tools.object + */ + + findKey: function( obj, value ) { + if ( typeof obj !== 'object' ) { + return null; + } + + var key; + + for ( key in obj ) { + if ( obj[ key ] === value ) { + return key; + } + } + + return null; + } } }; @@ -1381,6 +1996,78 @@ return result; } + + /** + * @member CKEDITOR.tools.array + * @method indexOf + * @inheritdoc CKEDITOR.tools#indexOf + */ + CKEDITOR.tools.array.indexOf = CKEDITOR.tools.indexOf; + + /** + * @member CKEDITOR.tools.array + * @method isArray + * @inheritdoc CKEDITOR.tools#isArray + */ + CKEDITOR.tools.array.isArray = CKEDITOR.tools.isArray; + + /** + * Left mouse button. + * + * @since 4.7.3 + * @readonly + * @property {Number} [=0] + * @member CKEDITOR + */ + CKEDITOR.MOUSE_BUTTON_LEFT = 0; + + /** + * Middle mouse button. + * + * @since 4.7.3 + * @readonly + * @property {Number} [=1] + * @member CKEDITOR + */ + CKEDITOR.MOUSE_BUTTON_MIDDLE = 1; + + /** + * Right mouse button. + * + * @since 4.7.3 + * @readonly + * @property {Number} [=2] + * @member CKEDITOR + */ + CKEDITOR.MOUSE_BUTTON_RIGHT = 2; + + /** + * The namespace containing functions to work on CSS properties. + * + * @since 4.6.1 + * @class CKEDITOR.tools.style + */ + + /** + * The namespace with helper functions to parse some common CSS properties. + * + * @since 4.6.1 + * @class CKEDITOR.tools.style.parse + */ + + /** + * The namespace with helper functions and polyfills for arrays. + * + * @since 4.6.1 + * @class CKEDITOR.tools.array + */ + + /** + * The namespace with helper functions and polyfills for objects. + * + * @since 4.7.1 + * @class CKEDITOR.tools.object + */ } )(); // PACKAGER_RENAME( CKEDITOR.tools )