Bugfix : prendre le innerHTML du body du document n'est pas une bonne idée, dans...
[ckeditor.git] / skins / ckeditor / _source / plugins / docprops / dialogs / docprops.js
1 /*
2 Copyright (c) 2003-2011, 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; margin: 0px auto',
549 inputStyle : 'text-align: center',
550 setup : function( doc, html, head, body )
551 {
552 this.setValue( body.getStyle( 'margin-top' ) || body.getAttribute( 'margintop' ) || '' );
553 },
554 commit : commitMargin( 'top' ),
555 onLoad : function()
556 {
557 this.getElement().getParent().setStyle( 'text-align', 'center' );
558 }
559 },
560 {
561 type : 'hbox',
562 children : [
563 {
564 type : 'text',
565 id : 'marginLeft',
566 label : lang.marginLeft,
567 style : 'width: 80px; text-align: center; margin: 0px auto',
568 inputStyle : 'text-align: center',
569 setup : function( doc, html, head, body )
570 {
571 this.setValue( body.getStyle( 'margin-left' ) || body.getAttribute( 'marginleft' ) || '' );
572 },
573 commit : commitMargin( 'left' ),
574 onLoad : function()
575 {
576 this.getElement().getParent().setStyle( 'text-align', 'center' );
577 }
578 },
579 {
580 type : 'text',
581 id : 'marginRight',
582 label : lang.marginRight,
583 style : 'width: 80px; text-align: center; margin: 0px auto',
584 inputStyle : 'text-align: center',
585 setup : function( doc, html, head, body )
586 {
587 this.setValue( body.getStyle( 'margin-right' ) || body.getAttribute( 'marginright' ) || '' );
588 },
589 commit : commitMargin( 'right' ),
590 onLoad : function()
591 {
592 this.getElement().getParent().setStyle( 'text-align', 'center' );
593 }
594 }
595 ]
596 },
597 {
598 type : 'text',
599 id : 'marginBottom',
600 label : lang.marginBottom,
601 style : 'width: 80px; text-align: center; margin: 0px auto',
602 inputStyle : 'text-align: center',
603 setup : function( doc, html, head, body )
604 {
605 this.setValue( body.getStyle( 'margin-bottom' ) || body.getAttribute( 'marginbottom' ) || '' );
606 },
607 commit : commitMargin( 'bottom' ),
608 onLoad : function()
609 {
610 this.getElement().getParent().setStyle( 'text-align', 'center' );
611 }
612 }
613 ]
614 }
615 ]
616 }
617 ]
618 },
619 {
620 id : 'meta',
621 label : lang.meta,
622 elements : [
623 {
624 type : 'textarea',
625 id : 'metaKeywords',
626 label : lang.metaKeywords,
627 setup : setupMeta( 'keywords' ),
628 commit : commitMeta( 'keywords' )
629 },
630 {
631 type : 'textarea',
632 id : 'metaDescription',
633 label : lang.metaDescription,
634 setup : setupMeta( 'description' ),
635 commit : commitMeta( 'description' )
636 },
637 {
638 type : 'text',
639 id : 'metaAuthor',
640 label : lang.metaAuthor,
641 setup : setupMeta( 'author' ),
642 commit : commitMeta( 'author' )
643 },
644 {
645 type : 'text',
646 id : 'metaCopyright',
647 label : lang.metaCopyright,
648 setup : setupMeta( 'copyright' ),
649 commit : commitMeta( 'copyright' )
650 }
651 ]
652 },
653 {
654 id : 'preview',
655 label : langCommon.preview,
656 elements : [
657 {
658 type : 'html',
659 id : 'previewHtml',
660 html : '<iframe src="' + previewSrc + '" style="width: 100%; height: 310px" hidefocus="true" frameborder="0" ' +
661 'id="cke_docProps_preview_iframe"></iframe>',
662 onLoad : function()
663 {
664 this.getDialog().on( 'selectPage', function( ev )
665 {
666 if ( ev.data.page == 'preview' )
667 {
668 var self = this;
669 setTimeout( function()
670 {
671 var doc = CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getFrameDocument(),
672 html = doc.getElementsByTag( 'html' ).getItem( 0 ),
673 head = doc.getHead(),
674 body = doc.getBody();
675 self.commitContent( doc, html, head, body, 1 );
676 }, 50 );
677 }
678 });
679 CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getAscendant( 'table' ).setStyle( 'height', '100%' );
680 }
681 }
682 ]
683 }
684 ]
685 };
686 });