e6fd66d0d359469858127a613f6733b7c100ba36
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / htmlarea / plugins / BlockStyle / block-style.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2007-2011 Stanislas Rolland <typo3(arobas)sjbr.ca>
5 * All rights reserved
6 *
7 * This script is part of the TYPO3 project. The TYPO3 project is
8 * free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * The GNU General Public License can be found at
14 * http://www.gnu.org/copyleft/gpl.html.
15 * A copy is found in the textfile GPL.txt and important notices to the license
16 * from the author is found in LICENSE.txt distributed with these scripts.
17 *
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This script is a modified version of a script published under the htmlArea License.
25 * A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /*
30 * Block Style Plugin for TYPO3 htmlArea RTE
31 *
32 * TYPO3 SVN ID: $Id$
33 */
34 HTMLArea.BlockStyle = Ext.extend(HTMLArea.Plugin, {
35 /*
36 * This function gets called by the class constructor
37 */
38 configurePlugin: function (editor) {
39 this.cssArray = {};
40 this.classesUrl = this.editorConfiguration.classesUrl;
41 this.pageTSconfiguration = this.editorConfiguration.buttons.blockstyle;
42 this.tags = (this.pageTSconfiguration && this.pageTSconfiguration.tags) ? this.pageTSconfiguration.tags : {};
43 if (typeof(this.editorConfiguration.classesTag) !== "undefined") {
44 if (this.editorConfiguration.classesTag.div) {
45 if (!this.tags.div) {
46 this.tags.div = new Object();
47 }
48 if (!this.tags.div.allowedClasses) {
49 this.tags.div.allowedClasses = this.editorConfiguration.classesTag.div;
50 }
51 }
52 if (this.editorConfiguration.classesTag.td) {
53 if (!this.tags.td) {
54 this.tags.td = new Object();
55 }
56 if (!this.tags.td.allowedClasses) {
57 this.tags.td.allowedClasses = this.editorConfiguration.classesTag.td;
58 }
59 }
60 if (this.editorConfiguration.classesTag.table) {
61 if (!this.tags.table) {
62 this.tags.table = new Object();
63 }
64 if (!this.tags.table.allowedClasses) {
65 this.tags.table.allowedClasses = this.editorConfiguration.classesTag.table;
66 }
67 }
68 }
69 var allowedClasses;
70 for (var tagName in this.tags) {
71 if (this.tags.hasOwnProperty(tagName)) {
72 if (this.tags[tagName].allowedClasses) {
73 allowedClasses = this.tags[tagName].allowedClasses.trim().split(",");
74 for (var cssClass in allowedClasses) {
75 if (allowedClasses.hasOwnProperty(cssClass)) {
76 allowedClasses[cssClass] = allowedClasses[cssClass].trim().replace(/\*/g, ".*");
77 }
78 }
79 this.tags[tagName].allowedClasses = new RegExp( "^(" + allowedClasses.join("|") + ")$", "i");
80 }
81 }
82 }
83 this.showTagFreeClasses = (this.pageTSconfiguration ? this.pageTSconfiguration.showTagFreeClasses : false) || this.editorConfiguration.showTagFreeClasses;
84 this.prefixLabelWithClassName = this.pageTSconfiguration ? this.pageTSconfiguration.prefixLabelWithClassName : false;
85 this.postfixLabelWithClassName = this.pageTSconfiguration ? this.pageTSconfiguration.postfixLabelWithClassName : false;
86 /*
87 * Registering plugin "About" information
88 */
89 var pluginInformation = {
90 version : '2.1',
91 developer : 'Stanislas Rolland',
92 developerUrl : 'http://www.sjbr.ca/',
93 copyrightOwner : 'Stanislas Rolland',
94 sponsor : this.localize('Technische Universitat Ilmenau'),
95 sponsorUrl : 'http://www.tu-ilmenau.de/',
96 license : 'GPL'
97 };
98 this.registerPluginInformation(pluginInformation);
99 /*
100 * Registering the drop-down list
101 */
102 var dropDownId = 'BlockStyle';
103 var fieldLabel = this.pageTSconfiguration ? this.pageTSconfiguration.fieldLabel : '';
104 if (Ext.isEmpty(fieldLabel) && this.isButtonInToolbar('I[Block style label]')) {
105 fieldLabel = this.localize('Block style label');
106 }
107 var dropDownConfiguration = {
108 id: dropDownId,
109 tooltip: this.localize(dropDownId + '-Tooltip'),
110 fieldLabel: fieldLabel,
111 options: [[this.localize('No style'), 'none']],
112 action: 'onChange',
113 storeFields: [ { name: 'text'}, { name: 'value'}, { name: 'style'} ],
114 tpl: '<tpl for="."><div ext:qtip="{value}" style="{style}text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>'
115 };
116 if (this.pageTSconfiguration) {
117 if (this.pageTSconfiguration.width) {
118 dropDownConfiguration.width = parseInt(this.pageTSconfiguration.width, 10);
119 }
120 if (this.pageTSconfiguration.listWidth) {
121 dropDownConfiguration.listWidth = parseInt(this.pageTSconfiguration.listWidth, 10);
122 }
123 if (this.pageTSconfiguration.maxHeight) {
124 dropDownConfiguration.maxHeight = parseInt(this.pageTSconfiguration.maxHeight, 10);
125 }
126 }
127 this.registerDropDown(dropDownConfiguration);
128 return true;
129 },
130 /*
131 * This handler gets called when some block style was selected in the drop-down list
132 */
133 onChange: function (editor, combo, record, index) {
134 var className = combo.getValue();
135 this.editor.focus();
136 var blocks = this.getSelectedBlocks();
137 for (var k = 0; k < blocks.length; ++k) {
138 var parent = blocks[k];
139 while (parent && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") {
140 parent = parent.parentNode;
141 }
142 if (!k) {
143 var tagName = parent.tagName.toLowerCase();
144 }
145 if (parent.tagName.toLowerCase() == tagName) {
146 this.applyClassChange(parent, className);
147 }
148 }
149 },
150 /*
151 * This function applies the class change to the node
152 */
153 applyClassChange: function (node, className) {
154 if (className == "none") {
155 var classNames = node.className.trim().split(" ");
156 for (var i = classNames.length; --i >= 0;) {
157 if (!HTMLArea.reservedClassNames.test(classNames[i])) {
158 HTMLArea.DOM.removeClass(node, classNames[i]);
159 if (node.nodeName.toLowerCase() === "table" && this.getPluginInstance('TableOperations')) {
160 this.getPluginInstance('TableOperations').removeAlternatingClasses(node, classNames[i]);
161 this.getPluginInstance('TableOperations').removeCountingClasses(node, classNames[i]);
162 }
163 break;
164 }
165 }
166 } else {
167 var nodeName = node.nodeName.toLowerCase();
168 if (this.tags && this.tags[nodeName] && this.tags[nodeName].allowedClasses) {
169 if (this.tags[nodeName].allowedClasses.test(className)) {
170 HTMLArea.DOM.addClass(node, className);
171 }
172 } else if (this.tags && this.tags.all && this.tags.all.allowedClasses) {
173 if (this.tags.all.allowedClasses.test(className)) {
174 HTMLArea.DOM.addClass(node, className);
175 }
176 } else {
177 HTMLArea.DOM.addClass(node, className);
178 }
179 if (nodeName === "table" && this.getPluginInstance('TableOperations')) {
180 this.getPluginInstance('TableOperations').reStyleTable(node);
181 }
182 }
183 },
184 /*
185 * This function gets the list of selected blocks
186 */
187 getSelectedBlocks: function () {
188 var block, range, i = 0, blocks = [];
189 var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
190 if (Ext.isGecko) {
191 var selection = this.editor._getSelection();
192 try {
193 while ((range = selection.getRangeAt(i++))) {
194 block = this.editor.getParentElement(selection, range);
195 blocks.push(statusBarSelection ? statusBarSelection : block);
196 }
197 } catch(e) {
198 /* finished walking through selection */
199 }
200 } else {
201 blocks.push(statusBarSelection ? statusBarSelection : this.editor.getParentElement());
202 }
203 return blocks;
204 },
205 /*
206 * This handler gets called when the editor is generated
207 */
208 onGenerate: function () {
209 // Monitor editor changing mode
210 this.editor.iframe.mon(this.editor, 'HTMLAreaEventModeChange', this.onModeChange, this);
211 // Create CSS Parser object
212 this.blockStyles = new HTMLArea.CSS.Parser({
213 prefixLabelWithClassName: this.prefixLabelWithClassName,
214 postfixLabelWithClassName: this.postfixLabelWithClassName,
215 showTagFreeClasses: this.showTagFreeClasses,
216 tags: this.tags,
217 editor: this.editor
218 });
219 // Monitor css parsing being completed
220 this.editor.iframe.mon(this.blockStyles, 'HTMLAreaEventCssParsingComplete', this.onCssParsingComplete, this);
221 this.blockStyles.initiateParsing();
222 },
223 /*
224 * This handler gets called when parsing of css classes is completed
225 */
226 onCssParsingComplete: function () {
227 if (this.blockStyles.isReady) {
228 this.cssArray = this.blockStyles.getClasses();
229 }
230 if (this.getEditorMode() === 'wysiwyg' && this.editor.isEditable()) {
231 this.updateValue('BlockStyle');
232 }
233 },
234 /*
235 * This handler gets called when the toolbar is being updated
236 */
237 onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
238 if (mode === 'wysiwyg' && this.editor.isEditable()) {
239 this.updateValue(button.itemId);
240 }
241 },
242 /*
243 * This handler gets called when the editor has changed its mode to "wysiwyg"
244 */
245 onModeChange: function(mode) {
246 if (mode === 'wysiwyg' && this.editor.isEditable()) {
247 this.updateValue('BlockStyle');
248 }
249 },
250 /*
251 * This function updates the current value of the dropdown list
252 */
253 updateValue: function(dropDownId) {
254 var dropDown = this.getButton(dropDownId);
255 if (dropDown) {
256 var classNames = new Array();
257 var tagName = null;
258 var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
259 var parent = statusBarSelection ? statusBarSelection : this.editor.getParentElement();
260 while (parent && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") {
261 parent = parent.parentNode;
262 }
263 if (parent) {
264 tagName = parent.nodeName.toLowerCase();
265 classNames = HTMLArea.DOM.getClassNames(parent);
266 }
267 if (tagName && tagName !== "body"){
268 this.buildDropDownOptions(dropDown, tagName);
269 this.setSelectedOption(dropDown, classNames);
270 } else {
271 this.initializeDropDown(dropDown);
272 dropDown.setDisabled(true);
273 }
274 }
275 },
276 /*
277 * This function reinitializes the options of the dropdown
278 */
279 initializeDropDown: function (dropDown) {
280 var store = dropDown.getStore();
281 store.removeAll(false);
282 store.insert(0, new store.recordType({
283 text: this.localize('No style'),
284 value: 'none'
285 }));
286 dropDown.setValue('none');
287 },
288 /*
289 * This function builds the options to be displayed in the dropDown box
290 */
291 buildDropDownOptions: function (dropDown, nodeName) {
292 var store = dropDown.getStore();
293 this.initializeDropDown(dropDown);
294 if (this.blockStyles.isReady) {
295 var allowedClasses = {};
296 if (Ext.isDefined(this.cssArray[nodeName])) {
297 allowedClasses = this.cssArray[nodeName];
298 } else if (this.showTagFreeClasses && Ext.isDefined(this.cssArray['all'])) {
299 allowedClasses = this.cssArray['all'];
300 }
301 Ext.iterate(allowedClasses, function (cssClass, value) {
302 var style = null;
303 if (!this.editor.config.disablePCexamples) {
304 if (HTMLArea.classesValues[cssClass] && !HTMLArea.classesNoShow[cssClass]) {
305 style = HTMLArea.classesValues[cssClass];
306 } else if (/-[0-9]+$/.test(cssClass) && HTMLArea.classesValues[RegExp.leftContext + '-']) {
307 style = HTMLArea.classesValues[RegExp.leftContext + '-'];
308 }
309 }
310 store.add(new store.recordType({
311 text: value,
312 value: cssClass,
313 style: style
314 }));
315 }, this);
316 }
317 },
318 /*
319 * This function sets the selected option of the dropDown box
320 */
321 setSelectedOption: function (dropDown, classNames, noUnknown, defaultClass) {
322 var store = dropDown.getStore();
323 dropDown.setValue('none');
324 if (classNames.length) {
325 var index = store.findExact('value', classNames[classNames.length-1]);
326 if (index != -1) {
327 dropDown.setValue(classNames[classNames.length-1]);
328 if (!defaultClass) {
329 store.getAt(0).set('text', this.localize('Remove style'));
330 }
331 }
332 if (index == -1 && !noUnknown) {
333 store.add(new store.recordType({
334 text: this.localize('Unknown style'),
335 value: classNames[classNames.length-1]
336 }));
337 index = store.getCount()-1;
338 dropDown.setValue(classNames[classNames.length-1]);
339 if (!defaultClass) {
340 store.getAt(0).set('text', this.localize('Remove style'));
341 }
342 }
343 store.each(function (option) {
344 if (store.indexOf(option) != index && (',' + classNames.join(',') + ',').indexOf(',' + option.get('value') + ',') != -1) {
345 store.removeAt(store.indexOf(option));
346 }
347 return true;
348 });
349 }
350 dropDown.setDisabled(!(store.getCount()>1));
351 }
352 });