1 /*
  2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5
  6 CKEDITOR.dialog.add( 'link', function( editor )
  7 {
  8 	// Handles the event when the "Target" selection box is changed.
  9 	var targetChanged = function()
 10 	{
 11 		var dialog = this.getDialog(),
 12 			popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ),
 13 			targetName = dialog.getContentElement( 'target', 'linkTargetName' ),
 14 			value = this.getValue();
 15
 16 		if ( !popupFeatures || !targetName )
 17 			return;
 18
 19 		popupFeatures = popupFeatures.getElement();
 20
 21 		if ( value == 'popup' )
 22 		{
 23 			popupFeatures.show();
 24 			targetName.setLabel( editor.lang.link.targetPopupName );
 25 		}
 26 		else
 27 		{
 28 			popupFeatures.hide();
 29 			targetName.setLabel( editor.lang.link.targetFrameName );
 30 			this.getDialog().setValueOf( 'target', 'linkTargetName', value.charAt( 0 ) == '_' ? value : '' );
 31 		}
 32 	};
 33
 34 	// Handles the event when the "Type" selection box is changed.
 35 	var linkTypeChanged = function()
 36 	{
 37 		var dialog = this.getDialog(),
 38 			partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
 39 			typeValue = this.getValue();
 40 		if ( typeValue == 'url' )
 41 		{
 42 			if ( editor.config.linkShowTargetTab )
 43 				dialog.showPage( 'target' );
 44 			if ( editor.config.linkUploadTab )
 45 				dialog.showPage( 'upload' );
 46 		}
 47 		else
 48 		{
 49 			dialog.hidePage( 'target' );
 50 			dialog.hidePage( 'upload' );
 51 		}
 52
 53 		for ( var i = 0 ; i < partIds.length ; i++ )
 54 		{
 55 			var element = dialog.getContentElement( 'info', partIds[i] );
 56 			if ( !element )
 57 				continue;
 58
 59 			element = element.getElement().getParent().getParent();
 60 			if ( partIds[i] == typeValue + 'Options' )
 61 				element.show();
 62 			else
 63 				element.hide();
 64 		}
 65 	};
 66
 67 	// Loads the parameters in a selected link to the link dialog fields.
 68 	var emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
 69 		emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,
 70 		emailBodyRegex = /body=([^;?:@&=$,\/]*)/,
 71 		anchorRegex = /^#(.*)$/,
 72 		urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
 73 		selectableTargets = /^(_(?:self|top|parent|blank))$/;
 74
 75 	var popupRegex =
 76 		/\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
 77 	var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
 78
 79 	var parseLink = function( editor, element )
 80 	{
 81 		var href = element ? ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) : '',
 82 			emailMatch = '',
 83 			anchorMatch = '',
 84 			urlMatch = false,
 85 			retval = {};
 86
 87 		if ( href != null )
 88 		{
 89 			emailMatch = href.match( emailRegex );
 90 			anchorMatch = href.match( anchorRegex );
 91 			urlMatch = href.match( urlRegex );
 92 		}
 93
 94 		// Load the link type and URL.
 95 		if ( emailMatch )
 96 		{
 97 			var subjectMatch = href.match( emailSubjectRegex ),
 98 				bodyMatch = href.match( emailBodyRegex );
 99 			retval.type = 'email';
100 			retval.email = {};
101 			retval.email.address = emailMatch[1];
102 			subjectMatch && ( retval.email.subject = decodeURIComponent( subjectMatch[1] ) );
103 			bodyMatch && ( retval.email.body = decodeURIComponent( bodyMatch[1] ) );
104 		}
105 		else if ( anchorMatch )
106 		{
107 			retval.type = 'anchor';
108 			retval.anchor = {};
109 			retval.anchor.name = retval.anchor.id = anchorMatch[1];
110 		}
111 		else if ( href && urlMatch )		// urlRegex matches empty strings, so need to check for href as well.
112 		{
113 			retval.type = 'url';
114 			retval.url = {};
115 			retval.url.protocol = urlMatch[1];
116 			retval.url.url = urlMatch[2];
117 		}
118 		else
119 			retval.type = 'url';
120
121 		// Load target and popup settings.
122 		if ( element )
123 		{
124 			var target = element.getAttribute( 'target' );
125 			retval.target = {};
126 			retval.adv = {};
127
128 			// IE BUG: target attribute is an empty string instead of null in IE if it's not set.
129 			if ( !target )
130 			{
131 				var onclick = element.getAttribute( '_cke_pa_onclick' ) || element.getAttribute( 'onclick' ),
132 					onclickMatch = onclick && onclick.match( popupRegex );
133 				if ( onclickMatch )
134 				{
135 					retval.target.type = 'popup';
136 					retval.target.name = onclickMatch[1];
137
138 					var featureMatch;
139 					while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) )
140 					{
141 						if ( featureMatch[2] == 'yes' || featureMatch[2] == '1' )
142 							retval.target[ featureMatch[1] ] = true;
143 						else if ( isFinite( featureMatch[2] ) )
144 							retval.target[ featureMatch[1] ] = featureMatch[2];
145 					}
146 				}
147 			}
148 			else
149 			{
150 				var targetMatch = target.match( selectableTargets );
151 				if ( targetMatch )
152 					retval.target.type = retval.target.name = target;
153 				else
154 				{
155 					retval.target.type = 'frame';
156 					retval.target.name = target;
157 				}
158 			}
159
160 			var me = this;
161 			var advAttr = function( inputName, attrName )
162 			{
163 				var value = element.getAttribute( attrName );
164 				if ( value != null )
165 					retval.adv[ inputName ] = value || '';
166 			};
167 			advAttr( 'advId', 'id' );
168 			advAttr( 'advLangDir', 'dir' );
169 			advAttr( 'advAccessKey', 'accessKey' );
170 			advAttr( 'advName', 'name' );
171 			advAttr( 'advLangCode', 'lang' );
172 			advAttr( 'advTabIndex', 'tabindex' );
173 			advAttr( 'advTitle', 'title' );
174 			advAttr( 'advContentType', 'type' );
175 			advAttr( 'advCSSClasses', 'class' );
176 			advAttr( 'advCharset', 'charset' );
177 			advAttr( 'advStyles', 'style' );
178 		}
179
180 		// Find out whether we have any anchors in the editor.
181 		// Get all IMG elements in CK document.
182 		var elements = editor.document.$.getElementsByTagName( 'img' ),
183 			realAnchors = editor.document.$.anchors,
184 			anchors = retval.anchors = [];
185 		for( var i = 0; i < elements.length ; i++ )
186 		{
187 			var item = elements.item( i );
188 			if ( item.getAttribute( '_cke_realelement' ) && item.getAttribute( '_cke_real_element_type' ) == 'anchor' )
189 			{
190 				var domElement = new CKEDITOR.dom.element( item );
191 				domElement = editor.restoreRealElement( domElement );
192 				anchors.push( domElement );
193 			}
194 		}
195 		for ( var i = 0 ; i < realAnchors.length ; i++ )
196 			anchors.push( realAnchors[i] );
197 		for ( var i = 0, item, length = anchors.length ; i < length && ( item = anchors.shift() ) ; i++ )
198 			anchors.push( { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) } );
199
200 		// Record down the selected element in the dialog.
201 		this._.selectedElement = element;
202
203 		return retval;
204 	};
205
206 	var setupParams = function( page, data )
207 	{
208 		if ( data[page] )
209 			this.setValue( data[page][this.id] || '' );
210 	};
211
212 	var setupPopupParams = function( data )
213 	{
214 		return setupParams.call( this, 'target', data );
215 	};
216
217 	var setupAdvParams = function( data )
218 	{
219 		return setupParams.call( this, 'adv', data );
220 	};
221
222 	var commitParams = function( page, data )
223 	{
224 		if ( !data[page] )
225 			data[page] = {};
226
227 		data[page][this.id] = this.getValue() || '';
228 	};
229
230 	var commitPopupParams = function( data )
231 	{
232 		return commitParams.call( this, 'target', data );
233 	};
234
235 	var commitAdvParams = function( data )
236 	{
237 		return commitParams.call( this, 'adv', data );
238 	};
239
240 	return {
241 		title : editor.lang.link.title,
242 		minWidth : 400,
243 		minHeight : 320,
244 		contents : [
245 			{
246 				id : 'info',
247 				label : editor.lang.link.info,
248 				title : editor.lang.link.info,
249 				elements :
250 				[
251 					{
252 						id : 'linkType',
253 						type : 'select',
254 						label : editor.lang.link.type,
255 						'default' : 'url',
256 						items :
257 						[
258 							[ editor.lang.common.url, 'url' ],
259 							[ editor.lang.link.toAnchor, 'anchor' ],
260 							[ editor.lang.link.toEmail, 'email' ]
261 						],
262 						onChange : linkTypeChanged,
263 						setup : function( data )
264 						{
265 							if ( data.type )
266 								this.setValue( data.type );
267 						},
268 						commit : function( data )
269 						{
270 							data.type = this.getValue();
271 						}
272 					},
273 					{
274 						type : 'vbox',
275 						id : 'urlOptions',
276 						children :
277 						[
278 							{
279 								type : 'hbox',
280 								widths : [ '25%', '75%' ],
281 								children :
282 								[
283 									{
284 										id : 'protocol',
285 										type : 'select',
286 										label : editor.lang.common.protocol,
287 										'default' : 'http://',
288 										style : 'width : 100%;',
289 										items :
290 										[
291 											[ 'http://' ],
292 											[ 'https://' ],
293 											[ 'ftp://' ],
294 											[ 'news://' ],
295 											[ '<other>', '' ]
296 										],
297 										setup : function( data )
298 										{
299 											if ( data.url )
300 												this.setValue( data.url.protocol );
301 										},
302 										commit : function( data )
303 										{
304 											if ( !data.url )
305 												data.url = {};
306
307 											data.url.protocol = this.getValue();
308 										}
309 									},
310 									{
311 										type : 'text',
312 										id : 'url',
313 										label : editor.lang.common.url,
314 										onLoad : function ()
315 										{
316 											this.allowOnChange = true;
317 										},
318 										onKeyUp : function()
319 										{
320 											this.allowOnChange = false;
321 											var	protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),
322 												url = this.getValue(),
323 												urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/gi,
324 												urlOnChangeTestOther = /^((javascript:)|[#\/\.])/gi;
325
326 											var protocol = urlOnChangeProtocol.exec( url );
327 											if ( protocol )
328 											{
329 												this.setValue( url.substr( protocol[ 0 ].length ) );
330 												protocolCmb.setValue( protocol[ 0 ].toLowerCase() );
331 											}
332 											else if ( urlOnChangeTestOther.test( url ) )
333 												protocolCmb.setValue( '' );
334
335 											this.allowOnChange = true;
336 										},
337 										onChange : function()
338 										{
339 											if ( this.allowOnChange )		// Dont't call on dialog load.
340 												this.onKeyUp();
341 										},
342 										validate : function()
343 										{
344 											var dialog = this.getDialog();
345
346 											if ( dialog.getContentElement( 'info', 'linkType' ) &&
347 													dialog.getValueOf( 'info', 'linkType' ) != 'url' )
348 												return true;
349
350 											if ( this.getDialog().fakeObj != false )	// Edit Anchor.
351 												return true;
352
353 											var func = CKEDITOR.dialog.validate.notEmpty( editor.lang.link.noUrl );
354 											return func.apply( this );
355 										},
356 										setup : function( data )
357 										{
358 											this.allowOnChange = false;
359 											if ( data.url )
360 												this.setValue( data.url.url );
361 											this.allowOnChange = true;
362
363 											var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
364 											if ( linkType && linkType.getValue() == 'url' )
365 												this.select();
366
367 										},
368 										commit : function( data )
369 										{
370 											if ( !data.url )
371 												data.url = {};
372
373 											data.url.url = this.getValue();
374 											this.allowOnChange = false;
375 										}
376 									}
377 								],
378 								setup : function( data )
379 								{
380 									if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
381 										this.getElement().show();
382 								}
383 							},
384 							{
385 								type : 'button',
386 								id : 'browse',
387 								label : editor.lang.common.browseServer
388 							}
389 						]
390 					},
391 					{
392 						type : 'vbox',
393 						id : 'anchorOptions',
394 						width : 260,
395 						align : 'center',
396 						padding : 0,
397 						children :
398 						[
399 							{
400 								type : 'html',
401 								id : 'selectAnchorText',
402 								html : CKEDITOR.tools.htmlEncode( editor.lang.link.selectAnchor ),
403 								setup : function( data )
404 								{
405 									if ( data.anchors.length > 0 )
406 										this.getElement().show();
407 									else
408 										this.getElement().hide();
409 								}
410 							},
411 							{
412 								type : 'html',
413 								id : 'noAnchors',
414 								style : 'text-align: center;',
415 								html : '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.link.noAnchors ) + '</div>',
416 								setup : function( data )
417 								{
418 									if ( data.anchors.length < 1 )
419 										this.getElement().show();
420 									else
421 										this.getElement().hide();
422 								}
423 							},
424 							{
425 								type : 'hbox',
426 								id : 'selectAnchor',
427 								children :
428 								[
429 									{
430 										type : 'select',
431 										id : 'anchorName',
432 										'default' : '',
433 										label : editor.lang.link.anchorName,
434 										style : 'width: 100%;',
435 										items :
436 										[
437 											[ '' ]
438 										],
439 										setup : function( data )
440 										{
441 											this.clear();
442 											this.add( '' );
443 											for ( var i = 0 ; i < data.anchors.length ; i++ )
444 											{
445 												if ( data.anchors[i].name )
446 													this.add( data.anchors[i].name );
447 											}
448
449 											if ( data.anchor )
450 												this.setValue( data.anchor.name );
451
452 											var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
453 											if ( linkType && linkType.getValue() == 'email' )
454 												this.focus();
455 										},
456 										commit : function( data )
457 										{
458 											if ( !data.anchor )
459 												data.anchor = {};
460
461 											data.anchor.name = this.getValue();
462 										}
463 									},
464 									{
465 										type : 'select',
466 										id : 'anchorId',
467 										'default' : '',
468 										label : editor.lang.link.anchorId,
469 										style : 'width: 100%;',
470 										items :
471 										[
472 											[ '' ]
473 										],
474 										setup : function( data )
475 										{
476 											this.clear();
477 											this.add( '' );
478 											for ( var i = 0 ; i < data.anchors.length ; i++ )
479 											{
480 												if ( data.anchors[i].id )
481 													this.add( data.anchors[i].id );
482 											}
483
484 											if ( data.anchor )
485 												this.setValue( data.anchor.id );
486 										},
487 										commit : function( data )
488 										{
489 											if ( !data.anchor )
490 												data.anchor = {};
491
492 											data.anchor.id = this.getValue();
493 										}
494 									}
495 								],
496 								setup : function( data )
497 								{
498 									if ( data.anchors.length > 0 )
499 										this.getElement().show();
500 									else
501 										this.getElement().hide();
502 								}
503 							}
504 						],
505 						setup : function( data )
506 						{
507 							if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
508 								this.getElement().hide();
509 						}
510 					},
511 					{
512 						type :  'vbox',
513 						id : 'emailOptions',
514 						padding : 1,
515 						children :
516 						[
517 							{
518 								type : 'text',
519 								id : 'emailAddress',
520 								label : editor.lang.link.emailAddress,
521 								validate : function()
522 								{
523 									var dialog = this.getDialog();
524
525 									if ( !dialog.getContentElement( 'info', 'linkType' ) ||
526 											dialog.getValueOf( 'info', 'linkType' ) != 'email' )
527 										return true;
528
529 									var func = CKEDITOR.dialog.validate.notEmpty( editor.lang.link.noEmail );
530 									return func.apply( this );
531 								},
532 								setup : function( data )
533 								{
534 									if ( data.email )
535 										this.setValue( data.email.address );
536
537 									var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
538 									if ( linkType && linkType.getValue() == 'email' )
539 										this.select();
540 								},
541 								commit : function( data )
542 								{
543 									if ( !data.email )
544 										data.email = {};
545
546 									data.email.address = this.getValue();
547 								}
548 							},
549 							{
550 								type : 'text',
551 								id : 'emailSubject',
552 								label : editor.lang.link.emailSubject,
553 								setup : function( data )
554 								{
555 									if ( data.email )
556 										this.setValue( data.email.subject );
557 								},
558 								commit : function( data )
559 								{
560 									if ( !data.email )
561 										data.email = {};
562
563 									data.email.subject = this.getValue();
564 								}
565 							},
566 							{
567 								type : 'textarea',
568 								id : 'emailBody',
569 								label : editor.lang.link.emailBody,
570 								rows : 3,
571 								'default' : '',
572 								setup : function( data )
573 								{
574 									if ( data.email )
575 										this.setValue( data.email.body );
576 								},
577 								commit : function( data )
578 								{
579 									if ( !data.email )
580 										data.email = {};
581
582 									data.email.body = this.getValue();
583 								}
584 							}
585 						],
586 						setup : function( data )
587 						{
588 							if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
589 								this.getElement().hide();
590 						}
591 					}
592 				]
593 			},
594 			{
595 				id : 'target',
596 				label : editor.lang.link.target,
597 				title : editor.lang.link.target,
598 				elements :
599 				[
600 					{
601 						type : 'hbox',
602 						widths : [ '50%', '50%' ],
603 						children :
604 						[
605 							{
606 								type : 'select',
607 								id : 'linkTargetType',
608 								label : editor.lang.link.target,
609 								'default' : 'notSet',
610 								style : 'width : 100%;',
611 								'items' :
612 								[
613 									[ editor.lang.link.targetNotSet, 'notSet' ],
614 									[ editor.lang.link.targetFrame, 'frame' ],
615 									[ editor.lang.link.targetPopup, 'popup' ],
616 									[ editor.lang.link.targetNew, '_blank' ],
617 									[ editor.lang.link.targetTop, '_top' ],
618 									[ editor.lang.link.targetSelf, '_self' ],
619 									[ editor.lang.link.targetParent, '_parent' ]
620 								],
621 								onChange : targetChanged,
622 								setup : function( data )
623 								{
624 									if ( data.target )
625 										this.setValue( data.target.type );
626 								},
627 								commit : function( data )
628 								{
629 									if ( !data.target )
630 										data.target = {};
631
632 									data.target.type = this.getValue();
633 								}
634 							},
635 							{
636 								type : 'text',
637 								id : 'linkTargetName',
638 								label : editor.lang.link.targetFrameName,
639 								'default' : '',
640 								setup : function( data )
641 								{
642 									if ( data.target )
643 										this.setValue( data.target.name );
644 								},
645 								commit : function( data )
646 								{
647 									if ( !data.target )
648 										data.target = {};
649
650 									data.target.name = this.getValue();
651 								}
652 							}
653 						]
654 					},
655 					{
656 						type : 'vbox',
657 						width : 260,
658 						align : 'center',
659 						padding : 2,
660 						id : 'popupFeatures',
661 						children :
662 						[
663 							{
664 								type : 'html',
665 								html : CKEDITOR.tools.htmlEncode( editor.lang.link.popupFeatures )
666 							},
667 							{
668 								type : 'hbox',
669 								children :
670 								[
671 									{
672 										type : 'checkbox',
673 										id : 'resizable',
674 										label : editor.lang.link.popupResizable,
675 										setup : setupPopupParams,
676 										commit : commitPopupParams
677 									},
678 									{
679 										type : 'checkbox',
680 										id : 'status',
681 										label : editor.lang.link.popupStatusBar,
682 										setup : setupPopupParams,
683 										commit : commitPopupParams
684
685 									}
686 								]
687 							},
688 							{
689 								type : 'hbox',
690 								children :
691 								[
692 									{
693 										type : 'checkbox',
694 										id : 'location',
695 										label : editor.lang.link.popupLocationBar,
696 										setup : setupPopupParams,
697 										commit : commitPopupParams
698
699 									},
700 									{
701 										type : 'checkbox',
702 										id : 'toolbar',
703 										label : editor.lang.link.popupToolbar,
704 										setup : setupPopupParams,
705 										commit : commitPopupParams
706
707 									}
708 								]
709 							},
710 							{
711 								type : 'hbox',
712 								children :
713 								[
714 									{
715 										type : 'checkbox',
716 										id : 'menubar',
717 										label : editor.lang.link.popupMenuBar,
718 										setup : setupPopupParams,
719 										commit : commitPopupParams
720
721 									},
722 									{
723 										type : 'checkbox',
724 										id : 'fullscreen',
725 										label : editor.lang.link.popupFullScreen,
726 										setup : setupPopupParams,
727 										commit : commitPopupParams
728
729 									}
730 								]
731 							},
732 							{
733 								type : 'hbox',
734 								children :
735 								[
736 									{
737 										type : 'checkbox',
738 										id : 'scrollbars',
739 										label : editor.lang.link.popupScrollBars,
740 										setup : setupPopupParams,
741 										commit : commitPopupParams
742
743 									},
744 									{
745 										type : 'checkbox',
746 										id : 'dependent',
747 										label : editor.lang.link.popupDependent,
748 										setup : setupPopupParams,
749 										commit : commitPopupParams
750
751 									}
752 								]
753 							},
754 							{
755 								type : 'hbox',
756 								children :
757 								[
758 									{
759 										type :  'text',
760 										widths : [ '30%', '70%' ],
761 										labelLayout : 'horizontal',
762 										label : editor.lang.link.popupWidth,
763 										id : 'width',
764 										setup : setupPopupParams,
765 										commit : commitPopupParams
766
767 									},
768 									{
769 										type :  'text',
770 										labelLayout : 'horizontal',
771 										widths : [ '55%', '45%' ],
772 										label : editor.lang.link.popupLeft,
773 										id : 'left',
774 										setup : setupPopupParams,
775 										commit : commitPopupParams
776
777 									}
778 								]
779 							},
780 							{
781 								type : 'hbox',
782 								children :
783 								[
784 									{
785 										type :  'text',
786 										labelLayout : 'horizontal',
787 										widths : [ '30%', '70%' ],
788 										label : editor.lang.link.popupHeight,
789 										id : 'height',
790 										setup : setupPopupParams,
791 										commit : commitPopupParams
792
793 									},
794 									{
795 										type :  'text',
796 										labelLayout : 'horizontal',
797 										label : editor.lang.link.popupTop,
798 										widths : [ '55%', '45%' ],
799 										id : 'top',
800 										setup : setupPopupParams,
801 										commit : commitPopupParams
802
803 									}
804 								]
805 							}
806 						]
807 					}
808 				]
809 			},
810 			{
811 				id : 'upload',
812 				label : editor.lang.link.upload,
813 				title : editor.lang.link.upload,
814 				elements :
815 				[
816 					{
817 						type : 'file',
818 						id : 'upload',
819 						label : editor.lang.common.upload,
820 						action : editor.config.linkUploadAction,
821 						size : 38
822 					},
823 					{
824 						type : 'fileButton',
825 						id : 'uploadButton',
826 						label : editor.lang.common.uploadSubmit,
827 						'for' : [ 'upload', 'upload' ]
828 					}
829 				]
830 			},
831 			{
832 				id : 'advanced',
833 				label : editor.lang.link.advanced,
834 				title : editor.lang.link.advanced,
835 				elements :
836 				[
837 					{
838 						type : 'vbox',
839 						padding : 1,
840 						children :
841 						[
842 							{
843 								type : 'hbox',
844 								widths : [ '45%', '35%', '20%' ],
845 								children :
846 								[
847 									{
848 										type : 'text',
849 										id : 'advId',
850 										label : editor.lang.link.id,
851 										setup : setupAdvParams,
852 										commit : commitAdvParams
853 									},
854 									{
855 										type : 'select',
856 										id : 'advLangDir',
857 										label : editor.lang.link.langDir,
858 										'default' : '',
859 										style : 'width: 100%;',
860 										items :
861 										[
862 											[ editor.lang.link.langDirNotSet, '' ],
863 											[ editor.lang.link.langDirLTR, 'ltr' ],
864 											[ editor.lang.link.langDirRTL, 'rtl' ]
865 										],
866 										setup : setupAdvParams,
867 										commit : commitAdvParams
868 									},
869 									{
870 										type : 'text',
871 										id : 'advAccessKey',
872 										label : editor.lang.link.acccessKey,
873 										maxLength : 1,
874 										setup : setupAdvParams,
875 										commit : commitAdvParams
876
877 									}
878 								]
879 							},
880 							{
881 								type : 'hbox',
882 								widths : [ '45%', '35%', '20%' ],
883 								children :
884 								[
885 									{
886 										type : 'text',
887 										label : editor.lang.link.name,
888 										id : 'advName',
889 										setup : setupAdvParams,
890 										commit : commitAdvParams
891
892 									},
893 									{
894 										type : 'text',
895 										label : editor.lang.link.langCode,
896 										id : 'advLangCode',
897 										'default' : '',
898 										setup : setupAdvParams,
899 										commit : commitAdvParams
900
901 									},
902 									{
903 										type : 'text',
904 										label : editor.lang.link.tabIndex,
905 										id : 'advTabIndex',
906 										maxLength : 5,
907 										setup : setupAdvParams,
908 										commit : commitAdvParams
909
910 									}
911 								]
912 							}
913 						]
914 					},
915 					{
916 						type : 'vbox',
917 						padding : 1,
918 						children :
919 						[
920 							{
921 								type : 'hbox',
922 								widths : [ '45%', '55%' ],
923 								children :
924 								[
925 									{
926 										type : 'text',
927 										label : editor.lang.link.advisoryTitle,
928 										'default' : '',
929 										id : 'advTitle',
930 										setup : setupAdvParams,
931 										commit : commitAdvParams
932
933 									},
934 									{
935 										type : 'text',
936 										label : editor.lang.link.advisoryContentType,
937 										'default' : '',
938 										id : 'advContentType',
939 										setup : setupAdvParams,
940 										commit : commitAdvParams
941
942 									}
943 								]
944 							},
945 							{
946 								type : 'hbox',
947 								widths : [ '45%', '55%' ],
948 								children :
949 								[
950 									{
951 										type : 'text',
952 										label : editor.lang.link.cssClasses,
953 										'default' : '',
954 										id : 'advCSSClasses',
955 										setup : setupAdvParams,
956 										commit : commitAdvParams
957
958 									},
959 									{
960 										type : 'text',
961 										label : editor.lang.link.charset,
962 										'default' : '',
963 										id : 'advCharset',
964 										setup : setupAdvParams,
965 										commit : commitAdvParams
966
967 									}
968 								]
969 							},
970 							{
971 								type : 'hbox',
972 								children :
973 								[
974 									{
975 										type : 'text',
976 										label : editor.lang.link.styles,
977 										'default' : '',
978 										id : 'advStyles',
979 										setup : setupAdvParams,
980 										commit : commitAdvParams
981
982 									}
983 								]
984 							}
985 						]
986 					}
987 				]
988 			}
989 		],
990 		onShow : function()
991 		{
992 			this.fakeObj = false;
993 			// IE BUG: Selection must be in the editor for getSelection() to work.
994 			this.restoreSelection();
995 			var editor = this.getParentEditor(),
996 				selection = editor.getSelection(),
997 				ranges = selection.getRanges(),
998 				element = null,
999 				me = this;
1000
1001 			// Fill in all the relevant fields if there's already one link selected.
1002 			if ( ranges.length == 1 )
1003 			{
1004 				ranges[0].enlarge( CKEDITOR.ENLARGE_ELEMENT );
1005
1006 				var rangeRoot = ranges[0].getCommonAncestor( true );
1007 				element = rangeRoot.getAscendant( 'a', true );
1008 				if ( element && element.getAttribute( 'href' ) )
1009 				{
1010 					selection.selectElement( element );
1011 					this.saveSelection();
1012 				}
1013 				else
1014 				{
1015 					element = rangeRoot.getAscendant( 'img', true );
1016 					if ( element && element.getAttribute( '_cke_real_element_type' ) && element.getAttribute( '_cke_real_element_type' ) == 'anchor' )
1017 					{
1018 						this.fakeObj = element;
1019 						element = editor.restoreRealElement( this.fakeObj );
1020 						selection.selectElement( this.fakeObj );
1021 						this.saveSelection();
1022 					}
1023 				}
1024 			}
1025
1026 			this.setupContent( parseLink.apply( this, [ editor, element ] ) );
1027 		},
1028 		onOk : function()
1029 		{
1030 			var attributes = { href : 'javascript:void(0)/*' + CKEDITOR.tools.getNextNumber() + '*/' },
1031 				removeAttributes = [],
1032 				data = { href : attributes.href },
1033 				me = this, editor = this.getParentEditor();
1034
1035 			this.commitContent( data );
1036
1037 			// Compose the URL.
1038 			switch ( data.type || 'url' )
1039 			{
1040 				case 'url':
1041 					var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
1042 						url = ( data.url && data.url.url ) || '';
1043 					attributes._cke_saved_href = protocol + url;
1044 					break;
1045 				case 'anchor':
1046 					var name = ( data.anchor && data.anchor.name ),
1047 						id = ( data.anchor && data.anchor.id );
1048 					attributes._cke_saved_href = '#' + ( name || id || '' );
1049 					break;
1050 				case 'email':
1051 					var address = ( data.email && data.email.address ),
1052 						subject = ( data.email && encodeURIComponent( data.email.subject || '' ) ),
1053 						body = ( data.email && encodeURIComponent( data.email.body || '' ) ),
1054 						linkList = [ 'mailto:', address ];
1055 					if ( subject || body )
1056 					{
1057 						var argList = [];
1058 						linkList.push( '?' );
1059 						subject && argList.push( 'subject=' + subject );
1060 						body && argList.push( 'body=' + body );
1061 						linkList.push( argList.join( '&' ) );
1062 					}
1063 					attributes._cke_saved_href = linkList.join( '' );
1064 					break;
1065 				default:
1066 			}
1067
1068 			// Popups and target.
1069 			if ( data.target )
1070 			{
1071 				if ( data.target.type == 'popup' )
1072 				{
1073 					var onclickList = [ 'window.open(this.href, \'',
1074 							data.target.name || '', '\', \'' ];
1075 					var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
1076 							'scrollbars', 'dependent' ];
1077 					var featureLength = featureList.length;
1078 					var addFeature = function( featureName )
1079 					{
1080 						if ( data.target[ featureName ] )
1081 							featureList.push( featureName + '=' + data.target[ featureName ] );
1082 					};
1083
1084 					for ( var i = 0 ; i < featureLength ; i++ )
1085 						featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ;
1086 					addFeature( 'width' );
1087 					addFeature( 'left' );
1088 					addFeature( 'height' );
1089 					addFeature( 'top' );
1090
1091 					onclickList.push( featureList.join( ',' ), '\'); return false;' );
1092 					attributes[ CKEDITOR.env.ie || CKEDITOR.env.webkit ? '_cke_pa_onclick' : 'onclick' ] = onclickList.join( '' );
1093 				}
1094 				else
1095 				{
1096 					if ( data.target.type != 'notSet' && data.target.name )
1097 						attributes.target = data.target.name;
1098 					removeAttributes.push( '_cke_pa_onclick', 'onclick' );
1099 				}
1100 			}
1101
1102 			// Advanced attributes.
1103 			if ( data.adv )
1104 			{
1105 				var advAttr = function( inputName, attrName )
1106 				{
1107 					var value = data.adv[ inputName ];
1108 					if ( value )
1109 						attributes[attrName] = value;
1110 					else
1111 						removeAttributes.push( attrName );
1112 				};
1113
1114 				if ( this._.selectedElement )
1115 					advAttr( 'advId', 'id' );
1116 				advAttr( 'advLangDir', 'dir' );
1117 				advAttr( 'advAccessKey', 'accessKey' );
1118 				advAttr( 'advName', 'name' );
1119 				advAttr( 'advLangCode', 'lang' );
1120 				advAttr( 'advTabIndex', 'tabindex' );
1121 				advAttr( 'advTitle', 'title' );
1122 				advAttr( 'advContentType', 'type' );
1123 				advAttr( 'advCSSClasses', 'class' );
1124 				advAttr( 'advCharset', 'charset' );
1125 				advAttr( 'advStyles', 'style' );
1126 			}
1127
1128 			if ( !this._.selectedElement )
1129 			{
1130 				// IE BUG: Selection must be in the editor for getSelection() to work.
1131 				this.restoreSelection();
1132 				this.clearSavedSelection();
1133
1134 				// Create element if current selection is collapsed.
1135 				var selection = editor.getSelection(),
1136 					ranges = selection.getRanges();
1137 				if ( ranges.length == 1 && ranges[0].collapsed )
1138 				{
1139 					var text = new CKEDITOR.dom.text( attributes._cke_saved_href, editor.document );
1140 					ranges[0].insertNode( text );
1141 					ranges[0].selectNodeContents( text );
1142 					selection.selectRanges( ranges );
1143 				}
1144
1145 				// Apply style.
1146 				var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
1147 				style.type = CKEDITOR.STYLE_INLINE;		// need to override... dunno why.
1148 				style.apply( editor.document );
1149
1150 				// Id. Apply only to the first link.
1151 				if ( data.adv && data.adv.advId != '' )
1152 				{
1153 					var links = this.getParentEditor().document.$.getElementsByTagName( 'a' );
1154 					for ( var i = 0 ; i < links.length ; i++ )
1155 					{
1156 						if ( links[i].href == attributes.href )
1157 						{
1158 							links[i].id = data.adv.advId;
1159 							break;
1160 						}
1161 					}
1162 				}
1163 			}
1164 			else
1165 			{
1166 				// We're only editing an existing link, so just overwrite the attributes.
1167 				var element = this._.selectedElement;
1168
1169 				// IE BUG: Setting the name attribute to an existing link doesn't work.
1170 				// Must re-create the link from weired syntax to workaround.
1171 				if ( CKEDITOR.env.ie && attributes.name != element.getAttribute( 'name' ) )
1172 				{
1173 					var newElement = new CKEDITOR.dom.element( '<a name="' + CKEDITOR.tools.htmlEncode( attributes.name ) + '">',
1174 							editor.document );
1175
1176 					this.restoreSelection();
1177 					var selection = editor.getSelection();
1178
1179 					element.moveChildren( newElement );
1180 					element.copyAttributes( newElement, { name : 1 } );
1181 					newElement.replace( element );
1182 					element = newElement;
1183
1184 					this.clearSavedSelection();
1185 					selection.selectElement( element );
1186 				}
1187
1188 				element.setAttributes( attributes );
1189 				element.removeAttributes( removeAttributes );
1190
1191 				// Make the element display as an anchor if a name has been set.
1192 				if ( element.getAttribute( 'name' ) )
1193 					element.addClass( 'cke_anchor' );
1194 				else
1195 					element.removeClass( 'cke_anchor' );
1196
1197 				if ( this.fakeObj )
1198 					editor.createFakeElement( element, 'cke_anchor', 'anchor' ).replace( this.fakeObj );
1199
1200 				delete this._.selectedElement;
1201 			}
1202 		},
1203 		onLoad : function()
1204 		{
1205 			if ( editor.config.linkUploadTab == false )
1206 				this.hidePage( 'upload' );		//Hide Upload tab.
1207
1208 			if ( editor.config.linkShowAdvancedTab == false )
1209 				this.hidePage( 'advanced' );		//Hide Advanded tab.
1210
1211 			if ( editor.config.linkBrowseServer == false )
1212 				this.getContentElement( 'info', 'browse' ).getElement().hide();
1213
1214 			if ( editor.config.linkShowTargetTab == false )
1215 				this.hidePage( 'target' );		//Hide Target tab.
1216
1217 		}
1218 	};
1219 } );
1220