source: main/trunk/greenstone3/web/interfaces/default/js/ckeditor/plugins/image/dialogs/image.js@ 32438

Last change on this file since 32438 was 32438, checked in by kjdon, 6 years ago

CKEditor: added Image and SourceDialog plugins to allow image tag editing, and source code editing. config.js modified to include the plugins

File size: 43.5 KB
Line 
1/**
2 * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5
6( function() {
7 var imageDialog = function( editor, dialogType ) {
8 // Load image preview.
9 var IMAGE = 1,
10 LINK = 2,
11 PREVIEW = 4,
12 CLEANUP = 8,
13 regexGetSize = /^\s*(\d+)((px)|\%)?\s*$/i,
14 regexGetSizeOrEmpty = /(^\s*(\d+)((px)|\%)?\s*$)|^$/i,
15 pxLengthRegex = /^\d+px$/;
16
17 var onSizeChange = function() {
18 var value = this.getValue(),
19 // This = input element.
20 dialog = this.getDialog(),
21 aMatch = value.match( regexGetSize ); // Check value
22 if ( aMatch ) {
23 if ( aMatch[ 2 ] == '%' ) // % is allowed - > unlock ratio.
24 switchLockRatio( dialog, false ); // Unlock.
25 value = aMatch[ 1 ];
26 }
27
28 // Only if ratio is locked
29 if ( dialog.lockRatio ) {
30 var oImageOriginal = dialog.originalElement;
31 if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) {
32 if ( this.id == 'txtHeight' ) {
33 if ( value && value != '0' )
34 value = Math.round( oImageOriginal.$.width * ( value / oImageOriginal.$.height ) );
35 if ( !isNaN( value ) )
36 dialog.setValueOf( 'info', 'txtWidth', value );
37 }
38 // this.id = txtWidth.
39 else {
40 if ( value && value != '0' )
41 value = Math.round( oImageOriginal.$.height * ( value / oImageOriginal.$.width ) );
42 if ( !isNaN( value ) )
43 dialog.setValueOf( 'info', 'txtHeight', value );
44 }
45 }
46 }
47 updatePreview( dialog );
48 };
49
50 var updatePreview = function( dialog ) {
51 //Don't load before onShow.
52 if ( !dialog.originalElement || !dialog.preview )
53 return 1;
54
55 // Read attributes and update imagePreview;
56 dialog.commitContent( PREVIEW, dialog.preview );
57 return 0;
58 };
59
60 // Custom commit dialog logic, where we're intended to give inline style
61 // field (txtdlgGenStyle) higher priority to avoid overwriting styles contribute
62 // by other fields.
63 function commitContent() {
64 var args = arguments;
65 var inlineStyleField = this.getContentElement( 'advanced', 'txtdlgGenStyle' );
66 inlineStyleField && inlineStyleField.commit.apply( inlineStyleField, args );
67
68 this.foreach( function( widget ) {
69 if ( widget.commit && widget.id != 'txtdlgGenStyle' )
70 widget.commit.apply( widget, args );
71 } );
72 }
73
74 // Avoid recursions.
75 var incommit;
76
77 // Synchronous field values to other impacted fields is required, e.g. border
78 // size change should alter inline-style text as well.
79 function commitInternally( targetFields ) {
80 if ( incommit )
81 return;
82
83 incommit = 1;
84
85 var dialog = this.getDialog(),
86 element = dialog.imageElement;
87 if ( element ) {
88 // Commit this field and broadcast to target fields.
89 this.commit( IMAGE, element );
90
91 targetFields = [].concat( targetFields );
92 var length = targetFields.length,
93 field;
94 for ( var i = 0; i < length; i++ ) {
95 field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );
96 // May cause recursion.
97 field && field.setup( IMAGE, element );
98 }
99 }
100
101 incommit = 0;
102 }
103
104 var switchLockRatio = function( dialog, value ) {
105 if ( !dialog.getContentElement( 'info', 'ratioLock' ) )
106 return null;
107
108 var oImageOriginal = dialog.originalElement;
109
110 // Dialog may already closed. (https://dev.ckeditor.com/ticket/5505)
111 if ( !oImageOriginal )
112 return null;
113
114 // Check image ratio and original image ratio, but respecting user's preference.
115 if ( value == 'check' ) {
116 if ( !dialog.userlockRatio && oImageOriginal.getCustomData( 'isReady' ) == 'true' ) {
117 var width = dialog.getValueOf( 'info', 'txtWidth' ),
118 height = dialog.getValueOf( 'info', 'txtHeight' ),
119 originalRatio = oImageOriginal.$.width * 1000 / oImageOriginal.$.height,
120 thisRatio = width * 1000 / height;
121 dialog.lockRatio = false; // Default: unlock ratio
122
123 if ( !width && !height )
124 dialog.lockRatio = true;
125 else if ( !isNaN( originalRatio ) && !isNaN( thisRatio ) ) {
126 if ( Math.round( originalRatio ) == Math.round( thisRatio ) )
127 dialog.lockRatio = true;
128 }
129 }
130 } else if ( value !== undefined )
131 dialog.lockRatio = value;
132 else {
133 dialog.userlockRatio = 1;
134 dialog.lockRatio = !dialog.lockRatio;
135 }
136
137 var ratioButton = CKEDITOR.document.getById( btnLockSizesId );
138 if ( dialog.lockRatio )
139 ratioButton.removeClass( 'cke_btn_unlocked' );
140 else
141 ratioButton.addClass( 'cke_btn_unlocked' );
142
143 ratioButton.setAttribute( 'aria-checked', dialog.lockRatio );
144
145 // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE
146 if ( CKEDITOR.env.hc ) {
147 var icon = ratioButton.getChild( 0 );
148 icon.setHtml( dialog.lockRatio ? CKEDITOR.env.ie ? '\u25A0' : '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' );
149 }
150
151 return dialog.lockRatio;
152 };
153
154 var resetSize = function( dialog, emptyValues ) {
155 var oImageOriginal = dialog.originalElement,
156 ready = oImageOriginal.getCustomData( 'isReady' ) == 'true';
157
158 if ( ready ) {
159 var widthField = dialog.getContentElement( 'info', 'txtWidth' ),
160 heightField = dialog.getContentElement( 'info', 'txtHeight' ),
161 widthValue, heightValue;
162
163 if ( emptyValues ) {
164 widthValue = 0;
165 heightValue = 0;
166 } else {
167 widthValue = oImageOriginal.$.width;
168 heightValue = oImageOriginal.$.height;
169 }
170
171 widthField && widthField.setValue( widthValue );
172 heightField && heightField.setValue( heightValue );
173 }
174 updatePreview( dialog );
175 };
176
177 var setupDimension = function( type, element ) {
178 if ( type != IMAGE )
179 return;
180
181 function checkDimension( size, defaultValue ) {
182 var aMatch = size.match( regexGetSize );
183 if ( aMatch ) {
184 // % is allowed.
185 if ( aMatch[ 2 ] == '%' ) {
186 aMatch[ 1 ] += '%';
187 switchLockRatio( dialog, false ); // Unlock ratio
188 }
189 return aMatch[ 1 ];
190 }
191 return defaultValue;
192 }
193
194 var dialog = this.getDialog(),
195 value = '',
196 dimension = this.id == 'txtWidth' ? 'width' : 'height',
197 size = element.getAttribute( dimension );
198
199 if ( size )
200 value = checkDimension( size, value );
201 value = checkDimension( element.getStyle( dimension ), value );
202
203 this.setValue( value );
204 };
205
206 var previewPreloader;
207
208 var onImgLoadEvent = function() {
209 // Image is ready.
210 var original = this.originalElement,
211 loader = CKEDITOR.document.getById( imagePreviewLoaderId );
212
213 original.setCustomData( 'isReady', 'true' );
214 original.removeListener( 'load', onImgLoadEvent );
215 original.removeListener( 'error', onImgLoadErrorEvent );
216 original.removeListener( 'abort', onImgLoadErrorEvent );
217
218 // Hide loader.
219 if ( loader )
220 loader.setStyle( 'display', 'none' );
221
222 // New image -> new dimensions
223 if ( !this.dontResetSize ) {
224 resetSize( this, editor.config.image_prefillDimensions === false );
225 }
226
227 if ( this.firstLoad ) {
228 CKEDITOR.tools.setTimeout( function() {
229 switchLockRatio( this, 'check' );
230 }, 0, this );
231 }
232
233 this.firstLoad = false;
234 this.dontResetSize = false;
235
236 // Possible fix for https://dev.ckeditor.com/ticket/12818.
237 updatePreview( this );
238 };
239
240 var onImgLoadErrorEvent = function() {
241 // Error. Image is not loaded.
242 var original = this.originalElement,
243 loader = CKEDITOR.document.getById( imagePreviewLoaderId );
244
245 original.removeListener( 'load', onImgLoadEvent );
246 original.removeListener( 'error', onImgLoadErrorEvent );
247 original.removeListener( 'abort', onImgLoadErrorEvent );
248
249 // Set Error image.
250 var noimage = CKEDITOR.getUrl( CKEDITOR.plugins.get( 'image' ).path + 'images/noimage.png' );
251
252 if ( this.preview )
253 this.preview.setAttribute( 'src', noimage );
254
255 // Hide loader.
256 if ( loader )
257 loader.setStyle( 'display', 'none' );
258
259 switchLockRatio( this, false ); // Unlock.
260 };
261
262 var numbering = function( id ) {
263 return CKEDITOR.tools.getNextId() + '_' + id;
264 },
265 btnLockSizesId = numbering( 'btnLockSizes' ),
266 btnResetSizeId = numbering( 'btnResetSize' ),
267 imagePreviewLoaderId = numbering( 'ImagePreviewLoader' ),
268 previewLinkId = numbering( 'previewLink' ),
269 previewImageId = numbering( 'previewImage' );
270
271 return {
272 title: editor.lang.image[ dialogType == 'image' ? 'title' : 'titleButton' ],
273 minWidth: ( CKEDITOR.skinName || editor.config.skin ) == 'moono-lisa' ? 500 : 420,
274 minHeight: 360,
275 onShow: function() {
276 this.imageElement = false;
277 this.linkElement = false;
278
279 // Default: create a new element.
280 this.imageEditMode = false;
281 this.linkEditMode = false;
282
283 this.lockRatio = true;
284 this.userlockRatio = 0;
285 this.dontResetSize = false;
286 this.firstLoad = true;
287 this.addLink = false;
288
289 var editor = this.getParentEditor(),
290 sel = editor.getSelection(),
291 element = sel && sel.getSelectedElement(),
292 link = element && editor.elementPath( element ).contains( 'a', 1 ),
293 loader = CKEDITOR.document.getById( imagePreviewLoaderId );
294
295 // Hide loader.
296 if ( loader )
297 loader.setStyle( 'display', 'none' );
298
299 // Create the preview before setup the dialog contents.
300 previewPreloader = new CKEDITOR.dom.element( 'img', editor.document );
301 this.preview = CKEDITOR.document.getById( previewImageId );
302
303 // Copy of the image
304 this.originalElement = editor.document.createElement( 'img' );
305 this.originalElement.setAttribute( 'alt', '' );
306 this.originalElement.setCustomData( 'isReady', 'false' );
307
308 if ( link ) {
309 this.linkElement = link;
310 this.linkEditMode = true;
311
312 // If there is an existing link, by default keep it (true).
313 // It will be removed if certain conditions are met and Link tab is enabled. (https://dev.ckeditor.com/ticket/13351)
314 this.addLink = true;
315
316 // Look for Image element.
317 var linkChildren = link.getChildren();
318 if ( linkChildren.count() == 1 ) {
319 var childTag = linkChildren.getItem( 0 );
320
321 if ( childTag.type == CKEDITOR.NODE_ELEMENT ) {
322 if ( childTag.is( 'img' ) || childTag.is( 'input' ) ) {
323 this.imageElement = linkChildren.getItem( 0 );
324 if ( this.imageElement.is( 'img' ) )
325 this.imageEditMode = 'img';
326 else if ( this.imageElement.is( 'input' ) )
327 this.imageEditMode = 'input';
328 }
329 }
330 }
331 // Fill out all fields.
332 if ( dialogType == 'image' )
333 this.setupContent( LINK, link );
334 }
335
336 // Edit given image element instead the one from selection.
337 if ( this.customImageElement ) {
338 this.imageEditMode = 'img';
339 this.imageElement = this.customImageElement;
340 delete this.customImageElement;
341 }
342 else if ( element && element.getName() == 'img' && !element.data( 'cke-realelement' ) ||
343 element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'image' ) {
344 this.imageEditMode = element.getName();
345 this.imageElement = element;
346 }
347
348 if ( this.imageEditMode ) {
349 // Use the original element as a buffer from since we don't want
350 // temporary changes to be committed, e.g. if the dialog is canceled.
351 this.cleanImageElement = this.imageElement;
352 this.imageElement = this.cleanImageElement.clone( true, true );
353
354 // Fill out all fields.
355 this.setupContent( IMAGE, this.imageElement );
356 }
357
358 // Refresh LockRatio button
359 switchLockRatio( this, true );
360
361 // Dont show preview if no URL given.
362 if ( !CKEDITOR.tools.trim( this.getValueOf( 'info', 'txtUrl' ) ) ) {
363 this.preview.removeAttribute( 'src' );
364 this.preview.setStyle( 'display', 'none' );
365 }
366 },
367 onOk: function() {
368 // Edit existing Image.
369 if ( this.imageEditMode ) {
370 var imgTagName = this.imageEditMode;
371
372 // Image dialog and Input element.
373 if ( dialogType == 'image' && imgTagName == 'input' && confirm( editor.lang.image.button2Img ) ) { // jshint ignore:line
374 // Replace INPUT-> IMG
375 imgTagName = 'img';
376 this.imageElement = editor.document.createElement( 'img' );
377 this.imageElement.setAttribute( 'alt', '' );
378 editor.insertElement( this.imageElement );
379 }
380 // ImageButton dialog and Image element.
381 else if ( dialogType != 'image' && imgTagName == 'img' && confirm( editor.lang.image.img2Button ) ) { // jshint ignore:line
382 // Replace IMG -> INPUT
383 imgTagName = 'input';
384 this.imageElement = editor.document.createElement( 'input' );
385 this.imageElement.setAttributes( {
386 type: 'image',
387 alt: ''
388 } );
389 editor.insertElement( this.imageElement );
390 } else {
391 // Restore the original element before all commits.
392 this.imageElement = this.cleanImageElement;
393 delete this.cleanImageElement;
394 }
395 }
396 // Create a new image.
397 else {
398 // Image dialog -> create IMG element.
399 if ( dialogType == 'image' )
400 this.imageElement = editor.document.createElement( 'img' );
401 else {
402 this.imageElement = editor.document.createElement( 'input' );
403 this.imageElement.setAttribute( 'type', 'image' );
404 }
405 this.imageElement.setAttribute( 'alt', '' );
406 }
407
408 // Create a new link.
409 if ( !this.linkEditMode )
410 this.linkElement = editor.document.createElement( 'a' );
411
412 // Set attributes.
413 this.commitContent( IMAGE, this.imageElement );
414 this.commitContent( LINK, this.linkElement );
415
416 // Remove empty style attribute.
417 if ( !this.imageElement.getAttribute( 'style' ) )
418 this.imageElement.removeAttribute( 'style' );
419
420 // Insert a new Image.
421 if ( !this.imageEditMode ) {
422 if ( this.addLink ) {
423 if ( !this.linkEditMode ) {
424 // Insert a new link.
425 editor.insertElement( this.linkElement );
426 this.linkElement.append( this.imageElement, false );
427 } else {
428 // We already have a link in editor.
429 if ( this.linkElement.equals( editor.getSelection().getSelectedElement() ) ) {
430 // If the link is selected outside, replace it's content rather than the link itself. ([<a>foo</a>])
431 this.linkElement.setHtml( '' );
432 this.linkElement.append( this.imageElement, false );
433 } else {
434 // Only inside of the link is selected, so replace it with image. (<a>[foo]</a>, <a>[f]oo</a>)
435 editor.insertElement( this.imageElement );
436 }
437 }
438 } else {
439 editor.insertElement( this.imageElement );
440 }
441 }
442 // Image already exists.
443 else {
444 // Add a new link element.
445 if ( !this.linkEditMode && this.addLink ) {
446 editor.insertElement( this.linkElement );
447 this.imageElement.appendTo( this.linkElement );
448 }
449 // Remove Link, Image exists.
450 else if ( this.linkEditMode && !this.addLink ) {
451 editor.getSelection().selectElement( this.linkElement );
452 editor.insertElement( this.imageElement );
453 }
454 }
455 },
456 onLoad: function() {
457 if ( dialogType != 'image' )
458 this.hidePage( 'Link' ); //Hide Link tab.
459 var doc = this._.element.getDocument();
460
461 if ( this.getContentElement( 'info', 'ratioLock' ) ) {
462 this.addFocusable( doc.getById( btnResetSizeId ), 5 );
463 this.addFocusable( doc.getById( btnLockSizesId ), 5 );
464 }
465
466 this.commitContent = commitContent;
467 },
468 onHide: function() {
469 if ( this.preview )
470 this.commitContent( CLEANUP, this.preview );
471
472 if ( this.originalElement ) {
473 this.originalElement.removeListener( 'load', onImgLoadEvent );
474 this.originalElement.removeListener( 'error', onImgLoadErrorEvent );
475 this.originalElement.removeListener( 'abort', onImgLoadErrorEvent );
476 this.originalElement.remove();
477 this.originalElement = false; // Dialog is closed.
478 }
479
480 delete this.imageElement;
481 },
482 contents: [ {
483 id: 'info',
484 label: editor.lang.image.infoTab,
485 accessKey: 'I',
486 elements: [ {
487 type: 'vbox',
488 padding: 0,
489 children: [ {
490 type: 'hbox',
491 widths: [ '280px', '110px' ],
492 align: 'right',
493 className: 'cke_dialog_image_url',
494 children: [ {
495 id: 'txtUrl',
496 type: 'text',
497 label: editor.lang.common.url,
498 required: true,
499 onChange: function() {
500 var dialog = this.getDialog(),
501 newUrl = this.getValue();
502
503 // Update original image.
504 // Prevent from load before onShow.
505 if ( newUrl.length > 0 ) {
506 dialog = this.getDialog();
507 var original = dialog.originalElement;
508
509 if ( dialog.preview ) {
510 dialog.preview.removeStyle( 'display' );
511 }
512
513 original.setCustomData( 'isReady', 'false' );
514 // Show loader.
515 var loader = CKEDITOR.document.getById( imagePreviewLoaderId );
516 if ( loader )
517 loader.setStyle( 'display', '' );
518
519 original.on( 'load', onImgLoadEvent, dialog );
520 original.on( 'error', onImgLoadErrorEvent, dialog );
521 original.on( 'abort', onImgLoadErrorEvent, dialog );
522 original.setAttribute( 'src', newUrl );
523
524 if ( dialog.preview ) {
525 // Query the preloader to figure out the url impacted by based href.
526 previewPreloader.setAttribute( 'src', newUrl );
527 dialog.preview.setAttribute( 'src', previewPreloader.$.src );
528 updatePreview( dialog );
529 }
530 }
531 // Dont show preview if no URL given.
532 else if ( dialog.preview ) {
533 dialog.preview.removeAttribute( 'src' );
534 dialog.preview.setStyle( 'display', 'none' );
535 }
536 },
537 setup: function( type, element ) {
538 if ( type == IMAGE ) {
539 var url = element.data( 'cke-saved-src' ) || element.getAttribute( 'src' );
540 var field = this;
541
542 this.getDialog().dontResetSize = true;
543
544 field.setValue( url ); // And call this.onChange()
545 // Manually set the initial value.(https://dev.ckeditor.com/ticket/4191)
546 field.setInitValue();
547 }
548 },
549 commit: function( type, element ) {
550 if ( type == IMAGE && ( this.getValue() || this.isChanged() ) ) {
551 element.data( 'cke-saved-src', this.getValue() );
552 element.setAttribute( 'src', this.getValue() );
553 } else if ( type == CLEANUP ) {
554 element.setAttribute( 'src', '' ); // If removeAttribute doesn't work.
555 element.removeAttribute( 'src' );
556 }
557 },
558 validate: CKEDITOR.dialog.validate.notEmpty( editor.lang.image.urlMissing )
559 },
560 {
561 type: 'button',
562 id: 'browse',
563 // v-align with the 'txtUrl' field.
564 // TODO: We need something better than a fixed size here.
565 style: 'display:inline-block;margin-top:14px;',
566 align: 'center',
567 label: editor.lang.common.browseServer,
568 hidden: true,
569 filebrowser: 'info:txtUrl'
570 } ]
571 } ]
572 },
573 {
574 id: 'txtAlt',
575 type: 'text',
576 label: editor.lang.image.alt,
577 accessKey: 'T',
578 'default': '',
579 onChange: function() {
580 updatePreview( this.getDialog() );
581 },
582 setup: function( type, element ) {
583 if ( type == IMAGE )
584 this.setValue( element.getAttribute( 'alt' ) );
585 },
586 commit: function( type, element ) {
587 if ( type == IMAGE ) {
588 if ( this.getValue() || this.isChanged() )
589 element.setAttribute( 'alt', this.getValue() );
590 } else if ( type == PREVIEW )
591 element.setAttribute( 'alt', this.getValue() );
592 else if ( type == CLEANUP ) {
593 element.removeAttribute( 'alt' );
594 }
595
596 }
597 },
598 {
599 type: 'hbox',
600 children: [ {
601 id: 'basic',
602 type: 'vbox',
603 children: [ {
604 type: 'hbox',
605 requiredContent: 'img{width,height}',
606 widths: [ '50%', '50%' ],
607 children: [ {
608 type: 'vbox',
609 padding: 1,
610 children: [ {
611 type: 'text',
612 width: '45px',
613 id: 'txtWidth',
614 label: editor.lang.common.width,
615 onKeyUp: onSizeChange,
616 onChange: function() {
617 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
618 },
619 validate: function() {
620 var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
621 isValid = !!( aMatch && parseInt( aMatch[ 1 ], 10 ) !== 0 );
622 if ( !isValid )
623 alert( editor.lang.common.invalidLength.replace( '%1', editor.lang.common.width ).replace( '%2', 'px, %' ) ); // jshint ignore:line
624 return isValid;
625 },
626 setup: setupDimension,
627 commit: function( type, element ) {
628 var value = this.getValue();
629 if ( type == IMAGE ) {
630 if ( value && editor.activeFilter.check( 'img{width,height}' ) )
631 element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
632 else
633 element.removeStyle( 'width' );
634
635 element.removeAttribute( 'width' );
636 } else if ( type == PREVIEW ) {
637 var aMatch = value.match( regexGetSize );
638 if ( !aMatch ) {
639 var oImageOriginal = this.getDialog().originalElement;
640 if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
641 element.setStyle( 'width', oImageOriginal.$.width + 'px' );
642 } else {
643 element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
644 }
645 } else if ( type == CLEANUP ) {
646 element.removeAttribute( 'width' );
647 element.removeStyle( 'width' );
648 }
649 }
650 },
651 {
652 type: 'text',
653 id: 'txtHeight',
654 width: '45px',
655 label: editor.lang.common.height,
656 onKeyUp: onSizeChange,
657 onChange: function() {
658 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
659 },
660 validate: function() {
661 var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
662 isValid = !!( aMatch && parseInt( aMatch[ 1 ], 10 ) !== 0 );
663 if ( !isValid )
664 alert( editor.lang.common.invalidLength.replace( '%1', editor.lang.common.height ).replace( '%2', 'px, %' ) ); // jshint ignore:line
665 return isValid;
666 },
667 setup: setupDimension,
668 commit: function( type, element ) {
669 var value = this.getValue();
670 if ( type == IMAGE ) {
671 if ( value && editor.activeFilter.check( 'img{width,height}' ) )
672 element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
673 else
674 element.removeStyle( 'height' );
675
676 element.removeAttribute( 'height' );
677 } else if ( type == PREVIEW ) {
678 var aMatch = value.match( regexGetSize );
679 if ( !aMatch ) {
680 var oImageOriginal = this.getDialog().originalElement;
681 if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
682 element.setStyle( 'height', oImageOriginal.$.height + 'px' );
683 } else {
684 element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
685 }
686 } else if ( type == CLEANUP ) {
687 element.removeAttribute( 'height' );
688 element.removeStyle( 'height' );
689 }
690 }
691 } ]
692 },
693 {
694 id: 'ratioLock',
695 type: 'html',
696 className: 'cke_dialog_image_ratiolock',
697 style: 'margin-top:30px;width:40px;height:40px;',
698 onLoad: function() {
699 // Activate Reset button
700 var resetButton = CKEDITOR.document.getById( btnResetSizeId ),
701 ratioButton = CKEDITOR.document.getById( btnLockSizesId );
702 if ( resetButton ) {
703 resetButton.on( 'click', function( evt ) {
704 resetSize( this );
705 evt.data && evt.data.preventDefault();
706 }, this.getDialog() );
707 resetButton.on( 'mouseover', function() {
708 this.addClass( 'cke_btn_over' );
709 }, resetButton );
710 resetButton.on( 'mouseout', function() {
711 this.removeClass( 'cke_btn_over' );
712 }, resetButton );
713 }
714 // Activate (Un)LockRatio button
715 if ( ratioButton ) {
716 ratioButton.on( 'click', function( evt ) {
717 switchLockRatio( this );
718
719 var oImageOriginal = this.originalElement,
720 width = this.getValueOf( 'info', 'txtWidth' );
721
722 if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' && width ) {
723 var height = oImageOriginal.$.height / oImageOriginal.$.width * width;
724 if ( !isNaN( height ) ) {
725 this.setValueOf( 'info', 'txtHeight', Math.round( height ) );
726 updatePreview( this );
727 }
728 }
729 evt.data && evt.data.preventDefault();
730 }, this.getDialog() );
731 ratioButton.on( 'mouseover', function() {
732 this.addClass( 'cke_btn_over' );
733 }, ratioButton );
734 ratioButton.on( 'mouseout', function() {
735 this.removeClass( 'cke_btn_over' );
736 }, ratioButton );
737 }
738 },
739 html: '<div>' +
740 '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.lockRatio +
741 '" class="cke_btn_locked" id="' + btnLockSizesId + '" role="checkbox"><span class="cke_icon"></span><span class="cke_label">' + editor.lang.image.lockRatio + '</span></a>' +
742 '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.resetSize +
743 '" class="cke_btn_reset" id="' + btnResetSizeId + '" role="button"><span class="cke_label">' + editor.lang.image.resetSize + '</span></a>' +
744 '</div>'
745 } ]
746 },
747 {
748 type: 'vbox',
749 padding: 1,
750 children: [ {
751 type: 'text',
752 id: 'txtBorder',
753 requiredContent: 'img{border-width}',
754 width: '60px',
755 label: editor.lang.image.border,
756 'default': '',
757 onKeyUp: function() {
758 updatePreview( this.getDialog() );
759 },
760 onChange: function() {
761 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
762 },
763 validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateBorder ),
764 setup: function( type, element ) {
765 if ( type == IMAGE ) {
766 var value,
767 borderStyle = element.getStyle( 'border-width' );
768 borderStyle = borderStyle && borderStyle.match( /^(\d+px)(?: \1 \1 \1)?$/ );
769 value = borderStyle && parseInt( borderStyle[ 1 ], 10 );
770 isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'border' ) );
771 this.setValue( value );
772 }
773 },
774 commit: function( type, element ) {
775 var value = parseInt( this.getValue(), 10 );
776 if ( type == IMAGE || type == PREVIEW ) {
777 if ( !isNaN( value ) ) {
778 element.setStyle( 'border-width', CKEDITOR.tools.cssLength( value ) );
779 element.setStyle( 'border-style', 'solid' );
780 } else if ( !value && this.isChanged() ) {
781 element.removeStyle( 'border' );
782 }
783
784 if ( type == IMAGE )
785 element.removeAttribute( 'border' );
786 } else if ( type == CLEANUP ) {
787 element.removeAttribute( 'border' );
788 element.removeStyle( 'border-width' );
789 element.removeStyle( 'border-style' );
790 element.removeStyle( 'border-color' );
791 }
792 }
793 },
794 {
795 type: 'text',
796 id: 'txtHSpace',
797 requiredContent: 'img{margin-left,margin-right}',
798 width: '60px',
799 label: editor.lang.image.hSpace,
800 'default': '',
801 onKeyUp: function() {
802 updatePreview( this.getDialog() );
803 },
804 onChange: function() {
805 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
806 },
807 validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateHSpace ),
808 setup: function( type, element ) {
809 if ( type == IMAGE ) {
810 var value, marginLeftPx, marginRightPx,
811 marginLeftStyle = element.getStyle( 'margin-left' ),
812 marginRightStyle = element.getStyle( 'margin-right' );
813
814 marginLeftStyle = marginLeftStyle && marginLeftStyle.match( pxLengthRegex );
815 marginRightStyle = marginRightStyle && marginRightStyle.match( pxLengthRegex );
816 marginLeftPx = parseInt( marginLeftStyle, 10 );
817 marginRightPx = parseInt( marginRightStyle, 10 );
818
819 value = ( marginLeftPx == marginRightPx ) && marginLeftPx;
820 isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'hspace' ) );
821
822 this.setValue( value );
823 }
824 },
825 commit: function( type, element ) {
826 var value = parseInt( this.getValue(), 10 );
827 if ( type == IMAGE || type == PREVIEW ) {
828 if ( !isNaN( value ) ) {
829 element.setStyle( 'margin-left', CKEDITOR.tools.cssLength( value ) );
830 element.setStyle( 'margin-right', CKEDITOR.tools.cssLength( value ) );
831 } else if ( !value && this.isChanged() ) {
832 element.removeStyle( 'margin-left' );
833 element.removeStyle( 'margin-right' );
834 }
835
836 if ( type == IMAGE )
837 element.removeAttribute( 'hspace' );
838 } else if ( type == CLEANUP ) {
839 element.removeAttribute( 'hspace' );
840 element.removeStyle( 'margin-left' );
841 element.removeStyle( 'margin-right' );
842 }
843 }
844 },
845 {
846 type: 'text',
847 id: 'txtVSpace',
848 requiredContent: 'img{margin-top,margin-bottom}',
849 width: '60px',
850 label: editor.lang.image.vSpace,
851 'default': '',
852 onKeyUp: function() {
853 updatePreview( this.getDialog() );
854 },
855 onChange: function() {
856 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
857 },
858 validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateVSpace ),
859 setup: function( type, element ) {
860 if ( type == IMAGE ) {
861 var value, marginTopPx, marginBottomPx,
862 marginTopStyle = element.getStyle( 'margin-top' ),
863 marginBottomStyle = element.getStyle( 'margin-bottom' );
864
865 marginTopStyle = marginTopStyle && marginTopStyle.match( pxLengthRegex );
866 marginBottomStyle = marginBottomStyle && marginBottomStyle.match( pxLengthRegex );
867 marginTopPx = parseInt( marginTopStyle, 10 );
868 marginBottomPx = parseInt( marginBottomStyle, 10 );
869
870 value = ( marginTopPx == marginBottomPx ) && marginTopPx;
871 isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'vspace' ) );
872 this.setValue( value );
873 }
874 },
875 commit: function( type, element ) {
876 var value = parseInt( this.getValue(), 10 );
877 if ( type == IMAGE || type == PREVIEW ) {
878 if ( !isNaN( value ) ) {
879 element.setStyle( 'margin-top', CKEDITOR.tools.cssLength( value ) );
880 element.setStyle( 'margin-bottom', CKEDITOR.tools.cssLength( value ) );
881 } else if ( !value && this.isChanged() ) {
882 element.removeStyle( 'margin-top' );
883 element.removeStyle( 'margin-bottom' );
884 }
885
886 if ( type == IMAGE )
887 element.removeAttribute( 'vspace' );
888 } else if ( type == CLEANUP ) {
889 element.removeAttribute( 'vspace' );
890 element.removeStyle( 'margin-top' );
891 element.removeStyle( 'margin-bottom' );
892 }
893 }
894 },
895 {
896 id: 'cmbAlign',
897 requiredContent: 'img{float}',
898 type: 'select',
899 widths: [ '35%', '65%' ],
900 style: 'width:90px',
901 label: editor.lang.common.align,
902 'default': '',
903 items: [
904 [ editor.lang.common.notSet, '' ],
905 [ editor.lang.common.left, 'left' ],
906 [ editor.lang.common.right, 'right' ]
907 // Backward compatible with v2 on setup when specified as attribute value,
908 // while these values are no more available as select options.
909 // [ editor.lang.image.alignAbsBottom , 'absBottom'],
910 // [ editor.lang.image.alignAbsMiddle , 'absMiddle'],
911 // [ editor.lang.image.alignBaseline , 'baseline'],
912 // [ editor.lang.image.alignTextTop , 'text-top'],
913 // [ editor.lang.image.alignBottom , 'bottom'],
914 // [ editor.lang.image.alignMiddle , 'middle'],
915 // [ editor.lang.image.alignTop , 'top']
916 ],
917 onChange: function() {
918 updatePreview( this.getDialog() );
919 commitInternally.call( this, 'advanced:txtdlgGenStyle' );
920 },
921 setup: function( type, element ) {
922 if ( type == IMAGE ) {
923 var value = element.getStyle( 'float' );
924 switch ( value ) {
925 // Ignore those unrelated values.
926 case 'inherit':
927 case 'none':
928 value = '';
929 }
930
931 !value && ( value = ( element.getAttribute( 'align' ) || '' ).toLowerCase() );
932 this.setValue( value );
933 }
934 },
935 commit: function( type, element ) {
936 var value = this.getValue();
937 if ( type == IMAGE || type == PREVIEW ) {
938 if ( value )
939 element.setStyle( 'float', value );
940 else
941 element.removeStyle( 'float' );
942
943 if ( type == IMAGE ) {
944 value = ( element.getAttribute( 'align' ) || '' ).toLowerCase();
945 switch ( value ) {
946 // we should remove it only if it matches "left" or "right",
947 // otherwise leave it intact.
948 case 'left':
949 case 'right':
950 element.removeAttribute( 'align' );
951 }
952 }
953 } else if ( type == CLEANUP ) {
954 element.removeStyle( 'float' );
955 }
956 }
957 } ]
958 } ]
959 },
960 {
961 type: 'vbox',
962 height: '250px',
963 children: [ {
964 type: 'html',
965 id: 'htmlPreview',
966 style: 'width:95%;',
967 html: '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) + '<br>' +
968 '<div id="' + imagePreviewLoaderId + '" class="ImagePreviewLoader" style="display:none"><div class="loading">&nbsp;</div></div>' +
969 '<div class="ImagePreviewBox"><table><tr><td>' +
970 '<a href="javascript:void(0)" target="_blank" onclick="return false;" id="' + previewLinkId + '">' +
971 '<img id="' + previewImageId + '" alt="" /></a>' +
972 // jscs:disable maximumLineLength
973 ( editor.config.image_previewText || 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ' +
974 'Maecenas feugiat consequat diam. Maecenas metus. Vivamus diam purus, cursus a, commodo non, facilisis vitae, ' +
975 'nulla. Aenean dictum lacinia tortor. Nunc iaculis, nibh non iaculis aliquam, orci felis euismod neque, sed ornare massa mauris sed velit. Nulla pretium mi et risus. Fusce mi pede, tempor id, cursus ac, ullamcorper nec, enim. Sed tortor. Curabitur molestie. Duis velit augue, condimentum at, ultrices a, luctus ut, orci. Donec pellentesque egestas eros. Integer cursus, augue in cursus faucibus, eros pede bibendum sem, in tempus tellus justo quis ligula. Etiam eget tortor. Vestibulum rutrum, est ut placerat elementum, lectus nisl aliquam velit, tempor aliquam eros nunc nonummy metus. In eros metus, gravida a, gravida sed, lobortis id, turpis. Ut ultrices, ipsum at venenatis fringilla, sem nulla lacinia tellus, eget aliquet turpis mauris non enim. Nam turpis. Suspendisse lacinia. Curabitur ac tortor ut ipsum egestas elementum. Nunc imperdiet gravida mauris.' ) +
976 // jscs:enable maximumLineLength
977 '</td></tr></table></div></div>'
978 } ]
979 } ]
980 } ]
981 },
982 {
983 id: 'Link',
984 requiredContent: 'a[href]',
985 label: editor.lang.image.linkTab,
986 padding: 0,
987 elements: [ {
988 id: 'txtUrl',
989 type: 'text',
990 label: editor.lang.common.url,
991 style: 'width: 100%',
992 'default': '',
993 setup: function( type, element ) {
994 if ( type == LINK ) {
995 var href = element.data( 'cke-saved-href' );
996 if ( !href )
997 href = element.getAttribute( 'href' );
998 this.setValue( href );
999 }
1000 },
1001 commit: function( type, element ) {
1002 if ( type == LINK ) {
1003 if ( this.getValue() || this.isChanged() ) {
1004 var url = this.getValue();
1005 element.data( 'cke-saved-href', url );
1006 element.setAttribute( 'href', url );
1007
1008 if ( this.getValue() || !editor.config.image_removeLinkByEmptyURL )
1009 this.getDialog().addLink = true;
1010 else
1011 this.getDialog().addLink = false;
1012 }
1013 }
1014 }
1015 },
1016 {
1017 type: 'button',
1018 id: 'browse',
1019 className: 'cke_dialog_image_browse',
1020 filebrowser: {
1021 action: 'Browse',
1022 target: 'Link:txtUrl',
1023 url: editor.config.filebrowserImageBrowseLinkUrl
1024 },
1025 style: 'float:right',
1026 hidden: true,
1027 label: editor.lang.common.browseServer
1028 },
1029 {
1030 id: 'cmbTarget',
1031 type: 'select',
1032 requiredContent: 'a[target]',
1033 label: editor.lang.common.target,
1034 'default': '',
1035 items: [
1036 [ editor.lang.common.notSet, '' ],
1037 [ editor.lang.common.targetNew, '_blank' ],
1038 [ editor.lang.common.targetTop, '_top' ],
1039 [ editor.lang.common.targetSelf, '_self' ],
1040 [ editor.lang.common.targetParent, '_parent' ]
1041 ],
1042 setup: function( type, element ) {
1043 if ( type == LINK )
1044 this.setValue( element.getAttribute( 'target' ) || '' );
1045 },
1046 commit: function( type, element ) {
1047 if ( type == LINK ) {
1048 if ( this.getValue() || this.isChanged() )
1049 element.setAttribute( 'target', this.getValue() );
1050 }
1051 }
1052 } ]
1053 },
1054 {
1055 id: 'Upload',
1056 hidden: true,
1057 filebrowser: 'uploadButton',
1058 label: editor.lang.image.upload,
1059 elements: [ {
1060 type: 'file',
1061 id: 'upload',
1062 label: editor.lang.image.btnUpload,
1063 style: 'height:40px',
1064 size: 38
1065 },
1066 {
1067 type: 'fileButton',
1068 id: 'uploadButton',
1069 filebrowser: 'info:txtUrl',
1070 label: editor.lang.image.btnUpload,
1071 'for': [ 'Upload', 'upload' ]
1072 } ]
1073 },
1074 {
1075 id: 'advanced',
1076 label: editor.lang.common.advancedTab,
1077 elements: [ {
1078 type: 'hbox',
1079 widths: [ '50%', '25%', '25%' ],
1080 children: [ {
1081 type: 'text',
1082 id: 'linkId',
1083 requiredContent: 'img[id]',
1084 label: editor.lang.common.id,
1085 setup: function( type, element ) {
1086 if ( type == IMAGE )
1087 this.setValue( element.getAttribute( 'id' ) );
1088 },
1089 commit: function( type, element ) {
1090 if ( type == IMAGE ) {
1091 if ( this.getValue() || this.isChanged() )
1092 element.setAttribute( 'id', this.getValue() );
1093 }
1094 }
1095 },
1096 {
1097 id: 'cmbLangDir',
1098 type: 'select',
1099 requiredContent: 'img[dir]',
1100 style: 'width : 100px;',
1101 label: editor.lang.common.langDir,
1102 'default': '',
1103 items: [
1104 [ editor.lang.common.notSet, '' ],
1105 [ editor.lang.common.langDirLtr, 'ltr' ],
1106 [ editor.lang.common.langDirRtl, 'rtl' ]
1107 ],
1108 setup: function( type, element ) {
1109 if ( type == IMAGE )
1110 this.setValue( element.getAttribute( 'dir' ) );
1111 },
1112 commit: function( type, element ) {
1113 if ( type == IMAGE ) {
1114 if ( this.getValue() || this.isChanged() )
1115 element.setAttribute( 'dir', this.getValue() );
1116 }
1117 }
1118 },
1119 {
1120 type: 'text',
1121 id: 'txtLangCode',
1122 requiredContent: 'img[lang]',
1123 label: editor.lang.common.langCode,
1124 'default': '',
1125 setup: function( type, element ) {
1126 if ( type == IMAGE )
1127 this.setValue( element.getAttribute( 'lang' ) );
1128 },
1129 commit: function( type, element ) {
1130 if ( type == IMAGE ) {
1131 if ( this.getValue() || this.isChanged() )
1132 element.setAttribute( 'lang', this.getValue() );
1133 }
1134 }
1135 } ]
1136 },
1137 {
1138 type: 'text',
1139 id: 'txtGenLongDescr',
1140 requiredContent: 'img[longdesc]',
1141 label: editor.lang.common.longDescr,
1142 setup: function( type, element ) {
1143 if ( type == IMAGE )
1144 this.setValue( element.getAttribute( 'longDesc' ) );
1145 },
1146 commit: function( type, element ) {
1147 if ( type == IMAGE ) {
1148 if ( this.getValue() || this.isChanged() )
1149 element.setAttribute( 'longDesc', this.getValue() );
1150 }
1151 }
1152 },
1153 {
1154 type: 'hbox',
1155 widths: [ '50%', '50%' ],
1156 children: [ {
1157 type: 'text',
1158 id: 'txtGenClass',
1159 requiredContent: 'img(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
1160 label: editor.lang.common.cssClass,
1161 'default': '',
1162 setup: function( type, element ) {
1163 if ( type == IMAGE )
1164 this.setValue( element.getAttribute( 'class' ) );
1165 },
1166 commit: function( type, element ) {
1167 if ( type == IMAGE ) {
1168 if ( this.getValue() || this.isChanged() )
1169 element.setAttribute( 'class', this.getValue() );
1170 }
1171 }
1172 },
1173 {
1174 type: 'text',
1175 id: 'txtGenTitle',
1176 requiredContent: 'img[title]',
1177 label: editor.lang.common.advisoryTitle,
1178 'default': '',
1179 onChange: function() {
1180 updatePreview( this.getDialog() );
1181 },
1182 setup: function( type, element ) {
1183 if ( type == IMAGE )
1184 this.setValue( element.getAttribute( 'title' ) );
1185 },
1186 commit: function( type, element ) {
1187 if ( type == IMAGE ) {
1188 if ( this.getValue() || this.isChanged() )
1189 element.setAttribute( 'title', this.getValue() );
1190 } else if ( type == PREVIEW )
1191 element.setAttribute( 'title', this.getValue() );
1192 else if ( type == CLEANUP ) {
1193 element.removeAttribute( 'title' );
1194 }
1195 }
1196 } ]
1197 },
1198 {
1199 type: 'text',
1200 id: 'txtdlgGenStyle',
1201 requiredContent: 'img{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
1202 label: editor.lang.common.cssStyle,
1203 validate: CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
1204 'default': '',
1205 setup: function( type, element ) {
1206 if ( type == IMAGE ) {
1207 var genStyle = element.getAttribute( 'style' );
1208 if ( !genStyle && element.$.style.cssText )
1209 genStyle = element.$.style.cssText;
1210 this.setValue( genStyle );
1211
1212 var height = element.$.style.height,
1213 width = element.$.style.width,
1214 aMatchH = ( height ? height : '' ).match( regexGetSize ),
1215 aMatchW = ( width ? width : '' ).match( regexGetSize );
1216
1217 this.attributesInStyle = {
1218 height: !!aMatchH,
1219 width: !!aMatchW
1220 };
1221 }
1222 },
1223 onChange: function() {
1224 commitInternally.call(
1225 this, [
1226 'info:cmbFloat',
1227 'info:cmbAlign',
1228 'info:txtVSpace',
1229 'info:txtHSpace',
1230 'info:txtBorder',
1231 'info:txtWidth',
1232 'info:txtHeight'
1233 ]
1234 );
1235 updatePreview( this );
1236 },
1237 commit: function( type, element ) {
1238 if ( type == IMAGE && ( this.getValue() || this.isChanged() ) )
1239 element.setAttribute( 'style', this.getValue() );
1240
1241 }
1242 } ]
1243 } ]
1244 };
1245 };
1246
1247 CKEDITOR.dialog.add( 'image', function( editor ) {
1248 return imageDialog( editor, 'image' );
1249 } );
1250
1251 CKEDITOR.dialog.add( 'imagebutton', function( editor ) {
1252 return imageDialog( editor, 'imagebutton' );
1253 } );
1254} )();
Note: See TracBrowser for help on using the repository browser.