650d0e1f6014dfef9816a35f8d07987eb2233b45
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Resources / Public / JavaScript / Plugins / DefaultImage.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * Image Plugin for TYPO3 htmlArea RTE
16 */
17 define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
18 'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
19 'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
20 function (Plugin, UserAgent, Util) {
21
22 var DefaultImage = function (editor, pluginName) {
23 this.constructor.super.call(this, editor, pluginName);
24 };
25 Util.inherit(DefaultImage, Plugin);
26 Util.apply(DefaultImage.prototype, {
27
28 /**
29 * This function gets called by the class constructor
30 */
31 configurePlugin: function (editor) {
32 this.baseURL = this.editorConfiguration.baseURL;
33 this.pageTSConfiguration = this.editorConfiguration.buttons.image;
34 if (this.pageTSConfiguration && this.pageTSConfiguration.properties && this.pageTSConfiguration.properties.removeItems) {
35 this.removeItems = this.pageTSConfiguration.properties.removeItems.split(',');
36 var layout = 0;
37 var padding = 0;
38 for (var i = 0, n = this.removeItems.length; i < n; ++i) {
39 this.removeItems[i] = this.removeItems[i].replace(/(?:^\s+|\s+$)/g, '');
40 if (/^(align|border|float)$/i.test(this.removeItems[i])) {
41 ++layout;
42 }
43 if (/^(paddingTop|paddingRight|paddingBottom|paddingLeft)$/i.test(this.removeItems[i])) {
44 ++padding;
45 }
46 }
47 if (layout == 3) {
48 this.removeItems.push('layout');
49 }
50 if (layout == 4) {
51 this.removeItems.push('padding');
52 }
53 this.removeItems = new RegExp( '^(' + this.removeItems.join('|') + ')$', 'i');
54 } else {
55 this.removeItems = new RegExp( '^(none)$', 'i');
56 }
57 /*
58 * Registering plugin "About" information
59 */
60 var pluginInformation = {
61 version : '2.3',
62 developer : 'Stanislas Rolland',
63 developerUrl : 'http://www.sjbr.ca/',
64 copyrightOwner : 'Stanislas Rolland',
65 sponsor : 'SJBR',
66 sponsorUrl : 'http://www.sjbr.ca/',
67 license : 'GPL'
68 };
69 this.registerPluginInformation(pluginInformation);
70 /*
71 * Registering the button
72 */
73 var buttonId = 'InsertImage';
74 var buttonConfiguration = {
75 id : buttonId,
76 tooltip : this.localize('insertimage'),
77 action : 'onButtonPress',
78 hotKey : (this.pageTSConfiguration ? this.pageTSConfiguration.hotKey : null),
79 dialog : true,
80 iconCls : 'htmlarea-action-image-edit'
81 };
82 this.registerButton(buttonConfiguration);
83 return true;
84 },
85 /*
86 * Sets of default configuration values for dialogue form fields
87 */
88 configDefaults: {
89 combo: {
90 editable: true,
91 selectOnFocus: true,
92 typeAhead: true,
93 triggerAction: 'all',
94 forceSelection: true,
95 mode: 'local',
96 valueField: 'value',
97 displayField: 'text',
98 helpIcon: true,
99 tpl: '<tpl for="."><div ext:qtip="{value}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{text}</div></tpl>'
100 }
101 },
102 /*
103 * This function gets called when the button was pressed.
104 *
105 * @param object editor: the editor instance
106 * @param string id: the button id or the key
107 *
108 * @return boolean false if action is completed
109 */
110 onButtonPress: function(editor, id) {
111 // Could be a button or its hotkey
112 var buttonId = this.translateHotKey(id);
113 buttonId = buttonId ? buttonId : id;
114 this.image = this.editor.getSelection().getParentElement();
115 if (this.image && !/^img$/i.test(this.image.nodeName)) {
116 this.image = null;
117 }
118 if (this.image) {
119 this.parameters = {
120 base: this.baseURL,
121 url: this.image.getAttribute('src'),
122 alt: this.image.alt,
123 border: isNaN(parseInt(this.image.style.borderWidth)) ? '' : parseInt(this.image.style.borderWidth),
124 align: this.image.style.verticalAlign ? this.image.style.verticalAlign : '',
125 paddingTop: isNaN(parseInt(this.image.style.paddingTop)) ? '' : parseInt(this.image.style.paddingTop),
126 paddingRight: isNaN(parseInt(this.image.style.paddingRight)) ? '' : parseInt(this.image.style.paddingRight),
127 paddingBottom: isNaN(parseInt(this.image.style.paddingBottom)) ? '' : parseInt(this.image.style.paddingBottom),
128 paddingLeft: isNaN(parseInt(this.image.style.paddingLeft)) ? '' : parseInt(this.image.style.paddingLeft),
129 cssFloat: this.image.style.cssFloat
130 };
131 } else {
132 this.parameters = {
133 base: this.baseURL,
134 url: '',
135 alt: '',
136 border: '',
137 align: '',
138 paddingTop: '',
139 paddingRight: '',
140 paddingBottom: '',
141 paddingLeft: '',
142 cssFloat: ''
143 };
144 }
145 // Open dialogue window
146 this.openDialogue(
147 buttonId,
148 this.getButton(buttonId).tooltip,
149 this.getWindowDimensions(
150 {
151 width: 460,
152 height:300
153 },
154 buttonId
155 ),
156 this.buildTabItems()
157 );
158 return false;
159 },
160 /*
161 * Open the dialogue window
162 *
163 * @param string buttonId: the button id
164 * @param string title: the window title
165 * @param integer dimensions: the opening width of the window
166 * @param object tabItems: the configuration of the tabbed panel
167 *
168 * @return void
169 */
170 openDialogue: function (buttonId, title, dimensions, tabItems) {
171 this.dialog = new Ext.Window({
172 title: this.localize(title) || title,
173 cls: 'htmlarea-window',
174 border: false,
175 width: dimensions.width,
176 height: 'auto',
177 iconCls: this.getButton(buttonId).iconCls,
178 listeners: {
179 close: {
180 fn: this.onClose,
181 scope: this
182 }
183 },
184 items: {
185 xtype: 'tabpanel',
186 itemId: 'tabpanel',
187 activeTab: 0,
188 defaults: {
189 xtype: 'container',
190 layout: 'form',
191 defaults: {
192 labelWidth: 100
193 }
194 },
195 listeners: {
196 tabchange: {
197 fn: this.syncHeight,
198 scope: this
199 }
200 },
201 items: tabItems
202 },
203 buttons: [
204 this.buildButtonConfig('OK', this.onOK),
205 this.buildButtonConfig('Cancel', this.onCancel)
206 ]
207 });
208 this.show();
209 },
210 /*
211 * Build the configuration of the the tab items
212 *
213 * @return array the configuration array of tab items
214 */
215 buildTabItems: function () {
216 var tabItems = [];
217 // General tab
218 tabItems.push({
219 title: this.localize('General'),
220 items: [{
221 xtype: 'fieldset',
222 defaultType: 'textfield',
223 defaults: {
224 helpIcon: true,
225 width: 300,
226 labelSeparator: ''
227 },
228 items: [{
229 itemId: 'url',
230 fieldLabel: this.localize('Image URL:'),
231 value: this.parameters.url,
232 helpTitle: this.localize('Enter the image URL here')
233 },{
234 itemId: 'alt',
235 fieldLabel: this.localize('Alternate text:'),
236 value: this.parameters.alt,
237 helpTitle: this.localize('For browsers that dont support images')
238 }
239 ]
240 },{
241 xtype: 'fieldset',
242 title: this.localize('Image Preview'),
243 items: [{
244 // The preview iframe
245 xtype: 'box',
246 itemId: 'image-preview',
247 autoEl: {
248 name: 'ipreview',
249 tag: 'iframe',
250 cls: 'image-preview',
251 src: this.parameters.url
252 }
253 },{
254 xtype: 'button',
255 minWidth: 150,
256 text: this.localize('Preview'),
257 itemId: 'preview',
258 style: {
259 marginTop: '5px',
260 'float': 'right'
261 },
262 listeners: {
263 click: {
264 fn: this.onPreviewClick,
265 scope: this
266 }
267 }
268 }
269 ]
270 }
271 ]
272 });
273 // Layout tab
274 if (!this.removeItems.test('layout')) {
275 tabItems.push({
276 title: this.localize('Layout'),
277 items: [{
278 xtype: 'fieldset',
279 defaultType: 'textfield',
280 defaults: {
281 helpIcon: true,
282 width: 250,
283 labelSeparator: ''
284 },
285 items: [
286 Util.apply({
287 xtype: 'combo',
288 fieldLabel: this.localize('Image alignment:'),
289 itemId: 'align',
290 value: this.parameters.align,
291 helpTitle: this.localize('Positioning of this image'),
292 store: new Ext.data.ArrayStore({
293 autoDestroy: true,
294 fields: [ { name: 'text'}, { name: 'value'}],
295 data: [
296 [this.localize('Not set'), ''],
297 [this.localize('Bottom'), 'bottom'],
298 [this.localize('Middle'), 'middle'],
299 [this.localize('Top'), 'top']
300 ]
301 }),
302 hidden: this.removeItems.test('align'),
303 hideLabel: this.removeItems.test('align')
304 }, this.configDefaults['combo'])
305 ,{
306 itemId: 'border',
307 fieldLabel: this.localize('Border thickness:'),
308 width: 100,
309 value: this.parameters.border,
310 helpTitle: this.localize('Leave empty for no border'),
311 hidden: this.removeItems.test('border'),
312 hideLabel: this.removeItems.test('border')
313 },
314 Util.apply({
315 xtype: 'combo',
316 fieldLabel: this.localize('Float:'),
317 itemId: 'cssFloat',
318 value: this.parameters.cssFloat,
319 helpTitle: this.localize('Where the image should float'),
320 store: new Ext.data.ArrayStore({
321 autoDestroy: true,
322 fields: [ { name: 'text'}, { name: 'value'}],
323 data: [
324 [this.localize('Not set'), ''],
325 [this.localize('Non-floating'), 'none'],
326 [this.localize('Left'), 'left'],
327 [this.localize('Right'), 'right']
328 ]
329 }),
330 hidden: this.removeItems.test('float'),
331 hideLabel: this.removeItems.test('float')
332 }, this.configDefaults['combo'])
333 ]
334 }]
335 });
336 }
337 // Padding tab
338 if (!this.removeItems.test('padding')) {
339 tabItems.push({
340 title: this.localize('Spacing and padding'),
341 items: [{
342 xtype: 'fieldset',
343 defaultType: 'textfield',
344 defaults: {
345 helpIcon: true,
346 width: 100,
347 labelSeparator: ''
348 },
349 items: [{
350 itemId: 'paddingTop',
351 fieldLabel: this.localize('Top:'),
352 value: this.parameters.paddingTop,
353 helpTitle: this.localize('Top padding'),
354 hidden: this.removeItems.test('paddingTop'),
355 hideLabel: this.removeItems.test('paddingTop')
356 },{
357 itemId: 'paddingRight',
358 fieldLabel: this.localize('Right:'),
359 value: this.parameters.paddingRight,
360 helpTitle: this.localize('Right padding'),
361 hidden: this.removeItems.test('paddingRight'),
362 hideLabel: this.removeItems.test('paddingRight')
363 },{
364 itemId: 'paddingBottom',
365 fieldLabel: this.localize('Bottom:'),
366 value: this.parameters.paddingBottom,
367 helpTitle: this.localize('Bottom padding'),
368 hidden: this.removeItems.test('paddingBottom'),
369 hideLabel: this.removeItems.test('paddingBottom')
370 },{
371 itemId: 'paddingLeft',
372 fieldLabel: this.localize('Left:'),
373 value: this.parameters.paddingLeft,
374 helpTitle: this.localize('Left padding'),
375 hidden: this.removeItems.test('paddingLeft'),
376 hideLabel: this.removeItems.test('paddingLeft')
377 }
378 ]
379 }]
380 });
381 }
382 return tabItems;
383 },
384 /*
385 * Handler invoked when the Preview button is clicked
386 */
387 onPreviewClick: function () {
388 var tabPanel = this.dialog.find('itemId', 'tabpanel')[0];
389 var urlField = this.dialog.find('itemId', 'url')[0];
390 var url = urlField.getValue().trim();
391 if (url) {
392 try {
393 window.ipreview.location.replace(url);
394 } catch (e) {
395 TYPO3.Dialog.InformationDialog({
396 title: this.localize('Image Preview'),
397 msg: this.localize('image_url_invalid'),
398 fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
399 });
400 }
401 } else {
402 TYPO3.Dialog.InformationDialog({
403 title: this.localize('Image Preview'),
404 msg: this.localize('image_url_first'),
405 fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
406 });
407 }
408 return false;
409 },
410 /*
411 * Handler invoked when the OK button is clicked
412 */
413 onOK: function () {
414 var urlField = this.dialog.find('itemId', 'url')[0];
415 var url = urlField.getValue().trim();
416 if (url) {
417 var fieldNames = ['url', 'alt', 'align', 'border', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'cssFloat'], fieldName;
418 for (var i = fieldNames.length; --i >= 0;) {
419 fieldName = fieldNames[i];
420 var field = this.dialog.find('itemId', fieldName)[0];
421 if (field && !field.hidden) {
422 this.parameters[fieldName] = field.getValue();
423 }
424 }
425 this.insertImage();
426 this.close();
427 } else {
428 var tabPanel = this.dialog.find('itemId', 'tabpanel')[0];
429 TYPO3.Dialog.InformationDialog({
430 title: this.localize('image_url'),
431 msg: this.localize('image_url_required'),
432 fn: function () { tabPanel.setActiveTab(0); urlField.focus(); }
433 });
434 }
435 return false;
436 },
437 /*
438 * Insert the image
439 */
440 insertImage: function() {
441 this.restoreSelection();
442 var image = this.image;
443 if (!image) {
444 var range = this.editor.getSelection().createRange();
445 this.editor.getSelection().execCommand('InsertImage', false, this.parameters.url);
446 if (UserAgent.isWebKit) {
447 this.editor.getDomNode().cleanAppleStyleSpans(this.editor.document.body);
448 }
449 var range = this.editor.getSelection().createRange();
450 image = range.startContainer;
451 image = image.lastChild;
452 while (image && !/^img$/i.test(image.nodeName)) {
453 image = image.previousSibling;
454 }
455 } else {
456 image.src = this.parameters.url;
457 }
458 if (/^img$/i.test(image.nodeName)) {
459 var value;
460 for (var fieldName in this.parameters) {
461 value = this.parameters[fieldName];
462 switch (fieldName) {
463 case 'alt':
464 image.alt = value;
465 break;
466 case 'border':
467 if (parseInt(value)) {
468 image.style.borderWidth = parseInt(value) + 'px';
469 image.style.borderStyle = 'solid';
470 } else {
471 image.style.borderWidth = '';
472 image.style.borderStyle = 'none';
473 }
474 break;
475 case 'align':
476 image.style.verticalAlign = value;
477 break;
478 case 'paddingTop':
479 case 'paddingRight':
480 case 'paddingBottom':
481 case 'paddingLeft':
482 if (parseInt(value)) {
483 image.style[fieldName] = parseInt(value) + 'px';
484 } else {
485 image.style[fieldName] = '';
486 }
487 break;
488 case 'cssFloat':
489 image.style.cssFloat = value;
490 break;
491 }
492 }
493 }
494 },
495
496 /**
497 * This function gets called when the toolbar is updated
498 */
499 onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
500 button.setInactive(true);
501 if (mode === 'wysiwyg' && this.editor.isEditable() && button.itemId === 'InsertImage' && !button.disabled) {
502 var image = this.editor.getSelection().getParentElement();
503 if (image && !/^img$/i.test(image.nodeName)) {
504 image = null;
505 }
506 if (image) {
507 button.setTooltip(this.localize('Modify image'));
508 button.setInactive(false);
509 } else {
510 button.setTooltip(this.localize('Insert image'));
511 }
512 }
513 }
514 });
515
516 return DefaultImage;
517
518 });