text: ﻿/**
   * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
   * For licensing, see LICENSE.html or http://ckeditor.com/license
   */
  
  CKEDITOR.dialog.add( 'link', function( editor ) {
  	var plugin = CKEDITOR.plugins.link;
  	// Handles the event when the "Target" selection box is changed.
  	var targetChanged = function() {
  			var dialog = this.getDialog(),
  				popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ),
  				targetName = dialog.getContentElement( 'target', 'linkTargetName' ),
  				value = this.getValue();
  
  			if ( !popupFeatures || !targetName )
  				return;
  
  			popupFeatures = popupFeatures.getElement();
  			popupFeatures.hide();
  			targetName.setValue( '' );
  
  			switch ( value ) {
  				case 'frame':
  					targetName.setLabel( editor.lang.link.targetFrameName );
  					targetName.getElement().show();
  					break;
  				case 'popup':
  					popupFeatures.show();
  					targetName.setLabel( editor.lang.link.targetPopupName );
  					targetName.getElement().show();
  					break;
  				default:
  					targetName.setValue( value );
  					targetName.getElement().hide();
  					break;
  			}
  
  		};
  
  	// Handles the event when the "Type" selection box is changed.
  	var linkTypeChanged = function() {
  			var dialog = this.getDialog(),
  				partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
  				typeValue = this.getValue(),
  				uploadTab = dialog.definition.getContents( 'upload' ),
  				uploadInitiallyHidden = uploadTab && uploadTab.hidden;
  
  			if ( typeValue == 'url' ) {
  				if ( editor.config.linkShowTargetTab )
  					dialog.showPage( 'target' );
  				if ( !uploadInitiallyHidden )
  					dialog.showPage( 'upload' );
  			} else {
  				dialog.hidePage( 'target' );
  				if ( !uploadInitiallyHidden )
  					dialog.hidePage( 'upload' );
  			}
  
  			for ( var i = 0; i < partIds.length; i++ ) {
  				var element = dialog.getContentElement( 'info', partIds[ i ] );
  				if ( !element )
  					continue;
  
  				element = element.getElement().getParent().getParent();
  				if ( partIds[ i ] == typeValue + 'Options' )
  					element.show();
  				else
  					element.hide();
  			}
  
  			dialog.layout();
  		};
  
  	// Loads the parameters in a selected link to the link dialog fields.
  	var javascriptProtocolRegex = /^javascript:/,
  		emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
  		emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,
  		emailBodyRegex = /body=([^;?:@&=$,\/]*)/,
  		anchorRegex = /^#(.*)$/,
  		urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
  		selectableTargets = /^(_(?:self|top|parent|blank))$/,
  		encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
  		functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/;
  
  	var popupRegex = /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
  	var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
  
  	var parseLink = function( editor, element ) {
  			var href = ( element && ( element.data( 'cke-saved-href' ) || element.getAttribute( 'href' ) ) ) || '',
  				javascriptMatch, emailMatch, anchorMatch, urlMatch,
  				retval = {};
  
  			if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) ) {
  				if ( emailProtection == 'encode' ) {
  					href = href.replace( encodedEmailLinkRegex, function( match, protectedAddress, rest ) {
  						return 'mailto:' +
  							String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) +
  							( rest && unescapeSingleQuote( rest ) );
  					});
  				}
  				// Protected email link as function call.
  				else if ( emailProtection ) {
  					href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs ) {
  						if ( funcName == compiledProtectionFunction.name ) {
  							retval.type = 'email';
  							var email = retval.email = {};
  
  							var paramRegex = /[^,\s]+/g,
  								paramQuoteRegex = /(^')|('$)/g,
  								paramsMatch = funcArgs.match( paramRegex ),
  								paramsMatchLength = paramsMatch.length,
  								paramName, paramVal;
  
  							for ( var i = 0; i < paramsMatchLength; i++ ) {
  								paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) );
  								paramName = compiledProtectionFunction.params[ i ].toLowerCase();
  								email[ paramName ] = paramVal;
  							}
  							email.address = [ email.name, email.domain ].join( '@' );
  						}
  					});
  				}
  			}
  
  			if ( !retval.type ) {
  				if ( ( anchorMatch = href.match( anchorRegex ) ) ) {
  					retval.type = 'anchor';
  					retval.anchor = {};
  					retval.anchor.name = retval.anchor.id = anchorMatch[ 1 ];
  				}
  				// Protected email link as encoded string.
  				else if ( ( emailMatch = href.match( emailRegex ) ) ) {
  					var subjectMatch = href.match( emailSubjectRegex ),
  						bodyMatch = href.match( emailBodyRegex );
  
  					retval.type = 'email';
  					var email = ( retval.email = {} );
  					email.address = emailMatch[ 1 ];
  					subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) );
  					bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) );
  				}
  				// urlRegex matches empty strings, so need to check for href as well.
  				else if ( href && ( urlMatch = href.match( urlRegex ) ) ) {
  					retval.type = 'url';
  					retval.url = {};
  					retval.url.protocol = urlMatch[ 1 ];
  					retval.url.url = urlMatch[ 2 ];
  				} else
  					retval.type = 'url';
  			}
  
  			// Load target and popup settings.
  			if ( element ) {
  				var target = element.getAttribute( 'target' );
  				retval.target = {};
  				retval.adv = {};
  
  				// IE BUG: target attribute is an empty string instead of null in IE if it's not set.
  				if ( !target ) {
  					var onclick = element.data( 'cke-pa-onclick' ) || element.getAttribute( 'onclick' ),
  						onclickMatch = onclick && onclick.match( popupRegex );
  					if ( onclickMatch ) {
  						retval.target.type = 'popup';
  						retval.target.name = onclickMatch[ 1 ];
  
  						var featureMatch;
  						while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[ 2 ] ) ) ) {
  							// Some values should remain numbers (#7300)
  							if ( ( featureMatch[ 2 ] == 'yes' || featureMatch[ 2 ] == '1' ) && !( featureMatch[ 1 ] in { height:1,width:1,top:1,left:1 } ) )
  								retval.target[ featureMatch[ 1 ] ] = true;
  							else if ( isFinite( featureMatch[ 2 ] ) )
  								retval.target[ featureMatch[ 1 ] ] = featureMatch[ 2 ];
  						}
  					}
  				} else {
  					var targetMatch = target.match( selectableTargets );
  					if ( targetMatch )
  						retval.target.type = retval.target.name = target;
  					else {
  						retval.target.type = 'frame';
  						retval.target.name = target;
  					}
  				}
  
  				var me = this;
  				var advAttr = function( inputName, attrName ) {
  						var value = element.getAttribute( attrName );
  						if ( value !== null )
  							retval.adv[ inputName ] = value || '';
  					};
  				advAttr( 'advId', 'id' );
  				advAttr( 'advLangDir', 'dir' );
  				advAttr( 'advAccessKey', 'accessKey' );
  
  				retval.adv.advName = element.data( 'cke-saved-name' ) || element.getAttribute( 'name' ) || '';
  				advAttr( 'advLangCode', 'lang' );
  				advAttr( 'advTabIndex', 'tabindex' );
  				advAttr( 'advTitle', 'title' );
  				advAttr( 'advContentType', 'type' );
  				CKEDITOR.plugins.link.synAnchorSelector ? retval.adv.advCSSClasses = getLinkClass( element ) : advAttr( 'advCSSClasses', 'class' );
  				advAttr( 'advCharset', 'charset' );
  				advAttr( 'advStyles', 'style' );
  				advAttr( 'advRel', 'rel' );
  			}
  
  			// Find out whether we have any anchors in the editor.
  			var anchors = retval.anchors = [],
  				i, count, item;
  
  			// For some browsers we set contenteditable="false" on anchors, making document.anchors not to include them, so we must traverse the links manually (#7893).
  			if ( CKEDITOR.plugins.link.emptyAnchorFix ) {
  				var links = editor.document.getElementsByTag( 'a' );
  				for ( i = 0, count = links.count(); i < count; i++ ) {
  					item = links.getItem( i );
  					if ( item.data( 'cke-saved-name' ) || item.hasAttribute( 'name' ) )
  						anchors.push({ name: item.data( 'cke-saved-name' ) || item.getAttribute( 'name' ), id: item.getAttribute( 'id' ) } );
  				}
  			} else {
  				var anchorList = new CKEDITOR.dom.nodeList( editor.document.$.anchors );
  				for ( i = 0, count = anchorList.count(); i < count; i++ ) {
  					item = anchorList.getItem( i );
  					anchors[ i ] = { name: item.getAttribute( 'name' ), id: item.getAttribute( 'id' ) };
  				}
  			}
  
  			if ( CKEDITOR.plugins.link.fakeAnchor ) {
  				var imgs = editor.document.getElementsByTag( 'img' );
  				for ( i = 0, count = imgs.count(); i < count; i++ ) {
  					if ( ( item = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, imgs.getItem( i ) ) ) )
  						anchors.push({ name: item.getAttribute( 'name' ), id: item.getAttribute( 'id' ) } );
  				}
  			}
  
  			// Record down the selected element in the dialog.
  			this._.selectedElement = element;
  			return retval;
  		};
  
  	var setupParams = function( page, data ) {
  			if ( data[ page ] )
  				this.setValue( data[ page ][ this.id ] || '' );
  		};
  
  	var setupPopupParams = function( data ) {
  			return setupParams.call( this, 'target', data );
  		};
  
  	var setupAdvParams = function( data ) {
  			return setupParams.call( this, 'adv', data );
  		};
  
  	var commitParams = function( page, data ) {
  			if ( !data[ page ] )
  				data[ page ] = {};
  
  			data[ page ][ this.id ] = this.getValue() || '';
  		};
  
  	var commitPopupParams = function( data ) {
  			return commitParams.call( this, 'target', data );
  		};
  
  	var commitAdvParams = function( data ) {
  			return commitParams.call( this, 'adv', data );
  		};
  
  	function unescapeSingleQuote( str ) {
  		return str.replace( /\\'/g, '\'' );
  	}
  
  	function escapeSingleQuote( str ) {
  		return str.replace( /'/g, '\\$&' );
  	}
  
  	var emailProtection = editor.config.emailProtection || '';
  
  	// Compile the protection function pattern.
  	if ( emailProtection && emailProtection != 'encode' ) {
  		var compiledProtectionFunction = {};
  
  		emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params ) {
  			compiledProtectionFunction.name = funcName;
  			compiledProtectionFunction.params = [];
  			params.replace( /[^,\s]+/g, function( param ) {
  				compiledProtectionFunction.params.push( param );
  			});
  		});
  	}
  
  	function protectEmailLinkAsFunction( email ) {
  		var retval,
  			name = compiledProtectionFunction.name,
  			params = compiledProtectionFunction.params,
  			paramName, paramValue;
  
  		retval = [ name, '(' ];
  		for ( var i = 0; i < params.length; i++ ) {
  			paramName = params[ i ].toLowerCase();
  			paramValue = email[ paramName ];
  
  			i > 0 && retval.push( ',' );
  			retval.push( '\'', paramValue ? escapeSingleQuote( encodeURIComponent( email[ paramName ] ) ) : '', '\'' );
  		}
  		retval.push( ')' );
  		return retval.join( '' );
  	}
  
  	function protectEmailAddressAsEncodedString( address ) {
  		var charCode,
  			length = address.length,
  			encodedChars = [];
  		for ( var i = 0; i < length; i++ ) {
  			charCode = address.charCodeAt( i );
  			encodedChars.push( charCode );
  		}
  		return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')';
  	}
  
  	function getLinkClass( ele ) {
  		var className = ele.getAttribute( 'class' );
  		return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : '';
  	}
  
  	var commonLang = editor.lang.common,
  		linkLang = editor.lang.link;
  
  	return {
  		title: linkLang.title,
  		minWidth: 350,
  		minHeight: 230,
  		contents: [
  			{
  			id: 'info',
  			label: linkLang.info,
  			title: linkLang.info,
  			elements: [
  				{
  				id: 'linkType',
  				type: 'select',
  				label: linkLang.type,
  				'default': 'url',
  				items: [
  					[ linkLang.toUrl, 'url' ],
  					[ linkLang.toAnchor, 'anchor' ],
  					[ linkLang.toEmail, 'email' ]
  					],
  				onChange: linkTypeChanged,
  				setup: function( data ) {
  					if ( data.type )
  						this.setValue( data.type );
  				},
  				commit: function( data ) {
  					data.type = this.getValue();
  				}
  			},
  				{
  				type: 'vbox',
  				id: 'urlOptions',
  				children: [
  					{
  					type: 'hbox',
  					widths: [ '25%', '75%' ],
  					children: [
  						{
  						id: 'protocol',
  						type: 'select',
  						label: commonLang.protocol,
  						'default': 'http://',
  						items: [
  							// Force 'ltr' for protocol names in BIDI. (#5433)
  							[ 'http://\u200E', 'http://' ],
  							[ 'https://\u200E', 'https://' ],
  							[ 'ftp://\u200E', 'ftp://' ],
  							[ 'news://\u200E', 'news://' ],
  							[ linkLang.other, '' ]
  							],
  						setup: function( data ) {
  							if ( data.url )
  								this.setValue( data.url.protocol || '' );
  						},
  						commit: function( data ) {
  							if ( !data.url )
  								data.url = {};
  
  							data.url.protocol = this.getValue();
  						}
  					},
  						{
  						type: 'text',
  						id: 'url',
  						label: commonLang.url,
  						required: true,
  						onLoad: function() {
  							this.allowOnChange = true;
  						},
  						onKeyUp: function() {
  							this.allowOnChange = false;
  							var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),
  								url = this.getValue(),
  								urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/i,
  								urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/i;
  
  							var protocol = urlOnChangeProtocol.exec( url );
  							if ( protocol ) {
  								this.setValue( url.substr( protocol[ 0 ].length ) );
  								protocolCmb.setValue( protocol[ 0 ].toLowerCase() );
  							} else if ( urlOnChangeTestOther.test( url ) )
  								protocolCmb.setValue( '' );
  
  							this.allowOnChange = true;
  						},
  						onChange: function() {
  							if ( this.allowOnChange ) // Dont't call on dialog load.
  							this.onKeyUp();
  						},
  						validate: function() {
  							var dialog = this.getDialog();
  
  							if ( dialog.getContentElement( 'info', 'linkType' ) && dialog.getValueOf( 'info', 'linkType' ) != 'url' )
  								return true;
  
  							if ( (/javascript\:/).test( this.getValue() ) ) {
  								alert( commonLang.invalidValue );
  								return false;
  							}
  
  							if ( this.getDialog().fakeObj ) // Edit Anchor.
  							return true;
  
  							var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noUrl );
  							return func.apply( this );
  						},
  						setup: function( data ) {
  							this.allowOnChange = false;
  							if ( data.url )
  								this.setValue( data.url.url );
  							this.allowOnChange = true;
  
  						},
  						commit: function( data ) {
  							// IE will not trigger the onChange event if the mouse has been used
  							// to carry all the operations #4724
  							this.onChange();
  
  							if ( !data.url )
  								data.url = {};
  
  							data.url.url = this.getValue();
  							this.allowOnChange = false;
  						}
  					}
  					],
  					setup: function( data ) {
  						if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
  							this.getElement().show();
  					}
  				},
  				{
  				    type : 'hbox',
  				    widths : [ '50%', '50%' ],
  				    children :
  				    [
  				        {
  				            type : 'button',
  				            id : 'browse',
  				            hidden : 'true',
  				            filebrowser : 'info:url',
  				            label : 'Link to File on Server'
  				        },
  				        {
  				            type : 'button',
  				            id : 'browseInternal',
  				            label : 'Link to node',
  				            align: 'right',
  				            filebrowser :
  				            {
  			            		action : 'Browse',
  		            			url : 'admin/node:view.crud?multiple=0&link=1'
  				            }
  				        },
                          {
                              type : 'button',
                              id : 'browseMedia',
                              label : 'Link to media',
                              align: 'right',
                              filebrowser :
                              {
                                  action : 'Browse',
                                  url : 'admin/media:view.crud?multiple=0&link=1'
                              }
                          }
  				    ]
  				} 
  				]
  			},
  				{
  				type: 'vbox',
  				id: 'anchorOptions',
  				width: 260,
  				align: 'center',
  				padding: 0,
  				children: [
  					{
  					type: 'fieldset',
  					id: 'selectAnchorText',
  					label: linkLang.selectAnchor,
  					setup: function( data ) {
  						if ( data.anchors.length > 0 )
  							this.getElement().show();
  						else
  							this.getElement().hide();
  					},
  					children: [
  						{
  						type: 'hbox',
  						id: 'selectAnchor',
  						children: [
  							{
  							type: 'select',
  							id: 'anchorName',
  							'default': '',
  							label: linkLang.anchorName,
  							style: 'width: 100%;',
  							items: [
  								[ '' ]
  								],
  							setup: function( data ) {
  								this.clear();
  								this.add( '' );
  								for ( var i = 0; i < data.anchors.length; i++ ) {
  									if ( data.anchors[ i ].name )
  										this.add( data.anchors[ i ].name );
  								}
  
  								if ( data.anchor )
  									this.setValue( data.anchor.name );
  
  								var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
  								if ( linkType && linkType.getValue() == 'email' )
  									this.focus();
  							},
  							commit: function( data ) {
  								if ( !data.anchor )
  									data.anchor = {};
  
  								data.anchor.name = this.getValue();
  							}
  						},
  							{
  							type: 'select',
  							id: 'anchorId',
  							'default': '',
  							label: linkLang.anchorId,
  							style: 'width: 100%;',
  							items: [
  								[ '' ]
  								],
  							setup: function( data ) {
  								this.clear();
  								this.add( '' );
  								for ( var i = 0; i < data.anchors.length; i++ ) {
  									if ( data.anchors[ i ].id )
  										this.add( data.anchors[ i ].id );
  								}
  
  								if ( data.anchor )
  									this.setValue( data.anchor.id );
  							},
  							commit: function( data ) {
  								if ( !data.anchor )
  									data.anchor = {};
  
  								data.anchor.id = this.getValue();
  							}
  						}
  						],
  						setup: function( data ) {
  							if ( data.anchors.length > 0 )
  								this.getElement().show();
  							else
  								this.getElement().hide();
  						}
  					}
  					]
  				},
  					{
  					type: 'html',
  					id: 'noAnchors',
  					style: 'text-align: center;',
  					html: '<div role="note" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( linkLang.noAnchors ) + '</div>',
  					// Focus the first element defined in above html.
  					focus: true,
  					setup: function( data ) {
  						if ( data.anchors.length < 1 )
  							this.getElement().show();
  						else
  							this.getElement().hide();
  					}
  				}
  				],
  				setup: function( data ) {
  					if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
  						this.getElement().hide();
  				}
  			},
  				{
  				type: 'vbox',
  				id: 'emailOptions',
  				padding: 1,
  				children: [
  					{
  					type: 'text',
  					id: 'emailAddress',
  					label: linkLang.emailAddress,
  					required: true,
  					validate: function() {
  						var dialog = this.getDialog();
  
  						if ( !dialog.getContentElement( 'info', 'linkType' ) || dialog.getValueOf( 'info', 'linkType' ) != 'email' )
  							return true;
  
  						var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noEmail );
  						return func.apply( this );
  					},
  					setup: function( data ) {
  						if ( data.email )
  							this.setValue( data.email.address );
  
  						var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
  						if ( linkType && linkType.getValue() == 'email' )
  							this.select();
  					},
  					commit: function( data ) {
  						if ( !data.email )
  							data.email = {};
  
  						data.email.address = this.getValue();
  					}
  				},
  					{
  					type: 'text',
  					id: 'emailSubject',
  					label: linkLang.emailSubject,
  					setup: function( data ) {
  						if ( data.email )
  							this.setValue( data.email.subject );
  					},
  					commit: function( data ) {
  						if ( !data.email )
  							data.email = {};
  
  						data.email.subject = this.getValue();
  					}
  				},
  					{
  					type: 'textarea',
  					id: 'emailBody',
  					label: linkLang.emailBody,
  					rows: 3,
  					'default': '',
  					setup: function( data ) {
  						if ( data.email )
  							this.setValue( data.email.body );
  					},
  					commit: function( data ) {
  						if ( !data.email )
  							data.email = {};
  
  						data.email.body = this.getValue();
  					}
  				}
  				],
  				setup: function( data ) {
  					if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
  						this.getElement().hide();
  				}
  			}
  			]
  		},
  			{
  			id: 'target',
  			requiredContent: 'a[target]', // This is not fully correct, because some target option requires JS.
  			label: linkLang.target,
  			title: linkLang.target,
  			elements: [
  				{
  				type: 'hbox',
  				widths: [ '50%', '50%' ],
  				children: [
  					{
  					type: 'select',
  					id: 'linkTargetType',
  					label: commonLang.target,
  					'default': 'notSet',
  					style: 'width : 100%;',
  					'items': [
  						[ commonLang.notSet, 'notSet' ],
  						[ linkLang.targetFrame, 'frame' ],
  						[ linkLang.targetPopup, 'popup' ],
  						[ commonLang.targetNew, '_blank' ],
  						[ commonLang.targetTop, '_top' ],
  						[ commonLang.targetSelf, '_self' ],
  						[ commonLang.targetParent, '_parent' ]
  						],
  					onChange: targetChanged,
  					setup: function( data ) {
  						if ( data.target )
  							this.setValue( data.target.type || 'notSet' );
  						targetChanged.call( this );
  					},
  					commit: function( data ) {
  						if ( !data.target )
  							data.target = {};
  
  						data.target.type = this.getValue();
  					}
  				},
  					{
  					type: 'text',
  					id: 'linkTargetName',
  					label: linkLang.targetFrameName,
  					'default': '',
  					setup: function( data ) {
  						if ( data.target )
  							this.setValue( data.target.name );
  					},
  					commit: function( data ) {
  						if ( !data.target )
  							data.target = {};
  
  						data.target.name = this.getValue().replace( /\W/gi, '' );
  					}
  				}
  				]
  			},
  				{
  				type: 'vbox',
  				width: '100%',
  				align: 'center',
  				padding: 2,
  				id: 'popupFeatures',
  				children: [
  					{
  					type: 'fieldset',
  					label: linkLang.popupFeatures,
  					children: [
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'checkbox',
  							id: 'resizable',
  							label: linkLang.popupResizable,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  						},
  							{
  							type: 'checkbox',
  							id: 'status',
  							label: linkLang.popupStatusBar,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					},
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'checkbox',
  							id: 'location',
  							label: linkLang.popupLocationBar,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						},
  							{
  							type: 'checkbox',
  							id: 'toolbar',
  							label: linkLang.popupToolbar,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					},
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'checkbox',
  							id: 'menubar',
  							label: linkLang.popupMenuBar,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						},
  							{
  							type: 'checkbox',
  							id: 'fullscreen',
  							label: linkLang.popupFullScreen,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					},
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'checkbox',
  							id: 'scrollbars',
  							label: linkLang.popupScrollBars,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						},
  							{
  							type: 'checkbox',
  							id: 'dependent',
  							label: linkLang.popupDependent,
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					},
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'text',
  							widths: [ '50%', '50%' ],
  							labelLayout: 'horizontal',
  							label: commonLang.width,
  							id: 'width',
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						},
  							{
  							type: 'text',
  							labelLayout: 'horizontal',
  							widths: [ '50%', '50%' ],
  							label: linkLang.popupLeft,
  							id: 'left',
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					},
  						{
  						type: 'hbox',
  						children: [
  							{
  							type: 'text',
  							labelLayout: 'horizontal',
  							widths: [ '50%', '50%' ],
  							label: commonLang.height,
  							id: 'height',
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						},
  							{
  							type: 'text',
  							labelLayout: 'horizontal',
  							label: linkLang.popupTop,
  							widths: [ '50%', '50%' ],
  							id: 'top',
  							setup: setupPopupParams,
  							commit: commitPopupParams
  
  						}
  						]
  					}
  					]
  				}
  				]
  			}
  			]
  		},
  			{
  			id: 'upload',
  			label: linkLang.upload,
  			title: linkLang.upload,
  			hidden: true,
  			filebrowser: 'uploadButton',
  			elements: [
  				{
  				type: 'file',
  				id: 'upload',
  				label: commonLang.upload,
  				style: 'height:40px',
  				size: 29
  			},
  				{
  				type: 'fileButton',
  				id: 'uploadButton',
  				label: commonLang.uploadSubmit,
  				filebrowser: 'info:url',
  				'for': [ 'upload', 'upload' ]
  			}
  			]
  		},
  			{
  			id: 'advanced',
  			label: linkLang.advanced,
  			title: linkLang.advanced,
  			elements: [
  				{
  				type: 'vbox',
  				padding: 1,
  				children: [
  					{
  					type: 'hbox',
  					widths: [ '45%', '35%', '20%' ],
  					children: [
  						{
  						type: 'text',
  						id: 'advId',
  						requiredContent: 'a[id]',
  						label: linkLang.id,
  						setup: setupAdvParams,
  						commit: commitAdvParams
  					},
  						{
  						type: 'select',
  						id: 'advLangDir',
  						requiredContent: 'a[dir]',
  						label: linkLang.langDir,
  						'default': '',
  						style: 'width:110px',
  						items: [
  							[ commonLang.notSet, '' ],
  							[ linkLang.langDirLTR, 'ltr' ],
  							[ linkLang.langDirRTL, 'rtl' ]
  							],
  						setup: setupAdvParams,
  						commit: commitAdvParams
  					},
  						{
  						type: 'text',
  						id: 'advAccessKey',
  						requiredContent: 'a[accesskey]',
  						width: '80px',
  						label: linkLang.acccessKey,
  						maxLength: 1,
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					}
  					]
  				},
  					{
  					type: 'hbox',
  					widths: [ '45%', '35%', '20%' ],
  					children: [
  						{
  						type: 'text',
  						label: linkLang.name,
  						id: 'advName',
  						requiredContent: 'a[name]',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					},
  						{
  						type: 'text',
  						label: linkLang.langCode,
  						id: 'advLangCode',
  						requiredContent: 'a[lang]',
  						width: '110px',
  						'default': '',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					},
  						{
  						type: 'text',
  						label: linkLang.tabIndex,
  						id: 'advTabIndex',
  						requiredContent: 'a[tabindex]',
  						width: '80px',
  						maxLength: 5,
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					}
  					]
  				}
  				]
  			},
  				{
  				type: 'vbox',
  				padding: 1,
  				children: [
  					{
  					type: 'hbox',
  					widths: [ '45%', '55%' ],
  					children: [
  						{
  						type: 'text',
  						label: linkLang.advisoryTitle,
  						requiredContent: 'a[title]',
  						'default': '',
  						id: 'advTitle',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					},
  						{
  						type: 'text',
  						label: linkLang.advisoryContentType,
  						requiredContent: 'a[type]',
  						'default': '',
  						id: 'advContentType',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					}
  					]
  				},
  					{
  					type: 'hbox',
  					widths: [ '45%', '55%' ],
  					children: [
  						{
  						type: 'text',
  						label: linkLang.cssClasses,
  						requiredContent: 'a(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
  						'default': '',
  						id: 'advCSSClasses',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					},
  						{
  						type: 'text',
  						label: linkLang.charset,
  						requiredContent: 'a[charset]',
  						'default': '',
  						id: 'advCharset',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  
  					}
  					]
  				},
  					{
  					type: 'hbox',
  					widths: [ '45%', '55%' ],
  					children: [
  						{
  						type: 'text',
  						label: linkLang.rel,
  						requiredContent: 'a[rel]',
  						'default': '',
  						id: 'advRel',
  						setup: setupAdvParams,
  						commit: commitAdvParams
  					},
  						{
  						type: 'text',
  						label: linkLang.styles,
  						requiredContent: 'a{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
  						'default': '',
  						id: 'advStyles',
  						validate: CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
  						setup: setupAdvParams,
  						commit: commitAdvParams
  					}
  					]
  				}
  				]
  			}
  			]
  		}
  		],
  		onShow: function() {
  			var editor = this.getParentEditor(),
  				selection = editor.getSelection(),
  				element = null;
  
  			// Fill in all the relevant fields if there's already one link selected.
  			if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
  				selection.selectElement( element );
  			else
  				element = null;
  
  			this.setupContent( parseLink.apply( this, [ editor, element ] ) );
  		},
  		onOk: function() {
  			var attributes = {},
  				removeAttributes = [],
  				data = {},
  				me = this,
  				editor = this.getParentEditor();
  
  			this.commitContent( data );
  
  			// Compose the URL.
  			switch ( data.type || 'url' ) {
  				case 'url':
  					var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
  						url = ( data.url && CKEDITOR.tools.trim( data.url.url ) ) || '';
  					attributes[ 'data-cke-saved-href' ] = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url;
  					break;
  				case 'anchor':
  					var name = ( data.anchor && data.anchor.name ),
  						id = ( data.anchor && data.anchor.id );
  					attributes[ 'data-cke-saved-href' ] = '#' + ( name || id || '' );
  					break;
  				case 'email':
  
  					var linkHref,
  					email = data.email,
  						address = email.address;
  
  					switch ( emailProtection ) {
  						case '':
  						case 'encode':
  							{
  								var subject = encodeURIComponent( email.subject || '' ),
  									body = encodeURIComponent( email.body || '' );
  
  								// Build the e-mail parameters first.
  								var argList = [];
  								subject && argList.push( 'subject=' + subject );
  								body && argList.push( 'body=' + body );
  								argList = argList.length ? '?' + argList.join( '&' ) : '';
  
  								if ( emailProtection == 'encode' ) {
  									linkHref = [ 'javascript:void(location.href=\'mailto:\'+',
  																															protectEmailAddressAsEncodedString( address ) ];
  									// parameters are optional.
  									argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' );
  
  									linkHref.push( ')' );
  								} else
  									linkHref = [ 'mailto:', address, argList ];
  
  								break;
  							}
  						default:
  							{
  								// Separating name and domain.
  								var nameAndDomain = address.split( '@', 2 );
  								email.name = nameAndDomain[ 0 ];
  								email.domain = nameAndDomain[ 1 ];
  
  								linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ];
  							}
  					}
  
  					attributes[ 'data-cke-saved-href' ] = linkHref.join( '' );
  					break;
  			}
  
  			// Popups and target.
  			if ( data.target ) {
  				if ( data.target.type == 'popup' ) {
  					var onclickList = [ 'window.open(this.href, \'',
  																			data.target.name || '', '\', \'' ];
  					var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
  																			'scrollbars', 'dependent' ];
  					var featureLength = featureList.length;
  					var addFeature = function( featureName ) {
  							if ( data.target[ featureName ] )
  								featureList.push( featureName + '=' + data.target[ featureName ] );
  						};
  
  					for ( var i = 0; i < featureLength; i++ )
  						featureList[ i ] = featureList[ i ] + ( data.target[ featureList[ i ] ] ? '=yes' : '=no' );
  					addFeature( 'width' );
  					addFeature( 'left' );
  					addFeature( 'height' );
  					addFeature( 'top' );
  
  					onclickList.push( featureList.join( ',' ), '\'); return false;' );
  					attributes[ 'data-cke-pa-onclick' ] = onclickList.join( '' );
  
  					// Add the "target" attribute. (#5074)
  					removeAttributes.push( 'target' );
  				} else {
  					if ( data.target.type != 'notSet' && data.target.name )
  						attributes.target = data.target.name;
  					else
  						removeAttributes.push( 'target' );
  
  					removeAttributes.push( 'data-cke-pa-onclick', 'onclick' );
  				}
  			}
  
  			// Advanced attributes.
  			if ( data.adv ) {
  				var advAttr = function( inputName, attrName ) {
  						var value = data.adv[ inputName ];
  						if ( value )
  							attributes[ attrName ] = value;
  						else
  							removeAttributes.push( attrName );
  					};
  
  				advAttr( 'advId', 'id' );
  				advAttr( 'advLangDir', 'dir' );
  				advAttr( 'advAccessKey', 'accessKey' );
  
  				if ( data.adv[ 'advName' ] )
  					attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ];
  				else
  					removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] );
  
  				advAttr( 'advLangCode', 'lang' );
  				advAttr( 'advTabIndex', 'tabindex' );
  				advAttr( 'advTitle', 'title' );
  				advAttr( 'advContentType', 'type' );
  				advAttr( 'advCSSClasses', 'class' );
  				advAttr( 'advCharset', 'charset' );
  				advAttr( 'advStyles', 'style' );
  				advAttr( 'advRel', 'rel' );
  			}
  
  
  			var selection = editor.getSelection();
  
  			// Browser need the "href" fro copy/paste link to work. (#6641)
  			attributes.href = attributes[ 'data-cke-saved-href' ];
  
  			if ( !this._.selectedElement ) {
  				var range = selection.getRanges( 1 )[ 0 ];
  
  				// Use link URL as text with a collapsed cursor.
  				if ( range.collapsed ) {
  					// Short mailto link text view (#5736).
  					var text = new CKEDITOR.dom.text( data.type == 'email' ? data.email.address : attributes[ 'data-cke-saved-href' ], editor.document );
  					range.insertNode( text );
  					range.selectNodeContents( text );
  				}
  
  				// Apply style.
  				var style = new CKEDITOR.style({ element: 'a', attributes: attributes } );
  				style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
  				style.applyToRange( range );
  				range.select();
  			} else {
  				// We're only editing an existing link, so just overwrite the attributes.
  				var element = this._.selectedElement,
  					href = element.data( 'cke-saved-href' ),
  					textView = element.getHtml();
  
  				element.setAttributes( attributes );
  				element.removeAttributes( removeAttributes );
  
  				if ( data.adv && data.adv.advName && CKEDITOR.plugins.link.synAnchorSelector )
  					element.addClass( element.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' );
  
  				// Update text view when user changes protocol (#4612).
  				if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 ) {
  					// Short mailto link text view (#5736).
  					element.setHtml( data.type == 'email' ? data.email.address : attributes[ 'data-cke-saved-href' ] );
  				}
  
  				selection.selectElement( element );
  				delete this._.selectedElement;
  			}
  		},
  		onLoad: function() {
  			if ( !editor.config.linkShowAdvancedTab )
  				this.hidePage( 'advanced' ); //Hide Advanded tab.
  
  			if ( !editor.config.linkShowTargetTab )
  				this.hidePage( 'target' ); //Hide Target tab.
  		},
  		// Inital focus on 'url' field if link is of type URL.
  		onFocus: function() {
  			var linkType = this.getContentElement( 'info', 'linkType' ),
  				urlField;
  			if ( linkType && linkType.getValue() == 'url' ) {
  				urlField = this.getContentElement( 'info', 'url' );
  				urlField.select();
  			}
  		}
  	};
  });
  
  /**
   * The e-mail address anti-spam protection option. The protection will be
   * applied when creating or modifying e-mail links through the editor interface.
   *
   * Two methods of protection can be choosed:
   *
   * 1. The e-mail parts (name, domain and any other query string) are
   *     assembled into a function call pattern. Such function must be
   *     provided by the developer in the pages that will use the contents.
   * 2. Only the e-mail address is obfuscated into a special string that
   *     has no meaning for humans or spam bots, but which is properly
   *     rendered and accepted by the browser.
   *
   * Both approaches require JavaScript to be enabled.
   *
   *		// href="mailto:tester@ckeditor.com?subject=subject&body=body"
   *		config.emailProtection = '';
   *
   *		// href="<a href=\"javascript:void(location.href=\'mailto:\'+String.fromCharCode(116,101,115,116,101,114,64,99,107,101,100,105,116,111,114,46,99,111,109)+\'?subject=subject&body=body\')\">e-mail</a>"
   *		config.emailProtection = 'encode';
   *
   *		// href="javascript:mt('tester','ckeditor.com','subject','body')"
   *		config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';
   *
   * @since 3.1
   * @cfg {String} [emailProtection='' (empty string = disabled)]
   * @member CKEDITOR.config
   */