From 9aa095d67d46c1eddfef3ea0be7ca1dab88be45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Mon, 31 Mar 2014 15:55:06 +0200 Subject: [PATCH 01/24] CKEDITOR.style#apply/remove's argument should be editor instance not document. --- core/style.js | 91 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/core/style.js b/core/style.js index 72ba8abc242..956a21f7198 100644 --- a/core/style.js +++ b/core/style.js @@ -125,59 +125,88 @@ CKEDITOR.STYLE_OBJECT = 3; }; /** - * Apply the style upon the editor's current selection. + * Applies the style upon the editor's current selection. Shorthand for + * {@link CKEDITOR.style#apply}. * * @member CKEDITOR.editor * @param {CKEDITOR.style} style */ CKEDITOR.editor.prototype.applyStyle = function( style ) { - if ( style.checkApplicable( this.elementPath() ) ) { - var initialEnterMode = style._.enterMode; - - // See comment in removeStyle. - if ( !initialEnterMode ) - style._.enterMode = this.activeEnterMode; - applyStyleOnSelection.call( style, this.getSelection() ); - style._.enterMode = initialEnterMode; - } + style.apply( this ); }; /** - * Remove the style from the editor's current selection. + * Removes the style from the editor's current selection. Shorthand for + * {@link CKEDITOR.style#remove}. * * @member CKEDITOR.editor * @param {CKEDITOR.style} style */ CKEDITOR.editor.prototype.removeStyle = function( style ) { - if ( style.checkApplicable( this.elementPath() ) ) { - var initialEnterMode = style._.enterMode; - - // There's no other way to pass editor's enter mode to the - // styles system and we need to do that (see #10190). - // However, we should not change style's enter mode if it was - // already set, because that could break backward compatibility. - if ( !initialEnterMode ) - style._.enterMode = this.activeEnterMode; - applyStyleOnSelection.call( style, this.getSelection(), 1 ); - style._.enterMode = initialEnterMode; - } + style.remove( this ); }; CKEDITOR.style.prototype = { /** - * @param {CKEDITOR.dom.document} document - * @todo + * Applies the style upon the editor's current selection. + * + * Before style is applied the method checks if {@link #checkApplicable style is applicable}. + * + * **Note:** The recommended way of applying style is by using the + * {@link CKEDITOR.editor#applyStyle} method which is a shorthand for this method. + * + * @param {CKEDITOR.editor/CKEDITOR.dom.document} editor The editor instance in which + * the style will be applied. + * A {@link CKEDITOR.dom.document} instance is accepted for backward compatibility + * reasons, although since CKEditor 4.4 this type of argument is deprecated. */ - apply: function( document ) { - applyStyleOnSelection.call( this, document.getSelection() ); + apply: function( editor ) { + // Backward compatibility. + if ( editor instanceof CKEDITOR.dom.document ) + return applyStyleOnSelection.call( this, editor.getSelection() ); + + if ( this.checkApplicable( editor.elementPath() ) ) { + var initialEnterMode = this._.enterMode; + + // See comment in removeStyle. + if ( !initialEnterMode ) + this._.enterMode = editor.activeEnterMode; + applyStyleOnSelection.call( this, editor.getSelection() ); + this._.enterMode = initialEnterMode; + } }, /** - * @param {CKEDITOR.dom.document} document - * @todo + * Removes the style from the editor's current selection. + * + * Before style is applied the method checks if {@link #checkApplicable style could be applied}. + * + * **Note:** The recommended way of removing style is by using the + * {@link CKEDITOR.editor#removeStyle} method which is a shorthand for this method. + * + * @param {CKEDITOR.editor/CKEDITOR.dom.document} editor The editor instance in which + * the style will be removed. + * A {@link CKEDITOR.dom.document} instance is accepted for backward compatibility + * reasons, although since CKEditor 4.4 this type of argument is deprecated. */ - remove: function( document ) { - applyStyleOnSelection.call( this, document.getSelection(), 1 ); + remove: function( editor ) { + // Backward compatibility. + if ( editor instanceof CKEDITOR.dom.document ) + return applyStyleOnSelection.call( this, editor.getSelection(), 1 ); + + if ( this.checkApplicable( editor.elementPath() ) ) { + var initialEnterMode = this._.enterMode; + + // Before CKEditor 4.4 style knew nothing about editor, so in order to provide enterMode + // which should be used developers were forced to hack the style object (see #10190). + // Since CKEditor 4.4 style knows about editor (at least when it's being applied/removed), but we + // use _.enterMode for backward compatibility with those hacks. + // Note: we should not change style's enter mode if it was already set. + if ( !initialEnterMode ) + this._.enterMode = editor.activeEnterMode; + applyStyleOnSelection.call( this, editor.getSelection(), 1 ); + this._.enterMode = initialEnterMode; + } }, /** From cfa2399ada78da9f79576e9950a1ccafbf39feda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Mon, 31 Mar 2014 16:03:35 +0200 Subject: [PATCH 02/24] Grouped methods augmenting editor in one place. --- core/style.js | 229 +++++++++++++++++++++++++------------------------- 1 file changed, 115 insertions(+), 114 deletions(-) diff --git a/core/style.js b/core/style.js index 956a21f7198..711b878ba83 100644 --- a/core/style.js +++ b/core/style.js @@ -5,57 +5,6 @@ 'use strict'; -/** - * Registers a function to be called whenever the selection position changes in the - * editing area. The current state is passed to the function. The possible - * states are {@link CKEDITOR#TRISTATE_ON} and {@link CKEDITOR#TRISTATE_OFF}. - * - * // Create a style object for the element. - * var style = new CKEDITOR.style( { element: 'b' } ); - * var editor = CKEDITOR.instances.editor1; - * editor.attachStyleStateChange( style, function( state ) { - * if ( state == CKEDITOR.TRISTATE_ON ) - * alert( 'The current state for the B element is ON' ); - * else - * alert( 'The current state for the B element is OFF' ); - * } ); - * - * @member CKEDITOR.editor - * @param {CKEDITOR.style} style The style to be watched. - * @param {Function} callback The function to be called. - */ -CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) { - // Try to get the list of attached callbacks. - var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; - - // If it doesn't exist, it means this is the first call. So, let's create - // all the structure to manage the style checks and the callback calls. - if ( !styleStateChangeCallbacks ) { - // Create the callbacks array. - styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; - - // Attach to the selectionChange event, so we can check the styles at - // that point. - this.on( 'selectionChange', function( ev ) { - // Loop throw all registered callbacks. - for ( var i = 0; i < styleStateChangeCallbacks.length; i++ ) { - var callback = styleStateChangeCallbacks[ i ]; - - // Check the current state for the style defined for that callback. - var currentState = callback.style.checkActive( ev.data.path ) ? - CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; - - // Call the callback function, passing the current state to it. - callback.fn.call( this, currentState ); - } - } ); - } - - // Save the callback info, so it can be checked on the next occurrence of - // selectionChange. - styleStateChangeCallbacks.push( { style: style, fn: callback } ); -}; - CKEDITOR.STYLE_BLOCK = 1; CKEDITOR.STYLE_INLINE = 2; CKEDITOR.STYLE_OBJECT = 3; @@ -124,28 +73,6 @@ CKEDITOR.STYLE_OBJECT = 3; }; }; - /** - * Applies the style upon the editor's current selection. Shorthand for - * {@link CKEDITOR.style#apply}. - * - * @member CKEDITOR.editor - * @param {CKEDITOR.style} style - */ - CKEDITOR.editor.prototype.applyStyle = function( style ) { - style.apply( this ); - }; - - /** - * Removes the style from the editor's current selection. Shorthand for - * {@link CKEDITOR.style#remove}. - * - * @member CKEDITOR.editor - * @param {CKEDITOR.style} style - */ - CKEDITOR.editor.prototype.removeStyle = function( style ) { - style.remove( this ); - }; - CKEDITOR.style.prototype = { /** * Applies the style upon the editor's current selection. @@ -1644,53 +1571,127 @@ CKEDITOR.loadStylesSet = function( name, url, callback ) { CKEDITOR.stylesSet.load( name, callback ); }; - -/** - * Gets the current styleSet for this instance. - * - * editor.getStylesSet( function( stylesDefinitions ) {} ); - * - * See also {@link CKEDITOR.editor#stylesSet} event. - * - * @param {Function} callback The function to be called with the styles data. - * @member CKEDITOR.editor - */ -CKEDITOR.editor.prototype.getStylesSet = function( callback ) { - if ( !this._.stylesDefinitions ) { - var editor = this, - // Respect the backwards compatible definition entry - configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet; - - // The false value means that none styles should be loaded. - if ( configStyleSet === false ) { - callback( null ); - return; +CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { + /** + * Registers a function to be called whenever the selection position changes in the + * editing area. The current state is passed to the function. The possible + * states are {@link CKEDITOR#TRISTATE_ON} and {@link CKEDITOR#TRISTATE_OFF}. + * + * // Create a style object for the element. + * var style = new CKEDITOR.style( { element: 'b' } ); + * var editor = CKEDITOR.instances.editor1; + * editor.attachStyleStateChange( style, function( state ) { + * if ( state == CKEDITOR.TRISTATE_ON ) + * alert( 'The current state for the B element is ON' ); + * else + * alert( 'The current state for the B element is OFF' ); + * } ); + * + * @member CKEDITOR.editor + * @param {CKEDITOR.style} style The style to be watched. + * @param {Function} callback The function to be called. + */ + attachStyleStateChange: function( style, callback ) { + // Try to get the list of attached callbacks. + var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; + + // If it doesn't exist, it means this is the first call. So, let's create + // all the structure to manage the style checks and the callback calls. + if ( !styleStateChangeCallbacks ) { + // Create the callbacks array. + styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; + + // Attach to the selectionChange event, so we can check the styles at + // that point. + this.on( 'selectionChange', function( ev ) { + // Loop throw all registered callbacks. + for ( var i = 0; i < styleStateChangeCallbacks.length; i++ ) { + var callback = styleStateChangeCallbacks[ i ]; + + // Check the current state for the style defined for that callback. + var currentState = callback.style.checkActive( ev.data.path ) ? + CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; + + // Call the callback function, passing the current state to it. + callback.fn.call( this, currentState ); + } + } ); } - // #5352 Allow to define the styles directly in the config object - if ( configStyleSet instanceof Array ) { - editor._.stylesDefinitions = configStyleSet; - callback( configStyleSet ); - return; - } + // Save the callback info, so it can be checked on the next occurrence of + // selectionChange. + styleStateChangeCallbacks.push( { style: style, fn: callback } ); + }, - // Default value is 'default'. - if ( !configStyleSet ) - configStyleSet = 'default'; + /** + * Applies the style upon the editor's current selection. Shorthand for + * {@link CKEDITOR.style#apply}. + * + * @member CKEDITOR.editor + * @param {CKEDITOR.style} style + */ + applyStyle: function( style ) { + style.apply( this ); + }, - var partsStylesSet = configStyleSet.split( ':' ), - styleSetName = partsStylesSet[ 0 ], - externalPath = partsStylesSet[ 1 ]; + /** + * Removes the style from the editor's current selection. Shorthand for + * {@link CKEDITOR.style#remove}. + * + * @member CKEDITOR.editor + * @param {CKEDITOR.style} style + */ + removeStyle: function( style ) { + style.remove( this ); + }, - CKEDITOR.stylesSet.addExternal( styleSetName, externalPath ? partsStylesSet.slice( 1 ).join( ':' ) : CKEDITOR.getUrl( 'styles.js' ), '' ); + /** + * Gets the current styleSet for this instance. + * + * editor.getStylesSet( function( stylesDefinitions ) {} ); + * + * See also {@link CKEDITOR.editor#stylesSet} event. + * + * @param {Function} callback The function to be called with the styles data. + * @member CKEDITOR.editor + */ + getStylesSet: function( callback ) { + if ( !this._.stylesDefinitions ) { + var editor = this, + // Respect the backwards compatible definition entry + configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet; + + // The false value means that none styles should be loaded. + if ( configStyleSet === false ) { + callback( null ); + return; + } - CKEDITOR.stylesSet.load( styleSetName, function( stylesSet ) { - editor._.stylesDefinitions = stylesSet[ styleSetName ]; - callback( editor._.stylesDefinitions ); - } ); - } else - callback( this._.stylesDefinitions ); -}; + // #5352 Allow to define the styles directly in the config object + if ( configStyleSet instanceof Array ) { + editor._.stylesDefinitions = configStyleSet; + callback( configStyleSet ); + return; + } + + // Default value is 'default'. + if ( !configStyleSet ) + configStyleSet = 'default'; + + var partsStylesSet = configStyleSet.split( ':' ), + styleSetName = partsStylesSet[ 0 ], + externalPath = partsStylesSet[ 1 ]; + + CKEDITOR.stylesSet.addExternal( styleSetName, externalPath ? partsStylesSet.slice( 1 ).join( ':' ) : CKEDITOR.getUrl( 'styles.js' ), '' ); + + CKEDITOR.stylesSet.load( styleSetName, function( stylesSet ) { + editor._.stylesDefinitions = stylesSet[ styleSetName ]; + callback( editor._.stylesDefinitions ); + } ); + } else + callback( this._.stylesDefinitions ); + } +} ); /** * Indicates that fully selected read-only elements will be included when From 90936e763ac37fe9b84fdec8c5a850ca2208ef85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Mon, 31 Mar 2014 17:01:45 +0200 Subject: [PATCH 03/24] Documentation improvements and code style fixes. --- core/style.js | 65 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/core/style.js b/core/style.js index 711b878ba83..517875a9d10 100644 --- a/core/style.js +++ b/core/style.js @@ -137,39 +137,59 @@ CKEDITOR.STYLE_OBJECT = 3; }, /** + * Applies the style upon provided range. Unlike {@link #apply} this + * method does not take care of setting selection, however the range + * is updated to the correct place. + * + * **Note:** If you want to apply the style upon the editor's selection + * you probably want to use {@link CKEDITOR.editor#applyStyle}. + * * @param {CKEDITOR.dom.range} range - * @todo */ applyToRange: function( range ) { - return ( this.applyToRange = - this.type == CKEDITOR.STYLE_INLINE ? applyInlineStyle : - this.type == CKEDITOR.STYLE_BLOCK ? applyBlockStyle : - this.type == CKEDITOR.STYLE_OBJECT ? applyObjectStyle : - null ).call( this, range ); + this.applyToRange = + this.type == CKEDITOR.STYLE_INLINE ? applyInlineStyle : + this.type == CKEDITOR.STYLE_BLOCK ? applyBlockStyle : + this.type == CKEDITOR.STYLE_OBJECT ? applyObjectStyle : + null; + + return this.applyToRange( range ); }, /** + * Removes the style from provided range. Unlike {@link #remove} this + * method does not take care of setting selection, however the range + * is updated to the correct place. + * + * **Note:** If you want to remove the style from the editor's selection + * you probably want to use {@link CKEDITOR.editor#removeStyle}. + * * @param {CKEDITOR.dom.range} range - * @todo */ removeFromRange: function( range ) { - return ( this.removeFromRange = - this.type == CKEDITOR.STYLE_INLINE ? removeInlineStyle : - this.type == CKEDITOR.STYLE_BLOCK ? removeBlockStyle : - this.type == CKEDITOR.STYLE_OBJECT ? removeObjectStyle : - null ).call( this, range ); + this.removeFromRange = + this.type == CKEDITOR.STYLE_INLINE ? removeInlineStyle : + this.type == CKEDITOR.STYLE_BLOCK ? removeBlockStyle : + this.type == CKEDITOR.STYLE_OBJECT ? removeObjectStyle : + null; + + return this.removeFromRange( range ); }, /** + * Applies style to the element. This methods bypasses every checks + * and applies style attributes directly on the provided element. Use with caution. + * + * See {@link CKEDITOR.editor#applyStyle}. + * * @param {CKEDITOR.dom.element} element - * @todo */ applyToObject: function( element ) { setupElement( element, this ); }, /** - * Get the style state inside an element path. + * Gets the style state inside an element path. * * @param {CKEDITOR.dom.elementPath} elementPath * @returns {Boolean} `true` if the element is active in the path. @@ -207,9 +227,9 @@ CKEDITOR.STYLE_OBJECT = 3; * Whether this style can be applied at the specified elements-path. * * @param {CKEDITOR.dom.elementPath} elementPath The elements-path to - * check the style against. + * check the style against. * @param {CKEDITOR.filter} [filter] If defined, the style will be - * checked against this filter as well. + * checked against this filter as well. * @returns {Boolean} `true` if this style can be applied at the element path. */ checkApplicable: function( elementPath, filter ) { @@ -227,12 +247,11 @@ CKEDITOR.STYLE_OBJECT = 3; }, /** - * Check if the element matches the current style definition. + * Checks if the element matches the current style definition. * * @param {CKEDITOR.dom.element} element * @param {Boolean} fullMatch * @returns {Boolean} - * @todo */ checkElementMatch: function( element, fullMatch ) { var def = this._.definition; @@ -281,7 +300,6 @@ CKEDITOR.STYLE_OBJECT = 3; * @param {CKEDITOR.dom.element} element * @param {Boolean} fullMatch * @returns {Boolean} - * @todo */ checkElementRemovable: function( element, fullMatch ) { // Check element matches the style itself. @@ -320,8 +338,8 @@ CKEDITOR.STYLE_OBJECT = 3; /** * Builds the preview HTML based on the styles definition. * - * @param label - * @todo + * @param {String} [label] The label used in the style preview. + * @return {String} The HTML of preview. */ buildPreview: function( label ) { var styleDefinition = this._.definition, @@ -357,12 +375,11 @@ CKEDITOR.STYLE_OBJECT = 3; }; /** - * Build the cssText based on the styles definition. + * Builds the inline style text based on the style definition. * * @static * @param styleDefinition - * @returns {String} - * @todo + * @returns {String} Inline style text. */ CKEDITOR.style.getStyleText = function( styleDefinition ) { // If we have already computed it, just return it. From 5267cb14d33acf1baa4476ce7f82f1becf05f74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Mon, 31 Mar 2014 18:11:47 +0200 Subject: [PATCH 04/24] Implemented basic custom style handlers registering. --- core/style.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/style.js b/core/style.js index 517875a9d10..fd75dd8e3e3 100644 --- a/core/style.js +++ b/core/style.js @@ -34,6 +34,8 @@ CKEDITOR.STYLE_OBJECT = 3; * @todo */ CKEDITOR.style = function( styleDefinition, variablesValues ) { + if ( typeof styleDefinition.type == 'string' ) + return new CKEDITOR.style.customHandlers[ styleDefinition.type ]( styleDefinition ); // Inline style text as attribute should be converted // to styles object. @@ -418,6 +420,21 @@ CKEDITOR.STYLE_OBJECT = 3; return ( styleDefinition._ST = stylesText ); }; + CKEDITOR.style.customHandlers = {}; + CKEDITOR.style.addCustomHandler = function( definition ) { + var styleClass = function( styleDefinition ) { + this._ = { + definition: styleDefinition + }; + }; + styleClass.prototype = CKEDITOR.tools.prototypedCopy( CKEDITOR.style.prototype ); + CKEDITOR.tools.extend( styleClass.prototype, definition, true ); + + this.customHandlers[ definition.type ] = styleClass; + + return styleClass; + }; + // Gets the parent element which blocks the styling for an element. This // can be done through read-only elements (contenteditable=false) or // elements with the "data-nostyle" attribute. From 55cec8da7adc586fdad4bd0d7ec59c92f45f8839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 11:56:05 +0200 Subject: [PATCH 05/24] Implemented styleHandler.setup. --- core/style.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/style.js b/core/style.js index fd75dd8e3e3..b5a5c437456 100644 --- a/core/style.js +++ b/core/style.js @@ -426,6 +426,9 @@ CKEDITOR.STYLE_OBJECT = 3; this._ = { definition: styleDefinition }; + + if ( this.setup ) + this.setup( styleDefinition ); }; styleClass.prototype = CKEDITOR.tools.prototypedCopy( CKEDITOR.style.prototype ); CKEDITOR.tools.extend( styleClass.prototype, definition, true ); From 112a3759fe94488aba0cb0bbf87402388288eff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 13:49:00 +0200 Subject: [PATCH 06/24] Added editor instance to required arguments of style instance methods. --- core/style.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/core/style.js b/core/style.js index b5a5c437456..c892eefb44f 100644 --- a/core/style.js +++ b/core/style.js @@ -147,6 +147,10 @@ CKEDITOR.STYLE_OBJECT = 3; * you probably want to use {@link CKEDITOR.editor#applyStyle}. * * @param {CKEDITOR.dom.range} range + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. */ applyToRange: function( range ) { this.applyToRange = @@ -167,6 +171,10 @@ CKEDITOR.STYLE_OBJECT = 3; * you probably want to use {@link CKEDITOR.editor#removeStyle}. * * @param {CKEDITOR.dom.range} range + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. */ removeFromRange: function( range ) { this.removeFromRange = @@ -185,6 +193,10 @@ CKEDITOR.STYLE_OBJECT = 3; * See {@link CKEDITOR.editor#applyStyle}. * * @param {CKEDITOR.dom.element} element + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. */ applyToObject: function( element ) { setupElement( element, this ); @@ -194,6 +206,10 @@ CKEDITOR.STYLE_OBJECT = 3; * Gets the style state inside an element path. * * @param {CKEDITOR.dom.elementPath} elementPath + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. * @returns {Boolean} `true` if the element is active in the path. */ checkActive: function( elementPath ) { @@ -230,11 +246,19 @@ CKEDITOR.STYLE_OBJECT = 3; * * @param {CKEDITOR.dom.elementPath} elementPath The elements-path to * check the style against. + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. * @param {CKEDITOR.filter} [filter] If defined, the style will be * checked against this filter as well. * @returns {Boolean} `true` if this style can be applied at the element path. */ - checkApplicable: function( elementPath, filter ) { + checkApplicable: function( elementPath, editor, filter ) { + // Backward compatibility. + if ( editor && editor instanceof CKEDITOR.filter ) + filter = editor; + if ( filter && !filter.check( this ) ) return false; @@ -253,6 +277,10 @@ CKEDITOR.STYLE_OBJECT = 3; * * @param {CKEDITOR.dom.element} element * @param {Boolean} fullMatch + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. * @returns {Boolean} */ checkElementMatch: function( element, fullMatch ) { @@ -301,6 +329,10 @@ CKEDITOR.STYLE_OBJECT = 3; * * @param {CKEDITOR.dom.element} element * @param {Boolean} fullMatch + * @param {CKEDITOR.editor} editor The editor instance. Required argument since + * CKEditor 4.4. The style system will work without it, but it's highly + * recommended to provide it for integration with all features. See the {#addCustomHandler} method + * documentation for argumentation. * @returns {Boolean} */ checkElementRemovable: function( element, fullMatch ) { From 61e42630fd37258b124949523cd2870ca7287e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 14:43:18 +0200 Subject: [PATCH 07/24] Pass editor instance to style's methods in every place where we can use custom style. --- core/style.js | 24 ++++++++++++------------ plugins/div/dialogs/div.js | 4 ++-- plugins/find/dialogs/find.js | 4 ++-- plugins/font/plugin.js | 2 +- plugins/format/plugin.js | 4 ++-- plugins/language/plugin.js | 2 +- plugins/link/dialogs/link.js | 2 +- plugins/stylescombo/plugin.js | 10 +++++----- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/core/style.js b/core/style.js index c892eefb44f..ab7dba02d8a 100644 --- a/core/style.js +++ b/core/style.js @@ -94,13 +94,13 @@ CKEDITOR.STYLE_OBJECT = 3; if ( editor instanceof CKEDITOR.dom.document ) return applyStyleOnSelection.call( this, editor.getSelection() ); - if ( this.checkApplicable( editor.elementPath() ) ) { + if ( this.checkApplicable( editor.elementPath(), editor ) ) { var initialEnterMode = this._.enterMode; // See comment in removeStyle. if ( !initialEnterMode ) this._.enterMode = editor.activeEnterMode; - applyStyleOnSelection.call( this, editor.getSelection() ); + applyStyleOnSelection.call( this, editor.getSelection(), 0, editor ); this._.enterMode = initialEnterMode; } }, @@ -123,7 +123,7 @@ CKEDITOR.STYLE_OBJECT = 3; if ( editor instanceof CKEDITOR.dom.document ) return applyStyleOnSelection.call( this, editor.getSelection(), 1 ); - if ( this.checkApplicable( editor.elementPath() ) ) { + if ( this.checkApplicable( editor.elementPath(), editor ) ) { var initialEnterMode = this._.enterMode; // Before CKEditor 4.4 style knew nothing about editor, so in order to provide enterMode @@ -133,7 +133,7 @@ CKEDITOR.STYLE_OBJECT = 3; // Note: we should not change style's enter mode if it was already set. if ( !initialEnterMode ) this._.enterMode = editor.activeEnterMode; - applyStyleOnSelection.call( this, editor.getSelection(), 1 ); + applyStyleOnSelection.call( this, editor.getSelection(), 1, editor ); this._.enterMode = initialEnterMode; } }, @@ -212,10 +212,10 @@ CKEDITOR.STYLE_OBJECT = 3; * documentation for argumentation. * @returns {Boolean} `true` if the element is active in the path. */ - checkActive: function( elementPath ) { + checkActive: function( elementPath, editor ) { switch ( this.type ) { case CKEDITOR.STYLE_BLOCK: - return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true ); + return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true, editor ); case CKEDITOR.STYLE_OBJECT: case CKEDITOR.STYLE_INLINE: @@ -234,7 +234,7 @@ CKEDITOR.STYLE_OBJECT = 3; continue; } - if ( this.checkElementRemovable( element, true ) ) + if ( this.checkElementRemovable( element, true, editor ) ) return true; } } @@ -335,9 +335,9 @@ CKEDITOR.STYLE_OBJECT = 3; * documentation for argumentation. * @returns {Boolean} */ - checkElementRemovable: function( element, fullMatch ) { + checkElementRemovable: function( element, fullMatch, editor ) { // Check element matches the style itself. - if ( this.checkElementMatch( element, fullMatch ) ) + if ( this.checkElementMatch( element, fullMatch, editor ) ) return true; // Check if the element matches the style overrides. @@ -1552,7 +1552,7 @@ CKEDITOR.STYLE_OBJECT = 3; return true; } - function applyStyleOnSelection( selection, remove ) { + function applyStyleOnSelection( selection, remove, editor ) { var doc = selection.document, ranges = selection.getRanges(), func = remove ? this.removeFromRange : this.applyToRange, @@ -1560,7 +1560,7 @@ CKEDITOR.STYLE_OBJECT = 3; var iterator = ranges.createIterator(); while ( ( range = iterator.getNextRange() ) ) - func.call( this, range ); + func.call( this, range, editor ); selection.selectRanges( ranges ); doc.removeCustomData( 'doc_processing_style' ); @@ -1678,7 +1678,7 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { var callback = styleStateChangeCallbacks[ i ]; // Check the current state for the style defined for that callback. - var currentState = callback.style.checkActive( ev.data.path ) ? + var currentState = callback.style.checkActive( ev.data.path, this ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; // Call the callback function, passing the current state to it. diff --git a/plugins/div/dialogs/div.js b/plugins/div/dialogs/div.js index 7630f28271a..146a864a17c 100644 --- a/plugins/div/dialogs/div.js +++ b/plugins/div/dialogs/div.js @@ -265,13 +265,13 @@ }, setup: function( element ) { for ( var name in styles ) - styles[ name ].checkElementRemovable( element, true ) && this.setValue( name, 1 ); + styles[ name ].checkElementRemovable( element, true, editor ) && this.setValue( name, 1 ); }, commit: function( element ) { var styleName; if ( ( styleName = this.getValue() ) ) { var style = styles[ styleName ]; - style.applyToObject( element ); + style.applyToObject( element, editor ); } else element.removeAttribute( 'style' ); diff --git a/plugins/find/dialogs/find.js b/plugins/find/dialogs/find.js index 51d09f0cdf9..dc34665c2a5 100755 --- a/plugins/find/dialogs/find.js +++ b/plugins/find/dialogs/find.js @@ -220,7 +220,7 @@ // Apply the highlight. var range = this.toDomRange(), bookmark = range.createBookmark(); - highlightStyle.applyToRange( range ); + highlightStyle.applyToRange( range, editor ); range.moveToBookmark( bookmark ); this._.highlightRange = range; @@ -242,7 +242,7 @@ return; var bookmark = this._.highlightRange.createBookmark(); - highlightStyle.removeFromRange( this._.highlightRange ); + highlightStyle.removeFromRange( this._.highlightRange, editor ); this._.highlightRange.moveToBookmark( bookmark ); this.updateFromDomRange( this._.highlightRange ); this._.highlightRange = null; diff --git a/plugins/font/plugin.js b/plugins/font/plugin.js index 63f8598966b..6fc3db718c3 100644 --- a/plugins/font/plugin.js +++ b/plugins/font/plugin.js @@ -79,7 +79,7 @@ // Check if the element is removable by any of // the styles. for ( var value in styles ) { - if ( styles[ value ].checkElementMatch( element, true ) ) { + if ( styles[ value ].checkElementMatch( element, true, editor ) ) { if ( value != currentValue ) this.setValue( value ); return; diff --git a/plugins/format/plugin.js b/plugins/format/plugin.js index 34e8e6d0f99..9f695793ec5 100644 --- a/plugins/format/plugin.js +++ b/plugins/format/plugin.js @@ -65,7 +65,7 @@ CKEDITOR.plugins.add( 'format', { var style = styles[ value ], elementPath = editor.elementPath(); - editor[ style.checkActive( elementPath ) ? 'removeStyle' : 'applyStyle' ]( style ); + editor[ style.checkActive( elementPath, editor ) ? 'removeStyle' : 'applyStyle' ]( style ); // Save the undo snapshot after all changes are affected. (#4899) setTimeout( function() { @@ -81,7 +81,7 @@ CKEDITOR.plugins.add( 'format', { this.refresh(); for ( var tag in styles ) { - if ( styles[ tag ].checkActive( elementPath ) ) { + if ( styles[ tag ].checkActive( elementPath, editor ) ) { if ( tag != currentTag ) this.setValue( tag, editor.lang.format[ 'tag_' + tag ] ); return; diff --git a/plugins/language/plugin.js b/plugins/language/plugin.js index 2ace9711503..5a665381990 100644 --- a/plugins/language/plugin.js +++ b/plugins/language/plugin.js @@ -39,7 +39,7 @@ var item = items[ 'language_' + languageId ]; if ( item ) - editor[ item.style.checkActive( editor.elementPath() ) ? 'removeStyle' : 'applyStyle' ]( item.style ); + editor[ item.style.checkActive( editor.elementPath(), editor ) ? 'removeStyle' : 'applyStyle' ]( item.style ); }, refresh: function( editor, path ) { this.setState( plugin.getCurrentLangElement( editor ) ? diff --git a/plugins/link/dialogs/link.js b/plugins/link/dialogs/link.js index 932b0310451..9725a3dd4fc 100755 --- a/plugins/link/dialogs/link.js +++ b/plugins/link/dialogs/link.js @@ -888,7 +888,7 @@ } ); style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why. - style.applyToRange( range ); + style.applyToRange( range, editor ); range.select(); } else { // We're only editing an existing link, so just overwrite the attributes. diff --git a/plugins/stylescombo/plugin.js b/plugins/stylescombo/plugin.js index 7a609976d9c..581bfe62d58 100644 --- a/plugins/stylescombo/plugin.js +++ b/plugins/stylescombo/plugin.js @@ -94,7 +94,7 @@ var style = styles[ value ], elementPath = editor.elementPath(); - editor[ style.checkActive( elementPath ) ? 'removeStyle' : 'applyStyle' ]( style ); + editor[ style.checkActive( elementPath, editor ) ? 'removeStyle' : 'applyStyle' ]( style ); editor.fire( 'saveSnapshot' ); }, @@ -111,7 +111,7 @@ // Check if the element is removable by any of // the styles. for ( var value in styles ) { - if ( styles[ value ].checkElementRemovable( element, true ) ) { + if ( styles[ value ].checkElementRemovable( element, true, editor ) ) { if ( value != currentValue ) this.setValue( value ); return; @@ -136,12 +136,12 @@ var style = styles[ name ], type = style.type; - if ( style.checkApplicable( elementPath, editor.activeFilter ) ) + if ( style.checkApplicable( elementPath, editor, editor.activeFilter ) ) counter[ type ]++; else this.hideItem( name ); - if ( style.checkActive( elementPath ) ) + if ( style.checkActive( elementPath, editor ) ) this.mark( name ); } @@ -164,7 +164,7 @@ for ( var name in styles ) { var style = styles[ name ]; - if ( style.checkApplicable( elementPath, editor.activeFilter ) ) + if ( style.checkApplicable( elementPath, editor, editor.activeFilter ) ) return; } this.setState( CKEDITOR.TRISTATE_DISABLED ); From fa34ecffe021beb771e4f2e8d8308dd0b9c0c5d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 19:10:50 +0200 Subject: [PATCH 08/24] Implemented widget styles handler. --- plugins/widget/plugin.js | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index c4780c3f6f2..a447de87499 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -2866,6 +2866,95 @@ widget.element.data( 'cke-widget-data', JSON.stringify( widget.data ) ); } + // + // WIDGET STYLE HANDLER --------------------------------------------------- + // + + ( function() { + + CKEDITOR.style.addCustomHandler( { + type: 'widget', + + setup: function( styleDefinition ) { + // Make it easier to access name of widget to which + // this style may be applied. + this.widget = styleDefinition.widget; + }, + + apply: function( editor ) { + // Before CKEditor 4.4 wasn't a required argument, so we need to + // handle a case when it wasn't provided. + if ( !( editor instanceof CKEDITOR.editor ) ) + return; + + // Theoretically we could bypass checkApplicable, get widget from + // widgets.focused and check its name, what would be faster, but then + // this custom style would work differently than the default style + // which checks if it's applicable before applying or removeing itself. + if ( this.checkApplicable( editor.elementPath(), editor ) ) + editor.widgets.focused.applyStyle( this ); + }, + + remove: function( editor ) { + // Before CKEditor 4.4 wasn't a required argument, so we need to + // handle a case when it wasn't provided. + if ( !( editor instanceof CKEDITOR.editor ) ) + return; + + if ( this.checkApplicable( editor.elementPath(), editor ) ) + editor.widgets.focused.removeStyle( this ); + }, + + checkActive: function( elementPath, editor ) { + return this.checkElementMatch( elementPath.lastElement, 0, editor ); + }, + + checkApplicable: function( elementPath, editor, filter ) { + // Before CKEditor 4.4 wasn't a required argument, so we need to + // handle a case when it wasn't provided. + if ( !( editor instanceof CKEDITOR.editor ) ) + return false; + + return this.checkElement( elementPath.lastElement, editor ); + }, + + checkElementMatch: checkElementMatch, + + checkElementRemovable: checkElementMatch, + + // Checks if element is a {@link CKEDITOR.plugins.widget#wrapper wrapper} of a + // a widget which name matches the widget name specified in style definition. + checkElement: function( element, editor ) { + if ( !isDomWidgetWrapper( element ) ) + return false; + + var widgetElement = element.getFirst( isDomWidgetElement ); + return widgetElement && widgetElement.data( 'widget' ) == this.widget; + }, + + applyToRange: notImplemented, + removeFromRange: notImplemented, + applyToObject: notImplemented + } ); + + function notImplemented() {} + + // @context style + function checkElementMatch( element, fullMatch, editor ) { + // Before CKEditor 4.4 wasn't a required argument, so we need to + // handle a case when it wasn't provided. + if ( !editor ) + return false; + + if ( !this.checkElement( element, editor ) ) + return false; + + var widget = editor.widgets.getByElement( element, true ); + return widget && widget.checkStyleActive( this ); + } + + } )(); + // // EXPOSE PUBLIC API ------------------------------------------------------ // From bac9fdffaab207fe5d52d46a21e101f1fe7bd69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 20:30:26 +0200 Subject: [PATCH 09/24] Implemented widget's methods for handlig classes and styles. --- plugins/widget/plugin.js | 132 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index a447de87499..300d25aae1c 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -861,6 +861,68 @@ } Widget.prototype = { + /** + * Adds a class to the widget element. This method is used by + * the {@link #applyStyle} method and should be overriden by widgets + * which should handle classes differently (e.g. add the to other elements). + * + * See also: {@link #removeClass}, {@link #hasClass}. + * + * @since 4.4 + * @param {String} className The class name to be added. + */ + addClass: function( className ) { + this.element.addClass( className ); + }, + + /** + * Applies specified style to the widget. It is highly recommended to use the + * {@link CKEDITOR.editor#applyStyle} or {@link CKEDITOR.style#apply} methods instead of + * using this method directly, because unlike editor's and style's methods, this one + * does not perform any checks. + * + * By default this method handles only classes defined in the style and passes + * them to the {@link #addClass} method. To handle classes differently or handle + * more of the style's properties you can override these methods. + * + * See also: {@link #checkStyleActive}, {@link #removeStyle}. + * + * @since 4.4 + * @param {CKEDITOR.style} style The custom widget style to be applied. + */ + applyStyle: function( style ) { + applyRemoveStyle( this, style, 1 ); + }, + + /** + * Checks if specified style is applied to this widget. It is highly recommended to use the + * {@link CKEDITOR.style#checkActive} method instead of using this method directly, + * because unlike style's method, this one does not perform any checks. + * + * By default this method handles only classes defined in the style and passes + * them to the {@link #hasClass} method. To handle classes differently or handle + * more of the style's properties you can override these methods. + * + * See also: {@link #applyStyle}, {@link #removeStyle}. + * + * @since 4.4 + * @param {CKEDITOR.style} style The custom widget style to be checked. + * @returns {Boolean} Whether style is applied to this widget. + */ + checkStyleActive: function( style ) { + var classes = getStyleClasses( style ), + cl; + + if ( !classes ) + return false; + + while ( ( cl = classes.pop() ) ) { + if ( !this.hasClass( cl ) ) + return false; + } + return true; + }, + /** * Destroys this widget instance. * @@ -973,6 +1035,21 @@ } ); }, + /** + * Checks if the widget element has specified class. This method is used by + * the {@link #checkStyleActive} method and should be overriden by widgets + * which should handle classes differently (e.g. on other elements). + * + * See also: {@link #removeClass}, {@link #addClass}. + * + * @since 4.4 + * @param {String} className The class to be checked. + * @param {Boolean} Whether widget has specified class. + */ + hasClass: function( className ) { + return this.element.hasClass( className ); + }, + /** * Initializes a nested editable. * @@ -1060,6 +1137,39 @@ this.editor.focus(); }, + /** + * Removes a class from the widget element. This method is used by + * the {@link #removeStyle} method and should be overriden by widgets + * which should handle classes differently (e.g. on other elements). + * + * See also: {@link #hasClass}, {@link #addClass}. + * + * @since 4.4 + * @param {String} className The class to be removed. + */ + removeClass: function( className ) { + this.element.removeClass( className ); + }, + + /** + * Removes specified style from the widget. It is highly recommended to use the + * {@link CKEDITOR.editor#removeStyle} or {@link CKEDITOR.style#remove} methods instead of + * using this method directly, because unlike editor's and style's methods, this one + * does not perform any checks. + * + * By default this method handles only classes defined in the style and passes + * them to the {@link #removeClass} method. To handle classes differently or handle + * more of the style's properties you can override these methods. + * + * See also {@link #checkStyleActive}, {@link #applyStyle}. + * + * @since 4.4 + * @param {CKEDITOR.style} style The custom widget style to be removed. + */ + removeStyle: function( style ) { + applyRemoveStyle( this, style, 0 ); + }, + /** * Sets widget value(s) in the {@link #property-data} object. * If the given value(s) modifies current ones, the {@link #event-data} event is fired. @@ -2469,6 +2579,20 @@ // LEFT, RIGHT, UP, DOWN, DEL, BACKSPACE - unblock default fake sel handlers. var keystrokesNotBlockedByWidget = { 37: 1, 38: 1, 39: 1, 40: 1, 8: 1, 46: 1 }; + // Applies or removes style's classes from widget. + // @param {CKEDITOR.style} style Custom widget style. + // @param {Boolean} apply Whether to apply or remove style. + function applyRemoveStyle( widget, style, apply ) { + var classes = getStyleClasses( style ), + cl; + + if ( !classes ) + return; + + while ( ( cl = classes.pop() ) ) + widget[ apply ? 'addClass' : 'removeClass' ]( cl ); + } + function cancel( evt ) { evt.cancel(); } @@ -2554,6 +2678,14 @@ }, 100 ); // Use 100ms, so Chrome (@Mac) will be able to grab the content. } + // Extracts classes array from style instance. + function getStyleClasses( style ) { + var attrs = style.getDefinition().attributes, + classes = attrs && attrs[ 'class' ]; + + return classes ? classes.split( /\s+/ ) : null; + } + // [IE] Force keeping focus because IE sometimes forgets to fire focus on main editable // when blurring nested editable. // @context widget From 66660a851dbac2819269bdba0b7b2b46dd2d0014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 1 Apr 2014 20:56:55 +0200 Subject: [PATCH 10/24] Added samples for styling widgets. --- plugins/widget/dev/contents.css | 23 +++++ plugins/widget/dev/widgetstyles.html | 136 +++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 plugins/widget/dev/contents.css create mode 100644 plugins/widget/dev/widgetstyles.html diff --git a/plugins/widget/dev/contents.css b/plugins/widget/dev/contents.css new file mode 100644 index 00000000000..1f04cbc63b9 --- /dev/null +++ b/plugins/widget/dev/contents.css @@ -0,0 +1,23 @@ +.thickBorder { + border-width: 5px; +} +.mediumBorder { + border-width: 2px; +} +img.thickBorder, img.mediumBorder { + border-style: solid; + border-color: #CCC; +} +.important.soMuch { + margin: 25px; + padding: 25px; + background: red; + border: none; +} + +span.redMarker { + background-color: red; +} +.invisible { + opacity: 0.1; +} \ No newline at end of file diff --git a/plugins/widget/dev/widgetstyles.html b/plugins/widget/dev/widgetstyles.html new file mode 100644 index 00000000000..15c38675874 --- /dev/null +++ b/plugins/widget/dev/widgetstyles.html @@ -0,0 +1,136 @@ + + + + + + Applying styles to widgets — CKEditor Sample + + + + + + +

Applying styles to widgets

+ +

Classic (iframe-based) Sample

+ + +

Inline Sample

+
+

Apollo 11

+ +
+ Saturn V +
Roll out of Saturn V on launch pad
+
+ +

Apollo 11 was the spaceflight that landed the first humans, Americans Neil Armstrong and Buzz Aldrin, on the Moon on [[July 20, 1969, at 20:18 UTC]]. Armstrong became the first to step onto the lunar surface 6 hours later on [[July 21 at 02:56 UTC]].

+ +

Armstrong spent about three and a half two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5 kg) of lunar material for return to Earth. A third member of the mission, Michael Collins, piloted the command spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.

+ +

Broadcasting and quotes

+ +

Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:

+ +
+

One small step for [a] man, one giant leap for mankind.

+
+ +

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

+ +
+

[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.

+
+ +
+ The Eagle +
The Eagle in lunar orbit
+
+ +

Technical details

+ +

Launched by a Saturn V rocket from Kennedy Space Center in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of NASA's Apollo program. The Apollo spacecraft had three parts:

+ +
    +
  1. Command Module with a cabin for the three astronauts which was the only part which landed back on Earth
  2. +
  3. Service Module which supported the Command Module with propulsion, electrical power, oxygen and water
  4. +
  5. Lunar Module for landing on the Moon.
  6. +
+ +

After being sent to the Moon by the Saturn V's upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the Sea of Tranquility. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the Pacific Ocean on July 24.

+
+ + + + From 3ab980407b77de2a0b284de6f22769ec79f0d981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 10:54:14 +0200 Subject: [PATCH 11/24] Set default assignedTo property of a custom style if not specified. --- core/style.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/style.js b/core/style.js index ab7dba02d8a..40311494ca5 100644 --- a/core/style.js +++ b/core/style.js @@ -462,8 +462,18 @@ CKEDITOR.STYLE_OBJECT = 3; if ( this.setup ) this.setup( styleDefinition ); }; - styleClass.prototype = CKEDITOR.tools.prototypedCopy( CKEDITOR.style.prototype ); - CKEDITOR.tools.extend( styleClass.prototype, definition, true ); + + styleClass.prototype = CKEDITOR.tools.extend( + // Prototype of CKEDITOR.style. + CKEDITOR.tools.prototypedCopy( CKEDITOR.style.prototype ), + // Defaults. + { + assignedTo: CKEDITOR.STYLE_OBJECT + }, + // Passed definition - overrides. + definition, + true + ); this.customHandlers[ definition.type ] = styleClass; From 8658194790d10cea1d28bba86c645c3562b23d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 12:21:04 +0200 Subject: [PATCH 12/24] Made stylescombo using assignedTo property of style. --- plugins/stylescombo/plugin.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/stylescombo/plugin.js b/plugins/stylescombo/plugin.js index 581bfe62d58..a37a037054b 100644 --- a/plugins/stylescombo/plugin.js +++ b/plugins/stylescombo/plugin.js @@ -24,7 +24,7 @@ if ( !stylesDefinitions ) return; - var style, styleName; + var style, styleName, styleType; // Put all styles into an Array. for ( var i = 0, count = stylesDefinitions.length; i < count; i++ ) { @@ -34,15 +34,16 @@ continue; styleName = styleDefinition.name; - style = new CKEDITOR.style( styleDefinition ); if ( !editor.filter.customConfig || editor.filter.check( style ) ) { style._name = styleName; style._.enterMode = config.enterMode; + // Get the type (which will be used to assign style to one of 3 groups) from assignedTo if it's defined. + style._.type = styleType = style.assignedTo || style.type; // Weight is used to sort styles (#9029). - style._.weight = i + ( style.type == CKEDITOR.STYLE_OBJECT ? 1 : style.type == CKEDITOR.STYLE_BLOCK ? 2 : 3 ) * 1000; + style._.weight = i + ( styleType == CKEDITOR.STYLE_OBJECT ? 1 : styleType == CKEDITOR.STYLE_BLOCK ? 2 : 3 ) * 1000; styles[ styleName ] = style; stylesList.push( style ); @@ -74,7 +75,7 @@ for ( i = 0, count = stylesList.length; i < count; i++ ) { style = stylesList[ i ]; styleName = style._name; - type = style.type; + type = style._.type; if ( type != lastType ) { this.startGroup( lang[ 'panelTitle' + String( type ) ] ); @@ -134,7 +135,7 @@ this.unmarkAll(); for ( var name in styles ) { var style = styles[ name ], - type = style.type; + type = style._.type; if ( style.checkApplicable( elementPath, editor, editor.activeFilter ) ) counter[ type ]++; From e4ed55fb1f0fa2322c98bfe1b65ed2b8b7202340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 12:33:53 +0200 Subject: [PATCH 13/24] Implemented simple buildPreview. --- plugins/widget/plugin.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index 300d25aae1c..57280f9bf48 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -3064,6 +3064,10 @@ return widgetElement && widgetElement.data( 'widget' ) == this.widget; }, + buildPreview: function( label ) { + return label || this._.definition.name; + }, + applyToRange: notImplemented, removeFromRange: notImplemented, applyToObject: notImplemented From 62a351414d89d380407cd571a9b800305c35d71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 13:07:28 +0200 Subject: [PATCH 14/24] Ordered styles used in the sample in more reasonable order. --- plugins/widget/dev/contents.css | 6 +++--- plugins/widget/dev/widgetstyles.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/widget/dev/contents.css b/plugins/widget/dev/contents.css index 1f04cbc63b9..c2b51d3630b 100644 --- a/plugins/widget/dev/contents.css +++ b/plugins/widget/dev/contents.css @@ -1,9 +1,9 @@ -.thickBorder { - border-width: 5px; -} .mediumBorder { border-width: 2px; } +.thickBorder { + border-width: 5px; +} img.thickBorder, img.mediumBorder { border-style: solid; border-color: #CCC; diff --git a/plugins/widget/dev/widgetstyles.html b/plugins/widget/dev/widgetstyles.html index 15c38675874..8dfb35c8d43 100644 --- a/plugins/widget/dev/widgetstyles.html +++ b/plugins/widget/dev/widgetstyles.html @@ -110,8 +110,8 @@

Technical details

CKEDITOR.disableAutoInline = true; var stylesSet = [ - { name: 'Thick border', type: 'widget', widget: 'image', attributes: { 'class': 'thickBorder' } }, { name: 'Medium border', type: 'widget', widget: 'image', attributes: { 'class': 'mediumBorder' } }, + { name: 'Thick border', type: 'widget', widget: 'image', attributes: { 'class': 'thickBorder' } }, { name: 'So important', type: 'widget', widget: 'image', attributes: { 'class': 'important soMuch' } }, { name: 'Red marker', type: 'widget', widget: 'placeholder', attributes: { 'class': 'redMarker' } }, From b79cb029f1b08d60273e9dd62deef4a4b320a9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 16:58:23 +0200 Subject: [PATCH 15/24] Use data.classes to store classes applied to widget. --- plugins/widget/plugin.js | 144 +++++++++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 12 deletions(-) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index 57280f9bf48..6ba25b87263 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -500,6 +500,34 @@ return newInstances; }, + /** + * Parses element classes string and returns an object + * which keys contain classes names. Skips all `cke_*` classes. + * + * This method is used by the {@link CKEDITOR.plugins.widget#getClasses} method and + * may be used if when overriding that method. + * + * @since 4.4 + * @param {String} classes String (value of `class` attribute). + * @returns {Object} Object containing classes or `null` if no classes found. + */ + parseElementClasses: function( classes ) { + if ( !classes ) + return null; + + var classes = CKEDITOR.tools.trim( classes ).split( /\s+/ ), + cl, + obj = {}, + hasClasses = 0; + + while ( ( cl = classes.pop() ) ) { + if ( cl.indexOf( 'cke_' ) == -1 ) + obj[ cl ] = hasClasses = 1; + } + + return hasClasses ? obj : null; + }, + /** * Wraps an element with a widget's non-editable container. * @@ -866,7 +894,10 @@ * the {@link #applyStyle} method and should be overriden by widgets * which should handle classes differently (e.g. add the to other elements). * - * See also: {@link #removeClass}, {@link #hasClass}. + * **Note**: This method should not be used directly. Use the {@link #setData} method to + * set the `classes` property. Read more in the {@link #setData} documentation. + * + * See also: {@link #removeClass}, {@link #hasClass}, {@link #getClasses}. * * @since 4.4 * @param {String} className The class name to be added. @@ -881,9 +912,15 @@ * using this method directly, because unlike editor's and style's methods, this one * does not perform any checks. * - * By default this method handles only classes defined in the style and passes - * them to the {@link #addClass} method. To handle classes differently or handle - * more of the style's properties you can override these methods. + * By default this method handles only classes defined in the style. It clones existing + * classes which are stored in the {@link #property-data widget data}'s `classes` property, + * adds new classes and calls the {@link #setData} method if at least one new class was added. + * Then, using the {@link #event-data} event listener widget applies modifications passing + * new classes to the {@link #addClass} method. + * + * If you need to handle classes differently than in the default way you can override the + * {@link #addClass} and related methods. You can also handle other style's property than classes + * by overriding this method. * * See also: {@link #checkStyleActive}, {@link #removeStyle}. * @@ -1035,12 +1072,28 @@ } ); }, + /** + * Returns widget element classes parsed to an object. This method + * is used to populate the `classes` property of widget's {@link #property-data}. + * + * This method reuses {@link CKEDITOR.plugins.widget.repository#parseElementClasses}. + * It should be overriden if widget should handle classes differently (e.g. on other elements). + * + * See also: {@link #removeClass}, {@link #addClass}, {@link #hasClass}. + * + * @since 4.4 + * @returns {Object} + */ + getClasses: function() { + return this.repository.parseElementClasses( this.element.getAttribute( 'class' ) ); + }, + /** * Checks if the widget element has specified class. This method is used by * the {@link #checkStyleActive} method and should be overriden by widgets * which should handle classes differently (e.g. on other elements). * - * See also: {@link #removeClass}, {@link #addClass}. + * See also: {@link #removeClass}, {@link #addClass}, {@link #getClasses}. * * @since 4.4 * @param {String} className The class to be checked. @@ -1142,6 +1195,9 @@ * the {@link #removeStyle} method and should be overriden by widgets * which should handle classes differently (e.g. on other elements). * + * **Note**: This method should not be used directly. Use the {@link #setData} method to + * set the `classes` property. Read more in the {@link #setData} documentation. + * * See also: {@link #hasClass}, {@link #addClass}. * * @since 4.4 @@ -1157,11 +1213,9 @@ * using this method directly, because unlike editor's and style's methods, this one * does not perform any checks. * - * By default this method handles only classes defined in the style and passes - * them to the {@link #removeClass} method. To handle classes differently or handle - * more of the style's properties you can override these methods. + * Read more about how applying/removing styles works in the {@link #applyStyle} method documentation. * - * See also {@link #checkStyleActive}, {@link #applyStyle}. + * See also {@link #checkStyleActive}, {@link #applyStyle}, {@link #getClasses}. * * @since 4.4 * @param {CKEDITOR.style} style The custom widget style to be removed. @@ -1185,6 +1239,22 @@ * in a JSON string, therefore {@link #property-data} should contain * only serializable data. * + * **Note:** There's a special data property - `classes`. It contains an object + * with classes which were returned by the {@link #getClasses} method during widget initialization. + * This property is then used by the {@link #applyStyle} and {@link #removeStyle} methods. + * When it's changed (the reference to object must be changed!) widget updates its classes by using the + * {@link #addClass} and {@link #removeClass} methods. + * + * // Adding new class. + * var classes = CKEDITOR.tools.clone( widget.data.classes ); + * classes.newClass = 1; + * widget.setData( 'classes', classes ); + * + * // Removing class. + * var classes = CKEDITOR.tools.clone( widget.data.classes ); + * delete classes.newClass; + * widget.setData( 'classes', classes ); + * * @param {String/Object} keyOrData * @param {Object} value * @chainable @@ -2583,14 +2653,31 @@ // @param {CKEDITOR.style} style Custom widget style. // @param {Boolean} apply Whether to apply or remove style. function applyRemoveStyle( widget, style, apply ) { - var classes = getStyleClasses( style ), + var changed = 0, + classes = getStyleClasses( style ), + updatedClasses = widget.data.classes || {}, cl; + // Ee... Something is wrong with this style. if ( !classes ) return; - while ( ( cl = classes.pop() ) ) - widget[ apply ? 'addClass' : 'removeClass' ]( cl ); + // Clone, because we need to break reference. + updatedClasses = CKEDITOR.tools.clone( updatedClasses ); + + while ( ( cl = classes.pop() ) ) { + if ( apply ) { + if ( !updatedClasses[ cl ] ) + changed = updatedClasses[ cl ] = 1; + } else { + if ( updatedClasses[ cl ] ) { + delete updatedClasses[ cl ]; + changed = 1; + } + } + } + if ( changed ) + widget.setData( 'classes', updatedClasses ); } function cancel( evt ) { @@ -2717,6 +2804,34 @@ } } + // Setup listener on widget#data which will update (remove/add) classes + // by comparing newly set classes with the old ones. + function setupDataClassesListener( widget ) { + // Note: previousClasses and newClasses may be null! + // Tip: for ( cl in null ) is correct. + var previousClasses = null; + + widget.on( 'data', function() { + var newClasses = this.data.classes, + cl; + + // When setting new classes one need to remember + // that he must break reference. + if ( previousClasses == newClasses ) + return; + + for ( cl in previousClasses ) { + // Avoid removing and adding classes again. + if ( !( newClasses && newClasses[ cl ] ) ) + this.removeClass( cl ); + } + for ( cl in newClasses ) + this.addClass( cl ); + + previousClasses = newClasses; + } ); + } + function setupDragHandler( widget ) { if ( !widget.draggable ) return; @@ -2923,6 +3038,7 @@ setupEditables( widget ); setupMask( widget ); setupDragHandler( widget ); + setupDataClassesListener( widget ); // #11145: [IE8] Non-editable content of widget is draggable. if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) { @@ -2978,6 +3094,10 @@ if ( startupData ) widget.setData( startupData ); + // Populate classes if they are not preset. + if ( !widget.data.classes ) + widget.setData( 'classes', widget.getClasses() ); + // Unblock data and... widget.dataReady = true; From 437520a5559ff0bdf5da24f09f923ec75717b297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Wed, 2 Apr 2014 17:03:51 +0200 Subject: [PATCH 16/24] Inline sample doesn't make sense without contents styles. --- plugins/widget/dev/widgetstyles.html | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/widget/dev/widgetstyles.html b/plugins/widget/dev/widgetstyles.html index 8dfb35c8d43..c589c4759ff 100644 --- a/plugins/widget/dev/widgetstyles.html +++ b/plugins/widget/dev/widgetstyles.html @@ -10,6 +10,7 @@ + From 40ddc1ce342afdbbf1d743455d8e7eb1dfff89d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Thu, 3 Apr 2014 11:46:45 +0200 Subject: [PATCH 17/24] Added support for style.toAllowedContentRules method. --- core/filter.js | 8 ++++++-- core/style.js | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/core/filter.js b/core/filter.js index 78305633fc8..c7bde835377 100644 --- a/core/filter.js +++ b/core/filter.js @@ -228,9 +228,13 @@ if ( typeof newRules == 'string' ) newRules = parseRulesString( newRules ); - else if ( newRules instanceof CKEDITOR.style ) + else if ( newRules instanceof CKEDITOR.style ) { + // If style has the cast method defined, use it and abort. + if ( newRules.toAllowedContentRules ) + return this.allow( newRules.toAllowedContentRules( this.editor ), featureName, overrideCustom ); + newRules = convertStyleToRules( newRules ); - else if ( CKEDITOR.tools.isArray( newRules ) ) { + } else if ( CKEDITOR.tools.isArray( newRules ) ) { for ( i = 0; i < newRules.length; ++i ) ret = this.allow( newRules[ i ], featureName, overrideCustom ); return ret; // Return last status. diff --git a/core/style.js b/core/style.js index 40311494ca5..4f504b588f7 100644 --- a/core/style.js +++ b/core/style.js @@ -403,9 +403,28 @@ CKEDITOR.STYLE_OBJECT = 3; return html.join( '' ); }, + /** + * Returns style definition. + * + * @since 4.1 + * @returns {Object} + */ getDefinition: function() { return this._.definition; } + + /** + * If defined (for example by {@link CKEDITOR.style.addCustomHandler custom style handler}) returns + * the {@link CKEDITOR.filter.allowedContentRules allowed content rules} which should be added to the + * {@link CKEDITOR.filter} when enabling this style. + * + * **Note:** This method is not defined in the {@link CKEDITOR.style} class. + * + * @since 4.4 + * @method toAllowedContentRules + * @param {CKEDITOR.editor} [editor] The editor instance. + * @returns {CKEDITOR.filter.allowedContentRules} The rules that should represent this style in the {@link CKEDITOR.filter}. + */ }; /** From 851e7686dc665aa3daa547d1fc64a68735bd26c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Thu, 3 Apr 2014 14:14:57 +0200 Subject: [PATCH 18/24] Introduced widgetDef.styleableElements and widgetDef.styleToAllowedContentRules. --- plugins/widget/plugin.js | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index 6ba25b87263..b04b4cf871a 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -3188,6 +3188,36 @@ return label || this._.definition.name; }, + // Use widget's styleableElements to make a rule allowing classes on + // specified elements or use widget's styleToAllowedContentRules method. + toAllowedContentRules: function( editor ) { + if ( !editor ) + return null; + + var widgetDef = editor.widgets.registered[ this.widget ], + classes, + rule = {}; + + if ( !widgetDef ) + return null; + + if ( widgetDef.styleableElements ) { + classes = this._.definition.attributes && this._.definition.attributes[ 'class' ]; + if ( !classes ) + return null; + + classes = CKEDITOR.tools.trim( classes ).split( /\s+/ ).join( ',' ); + rule[ widgetDef.styleableElements ] = { + classes: classes, + propertiesOnly: true + }; + return rule; + } + if ( widgetDef.styleToAllowedContentRules ) + return widgetDef.styleToAllowedContentRules( this ); + return null; + }, + applyToRange: notImplemented, removeFromRange: notImplemented, applyToObject: notImplemented @@ -3482,6 +3512,72 @@ * @property {Boolean} draggable */ +/** + * Names of element(s) (separated by spaces) for which the {@link CKEDITOR.filter} should allow classes + * defined in widget styles. For example if your widget is upcasted from simple `
` + * element, then in order to make it styleable you can set: + * + * editor.widgets.add( 'customWidget', { + * upcast: function( element ) { + * return element.name == 'div'; + * }, + * + * // ... + * + * styleableElements: 'div' + * } ); + * + * Then, when a following style is defined: + * + * { + * name: 'Thick border', type: 'widget', widget: 'customWidget', + * attributes: { 'class': 'thickBorder' } + * } + * + * In the {@link CKEDITOR.filter} a rule allowing `thickBorder` class for `div` elements will be registered. + * + * If you need to have more freedom when transforming widget style to allowed content rules, + * you can use the {@link #styleToAllowedContentRules} callback. + * + * @since 4.4 + * @property {String} styleableElements + */ + +/** + * Function transforming custom widget's {@link CKEDITOR.style} instance into + * {@link CKEDITOR.filter.allowedContentRules}. It may be used when a static + * {@link #styleableElements} property is not enough to inform the {@link CKEDITOR.filter} + * what HTML features should be enabled when allowing given style. + * + * In most cases, when style's classes just have to be added to element name(s) used by + * widget element, it's recommended to use simpler {@link #styleableElements} property. + * + * editor.widgets.add( 'customWidget', { + * // ... + * + * styleToAllowedContentRules: funciton( style ) { + * // Retrieve classes defined in the style. + * var classes = style.getDefinition().attributes[ 'classes' ]; + * // Allowed content rule accepts classes separated with commas, not spaces. + * classes = classes.split( /\s+/ ).join( ',' ); + * + * // Do something crazy - for example return allowed content rules in object format, + * // with custom match property. + * return { + * h1: { + * match: isWidgetElement, + * classes: classes + * } + * }; + * } + * } ); + * + * @since 4.4 + * @property {Function} styleToAllowedContentRules + * @param {CKEDITOR.style} styleToAllowedContentRules.style The style to be transformed. + * @returns {CKEDITOR.filter.allowedContentRules} styleToAllowedContentRules.return + */ + /** * This is an abstract class that describes the definition of a widget's nested editable. * It is a type of values in the {@link CKEDITOR.plugins.widget.definition#editables} object. From eb161813c33f56a38e12840d2fa16b30bfee7ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Thu, 3 Apr 2014 14:27:48 +0200 Subject: [PATCH 19/24] Added mathjax to the widget styles sample. --- plugins/widget/dev/widgetstyles.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/widget/dev/widgetstyles.html b/plugins/widget/dev/widgetstyles.html index c589c4759ff..d9b83f86b36 100644 --- a/plugins/widget/dev/widgetstyles.html +++ b/plugins/widget/dev/widgetstyles.html @@ -37,6 +37,8 @@

Broadcasting and quotes

One small step for [a] man, one giant leap for mankind.

+

\( \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \)

+

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

@@ -82,6 +84,8 @@

Broadcasting and quotes

One small step for [a] man, one giant leap for mankind.

+

\( \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) \)

+

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

@@ -116,18 +120,20 @@

Technical details

{ name: 'So important', type: 'widget', widget: 'image', attributes: { 'class': 'important soMuch' } }, { name: 'Red marker', type: 'widget', widget: 'placeholder', attributes: { 'class': 'redMarker' } }, - { name: 'Invisible', type: 'widget', widget: 'placeholder', attributes: { 'class': 'invisible' } }, + { name: 'Invisible Placeholder', type: 'widget', widget: 'placeholder', attributes: { 'class': 'invisible' } }, + + { name: 'Invisible Mathjax', type: 'widget', widget: 'mathjax', attributes: { 'class': 'invisible' } } ]; CKEDITOR.replace( 'editor1', { - extraPlugins: 'placeholder,image2', + extraPlugins: 'placeholder,image2,mathjax', contentsCss: [ '../../../contents.css', 'contents.css' ], stylesSet: stylesSet, height: 300 } ); CKEDITOR.inline( 'editor2', { - extraPlugins: 'placeholder,image2', + extraPlugins: 'placeholder,image2,mathjax', stylesSet: stylesSet, height: 300 } ); From 5b6aeaa0347940ea9def443b0b2ef6a1c1343f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Thu, 3 Apr 2014 14:46:51 +0200 Subject: [PATCH 20/24] Added getClassesArray helper and documentation to make the class visible in API docs. --- plugins/widget/plugin.js | 72 ++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index b04b4cf871a..127df0d0c22 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -3124,6 +3124,13 @@ ( function() { + /** + * The class representing a widget style. + * + * @since 4.4 + * @class CKEDITOR.style.customHandlers.widget + * @extends CKEDITOR.style + */ CKEDITOR.style.addCustomHandler( { type: 'widget', @@ -3174,8 +3181,14 @@ checkElementRemovable: checkElementMatch, - // Checks if element is a {@link CKEDITOR.plugins.widget#wrapper wrapper} of a - // a widget which name matches the widget name specified in style definition. + /** + * Checks if element is a {@link CKEDITOR.plugins.widget#wrapper wrapper} of a + * a widget which name matches the {@link #widget widget name} specified in style definition. + * + * @param {CKEDITOR.dom.element} element + * @param {CKEDITOR.editor} editor + * @returns {Boolean} + */ checkElement: function( element, editor ) { if ( !isDomWidgetWrapper( element ) ) return false; @@ -3188,8 +3201,16 @@ return label || this._.definition.name; }, - // Use widget's styleableElements to make a rule allowing classes on - // specified elements or use widget's styleToAllowedContentRules method. + /** + * Returns allowed content rules which should be registered for this style. + * Uses widget's {@link CKEDITOR.plugins.widget.definition#styleableElements} to make a rule + * allowing classes on specified elements or use widget's + * {@link CKEDITOR.plugins.widget.definition#styleToAllowedContentRules} method to transform style + * into allowed content rules. + * + * @param {CKEDITOR.editor} The editor instance. + * @returns {CKEDITOR.filter.allowedContentRules} + */ toAllowedContentRules: function( editor ) { if ( !editor ) return null; @@ -3202,11 +3223,10 @@ return null; if ( widgetDef.styleableElements ) { - classes = this._.definition.attributes && this._.definition.attributes[ 'class' ]; + classes = this.getClassesArray(); if ( !classes ) return null; - classes = CKEDITOR.tools.trim( classes ).split( /\s+/ ).join( ',' ); rule[ widgetDef.styleableElements ] = { classes: classes, propertiesOnly: true @@ -3218,8 +3238,36 @@ return null; }, + /** + * Returns classes defined in the style in form of array. + * + * @returns {String[]} + */ + getClassesArray: function() { + var classes = this._.definition.attributes && this._.definition.attributes[ 'class' ]; + + return classes ? CKEDITOR.tools.trim( classes ).split( /\s+/ ) : null; + }, + + /** + * Not implemented. + * + * @method applyToRange + */ applyToRange: notImplemented, + + /** + * Not implemented. + * + * @method removeFromRange + */ removeFromRange: notImplemented, + + /** + * Not implemented. + * + * @method applyToObject + */ applyToObject: notImplemented } ); @@ -3552,20 +3600,22 @@ * In most cases, when style's classes just have to be added to element name(s) used by * widget element, it's recommended to use simpler {@link #styleableElements} property. * + * In order to get parsed classes from the style definition you can use + * {@link CKEDITOR.style.customHandlers.widget#getClassesArray}. + * * editor.widgets.add( 'customWidget', { * // ... * * styleToAllowedContentRules: funciton( style ) { * // Retrieve classes defined in the style. - * var classes = style.getDefinition().attributes[ 'classes' ]; - * // Allowed content rule accepts classes separated with commas, not spaces. - * classes = classes.split( /\s+/ ).join( ',' ); + * var classes = style.getClassesArray(); * * // Do something crazy - for example return allowed content rules in object format, - * // with custom match property. + * // with custom match property and propertiesOnly flag. * return { * h1: { * match: isWidgetElement, + * propertiesOnly: true, * classes: classes * } * }; @@ -3574,7 +3624,7 @@ * * @since 4.4 * @property {Function} styleToAllowedContentRules - * @param {CKEDITOR.style} styleToAllowedContentRules.style The style to be transformed. + * @param {CKEDITOR.style.customHandlers.widget} styleToAllowedContentRules.style The style to be transformed. * @returns {CKEDITOR.filter.allowedContentRules} styleToAllowedContentRules.return */ From 0838871f5e4a22e6e2c9ce3b4b1642a37c40d83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Thu, 3 Apr 2014 15:02:37 +0200 Subject: [PATCH 21/24] Integrate existing widgets with widgets styles integration with ACF. --- plugins/image2/plugin.js | 2 ++ plugins/mathjax/plugin.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/plugins/image2/plugin.js b/plugins/image2/plugin.js index 23750059577..6b0593a4d2f 100644 --- a/plugins/image2/plugin.js +++ b/plugins/image2/plugin.js @@ -272,6 +272,8 @@ features: getWidgetFeatures( editor ), + styleableElements: 'img figure', + // This widget converts style-driven dimensions to attributes. contentTransformations: [ [ 'img[width]: sizeToAttribute' ] diff --git a/plugins/mathjax/plugin.js b/plugins/mathjax/plugin.js index 751e0d7f15d..ac272257589 100644 --- a/plugins/mathjax/plugin.js +++ b/plugins/mathjax/plugin.js @@ -28,6 +28,15 @@ button: editor.lang.mathjax.button, mask: true, allowedContent: 'span(!' + cls + ')', + // Allow style classes only on spans having mathjax class. + styleToAllowedContentRules: function( style ) { + var classes = style.getClassesArray(); + if ( !classes ) + return null; + classes.push( '!' + cls ); + + return 'span(' + classes.join( ',' ) + ')'; + }, pathName: editor.lang.mathjax.pathName, template: '', From 9c4a8831fd0c04227ca519a10e1191c6a563f20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Mon, 7 Apr 2014 16:25:15 +0200 Subject: [PATCH 22/24] Fixed JSLint issue. --- plugins/widget/plugin.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/widget/plugin.js b/plugins/widget/plugin.js index 127df0d0c22..003e52e829d 100644 --- a/plugins/widget/plugin.js +++ b/plugins/widget/plugin.js @@ -515,8 +515,9 @@ if ( !classes ) return null; - var classes = CKEDITOR.tools.trim( classes ).split( /\s+/ ), - cl, + classes = CKEDITOR.tools.trim( classes ).split( /\s+/ ); + + var cl, obj = {}, hasClasses = 0; From 3f85553b991043bdfd50101fc5aa4f184757ce7c Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Mon, 7 Apr 2014 15:58:07 +0200 Subject: [PATCH 23/24] Integrated Image2 with widget styles. --- plugins/image2/plugin.js | 57 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/plugins/image2/plugin.js b/plugins/image2/plugin.js index 6b0593a4d2f..03ef1716290 100644 --- a/plugins/image2/plugin.js +++ b/plugins/image2/plugin.js @@ -214,7 +214,8 @@ // @param {CKEDITOR.editor} // @returns {Object} function widgetDef( editor ) { - var alignClasses = editor.config.image2_alignClasses; + var alignClasses = editor.config.image2_alignClasses, + captionedClass = editor.config.image2_captionedClass; function deflate() { if ( this.deflated ) @@ -339,6 +340,13 @@ alt: this.data.alt } ); + // If shifting non-captioned -> captioned, remove classes + // related to styles from . + if ( this.oldData && !this.oldData.hasCaption && this.data.hasCaption ) { + for ( var c in this.data.classes ) + this.parts.image.removeClass( c ); + } + // Set dimensions of the image according to gathered data. // Do it only when the attributes are allowed (#11004). if ( editor.filter.checkFeature( features.dimension ) ) @@ -427,6 +435,43 @@ }, this ); }, + // Overrides default method to handle internal mutability of Image2. + // @see CKEDITOR.plugins.widget#addClass + addClass: function( className ) { + getStyleableElement( this ).addClass( className ); + }, + + // Overrides default method to handle internal mutability of Image2. + // @see CKEDITOR.plugins.widget#hasClass + hasClass: function( className ) { + return getStyleableElement( this ).hasClass( className ); + }, + + // Overrides default method to handle internal mutability of Image2. + // @see CKEDITOR.plugins.widget#removeClass + removeClass: function( className ) { + getStyleableElement( this ).removeClass( className ); + }, + + // Overrides default method to handle internal mutability of Image2. + // @see CKEDITOR.plugins.widget#getClasses + getClasses: ( function() { + var classRegex = new RegExp( '^(' + [].concat( captionedClass, alignClasses ).join( '|' ) + ')$' ); + + return function() { + var classes = this.repository.parseElementClasses( getStyleableElement( this ).getAttribute( 'class' ) ); + + // Neither config.image2_captionedClass nor config.image2_alignClasses + // do not belong to style classes. + for ( var c in classes ) { + if ( classRegex.test( c ) ) + delete classes[ c ]; + } + + return classes; + }; + } )(), + upcast: upcastWidgetElement( editor ), downcast: downcastWidgetElement( editor ) }; @@ -1458,6 +1503,16 @@ return features; } + + // Returns element which is styled, considering current + // state of the widget. + // + // @see CKEDITOR.plugins.widget#applyStyle + // @param {CKEDITOR.plugins.widget} widget + // @returns {CKEDITOR.dom.element} + function getStyleableElement( widget ) { + return widget.data.hasCaption ? widget.element : widget.parts.image; + } } )(); /** From b82ce655c7e71a906af1a105f224fb2a04b918f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Reinmar=20Koszuli=C5=84ski?= Date: Tue, 8 Apr 2014 12:34:16 +0200 Subject: [PATCH 24/24] Added changelog entry. --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d63b9f5f58d..b26c07b6687 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ CKEditor 4 Changelog * Default class of captioned image has changed to _"image"_ (was: _"caption"_). Please note that once edited in CKEditor 4.4+, all existing images of the _"caption"_ class (`
`) will be [filtered out](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) unless [`config.image2_captionedClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image2_captionedClass) option is set to _"caption"_. For backward compatibility (i.e. when upgrading), it is highly recommended to do so, to also prevent CSS conflicts, etc.. This does not apply to new CKEditor integrations. * Widgets without defined buttons are not registered automatically to the ACF any more. Before CKEditor 4.4 widgets were registered to the ACF what was an incorrect behavior ([#11567](http://dev.ckeditor.com/ticket/11567)). This change should not have any impact on standard scenarios, but if your button does not execute widget command you have to set [`allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.feature-property-allowedContent) and [`requiredContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.feature-property-requiredContent) properties on it manually, because editor will not be able to find them. * [Showborders](http://ckeditor.com/addon/showborders) plugin was added to the Standard package in order to ensure that unstyled tables are still visible for user ([#11665](http://dev.ckeditor.com/ticket/11665)). +* Since CKEditor 4.4 the editor instance should be passed to [`CKEDITOR.style`](http://docs.ckeditor.com/#!/api/CKEDITOR.style) methods to ensure full compatibility (e.g. applying styles to widgets requires that). We ensured backward compatibility though, so the [`CKEDITOR.style`](http://docs.ckeditor.com/#!/api/CKEDITOR.style) will work even when editor instance is not provided. New Features: @@ -30,6 +31,11 @@ New Features: If defined, the editor produces classes instead of inline styles for aligned images. * Default caption of the image can be translated (customized) with `editor.lang.image2.captionPlaceholder`. * [#11341](http://dev.ckeditor.com/ticket/11341): [Enhanced Image](http://ckeditor.com/addon/image2) plugin: it's now possible to link any kind of an image. +* [#11297](http://dev.ckeditor.com/ticket/11297): Styles can now be applied to widgets. Definition of a style which can be applied to specific widget must contain two additional properties – `type` and `widget`. Read more in the **TODO**. Note that by default widgets support only classes and no other attributes or styles. Related changes and features: + * Introduced [`CKEDITOR.style.addCustomHandler`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-static-method-addCustomHandler) method for registering custom style handlers. + * The [`CKEDITOR.style.apply`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-apply) and [`CKEDITOR.style.remove`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-remove) methods are now called with editor instance instead of document so they can be reused by [`CKEDITOR.editor.applyStyle`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) and [`CKEDITOR.editor.removeStyle`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-removeStyle) methods. Backward compatibility was preserved, but from CKEditor 4.4 it is highly recommended to pass editor instead of document to these methods. + * Many new methods and properties were introduced in the [Widget API](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget) to make the way how styles are handled by widgets fully customizable. See: [`widget.definition.styleableElements`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-styleableElements), [`widget.definition.styleToAllowedContentRule`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-styleToAllowedContentRules), [`widget.addClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-addClass), [`widget.removeClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-removeClass) [`widget.getClasses`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-getClasses), [`widget.hasClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-hasClass), [`widget.applyStyle`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-applyStyle), [`widget.removeStyle`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-removeStyle), [`widget.checkStyleActive`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-checkStyleActive). + * Integration with the [Allowed Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) required a [CKEDITOR.style.toAllowedContent](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-toAllowedContentRules) method which can be implemented by custom style handler and if exists is used by the [`CKEDITOR.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.filter) to translate a style to [allowed content rules](http://docs.ckeditor.com/#!/api/CKEDITOR.filter.allowedContentRules). Fixed Issues: