2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
6 CKEDITOR
.dialog
.add( 'link', function( editor
)
8 var plugin
= CKEDITOR
.plugins
.link
;
9 // Handles the event when the "Target" selection box is changed.
10 var targetChanged = function()
12 var dialog
= this.getDialog(),
13 popupFeatures
= dialog
.getContentElement( 'target', 'popupFeatures' ),
14 targetName
= dialog
.getContentElement( 'target', 'linkTargetName' ),
15 value
= this.getValue();
17 if ( !popupFeatures
|| !targetName
)
20 popupFeatures
= popupFeatures
.getElement();
22 targetName
.setValue( '' );
27 targetName
.setLabel( editor
.lang
.link
.targetFrameName
);
28 targetName
.getElement().show();
32 targetName
.setLabel( editor
.lang
.link
.targetPopupName
);
33 targetName
.getElement().show();
36 targetName
.setValue( value
);
37 targetName
.getElement().hide();
43 // Handles the event when the "Type" selection box is changed.
44 var linkTypeChanged = function()
46 var dialog
= this.getDialog(),
47 partIds
= [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
48 typeValue
= this.getValue(),
49 uploadTab
= dialog
.definition
.getContents( 'upload' ),
50 uploadInitiallyHidden
= uploadTab
&& uploadTab
.hidden
;
52 if ( typeValue
== 'url' )
54 if ( editor
.config
.linkShowTargetTab
)
55 dialog
.showPage( 'target' );
56 if ( !uploadInitiallyHidden
)
57 dialog
.showPage( 'upload' );
61 dialog
.hidePage( 'target' );
62 if ( !uploadInitiallyHidden
)
63 dialog
.hidePage( 'upload' );
66 for ( var i
= 0 ; i
< partIds
.length
; i
++ )
68 var element
= dialog
.getContentElement( 'info', partIds
[i
] );
72 element
= element
.getElement().getParent().getParent();
73 if ( partIds
[i
] == typeValue
+ 'Options' )
82 // Loads the parameters in a selected link to the link dialog fields.
83 var javascriptProtocolRegex
= /^javascript:/,
84 emailRegex
= /^mailto:([^?]+)(?:\?(.+))?$/,
85 emailSubjectRegex
= /subject=([^;?:@&=$,\/]*)/,
86 emailBodyRegex
= /body=([^;?:@&=$,\/]*)/,
87 anchorRegex
= /^#(.*)$/,
88 urlRegex
= /^((?:http|https|ftp|news):\/\/)?(.*)$/,
89 selectableTargets
= /^(_(?:self|top|parent|blank))$/,
90 encodedEmailLinkRegex
= /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
91 functionCallProtectedEmailLinkRegex
= /^javascript:([^(]+)\(([^)]+)\)$/;
94 /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
95 var popupFeaturesRegex
= /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
97 var parseLink = function( editor
, element
)
99 var href
= ( element
&& ( element
.data( 'cke-saved-href' ) || element
.getAttribute( 'href' ) ) ) || '',
106 if ( ( javascriptMatch
= href
.match( javascriptProtocolRegex
) ) )
108 if ( emailProtection
== 'encode' )
110 href
= href
.replace( encodedEmailLinkRegex
,
111 function ( match
, protectedAddress
, rest
)
114 String
.fromCharCode
.apply( String
, protectedAddress
.split( ',' ) ) +
115 ( rest
&& unescapeSingleQuote( rest
) );
118 // Protected email link as function call.
119 else if ( emailProtection
)
121 href
.replace( functionCallProtectedEmailLinkRegex
, function( match
, funcName
, funcArgs
)
123 if ( funcName
== compiledProtectionFunction
.name
)
125 retval
.type
= 'email';
126 var email
= retval
.email
= {};
128 var paramRegex
= /[^,\s]+/g,
129 paramQuoteRegex
= /(^')|('$)/g,
130 paramsMatch
= funcArgs
.match( paramRegex
),
131 paramsMatchLength
= paramsMatch
.length
,
135 for ( var i
= 0; i
< paramsMatchLength
; i
++ )
137 paramVal
= decodeURIComponent( unescapeSingleQuote( paramsMatch
[ i
].replace( paramQuoteRegex
, '' ) ) );
138 paramName
= compiledProtectionFunction
.params
[ i
].toLowerCase();
139 email
[ paramName
] = paramVal
;
141 email
.address
= [ email
.name
, email
.domain
].join( '@' );
149 if ( ( anchorMatch
= href
.match( anchorRegex
) ) )
151 retval
.type
= 'anchor';
153 retval
.anchor
.name
= retval
.anchor
.id
= anchorMatch
[1];
155 // Protected email link as encoded string.
156 else if ( ( emailMatch
= href
.match( emailRegex
) ) )
158 var subjectMatch
= href
.match( emailSubjectRegex
),
159 bodyMatch
= href
.match( emailBodyRegex
);
161 retval
.type
= 'email';
162 var email
= ( retval
.email
= {} );
163 email
.address
= emailMatch
[ 1 ];
164 subjectMatch
&& ( email
.subject
= decodeURIComponent( subjectMatch
[ 1 ] ) );
165 bodyMatch
&& ( email
.body
= decodeURIComponent( bodyMatch
[ 1 ] ) );
167 // urlRegex matches empty strings, so need to check for href as well.
168 else if ( href
&& ( urlMatch
= href
.match( urlRegex
) ) )
172 retval
.url
.protocol
= urlMatch
[1];
173 retval
.url
.url
= urlMatch
[2];
179 // Load target and popup settings.
182 var target
= element
.getAttribute( 'target' );
186 // IE BUG: target attribute is an empty string instead of null in IE if it's not set.
189 var onclick
= element
.data( 'cke-pa-onclick' ) || element
.getAttribute( 'onclick' ),
190 onclickMatch
= onclick
&& onclick
.match( popupRegex
);
193 retval
.target
.type
= 'popup';
194 retval
.target
.name
= onclickMatch
[1];
197 while ( ( featureMatch
= popupFeaturesRegex
.exec( onclickMatch
[2] ) ) )
199 // Some values should remain numbers (#7300)
200 if ( ( featureMatch
[2] == 'yes' || featureMatch
[2] == '1' ) && !( featureMatch
[1] in { height
:1, width
:1, top
:1, left
:1 } ) )
201 retval
.target
[ featureMatch
[1] ] = true;
202 else if ( isFinite( featureMatch
[2] ) )
203 retval
.target
[ featureMatch
[1] ] = featureMatch
[2];
209 var targetMatch
= target
.match( selectableTargets
);
211 retval
.target
.type
= retval
.target
.name
= target
;
214 retval
.target
.type
= 'frame';
215 retval
.target
.name
= target
;
220 var advAttr = function( inputName
, attrName
)
222 var value
= element
.getAttribute( attrName
);
223 if ( value
!== null )
224 retval
.adv
[ inputName
] = value
|| '';
226 advAttr( 'advId', 'id' );
227 advAttr( 'advLangDir', 'dir' );
228 advAttr( 'advAccessKey', 'accessKey' );
231 element
.data( 'cke-saved-name' )
232 || element
.getAttribute( 'name' )
234 advAttr( 'advLangCode', 'lang' );
235 advAttr( 'advTabIndex', 'tabindex' );
236 advAttr( 'advTitle', 'title' );
237 advAttr( 'advContentType', 'type' );
238 CKEDITOR
.plugins
.link
.synAnchorSelector
?
239 retval
.adv
.advCSSClasses
= getLinkClass( element
)
240 : advAttr( 'advCSSClasses', 'class' );
241 advAttr( 'advCharset', 'charset' );
242 advAttr( 'advStyles', 'style' );
243 advAttr( 'advRel', 'rel' );
246 // Find out whether we have any anchors in the editor.
247 var anchors
= retval
.anchors
= [],
250 // For some browsers we set contenteditable="false" on anchors, making document.anchors not to include them, so we must traverse the links manually (#7893).
251 if ( CKEDITOR
.plugins
.link
.emptyAnchorFix
)
253 var links
= editor
.document
.getElementsByTag( 'a' );
254 for ( i
= 0, count
= links
.count(); i
< count
; i
++ )
256 item
= links
.getItem( i
);
257 if ( item
.data( 'cke-saved-name' ) || item
.hasAttribute( 'name' ) )
258 anchors
.push( { name
: item
.data( 'cke-saved-name' ) || item
.getAttribute( 'name' ), id
: item
.getAttribute( 'id' ) } );
263 var anchorList
= new CKEDITOR
.dom
.nodeList( editor
.document
.$.anchors
);
264 for ( var i
= 0, count
= anchorList
.count(); i
< count
; i
++ )
266 item
= anchorList
.getItem( i
);
267 anchors
[ i
] = { name
: item
.getAttribute( 'name' ), id
: item
.getAttribute( 'id' ) };
271 if ( CKEDITOR
.plugins
.link
.fakeAnchor
)
273 var imgs
= editor
.document
.getElementsByTag( 'img' );
274 for ( i
= 0, count
= imgs
.count(); i
< count
; i
++ )
276 if ( ( item
= CKEDITOR
.plugins
.link
.tryRestoreFakeAnchor( editor
, imgs
.getItem( i
) ) ) )
277 anchors
.push( { name
: item
.getAttribute( 'name' ), id
: item
.getAttribute( 'id' ) } );
281 // Record down the selected element in the dialog.
282 this._
.selectedElement
= element
;
286 var setupParams = function( page
, data
)
289 this.setValue( data
[page
][this.id
] || '' );
292 var setupPopupParams = function( data
)
294 return setupParams
.call( this, 'target', data
);
297 var setupAdvParams = function( data
)
299 return setupParams
.call( this, 'adv', data
);
302 var commitParams = function( page
, data
)
307 data
[page
][this.id
] = this.getValue() || '';
310 var commitPopupParams = function( data
)
312 return commitParams
.call( this, 'target', data
);
315 var commitAdvParams = function( data
)
317 return commitParams
.call( this, 'adv', data
);
320 function unescapeSingleQuote( str
)
322 return str
.replace( /\\'/g, '\'' );
325 function escapeSingleQuote( str
)
327 return str
.replace( /'/g, '\\$&' );
330 var emailProtection = editor.config.emailProtection || '';
332 // Compile the protection function pattern.
333 if ( emailProtection && emailProtection != 'encode
' )
335 var compiledProtectionFunction = {};
337 emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params )
339 compiledProtectionFunction.name = funcName;
340 compiledProtectionFunction.params = [];
341 params.replace( /[^,\s]+/g, function( param )
343 compiledProtectionFunction.params.push( param );
348 function protectEmailLinkAsFunction( email )
351 name = compiledProtectionFunction.name,
352 params = compiledProtectionFunction.params,
356 retval = [ name, '(' ];
357 for ( var i = 0; i < params.length; i++ )
359 paramName = params[ i ].toLowerCase();
360 paramValue = email[ paramName ];
362 i > 0 && retval.push( ',' );
365 escapeSingleQuote( encodeURIComponent( email[ paramName ] ) )
370 return retval.join( '' );
373 function protectEmailAddressAsEncodedString( address )
376 length = address.length,
378 for ( var i = 0; i < length; i++ )
380 charCode = address.charCodeAt( i );
381 encodedChars.push( charCode );
383 return 'String
.fromCharCode(' + encodedChars.join( ',' ) + ')';
386 function getLinkClass( ele )
388 var className = ele.getAttribute( 'class' );
389 return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : '';
392 var commonLang = editor.lang.common,
393 linkLang = editor.lang.link;
396 title : linkLang.title,
402 label : linkLang.info,
403 title : linkLang.info,
409 label : linkLang.type,
413 [ linkLang.toUrl, 'url
' ],
414 [ linkLang.toAnchor, 'anchor
' ],
415 [ linkLang.toEmail, 'email
' ]
417 onChange : linkTypeChanged,
418 setup : function( data )
421 this.setValue( data.type );
423 commit : function( data )
425 data.type = this.getValue();
435 widths : [ '25%', '75%' ],
441 label : commonLang.protocol,
442 'default' : 'http
://',
445 // Force 'ltr' for protocol names in BIDI. (#5433)
446 [ 'http://\u200E', 'http://' ],
447 [ 'https://\u200E', 'https://' ],
448 [ 'ftp://\u200E', 'ftp://' ],
449 [ 'news://\u200E', 'news://' ],
450 [ linkLang
.other
, '' ]
452 setup : function( data
)
455 this.setValue( data
.url
.protocol
|| '' );
457 commit : function( data
)
462 data
.url
.protocol
= this.getValue();
468 label
: commonLang
.url
,
472 this.allowOnChange
= true;
476 this.allowOnChange
= false;
477 var protocolCmb
= this.getDialog().getContentElement( 'info', 'protocol' ),
478 url
= this.getValue(),
479 urlOnChangeProtocol
= /^(http|https|ftp|news):\/\/(?=.)/i,
480 urlOnChangeTestOther
= /^((javascript:)|[#\/\.\?])/i;
482 var protocol
= urlOnChangeProtocol
.exec( url
);
485 this.setValue( url
.substr( protocol
[ 0 ].length
) );
486 protocolCmb
.setValue( protocol
[ 0 ].toLowerCase() );
488 else if ( urlOnChangeTestOther
.test( url
) )
489 protocolCmb
.setValue( '' );
491 this.allowOnChange
= true;
493 onChange : function()
495 if ( this.allowOnChange
) // Dont't call on dialog load.
498 validate : function()
500 var dialog
= this.getDialog();
502 if ( dialog
.getContentElement( 'info', 'linkType' ) &&
503 dialog
.getValueOf( 'info', 'linkType' ) != 'url' )
506 if ( this.getDialog().fakeObj
) // Edit Anchor.
509 var func
= CKEDITOR
.dialog
.validate
.notEmpty( linkLang
.noUrl
);
510 return func
.apply( this );
512 setup : function( data
)
514 this.allowOnChange
= false;
516 this.setValue( data
.url
.url
);
517 this.allowOnChange
= true;
520 commit : function( data
)
522 // IE will not trigger the onChange event if the mouse has been used
523 // to carry all the operations #4724
529 data
.url
.url
= this.getValue();
530 this.allowOnChange
= false;
534 setup : function( data
)
536 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
537 this.getElement().show();
544 filebrowser
: 'info:url',
545 label
: commonLang
.browseServer
551 id
: 'anchorOptions',
559 id
: 'selectAnchorText',
560 label
: linkLang
.selectAnchor
,
561 setup : function( data
)
563 if ( data
.anchors
.length
> 0 )
564 this.getElement().show();
566 this.getElement().hide();
579 label
: linkLang
.anchorName
,
580 style
: 'width: 100%;',
585 setup : function( data
)
589 for ( var i
= 0 ; i
< data
.anchors
.length
; i
++ )
591 if ( data
.anchors
[i
].name
)
592 this.add( data
.anchors
[i
].name
);
596 this.setValue( data
.anchor
.name
);
598 var linkType
= this.getDialog().getContentElement( 'info', 'linkType' );
599 if ( linkType
&& linkType
.getValue() == 'email' )
602 commit : function( data
)
607 data
.anchor
.name
= this.getValue();
614 label
: linkLang
.anchorId
,
615 style
: 'width: 100%;',
620 setup : function( data
)
624 for ( var i
= 0 ; i
< data
.anchors
.length
; i
++ )
626 if ( data
.anchors
[i
].id
)
627 this.add( data
.anchors
[i
].id
);
631 this.setValue( data
.anchor
.id
);
633 commit : function( data
)
638 data
.anchor
.id
= this.getValue();
642 setup : function( data
)
644 if ( data
.anchors
.length
> 0 )
645 this.getElement().show();
647 this.getElement().hide();
655 style
: 'text-align: center;',
656 html
: '<div role="label" tabIndex="-1">' + CKEDITOR
.tools
.htmlEncode( linkLang
.noAnchors
) + '</div>',
657 // Focus the first element defined in above html.
659 setup : function( data
)
661 if ( data
.anchors
.length
< 1 )
662 this.getElement().show();
664 this.getElement().hide();
668 setup : function( data
)
670 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
671 this.getElement().hide();
683 label
: linkLang
.emailAddress
,
685 validate : function()
687 var dialog
= this.getDialog();
689 if ( !dialog
.getContentElement( 'info', 'linkType' ) ||
690 dialog
.getValueOf( 'info', 'linkType' ) != 'email' )
693 var func
= CKEDITOR
.dialog
.validate
.notEmpty( linkLang
.noEmail
);
694 return func
.apply( this );
696 setup : function( data
)
699 this.setValue( data
.email
.address
);
701 var linkType
= this.getDialog().getContentElement( 'info', 'linkType' );
702 if ( linkType
&& linkType
.getValue() == 'email' )
705 commit : function( data
)
710 data
.email
.address
= this.getValue();
716 label
: linkLang
.emailSubject
,
717 setup : function( data
)
720 this.setValue( data
.email
.subject
);
722 commit : function( data
)
727 data
.email
.subject
= this.getValue();
733 label
: linkLang
.emailBody
,
736 setup : function( data
)
739 this.setValue( data
.email
.body
);
741 commit : function( data
)
746 data
.email
.body
= this.getValue();
750 setup : function( data
)
752 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
753 this.getElement().hide();
760 label
: linkLang
.target
,
761 title
: linkLang
.target
,
766 widths
: [ '50%', '50%' ],
771 id
: 'linkTargetType',
772 label
: commonLang
.target
,
773 'default' : 'notSet',
774 style
: 'width : 100%;',
777 [ commonLang
.notSet
, 'notSet' ],
778 [ linkLang
.targetFrame
, 'frame' ],
779 [ linkLang
.targetPopup
, 'popup' ],
780 [ commonLang
.targetNew
, '_blank' ],
781 [ commonLang
.targetTop
, '_top' ],
782 [ commonLang
.targetSelf
, '_self' ],
783 [ commonLang
.targetParent
, '_parent' ]
785 onChange
: targetChanged
,
786 setup : function( data
)
789 this.setValue( data
.target
.type
|| 'notSet' );
790 targetChanged
.call( this );
792 commit : function( data
)
797 data
.target
.type
= this.getValue();
802 id
: 'linkTargetName',
803 label
: linkLang
.targetFrameName
,
805 setup : function( data
)
808 this.setValue( data
.target
.name
);
810 commit : function( data
)
815 data
.target
.name
= this.getValue().replace(/\W/gi, '');
825 id
: 'popupFeatures',
830 label
: linkLang
.popupFeatures
,
840 label
: linkLang
.popupResizable
,
841 setup
: setupPopupParams
,
842 commit
: commitPopupParams
847 label
: linkLang
.popupStatusBar
,
848 setup
: setupPopupParams
,
849 commit
: commitPopupParams
861 label
: linkLang
.popupLocationBar
,
862 setup
: setupPopupParams
,
863 commit
: commitPopupParams
869 label
: linkLang
.popupToolbar
,
870 setup
: setupPopupParams
,
871 commit
: commitPopupParams
883 label
: linkLang
.popupMenuBar
,
884 setup
: setupPopupParams
,
885 commit
: commitPopupParams
891 label
: linkLang
.popupFullScreen
,
892 setup
: setupPopupParams
,
893 commit
: commitPopupParams
905 label
: linkLang
.popupScrollBars
,
906 setup
: setupPopupParams
,
907 commit
: commitPopupParams
913 label
: linkLang
.popupDependent
,
914 setup
: setupPopupParams
,
915 commit
: commitPopupParams
926 widths
: [ '50%', '50%' ],
927 labelLayout
: 'horizontal',
928 label
: commonLang
.width
,
930 setup
: setupPopupParams
,
931 commit
: commitPopupParams
936 labelLayout
: 'horizontal',
937 widths
: [ '50%', '50%' ],
938 label
: linkLang
.popupLeft
,
940 setup
: setupPopupParams
,
941 commit
: commitPopupParams
952 labelLayout
: 'horizontal',
953 widths
: [ '50%', '50%' ],
954 label
: commonLang
.height
,
956 setup
: setupPopupParams
,
957 commit
: commitPopupParams
962 labelLayout
: 'horizontal',
963 label
: linkLang
.popupTop
,
964 widths
: [ '50%', '50%' ],
966 setup
: setupPopupParams
,
967 commit
: commitPopupParams
980 label
: linkLang
.upload
,
981 title
: linkLang
.upload
,
983 filebrowser
: 'uploadButton',
989 label
: commonLang
.upload
,
990 style
: 'height:40px',
996 label
: commonLang
.uploadSubmit
,
997 filebrowser
: 'info:url',
998 'for' : [ 'upload', 'upload' ]
1004 label
: linkLang
.advanced
,
1005 title
: linkLang
.advanced
,
1015 widths
: [ '45%', '35%', '20%' ],
1021 label
: linkLang
.id
,
1022 setup
: setupAdvParams
,
1023 commit
: commitAdvParams
1028 label
: linkLang
.langDir
,
1030 style
: 'width:110px',
1033 [ commonLang
.notSet
, '' ],
1034 [ linkLang
.langDirLTR
, 'ltr' ],
1035 [ linkLang
.langDirRTL
, 'rtl' ]
1037 setup
: setupAdvParams
,
1038 commit
: commitAdvParams
1042 id
: 'advAccessKey',
1044 label
: linkLang
.acccessKey
,
1046 setup
: setupAdvParams
,
1047 commit
: commitAdvParams
1054 widths
: [ '45%', '35%', '20%' ],
1059 label
: linkLang
.name
,
1061 setup
: setupAdvParams
,
1062 commit
: commitAdvParams
1067 label
: linkLang
.langCode
,
1071 setup
: setupAdvParams
,
1072 commit
: commitAdvParams
1077 label
: linkLang
.tabIndex
,
1081 setup
: setupAdvParams
,
1082 commit
: commitAdvParams
1096 widths
: [ '45%', '55%' ],
1101 label
: linkLang
.advisoryTitle
,
1104 setup
: setupAdvParams
,
1105 commit
: commitAdvParams
1110 label
: linkLang
.advisoryContentType
,
1112 id
: 'advContentType',
1113 setup
: setupAdvParams
,
1114 commit
: commitAdvParams
1121 widths
: [ '45%', '55%' ],
1126 label
: linkLang
.cssClasses
,
1128 id
: 'advCSSClasses',
1129 setup
: setupAdvParams
,
1130 commit
: commitAdvParams
1135 label
: linkLang
.charset
,
1138 setup
: setupAdvParams
,
1139 commit
: commitAdvParams
1146 widths
: [ '45%', '55%' ],
1151 label
: linkLang
.rel
,
1154 setup
: setupAdvParams
,
1155 commit
: commitAdvParams
1159 label
: linkLang
.styles
,
1162 setup
: setupAdvParams
,
1163 commit
: commitAdvParams
1174 var editor
= this.getParentEditor(),
1175 selection
= editor
.getSelection(),
1178 // Fill in all the relevant fields if there's already one link selected.
1179 if ( ( element
= plugin
.getSelectedLink( editor
) ) && element
.hasAttribute( 'href' ) )
1180 selection
.selectElement( element
);
1184 this.setupContent( parseLink
.apply( this, [ editor
, element
] ) );
1188 var attributes
= {},
1189 removeAttributes
= [],
1192 editor
= this.getParentEditor();
1194 this.commitContent( data
);
1197 switch ( data
.type
|| 'url' )
1200 var protocol
= ( data
.url
&& data
.url
.protocol
!= undefined ) ? data
.url
.protocol
: 'http://',
1201 url
= ( data
.url
&& CKEDITOR
.tools
.trim( data
.url
.url
) ) || '';
1202 attributes
[ 'data-cke-saved-href' ] = ( url
.indexOf( '/' ) === 0 ) ? url
: protocol
+ url
;
1205 var name
= ( data
.anchor
&& data
.anchor
.name
),
1206 id
= ( data
.anchor
&& data
.anchor
.id
);
1207 attributes
[ 'data-cke-saved-href' ] = '#' + ( name
|| id
|| '' );
1213 address
= email
.address
;
1215 switch( emailProtection
)
1220 var subject
= encodeURIComponent( email
.subject
|| '' ),
1221 body
= encodeURIComponent( email
.body
|| '' );
1223 // Build the e-mail parameters first.
1225 subject
&& argList
.push( 'subject=' + subject
);
1226 body
&& argList
.push( 'body=' + body
);
1227 argList
= argList
.length
? '?' + argList
.join( '&' ) : '';
1229 if ( emailProtection
== 'encode' )
1231 linkHref
= [ 'javascript:void(location.href=\'mailto:\'+',
1232 protectEmailAddressAsEncodedString( address
) ];
1233 // parameters are optional.
1234 argList
&& linkHref
.push( '+\'', escapeSingleQuote( argList
), '\'' );
1236 linkHref
.push( ')' );
1239 linkHref
= [ 'mailto:', address
, argList
];
1245 // Separating name and domain.
1246 var nameAndDomain
= address
.split( '@', 2 );
1247 email
.name
= nameAndDomain
[ 0 ];
1248 email
.domain
= nameAndDomain
[ 1 ];
1250 linkHref
= [ 'javascript:', protectEmailLinkAsFunction( email
) ];
1254 attributes
[ 'data-cke-saved-href' ] = linkHref
.join( '' );
1258 // Popups and target.
1261 if ( data
.target
.type
== 'popup' )
1263 var onclickList
= [ 'window.open(this.href, \'',
1264 data
.target
.name
|| '', '\', \'' ];
1265 var featureList
= [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
1266 'scrollbars', 'dependent' ];
1267 var featureLength
= featureList
.length
;
1268 var addFeature = function( featureName
)
1270 if ( data
.target
[ featureName
] )
1271 featureList
.push( featureName
+ '=' + data
.target
[ featureName
] );
1274 for ( var i
= 0 ; i
< featureLength
; i
++ )
1275 featureList
[i
] = featureList
[i
] + ( data
.target
[ featureList
[i
] ] ? '=yes' : '=no' ) ;
1276 addFeature( 'width' );
1277 addFeature( 'left' );
1278 addFeature( 'height' );
1279 addFeature( 'top' );
1281 onclickList
.push( featureList
.join( ',' ), '\'); return false;' );
1282 attributes
[ 'data-cke-pa-onclick' ] = onclickList
.join( '' );
1284 // Add the "target" attribute. (#5074)
1285 removeAttributes
.push( 'target' );
1289 if ( data
.target
.type
!= 'notSet' && data
.target
.name
)
1290 attributes
.target
= data
.target
.name
;
1292 removeAttributes
.push( 'target' );
1294 removeAttributes
.push( 'data-cke-pa-onclick', 'onclick' );
1298 // Advanced attributes.
1301 var advAttr = function( inputName
, attrName
)
1303 var value
= data
.adv
[ inputName
];
1305 attributes
[attrName
] = value
;
1307 removeAttributes
.push( attrName
);
1310 advAttr( 'advId', 'id' );
1311 advAttr( 'advLangDir', 'dir' );
1312 advAttr( 'advAccessKey', 'accessKey' );
1314 if ( data
.adv
[ 'advName' ] )
1315 attributes
[ 'name' ] = attributes
[ 'data-cke-saved-name' ] = data
.adv
[ 'advName' ];
1317 removeAttributes
= removeAttributes
.concat( [ 'data-cke-saved-name', 'name' ] );
1319 advAttr( 'advLangCode', 'lang' );
1320 advAttr( 'advTabIndex', 'tabindex' );
1321 advAttr( 'advTitle', 'title' );
1322 advAttr( 'advContentType', 'type' );
1323 advAttr( 'advCSSClasses', 'class' );
1324 advAttr( 'advCharset', 'charset' );
1325 advAttr( 'advStyles', 'style' );
1326 advAttr( 'advRel', 'rel' );
1330 // Browser need the "href" fro copy/paste link to work. (#6641)
1331 attributes
.href
= attributes
[ 'data-cke-saved-href' ];
1333 if ( !this._
.selectedElement
)
1335 // Create element if current selection is collapsed.
1336 var selection
= editor
.getSelection(),
1337 ranges
= selection
.getRanges( true );
1338 if ( ranges
.length
== 1 && ranges
[0].collapsed
)
1340 // Short mailto link text view (#5736).
1341 var text
= new CKEDITOR
.dom
.text( data
.type
== 'email' ?
1342 data
.email
.address
: attributes
[ 'data-cke-saved-href' ], editor
.document
);
1343 ranges
[0].insertNode( text
);
1344 ranges
[0].selectNodeContents( text
);
1345 selection
.selectRanges( ranges
);
1349 var style
= new CKEDITOR
.style( { element
: 'a', attributes
: attributes
} );
1350 style
.type
= CKEDITOR
.STYLE_INLINE
; // need to override... dunno why.
1351 style
.apply( editor
.document
);
1355 // We're only editing an existing link, so just overwrite the attributes.
1356 var element
= this._
.selectedElement
,
1357 href
= element
.data( 'cke-saved-href' ),
1358 textView
= element
.getHtml();
1360 element
.setAttributes( attributes
);
1361 element
.removeAttributes( removeAttributes
);
1363 if ( data
.adv
&& data
.adv
.advName
&& CKEDITOR
.plugins
.link
.synAnchorSelector
)
1364 element
.addClass( element
.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' );
1366 // Update text view when user changes protocol (#4612).
1367 if ( href
== textView
|| data
.type
== 'email' && textView
.indexOf( '@' ) != -1 )
1369 // Short mailto link text view (#5736).
1370 element
.setHtml( data
.type
== 'email' ?
1371 data
.email
.address
: attributes
[ 'data-cke-saved-href' ] );
1374 delete this._
.selectedElement
;
1379 if ( !editor
.config
.linkShowAdvancedTab
)
1380 this.hidePage( 'advanced' ); //Hide Advanded tab.
1382 if ( !editor
.config
.linkShowTargetTab
)
1383 this.hidePage( 'target' ); //Hide Target tab.
1386 // Inital focus on 'url' field if link is of type URL.
1387 onFocus : function()
1389 var linkType
= this.getContentElement( 'info', 'linkType' ),
1391 if ( linkType
&& linkType
.getValue() == 'url' )
1393 urlField
= this.getContentElement( 'info', 'url' );
1401 * The e-mail address anti-spam protection option. The protection will be
1402 * applied when creating or modifying e-mail links through the editor interface.<br>
1403 * Two methods of protection can be choosed:
1404 * <ol> <li>The e-mail parts (name, domain and any other query string) are
1405 * assembled into a function call pattern. Such function must be
1406 * provided by the developer in the pages that will use the contents.
1407 * <li>Only the e-mail address is obfuscated into a special string that
1408 * has no meaning for humans or spam bots, but which is properly
1409 * rendered and accepted by the browser.</li></ol>
1410 * Both approaches require JavaScript to be enabled.
1411 * @name CKEDITOR.config.emailProtection
1414 * @default '' (empty string = disabled)
1416 * // href="mailto:tester@ckeditor.com?subject=subject&body=body"
1417 * config.emailProtection = '';
1419 * // 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>"
1420 * config.emailProtection = 'encode';
1422 * // href="javascript:mt('tester','ckeditor.com','subject','body')"
1423 * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';