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