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 	/*
  9 	 * It is possible to set things in three different places.
 10 	 * 1. As attributes in the object tag.
 11 	 * 2. As param tags under the object tag.
 12 	 * 3. As attributes in the embed tag.
 13 	 * It is possible for a single attribute to be present in more than one place.
 14 	 * So let's define a mapping between a sementic attribute and its syntactic
 15 	 * equivalents.
 16 	 * Then we'll set and retrieve attribute values according to the mapping,
 17 	 * instead of having to check and set each syntactic attribute every time.
 18 	 *
 19 	 * Reference: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701
 20 	 */
 21 	var ATTRTYPE_OBJECT = 1,
 22 		ATTRTYPE_PARAM = 2,
 23 		ATTRTYPE_EMBED = 4;
 24 
 25 	var attributesMap =
 26 	{
 27 		id : [ { type : ATTRTYPE_OBJECT, name :  'id' } ],
 28 		classid : [ { type : ATTRTYPE_OBJECT, name : 'classid' } ],
 29 		codebase : [ { type : ATTRTYPE_OBJECT, name : 'codebase'} ],
 30 		pluginspage : [ { type : ATTRTYPE_EMBED, name : 'pluginspage' } ],
 31 		src : [ { type : ATTRTYPE_PARAM, name : 'movie' }, { type : ATTRTYPE_EMBED, name : 'src' }, { type : ATTRTYPE_OBJECT, name :  'data' } ],
 32 		name : [ { type : ATTRTYPE_EMBED, name : 'name' } ],
 33 		align : [ { type : ATTRTYPE_OBJECT, name : 'align' } ],
 34 		'class' : [ { type : ATTRTYPE_OBJECT, name : 'class' }, { type : ATTRTYPE_EMBED, name : 'class'} ],
 35 		width : [ { type : ATTRTYPE_OBJECT, name : 'width' }, { type : ATTRTYPE_EMBED, name : 'width' } ],
 36 		height : [ { type : ATTRTYPE_OBJECT, name : 'height' }, { type : ATTRTYPE_EMBED, name : 'height' } ],
 37 		hSpace : [ { type : ATTRTYPE_OBJECT, name : 'hSpace' }, { type : ATTRTYPE_EMBED, name : 'hSpace' } ],
 38 		vSpace : [ { type : ATTRTYPE_OBJECT, name : 'vSpace' }, { type : ATTRTYPE_EMBED, name : 'vSpace' } ],
 39 		style : [ { type : ATTRTYPE_OBJECT, name : 'style' }, { type : ATTRTYPE_EMBED, name : 'style' } ],
 40 		type : [ { type : ATTRTYPE_EMBED, name : 'type' } ]
 41 	};
 42 
 43 	var names = [ 'play', 'loop', 'menu', 'quality', 'scale', 'salign', 'wmode', 'bgcolor', 'base', 'flashvars', 'allowScriptAccess',
 44 		'allowFullScreen' ];
 45 	for ( var i = 0 ; i < names.length ; i++ )
 46 		attributesMap[ names[i] ] = [ { type : ATTRTYPE_EMBED, name : names[i] }, { type : ATTRTYPE_PARAM, name : names[i] } ];
 47 	names = [ 'allowFullScreen', 'play', 'loop', 'menu' ];
 48 	for ( i = 0 ; i < names.length ; i++ )
 49 		attributesMap[ names[i] ][0]['default'] = attributesMap[ names[i] ][1]['default'] = true;
 50 
 51 	var defaultToPixel = CKEDITOR.tools.cssLength;
 52 
 53 	function loadValue( objectNode, embedNode, paramMap )
 54 	{
 55 		var attributes = attributesMap[ this.id ];
 56 		if ( !attributes )
 57 			return;
 58 
 59 		var isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox );
 60 		for ( var i = 0 ; i < attributes.length ; i++ )
 61 		{
 62 			var attrDef = attributes[ i ];
 63 			switch ( attrDef.type )
 64 			{
 65 				case ATTRTYPE_OBJECT:
 66 					if ( !objectNode )
 67 						continue;
 68 					if ( objectNode.getAttribute( attrDef.name ) !== null )
 69 					{
 70 						var value = objectNode.getAttribute( attrDef.name );
 71 						if ( isCheckbox )
 72 							this.setValue( value.toLowerCase() == 'true' );
 73 						else
 74 							this.setValue( value );
 75 						return;
 76 					}
 77 					else if ( isCheckbox )
 78 						this.setValue( !!attrDef[ 'default' ] );
 79 					break;
 80 				case ATTRTYPE_PARAM:
 81 					if ( !objectNode )
 82 						continue;
 83 					if ( attrDef.name in paramMap )
 84 					{
 85 						value = paramMap[ attrDef.name ];
 86 						if ( isCheckbox )
 87 							this.setValue( value.toLowerCase() == 'true' );
 88 						else
 89 							this.setValue( value );
 90 						return;
 91 					}
 92 					else if ( isCheckbox )
 93 						this.setValue( !!attrDef[ 'default' ] );
 94 					break;
 95 				case ATTRTYPE_EMBED:
 96 					if ( !embedNode )
 97 						continue;
 98 					if ( embedNode.getAttribute( attrDef.name ) )
 99 					{
100 						value = embedNode.getAttribute( attrDef.name );
101 						if ( isCheckbox )
102 							this.setValue( value.toLowerCase() == 'true' );
103 						else
104 							this.setValue( value );
105 						return;
106 					}
107 					else if ( isCheckbox )
108 						this.setValue( !!attrDef[ 'default' ] );
109 			}
110 		}
111 	}
112 
113 	function commitValue( objectNode, embedNode, paramMap )
114 	{
115 		var attributes = attributesMap[ this.id ];
116 		if ( !attributes )
117 			return;
118 
119 		var isRemove = ( this.getValue() === '' ),
120 			isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox );
121 
122 		for ( var i = 0 ; i < attributes.length ; i++ )
123 		{
124 			var attrDef = attributes[i];
125 			switch ( attrDef.type )
126 			{
127 				case ATTRTYPE_OBJECT:
128 					// Avoid applying the data attribute when not needed (#7733)
129 					if ( !objectNode || ( attrDef.name == 'data' && embedNode && !objectNode.hasAttribute( 'data' ) ) )
130 						continue;
131 					var value = this.getValue();
132 					if ( isRemove || isCheckbox && value === attrDef[ 'default' ] )
133 						objectNode.removeAttribute( attrDef.name );
134 					else
135 						objectNode.setAttribute( attrDef.name, value );
136 					break;
137 				case ATTRTYPE_PARAM:
138 					if ( !objectNode )
139 						continue;
140 					value = this.getValue();
141 					if ( isRemove || isCheckbox && value === attrDef[ 'default' ] )
142 					{
143 						if ( attrDef.name in paramMap )
144 							paramMap[ attrDef.name ].remove();
145 					}
146 					else
147 					{
148 						if ( attrDef.name in paramMap )
149 							paramMap[ attrDef.name ].setAttribute( 'value', value );
150 						else
151 						{
152 							var param = CKEDITOR.dom.element.createFromHtml( '<cke:param></cke:param>', objectNode.getDocument() );
153 							param.setAttributes( { name : attrDef.name, value : value } );
154 							if ( objectNode.getChildCount() < 1 )
155 								param.appendTo( objectNode );
156 							else
157 								param.insertBefore( objectNode.getFirst() );
158 						}
159 					}
160 					break;
161 				case ATTRTYPE_EMBED:
162 					if ( !embedNode )
163 						continue;
164 					value = this.getValue();
165 					if ( isRemove || isCheckbox && value === attrDef[ 'default' ])
166 						embedNode.removeAttribute( attrDef.name );
167 					else
168 						embedNode.setAttribute( attrDef.name, value );
169 			}
170 		}
171 	}
172 
173 	CKEDITOR.dialog.add( 'flash', function( editor )
174 	{
175 		var makeObjectTag = !editor.config.flashEmbedTagOnly,
176 			makeEmbedTag = editor.config.flashAddEmbedTag || editor.config.flashEmbedTagOnly;
177 
178 		var previewPreloader,
179 			previewAreaHtml = '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) +'<br>' +
180 			'<div id="cke_FlashPreviewLoader' + CKEDITOR.tools.getNextNumber() + '" style="display:none"><div class="loading"> </div></div>' +
181 			'<div id="cke_FlashPreviewBox' + CKEDITOR.tools.getNextNumber() + '" class="FlashPreviewBox"></div></div>';
182 
183 		return {
184 			title : editor.lang.flash.title,
185 			minWidth : 420,
186 			minHeight : 310,
187 			onShow : function()
188 			{
189 				// Clear previously saved elements.
190 				this.fakeImage = this.objectNode = this.embedNode = null;
191 				previewPreloader = new CKEDITOR.dom.element( 'embed', editor.document );
192 
193 				// Try to detect any embed or object tag that has Flash parameters.
194 				var fakeImage = this.getSelectedElement();
195 				if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'flash' )
196 				{
197 					this.fakeImage = fakeImage;
198 
199 					var realElement = editor.restoreRealElement( fakeImage ),
200 						objectNode = null, embedNode = null, paramMap = {};
201 					if ( realElement.getName() == 'cke:object' )
202 					{
203 						objectNode = realElement;
204 						var embedList = objectNode.getElementsByTag( 'embed', 'cke' );
205 						if ( embedList.count() > 0 )
206 							embedNode = embedList.getItem( 0 );
207 						var paramList = objectNode.getElementsByTag( 'param', 'cke' );
208 						for ( var i = 0, length = paramList.count() ; i < length ; i++ )
209 						{
210 							var item = paramList.getItem( i ),
211 								name = item.getAttribute( 'name' ),
212 								value = item.getAttribute( 'value' );
213 							paramMap[ name ] = value;
214 						}
215 					}
216 					else if ( realElement.getName() == 'cke:embed' )
217 						embedNode = realElement;
218 
219 					this.objectNode = objectNode;
220 					this.embedNode = embedNode;
221 
222 					this.setupContent( objectNode, embedNode, paramMap, fakeImage );
223 				}
224 			},
225 			onOk : function()
226 			{
227 				// If there's no selected object or embed, create one. Otherwise, reuse the
228 				// selected object and embed nodes.
229 				var objectNode = null,
230 					embedNode = null,
231 					paramMap = null;
232 				if ( !this.fakeImage )
233 				{
234 					if ( makeObjectTag )
235 					{
236 						objectNode = CKEDITOR.dom.element.createFromHtml( '<cke:object></cke:object>', editor.document );
237 						var attributes = {
238 							classid : 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
239 							codebase : 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
240 						};
241 						objectNode.setAttributes( attributes );
242 					}
243 					if ( makeEmbedTag )
244 					{
245 						embedNode = CKEDITOR.dom.element.createFromHtml( '<cke:embed></cke:embed>', editor.document );
246 						embedNode.setAttributes(
247 							{
248 								type : 'application/x-shockwave-flash',
249 								pluginspage : 'http://www.macromedia.com/go/getflashplayer'
250 							} );
251 						if ( objectNode )
252 							embedNode.appendTo( objectNode );
253 					}
254 				}
255 				else
256 				{
257 					objectNode = this.objectNode;
258 					embedNode = this.embedNode;
259 				}
260 
261 				// Produce the paramMap if there's an object tag.
262 				if ( objectNode )
263 				{
264 					paramMap = {};
265 					var paramList = objectNode.getElementsByTag( 'param', 'cke' );
266 					for ( var i = 0, length = paramList.count() ; i < length ; i++ )
267 						paramMap[ paramList.getItem( i ).getAttribute( 'name' ) ] = paramList.getItem( i );
268 				}
269 
270 				// A subset of the specified attributes/styles
271 				// should also be applied on the fake element to
272 				// have better visual effect. (#5240)
273 				var extraStyles = {}, extraAttributes = {};
274 				this.commitContent( objectNode, embedNode, paramMap, extraStyles, extraAttributes );
275 
276 				// Refresh the fake image.
277 				var newFakeImage = editor.createFakeElement( objectNode || embedNode, 'cke_flash', 'flash', true );
278 				newFakeImage.setAttributes( extraAttributes );
279 				newFakeImage.setStyles( extraStyles );
280 				if ( this.fakeImage )
281 				{
282 					newFakeImage.replace( this.fakeImage );
283 					editor.getSelection().selectElement( newFakeImage );
284 				}
285 				else
286 					editor.insertElement( newFakeImage );
287 			},
288 
289 			onHide : function()
290 			{
291 				if ( this.preview )
292 					this.preview.setHtml('');
293 			},
294 
295 			contents : [
296 				{
297 					id : 'info',
298 					label : editor.lang.common.generalTab,
299 					accessKey : 'I',
300 					elements :
301 					[
302 						{
303 							type : 'vbox',
304 							padding : 0,
305 							children :
306 							[
307 								{
308 									type : 'hbox',
309 									widths : [ '280px', '110px' ],
310 									align : 'right',
311 									children :
312 									[
313 										{
314 											id : 'src',
315 											type : 'text',
316 											label : editor.lang.common.url,
317 											required : true,
318 											validate : CKEDITOR.dialog.validate.notEmpty( editor.lang.flash.validateSrc ),
319 											setup : loadValue,
320 											commit : commitValue,
321 											onLoad : function()
322 											{
323 												var dialog = this.getDialog(),
324 												updatePreview = function( src ){
325 													// Query the preloader to figure out the url impacted by based href.
326 													previewPreloader.setAttribute( 'src', src );
327 													dialog.preview.setHtml( '<embed height="100%" width="100%" src="'
328 														+ CKEDITOR.tools.htmlEncode( previewPreloader.getAttribute( 'src' ) )
329 														+ '" type="application/x-shockwave-flash"></embed>' );
330 												};
331 												// Preview element
332 												dialog.preview = dialog.getContentElement( 'info', 'preview' ).getElement().getChild( 3 );
333 
334 												// Sync on inital value loaded.
335 												this.on( 'change', function( evt ){
336 
337 														if ( evt.data && evt.data.value )
338 															updatePreview( evt.data.value );
339 													} );
340 												// Sync when input value changed.
341 												this.getInputElement().on( 'change', function( evt ){
342 
343 													updatePreview( this.getValue() );
344 												}, this );
345 											}
346 										},
347 										{
348 											type : 'button',
349 											id : 'browse',
350 											filebrowser : 'info:src',
351 											hidden : true,
352 											// v-align with the 'src' field.
353 											// TODO: We need something better than a fixed size here.
354 											style : 'display:inline-block;margin-top:10px;',
355 											label : editor.lang.common.browseServer
356 										}
357 									]
358 								}
359 							]
360 						},
361 						{
362 							type : 'hbox',
363 							widths : [ '25%', '25%', '25%', '25%', '25%' ],
364 							children :
365 							[
366 								{
367 									type : 'text',
368 									id : 'width',
369 									style : 'width:95px',
370 									label : editor.lang.common.width,
371 									validate : CKEDITOR.dialog.validate.htmlLength( editor.lang.common.invalidHtmlLength.replace( '%1', editor.lang.common.width ) ),
372 									setup : loadValue,
373 									commit : commitValue
374 								},
375 								{
376 									type : 'text',
377 									id : 'height',
378 									style : 'width:95px',
379 									label : editor.lang.common.height,
380 									validate : CKEDITOR.dialog.validate.htmlLength( editor.lang.common.invalidHtmlLength.replace( '%1', editor.lang.common.height ) ),
381 									setup : loadValue,
382 									commit : commitValue
383 								},
384 								{
385 									type : 'text',
386 									id : 'hSpace',
387 									style : 'width:95px',
388 									label : editor.lang.flash.hSpace,
389 									validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateHSpace ),
390 									setup : loadValue,
391 									commit : commitValue
392 								},
393 								{
394 									type : 'text',
395 									id : 'vSpace',
396 									style : 'width:95px',
397 									label : editor.lang.flash.vSpace,
398 									validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateVSpace ),
399 									setup : loadValue,
400 									commit : commitValue
401 								}
402 							]
403 						},
404 
405 						{
406 							type : 'vbox',
407 							children :
408 							[
409 								{
410 									type : 'html',
411 									id : 'preview',
412 									style : 'width:95%;',
413 									html : previewAreaHtml
414 								}
415 							]
416 						}
417 					]
418 				},
419 				{
420 					id : 'Upload',
421 					hidden : true,
422 					filebrowser : 'uploadButton',
423 					label : editor.lang.common.upload,
424 					elements :
425 					[
426 						{
427 							type : 'file',
428 							id : 'upload',
429 							label : editor.lang.common.upload,
430 							size : 38
431 						},
432 						{
433 							type : 'fileButton',
434 							id : 'uploadButton',
435 							label : editor.lang.common.uploadSubmit,
436 							filebrowser : 'info:src',
437 							'for' : [ 'Upload', 'upload' ]
438 						}
439 					]
440 				},
441 				{
442 					id : 'properties',
443 					label : editor.lang.flash.propertiesTab,
444 					elements :
445 					[
446 						{
447 							type : 'hbox',
448 							widths : [ '50%', '50%' ],
449 							children :
450 							[
451 								{
452 									id : 'scale',
453 									type : 'select',
454 									label : editor.lang.flash.scale,
455 									'default' : '',
456 									style : 'width : 100%;',
457 									items :
458 									[
459 										[ editor.lang.common.notSet , ''],
460 										[ editor.lang.flash.scaleAll, 'showall' ],
461 										[ editor.lang.flash.scaleNoBorder, 'noborder' ],
462 										[ editor.lang.flash.scaleFit, 'exactfit' ]
463 									],
464 									setup : loadValue,
465 									commit : commitValue
466 								},
467 								{
468 									id : 'allowScriptAccess',
469 									type : 'select',
470 									label : editor.lang.flash.access,
471 									'default' : '',
472 									style : 'width : 100%;',
473 									items :
474 									[
475 										[ editor.lang.common.notSet , ''],
476 										[ editor.lang.flash.accessAlways, 'always' ],
477 										[ editor.lang.flash.accessSameDomain, 'samedomain' ],
478 										[ editor.lang.flash.accessNever, 'never' ]
479 									],
480 									setup : loadValue,
481 									commit : commitValue
482 								}
483 							]
484 						},
485 						{
486 							type : 'hbox',
487 							widths : [ '50%', '50%' ],
488 							children :
489 							[
490 								{
491 									id : 'wmode',
492 									type : 'select',
493 									label : editor.lang.flash.windowMode,
494 									'default' : '',
495 									style : 'width : 100%;',
496 									items :
497 									[
498 										[ editor.lang.common.notSet , '' ],
499 										[ editor.lang.flash.windowModeWindow, 'window' ],
500 										[ editor.lang.flash.windowModeOpaque, 'opaque' ],
501 										[ editor.lang.flash.windowModeTransparent, 'transparent' ]
502 									],
503 									setup : loadValue,
504 									commit : commitValue
505 								},
506 								{
507 									id : 'quality',
508 									type : 'select',
509 									label : editor.lang.flash.quality,
510 									'default' : 'high',
511 									style : 'width : 100%;',
512 									items :
513 									[
514 										[ editor.lang.common.notSet , '' ],
515 										[ editor.lang.flash.qualityBest, 'best' ],
516 										[ editor.lang.flash.qualityHigh, 'high' ],
517 										[ editor.lang.flash.qualityAutoHigh, 'autohigh' ],
518 										[ editor.lang.flash.qualityMedium, 'medium' ],
519 										[ editor.lang.flash.qualityAutoLow, 'autolow' ],
520 										[ editor.lang.flash.qualityLow, 'low' ]
521 									],
522 									setup : loadValue,
523 									commit : commitValue
524 								}
525 							]
526 						},
527 						{
528 							type : 'hbox',
529 							widths : [ '50%', '50%' ],
530 							children :
531 							[
532 								{
533 									id : 'align',
534 									type : 'select',
535 									label : editor.lang.common.align,
536 									'default' : '',
537 									style : 'width : 100%;',
538 									items :
539 									[
540 										[ editor.lang.common.notSet , ''],
541 										[ editor.lang.common.alignLeft , 'left'],
542 										[ editor.lang.flash.alignAbsBottom , 'absBottom'],
543 										[ editor.lang.flash.alignAbsMiddle , 'absMiddle'],
544 										[ editor.lang.flash.alignBaseline , 'baseline'],
545 										[ editor.lang.common.alignBottom , 'bottom'],
546 										[ editor.lang.common.alignMiddle , 'middle'],
547 										[ editor.lang.common.alignRight , 'right'],
548 										[ editor.lang.flash.alignTextTop , 'textTop'],
549 										[ editor.lang.common.alignTop , 'top']
550 									],
551 									setup : loadValue,
552 									commit : function( objectNode, embedNode, paramMap, extraStyles, extraAttributes )
553 									{
554 										var value = this.getValue();
555 										commitValue.apply( this, arguments );
556 										value && ( extraAttributes.align = value );
557 									}
558 								},
559 								{
560 									type : 'html',
561 									html : '<div></div>'
562 								}
563 							]
564 						},
565 						{
566 							type : 'fieldset',
567 							label : CKEDITOR.tools.htmlEncode( editor.lang.flash.flashvars ),
568 							children :
569 							[
570 								{
571 									type : 'vbox',
572 									padding : 0,
573 									children :
574 									[
575 										{
576 											type : 'checkbox',
577 											id : 'menu',
578 											label : editor.lang.flash.chkMenu,
579 											'default' : true,
580 											setup : loadValue,
581 											commit : commitValue
582 										},
583 										{
584 											type : 'checkbox',
585 											id : 'play',
586 											label : editor.lang.flash.chkPlay,
587 											'default' : true,
588 											setup : loadValue,
589 											commit : commitValue
590 										},
591 										{
592 											type : 'checkbox',
593 											id : 'loop',
594 											label : editor.lang.flash.chkLoop,
595 											'default' : true,
596 											setup : loadValue,
597 											commit : commitValue
598 										},
599 										{
600 											type : 'checkbox',
601 											id : 'allowFullScreen',
602 											label : editor.lang.flash.chkFull,
603 											'default' : true,
604 											setup : loadValue,
605 											commit : commitValue
606 										}
607 									]
608 								}
609 							]
610 						}
611 					]
612 				},
613 				{
614 					id : 'advanced',
615 					label : editor.lang.common.advancedTab,
616 					elements :
617 					[
618 						{
619 							type : 'hbox',
620 							children :
621 							[
622 								{
623 									type : 'text',
624 									id : 'id',
625 									label : editor.lang.common.id,
626 									setup : loadValue,
627 									commit : commitValue
628 								}
629 							]
630 						},
631 						{
632 							type : 'hbox',
633 							widths : [ '45%', '55%' ],
634 							children :
635 							[
636 								{
637 									type : 'text',
638 									id : 'bgcolor',
639 									label : editor.lang.flash.bgcolor,
640 									setup : loadValue,
641 									commit : commitValue
642 								},
643 								{
644 									type : 'text',
645 									id : 'class',
646 									label : editor.lang.common.cssClass,
647 									setup : loadValue,
648 									commit : commitValue
649 								}
650 							]
651 						},
652 						{
653 							type : 'text',
654 							id : 'style',
655 							validate : CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
656 							label : editor.lang.common.cssStyle,
657 							setup : loadValue,
658 							commit : commitValue
659 						}
660 					]
661 				}
662 			]
663 		};
664 	} );
665 })();
666