#9922 closed Bug (duplicate)
Stylesheet parser should accept CSS classes without elements
Reported by: | Olek Nowodziński | Owned by: | |
---|---|---|---|
Priority: | Normal | Milestone: | |
Component: | General | Version: | 4.0.2 |
Keywords: | Cc: |
Description
Suppose we want our CSS class to be parsed:
.myclass { color: red; }
So the setup for this is as follows:
config.stylesheetParser_skipSelectors = /^body\./i; // need to override this config.stylesheetParser_validSelectors = /\.\w+/;
This setup works, however the list of styles contains a broken style because .myclass
isn't associated with any element.
Attachments (1)
Change History (9)
Changed 12 years ago by
Attachment: | ssparserPureClass.png added |
---|
comment:2 Changed 12 years ago by
The problem isn't the stylesheet parser but the Style system in CKEditor that doesn't allow to create generic rules for any element.
The parser could try to parse those rules and asign them to span, div, etc... but it will be just a wild guess, the fix must come first in the Style system.
comment:3 Changed 12 years ago by
Resolution: | → duplicate |
---|---|
Status: | new → closed |
We have this ticket already - #5980.
In short it is not possible to use wildcard (not assigned to any element) style because you have to present it somehow in dropdown.
comment:4 Changed 9 years ago by
Hello a.nowodzinski,
after 22 hour I found your post on stackoverflow and this one.
After a mix of patches it work now also for me.
Patched File:
/** * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ /** * @fileOverview stylesheetParser plugin. */ ( function() { // We want to extract only the elements with classes defined in the stylesheets: function parseClasses( aRules, skipSelectors, validSelectors ) { // aRules are just the different rules in the style sheets // We want to merge them and them split them by commas, so we end up with only // the selectors var s = aRules.join( ' ' ); // Remove selectors splitting the elements, leave only the class selector (.) s = s.replace( /(,|>|\+|~)/g, ' ' ); // Remove attribute selectors: table[border="0"] s = s.replace( /\[[^\]]*/g, '' ); // Remove Ids: div#main s = s.replace( /#[^\s]*/g, '' ); // Remove pseudo-selectors and pseudo-elements: :hover :nth-child(2n+1) ::before s = s.replace( /\:{1,2}[^\s]*/g, '' ); s = s.replace( /\s+/g, ' ' ); var aSelectors = s.split( ' ' ), aClasses = []; for ( var i = 0; i < aSelectors.length; i++ ) { var selector = aSelectors[ i ]; if ( validSelectors.test( selector ) && !skipSelectors.test( selector ) ) { // If we still don't know about this one, add it if ( CKEDITOR.tools.indexOf( aClasses, selector ) == -1 ) aClasses.push( selector ); } } return aClasses; } function LoadStylesCSS( theDoc, skipSelectors, validSelectors ) { var styles = [], // It will hold all the rules of the applied stylesheets (except those internal to CKEditor) aRules = [], i; for ( i = 0; i < theDoc.styleSheets.length; i++ ) { var sheet = theDoc.styleSheets[ i ], node = sheet.ownerNode || sheet.owningElement; // Skip the internal stylesheets if ( node.getAttribute( 'data-cke-temp' ) ) continue; // Exclude stylesheets injected by extensions if ( sheet.href && sheet.href.substr( 0, 9 ) == 'chrome://' ) continue; // Bulletproof with x-domain content stylesheet. try { var sheetRules = sheet.cssRules || sheet.rules; for ( var j = 0; j < sheetRules.length; j++ ) aRules.push( sheetRules[ j ].selectorText ); } catch ( e ) {} } var aClasses = parseClasses( aRules, skipSelectors, validSelectors ); // Add each style to our "Styles" collection. for ( i = 0; i < aClasses.length; i++ ) { // Stylesheet parser should accept CSS classes without elements - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/9922) var oElement = aClasses[ i ].split( '.' ), element, sClassName; if ( !oElement.length ) { element = '', sClassName = oElement; } else { element = oElement[ 0 ].toLowerCase(), sClassName = oElement[ 1 ]; } styles.push({ name: element + '.' + sClassName, element: !element.length ? 'span' : element, attributes: { 'class': sClassName } }); } console.log(styles); return styles; } // Register a plugin named "stylesheetparser". CKEDITOR.plugins.add( 'stylesheetparser', { init: function( editor ) { // Stylesheet parser is incompatible with filter (#10136). editor.filter.disable(); var cachedDefinitions; editor.once( 'stylesSet', function( evt ) { // Cancel event and fire it again when styles are ready. evt.cancel(); // Overwrite editor#getStylesSet asap (contentDom is the first moment // when editor.document is ready), but before stylescombo reads styles set (priority 5). editor.once( 'contentDom', function() { window.setTimeout( function() { // Patch stylesheet parser error if file not on cache - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/8832) editor.getStylesSet( function( definitions ) { // Rules that must be skipped var skipSelectors = editor.config.stylesheetParser_skipSelectors || ( /(^body\.|^\.)/i ), // Rules that are valid validSelectors = editor.config.stylesheetParser_validSelectors || ( /\w+\.\w+/ ); cachedDefinitions = definitions.concat( LoadStylesCSS( editor.document.$, skipSelectors, validSelectors ) ); editor.getStylesSet = function( callback ) { if ( cachedDefinitions ) return callback( cachedDefinitions ); }; editor.fire( 'stylesSet', { styles: cachedDefinitions } ); } ); }, 1500); } ); }, null, null, 1 ); } } ); } )(); /** * A regular expression that defines whether a CSS rule will be * skipped by the Stylesheet Parser plugin. A CSS rule matching * the regular expression will be ignored and will not be available * in the Styles drop-down list. * * // Ignore rules for body and caption elements, classes starting with "high", and any class defined for no specific element. * config.stylesheetParser_skipSelectors = /(^body\.|^caption\.|\.high|^\.)/i; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_skipSelectors=/(^body\.|^\.)/i] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_validSelectors */ /** * A regular expression that defines which CSS rules will be used * by the Stylesheet Parser plugin. A CSS rule matching the regular * expression will be available in the Styles drop-down list. * * // Only add rules for p and span elements. * config.stylesheetParser_validSelectors = /\^(p|span)\.\w+/; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_validSelectors=/\w+\.\w+/] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_skipSelectors */
But it work only on Chrome on FF the Combo is still empty, do you have an idea?
Also is it possiblke to increase the size of the Combo-List a little bit,
because the user see only
<class="....
comment:5 Changed 9 years ago by
Addition:
if I not overwrite the default styles with:
stylesSet: [],
it work also for FF.
My Config: CKEDITOR.replace('ckeContent', { language: 'en', bodyClass: 'pnlCMS', stylesheetParser_validSelectors: /\.\w+/, stylesheetParser_skipSelectors: /body\./i, allowedContent: true, extraPlugins: 'stylesheetparser', contentsCss: './my.css', on: {
save: function(evt) {
saveContent(); return false;
}
}, });
My plugin.js
/** * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ /** * @fileOverview stylesheetParser plugin. */ ( function() { // We want to extract only the elements with classes defined in the stylesheets: function parseClasses( aRules, skipSelectors, validSelectors ) { // aRules are just the different rules in the style sheets // We want to merge them and them split them by commas, so we end up with only // the selectors var s = aRules.join( ' ' ); // Remove selectors splitting the elements, leave only the class selector (.) s = s.replace( /(,|>|\+|~)/g, ' ' ); // Remove attribute selectors: table[border="0"] s = s.replace( /\[[^\]]*/g, '' ); // Remove Ids: div#main s = s.replace( /#[^\s]*/g, '' ); // Remove pseudo-selectors and pseudo-elements: :hover :nth-child(2n+1) ::before s = s.replace( /\:{1,2}[^\s]*/g, '' ); s = s.replace( /\s+/g, ' ' ); var aSelectors = s.split( ' ' ), aClasses = []; for ( var i = 0; i < aSelectors.length; i++ ) { var selector = aSelectors[ i ]; if ( validSelectors.test( selector ) && !skipSelectors.test( selector ) ) { // If we still don't know about this one, add it if ( CKEDITOR.tools.indexOf( aClasses, selector ) == -1 ) aClasses.push( selector ); } } return aClasses; } function LoadStylesCSS( theDoc, skipSelectors, validSelectors ) { var styles = [], // It will hold all the rules of the applied stylesheets (except those internal to CKEditor) aRules = [], i; for ( i = 0; i < theDoc.styleSheets.length; i++ ) { var sheet = theDoc.styleSheets[ i ], node = sheet.ownerNode || sheet.owningElement; // Skip the internal stylesheets if ( node.getAttribute( 'data-cke-temp' ) ) continue; // Exclude stylesheets injected by extensions if ( sheet.href && sheet.href.substr( 0, 9 ) == 'chrome://' ) continue; // Bulletproof with x-domain content stylesheet. try { var sheetRules = sheet.cssRules || sheet.rules; for ( var j = 0; j < sheetRules.length; j++ ) aRules.push( sheetRules[ j ].selectorText ); } catch ( e ) {} } var aClasses = parseClasses( aRules, skipSelectors, validSelectors ); // Add each style to our "Styles" collection. for ( i = 0; i < aClasses.length; i++ ) { // Stylesheet parser should accept CSS classes without elements - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/9922) var oElement = aClasses[ i ].split( '.' ), element, sClassName, name; if ( !oElement.length ) { element = '', sClassName = oElement; name = element + '.' + sClassName; } else { element = oElement[ 0 ].toLowerCase(), sClassName = oElement[ 1 ]; name = (element?element:'') + '.' + sClassName; } styles.push({ name: name, element: !element.length ? 'span' : element, attributes: { 'class': sClassName } }); } return styles; } // Register a plugin named "stylesheetparser". CKEDITOR.plugins.add( 'stylesheetparser', { init: function( editor ) { // Stylesheet parser is incompatible with filter (#10136). editor.filter.disable(); var cachedDefinitions; if(typeof timer_delay_parse_styles != 'undefined') window.clearTimeout( timer_delay_parse_styles ); timer_delay_parse_styles = null; editor.once( 'stylesSet', function( evt ) { // Cancel event and fire it again when styles are ready. evt.cancel(); // Overwrite editor#getStylesSet asap (contentDom is the first moment // when editor.document is ready), but before stylescombo reads styles set (priority 5). editor.once( 'contentDom', function() { // Use a delay before parsing the stylesheet to avoid errors with Firefox 4. #7784 timer_delay_parse_styles = window.setTimeout( function() { // Patch stylesheet parser error if file not on cache - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/8832) editor.getStylesSet( function( definitions ) { // Rules that must be skipped var skipSelectors = editor.config.stylesheetParser_skipSelectors || ( /(^body\.|^\.)/i ), // Rules that are valid validSelectors = editor.config.stylesheetParser_validSelectors || ( /\w+\.\w+/ ); cachedDefinitions = definitions.concat( LoadStylesCSS( editor.document.$, skipSelectors, validSelectors ) ); editor.getStylesSet = function( callback ) { if ( cachedDefinitions ) return callback( cachedDefinitions ); }; editor.fire( 'stylesSet', { styles: cachedDefinitions } ); } ); }, 1500); } ); }, null, null, 1 ); } } ); } )(); /** * A regular expression that defines whether a CSS rule will be * skipped by the Stylesheet Parser plugin. A CSS rule matching * the regular expression will be ignored and will not be available * in the Styles drop-down list. * * // Ignore rules for body and caption elements, classes starting with "high", and any class defined for no specific element. * config.stylesheetParser_skipSelectors = /(^body\.|^caption\.|\.high|^\.)/i; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_skipSelectors=/(^body\.|^\.)/i] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_validSelectors */ /** * A regular expression that defines which CSS rules will be used * by the Stylesheet Parser plugin. A CSS rule matching the regular * expression will be available in the Styles drop-down list. * * // Only add rules for p and span elements. * config.stylesheetParser_validSelectors = /\^(p|span)\.\w+/; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_validSelectors=/\w+\.\w+/] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_skipSelectors */
comment:6 Changed 9 years ago by
@IcemanX could I ask you to submit your pull request to ckeditor-dev ?
Please note that it is required to provide tests to your PR and describe what or which bug does it fix exactly (In this case this is #5980). Please see e.g. https://github.com/ckeditor/ckeditor-dev/pull/185 or other accepted pull requests.
Please also read http://docs.ckeditor.com/#!/guide/dev_contributing_code
To make dropdown wider, please see http://dev.ckeditor.com/ticket/6162#comment:18.
comment:7 Changed 9 years ago by
My last fixes with "pull request" on Github https://github.com/ckeditor/ckeditor-dev/pull/203
/** * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ /** * @fileOverview stylesheetParser plugin. */ ( function() { // We want to extract only the elements with classes defined in the stylesheets: function parseClasses( aRules, skipSelectors, validSelectors ) { // aRules are just the different rules in the style sheets // We want to merge them and them split them by commas, so we end up with only // the selectors var s = aRules.join( ' ' ); // Remove selectors splitting the elements, leave only the class selector (.) s = s.replace( /(,|>|\+|~)/g, ' ' ); // Remove attribute selectors: table[border="0"] s = s.replace( /\[[^\]]*/g, '' ); // Remove Ids: div#main s = s.replace( /#[^\s]*/g, '' ); // Remove pseudo-selectors and pseudo-elements: :hover :nth-child(2n+1) ::before s = s.replace( /\:{1,2}[^\s]*/g, '' ); s = s.replace( /\s+/g, ' ' ); var aSelectors = s.split( ' ' ), aClasses = []; for ( var i = 0; i < aSelectors.length; i++ ) { var selector = aSelectors[ i ]; if ( validSelectors.test( selector ) && !skipSelectors.test( selector ) ) { // If we still don't know about this one, add it if ( CKEDITOR.tools.indexOf( aClasses, selector ) == -1 ) aClasses.push( selector ); } } return aClasses; } function LoadStylesCSS( theDoc, skipSelectors, validSelectors ) { var styles = [], // It will hold all the rules of the applied stylesheets (except those internal to CKEditor) aRules = [], i; for ( i = 0; i < theDoc.styleSheets.length; i++ ) { var sheet = theDoc.styleSheets[ i ], node = sheet.ownerNode || sheet.owningElement; // Skip the internal stylesheets if ( node.getAttribute( 'data-cke-temp' ) ) continue; // Exclude stylesheets injected by extensions if ( sheet.href && sheet.href.substr( 0, 9 ) == 'chrome://' ) continue; // Bulletproof with x-domain content stylesheet. try { var sheetRules = sheet.cssRules || sheet.rules; for ( var j = 0; j < sheetRules.length; j++ ) aRules.push( sheetRules[ j ].selectorText ); } catch ( e ) {} } var aClasses = parseClasses( aRules, skipSelectors, validSelectors ); // Add each style to our "Styles" collection. for ( i = 0; i < aClasses.length; i++ ) { // Stylesheet parser should accept CSS classes without elements (sample: .table_darkgrey) - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/9922) var oElement = aClasses[ i ].split( '.' ), element, sClassName, name; if ( !oElement.length ) { element = '', sClassName = oElement; name = element + '.' + sClassName; } else { element = oElement[ 0 ].toLowerCase(), sClassName = oElement[ 1 ]; name = (element?element:'') + '.' + sClassName; } styles.push({ name: name, element: !element.length ? 'span' : element, attributes: { 'class': sClassName } }); } return styles; } // Register a plugin named "stylesheetparser". CKEDITOR.plugins.add( 'stylesheetparser', { init: function( editor ) { // Stylesheet parser is incompatible with filter (#10136). editor.filter.disable(); var cachedDefinitions; if(typeof timer_delay_parse_styles != 'undefined') window.clearTimeout( timer_delay_parse_styles ); timer_delay_parse_styles = null; editor.once( 'stylesSet', function( evt ) { // Cancel event and fire it again when styles are ready. evt.cancel(); // Overwrite editor#getStylesSet asap (contentDom is the first moment // when editor.document is ready), but before stylescombo reads styles set (priority 5). editor.once( 'contentDom', function() { // Use a delay before parsing the stylesheet to avoid errors with Firefox 4 and Safari. #7784 timer_delay_parse_styles = window.setTimeout( function() { // Patch stylesheet parser error if file not on cache - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/8832) editor.getStylesSet( function( definitions ) { // Rules that must be skipped var skipSelectors = editor.config.stylesheetParser_skipSelectors || ( /^body\./i ), // Stylesheet parser should accept CSS classes without elements (sample: .table_darkgrey) - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/9922) // Rules that are valid validSelectors = editor.config.stylesheetParser_validSelectors || ( /\.\w+/ ); // Stylesheet parser should accept CSS classes without elements (sample: .table_darkgrey) - IcemanX 2015.07.22 - en.cescon.de (http://dev.ckeditor.com/ticket/9922) cachedDefinitions = definitions.concat( LoadStylesCSS( editor.document.$, skipSelectors, validSelectors ) ); editor.getStylesSet = function( callback ) { if ( cachedDefinitions ) return callback( cachedDefinitions ); }; editor.fire( 'stylesSet', { styles: cachedDefinitions } ); } ); }, 1500); } ); }, null, null, 1 ); } } ); } )(); /** * A regular expression that defines whether a CSS rule will be * skipped by the Stylesheet Parser plugin. A CSS rule matching * the regular expression will be ignored and will not be available * in the Styles drop-down list. * * // Ignore rules for body and caption elements, classes starting with "high", and any class defined for no specific element. * config.stylesheetParser_skipSelectors = /(^body\.|^caption\.|\.high|^\.)/i; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_skipSelectors=/(^body\.|^\.)/i] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_validSelectors */ /** * A regular expression that defines which CSS rules will be used * by the Stylesheet Parser plugin. A CSS rule matching the regular * expression will be available in the Styles drop-down list. * * // Only add rules for p and span elements. * config.stylesheetParser_validSelectors = /\^(p|span)\.\w+/; * * @since 3.6 * @cfg {RegExp} [stylesheetParser_validSelectors=/\w+\.\w+/] * @member CKEDITOR.config * @see CKEDITOR.config#stylesheetParser_skipSelectors */
comment:8 Changed 9 years ago by
Unfortunately, I needed to close the PRs. Please keep comment:2 in mind. As Alfonso correctly commented, the real fix would need to be a feature of the styles system, not a workaround in the stylesheetparser plugin.