/*\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
emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,\r
emailBodyRegex = /body=([^;?:@&=$,\/]*)/,\r
anchorRegex = /^#(.*)$/,\r
- urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,\r
- selectableTargets = /^(_(?:self|top|parent|blank))$/;\r
+ urlRegex = /^(?!javascript)((?:http|https|ftp|news):\/\/)?(.*)$/,\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
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
+ emailMatch,\r
+ anchorMatch,\r
+ urlMatch,\r
retval = {};\r
\r
- if ( href )\r
- {\r
- emailMatch = href.match( emailRegex );\r
- anchorMatch = href.match( anchorRegex );\r
- urlMatch = href.match( urlRegex );\r
- }\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
+ if ( ( anchorMatch = href.match( anchorRegex ) ) )\r
{\r
retval.type = 'anchor';\r
retval.anchor = {};\r
retval.anchor.name = retval.anchor.id = anchorMatch[1];\r
}\r
- else if ( href && urlMatch ) // urlRegex matches empty strings, so need to check for href as well.\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
+ // Protected email link as encoded string.\r
+ else if ( !emailProtection || emailProtection == 'encode' )\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
+\r
+ emailMatch = href.match( emailRegex );\r
+\r
+ if( emailMatch )\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
+ }\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
+ 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
else\r
retval.type = 'url';\r
\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
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
\r
}\r
};\r
-} );\r
+});\r
+\r
+/**\r
+ * The e-mail address anti-spam protection option.\r
+ * @name CKEDITOR.config.emailProtection\r
+ * @type {String}\r
+ * Two forms of protection could be choosed from :\r
+ * 1. The whole address parts ( name, domain with any other query string ) are assembled into a\r
+ * function call pattern which invoke you own provided function, with the specified arguments.\r
+ * 2. Only the e-mail address is obfuscated into unicode code point sequences, replacement are\r
+ * done by a String.fromCharCode() call.\r
+ * Note: Both approaches require JavaScript to be enabled.\r
+ * @default ''\r
+ * @example\r
+ * config.emailProtection = '';\r
+ * // href="mailto:tester@ckeditor.com?subject=subject&body=body"\r
+ * config.emailProtection = 'encode';\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 = 'mt(NAME,DOMAIN,SUBJECT,BODY)';\r
+ * // href="javascript:mt('tester','ckeditor.com','subject','body')"\r
+ */\r