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