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 CKEDITOR.dialog.add( 'docProps', function( editor ) 7 { 8 var lang = editor.lang.docprops, 9 langCommon = editor.lang.common, 10 metaHash = {}; 11 12 function getDialogValue( dialogName, callback ) 13 { 14 var onOk = function() 15 { 16 releaseHandlers( this ); 17 callback( this, this._.parentDialog ); 18 }; 19 var releaseHandlers = function( dialog ) 20 { 21 dialog.removeListener( 'ok', onOk ); 22 dialog.removeListener( 'cancel', releaseHandlers ); 23 }; 24 var bindToDialog = function( dialog ) 25 { 26 dialog.on( 'ok', onOk ); 27 dialog.on( 'cancel', releaseHandlers ); 28 }; 29 editor.execCommand( dialogName ); 30 if ( editor._.storedDialogs.colordialog ) 31 bindToDialog( editor._.storedDialogs.colordialog ); 32 else 33 { 34 CKEDITOR.on( 'dialogDefinition', function( e ) 35 { 36 if ( e.data.name != dialogName ) 37 return; 38 39 var definition = e.data.definition; 40 41 e.removeListener(); 42 definition.onLoad = CKEDITOR.tools.override( definition.onLoad, function( orginal ) 43 { 44 return function() 45 { 46 bindToDialog( this ); 47 definition.onLoad = orginal; 48 if ( typeof orginal == 'function' ) 49 orginal.call( this ); 50 }; 51 }); 52 }); 53 } 54 } 55 function handleOther() 56 { 57 var dialog = this.getDialog(), 58 other = dialog.getContentElement( 'general', this.id + 'Other' ); 59 if ( !other ) 60 return; 61 if ( this.getValue() == 'other' ) 62 { 63 other.getInputElement().removeAttribute( 'readOnly' ); 64 other.focus(); 65 other.getElement().removeClass( 'cke_disabled' ); 66 } 67 else 68 { 69 other.getInputElement().setAttribute( 'readOnly', true ); 70 other.getElement().addClass( 'cke_disabled' ); 71 } 72 } 73 function commitMeta( name, isHttp, value ) 74 { 75 return function( doc, html, head ) 76 { 77 var hash = metaHash, 78 val = typeof value != 'undefined' ? value : this.getValue(); 79 if ( !val && ( name in hash ) ) 80 hash[ name ].remove(); 81 else if ( val && ( name in hash ) ) 82 hash[ name ].setAttribute( 'content', val ); 83 else if ( val ) 84 { 85 var meta = new CKEDITOR.dom.element( 'meta', editor.document ); 86 meta.setAttribute( isHttp ? 'http-equiv' : 'name', name ); 87 meta.setAttribute( 'content', val ); 88 head.append( meta ); 89 } 90 }; 91 } 92 function setupMeta( name, ret ) 93 { 94 return function() 95 { 96 var hash = metaHash, 97 result = ( name in hash ) ? hash[ name ].getAttribute( 'content' ) || '' : ''; 98 if ( ret ) 99 return result; 100 this.setValue( result ); 101 return null; 102 }; 103 } 104 function commitMargin( name ) 105 { 106 return function( doc, html, head, body ) 107 { 108 body.removeAttribute( 'margin' + name ); 109 var val = this.getValue(); 110 if ( val !== '' ) 111 body.setStyle( 'margin-' + name, CKEDITOR.tools.cssLength( val ) ); 112 else 113 body.removeStyle( 'margin-' + name ); 114 }; 115 } 116 117 function createMetaHash( doc ) 118 { 119 var hash = {}, 120 metas = doc.getElementsByTag( 'meta' ), 121 count = metas.count(); 122 123 for ( var i = 0; i < count; i++ ) 124 { 125 var meta = metas.getItem( i ); 126 hash[ meta.getAttribute( meta.hasAttribute( 'http-equiv' ) ? 'http-equiv' : 'name' ).toLowerCase() ] = meta; 127 } 128 return hash; 129 } 130 // We cannot just remove the style from the element, as it might be affected from non-inline stylesheets. 131 // To get the proper result, we should manually set the inline style to its default value. 132 function resetStyle( element, prop, resetVal ) 133 { 134 element.removeStyle( prop ); 135 if ( element.getComputedStyle( prop ) != resetVal ) 136 element.setStyle( prop, resetVal ); 137 } 138 139 // Utilty to shorten the creation of color fields in the dialog. 140 var colorField = function( id, label, fieldProps ) 141 { 142 return { 143 type : 'hbox', 144 padding : 0, 145 widths : [ '60%', '40%' ], 146 children : [ 147 CKEDITOR.tools.extend( { 148 type : 'text', 149 id : id, 150 label : lang[ label ] 151 }, fieldProps || {}, 1 ), 152 { 153 type : 'button', 154 id : id + 'Choose', 155 label : lang.chooseColor, 156 className : 'colorChooser', 157 onClick : function() 158 { 159 var self = this; 160 getDialogValue( 'colordialog', function( colorDialog ) 161 { 162 var dialog = self.getDialog(); 163 dialog.getContentElement( dialog._.currentTabId, id ).setValue( colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue() ); 164 }); 165 } 166 } 167 ] 168 }; 169 }; 170 var previewSrc = 'javascript:' + 171 'void((function(){' + 172 encodeURIComponent( 173 'document.open();' + 174 ( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + document.domain + '\';' : '' ) + 175 'document.write( \'<html style="background-color: #ffffff; height: 100%"><head></head><body style="width: 100%; height: 100%; margin: 0px">' + lang.previewHtml + '</body></html>\' );' + 176 'document.close();' 177 ) + 178 '})())'; 179 180 return { 181 title : lang.title, 182 minHeight: 330, 183 minWidth: 500, 184 onShow : function() 185 { 186 var doc = editor.document, 187 html = doc.getElementsByTag( 'html' ).getItem( 0 ), 188 head = doc.getHead(), 189 body = doc.getBody(); 190 metaHash = createMetaHash( doc ); 191 this.setupContent( doc, html, head, body ); 192 }, 193 onHide : function() 194 { 195 metaHash = {}; 196 }, 197 onOk : function() 198 { 199 var doc = editor.document, 200 html = doc.getElementsByTag( 'html' ).getItem( 0 ), 201 head = doc.getHead(), 202 body = doc.getBody(); 203 this.commitContent( doc, html, head, body ); 204 }, 205 contents : [ 206 { 207 id : 'general', 208 label : langCommon.generalTab, 209 elements : [ 210 { 211 type : 'text', 212 id : 'title', 213 label : lang.docTitle, 214 setup : function( doc ) 215 { 216 this.setValue( doc.getElementsByTag( 'title' ).getItem( 0 ).data( 'cke-title' ) ); 217 }, 218 commit : function( doc, html, head, body, isPreview ) 219 { 220 if ( isPreview ) 221 return; 222 doc.getElementsByTag( 'title' ).getItem( 0 ).data( 'cke-title', this.getValue() ); 223 } 224 }, 225 { 226 type : 'hbox', 227 children : [ 228 { 229 type : 'select', 230 id : 'dir', 231 label : langCommon.langDir, 232 style : 'width: 100%', 233 items : [ 234 [ langCommon.notSet , '' ], 235 [ langCommon.langDirLtr, 'ltr' ], 236 [ langCommon.langDirRtl, 'rtl' ] 237 ], 238 setup : function( doc, html, head, body ) 239 { 240 this.setValue( body.getDirection() || '' ); 241 }, 242 commit : function( doc, html, head, body ) 243 { 244 var val = this.getValue(); 245 if ( val ) 246 body.setAttribute( 'dir', val ); 247 else 248 body.removeAttribute( 'dir' ); 249 body.removeStyle( 'direction' ); 250 } 251 }, 252 { 253 type : 'text', 254 id : 'langCode', 255 label : langCommon.langCode, 256 setup : function( doc, html ) 257 { 258 this.setValue( html.getAttribute( 'xml:lang' ) || html.getAttribute( 'lang' ) || '' ); 259 }, 260 commit : function( doc, html, head, body, isPreview ) 261 { 262 if ( isPreview ) 263 return; 264 var val = this.getValue(); 265 if ( val ) 266 html.setAttributes( { 'xml:lang' : val, lang : val } ); 267 else 268 html.removeAttributes( { 'xml:lang' : 1, lang : 1 } ); 269 } 270 } 271 ] 272 }, 273 { 274 type : 'hbox', 275 children : [ 276 { 277 type : 'select', 278 id : 'charset', 279 label : lang.charset, 280 style : 'width: 100%', 281 items : [ 282 [ langCommon.notSet, '' ], 283 [ lang.charsetASCII, 'us-ascii' ], 284 [ lang.charsetCE, 'iso-8859-2' ], 285 [ lang.charsetCT, 'big5' ], 286 [ lang.charsetCR, 'iso-8859-5' ], 287 [ lang.charsetGR, 'iso-8859-7' ], 288 [ lang.charsetJP, 'iso-2022-jp' ], 289 [ lang.charsetKR, 'iso-2022-kr' ], 290 [ lang.charsetTR, 'iso-8859-9' ], 291 [ lang.charsetUN, 'utf-8' ], 292 [ lang.charsetWE, 'iso-8859-1' ], 293 [ lang.other, 'other' ] 294 ], 295 'default' : '', 296 onChange : function() 297 { 298 this.getDialog().selectedCharset = this.getValue() != 'other' ? this.getValue() : ''; 299 handleOther.call( this ); 300 }, 301 setup : function() 302 { 303 this.metaCharset = ( 'charset' in metaHash ); 304 305 var func = setupMeta( this.metaCharset ? 'charset' : 'content-type', 1, 1 ), 306 val = func.call( this ); 307 308 !this.metaCharset && val.match( /charset=[^=]+$/ ) && ( val = val.substring( val.indexOf( '=' ) + 1 ) ); 309 310 if ( val ) 311 { 312 this.setValue( val.toLowerCase() ); 313 if ( !this.getValue() ) 314 { 315 this.setValue( 'other' ); 316 var other = this.getDialog().getContentElement( 'general', 'charsetOther' ); 317 other && other.setValue( val ); 318 } 319 this.getDialog().selectedCharset = val; 320 } 321 322 handleOther.call( this ); 323 }, 324 commit : function( doc, html, head, body, isPreview ) 325 { 326 if ( isPreview ) 327 return; 328 var value = this.getValue(), 329 other = this.getDialog().getContentElement( 'general', 'charsetOther' ); 330 331 value == 'other' && ( value = other ? other.getValue() : '' ); 332 333 value && !this.metaCharset && ( value = ( metaHash[ 'content-type' ] ? metaHash[ 'content-type' ].getAttribute( 'content' ).split( ';' )[0] : 'text/html' ) + '; charset=' + value ); 334 335 var func = commitMeta( this.metaCharset ? 'charset' : 'content-type', 1, value ); 336 func.call( this, doc, html, head ); 337 } 338 }, 339 { 340 type : 'text', 341 id : 'charsetOther', 342 label : lang.charsetOther, 343 onChange : function(){ this.getDialog().selectedCharset = this.getValue(); } 344 } 345 ] 346 }, 347 { 348 type : 'hbox', 349 children : [ 350 { 351 type : 'select', 352 id : 'docType', 353 label : lang.docType, 354 style : 'width: 100%', 355 items : [ 356 [ langCommon.notSet , '' ], 357 [ 'XHTML 1.1', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ], 358 [ 'XHTML 1.0 Transitional', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' ], 359 [ 'XHTML 1.0 Strict', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' ], 360 [ 'XHTML 1.0 Frameset', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">' ], 361 [ 'HTML 5', '<!DOCTYPE html>' ], 362 [ 'HTML 4.01 Transitional', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' ], 363 [ 'HTML 4.01 Strict', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' ], 364 [ 'HTML 4.01 Frameset', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">' ], 365 [ 'HTML 3.2', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">' ], 366 [ 'HTML 2.0', '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">' ], 367 [ lang.other, 'other' ] 368 ], 369 onChange : handleOther, 370 setup : function() 371 { 372 if ( editor.docType ) 373 { 374 this.setValue( editor.docType ); 375 if ( !this.getValue() ) 376 { 377 this.setValue( 'other' ); 378 var other = this.getDialog().getContentElement( 'general', 'docTypeOther' ); 379 other && other.setValue( editor.docType ); 380 } 381 } 382 handleOther.call( this ); 383 }, 384 commit : function( doc, html, head, body, isPreview ) 385 { 386 if ( isPreview ) 387 return; 388 var value = this.getValue(), 389 other = this.getDialog().getContentElement( 'general', 'docTypeOther' ); 390 editor.docType = value == 'other' ? ( other ? other.getValue() : '' ) : value; 391 } 392 }, 393 { 394 type : 'text', 395 id : 'docTypeOther', 396 label : lang.docTypeOther 397 } 398 ] 399 }, 400 { 401 type : 'checkbox', 402 id : 'xhtmlDec', 403 label : lang.xhtmlDec, 404 setup : function() 405 { 406 this.setValue( !!editor.xmlDeclaration ); 407 }, 408 commit : function( doc, html, head, body, isPreview ) 409 { 410 if ( isPreview ) 411 return; 412 if ( this.getValue() ) 413 { 414 editor.xmlDeclaration = '<?xml version="1.0" encoding="' + ( this.getDialog().selectedCharset || 'utf-8' )+ '"?>' ; 415 html.setAttribute( 'xmlns', 'http://www.w3.org/1999/xhtml' ); 416 } 417 else 418 { 419 editor.xmlDeclaration = ''; 420 html.removeAttribute( 'xmlns' ); 421 } 422 } 423 } 424 ] 425 }, 426 { 427 id : 'design', 428 label : lang.design, 429 elements : [ 430 { 431 type : 'hbox', 432 widths : [ '60%', '40%' ], 433 children : [ 434 { 435 type : 'vbox', 436 children : [ 437 colorField( 'txtColor', 'txtColor', 438 { 439 setup : function( doc, html, head, body ) 440 { 441 this.setValue( body.getComputedStyle( 'color' ) ); 442 }, 443 commit : function( doc, html, head, body, isPreview ) 444 { 445 if ( this.isChanged() || isPreview ) 446 { 447 body.removeAttribute( 'text' ); 448 var val = this.getValue(); 449 if ( val ) 450 body.setStyle( 'color', val ); 451 else 452 body.removeStyle( 'color' ); 453 } 454 } 455 }), 456 colorField( 'bgColor', 'bgColor', { 457 setup : function( doc, html, head, body ) 458 { 459 var val = body.getComputedStyle( 'background-color' ) || ''; 460 this.setValue( val == 'transparent' ? '' : val ); 461 }, 462 commit : function( doc, html, head, body, isPreview ) 463 { 464 if ( this.isChanged() || isPreview ) 465 { 466 body.removeAttribute( 'bgcolor' ); 467 var val = this.getValue(); 468 if ( val ) 469 body.setStyle( 'background-color', val ); 470 else 471 resetStyle( body, 'background-color', 'transparent' ); 472 } 473 } 474 }), 475 { 476 type : 'hbox', 477 widths : [ '60%', '40%' ], 478 padding : 1, 479 children : [ 480 { 481 type : 'text', 482 id : 'bgImage', 483 label : lang.bgImage, 484 setup : function( doc, html, head, body ) 485 { 486 var val = body.getComputedStyle( 'background-image' ) || ''; 487 if ( val == 'none' ) 488 val = ''; 489 else 490 { 491 val = val.replace( /url\(\s*(["']?)\s*([^\)]*)\s*\1\s*\)/i, function( match, quote, url ) 492 { 493 return url; 494 }); 495 } 496 this.setValue( val ); 497 }, 498 commit : function( doc, html, head, body ) 499 { 500 body.removeAttribute( 'background' ); 501 var val = this.getValue(); 502 if ( val ) 503 body.setStyle( 'background-image', 'url(' + val + ')' ); 504 else 505 resetStyle( body, 'background-image', 'none' ); 506 } 507 }, 508 { 509 type : 'button', 510 id : 'bgImageChoose', 511 label : langCommon.browseServer, 512 style : 'display:inline-block;margin-top:10px;', 513 hidden : true, 514 filebrowser : 'design:bgImage' 515 } 516 ] 517 }, 518 { 519 type : 'checkbox', 520 id : 'bgFixed', 521 label : lang.bgFixed, 522 setup : function( doc, html, head, body ) 523 { 524 this.setValue( body.getComputedStyle( 'background-attachment' ) == 'fixed' ); 525 }, 526 commit : function( doc, html, head, body ) 527 { 528 if ( this.getValue() ) 529 body.setStyle( 'background-attachment', 'fixed' ); 530 else 531 resetStyle( body, 'background-attachment', 'scroll' ); 532 } 533 } 534 ] 535 }, 536 { 537 type : 'vbox', 538 children : [ 539 { 540 type : 'html', 541 id : 'marginTitle', 542 html : '<div style="text-align: center; margin: 0px auto; font-weight: bold">' + lang.margin + '</div>' 543 }, 544 { 545 type : 'text', 546 id : 'marginTop', 547 label : lang.marginTop, 548 style : 'width: 80px; text-align: center', 549 align : 'center', 550 inputStyle : 'text-align: center', 551 setup : function( doc, html, head, body ) 552 { 553 this.setValue( body.getStyle( 'margin-top' ) || body.getAttribute( 'margintop' ) || '' ); 554 }, 555 commit : commitMargin( 'top' ) 556 }, 557 { 558 type : 'hbox', 559 children : [ 560 { 561 type : 'text', 562 id : 'marginLeft', 563 label : lang.marginLeft, 564 style : 'width: 80px; text-align: center', 565 align : 'center', 566 inputStyle : 'text-align: center', 567 setup : function( doc, html, head, body ) 568 { 569 this.setValue( body.getStyle( 'margin-left' ) || body.getAttribute( 'marginleft' ) || '' ); 570 }, 571 commit : commitMargin( 'left' ) 572 }, 573 { 574 type : 'text', 575 id : 'marginRight', 576 label : lang.marginRight, 577 style : 'width: 80px; text-align: center', 578 align : 'center', 579 inputStyle : 'text-align: center', 580 setup : function( doc, html, head, body ) 581 { 582 this.setValue( body.getStyle( 'margin-right' ) || body.getAttribute( 'marginright' ) || '' ); 583 }, 584 commit : commitMargin( 'right' ) 585 } 586 ] 587 }, 588 { 589 type : 'text', 590 id : 'marginBottom', 591 label : lang.marginBottom, 592 style : 'width: 80px; text-align: center', 593 align : 'center', 594 inputStyle : 'text-align: center', 595 setup : function( doc, html, head, body ) 596 { 597 this.setValue( body.getStyle( 'margin-bottom' ) || body.getAttribute( 'marginbottom' ) || '' ); 598 }, 599 commit : commitMargin( 'bottom' ) 600 } 601 ] 602 } 603 ] 604 } 605 ] 606 }, 607 { 608 id : 'meta', 609 label : lang.meta, 610 elements : [ 611 { 612 type : 'textarea', 613 id : 'metaKeywords', 614 label : lang.metaKeywords, 615 setup : setupMeta( 'keywords' ), 616 commit : commitMeta( 'keywords' ) 617 }, 618 { 619 type : 'textarea', 620 id : 'metaDescription', 621 label : lang.metaDescription, 622 setup : setupMeta( 'description' ), 623 commit : commitMeta( 'description' ) 624 }, 625 { 626 type : 'text', 627 id : 'metaAuthor', 628 label : lang.metaAuthor, 629 setup : setupMeta( 'author' ), 630 commit : commitMeta( 'author' ) 631 }, 632 { 633 type : 'text', 634 id : 'metaCopyright', 635 label : lang.metaCopyright, 636 setup : setupMeta( 'copyright' ), 637 commit : commitMeta( 'copyright' ) 638 } 639 ] 640 }, 641 { 642 id : 'preview', 643 label : langCommon.preview, 644 elements : [ 645 { 646 type : 'html', 647 id : 'previewHtml', 648 html : '<iframe src="' + previewSrc + '" style="width: 100%; height: 310px" hidefocus="true" frameborder="0" ' + 649 'id="cke_docProps_preview_iframe"></iframe>', 650 onLoad : function() 651 { 652 this.getDialog().on( 'selectPage', function( ev ) 653 { 654 if ( ev.data.page == 'preview' ) 655 { 656 var self = this; 657 setTimeout( function() 658 { 659 var doc = CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getFrameDocument(), 660 html = doc.getElementsByTag( 'html' ).getItem( 0 ), 661 head = doc.getHead(), 662 body = doc.getBody(); 663 self.commitContent( doc, html, head, body, 1 ); 664 }, 50 ); 665 } 666 }); 667 CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getAscendant( 'table' ).setStyle( 'height', '100%' ); 668 } 669 } 670 ] 671 } 672 ] 673 }; 674 }); 675