2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
7 var imageDialog = function( editor
, dialogType
) {
13 regexGetSize
= /^\s*(\d+)((px)|\%)?\s*$/i,
14 regexGetSizeOrEmpty
= /(^\s*(\d+)((px)|\%)?\s*$)|^$/i,
15 pxLengthRegex
= /^\d+px$/;
17 var onSizeChange = function() {
18 var value
= this.getValue(),
19 // This = input element.
20 dialog
= this.getDialog(),
21 aMatch
= value
.match( regexGetSize
); // Check value
23 if ( aMatch
[ 2 ] == '%' ) // % is allowed - > unlock ratio.
24 switchLockRatio( dialog
, false ); // Unlock.
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
);
38 // this.id = txtWidth.
40 if ( value
&& value
!= '0' )
41 value
= Math
.round( oImageOriginal
.$.height
* ( value
/ oImageOriginal
.$.width
) );
42 if ( !isNaN( value
) )
43 dialog
.setValueOf( 'info', 'txtHeight', value
);
47 updatePreview( dialog
);
50 var updatePreview = function( dialog
) {
51 //Don't load before onShow.
52 if ( !dialog
.originalElement
|| !dialog
.preview
)
55 // Read attributes and update imagePreview;
56 dialog
.commitContent( PREVIEW
, dialog
.preview
);
60 // Custom commit dialog logic, where we're intended to give inline style
61 // field (txtdlgGenStyle) higher priority to avoid overwriting styles contribute
63 function commitContent() {
65 var inlineStyleField
= this.getContentElement( 'advanced', 'txtdlgGenStyle' );
66 inlineStyleField
&& inlineStyleField
.commit
.apply( inlineStyleField
, args
);
68 this.foreach( function( widget
) {
69 if ( widget
.commit
&& widget
.id
!= 'txtdlgGenStyle' )
70 widget
.commit
.apply( widget
, args
);
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
) {
85 var dialog
= this.getDialog(),
86 element
= dialog
.imageElement
;
88 // Commit this field and broadcast to target fields.
89 this.commit( IMAGE
, element
);
91 targetFields
= [].concat( targetFields
);
92 var length
= targetFields
.length
,
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
);
104 var switchLockRatio = function( dialog
, value
) {
105 if ( !dialog
.getContentElement( 'info', 'ratioLock' ) )
108 var oImageOriginal
= dialog
.originalElement
;
110 // Dialog may already closed. (http://dev.ckeditor.com/ticket/5505)
111 if ( !oImageOriginal
)
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
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;
130 } else if ( value
!== undefined )
131 dialog
.lockRatio
= value
;
133 dialog
.userlockRatio
= 1;
134 dialog
.lockRatio
= !dialog
.lockRatio
;
137 var ratioButton
= CKEDITOR
.document
.getById( btnLockSizesId
);
138 if ( dialog
.lockRatio
)
139 ratioButton
.removeClass( 'cke_btn_unlocked' );
141 ratioButton
.addClass( 'cke_btn_unlocked' );
143 ratioButton
.setAttribute( 'aria-checked', dialog
.lockRatio
);
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' );
151 return dialog
.lockRatio
;
154 var resetSize = function( dialog
, emptyValues
) {
155 var oImageOriginal
= dialog
.originalElement
,
156 ready
= oImageOriginal
.getCustomData( 'isReady' ) == 'true';
159 var widthField
= dialog
.getContentElement( 'info', 'txtWidth' ),
160 heightField
= dialog
.getContentElement( 'info', 'txtHeight' ),
161 widthValue
, heightValue
;
167 widthValue
= oImageOriginal
.$.width
;
168 heightValue
= oImageOriginal
.$.height
;
171 widthField
&& widthField
.setValue( widthValue
);
172 heightField
&& heightField
.setValue( heightValue
);
174 updatePreview( dialog
);
177 var setupDimension = function( type
, element
) {
181 function checkDimension( size
, defaultValue
) {
182 var aMatch
= size
.match( regexGetSize
);
185 if ( aMatch
[ 2 ] == '%' ) {
187 switchLockRatio( dialog
, false ); // Unlock ratio
194 var dialog
= this.getDialog(),
196 dimension
= this.id
== 'txtWidth' ? 'width' : 'height',
197 size
= element
.getAttribute( dimension
);
200 value
= checkDimension( size
, value
);
201 value
= checkDimension( element
.getStyle( dimension
), value
);
203 this.setValue( value
);
206 var previewPreloader
;
208 var onImgLoadEvent = function() {
210 var original
= this.originalElement
,
211 loader
= CKEDITOR
.document
.getById( imagePreviewLoaderId
);
213 original
.setCustomData( 'isReady', 'true' );
214 original
.removeListener( 'load', onImgLoadEvent
);
215 original
.removeListener( 'error', onImgLoadErrorEvent
);
216 original
.removeListener( 'abort', onImgLoadErrorEvent
);
220 loader
.setStyle( 'display', 'none' );
222 // New image -> new dimensions
223 if ( !this.dontResetSize
) {
224 resetSize( this, editor
.config
.image_prefillDimensions
=== false );
227 if ( this.firstLoad
) {
228 CKEDITOR
.tools
.setTimeout( function() {
229 switchLockRatio( this, 'check' );
233 this.firstLoad
= false;
234 this.dontResetSize
= false;
236 // Possible fix for http://dev.ckeditor.com/ticket/12818.
237 updatePreview( this );
240 var onImgLoadErrorEvent = function() {
241 // Error. Image is not loaded.
242 var original
= this.originalElement
,
243 loader
= CKEDITOR
.document
.getById( imagePreviewLoaderId
);
245 original
.removeListener( 'load', onImgLoadEvent
);
246 original
.removeListener( 'error', onImgLoadErrorEvent
);
247 original
.removeListener( 'abort', onImgLoadErrorEvent
);
250 var noimage
= CKEDITOR
.getUrl( CKEDITOR
.plugins
.get( 'image' ).path
+ 'images/noimage.png' );
253 this.preview
.setAttribute( 'src', noimage
);
257 loader
.setStyle( 'display', 'none' );
259 switchLockRatio( this, false ); // Unlock.
262 var numbering = function( id
) {
263 return CKEDITOR
.tools
.getNextId() + '_' + id
;
265 btnLockSizesId
= numbering( 'btnLockSizes' ),
266 btnResetSizeId
= numbering( 'btnResetSize' ),
267 imagePreviewLoaderId
= numbering( 'ImagePreviewLoader' ),
268 previewLinkId
= numbering( 'previewLink' ),
269 previewImageId
= numbering( 'previewImage' );
272 title: editor
.lang
.image
[ dialogType
== 'image' ? 'title' : 'titleButton' ],
273 minWidth: ( CKEDITOR
.skinName
|| editor
.config
.skin
) == 'moono-lisa' ? 500 : 420,
276 this.imageElement
= false;
277 this.linkElement
= false;
279 // Default: create a new element.
280 this.imageEditMode
= false;
281 this.linkEditMode
= false;
283 this.lockRatio
= true;
284 this.userlockRatio
= 0;
285 this.dontResetSize
= false;
286 this.firstLoad
= true;
287 this.addLink
= false;
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
);
297 loader
.setStyle( 'display', 'none' );
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
);
304 this.originalElement
= editor
.document
.createElement( 'img' );
305 this.originalElement
.setAttribute( 'alt', '' );
306 this.originalElement
.setCustomData( 'isReady', 'false' );
309 this.linkElement
= link
;
310 this.linkEditMode
= true;
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. (http://dev.ckeditor.com/ticket/13351)
316 // Look for Image element.
317 var linkChildren
= link
.getChildren();
318 if ( linkChildren
.count() == 1 ) {
319 var childTag
= linkChildren
.getItem( 0 );
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';
331 // Fill out all fields.
332 if ( dialogType
== 'image' )
333 this.setupContent( LINK
, link
);
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
;
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
;
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 );
354 // Fill out all fields.
355 this.setupContent( IMAGE
, this.imageElement
);
358 // Refresh LockRatio button
359 switchLockRatio( this, true );
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' );
368 // Edit existing Image.
369 if ( this.imageEditMode
) {
370 var imgTagName
= this.imageEditMode
;
372 // Image dialog and Input element.
373 if ( dialogType
== 'image' && imgTagName
== 'input' && confirm( editor
.lang
.image
.button2Img
) ) { // jshint ignore:line
374 // Replace INPUT-> IMG
376 this.imageElement
= editor
.document
.createElement( 'img' );
377 this.imageElement
.setAttribute( 'alt', '' );
378 editor
.insertElement( this.imageElement
);
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( {
389 editor
.insertElement( this.imageElement
);
391 // Restore the original element before all commits.
392 this.imageElement
= this.cleanImageElement
;
393 delete this.cleanImageElement
;
396 // Create a new image.
398 // Image dialog -> create IMG element.
399 if ( dialogType
== 'image' )
400 this.imageElement
= editor
.document
.createElement( 'img' );
402 this.imageElement
= editor
.document
.createElement( 'input' );
403 this.imageElement
.setAttribute( 'type', 'image' );
405 this.imageElement
.setAttribute( 'alt', '' );
408 // Create a new link.
409 if ( !this.linkEditMode
)
410 this.linkElement
= editor
.document
.createElement( 'a' );
413 this.commitContent( IMAGE
, this.imageElement
);
414 this.commitContent( LINK
, this.linkElement
);
416 // Remove empty style attribute.
417 if ( !this.imageElement
.getAttribute( 'style' ) )
418 this.imageElement
.removeAttribute( 'style' );
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 );
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 );
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
);
439 editor
.insertElement( this.imageElement
);
442 // Image already exists.
444 // Add a new link element.
445 if ( !this.linkEditMode
&& this.addLink
) {
446 editor
.insertElement( this.linkElement
);
447 this.imageElement
.appendTo( this.linkElement
);
449 // Remove Link, Image exists.
450 else if ( this.linkEditMode
&& !this.addLink
) {
451 editor
.getSelection().selectElement( this.linkElement
);
452 editor
.insertElement( this.imageElement
);
457 if ( dialogType
!= 'image' )
458 this.hidePage( 'Link' ); //Hide Link tab.
459 var doc
= this._
.element
.getDocument();
461 if ( this.getContentElement( 'info', 'ratioLock' ) ) {
462 this.addFocusable( doc
.getById( btnResetSizeId
), 5 );
463 this.addFocusable( doc
.getById( btnLockSizesId
), 5 );
466 this.commitContent
= commitContent
;
470 this.commitContent( CLEANUP
, this.preview
);
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.
480 delete this.imageElement
;
484 label: editor
.lang
.image
.infoTab
,
491 widths: [ '280px', '110px' ],
493 className: 'cke_dialog_image_url',
497 label: editor
.lang
.common
.url
,
499 onChange: function() {
500 var dialog
= this.getDialog(),
501 newUrl
= this.getValue();
503 // Update original image.
504 // Prevent from load before onShow.
505 if ( newUrl
.length
> 0 ) {
506 dialog
= this.getDialog();
507 var original
= dialog
.originalElement
;
509 if ( dialog
.preview
) {
510 dialog
.preview
.removeStyle( 'display' );
513 original
.setCustomData( 'isReady', 'false' );
515 var loader
= CKEDITOR
.document
.getById( imagePreviewLoaderId
);
517 loader
.setStyle( 'display', '' );
519 original
.on( 'load', onImgLoadEvent
, dialog
);
520 original
.on( 'error', onImgLoadErrorEvent
, dialog
);
521 original
.on( 'abort', onImgLoadErrorEvent
, dialog
);
522 original
.setAttribute( 'src', newUrl
);
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
);
531 // Dont show preview if no URL given.
532 else if ( dialog
.preview
) {
533 dialog
.preview
.removeAttribute( 'src' );
534 dialog
.preview
.setStyle( 'display', 'none' );
537 setup: function( type
, element
) {
538 if ( type
== IMAGE
) {
539 var url
= element
.data( 'cke-saved-src' ) || element
.getAttribute( 'src' );
542 this.getDialog().dontResetSize
= true;
544 field
.setValue( url
); // And call this.onChange()
545 // Manually set the initial value.(http://dev.ckeditor.com/ticket/4191)
546 field
.setInitValue();
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' );
558 validate: CKEDITOR
.dialog
.validate
.notEmpty( editor
.lang
.image
.urlMissing
)
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;',
567 label: editor
.lang
.common
.browseServer
,
569 filebrowser: 'info:txtUrl'
576 label: editor
.lang
.image
.alt
,
579 onChange: function() {
580 updatePreview( this.getDialog() );
582 setup: function( type
, element
) {
584 this.setValue( element
.getAttribute( 'alt' ) );
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' );
605 requiredContent: 'img{width,height}',
606 widths: [ '50%', '50%' ],
614 label: editor
.lang
.common
.width
,
615 onKeyUp: onSizeChange
,
616 onChange: function() {
617 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
619 validate: function() {
620 var aMatch
= this.getValue().match( regexGetSizeOrEmpty
),
621 isValid
= !!( aMatch
&& parseInt( aMatch
[ 1 ], 10 ) !== 0 );
623 alert( editor
.lang
.common
.invalidWidth
); // jshint ignore:line
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
) );
633 element
.removeStyle( 'width' );
635 element
.removeAttribute( 'width' );
636 } else if ( type
== PREVIEW
) {
637 var aMatch
= value
.match( regexGetSize
);
639 var oImageOriginal
= this.getDialog().originalElement
;
640 if ( oImageOriginal
.getCustomData( 'isReady' ) == 'true' )
641 element
.setStyle( 'width', oImageOriginal
.$.width
+ 'px' );
643 element
.setStyle( 'width', CKEDITOR
.tools
.cssLength( value
) );
645 } else if ( type
== CLEANUP
) {
646 element
.removeAttribute( 'width' );
647 element
.removeStyle( 'width' );
655 label: editor
.lang
.common
.height
,
656 onKeyUp: onSizeChange
,
657 onChange: function() {
658 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
660 validate: function() {
661 var aMatch
= this.getValue().match( regexGetSizeOrEmpty
),
662 isValid
= !!( aMatch
&& parseInt( aMatch
[ 1 ], 10 ) !== 0 );
664 alert( editor
.lang
.common
.invalidHeight
); // jshint ignore:line
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
) );
674 element
.removeStyle( 'height' );
676 element
.removeAttribute( 'height' );
677 } else if ( type
== PREVIEW
) {
678 var aMatch
= value
.match( regexGetSize
);
680 var oImageOriginal
= this.getDialog().originalElement
;
681 if ( oImageOriginal
.getCustomData( 'isReady' ) == 'true' )
682 element
.setStyle( 'height', oImageOriginal
.$.height
+ 'px' );
684 element
.setStyle( 'height', CKEDITOR
.tools
.cssLength( value
) );
686 } else if ( type
== CLEANUP
) {
687 element
.removeAttribute( 'height' );
688 element
.removeStyle( 'height' );
696 className: 'cke_dialog_image_ratiolock',
697 style: 'margin-top:30px;width:40px;height:40px;',
699 // Activate Reset button
700 var resetButton
= CKEDITOR
.document
.getById( btnResetSizeId
),
701 ratioButton
= CKEDITOR
.document
.getById( btnLockSizesId
);
703 resetButton
.on( 'click', function( evt
) {
705 evt
.data
&& evt
.data
.preventDefault();
706 }, this.getDialog() );
707 resetButton
.on( 'mouseover', function() {
708 this.addClass( 'cke_btn_over' );
710 resetButton
.on( 'mouseout', function() {
711 this.removeClass( 'cke_btn_over' );
714 // Activate (Un)LockRatio button
716 ratioButton
.on( 'click', function( evt
) {
717 switchLockRatio( this );
719 var oImageOriginal
= this.originalElement
,
720 width
= this.getValueOf( 'info', 'txtWidth' );
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 );
729 evt
.data
&& evt
.data
.preventDefault();
730 }, this.getDialog() );
731 ratioButton
.on( 'mouseover', function() {
732 this.addClass( 'cke_btn_over' );
734 ratioButton
.on( 'mouseout', function() {
735 this.removeClass( 'cke_btn_over' );
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>' +
753 requiredContent: 'img{border-width}',
755 label: editor
.lang
.image
.border
,
757 onKeyUp: function() {
758 updatePreview( this.getDialog() );
760 onChange: function() {
761 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
763 validate: CKEDITOR
.dialog
.validate
.integer( editor
.lang
.image
.validateBorder
),
764 setup: function( type
, element
) {
765 if ( type
== IMAGE
) {
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
);
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' );
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' );
797 requiredContent: 'img{margin-left,margin-right}',
799 label: editor
.lang
.image
.hSpace
,
801 onKeyUp: function() {
802 updatePreview( this.getDialog() );
804 onChange: function() {
805 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
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' );
814 marginLeftStyle
= marginLeftStyle
&& marginLeftStyle
.match( pxLengthRegex
);
815 marginRightStyle
= marginRightStyle
&& marginRightStyle
.match( pxLengthRegex
);
816 marginLeftPx
= parseInt( marginLeftStyle
, 10 );
817 marginRightPx
= parseInt( marginRightStyle
, 10 );
819 value
= ( marginLeftPx
== marginRightPx
) && marginLeftPx
;
820 isNaN( parseInt( value
, 10 ) ) && ( value
= element
.getAttribute( 'hspace' ) );
822 this.setValue( value
);
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' );
837 element
.removeAttribute( 'hspace' );
838 } else if ( type
== CLEANUP
) {
839 element
.removeAttribute( 'hspace' );
840 element
.removeStyle( 'margin-left' );
841 element
.removeStyle( 'margin-right' );
848 requiredContent: 'img{margin-top,margin-bottom}',
850 label: editor
.lang
.image
.vSpace
,
852 onKeyUp: function() {
853 updatePreview( this.getDialog() );
855 onChange: function() {
856 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
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' );
865 marginTopStyle
= marginTopStyle
&& marginTopStyle
.match( pxLengthRegex
);
866 marginBottomStyle
= marginBottomStyle
&& marginBottomStyle
.match( pxLengthRegex
);
867 marginTopPx
= parseInt( marginTopStyle
, 10 );
868 marginBottomPx
= parseInt( marginBottomStyle
, 10 );
870 value
= ( marginTopPx
== marginBottomPx
) && marginTopPx
;
871 isNaN( parseInt( value
, 10 ) ) && ( value
= element
.getAttribute( 'vspace' ) );
872 this.setValue( value
);
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' );
887 element
.removeAttribute( 'vspace' );
888 } else if ( type
== CLEANUP
) {
889 element
.removeAttribute( 'vspace' );
890 element
.removeStyle( 'margin-top' );
891 element
.removeStyle( 'margin-bottom' );
897 requiredContent: 'img{float}',
899 widths: [ '35%', '65%' ],
901 label: editor
.lang
.common
.align
,
904 [ editor
.lang
.common
.notSet
, '' ],
905 [ editor
.lang
.common
.alignLeft
, 'left' ],
906 [ editor
.lang
.common
.alignRight
, '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']
917 onChange: function() {
918 updatePreview( this.getDialog() );
919 commitInternally
.call( this, 'advanced:txtdlgGenStyle' );
921 setup: function( type
, element
) {
922 if ( type
== IMAGE
) {
923 var value
= element
.getStyle( 'float' );
925 // Ignore those unrelated values.
931 !value
&& ( value
= ( element
.getAttribute( 'align' ) || '' ).toLowerCase() );
932 this.setValue( value
);
935 commit: function( type
, element
) {
936 var value
= this.getValue();
937 if ( type
== IMAGE
|| type
== PREVIEW
) {
939 element
.setStyle( 'float', value
);
941 element
.removeStyle( 'float' );
943 if ( type
== IMAGE
) {
944 value
= ( element
.getAttribute( 'align' ) || '' ).toLowerCase();
946 // we should remove it only if it matches "left" or "right",
947 // otherwise leave it intact.
950 element
.removeAttribute( 'align' );
953 } else if ( type
== CLEANUP
) {
954 element
.removeStyle( 'float' );
967 html: '<div>' + CKEDITOR
.tools
.htmlEncode( editor
.lang
.common
.preview
) + '<br>' +
968 '<div id="' + imagePreviewLoaderId
+ '" class="ImagePreviewLoader" style="display:none"><div class="loading"> </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>'
984 requiredContent: 'a[href]',
985 label: editor
.lang
.image
.linkTab
,
990 label: editor
.lang
.common
.url
,
991 style: 'width: 100%',
993 setup: function( type
, element
) {
994 if ( type
== LINK
) {
995 var href
= element
.data( 'cke-saved-href' );
997 href
= element
.getAttribute( 'href' );
998 this.setValue( href
);
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
);
1008 if ( this.getValue() || !editor
.config
.image_removeLinkByEmptyURL
)
1009 this.getDialog().addLink
= true;
1011 this.getDialog().addLink
= false;
1019 className: 'cke_dialog_image_browse',
1022 target: 'Link:txtUrl',
1023 url: editor
.config
.filebrowserImageBrowseLinkUrl
1025 style: 'float:right',
1027 label: editor
.lang
.common
.browseServer
1032 requiredContent: 'a[target]',
1033 label: editor
.lang
.common
.target
,
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' ]
1042 setup: function( type
, element
) {
1044 this.setValue( element
.getAttribute( 'target' ) || '' );
1046 commit: function( type
, element
) {
1047 if ( type
== LINK
) {
1048 if ( this.getValue() || this.isChanged() )
1049 element
.setAttribute( 'target', this.getValue() );
1057 filebrowser: 'uploadButton',
1058 label: editor
.lang
.image
.upload
,
1062 label: editor
.lang
.image
.btnUpload
,
1063 style: 'height:40px',
1069 filebrowser: 'info:txtUrl',
1070 label: editor
.lang
.image
.btnUpload
,
1071 'for': [ 'Upload', 'upload' ]
1076 label: editor
.lang
.common
.advancedTab
,
1079 widths: [ '50%', '25%', '25%' ],
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' ) );
1089 commit: function( type
, element
) {
1090 if ( type
== IMAGE
) {
1091 if ( this.getValue() || this.isChanged() )
1092 element
.setAttribute( 'id', this.getValue() );
1099 requiredContent: 'img[dir]',
1100 style: 'width : 100px;',
1101 label: editor
.lang
.common
.langDir
,
1104 [ editor
.lang
.common
.notSet
, '' ],
1105 [ editor
.lang
.common
.langDirLtr
, 'ltr' ],
1106 [ editor
.lang
.common
.langDirRtl
, 'rtl' ]
1108 setup: function( type
, element
) {
1109 if ( type
== IMAGE
)
1110 this.setValue( element
.getAttribute( 'dir' ) );
1112 commit: function( type
, element
) {
1113 if ( type
== IMAGE
) {
1114 if ( this.getValue() || this.isChanged() )
1115 element
.setAttribute( 'dir', this.getValue() );
1122 requiredContent: 'img[lang]',
1123 label: editor
.lang
.common
.langCode
,
1125 setup: function( type
, element
) {
1126 if ( type
== IMAGE
)
1127 this.setValue( element
.getAttribute( 'lang' ) );
1129 commit: function( type
, element
) {
1130 if ( type
== IMAGE
) {
1131 if ( this.getValue() || this.isChanged() )
1132 element
.setAttribute( 'lang', this.getValue() );
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' ) );
1146 commit: function( type
, element
) {
1147 if ( type
== IMAGE
) {
1148 if ( this.getValue() || this.isChanged() )
1149 element
.setAttribute( 'longDesc', this.getValue() );
1155 widths: [ '50%', '50%' ],
1159 requiredContent: 'img(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
1160 label: editor
.lang
.common
.cssClass
,
1162 setup: function( type
, element
) {
1163 if ( type
== IMAGE
)
1164 this.setValue( element
.getAttribute( 'class' ) );
1166 commit: function( type
, element
) {
1167 if ( type
== IMAGE
) {
1168 if ( this.getValue() || this.isChanged() )
1169 element
.setAttribute( 'class', this.getValue() );
1176 requiredContent: 'img[title]',
1177 label: editor
.lang
.common
.advisoryTitle
,
1179 onChange: function() {
1180 updatePreview( this.getDialog() );
1182 setup: function( type
, element
) {
1183 if ( type
== IMAGE
)
1184 this.setValue( element
.getAttribute( 'title' ) );
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' );
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
),
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
);
1212 var height
= element
.$.style
.height
,
1213 width
= element
.$.style
.width
,
1214 aMatchH
= ( height
? height : '' ).match( regexGetSize
),
1215 aMatchW
= ( width
? width : '' ).match( regexGetSize
);
1217 this.attributesInStyle
= {
1223 onChange: function() {
1224 commitInternally
.call(
1235 updatePreview( this );
1237 commit: function( type
, element
) {
1238 if ( type
== IMAGE
&& ( this.getValue() || this.isChanged() ) )
1239 element
.setAttribute( 'style', this.getValue() );
1247 CKEDITOR
.dialog
.add( 'image', function( editor
) {
1248 return imageDialog( editor
, 'image' );
1251 CKEDITOR
.dialog
.add( 'imagebutton', function( editor
) {
1252 return imageDialog( editor
, 'imagebutton' );