1 /*
  2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5 
  6 /**
  7  * @stylesheetParser plugin.
  8  */
  9 
 10 (function()
 11 {
 12 	// We want to extract only the elements with classes defined in the stylesheets:
 13 	function parseClasses( aRules, skipSelectors, validSelectors )
 14 	{
 15 		// aRules are just the different rules in the style sheets
 16 		// We want to merge them and them split them by commas, so we end up with only
 17 		// the selectors
 18 		var s = aRules.join(' ');
 19 		// Remove selectors splitting the elements, leave only the class selector (.)
 20 		s = s.replace( /(,|>|\+|~)/g, ' ' );
 21 		// Remove attribute selectors: table[border="0"]
 22 		s = s.replace( /\[[^\]]*/g, '' );
 23 		// Remove Ids: div#main
 24 		s = s.replace( /#[^\s]*/g, '' );
 25 		// Remove pseudo-selectors and pseudo-elements: :hover :nth-child(2n+1) ::before
 26 		s = s.replace( /\:{1,2}[^\s]*/g, '' );
 27 
 28 		s = s.replace( /\s+/g, ' ' );
 29 
 30 		var aSelectors = s.split( ' ' ),
 31 			aClasses = [];
 32 
 33 		for ( var i = 0; i < aSelectors.length ; i++ )
 34 		{
 35 			var selector = aSelectors[ i ];
 36 
 37 			if ( validSelectors.test( selector ) && !skipSelectors.test( selector ) )
 38 			{
 39 				// If we still don't know about this one, add it
 40 				if ( CKEDITOR.tools.indexOf( aClasses, selector ) == -1 )
 41 					aClasses.push( selector );
 42 			}
 43 		}
 44 
 45 		return aClasses;
 46 	}
 47 
 48 	function LoadStylesCSS( theDoc, skipSelectors, validSelectors )
 49 	{
 50 		var styles = [],
 51 			// It will hold all the rules of the applied stylesheets (except those internal to CKEditor)
 52 			aRules = [],
 53 			i;
 54 
 55 		for ( i = 0; i < theDoc.styleSheets.length; i++ )
 56 		{
 57 			var sheet = theDoc.styleSheets[ i ],
 58 				node = sheet.ownerNode || sheet.owningElement;
 59 
 60 			// Skip the internal stylesheets
 61 			if ( node.getAttribute( 'data-cke-temp' ) )
 62 				continue;
 63 
 64 			// Exclude stylesheets injected by extensions
 65 			if ( sheet.href && sheet.href.substr(0, 9) == 'chrome://' )
 66 				continue;
 67 
 68 			var sheetRules = sheet.cssRules || sheet.rules;
 69 			for ( var j = 0; j < sheetRules.length; j++ )
 70 				aRules.push( sheetRules[ j ].selectorText );
 71 		}
 72 
 73 		var aClasses = parseClasses( aRules, skipSelectors, validSelectors );
 74 
 75 		// Add each style to our "Styles" collection.
 76 		for ( i = 0; i < aClasses.length; i++ )
 77 		{
 78 			var oElement = aClasses[ i ].split( '.' ),
 79 				element = oElement[ 0 ].toLowerCase(),
 80 				sClassName = oElement[ 1 ];
 81 
 82 			styles.push( {
 83 				name : element + '.' + sClassName,
 84 				element : element,
 85 				attributes : {'class' : sClassName}
 86 			});
 87 		}
 88 
 89 		return styles;
 90 	}
 91 
 92 	// Register a plugin named "stylesheetparser".
 93 	CKEDITOR.plugins.add( 'stylesheetparser',
 94 	{
 95 		requires: [ 'styles' ],
 96 		onLoad : function()
 97 		{
 98 			var obj = CKEDITOR.editor.prototype;
 99 			obj.getStylesSet = CKEDITOR.tools.override( obj.getStylesSet,  function( org )
100 			{
101 				return function( callback )
102 				{
103 					var self = this;
104 					org.call( this, function( definitions )
105 					{
106 						// Rules that must be skipped
107 						var skipSelectors = self.config.stylesheetParser_skipSelectors || ( /(^body\.|^\.)/i ),
108 							// Rules that are valid
109 							validSelectors = self.config.stylesheetParser_validSelectors || ( /\w+\.\w+/ );
110 
111 						callback( ( self._.stylesDefinitions = definitions.concat( LoadStylesCSS( self.document.$, skipSelectors, validSelectors ) ) ) );
112 					});
113 				};
114 			});
115 
116 		}
117 	});
118 })();
119 
120 
121 /**
122  * A regular expression that defines whether a CSS rule will be
123  * skipped by the Stylesheet Parser plugin. A CSS rule matching
124  * the regular expression will be ignored and will not be available
125  * in the Styles drop-down list.
126  * @name CKEDITOR.config.stylesheetParser_skipSelectors
127  * @type RegExp
128  * @default /(^body\.|^\.)/i
129  * @since 3.6
130  * @see CKEDITOR.config.stylesheetParser_validSelectors
131  * @example
132  * // Ignore rules for body and caption elements, classes starting with "high", and any class defined for no specific element.
133  * config.stylesheetParser_skipSelectors = /(^body\.|^caption\.|\.high|^\.)/i;
134  */
135 
136  /**
137  * A regular expression that defines which CSS rules will be used
138  * by the Stylesheet Parser plugin. A CSS rule matching the regular
139  * expression will be available in the Styles drop-down list.
140  * @name CKEDITOR.config.stylesheetParser_validSelectors
141  * @type RegExp
142  * @default /\w+\.\w+/
143  * @since 3.6
144  * @see CKEDITOR.config.stylesheetParser_skipSelectors
145  * @example
146  * // Only add rules for p and span elements.
147  * config.stylesheetParser_validSelectors = /\^(p|span)\.\w+/;
148  */
149