/*\r
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
CKEDITOR.dialog.add( 'link', function( editor )\r
{\r
+ var plugin = CKEDITOR.plugins.link;\r
// Handles the event when the "Target" selection box is changed.\r
var targetChanged = function()\r
{\r
return;\r
\r
popupFeatures = popupFeatures.getElement();\r
+ popupFeatures.hide();\r
+ targetName.setValue( '' );\r
+\r
+ switch ( value )\r
+ {\r
+ case 'frame' :\r
+ targetName.setLabel( editor.lang.link.targetFrameName );\r
+ targetName.getElement().show();\r
+ break;\r
+ case 'popup' :\r
+ popupFeatures.show();\r
+ targetName.setLabel( editor.lang.link.targetPopupName );\r
+ targetName.getElement().show();\r
+ break;\r
+ default :\r
+ targetName.setValue( value );\r
+ targetName.getElement().hide();\r
+ break;\r
+ }\r
\r
- if ( value == 'popup' )\r
- {\r
- popupFeatures.show();\r
- targetName.setLabel( editor.lang.link.targetPopupName );\r
- }\r
- else\r
- {\r
- popupFeatures.hide();\r
- targetName.setLabel( editor.lang.link.targetFrameName );\r
- this.getDialog().setValueOf( 'target', 'linkTargetName', value.charAt( 0 ) == '_' ? value : '' );\r
- }\r
};\r
\r
// Handles the event when the "Type" selection box is changed.\r
var dialog = this.getDialog(),\r
partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],\r
typeValue = this.getValue(),\r
- uploadInitiallyHidden = dialog.definition.getContents( 'upload' ).hidden;\r
+ uploadTab = dialog.definition.getContents( 'upload' ),\r
+ uploadInitiallyHidden = uploadTab && uploadTab.hidden;\r
\r
if ( typeValue == 'url' )\r
{\r
};\r
\r
// Loads the parameters in a selected link to the link dialog fields.\r
- var emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,\r
+ var javascriptProtocolRegex = /^javascript:/,\r
+ emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,\r
emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,\r
emailBodyRegex = /body=([^;?:@&=$,\/]*)/,\r
anchorRegex = /^#(.*)$/,\r
urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,\r
- selectableTargets = /^(_(?:self|top|parent|blank))$/;\r
+ selectableTargets = /^(_(?:self|top|parent|blank))$/,\r
+ encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,\r
+ functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/;\r
\r
var popupRegex =\r
/\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;\r
\r
var parseLink = function( editor, element )\r
{\r
- var href = element ? ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) : '',\r
- emailMatch = '',\r
- anchorMatch = '',\r
- urlMatch = false,\r
+ var href = ( element && ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) ) || '',\r
+ javascriptMatch,\r
+ emailMatch,\r
+ anchorMatch,\r
+ urlMatch,\r
retval = {};\r
\r
- if ( href )\r
+ if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) )\r
{\r
- emailMatch = href.match( emailRegex );\r
- anchorMatch = href.match( anchorRegex );\r
- urlMatch = href.match( urlRegex );\r
- }\r
+ if ( emailProtection == 'encode' )\r
+ {\r
+ href = href.replace( encodedEmailLinkRegex,\r
+ function ( match, protectedAddress, rest )\r
+ {\r
+ return 'mailto:' +\r
+ String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) +\r
+ ( rest && unescapeSingleQuote( rest ) );\r
+ });\r
+ }\r
+ // Protected email link as function call.\r
+ else if ( emailProtection )\r
+ {\r
+ href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs )\r
+ {\r
+ if ( funcName == compiledProtectionFunction.name )\r
+ {\r
+ retval.type = 'email';\r
+ var email = retval.email = {};\r
\r
- // Load the link type and URL.\r
- if ( emailMatch )\r
- {\r
- var subjectMatch = href.match( emailSubjectRegex ),\r
- bodyMatch = href.match( emailBodyRegex );\r
- retval.type = 'email';\r
- retval.email = {};\r
- retval.email.address = emailMatch[1];\r
- subjectMatch && ( retval.email.subject = decodeURIComponent( subjectMatch[1] ) );\r
- bodyMatch && ( retval.email.body = decodeURIComponent( bodyMatch[1] ) );\r
- }\r
- else if ( anchorMatch )\r
- {\r
- retval.type = 'anchor';\r
- retval.anchor = {};\r
- retval.anchor.name = retval.anchor.id = anchorMatch[1];\r
+ var paramRegex = /[^,\s]+/g,\r
+ paramQuoteRegex = /(^')|('$)/g,\r
+ paramsMatch = funcArgs.match( paramRegex ),\r
+ paramsMatchLength = paramsMatch.length,\r
+ paramName,\r
+ paramVal;\r
+\r
+ for ( var i = 0; i < paramsMatchLength; i++ )\r
+ {\r
+ paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) );\r
+ paramName = compiledProtectionFunction.params[ i ].toLowerCase();\r
+ email[ paramName ] = paramVal;\r
+ }\r
+ email.address = [ email.name, email.domain ].join( '@' );\r
+ }\r
+ } );\r
+ }\r
}\r
- else if ( href && urlMatch ) // urlRegex matches empty strings, so need to check for href as well.\r
+\r
+ if ( !retval.type )\r
{\r
- retval.type = 'url';\r
- retval.url = {};\r
- retval.url.protocol = urlMatch[1];\r
- retval.url.url = urlMatch[2];\r
+ if ( ( anchorMatch = href.match( anchorRegex ) ) )\r
+ {\r
+ retval.type = 'anchor';\r
+ retval.anchor = {};\r
+ retval.anchor.name = retval.anchor.id = anchorMatch[1];\r
+ }\r
+ // Protected email link as encoded string.\r
+ else if ( ( emailMatch = href.match( emailRegex ) ) )\r
+ {\r
+ var subjectMatch = href.match( emailSubjectRegex ),\r
+ bodyMatch = href.match( emailBodyRegex );\r
+\r
+ retval.type = 'email';\r
+ var email = ( retval.email = {} );\r
+ email.address = emailMatch[ 1 ];\r
+ subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) );\r
+ bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) );\r
+ }\r
+ // urlRegex matches empty strings, so need to check for href as well.\r
+ else if ( href && ( urlMatch = href.match( urlRegex ) ) )\r
+ {\r
+ retval.type = 'url';\r
+ retval.url = {};\r
+ retval.url.protocol = urlMatch[1];\r
+ retval.url.url = urlMatch[2];\r
+ }\r
+ else\r
+ retval.type = 'url';\r
}\r
- else\r
- retval.type = 'url';\r
\r
// Load target and popup settings.\r
if ( element )\r
realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ),\r
anchors = retval.anchors = [];\r
\r
- for( var i = 0; i < elements.count() ; i++ )\r
+ for ( var i = 0; i < elements.count() ; i++ )\r
{\r
var item = elements.getItem( i );\r
if ( item.getAttribute( '_cke_realelement' ) && item.getAttribute( '_cke_real_element_type' ) == 'anchor' )\r
return commitParams.call( this, 'adv', data );\r
};\r
\r
+ function unescapeSingleQuote( str )\r
+ {\r
+ return str.replace( /\\'/g, '\'' );\r
+ }\r
+\r
+ function escapeSingleQuote( str )\r
+ {\r
+ return str.replace( /'/g, '\\$&' );\r
+ }\r
+\r
+ var emailProtection = editor.config.emailProtection || '';\r
+\r
+ // Compile the protection function pattern.\r
+ if ( emailProtection && emailProtection != 'encode' )\r
+ {\r
+ var compiledProtectionFunction = {};\r
+\r
+ emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params )\r
+ {\r
+ compiledProtectionFunction.name = funcName;\r
+ compiledProtectionFunction.params = [];\r
+ params.replace( /[^,\s]+/g, function( param )\r
+ {\r
+ compiledProtectionFunction.params.push( param );\r
+ } );\r
+ } );\r
+ }\r
+\r
+ function protectEmailLinkAsFunction( email )\r
+ {\r
+ var retval,\r
+ name = compiledProtectionFunction.name,\r
+ params = compiledProtectionFunction.params,\r
+ paramName,\r
+ paramValue;\r
+\r
+ retval = [ name, '(' ];\r
+ for ( var i = 0; i < params.length; i++ )\r
+ {\r
+ paramName = params[ i ].toLowerCase();\r
+ paramValue = email[ paramName ];\r
+\r
+ i > 0 && retval.push( ',' );\r
+ retval.push( '\'',\r
+ paramValue ?\r
+ escapeSingleQuote( encodeURIComponent( email[ paramName ] ) )\r
+ : '',\r
+ '\'');\r
+ }\r
+ retval.push( ')' );\r
+ return retval.join( '' );\r
+ }\r
+\r
+ function protectEmailAddressAsEncodedString( address )\r
+ {\r
+ var charCode,\r
+ length = address.length,\r
+ encodedChars = [];\r
+ for ( var i = 0; i < length; i++ )\r
+ {\r
+ charCode = address.charCodeAt( i );\r
+ encodedChars.push( charCode );\r
+ }\r
+ return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')';\r
+ }\r
+\r
return {\r
title : editor.lang.link.title,\r
minWidth : 350,\r
'default' : 'url',\r
items :\r
[\r
- [ editor.lang.common.url, 'url' ],\r
+ [ editor.lang.link.toUrl, 'url' ],\r
[ editor.lang.link.toAnchor, 'anchor' ],\r
[ editor.lang.link.toEmail, 'email' ]\r
],\r
type : 'select',\r
label : editor.lang.common.protocol,\r
'default' : 'http://',\r
- style : 'width : 100%;',\r
items :\r
[\r
- [ 'http://' ],\r
- [ 'https://' ],\r
- [ 'ftp://' ],\r
- [ 'news://' ],\r
- [ '<other>', '' ]\r
+ // Force 'ltr' for protocol names in BIDI. (#5433)\r
+ [ 'http://\u200E', 'http://' ],\r
+ [ 'https://\u200E', 'https://' ],\r
+ [ 'ftp://\u200E', 'ftp://' ],\r
+ [ 'news://\u200E', 'news://' ],\r
+ [ editor.lang.link.other , '' ]\r
],\r
setup : function( data )\r
{\r
if ( data.url )\r
- this.setValue( data.url.protocol );\r
+ this.setValue( data.url.protocol || '' );\r
},\r
commit : function( data )\r
{\r
type : 'text',\r
id : 'url',\r
label : editor.lang.common.url,\r
+ required: true,\r
onLoad : function ()\r
{\r
this.allowOnChange = true;\r
var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),\r
url = this.getValue(),\r
urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/gi,\r
- urlOnChangeTestOther = /^((javascript:)|[#\/\.])/gi;\r
+ urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/gi;\r
\r
var protocol = urlOnChangeProtocol.exec( url );\r
if ( protocol )\r
this.setValue( data.url.url );\r
this.allowOnChange = true;\r
\r
- var linkType = this.getDialog().getContentElement( 'info', 'linkType' );\r
- if ( linkType && linkType.getValue() == 'url' )\r
- this.select();\r
-\r
},\r
commit : function( data )\r
{\r
+ // IE will not trigger the onChange event if the mouse has been used\r
+ // to carry all the operations #4724\r
+ this.onChange();\r
+\r
if ( !data.url )\r
data.url = {};\r
\r
children :\r
[\r
{\r
- type : 'html',\r
+ type : 'fieldset',\r
id : 'selectAnchorText',\r
- html : CKEDITOR.tools.htmlEncode( editor.lang.link.selectAnchor ),\r
+ label : editor.lang.link.selectAnchor,\r
setup : function( data )\r
{\r
if ( data.anchors.length > 0 )\r
this.getElement().show();\r
else\r
this.getElement().hide();\r
- }\r
- },\r
- {\r
- type : 'html',\r
- id : 'noAnchors',\r
- style : 'text-align: center;',\r
- html : '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.link.noAnchors ) + '</div>',\r
- setup : function( data )\r
- {\r
- if ( data.anchors.length < 1 )\r
- this.getElement().show();\r
- else\r
- this.getElement().hide();\r
- }\r
- },\r
- {\r
- type : 'hbox',\r
- id : 'selectAnchor',\r
+ },\r
children :\r
[\r
{\r
- type : 'select',\r
- id : 'anchorName',\r
- 'default' : '',\r
- label : editor.lang.link.anchorName,\r
- style : 'width: 100%;',\r
- items :\r
+ type : 'hbox',\r
+ id : 'selectAnchor',\r
+ children :\r
[\r
- [ '' ]\r
- ],\r
- setup : function( data )\r
- {\r
- this.clear();\r
- this.add( '' );\r
- for ( var i = 0 ; i < data.anchors.length ; i++ )\r
{\r
- if ( data.anchors[i].name )\r
- this.add( data.anchors[i].name );\r
+ type : 'select',\r
+ id : 'anchorName',\r
+ 'default' : '',\r
+ label : editor.lang.link.anchorName,\r
+ style : 'width: 100%;',\r
+ items :\r
+ [\r
+ [ '' ]\r
+ ],\r
+ setup : function( data )\r
+ {\r
+ this.clear();\r
+ this.add( '' );\r
+ for ( var i = 0 ; i < data.anchors.length ; i++ )\r
+ {\r
+ if ( data.anchors[i].name )\r
+ this.add( data.anchors[i].name );\r
+ }\r
+\r
+ if ( data.anchor )\r
+ this.setValue( data.anchor.name );\r
+\r
+ var linkType = this.getDialog().getContentElement( 'info', 'linkType' );\r
+ if ( linkType && linkType.getValue() == 'email' )\r
+ this.focus();\r
+ },\r
+ commit : function( data )\r
+ {\r
+ if ( !data.anchor )\r
+ data.anchor = {};\r
+\r
+ data.anchor.name = this.getValue();\r
+ }\r
+ },\r
+ {\r
+ type : 'select',\r
+ id : 'anchorId',\r
+ 'default' : '',\r
+ label : editor.lang.link.anchorId,\r
+ style : 'width: 100%;',\r
+ items :\r
+ [\r
+ [ '' ]\r
+ ],\r
+ setup : function( data )\r
+ {\r
+ this.clear();\r
+ this.add( '' );\r
+ for ( var i = 0 ; i < data.anchors.length ; i++ )\r
+ {\r
+ if ( data.anchors[i].id )\r
+ this.add( data.anchors[i].id );\r
+ }\r
+\r
+ if ( data.anchor )\r
+ this.setValue( data.anchor.id );\r
+ },\r
+ commit : function( data )\r
+ {\r
+ if ( !data.anchor )\r
+ data.anchor = {};\r
+\r
+ data.anchor.id = this.getValue();\r
+ }\r
}\r
-\r
- if ( data.anchor )\r
- this.setValue( data.anchor.name );\r
-\r
- var linkType = this.getDialog().getContentElement( 'info', 'linkType' );\r
- if ( linkType && linkType.getValue() == 'email' )\r
- this.focus();\r
- },\r
- commit : function( data )\r
- {\r
- if ( !data.anchor )\r
- data.anchor = {};\r
-\r
- data.anchor.name = this.getValue();\r
- }\r
- },\r
- {\r
- type : 'select',\r
- id : 'anchorId',\r
- 'default' : '',\r
- label : editor.lang.link.anchorId,\r
- style : 'width: 100%;',\r
- items :\r
- [\r
- [ '' ]\r
],\r
setup : function( data )\r
{\r
- this.clear();\r
- this.add( '' );\r
- for ( var i = 0 ; i < data.anchors.length ; i++ )\r
- {\r
- if ( data.anchors[i].id )\r
- this.add( data.anchors[i].id );\r
- }\r
-\r
- if ( data.anchor )\r
- this.setValue( data.anchor.id );\r
- },\r
- commit : function( data )\r
- {\r
- if ( !data.anchor )\r
- data.anchor = {};\r
-\r
- data.anchor.id = this.getValue();\r
+ if ( data.anchors.length > 0 )\r
+ this.getElement().show();\r
+ else\r
+ this.getElement().hide();\r
}\r
}\r
- ],\r
+ ]\r
+ },\r
+ {\r
+ type : 'html',\r
+ id : 'noAnchors',\r
+ style : 'text-align: center;',\r
+ html : '<div role="label" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( editor.lang.link.noAnchors ) + '</div>',\r
+ // Focus the first element defined in above html.\r
+ focus : true,\r
setup : function( data )\r
{\r
- if ( data.anchors.length > 0 )\r
+ if ( data.anchors.length < 1 )\r
this.getElement().show();\r
else\r
this.getElement().hide();\r
type : 'text',\r
id : 'emailAddress',\r
label : editor.lang.link.emailAddress,\r
+ required : true,\r
validate : function()\r
{\r
var dialog = this.getDialog();\r
{\r
type : 'select',\r
id : 'linkTargetType',\r
- label : editor.lang.link.target,\r
+ label : editor.lang.common.target,\r
'default' : 'notSet',\r
style : 'width : 100%;',\r
'items' :\r
[\r
- [ editor.lang.link.targetNotSet, 'notSet' ],\r
+ [ editor.lang.common.notSet, 'notSet' ],\r
[ editor.lang.link.targetFrame, 'frame' ],\r
[ editor.lang.link.targetPopup, 'popup' ],\r
- [ editor.lang.link.targetNew, '_blank' ],\r
- [ editor.lang.link.targetTop, '_top' ],\r
- [ editor.lang.link.targetSelf, '_self' ],\r
- [ editor.lang.link.targetParent, '_parent' ]\r
+ [ editor.lang.common.targetNew, '_blank' ],\r
+ [ editor.lang.common.targetTop, '_top' ],\r
+ [ editor.lang.common.targetSelf, '_self' ],\r
+ [ editor.lang.common.targetParent, '_parent' ]\r
],\r
onChange : targetChanged,\r
setup : function( data )\r
if ( !data.target )\r
data.target = {};\r
\r
- data.target.name = this.getValue();\r
+ data.target.name = this.getValue().replace(/\W/gi, '');\r
}\r
}\r
]\r
children :\r
[\r
{\r
- type : 'html',\r
- html : CKEDITOR.tools.htmlEncode( editor.lang.link.popupFeatures )\r
- },\r
- {\r
- type : 'hbox',\r
- children :\r
- [\r
- {\r
- type : 'checkbox',\r
- id : 'resizable',\r
- label : editor.lang.link.popupResizable,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
- },\r
- {\r
- type : 'checkbox',\r
- id : 'status',\r
- label : editor.lang.link.popupStatusBar,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
-\r
- }\r
- ]\r
- },\r
- {\r
- type : 'hbox',\r
+ type : 'fieldset',\r
+ label : editor.lang.link.popupFeatures,\r
children :\r
[\r
{\r
- type : 'checkbox',\r
- id : 'location',\r
- label : editor.lang.link.popupLocationBar,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'resizable',\r
+ label : editor.lang.link.popupResizable,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'status',\r
+ label : editor.lang.link.popupStatusBar,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
},\r
{\r
- type : 'checkbox',\r
- id : 'toolbar',\r
- label : editor.lang.link.popupToolbar,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'location',\r
+ label : editor.lang.link.popupLocationBar,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
- }\r
- ]\r
- },\r
- {\r
- type : 'hbox',\r
- children :\r
- [\r
- {\r
- type : 'checkbox',\r
- id : 'menubar',\r
- label : editor.lang.link.popupMenuBar,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'toolbar',\r
+ label : editor.lang.link.popupToolbar,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
},\r
{\r
- type : 'checkbox',\r
- id : 'fullscreen',\r
- label : editor.lang.link.popupFullScreen,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'menubar',\r
+ label : editor.lang.link.popupMenuBar,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
- }\r
- ]\r
- },\r
- {\r
- type : 'hbox',\r
- children :\r
- [\r
- {\r
- type : 'checkbox',\r
- id : 'scrollbars',\r
- label : editor.lang.link.popupScrollBars,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'fullscreen',\r
+ label : editor.lang.link.popupFullScreen,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
},\r
{\r
- type : 'checkbox',\r
- id : 'dependent',\r
- label : editor.lang.link.popupDependent,\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'scrollbars',\r
+ label : editor.lang.link.popupScrollBars,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
- }\r
- ]\r
- },\r
- {\r
- type : 'hbox',\r
- children :\r
- [\r
- {\r
- type : 'text',\r
- widths : [ '30%', '70%' ],\r
- labelLayout : 'horizontal',\r
- label : editor.lang.link.popupWidth,\r
- id : 'width',\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'dependent',\r
+ label : editor.lang.link.popupDependent,\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
},\r
{\r
- type : 'text',\r
- labelLayout : 'horizontal',\r
- widths : [ '55%', '45%' ],\r
- label : editor.lang.link.popupLeft,\r
- id : 'left',\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
-\r
- }\r
- ]\r
- },\r
- {\r
- type : 'hbox',\r
- children :\r
- [\r
- {\r
- type : 'text',\r
- labelLayout : 'horizontal',\r
- widths : [ '30%', '70%' ],\r
- label : editor.lang.link.popupHeight,\r
- id : 'height',\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'text',\r
+ widths : [ '30%', '70%' ],\r
+ labelLayout : 'horizontal',\r
+ label : editor.lang.link.popupWidth,\r
+ id : 'width',\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
+\r
+ },\r
+ {\r
+ type : 'text',\r
+ labelLayout : 'horizontal',\r
+ widths : [ '55%', '45%' ],\r
+ label : editor.lang.link.popupLeft,\r
+ id : 'left',\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
},\r
{\r
- type : 'text',\r
- labelLayout : 'horizontal',\r
- label : editor.lang.link.popupTop,\r
- widths : [ '55%', '45%' ],\r
- id : 'top',\r
- setup : setupPopupParams,\r
- commit : commitPopupParams\r
+ type : 'hbox',\r
+ children :\r
+ [\r
+ {\r
+ type : 'text',\r
+ labelLayout : 'horizontal',\r
+ widths : [ '30%', '70%' ],\r
+ label : editor.lang.link.popupHeight,\r
+ id : 'height',\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
+\r
+ },\r
+ {\r
+ type : 'text',\r
+ labelLayout : 'horizontal',\r
+ label : editor.lang.link.popupTop,\r
+ widths : [ '55%', '45%' ],\r
+ id : 'top',\r
+ setup : setupPopupParams,\r
+ commit : commitPopupParams\r
\r
+ }\r
+ ]\r
}\r
]\r
}\r
style : 'width:110px',\r
items :\r
[\r
- [ editor.lang.link.langDirNotSet, '' ],\r
+ [ editor.lang.common.notSet, '' ],\r
[ editor.lang.link.langDirLTR, 'ltr' ],\r
[ editor.lang.link.langDirRTL, 'rtl' ]\r
],\r
\r
var editor = this.getParentEditor(),\r
selection = editor.getSelection(),\r
- ranges = selection.getRanges(),\r
- element = null,\r
- me = this;\r
+ element = null;\r
+\r
// Fill in all the relevant fields if there's already one link selected.\r
- if ( ranges.length == 1 )\r
+ if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )\r
+ selection.selectElement( element );\r
+ else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )\r
+ && element.getAttribute( '_cke_real_element_type' )\r
+ && element.getAttribute( '_cke_real_element_type' ) == 'anchor' )\r
{\r
-\r
- var rangeRoot = ranges[0].getCommonAncestor( true );\r
- element = rangeRoot.getAscendant( 'a', true );\r
- if ( element && element.getAttribute( 'href' ) )\r
- {\r
- selection.selectElement( element );\r
- }\r
- else if ( ( element = rangeRoot.getAscendant( 'img', true ) ) &&\r
- element.getAttribute( '_cke_real_element_type' ) &&\r
- element.getAttribute( '_cke_real_element_type' ) == 'anchor' )\r
- {\r
- this.fakeObj = element;\r
- element = editor.restoreRealElement( this.fakeObj );\r
- selection.selectElement( this.fakeObj );\r
- }\r
- else\r
- element = null;\r
+ this.fakeObj = element;\r
+ element = editor.restoreRealElement( this.fakeObj );\r
+ selection.selectElement( this.fakeObj );\r
}\r
+ else\r
+ element = null;\r
\r
this.setupContent( parseLink.apply( this, [ editor, element ] ) );\r
},\r
var attributes = { href : 'javascript:void(0)/*' + CKEDITOR.tools.getNextNumber() + '*/' },\r
removeAttributes = [],\r
data = { href : attributes.href },\r
- me = this, editor = this.getParentEditor();\r
+ me = this,\r
+ editor = this.getParentEditor();\r
\r
this.commitContent( data );\r
\r
attributes._cke_saved_href = '#' + ( name || id || '' );\r
break;\r
case 'email':\r
- var address = ( data.email && data.email.address ),\r
- subject = ( data.email && encodeURIComponent( data.email.subject || '' ) ),\r
- body = ( data.email && encodeURIComponent( data.email.body || '' ) ),\r
- linkList = [ 'mailto:', address ];\r
- if ( subject || body )\r
+\r
+ var linkHref,\r
+ email = data.email,\r
+ address = email.address;\r
+\r
+ switch( emailProtection )\r
{\r
- var argList = [];\r
- linkList.push( '?' );\r
- subject && argList.push( 'subject=' + subject );\r
- body && argList.push( 'body=' + body );\r
- linkList.push( argList.join( '&' ) );\r
+ case '' :\r
+ case 'encode' :\r
+ {\r
+ var subject = encodeURIComponent( email.subject || '' ),\r
+ body = encodeURIComponent( email.body || '' );\r
+\r
+ // Build the e-mail parameters first.\r
+ var argList = [];\r
+ subject && argList.push( 'subject=' + subject );\r
+ body && argList.push( 'body=' + body );\r
+ argList = argList.length ? '?' + argList.join( '&' ) : '';\r
+\r
+ if ( emailProtection == 'encode' )\r
+ {\r
+ linkHref = [ 'javascript:void(location.href=\'mailto:\'+',\r
+ protectEmailAddressAsEncodedString( address ) ];\r
+ // parameters are optional.\r
+ argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' );\r
+\r
+ linkHref.push( ')' );\r
+ }\r
+ else\r
+ linkHref = [ 'mailto:', address, argList ];\r
+\r
+ break;\r
+ }\r
+ default :\r
+ {\r
+ // Separating name and domain.\r
+ var nameAndDomain = address.split( '@', 2 );\r
+ email.name = nameAndDomain[ 0 ];\r
+ email.domain = nameAndDomain[ 1 ];\r
+\r
+ linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ];\r
+ }\r
}\r
- attributes._cke_saved_href = linkList.join( '' );\r
+\r
+ attributes._cke_saved_href = linkHref.join( '' );\r
break;\r
- default:\r
}\r
\r
// Popups and target.\r
addFeature( 'top' );\r
\r
onclickList.push( featureList.join( ',' ), '\'); return false;' );\r
- attributes[ CKEDITOR.env.ie || CKEDITOR.env.webkit ? '_cke_pa_onclick' : 'onclick' ] = onclickList.join( '' );\r
+ attributes[ '_cke_pa_onclick' ] = onclickList.join( '' );\r
}\r
else\r
{\r
if ( data.target.type != 'notSet' && data.target.name )\r
attributes.target = data.target.name;\r
+ else\r
+ removeAttributes.push( 'target' );\r
+\r
removeAttributes.push( '_cke_pa_onclick', 'onclick' );\r
}\r
}\r
{\r
// Create element if current selection is collapsed.\r
var selection = editor.getSelection(),\r
- ranges = selection.getRanges();\r
+ ranges = selection.getRanges( true );\r
if ( ranges.length == 1 && ranges[0].collapsed )\r
{\r
- var text = new CKEDITOR.dom.text( attributes._cke_saved_href, editor.document );\r
+ // Short mailto link text view (#5736).\r
+ var text = new CKEDITOR.dom.text( data.type == 'email' ?\r
+ data.email.address : attributes._cke_saved_href, editor.document );\r
ranges[0].insertNode( text );\r
ranges[0].selectNodeContents( text );\r
selection.selectRanges( ranges );\r
else\r
{\r
// We're only editing an existing link, so just overwrite the attributes.\r
- var element = this._.selectedElement;\r
+ var element = this._.selectedElement,\r
+ href = element.getAttribute( '_cke_saved_href' ),\r
+ textView = element.getHtml();\r
\r
// IE BUG: Setting the name attribute to an existing link doesn't work.\r
// Must re-create the link from weired syntax to workaround.\r
\r
element.setAttributes( attributes );\r
element.removeAttributes( removeAttributes );\r
-\r
+ // Update text view when user changes protocol (#4612).\r
+ if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 )\r
+ {\r
+ // Short mailto link text view (#5736).\r
+ element.setHtml( data.type == 'email' ?\r
+ data.email.address : attributes._cke_saved_href );\r
+ }\r
// Make the element display as an anchor if a name has been set.\r
if ( element.getAttribute( 'name' ) )\r
element.addClass( 'cke_anchor' );\r
if ( !editor.config.linkShowTargetTab )\r
this.hidePage( 'target' ); //Hide Target tab.\r
\r
+ },\r
+ // Inital focus on 'url' field if link is of type URL.\r
+ onFocus : function()\r
+ {\r
+ var linkType = this.getContentElement( 'info', 'linkType' ),\r
+ urlField;\r
+ if ( linkType && linkType.getValue( ) == 'url' )\r
+ {\r
+ urlField = this.getContentElement( 'info', 'url' );\r
+ urlField.select();\r
+ }\r
}\r
};\r
-} );\r
+});\r
+\r
+/**\r
+ * The e-mail address anti-spam protection option. The protection will be\r
+ * applied when creating or modifying e-mail links through the editor interface.<br>\r
+ * Two methods of protection can be choosed:\r
+ * <ol> <li>The e-mail parts (name, domain and any other query string) are\r
+ * assembled into a function call pattern. Such function must be\r
+ * provided by the developer in the pages that will use the contents.\r
+ * <li>Only the e-mail address is obfuscated into a special string that\r
+ * has no meaning for humans or spam bots, but which is properly\r
+ * rendered and accepted by the browser.</li></ol>\r
+ * Both approaches require JavaScript to be enabled.\r
+ * @name CKEDITOR.config.emailProtection\r
+ * @since 3.1\r
+ * @type String\r
+ * @default '' (empty string = disabled)\r
+ * @example\r
+ * // href="mailto:tester@ckeditor.com?subject=subject&body=body"\r
+ * config.emailProtection = '';\r
+ * @example\r
+ * // 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>"\r
+ * config.emailProtection = 'encode';\r
+ * @example\r
+ * // href="javascript:mt('tester','ckeditor.com','subject','body')"\r
+ * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';\r
+ */\r