55fc546b75d3a26b16b277a9cb2ea6e67575846d
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / htmlarea / plugins / RemoveFormat / remove-format.js
1 /***************************************************************
2 * Copyright notice
3 *
4 * (c) 2005-2010 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 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /*
28 * Remove Format Plugin for TYPO3 htmlArea RTE
29 *
30 * TYPO3 SVN ID: $Id$
31 */
32 HTMLArea.RemoveFormat = HTMLArea.Plugin.extend({
33 constructor: function(editor, pluginName) {
34 this.base(editor, pluginName);
35 },
36 /*
37 * This function gets called by the class constructor
38 */
39 configurePlugin: function(editor) {
40 /*
41 * Registering plugin "About" information
42 */
43 var pluginInformation = {
44 version : '2.1',
45 developer : 'Stanislas Rolland',
46 developerUrl : 'http://www.sjbr.ca/',
47 copyrightOwner : 'Stanislas Rolland',
48 sponsor : 'SJBR',
49 sponsorUrl : 'http://www.sjbr.ca/',
50 license : 'GPL'
51 };
52 this.registerPluginInformation(pluginInformation);
53 /*
54 * Registering the button
55 */
56 var buttonId = 'RemoveFormat';
57 var buttonConfiguration = {
58 id : buttonId,
59 tooltip : this.localize(buttonId + 'Tooltip'),
60 iconCls : 'htmlarea-action-remove-format',
61 action : 'onButtonPress',
62 dialog : true
63 };
64 this.registerButton(buttonConfiguration);
65 return true;
66 },
67 /*
68 * This function gets called when the button was pressed.
69 *
70 * @param object editor: the editor instance
71 * @param string id: the button id or the key
72 *
73 * @return boolean false if action is completed
74 */
75 onButtonPress: function (editor, id, target) {
76 // Could be a button or its hotkey
77 var buttonId = this.translateHotKey(id);
78 buttonId = buttonId ? buttonId : id;
79 // Open dialogue window
80 this.openDialogue(
81 buttonId,
82 'Remove formatting',
83 this.getWindowDimensions(
84 {
85 width: 260,
86 height:260
87 },
88 buttonId
89 )
90 );
91 return false;
92 },
93 /*
94 * Open the dialogue window
95 *
96 * @param string buttonId: the button id
97 * @param string title: the window title
98 * @param object dimensions: the opening dimensions of the window
99 *
100 * @return void
101 */
102 openDialogue: function (buttonId, title, dimensions) {
103 this.dialog = new Ext.Window({
104 title: this.localize(title),
105 cls: 'htmlarea-window',
106 border: false,
107 width: dimensions.width,
108 height: 'auto',
109 // As of ExtJS 3.1, JS error with IE when the window is resizable
110 resizable: !Ext.isIE,
111 iconCls: this.getButton(buttonId).iconCls,
112 listeners: {
113 close: {
114 fn: this.onClose,
115 scope: this
116 }
117 },
118 items: [{
119 xtype: 'fieldset',
120 title: this.localize('Cleaning Area'),
121 defaultType: 'radio',
122 labelWidth: 140,
123 defaults: {
124 labelSeparator: ''
125 },
126 items: [{
127 itemId: 'selection',
128 fieldLabel: this.localize('Selection'),
129 name: 'htmlarea-removeFormat-area'
130 },{
131 itemId: 'allContent',
132 fieldLabel: this.localize('All'),
133 checked: true,
134 name: 'htmlarea-removeFormat-area'
135 }
136 ]
137 },{
138 xtype: 'fieldset',
139 defaultType: 'checkbox',
140 title: this.localize('Cleaning options'),
141 labelWidth: 170,
142 defaults: {
143 labelSeparator: ''
144 },
145 items: [{
146 itemId: 'formatting',
147 fieldLabel: this.localize('Formatting:')
148 },{
149 itemId: 'msWordFormatting',
150 fieldLabel: this.localize('MS Word Formatting:'),
151 checked: true
152 },{
153 itemId: 'typographical',
154 fieldLabel: this.localize('Typographical punctuation:'),
155 },{
156 itemId: 'spaces',
157 fieldLabel: this.localize('Spaces')
158 },{
159 itemId: 'images',
160 fieldLabel: this.localize('Images:')
161 },{
162 itemId: 'allHtml',
163 fieldLabel: this.localize('All HTML:')
164 }
165 ]
166 }
167 ],
168 buttons: [
169 this.buildButtonConfig('OK', this.onOK),
170 this.buildButtonConfig('Cancel', this.onCancel)
171 ]
172 });
173 this.show();
174 },
175 /*
176 * Handler when the OK button is pressed
177 */
178 onOK: function () {
179 var fields = [
180 'selection',
181 'allContent',
182 'formatting',
183 'msWordFormatting',
184 'typographical',
185 'spaces',
186 'images',
187 'allHtml'
188 ];
189 var params = {};
190 Ext.each(fields, function (field) {
191 params[field] = this.dialog.find('itemId', field)[0].getValue();
192 }, this);
193 if (params['allHtml'] || params['formatting'] || params['spaces'] || params['images'] || params['msWordFormatting'] || params['typographical']) {
194 this.applyRequest(params);
195 this.close();
196 } else {
197 TYPO3.Dialog.InformationDialog({
198 title: this.getButton('RemoveFormat').tooltip.title,
199 msg: this.localize('Select the type of formatting you wish to remove.')
200 });
201 }
202 return false;
203 },
204 /*
205 * Perform the cleaning request
206 * @param object params: the values of the form fields
207 *
208 * @return void
209 */
210 applyRequest: function(params) {
211 var editor = this.editor;
212 editor.focus();
213 this.restoreSelection();
214 if (params['allContent']) {
215 var html = editor.getInnerHTML();
216 } else {
217 var html = editor.getSelectedHTML();
218 }
219 if (html) {
220 if (params['allHtml']) {
221 html = html.replace(/<[\!]*?[^<>]*?>/g, "");
222 }
223 if (params['formatting']) {
224 // Remove font, b, strong, i, em, u, strike, span and other inline tags
225 html = html.replace(/<\/?(abbr|acronym|b[^a-zA-Z]|big|cite|code|em[^a-zA-Z]|font|i[^a-zA-Z]|q|s[^a-zA-Z]|samp|small|span|strike|strong|sub|sup|tt|u[^a-zA-Z]|var)[^>]*>/gi, "");
226 // Keep tags, strip attributes
227 html = html.replace(/[ \t\n\r]+(style|class|align|cellpadding|cellspacing|frame|bgcolor)=\"[^>\"]*\"/gi, "");
228 }
229 if (params['spaces']) {
230 // Replace non-breaking spaces by normal spaces
231 html = html.replace(/&nbsp;/g, " ");
232 }
233 if (params['images']) {
234 // remove any IMG tag
235 html = html.replace(/<\/?img[^>]*>/gi, "");
236 }
237 if (params['msWordFormatting']) {
238 // Make one line
239 html = html.replace(/[ \r\n\t]+/g, " ");
240 // Clean up tags
241 html = html.replace(/<(b|strong|i|em|p|li|ul) [^>]*>/gi, "<$1>");
242 // Keep tags, strip attributes
243 html = html.replace(/ (style|class|align)=\"[^>\"]*\"/gi, "");
244 // kill unwanted tags: span, div, ?xml:, st1:, [a-z]:, meta, link
245 html = html.replace(/<\/?span[^>]*>/gi, "").
246 replace(/<\/?div[^>]*>/gi, "").
247 replace(/<\?xml:[^>]*>/gi, "").
248 replace(/<\/?st1:[^>]*>/gi, "").
249 replace(/<\/?[a-z]:[^>]*>/g, "").
250 replace(/<\/?meta[^>]*>/g, "").
251 replace(/<\/?link[^>]*>/g, "");
252 // remove unwanted tags and their contents: style, title
253 html = html.replace(/<style[^>]*>.*<\/style[^>]*>/gi, "").
254 replace(/<title[^>]*>.*<\/title[^>]*>/gi, "");
255 // remove comments
256 html = html.replace(/<!--[^>]*>/gi, "");
257 // Remove inline elements resets
258 html = html.replace(/<\/(b[^a-zA-Z]|big|i[^a-zA-Z]|s[^a-zA-Z]|small|strike|tt|u[^a-zA-Z])><\1>/gi, "");
259 // Remove double tags
260 var oldlen = html.length + 1;
261 while(oldlen > html.length) {
262 oldlen = html.length;
263 // Remove double opening tags
264 html = html.replace(/<([a-z][a-z]*)> *<\/\1>/gi, " ").replace(/<([a-z][a-z]*)> *<\/?([a-z][^>]*)> *<\/\1>/gi, "<$2>");
265 // Remove double closing tags
266 html = html.replace(/<([a-z][a-z]*)><\1>/gi, "<$1>").replace(/<\/([a-z][a-z]*)><\/\1>/gi, "<\/$1>");
267 // Remove multiple spaces
268 html = html.replace(/[\x20]+/gi, " ");
269 }
270 }
271 if (params['typographical']) {
272 // Remove typographical punctuation
273 // Search pattern stored here
274 var SrcCd;
275 // Replace horizontal ellipsis with three periods
276 SrcCd = String.fromCharCode(8230);
277 html = html.replace(new RegExp(SrcCd, 'g'), '...');
278 // Replace en-dash and em-dash with hyphen
279 SrcCd = String.fromCharCode(8211) + '|' + String.fromCharCode(8212);
280 html = html.replace(new RegExp(SrcCd, 'g'), '-');
281 html = html.replace(new RegExp(SrcCd, 'g'), "'");
282 // Replace double low-9 / left double / right double quotation mark with double quote
283 SrcCd = String.fromCharCode(8222) + '|' + String.fromCharCode(8220) + '|' + String.fromCharCode(8221);
284 html = html.replace(new RegExp(SrcCd, 'g'), '"');
285 // Replace left single / right single / single low-9 quotation mark with sigle quote
286 SrcCd = String.fromCharCode(8216) + '|' + String.fromCharCode(8217) + '|' + String.fromCharCode(8218);
287 html = html.replace(new RegExp(SrcCd, 'g'), "'");
288 // Replace single left/right-pointing angle quotation mark with single quote
289 SrcCd = String.fromCharCode(8249) + '|' + String.fromCharCode(8250);
290 html = html.replace(new RegExp(SrcCd, 'g'), "'");
291 // Replace left/right-pointing double angle quotation mark (left/right pointing guillemet) with double quote
292 SrcCd = String.fromCharCode(171) + '|' + String.fromCharCode(187);
293 html = html.replace(new RegExp(SrcCd, 'g'), '"');
294 // Replace grave accent (spacing grave) and acute accent (spacing acute) with apostrophe (single quote)
295 SrcCd = String.fromCharCode(96) + '|' + String.fromCharCode(180);
296 }
297 if (params['allContent']) {
298 editor.setHTML(html);
299 } else {
300 editor.insertHTML(html);
301 }
302 }
303 }
304 });