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