]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/plugins/link/dialogs/link.js
914471fd1a0e8591d62e0a66897d2af5032d784f
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / link / dialogs / link.js
1 /**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6 'use strict';
7
8 ( function() {
9 CKEDITOR.dialog.add( 'link', function( editor ) {
10 var plugin = CKEDITOR.plugins.link,
11 initialLinkText;
12
13 // Handles the event when the "Target" selection box is changed.
14 var targetChanged = function() {
15 var dialog = this.getDialog(),
16 popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ),
17 targetName = dialog.getContentElement( 'target', 'linkTargetName' ),
18 value = this.getValue();
19
20 if ( !popupFeatures || !targetName )
21 return;
22
23 popupFeatures = popupFeatures.getElement();
24 popupFeatures.hide();
25 targetName.setValue( '' );
26
27 switch ( value ) {
28 case 'frame':
29 targetName.setLabel( editor.lang.link.targetFrameName );
30 targetName.getElement().show();
31 break;
32 case 'popup':
33 popupFeatures.show();
34 targetName.setLabel( editor.lang.link.targetPopupName );
35 targetName.getElement().show();
36 break;
37 default:
38 targetName.setValue( value );
39 targetName.getElement().hide();
40 break;
41 }
42
43 };
44
45 // Handles the event when the "Type" selection box is changed.
46 var linkTypeChanged = function() {
47 var dialog = this.getDialog(),
48 partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
49 typeValue = this.getValue(),
50 uploadTab = dialog.definition.getContents( 'upload' ),
51 uploadInitiallyHidden = uploadTab && uploadTab.hidden;
52
53 if ( typeValue == 'url' ) {
54 if ( editor.config.linkShowTargetTab )
55 dialog.showPage( 'target' );
56 if ( !uploadInitiallyHidden )
57 dialog.showPage( 'upload' );
58 } else {
59 dialog.hidePage( 'target' );
60 if ( !uploadInitiallyHidden )
61 dialog.hidePage( 'upload' );
62 }
63
64 for ( var i = 0; i < partIds.length; i++ ) {
65 var element = dialog.getContentElement( 'info', partIds[ i ] );
66 if ( !element )
67 continue;
68
69 element = element.getElement().getParent().getParent();
70 if ( partIds[ i ] == typeValue + 'Options' )
71 element.show();
72 else
73 element.hide();
74 }
75
76 dialog.layout();
77 };
78
79 var setupParams = function( page, data ) {
80 if ( data[ page ] )
81 this.setValue( data[ page ][ this.id ] || '' );
82 };
83
84 var setupPopupParams = function( data ) {
85 return setupParams.call( this, 'target', data );
86 };
87
88 var setupAdvParams = function( data ) {
89 return setupParams.call( this, 'advanced', data );
90 };
91
92 var commitParams = function( page, data ) {
93 if ( !data[ page ] )
94 data[ page ] = {};
95
96 data[ page ][ this.id ] = this.getValue() || '';
97 };
98
99 var commitPopupParams = function( data ) {
100 return commitParams.call( this, 'target', data );
101 };
102
103 var commitAdvParams = function( data ) {
104 return commitParams.call( this, 'advanced', data );
105 };
106
107 var commonLang = editor.lang.common,
108 linkLang = editor.lang.link,
109 anchors;
110
111 return {
112 title: linkLang.title,
113 minWidth: ( CKEDITOR.skinName || editor.config.skin ) == 'moono-lisa' ? 450 : 350,
114 minHeight: 240,
115 contents: [ {
116 id: 'info',
117 label: linkLang.info,
118 title: linkLang.info,
119 elements: [ {
120 type: 'text',
121 id: 'linkDisplayText',
122 label: linkLang.displayText,
123 setup: function() {
124 this.enable();
125
126 this.setValue( editor.getSelection().getSelectedText() );
127
128 // Keep inner text so that it can be compared in commit function. By obtaining value from getData()
129 // we get value stripped from new line chars which is important when comparing the value later on.
130 initialLinkText = this.getValue();
131 },
132 commit: function( data ) {
133 data.linkText = this.isEnabled() ? this.getValue() : '';
134 }
135 },
136 {
137 id: 'linkType',
138 type: 'select',
139 label: linkLang.type,
140 'default': 'url',
141 items: [
142 [ linkLang.toUrl, 'url' ],
143 [ linkLang.toAnchor, 'anchor' ],
144 [ linkLang.toEmail, 'email' ]
145 ],
146 onChange: linkTypeChanged,
147 setup: function( data ) {
148 this.setValue( data.type || 'url' );
149 },
150 commit: function( data ) {
151 data.type = this.getValue();
152 }
153 },
154 {
155 type: 'vbox',
156 id: 'urlOptions',
157 children: [ {
158 type: 'hbox',
159 widths: [ '25%', '75%' ],
160 children: [ {
161 id: 'protocol',
162 type: 'select',
163 label: commonLang.protocol,
164 'default': 'http://',
165 items: [
166 // Force 'ltr' for protocol names in BIDI. (#5433)
167 [ 'http://\u200E', 'http://' ],
168 [ 'https://\u200E', 'https://' ],
169 [ 'ftp://\u200E', 'ftp://' ],
170 [ 'news://\u200E', 'news://' ],
171 [ linkLang.other, '' ]
172 ],
173 setup: function( data ) {
174 if ( data.url )
175 this.setValue( data.url.protocol || '' );
176 },
177 commit: function( data ) {
178 if ( !data.url )
179 data.url = {};
180
181 data.url.protocol = this.getValue();
182 }
183 },
184 {
185 type: 'text',
186 id: 'url',
187 label: commonLang.url,
188 required: true,
189 onLoad: function() {
190 this.allowOnChange = true;
191 },
192 onKeyUp: function() {
193 this.allowOnChange = false;
194 var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),
195 url = this.getValue(),
196 urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/i,
197 urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/i;
198
199 var protocol = urlOnChangeProtocol.exec( url );
200 if ( protocol ) {
201 this.setValue( url.substr( protocol[ 0 ].length ) );
202 protocolCmb.setValue( protocol[ 0 ].toLowerCase() );
203 } else if ( urlOnChangeTestOther.test( url ) ) {
204 protocolCmb.setValue( '' );
205 }
206
207 this.allowOnChange = true;
208 },
209 onChange: function() {
210 if ( this.allowOnChange ) // Dont't call on dialog load.
211 this.onKeyUp();
212 },
213 validate: function() {
214 var dialog = this.getDialog();
215
216 if ( dialog.getContentElement( 'info', 'linkType' ) && dialog.getValueOf( 'info', 'linkType' ) != 'url' )
217 return true;
218
219 if ( !editor.config.linkJavaScriptLinksAllowed && ( /javascript\:/ ).test( this.getValue() ) ) {
220 alert( commonLang.invalidValue ); // jshint ignore:line
221 return false;
222 }
223
224 if ( this.getDialog().fakeObj ) // Edit Anchor.
225 return true;
226
227 var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noUrl );
228 return func.apply( this );
229 },
230 setup: function( data ) {
231 this.allowOnChange = false;
232 if ( data.url )
233 this.setValue( data.url.url );
234 this.allowOnChange = true;
235
236 },
237 commit: function( data ) {
238 // IE will not trigger the onChange event if the mouse has been used
239 // to carry all the operations #4724
240 this.onChange();
241
242 if ( !data.url )
243 data.url = {};
244
245 data.url.url = this.getValue();
246 this.allowOnChange = false;
247 }
248 } ],
249 setup: function() {
250 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
251 this.getElement().show();
252 }
253 },
254 {
255 type: 'button',
256 id: 'browse',
257 hidden: 'true',
258 filebrowser: 'info:url',
259 label: commonLang.browseServer
260 } ]
261 },
262 {
263 type: 'vbox',
264 id: 'anchorOptions',
265 width: 260,
266 align: 'center',
267 padding: 0,
268 children: [ {
269 type: 'fieldset',
270 id: 'selectAnchorText',
271 label: linkLang.selectAnchor,
272 setup: function() {
273 anchors = plugin.getEditorAnchors( editor );
274
275 this.getElement()[ anchors && anchors.length ? 'show' : 'hide' ]();
276 },
277 children: [ {
278 type: 'hbox',
279 id: 'selectAnchor',
280 children: [ {
281 type: 'select',
282 id: 'anchorName',
283 'default': '',
284 label: linkLang.anchorName,
285 style: 'width: 100%;',
286 items: [
287 [ '' ]
288 ],
289 setup: function( data ) {
290 this.clear();
291 this.add( '' );
292
293 if ( anchors ) {
294 for ( var i = 0; i < anchors.length; i++ ) {
295 if ( anchors[ i ].name )
296 this.add( anchors[ i ].name );
297 }
298 }
299
300 if ( data.anchor )
301 this.setValue( data.anchor.name );
302
303 var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
304 if ( linkType && linkType.getValue() == 'email' )
305 this.focus();
306 },
307 commit: function( data ) {
308 if ( !data.anchor )
309 data.anchor = {};
310
311 data.anchor.name = this.getValue();
312 }
313 },
314 {
315 type: 'select',
316 id: 'anchorId',
317 'default': '',
318 label: linkLang.anchorId,
319 style: 'width: 100%;',
320 items: [
321 [ '' ]
322 ],
323 setup: function( data ) {
324 this.clear();
325 this.add( '' );
326
327 if ( anchors ) {
328 for ( var i = 0; i < anchors.length; i++ ) {
329 if ( anchors[ i ].id )
330 this.add( anchors[ i ].id );
331 }
332 }
333
334 if ( data.anchor )
335 this.setValue( data.anchor.id );
336 },
337 commit: function( data ) {
338 if ( !data.anchor )
339 data.anchor = {};
340
341 data.anchor.id = this.getValue();
342 }
343 } ],
344 setup: function() {
345 this.getElement()[ anchors && anchors.length ? 'show' : 'hide' ]();
346 }
347 } ]
348 },
349 {
350 type: 'html',
351 id: 'noAnchors',
352 style: 'text-align: center;',
353 html: '<div role="note" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( linkLang.noAnchors ) + '</div>',
354 // Focus the first element defined in above html.
355 focus: true,
356 setup: function() {
357 this.getElement()[ anchors && anchors.length ? 'hide' : 'show' ]();
358 }
359 } ],
360 setup: function() {
361 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
362 this.getElement().hide();
363 }
364 },
365 {
366 type: 'vbox',
367 id: 'emailOptions',
368 padding: 1,
369 children: [ {
370 type: 'text',
371 id: 'emailAddress',
372 label: linkLang.emailAddress,
373 required: true,
374 validate: function() {
375 var dialog = this.getDialog();
376
377 if ( !dialog.getContentElement( 'info', 'linkType' ) || dialog.getValueOf( 'info', 'linkType' ) != 'email' )
378 return true;
379
380 var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noEmail );
381 return func.apply( this );
382 },
383 setup: function( data ) {
384 if ( data.email )
385 this.setValue( data.email.address );
386
387 var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
388 if ( linkType && linkType.getValue() == 'email' )
389 this.select();
390 },
391 commit: function( data ) {
392 if ( !data.email )
393 data.email = {};
394
395 data.email.address = this.getValue();
396 }
397 },
398 {
399 type: 'text',
400 id: 'emailSubject',
401 label: linkLang.emailSubject,
402 setup: function( data ) {
403 if ( data.email )
404 this.setValue( data.email.subject );
405 },
406 commit: function( data ) {
407 if ( !data.email )
408 data.email = {};
409
410 data.email.subject = this.getValue();
411 }
412 },
413 {
414 type: 'textarea',
415 id: 'emailBody',
416 label: linkLang.emailBody,
417 rows: 3,
418 'default': '',
419 setup: function( data ) {
420 if ( data.email )
421 this.setValue( data.email.body );
422 },
423 commit: function( data ) {
424 if ( !data.email )
425 data.email = {};
426
427 data.email.body = this.getValue();
428 }
429 } ],
430 setup: function() {
431 if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
432 this.getElement().hide();
433 }
434 } ]
435 },
436 {
437 id: 'target',
438 requiredContent: 'a[target]', // This is not fully correct, because some target option requires JS.
439 label: linkLang.target,
440 title: linkLang.target,
441 elements: [ {
442 type: 'hbox',
443 widths: [ '50%', '50%' ],
444 children: [ {
445 type: 'select',
446 id: 'linkTargetType',
447 label: commonLang.target,
448 'default': 'notSet',
449 style: 'width : 100%;',
450 'items': [
451 [ commonLang.notSet, 'notSet' ],
452 [ linkLang.targetFrame, 'frame' ],
453 [ linkLang.targetPopup, 'popup' ],
454 [ commonLang.targetNew, '_blank' ],
455 [ commonLang.targetTop, '_top' ],
456 [ commonLang.targetSelf, '_self' ],
457 [ commonLang.targetParent, '_parent' ]
458 ],
459 onChange: targetChanged,
460 setup: function( data ) {
461 if ( data.target )
462 this.setValue( data.target.type || 'notSet' );
463 targetChanged.call( this );
464 },
465 commit: function( data ) {
466 if ( !data.target )
467 data.target = {};
468
469 data.target.type = this.getValue();
470 }
471 },
472 {
473 type: 'text',
474 id: 'linkTargetName',
475 label: linkLang.targetFrameName,
476 'default': '',
477 setup: function( data ) {
478 if ( data.target )
479 this.setValue( data.target.name );
480 },
481 commit: function( data ) {
482 if ( !data.target )
483 data.target = {};
484
485 data.target.name = this.getValue().replace( /([^\x00-\x7F]|\s)/gi, '' );
486 }
487 } ]
488 },
489 {
490 type: 'vbox',
491 width: '100%',
492 align: 'center',
493 padding: 2,
494 id: 'popupFeatures',
495 children: [ {
496 type: 'fieldset',
497 label: linkLang.popupFeatures,
498 children: [ {
499 type: 'hbox',
500 children: [ {
501 type: 'checkbox',
502 id: 'resizable',
503 label: linkLang.popupResizable,
504 setup: setupPopupParams,
505 commit: commitPopupParams
506 },
507 {
508 type: 'checkbox',
509 id: 'status',
510 label: linkLang.popupStatusBar,
511 setup: setupPopupParams,
512 commit: commitPopupParams
513
514 } ]
515 },
516 {
517 type: 'hbox',
518 children: [ {
519 type: 'checkbox',
520 id: 'location',
521 label: linkLang.popupLocationBar,
522 setup: setupPopupParams,
523 commit: commitPopupParams
524
525 },
526 {
527 type: 'checkbox',
528 id: 'toolbar',
529 label: linkLang.popupToolbar,
530 setup: setupPopupParams,
531 commit: commitPopupParams
532
533 } ]
534 },
535 {
536 type: 'hbox',
537 children: [ {
538 type: 'checkbox',
539 id: 'menubar',
540 label: linkLang.popupMenuBar,
541 setup: setupPopupParams,
542 commit: commitPopupParams
543
544 },
545 {
546 type: 'checkbox',
547 id: 'fullscreen',
548 label: linkLang.popupFullScreen,
549 setup: setupPopupParams,
550 commit: commitPopupParams
551
552 } ]
553 },
554 {
555 type: 'hbox',
556 children: [ {
557 type: 'checkbox',
558 id: 'scrollbars',
559 label: linkLang.popupScrollBars,
560 setup: setupPopupParams,
561 commit: commitPopupParams
562
563 },
564 {
565 type: 'checkbox',
566 id: 'dependent',
567 label: linkLang.popupDependent,
568 setup: setupPopupParams,
569 commit: commitPopupParams
570
571 } ]
572 },
573 {
574 type: 'hbox',
575 children: [ {
576 type: 'text',
577 widths: [ '50%', '50%' ],
578 labelLayout: 'horizontal',
579 label: commonLang.width,
580 id: 'width',
581 setup: setupPopupParams,
582 commit: commitPopupParams
583
584 },
585 {
586 type: 'text',
587 labelLayout: 'horizontal',
588 widths: [ '50%', '50%' ],
589 label: linkLang.popupLeft,
590 id: 'left',
591 setup: setupPopupParams,
592 commit: commitPopupParams
593
594 } ]
595 },
596 {
597 type: 'hbox',
598 children: [ {
599 type: 'text',
600 labelLayout: 'horizontal',
601 widths: [ '50%', '50%' ],
602 label: commonLang.height,
603 id: 'height',
604 setup: setupPopupParams,
605 commit: commitPopupParams
606
607 },
608 {
609 type: 'text',
610 labelLayout: 'horizontal',
611 label: linkLang.popupTop,
612 widths: [ '50%', '50%' ],
613 id: 'top',
614 setup: setupPopupParams,
615 commit: commitPopupParams
616
617 } ]
618 } ]
619 } ]
620 } ]
621 },
622 {
623 id: 'upload',
624 label: linkLang.upload,
625 title: linkLang.upload,
626 hidden: true,
627 filebrowser: 'uploadButton',
628 elements: [ {
629 type: 'file',
630 id: 'upload',
631 label: commonLang.upload,
632 style: 'height:40px',
633 size: 29
634 },
635 {
636 type: 'fileButton',
637 id: 'uploadButton',
638 label: commonLang.uploadSubmit,
639 filebrowser: 'info:url',
640 'for': [ 'upload', 'upload' ]
641 } ]
642 },
643 {
644 id: 'advanced',
645 label: linkLang.advanced,
646 title: linkLang.advanced,
647 elements: [ {
648 type: 'vbox',
649 padding: 1,
650 children: [ {
651 type: 'hbox',
652 widths: [ '45%', '35%', '20%' ],
653 children: [ {
654 type: 'text',
655 id: 'advId',
656 requiredContent: 'a[id]',
657 label: linkLang.id,
658 setup: setupAdvParams,
659 commit: commitAdvParams
660 },
661 {
662 type: 'select',
663 id: 'advLangDir',
664 requiredContent: 'a[dir]',
665 label: linkLang.langDir,
666 'default': '',
667 style: 'width:110px',
668 items: [
669 [ commonLang.notSet, '' ],
670 [ linkLang.langDirLTR, 'ltr' ],
671 [ linkLang.langDirRTL, 'rtl' ]
672 ],
673 setup: setupAdvParams,
674 commit: commitAdvParams
675 },
676 {
677 type: 'text',
678 id: 'advAccessKey',
679 requiredContent: 'a[accesskey]',
680 width: '80px',
681 label: linkLang.acccessKey,
682 maxLength: 1,
683 setup: setupAdvParams,
684 commit: commitAdvParams
685
686 } ]
687 },
688 {
689 type: 'hbox',
690 widths: [ '45%', '35%', '20%' ],
691 children: [ {
692 type: 'text',
693 label: linkLang.name,
694 id: 'advName',
695 requiredContent: 'a[name]',
696 setup: setupAdvParams,
697 commit: commitAdvParams
698
699 },
700 {
701 type: 'text',
702 label: linkLang.langCode,
703 id: 'advLangCode',
704 requiredContent: 'a[lang]',
705 width: '110px',
706 'default': '',
707 setup: setupAdvParams,
708 commit: commitAdvParams
709
710 },
711 {
712 type: 'text',
713 label: linkLang.tabIndex,
714 id: 'advTabIndex',
715 requiredContent: 'a[tabindex]',
716 width: '80px',
717 maxLength: 5,
718 setup: setupAdvParams,
719 commit: commitAdvParams
720
721 } ]
722 } ]
723 },
724 {
725 type: 'vbox',
726 padding: 1,
727 children: [ {
728 type: 'hbox',
729 widths: [ '45%', '55%' ],
730 children: [ {
731 type: 'text',
732 label: linkLang.advisoryTitle,
733 requiredContent: 'a[title]',
734 'default': '',
735 id: 'advTitle',
736 setup: setupAdvParams,
737 commit: commitAdvParams
738
739 },
740 {
741 type: 'text',
742 label: linkLang.advisoryContentType,
743 requiredContent: 'a[type]',
744 'default': '',
745 id: 'advContentType',
746 setup: setupAdvParams,
747 commit: commitAdvParams
748
749 } ]
750 },
751 {
752 type: 'hbox',
753 widths: [ '45%', '55%' ],
754 children: [ {
755 type: 'text',
756 label: linkLang.cssClasses,
757 requiredContent: 'a(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
758 'default': '',
759 id: 'advCSSClasses',
760 setup: setupAdvParams,
761 commit: commitAdvParams
762
763 },
764 {
765 type: 'text',
766 label: linkLang.charset,
767 requiredContent: 'a[charset]',
768 'default': '',
769 id: 'advCharset',
770 setup: setupAdvParams,
771 commit: commitAdvParams
772
773 } ]
774 },
775 {
776 type: 'hbox',
777 widths: [ '45%', '55%' ],
778 children: [ {
779 type: 'text',
780 label: linkLang.rel,
781 requiredContent: 'a[rel]',
782 'default': '',
783 id: 'advRel',
784 setup: setupAdvParams,
785 commit: commitAdvParams
786 },
787 {
788 type: 'text',
789 label: linkLang.styles,
790 requiredContent: 'a{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
791 'default': '',
792 id: 'advStyles',
793 validate: CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
794 setup: setupAdvParams,
795 commit: commitAdvParams
796 } ]
797 },
798 {
799 type: 'hbox',
800 widths: [ '45%', '55%' ],
801 children: [ {
802 type: 'checkbox',
803 id: 'download',
804 requiredContent: 'a[download]',
805 label: linkLang.download,
806 setup: function( data ) {
807 if ( data.download !== undefined )
808 this.setValue( 'checked', 'checked' );
809 },
810 commit: function( data ) {
811 if ( this.getValue() ) {
812 data.download = this.getValue();
813 }
814 }
815 } ]
816 } ]
817 } ]
818 } ],
819 onShow: function() {
820 var editor = this.getParentEditor(),
821 selection = editor.getSelection(),
822 selectedElement = selection.getSelectedElement(),
823 displayTextField = this.getContentElement( 'info', 'linkDisplayText' ).getElement().getParent().getParent(),
824 element = null;
825
826 // Fill in all the relevant fields if there's already one link selected.
827 if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) ) {
828 // Don't change selection if some element is already selected.
829 // For example - don't destroy fake selection.
830 if ( !selectedElement ) {
831 selection.selectElement( element );
832 selectedElement = element;
833 }
834 } else {
835 element = null;
836 }
837
838 // Here we'll decide whether or not we want to show Display Text field.
839 if ( plugin.showDisplayTextForElement( selectedElement, editor ) ) {
840 displayTextField.show();
841 } else {
842 displayTextField.hide();
843 }
844
845 var data = plugin.parseLinkAttributes( editor, element );
846
847 // Record down the selected element in the dialog.
848 this._.selectedElement = element;
849
850 this.setupContent( data );
851 },
852 onOk: function() {
853 var data = {};
854
855 // Collect data from fields.
856 this.commitContent( data );
857
858 var selection = editor.getSelection(),
859 attributes = plugin.getLinkAttributes( editor, data ),
860 bm,
861 nestedLinks;
862
863 if ( !this._.selectedElement ) {
864 var range = selection.getRanges()[ 0 ],
865 text;
866
867 // Use link URL as text with a collapsed cursor.
868 if ( range.collapsed ) {
869 // Short mailto link text view (#5736).
870 text = new CKEDITOR.dom.text( data.linkText || ( data.type == 'email' ?
871 data.email.address : attributes.set[ 'data-cke-saved-href' ] ), editor.document );
872 range.insertNode( text );
873 range.selectNodeContents( text );
874 } else if ( initialLinkText !== data.linkText ) {
875 text = new CKEDITOR.dom.text( data.linkText, editor.document );
876
877 // Shrink range to preserve block element.
878 range.shrink( CKEDITOR.SHRINK_TEXT );
879
880 // Use extractHtmlFromRange to remove markup within the selection. Also this method is a little
881 // smarter than range#deleteContents as it plays better e.g. with table cells.
882 editor.editable().extractHtmlFromRange( range );
883
884 range.insertNode( text );
885 }
886
887 // Editable links nested within current range should be removed, so that the link is applied to whole selection.
888 nestedLinks = range._find( 'a' );
889
890 for ( var i = 0; i < nestedLinks.length; i++ ) {
891 nestedLinks[ i ].remove( true );
892 }
893
894 // Apply style.
895 var style = new CKEDITOR.style( {
896 element: 'a',
897 attributes: attributes.set
898 } );
899
900 style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
901 style.applyToRange( range, editor );
902 range.select();
903 } else {
904 // We're only editing an existing link, so just overwrite the attributes.
905 var element = this._.selectedElement,
906 href = element.data( 'cke-saved-href' ),
907 textView = element.getHtml(),
908 newText;
909
910 element.setAttributes( attributes.set );
911 element.removeAttributes( attributes.removed );
912
913 if ( data.linkText && initialLinkText != data.linkText ) {
914 // Display text has been changed.
915 newText = data.linkText;
916 } else if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 ) {
917 // Update text view when user changes protocol (#4612).
918 // Short mailto link text view (#5736).
919 newText = data.type == 'email' ? data.email.address : attributes.set[ 'data-cke-saved-href' ];
920 }
921
922 if ( newText ) {
923 element.setText( newText );
924 // We changed the content, so need to select it again.
925 selection.selectElement( element );
926 }
927
928 delete this._.selectedElement;
929 }
930 },
931 onLoad: function() {
932 if ( !editor.config.linkShowAdvancedTab )
933 this.hidePage( 'advanced' ); //Hide Advanded tab.
934
935 if ( !editor.config.linkShowTargetTab )
936 this.hidePage( 'target' ); //Hide Target tab.
937 },
938 // Inital focus on 'url' field if link is of type URL.
939 onFocus: function() {
940 var linkType = this.getContentElement( 'info', 'linkType' ),
941 urlField;
942
943 if ( linkType && linkType.getValue() == 'url' ) {
944 urlField = this.getContentElement( 'info', 'url' );
945 urlField.select();
946 }
947 }
948 };
949 } );
950 } )();
951 // jscs:disable maximumLineLength
952 /**
953 * The e-mail address anti-spam protection option. The protection will be
954 * applied when creating or modifying e-mail links through the editor interface.
955 *
956 * Two methods of protection can be chosen:
957 *
958 * 1. The e-mail parts (name, domain, and any other query string) are
959 * assembled into a function call pattern. Such function must be
960 * provided by the developer in the pages that will use the contents.
961 * 2. Only the e-mail address is obfuscated into a special string that
962 * has no meaning for humans or spam bots, but which is properly
963 * rendered and accepted by the browser.
964 *
965 * Both approaches require JavaScript to be enabled.
966 *
967 * // href="mailto:tester@ckeditor.com?subject=subject&body=body"
968 * config.emailProtection = '';
969 *
970 * // 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>"
971 * config.emailProtection = 'encode';
972 *
973 * // href="javascript:mt('tester','ckeditor.com','subject','body')"
974 * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';
975 *
976 * @since 3.1
977 * @cfg {String} [emailProtection='' (empty string = disabled)]
978 * @member CKEDITOR.config
979 */