2f640f25f16359c3e22c3000239e8816fe0e217d
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / htmlarea / plugins / DynamicCSS / dynamiccss.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2004 systemconcept.de. Authored by Holger Hees, sponsored by http://www.systemconcept.de.
5 * (c) 2004-2007 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This script is a modified version of a script published under the htmlArea License.
26 * A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30 /*
31 * Dynamic CSS Plugin for TYPO3 htmlArea RTE
32 *
33 * TYPO3 CVS ID: $Id$
34 */
35 DynamicCSS = HTMLArea.Plugin.extend({
36
37 constructor : function(editor, pluginName) {
38 this.base(editor, pluginName);
39 },
40
41 /*
42 * This function gets called by the class constructor
43 */
44 configurePlugin : function(editor) {
45 var editorNumber = editor._editorNumber;
46
47 /* Registering plugin "About" information */
48 var pluginInformation = {
49 version : "1.9",
50 developer : "Holger Hees & Stanislas Rolland",
51 developerUrl : "http://www.fructifor.ca/",
52 copyrightOwner : "Holger Hees & Stanislas Rolland",
53 sponsor : "Fructifor Inc.",
54 sponsorUrl : "System Concept GmbH & Fructifor Inc.",
55 license : "GPL"
56 };
57 this.registerPluginInformation(pluginInformation);
58
59 /* Registering the dropdown list */
60 var buttonId = "DynamicCSS-class";
61 var dropDownConfiguration = {
62 id : buttonId,
63 tooltip : this.localize("DynamicCSSStyleTooltip"),
64 options : {"":""},
65 action : "onSelect",
66 refresh : "generate",
67 context : "*",
68 cssArray : new Object(),
69 parseCount : 1,
70 loaded : false,
71 timeout : null,
72 lastTag : "",
73 lastClass : "",
74 showTagFreeClasses : RTEarea[editorNumber]["showTagFreeClasses"],
75 classesUrl : RTEarea[editorNumber]["classesUrl"],
76 classesTag : RTEarea[editorNumber]["classesTag"]
77 };
78 this.registerDropDown(dropDownConfiguration);
79
80 return true;
81 },
82
83 onSelect : function(editor, buttonId) {
84 var obj = this.editorConfiguration.customSelects[buttonId];
85 var tbobj = editor._toolbarObjects[buttonId];
86 var index = document.getElementById(tbobj.elementId).selectedIndex;
87 var className = document.getElementById(tbobj.elementId).value;
88
89 editor.focusEditor();
90 var blocks = this.getSelectedBlocks(editor);
91 for (var k = 0; k < blocks.length; ++k) {
92 var parent = blocks[k];
93 while (typeof(parent) != "undefined" && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") parent = parent.parentNode;
94 if (!k) var tagName = parent.tagName.toLowerCase();
95 if (parent.tagName.toLowerCase() == tagName) {
96 var cls = parent.className.trim().split(" ");
97 for (var i = cls.length; i > 0;) if(!HTMLArea.reservedClassNames.test(cls[--i])) HTMLArea._removeClass(parent,cls[i]);
98 if(className != 'none'){
99 HTMLArea._addClass(parent,className);
100 obj.lastClass = className;
101 }
102 }
103 }
104 },
105
106 getSelectedBlocks : function(editor) {
107 var block, range, i = 0, blocks = [];
108 if (HTMLArea.is_gecko && !HTMLArea.is_safari && !HTMLArea.is_opera) {
109 var sel = editor._getSelection();
110 try {
111 while (range = sel.getRangeAt(i++)) {
112 block = editor.getParentElement(sel, range);
113 blocks.push(block);
114 }
115 } catch(e) {
116 /* finished walking through selection */
117 }
118 } else {
119 blocks.push(editor.getParentElement());
120 }
121 return blocks;
122 },
123
124 onGenerate : function() {
125 var editor = this.editor;
126 var obj = editor.config.customSelects["DynamicCSS-class"];
127 if(HTMLArea.is_gecko) this.generate(editor);
128 },
129
130 onUpdateToolbar : function() {
131 var editor = this.editor;
132 var obj = editor.config.customSelects["DynamicCSS-class"];
133 if (HTMLArea.is_gecko && editor.getMode() === "wysiwyg" && editor.isEditable()) {
134 if(obj.loaded) {
135 this.updateValue(editor,obj);
136 } else {
137 if(obj.timeout) {
138 if(editor._iframe.contentWindow) { editor._iframe.contentWindow.clearTimeout(obj.timeout); } else { window.clearTimeout(obj.timeout); }
139 obj.timeout = null;
140 }
141 this.generate(editor);
142 }
143 } else if (editor.getMode() === "textmode") {
144 var select = document.getElementById(editor._toolbarObjects[obj.id].elementId);
145 select.disabled = true;
146 select.className = "buttonDisabled";
147 }
148 },
149
150 generate : function(editor) {
151 var obj = editor.config.customSelects["DynamicCSS-class"];
152 var classesUrl = obj["classesUrl"];
153 if (classesUrl && typeof(HTMLArea.classesLabels) == "undefined") {
154 var classesData = HTMLArea._getScript(0, false, classesUrl);
155 if (classesData) eval(classesData);
156 }
157 // Let us load the style sheets
158 if(obj.loaded) this.updateValue(editor,obj);
159 else this.getCSSArray(editor);
160 },
161
162 getCSSArray : function(editor) {
163 var obj = editor.config.customSelects["DynamicCSS-class"];
164 obj.cssArray = this.parseStyleSheet(editor);
165 if( !obj.loaded && obj.parseCount<17 ) {
166 var getCSSArrayLaterFunctRef = DynamicCSS.getCSSArrayLater(editor, this);
167 obj.timeout = editor._iframe.contentWindow ? editor._iframe.contentWindow.setTimeout(getCSSArrayLaterFunctRef, 200) : window.setTimeout(getCSSArrayLaterFunctRef, 200);
168 obj.parseCount++ ;
169 } else {
170 obj.timeout = null;
171 obj.loaded = true;
172 this.updateValue(editor,obj);
173 }
174 },
175
176 onMode : function(mode) {
177 var editor = this.editor;
178 if (mode == 'wysiwyg' && editor.isEditable()) {
179 var obj = editor.config.customSelects["DynamicCSS-class"];
180 if (obj.loaded) {
181 this.updateValue(editor,obj);
182 } else {
183 if(obj.timeout) {
184 if (editor._iframe.contentWindow) editor._iframe.contentWindow.clearTimeout(obj.timeout);
185 else window.clearTimeout(obj.timeout);
186 obj.timeout = null;
187 }
188 this.generate(editor);
189 }
190 }
191 },
192
193 updateValue : function(editor,obj) {
194 var cssClass, i;
195 if(!obj.loaded) {
196 if(obj.timeout) {
197 if(editor._iframe.contentWindow) editor._iframe.contentWindow.clearTimeout(obj.timeout);
198 else window.clearTimeout(obj.timeout);
199 obj.timeout = null;
200 }
201 this.generate(editor);
202 }
203 var cssArray = obj.cssArray;
204 var tagName = "body";
205 var className = "";
206 var parent = editor.getParentElement();
207 while(parent && typeof(parent) != "undefined" && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") parent = parent.parentNode;
208 if(parent) {
209 tagName = parent.nodeName.toLowerCase();
210 className = parent.className;
211 if(HTMLArea.reservedClassNames.test(className)) {
212 var cls = className.split(" ");
213 for (var i = cls.length; i > 0;) if(!HTMLArea.reservedClassNames.test(cls[--i])) className = cls[i];
214 }
215 }
216 if(obj.lastTag != tagName || obj.lastClass != className){
217 obj.lastTag = tagName;
218 obj.lastClass = className;
219 var select = document.getElementById(editor._toolbarObjects[obj.id].elementId);
220 while(select.options.length>0) select.options[select.length-1] = null;
221 select.options[0]=new Option(this.localize("Default"),'none');
222 if(cssArray){
223 // style class only allowed if parent tag is not body or editor is in fullpage mode
224 if(tagName != 'body' || editor.config.fullPage){
225 if(cssArray[tagName]){
226 for (cssClass in cssArray[tagName]){
227 if (cssArray[tagName].hasOwnProperty(cssClass)) {
228 if (cssClass == 'none') {
229 select.options[0] = new Option(cssArray[tagName][cssClass],cssClass);
230 } else {
231 select.options[select.options.length] = new Option(cssArray[tagName][cssClass],cssClass);
232 if (!editor.config.disablePCexamples && HTMLArea.classesValues && HTMLArea.classesValues[cssClass] && !HTMLArea.classesNoShow[cssClass]) select.options[select.options.length-1].setAttribute("style", HTMLArea.classesValues[cssClass]);
233 }
234 }
235 }
236 }
237 if (cssArray['all']){
238 for (cssClass in cssArray['all']){
239 if (cssArray['all'].hasOwnProperty(cssClass)) {
240 select.options[select.options.length] = new Option(cssArray['all'][cssClass],cssClass);
241 if (!editor.config.disablePCexamples && HTMLArea.classesValues && HTMLArea.classesValues[cssClass] && !HTMLArea.classesNoShow[cssClass]) select.options[select.options.length-1].setAttribute("style", HTMLArea.classesValues[cssClass]);
242 }
243 }
244 }
245 } else {
246 if(cssArray[tagName] && cssArray[tagName]['none']) select.options[0] = new Option(cssArray[tagName]['none'],'none');
247 }
248 }
249 select.selectedIndex = 0;
250 if (typeof(className) != "undefined" && /\S/.test(className) && !HTMLArea.reservedClassNames.test(className) ) {
251 for (i = select.options.length; --i >= 0;) {
252 var option = select.options[i];
253 if (className == option.value) {
254 select.selectedIndex = i;
255 break;
256 }
257 }
258 if (select.selectedIndex == 0) {
259 select.options[select.options.length] = new Option(this.localize("Undefined"),className);
260 select.selectedIndex = select.options.length-1;
261 }
262 }
263 if (select.options.length > 1) {
264 select.disabled = false;
265 } else select.disabled = true;
266 if(HTMLArea.is_gecko) select.removeAttribute('class');
267 else select.removeAttribute('className');
268 if (select.disabled) HTMLArea._addClass(select, "buttonDisabled");
269 }
270 },
271
272 parseStyleSheet : function(editor) {
273 var obj = editor.config.customSelects["DynamicCSS-class"];
274 var iframe = editor._iframe.contentWindow ? editor._iframe.contentWindow.document : editor._iframe.contentDocument;
275 var newCssArray = new Object();
276 obj.loaded = true;
277 for (var i = 0; i < iframe.styleSheets.length; i++) {
278 // Mozilla
279 if(HTMLArea.is_gecko){
280 try { newCssArray = this.applyCSSRule(editor,iframe.styleSheets[i].cssRules,newCssArray); }
281 catch (e) { obj.loaded = false; }
282 } else {
283 try{
284 // @import StyleSheets (IE)
285 if (iframe.styleSheets[i].imports) newCssArray = this.applyCSSIEImport(editor,iframe.styleSheets[i].imports,newCssArray);
286 if (iframe.styleSheets[i].rules) newCssArray = this.applyCSSRule(editor,iframe.styleSheets[i].rules,newCssArray);
287 } catch (e) { obj.loaded = false; }
288 }
289 }
290 return newCssArray;
291 },
292
293 applyCSSRule : function(editor, cssRules, cssArray){
294 var cssElements = new Array(),
295 cssElement = new Array(),
296 newCssArray = new Object(),
297 classParts = new Array(),
298 tagName, className, rule, k,
299 obj = editor.config.customSelects["DynamicCSS-class"];
300 newCssArray = cssArray;
301
302 for (rule = 0; rule < cssRules.length; rule++) {
303 // StyleRule
304 if (cssRules[rule].selectorText) {
305 if (cssRules[rule].selectorText.search(/:+/) == -1) {
306 // split equal Styles e.g. head, body {border:0px}
307 cssElements = cssRules[rule].selectorText.split(",");
308 for (k = 0; k < cssElements.length; k++) {
309 cssElement = cssElements[k].split(".");
310 tagName = cssElement[0].toLowerCase().trim();
311 if (!tagName) tagName = "all";
312 className = cssElement[1];
313 if (className) {
314 classParts = className.trim().split(" ");
315 className = classParts[0];
316 }
317 if (!HTMLArea.reservedClassNames.test(className) && ((tagName == "all" && obj["showTagFreeClasses"] == true) || (tagName != "all" && (!obj["classesTag"] || !obj["classesTag"][tagName])) || (tagName != "all" && obj["classesTag"][tagName].indexOf(className) != -1)) ) {
318 if (!newCssArray[tagName]) newCssArray[tagName] = new Object();
319 if (className) {
320 cssName = className;
321 if (HTMLArea.classesLabels) cssName = HTMLArea.classesLabels[className] ? HTMLArea.classesLabels[className] : cssName ;
322 if (tagName != 'all') cssName = '<'+cssName+'>';
323 } else {
324 className='none';
325 if (tagName=='all') cssName=this.localize("Default");
326 else cssName='<'+this.localize("Default")+'>';
327 }
328 newCssArray[tagName][className]=cssName;
329 }
330 }
331 }
332 } else {
333 // ImportRule (Mozilla)
334 if (cssRules[rule].styleSheet) {
335 newCssArray = this.applyCSSRule(editor, cssRules[rule].styleSheet.cssRules, newCssArray);
336 }
337 // MediaRule (Mozilla)
338 if (cssRules[rule].cssRules) {
339 newCssArray = this.applyCSSRule(editor, cssRules[rule].cssRules, newCssArray);
340 }
341 }
342 }
343 return newCssArray;
344 },
345
346 applyCSSIEImport : function(editor,cssIEImport,cssArray){
347 var newCssArray = new Object();
348 newCssArray = cssArray;
349 for (var i=0;i<cssIEImport.length;i++){
350 if(cssIEImport[i].imports){
351 newCssArray = this.applyCSSIEImport(editor,cssIEImport[i].imports,newCssArray);
352 }
353 if(cssIEImport[i].rules){
354 newCssArray = this.applyCSSRule(editor,cssIEImport[i].rules,newCssArray);
355 }
356 }
357 return newCssArray;
358 }
359 });
360
361 DynamicCSS.getCSSArrayLater = function(editor,instance) {
362 return (function() {
363 instance.getCSSArray(editor);
364 });
365 };
366