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