|
1 /** |
|
2 * plugin.js |
|
3 * |
|
4 * Copyright, Moxiecode Systems AB |
|
5 * Released under LGPL License. |
|
6 * |
|
7 * License: http://www.tinymce.com/license |
|
8 * Contributing: http://www.tinymce.com/contributing |
|
9 */ |
|
10 |
|
11 /*global tinymce:true */ |
|
12 |
|
13 tinymce.PluginManager.add('importcss', function(editor) { |
|
14 var self = this, each = tinymce.each; |
|
15 |
|
16 function compileFilter(filter) { |
|
17 if (typeof filter == "string") { |
|
18 return function(value) { |
|
19 return value.indexOf(filter) !== -1; |
|
20 }; |
|
21 } else if (filter instanceof RegExp) { |
|
22 return function(value) { |
|
23 return filter.test(value); |
|
24 }; |
|
25 } |
|
26 |
|
27 return filter; |
|
28 } |
|
29 |
|
30 function getSelectors(doc, fileFilter) { |
|
31 var selectors = [], contentCSSUrls = {}; |
|
32 |
|
33 function append(styleSheet, imported) { |
|
34 var href = styleSheet.href, rules; |
|
35 |
|
36 if (!href || !fileFilter(href, imported)) { |
|
37 return; |
|
38 } |
|
39 |
|
40 each(styleSheet.imports, function(styleSheet) { |
|
41 append(styleSheet, true); |
|
42 }); |
|
43 |
|
44 try { |
|
45 rules = styleSheet.cssRules || styleSheet.rules; |
|
46 } catch (e) { |
|
47 // Firefox fails on rules to remote domain for example: |
|
48 // @import url(//fonts.googleapis.com/css?family=Pathway+Gothic+One); |
|
49 } |
|
50 |
|
51 each(rules, function(cssRule) { |
|
52 if (cssRule.styleSheet) { |
|
53 append(cssRule.styleSheet, true); |
|
54 } else if (cssRule.selectorText) { |
|
55 each(cssRule.selectorText.split(','), function(selector) { |
|
56 selectors.push(tinymce.trim(selector)); |
|
57 }); |
|
58 } |
|
59 }); |
|
60 } |
|
61 |
|
62 each(editor.contentCSS, function(url) { |
|
63 contentCSSUrls[url] = true; |
|
64 }); |
|
65 |
|
66 if (!fileFilter) { |
|
67 fileFilter = function(href, imported) { |
|
68 return imported || contentCSSUrls[href]; |
|
69 }; |
|
70 } |
|
71 |
|
72 try { |
|
73 each(doc.styleSheets, function(styleSheet) { |
|
74 append(styleSheet); |
|
75 }); |
|
76 } catch (e) {} |
|
77 |
|
78 return selectors; |
|
79 } |
|
80 |
|
81 function convertSelectorToFormat(selectorText) { |
|
82 var format; |
|
83 |
|
84 // Parse simple element.class1, .class1 |
|
85 var selector = /^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(selectorText); |
|
86 if (!selector) { |
|
87 return; |
|
88 } |
|
89 |
|
90 var elementName = selector[1]; |
|
91 var classes = selector[2].substr(1).split('.').join(' '); |
|
92 var inlineSelectorElements = tinymce.makeMap('a,img'); |
|
93 |
|
94 // element.class - Produce block formats |
|
95 if (selector[1]) { |
|
96 format = { |
|
97 title: selectorText |
|
98 }; |
|
99 |
|
100 if (editor.schema.getTextBlockElements()[elementName]) { |
|
101 // Text block format ex: h1.class1 |
|
102 format.block = elementName; |
|
103 } else if (editor.schema.getBlockElements()[elementName] || inlineSelectorElements[elementName.toLowerCase()]) { |
|
104 // Block elements such as table.class and special inline elements such as a.class or img.class |
|
105 format.selector = elementName; |
|
106 } else { |
|
107 // Inline format strong.class1 |
|
108 format.inline = elementName; |
|
109 } |
|
110 } else if (selector[2]) { |
|
111 // .class - Produce inline span with classes |
|
112 format = { |
|
113 inline: 'span', |
|
114 title: selectorText.substr(1), |
|
115 classes: classes |
|
116 }; |
|
117 } |
|
118 |
|
119 // Append to or override class attribute |
|
120 if (editor.settings.importcss_merge_classes !== false) { |
|
121 format.classes = classes; |
|
122 } else { |
|
123 format.attributes = {"class": classes}; |
|
124 } |
|
125 |
|
126 return format; |
|
127 } |
|
128 |
|
129 editor.on('renderFormatsMenu', function(e) { |
|
130 var settings = editor.settings, selectors = {}; |
|
131 var selectorConverter = settings.importcss_selector_converter || convertSelectorToFormat; |
|
132 var selectorFilter = compileFilter(settings.importcss_selector_filter), ctrl = e.control; |
|
133 |
|
134 if (!editor.settings.importcss_append) { |
|
135 ctrl.items().remove(); |
|
136 } |
|
137 |
|
138 // Setup new groups collection by cloning the configured one |
|
139 var groups = []; |
|
140 tinymce.each(settings.importcss_groups, function(group) { |
|
141 group = tinymce.extend({}, group); |
|
142 group.filter = compileFilter(group.filter); |
|
143 groups.push(group); |
|
144 }); |
|
145 |
|
146 each(getSelectors(e.doc || editor.getDoc(), compileFilter(settings.importcss_file_filter)), function(selector) { |
|
147 if (selector.indexOf('.mce-') === -1) { |
|
148 if (!selectors[selector] && (!selectorFilter || selectorFilter(selector))) { |
|
149 var format = selectorConverter.call(self, selector), menu; |
|
150 |
|
151 if (format) { |
|
152 var formatName = format.name || tinymce.DOM.uniqueId(); |
|
153 |
|
154 if (groups) { |
|
155 for (var i = 0; i < groups.length; i++) { |
|
156 if (!groups[i].filter || groups[i].filter(selector)) { |
|
157 if (!groups[i].item) { |
|
158 groups[i].item = {text: groups[i].title, menu: []}; |
|
159 } |
|
160 |
|
161 menu = groups[i].item.menu; |
|
162 break; |
|
163 } |
|
164 } |
|
165 } |
|
166 |
|
167 editor.formatter.register(formatName, format); |
|
168 |
|
169 var menuItem = tinymce.extend({}, ctrl.settings.itemDefaults, { |
|
170 text: format.title, |
|
171 format: formatName |
|
172 }); |
|
173 |
|
174 if (menu) { |
|
175 menu.push(menuItem); |
|
176 } else { |
|
177 ctrl.add(menuItem); |
|
178 } |
|
179 } |
|
180 |
|
181 selectors[selector] = true; |
|
182 } |
|
183 } |
|
184 }); |
|
185 |
|
186 each(groups, function(group) { |
|
187 ctrl.add(group.item); |
|
188 }); |
|
189 |
|
190 e.control.renderNew(); |
|
191 }); |
|
192 |
|
193 // Expose default convertSelectorToFormat implementation |
|
194 self.convertSelectorToFormat = convertSelectorToFormat; |
|
195 }); |