(
	function() {
		CKEDITOR.plugins.add('my_link');

		CKEDITOR.on(
			'dialogDefinition',
			function(ev) {
				var dialogName = ev.data.name;
				var dialogDefinition = ev.data.definition;
				var editor = this;

				if (dialogName == 'link') {
					var plugin = CKEDITOR.plugins.link;
					var $internal_page_mapping = {}; // mapping between @@PAGE_ID@@ and real url (for anchor loading)

					var load_page_anchors = function ($page_id, dialog) {
						var $url;
						var $tmp_iframe = $('#tmp_iframe');
						var $internal_page_anchors = dialog.getContentElement( 'info', 'internalPageAnchors' );

						var $current_anchor = $('#' + $internal_page_anchors.domId).data('anchor');

						$internal_page_anchors.clear();
						$internal_page_anchors.add( 'Loading ...', '' );

						if ( $tmp_iframe.length == 0 ) {
							$tmp_iframe = $('<iframe width="0" height="0" id="tmp_iframe"></iframe>').appendTo('body');

							$tmp_iframe.get(0).onload = function () {
								var $anchors = $('a[name]', $tmp_iframe.get(0).contentWindow.document);

								$internal_page_anchors.allowOnChange = false;
								$internal_page_anchors.clear();
								$internal_page_anchors.add( $anchors.length > 0 ? '' : '(No anchors available in the document)', '' );

								$anchors.each(
									function () {
										$internal_page_anchors.add( $(this).attr('name') );
									}
								);

								$internal_page_anchors.setValue($current_anchor);
								$internal_page_anchors.allowOnChange = true;
							}
						}

						$url = $internal_page_mapping[$page_id];
						$url += ($url.indexOf('?') ? '&' : '?') + 'skip_last_template=1';

						$tmp_iframe.attr('src', $url);

					}

					// Handles the event when the "Type" selection box is changed.
					var linkTypeChanged = function()
					{
						var dialog = this.getDialog(),
							partIds = [ 'internalOptions', 'urlOptions', 'anchorOptions', 'emailOptions' ],
							typeValue = this.getValue(),
							uploadTab = dialog.definition.getContents( 'upload' ),
							uploadInitiallyHidden = uploadTab && uploadTab.hidden;


						switch ( typeValue ) {
							case 'url':
								if ( editor.config.linkShowTargetTab )
									dialog.showPage( 'target' );
								if ( !uploadInitiallyHidden )
									dialog.showPage( 'upload' );
								break;

							case 'internal':
								if ( editor.config.linkShowTargetTab )
									dialog.showPage( 'target' );

								if ( !uploadInitiallyHidden )
									dialog.hidePage( 'upload' );

								// select internal page according to link url (on link type change)
								var $url = dialog.getContentElement('info', 'url').getValue();

								if ( $internal_page_mapping[$url] !== undefined ) {
									dialog.getContentElement('info', 'internal_page').setValue($url);
								}

								break;

							default:
								dialog.hidePage( 'target' );
								if ( !uploadInitiallyHidden )
									dialog.hidePage( 'upload' );
								break;
						}

						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();
					};

					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:([^(]+)\(([^)]+)\)$/,
						internalPageRegex = /^@@[\d]+@@(#.*$|$)/; // CUSTOM

					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 ) ) )
						{
							var emailProtection = editor.config.emailProtection || '';

							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 ] ) );
							}

							// CUSTOM: begin
							else if (  href && href.match( internalPageRegex ) )
							{
								retval.type = 'internal';
								retval.url = {};
								retval.url.protocol = '';
								retval.anchor = {};

								if ( href.match(/^(.*)#(.*)$/) ) {
									retval.url.url = RegExp.$1;
									retval.internal_anchor = RegExp.$2;
								}
								else {
									retval.url.url = href;
									retval.internal_anchor = '';
								}
							}
							// CUSTOM: end

							// 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];
							}
							// CUSTOM: begin
							else {
								retval.type = 'internal';
								retval.url = {};
								retval.url.protocol = '';
								retval.url.url = '';

								retval.internal_anchor = '';
							}
							// CUSTOM: end
						}

						// 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' );
							advAttr( 'advCSSClasses', 'class' );
							advAttr( 'advCharset', 'charset' );
							advAttr( 'advStyles', 'style' );
							advAttr( 'advRel', 'rel' );
						}

						// Find out whether we have any anchors in the editor.
						// Get all IMG elements in CK document.
						var elements = editor.document.getElementsByTag( 'img' ),
							realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ),
							anchors = retval.anchors = [];

						for ( var i = 0; i < elements.count() ; i++ )
						{
							var item = elements.getItem( i );
							if ( item.data( 'cke-realelement' ) && item.data( 'cke-real-element-type' ) == 'anchor' )
								anchors.push( editor.restoreRealElement( item ) );
						}

						for ( i = 0 ; i < realAnchors.count() ; i++ )
							anchors.push( realAnchors.getItem( i ) );

						for ( i = 0 ; i < anchors.length ; i++ )
						{
							item = anchors[ i ];
							anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) };
						}

						// Record down the selected element in the dialog.
						this._.selectedElement = element;

						return retval;
					};



					var infoTab = dialogDefinition.getContents( 'info' );
					var linkType = infoTab.get('linkType');

					linkType['items'].unshift( ['Internal Web Page', 'internal'] );
					linkType['default'] = 'internal';
					linkType['onChange'] = linkTypeChanged;

					infoTab.add(
						{
							type : 'vbox',
							id : 'internalOptions',
							children :
							[
								{
									type : 'select',
									id : 'internal_page',
									label : 'Internal Page Name',
									items : [
										[ '' ]
									],
									onChange: function ( ) {
										// place internal page ID from this dropdown into URL field of the dialog
										var dialog = this.getDialog();

										this.allowOnChange = false;
										dialog.getContentElement('info', 'url').setValue( this.getValue() );
										this.allowOnChange = true;

										load_page_anchors( this.getValue(), dialog );
									},
									setup : function( data ) {
										var $me = this;
										var $link_url = data.url ? data.url.url : '';
										var $ajax_url = CKEDITOR.basePath.replace( /core.*$/, 'admin/index.php?events[fck]=OnLoadCmsTree' );
										var $internal_page_anchors = this.getDialog().getContentElement( 'info', 'internalPageAnchors' );

										$('#' + $internal_page_anchors.domId).data('anchor', data.internal_anchor);

										$.ajax(
											{
												type: 'GET',
												url: $ajax_url,
												async: false,
												success: function ($xml) {
													$me.allowOnChange = false;
													$me.clear();
													$me.add( '' );

													$('CmsPage', $xml).each(
														function ($i) {
															var $node = $(this);
															var cms_id = $node.attr('st_id');
															var cms_page = $node.attr('path');
															var real_url = $node.attr('real_url');

															$internal_page_mapping[cms_page] = real_url;
															$me.add( $node.attr('title').replace(/&amp;/g, '&') , cms_page );

															if ( $link_url && $link_url.match(cms_page + '$') ) {
																$me.setValue(cms_page);

																load_page_anchors( cms_page, $me.getDialog() );
															}
														}
													);

													// undo CKEditor default "htmlspecialchars" on all HTML
													$('option', '#' + $me.domId).each(
														function ($index) {
															var $option = $(this),
																$level_indicator = '&mdash;',
																$html = $option.html().replace(/&amp;/g, '&'),
																$level = ($html.length - $html.replace(new RegExp($level_indicator, 'g'), '').length ) / $level_indicator.length;

															if ( $html.match(/^(.*?) (.*?) (\(.*?\))$/) ) {
																// "&mdash;&mdash; rss (news/rss.html)"
																$level_indicator = RegExp.$1;
																var $page_name = RegExp.$2,
																	$page_url = RegExp.$3;

																$html = $level_indicator + ' ' + $page_name + ' ' + $page_url;
															}

															$option.html($html);
														}
													);

													$me.allowOnChange = true;
													$me.getDialog().layout();
												},
												dataType: 'xml'
											}
										);

										var linkType = this.getDialog().getContentElement( 'info', 'linkType' );

										if ( linkType && linkType.getValue() == 'internal' ) {
											this.focus();
										}
									},
								},
								{
									type : 'select',
									id : 'internalPageAnchors',
									label : 'Select an Anchor',
									items : [
										[ '' ]
									],
									commit : function( data ) {
										data.internal_anchor = this.getValue();
										this.allowOnChange = false;
									}
								}
							]
						}
					);

					dialogDefinition['onShow'] = function() {
						this.fakeObj = false;

						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 if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )
								&& element.data( 'cke-real-element-type' )
								&& element.data( 'cke-real-element-type' ) == 'anchor' )
						{
							this.fakeObj = element;
							element = editor.restoreRealElement( this.fakeObj );
							selection.selectElement( this.fakeObj );
						}
						else
							element = null;

						this.setupContent( parseLink.apply( this, [ editor, element ] ) );
					};

					dialogDefinition['onOk'] = function() {
						var attributes = {},
							removeAttributes = [],
							data = {},
							me = this,
							editor = this.getParentEditor();

						this.commitContent( data );

						// Compose the URL.
						switch ( data.type || 'internal' )
						{
							// CUSTOM: begin
							case 'internal':
								var url = ( data.url && data.url.url ) || '';
								var anchor = data.internal_anchor || '';

								attributes[ 'data-cke-saved-href' ] = url;

								if ( anchor ) {
									attributes[ 'data-cke-saved-href' ] += '#' + anchor;
								}
								break;
							// CUSTOM: end

							case 'url':
								var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
									url = ( data.url && 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,
									emailProtection = editor.config.emailProtection || '';

								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' ];
								attributes[ 'class' ] = ( attributes[ 'class' ] ? attributes[ 'class' ] + ' ' : '' ) + 'cke_anchor';
							}
							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;
						}
					};
				}
			}
		);
	}
)();
