diff options
Diffstat (limited to 'sources/plugins/link/plugin.js')
-rw-r--r-- | sources/plugins/link/plugin.js | 104 |
1 files changed, 76 insertions, 28 deletions
diff --git a/sources/plugins/link/plugin.js b/sources/plugins/link/plugin.js index 94d582b..120097f 100644 --- a/sources/plugins/link/plugin.js +++ b/sources/plugins/link/plugin.js | |||
@@ -9,7 +9,7 @@ | |||
9 | CKEDITOR.plugins.add( 'link', { | 9 | CKEDITOR.plugins.add( 'link', { |
10 | requires: 'dialog,fakeobjects', | 10 | requires: 'dialog,fakeobjects', |
11 | // jscs:disable maximumLineLength | 11 | // jscs:disable maximumLineLength |
12 | lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE% | 12 | lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE% |
13 | // jscs:enable maximumLineLength | 13 | // jscs:enable maximumLineLength |
14 | icons: 'anchor,anchor-rtl,link,unlink', // %REMOVE_LINE_CORE% | 14 | icons: 'anchor,anchor-rtl,link,unlink', // %REMOVE_LINE_CORE% |
15 | hidpi: true, // %REMOVE_LINE_CORE% | 15 | hidpi: true, // %REMOVE_LINE_CORE% |
@@ -92,9 +92,12 @@ | |||
92 | CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' ); | 92 | CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' ); |
93 | 93 | ||
94 | editor.on( 'doubleclick', function( evt ) { | 94 | editor.on( 'doubleclick', function( evt ) { |
95 | var element = CKEDITOR.plugins.link.getSelectedLink( editor ) || evt.data.element; | 95 | // If the link has descendants and the last part of it is also a part of a word partially |
96 | // unlinked, clicked element may be a descendant of the link, not the link itself (http://dev.ckeditor.com/ticket/11956). | ||
97 | // The evt.data.element.getAscendant( 'img', 1 ) condition allows opening anchor dialog if the anchor is empty (#501). | ||
98 | var element = evt.data.element.getAscendant( { a: 1, img: 1 }, true ); | ||
96 | 99 | ||
97 | if ( !element.isReadOnly() ) { | 100 | if ( element && !element.isReadOnly() ) { |
98 | if ( element.is( 'a' ) ) { | 101 | if ( element.is( 'a' ) ) { |
99 | evt.data.dialog = ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ? 'anchor' : 'link'; | 102 | evt.data.dialog = ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ? 'anchor' : 'link'; |
100 | 103 | ||
@@ -108,7 +111,7 @@ | |||
108 | 111 | ||
109 | // If event was cancelled, link passed in event data will not be selected. | 112 | // If event was cancelled, link passed in event data will not be selected. |
110 | editor.on( 'doubleclick', function( evt ) { | 113 | editor.on( 'doubleclick', function( evt ) { |
111 | // Make sure both links and anchors are selected (#11822). | 114 | // Make sure both links and anchors are selected (http://dev.ckeditor.com/ticket/11822). |
112 | if ( evt.data.dialog in { link: 1, anchor: 1 } && evt.data.link ) | 115 | if ( evt.data.dialog in { link: 1, anchor: 1 } && evt.data.link ) |
113 | editor.getSelection().selectElement( evt.data.link ); | 116 | editor.getSelection().selectElement( evt.data.link ); |
114 | }, null, null, 20 ); | 117 | }, null, null, 20 ); |
@@ -311,20 +314,38 @@ | |||
311 | * | 314 | * |
312 | * @since 3.2.1 | 315 | * @since 3.2.1 |
313 | * @param {CKEDITOR.editor} editor | 316 | * @param {CKEDITOR.editor} editor |
317 | * @param {Boolean} [returnMultiple=false] Indicates whether the function should return only the first selected link or all of them. | ||
318 | * @returns {CKEDITOR.dom.element/CKEDITOR.dom.element[]/null} A single link element or an array of link | ||
319 | * elements relevant to the current selection. | ||
314 | */ | 320 | */ |
315 | getSelectedLink: function( editor ) { | 321 | getSelectedLink: function( editor, returnMultiple ) { |
316 | var selection = editor.getSelection(); | 322 | var selection = editor.getSelection(), |
317 | var selectedElement = selection.getSelectedElement(); | 323 | selectedElement = selection.getSelectedElement(), |
318 | if ( selectedElement && selectedElement.is( 'a' ) ) | 324 | ranges = selection.getRanges(), |
325 | links = [], | ||
326 | link, | ||
327 | range, | ||
328 | i; | ||
329 | |||
330 | if ( !returnMultiple && selectedElement && selectedElement.is( 'a' ) ) { | ||
319 | return selectedElement; | 331 | return selectedElement; |
332 | } | ||
333 | |||
334 | for ( i = 0; i < ranges.length; i++ ) { | ||
335 | range = selection.getRanges()[ i ]; | ||
320 | 336 | ||
321 | var range = selection.getRanges()[ 0 ]; | 337 | // Skip bogus to cover cases of multiple selection inside tables (#tp2245). |
338 | range.shrink( CKEDITOR.SHRINK_TEXT, false, { skipBogus: true } ); | ||
339 | link = editor.elementPath( range.getCommonAncestor() ).contains( 'a', 1 ); | ||
322 | 340 | ||
323 | if ( range ) { | 341 | if ( link && returnMultiple ) { |
324 | range.shrink( CKEDITOR.SHRINK_TEXT ); | 342 | links.push( link ); |
325 | return editor.elementPath( range.getCommonAncestor() ).contains( 'a', 1 ); | 343 | } else if ( link ) { |
344 | return link; | ||
345 | } | ||
326 | } | 346 | } |
327 | return null; | 347 | |
348 | return returnMultiple ? links : null; | ||
328 | }, | 349 | }, |
329 | 350 | ||
330 | /** | 351 | /** |
@@ -340,7 +361,7 @@ | |||
340 | var editable = editor.editable(), | 361 | var editable = editor.editable(), |
341 | 362 | ||
342 | // The scope of search for anchors is the entire document for inline editors | 363 | // The scope of search for anchors is the entire document for inline editors |
343 | // and editor's editable for classic editor/divarea (#11359). | 364 | // and editor's editable for classic editor/divarea (http://dev.ckeditor.com/ticket/11359). |
344 | scope = ( editable.isInline() && !editor.plugins.divarea ) ? editor.document : editable, | 365 | scope = ( editable.isInline() && !editor.plugins.divarea ) ? editor.document : editable, |
345 | 366 | ||
346 | links = scope.getElementsByTag( 'a' ), | 367 | links = scope.getElementsByTag( 'a' ), |
@@ -384,7 +405,7 @@ | |||
384 | fakeAnchor: true, | 405 | fakeAnchor: true, |
385 | 406 | ||
386 | /** | 407 | /** |
387 | * For browsers that do not support CSS3 `a[name]:empty()`. Note that IE9 is included because of #7783. | 408 | * For browsers that do not support CSS3 `a[name]:empty()`. Note that IE9 is included because of http://dev.ckeditor.com/ticket/7783. |
388 | * | 409 | * |
389 | * @readonly | 410 | * @readonly |
390 | * @deprecated 4.3.3 It is set to `false` in every browser. | 411 | * @deprecated 4.3.3 It is set to `false` in every browser. |
@@ -438,7 +459,7 @@ | |||
438 | if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) ) { | 459 | if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) ) { |
439 | if ( emailProtection == 'encode' ) { | 460 | if ( emailProtection == 'encode' ) { |
440 | href = href.replace( encodedEmailLinkRegex, function( match, protectedAddress, rest ) { | 461 | href = href.replace( encodedEmailLinkRegex, function( match, protectedAddress, rest ) { |
441 | // Without it 'undefined' is appended to e-mails without subject and body (#9192). | 462 | // Without it 'undefined' is appended to e-mails without subject and body (http://dev.ckeditor.com/ticket/9192). |
442 | rest = rest || ''; | 463 | rest = rest || ''; |
443 | 464 | ||
444 | return 'mailto:' + | 465 | return 'mailto:' + |
@@ -513,7 +534,7 @@ | |||
513 | 534 | ||
514 | var featureMatch; | 535 | var featureMatch; |
515 | while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[ 2 ] ) ) ) { | 536 | while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[ 2 ] ) ) ) { |
516 | // Some values should remain numbers (#7300) | 537 | // Some values should remain numbers (http://dev.ckeditor.com/ticket/7300) |
517 | if ( ( featureMatch[ 2 ] == 'yes' || featureMatch[ 2 ] == '1' ) && !( featureMatch[ 1 ] in { height: 1, width: 1, top: 1, left: 1 } ) ) | 538 | if ( ( featureMatch[ 2 ] == 'yes' || featureMatch[ 2 ] == '1' ) && !( featureMatch[ 1 ] in { height: 1, width: 1, top: 1, left: 1 } ) ) |
518 | retval.target[ featureMatch[ 1 ] ] = true; | 539 | retval.target[ featureMatch[ 1 ] ] = true; |
519 | else if ( isFinite( featureMatch[ 2 ] ) ) | 540 | else if ( isFinite( featureMatch[ 2 ] ) ) |
@@ -689,7 +710,7 @@ | |||
689 | set[ 'data-cke-saved-name' ] = set.name; | 710 | set[ 'data-cke-saved-name' ] = set.name; |
690 | } | 711 | } |
691 | 712 | ||
692 | // Browser need the "href" fro copy/paste link to work. (#6641) | 713 | // Browser need the "href" fro copy/paste link to work. (http://dev.ckeditor.com/ticket/6641) |
693 | if ( set[ 'data-cke-saved-href' ] ) | 714 | if ( set[ 'data-cke-saved-href' ] ) |
694 | set.href = set[ 'data-cke-saved-href' ]; | 715 | set.href = set[ 'data-cke-saved-href' ]; |
695 | 716 | ||
@@ -726,21 +747,26 @@ | |||
726 | */ | 747 | */ |
727 | showDisplayTextForElement: function( element, editor ) { | 748 | showDisplayTextForElement: function( element, editor ) { |
728 | var undesiredElements = { | 749 | var undesiredElements = { |
729 | img: 1, | 750 | img: 1, |
730 | table: 1, | 751 | table: 1, |
731 | tbody: 1, | 752 | tbody: 1, |
732 | thead: 1, | 753 | thead: 1, |
733 | tfoot: 1, | 754 | tfoot: 1, |
734 | input: 1, | 755 | input: 1, |
735 | select: 1, | 756 | select: 1, |
736 | textarea: 1 | 757 | textarea: 1 |
737 | }; | 758 | }, |
759 | selection = editor.getSelection(); | ||
738 | 760 | ||
739 | // Widget duck typing, we don't want to show display text for widgets. | 761 | // Widget duck typing, we don't want to show display text for widgets. |
740 | if ( editor.widgets && editor.widgets.focused ) { | 762 | if ( editor.widgets && editor.widgets.focused ) { |
741 | return false; | 763 | return false; |
742 | } | 764 | } |
743 | 765 | ||
766 | if ( selection && selection.getRanges().length > 1 ) { | ||
767 | return false; | ||
768 | } | ||
769 | |||
744 | return !element || !element.getName || !element.is( undesiredElements ); | 770 | return !element || !element.getName || !element.is( undesiredElements ); |
745 | } | 771 | } |
746 | }; | 772 | }; |
@@ -750,8 +776,29 @@ | |||
750 | CKEDITOR.unlinkCommand = function() {}; | 776 | CKEDITOR.unlinkCommand = function() {}; |
751 | CKEDITOR.unlinkCommand.prototype = { | 777 | CKEDITOR.unlinkCommand.prototype = { |
752 | exec: function( editor ) { | 778 | exec: function( editor ) { |
779 | // IE/Edge removes link from selection while executing "unlink" command when cursor | ||
780 | // is right before/after link's text. Therefore whole link must be selected and the | ||
781 | // position of cursor must be restored to its initial state after unlinking. (http://dev.ckeditor.com/ticket/13062) | ||
782 | if ( CKEDITOR.env.ie ) { | ||
783 | var range = editor.getSelection().getRanges()[ 0 ], | ||
784 | link = ( range.getPreviousEditableNode() && range.getPreviousEditableNode().getAscendant( 'a', true ) ) || | ||
785 | ( range.getNextEditableNode() && range.getNextEditableNode().getAscendant( 'a', true ) ), | ||
786 | bookmark; | ||
787 | |||
788 | if ( range.collapsed && link ) { | ||
789 | bookmark = range.createBookmark(); | ||
790 | range.selectNodeContents( link ); | ||
791 | range.select(); | ||
792 | } | ||
793 | } | ||
794 | |||
753 | var style = new CKEDITOR.style( { element: 'a', type: CKEDITOR.STYLE_INLINE, alwaysRemoveElement: 1 } ); | 795 | var style = new CKEDITOR.style( { element: 'a', type: CKEDITOR.STYLE_INLINE, alwaysRemoveElement: 1 } ); |
754 | editor.removeStyle( style ); | 796 | editor.removeStyle( style ); |
797 | |||
798 | if ( bookmark ) { | ||
799 | range.moveToBookmark( bookmark ); | ||
800 | range.select(); | ||
801 | } | ||
755 | }, | 802 | }, |
756 | 803 | ||
757 | refresh: function( editor, path ) { | 804 | refresh: function( editor, path ) { |
@@ -768,7 +815,8 @@ | |||
768 | 815 | ||
769 | contextSensitive: 1, | 816 | contextSensitive: 1, |
770 | startDisabled: 1, | 817 | startDisabled: 1, |
771 | requiredContent: 'a[href]' | 818 | requiredContent: 'a[href]', |
819 | editorFocus: 1 | ||
772 | }; | 820 | }; |
773 | 821 | ||
774 | CKEDITOR.removeAnchorCommand = function() {}; | 822 | CKEDITOR.removeAnchorCommand = function() {}; |