@ckeditor/ckeditor5-link 44.3.0 → 45.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/build/link.js +2 -2
- package/build/translations/af.js +1 -1
- package/build/translations/ar.js +1 -1
- package/build/translations/ast.js +1 -1
- package/build/translations/az.js +1 -1
- package/build/translations/be.js +1 -0
- package/build/translations/bg.js +1 -1
- package/build/translations/bn.js +1 -1
- package/build/translations/bs.js +1 -1
- package/build/translations/ca.js +1 -1
- package/build/translations/cs.js +1 -1
- package/build/translations/da.js +1 -1
- package/build/translations/de-ch.js +1 -1
- package/build/translations/de.js +1 -1
- package/build/translations/el.js +1 -1
- package/build/translations/en-au.js +1 -1
- package/build/translations/en-gb.js +1 -1
- package/build/translations/eo.js +1 -1
- package/build/translations/es-co.js +1 -1
- package/build/translations/es.js +1 -1
- package/build/translations/et.js +1 -1
- package/build/translations/eu.js +1 -1
- package/build/translations/fa.js +1 -1
- package/build/translations/fi.js +1 -1
- package/build/translations/fr.js +1 -1
- package/build/translations/gl.js +1 -1
- package/build/translations/gu.js +1 -1
- package/build/translations/he.js +1 -1
- package/build/translations/hi.js +1 -1
- package/build/translations/hr.js +1 -1
- package/build/translations/hu.js +1 -1
- package/build/translations/hy.js +1 -1
- package/build/translations/id.js +1 -1
- package/build/translations/it.js +1 -1
- package/build/translations/ja.js +1 -1
- package/build/translations/jv.js +1 -1
- package/build/translations/kk.js +1 -1
- package/build/translations/km.js +1 -1
- package/build/translations/kn.js +1 -1
- package/build/translations/ko.js +1 -1
- package/build/translations/ku.js +1 -1
- package/build/translations/lt.js +1 -1
- package/build/translations/lv.js +1 -1
- package/build/translations/ms.js +1 -1
- package/build/translations/nb.js +1 -1
- package/build/translations/ne.js +1 -1
- package/build/translations/nl.js +1 -1
- package/build/translations/no.js +1 -1
- package/build/translations/oc.js +1 -1
- package/build/translations/pl.js +1 -1
- package/build/translations/pt-br.js +1 -1
- package/build/translations/pt.js +1 -1
- package/build/translations/ro.js +1 -1
- package/build/translations/ru.js +1 -1
- package/build/translations/si.js +1 -1
- package/build/translations/sk.js +1 -1
- package/build/translations/sl.js +1 -1
- package/build/translations/sq.js +1 -1
- package/build/translations/sr-latn.js +1 -1
- package/build/translations/sr.js +1 -1
- package/build/translations/sv.js +1 -1
- package/build/translations/th.js +1 -1
- package/build/translations/ti.js +1 -1
- package/build/translations/tk.js +1 -1
- package/build/translations/tr.js +1 -1
- package/build/translations/tt.js +1 -1
- package/build/translations/ug.js +1 -1
- package/build/translations/uk.js +1 -1
- package/build/translations/ur.js +1 -1
- package/build/translations/uz.js +1 -1
- package/build/translations/vi.js +1 -1
- package/build/translations/zh-cn.js +1 -1
- package/build/translations/zh.js +1 -1
- package/ckeditor5-metadata.json +2 -2
- package/dist/index-editor.css +87 -47
- package/dist/index.css +108 -58
- package/dist/index.css.map +1 -1
- package/dist/index.js +1161 -425
- package/dist/index.js.map +1 -1
- package/dist/translations/af.js +1 -1
- package/dist/translations/af.umd.js +1 -1
- package/dist/translations/ar.js +1 -1
- package/dist/translations/ar.umd.js +1 -1
- package/dist/translations/ast.js +1 -1
- package/dist/translations/ast.umd.js +1 -1
- package/dist/translations/az.js +1 -1
- package/dist/translations/az.umd.js +1 -1
- package/dist/translations/be.d.ts +8 -0
- package/dist/translations/be.js +5 -0
- package/dist/translations/be.umd.js +11 -0
- package/dist/translations/bg.js +1 -1
- package/dist/translations/bg.umd.js +1 -1
- package/dist/translations/bn.js +1 -1
- package/dist/translations/bn.umd.js +1 -1
- package/dist/translations/bs.js +1 -1
- package/dist/translations/bs.umd.js +1 -1
- package/dist/translations/ca.js +1 -1
- package/dist/translations/ca.umd.js +1 -1
- package/dist/translations/cs.js +1 -1
- package/dist/translations/cs.umd.js +1 -1
- package/dist/translations/da.js +1 -1
- package/dist/translations/da.umd.js +1 -1
- package/dist/translations/de-ch.js +1 -1
- package/dist/translations/de-ch.umd.js +1 -1
- package/dist/translations/de.js +1 -1
- package/dist/translations/de.umd.js +1 -1
- package/dist/translations/el.js +1 -1
- package/dist/translations/el.umd.js +1 -1
- package/dist/translations/en-au.js +1 -1
- package/dist/translations/en-au.umd.js +1 -1
- package/dist/translations/en-gb.js +1 -1
- package/dist/translations/en-gb.umd.js +1 -1
- package/dist/translations/en.js +1 -1
- package/dist/translations/en.umd.js +1 -1
- package/dist/translations/eo.js +1 -1
- package/dist/translations/eo.umd.js +1 -1
- package/dist/translations/es-co.js +1 -1
- package/dist/translations/es-co.umd.js +1 -1
- package/dist/translations/es.js +1 -1
- package/dist/translations/es.umd.js +1 -1
- package/dist/translations/et.js +1 -1
- package/dist/translations/et.umd.js +1 -1
- package/dist/translations/eu.js +1 -1
- package/dist/translations/eu.umd.js +1 -1
- package/dist/translations/fa.js +1 -1
- package/dist/translations/fa.umd.js +1 -1
- package/dist/translations/fi.js +1 -1
- package/dist/translations/fi.umd.js +1 -1
- package/dist/translations/fr.js +1 -1
- package/dist/translations/fr.umd.js +1 -1
- package/dist/translations/gl.js +1 -1
- package/dist/translations/gl.umd.js +1 -1
- package/dist/translations/gu.js +1 -1
- package/dist/translations/gu.umd.js +1 -1
- package/dist/translations/he.js +1 -1
- package/dist/translations/he.umd.js +1 -1
- package/dist/translations/hi.js +1 -1
- package/dist/translations/hi.umd.js +1 -1
- package/dist/translations/hr.js +1 -1
- package/dist/translations/hr.umd.js +1 -1
- package/dist/translations/hu.js +1 -1
- package/dist/translations/hu.umd.js +1 -1
- package/dist/translations/hy.js +1 -1
- package/dist/translations/hy.umd.js +1 -1
- package/dist/translations/id.js +1 -1
- package/dist/translations/id.umd.js +1 -1
- package/dist/translations/it.js +1 -1
- package/dist/translations/it.umd.js +1 -1
- package/dist/translations/ja.js +1 -1
- package/dist/translations/ja.umd.js +1 -1
- package/dist/translations/jv.js +1 -1
- package/dist/translations/jv.umd.js +1 -1
- package/dist/translations/kk.js +1 -1
- package/dist/translations/kk.umd.js +1 -1
- package/dist/translations/km.js +1 -1
- package/dist/translations/km.umd.js +1 -1
- package/dist/translations/kn.js +1 -1
- package/dist/translations/kn.umd.js +1 -1
- package/dist/translations/ko.js +1 -1
- package/dist/translations/ko.umd.js +1 -1
- package/dist/translations/ku.js +1 -1
- package/dist/translations/ku.umd.js +1 -1
- package/dist/translations/lt.js +1 -1
- package/dist/translations/lt.umd.js +1 -1
- package/dist/translations/lv.js +1 -1
- package/dist/translations/lv.umd.js +1 -1
- package/dist/translations/ms.js +1 -1
- package/dist/translations/ms.umd.js +1 -1
- package/dist/translations/nb.js +1 -1
- package/dist/translations/nb.umd.js +1 -1
- package/dist/translations/ne.js +1 -1
- package/dist/translations/ne.umd.js +1 -1
- package/dist/translations/nl.js +1 -1
- package/dist/translations/nl.umd.js +1 -1
- package/dist/translations/no.js +1 -1
- package/dist/translations/no.umd.js +1 -1
- package/dist/translations/oc.js +1 -1
- package/dist/translations/oc.umd.js +1 -1
- package/dist/translations/pl.js +1 -1
- package/dist/translations/pl.umd.js +1 -1
- package/dist/translations/pt-br.js +1 -1
- package/dist/translations/pt-br.umd.js +1 -1
- package/dist/translations/pt.js +1 -1
- package/dist/translations/pt.umd.js +1 -1
- package/dist/translations/ro.js +1 -1
- package/dist/translations/ro.umd.js +1 -1
- package/dist/translations/ru.js +1 -1
- package/dist/translations/ru.umd.js +1 -1
- package/dist/translations/si.js +1 -1
- package/dist/translations/si.umd.js +1 -1
- package/dist/translations/sk.js +1 -1
- package/dist/translations/sk.umd.js +1 -1
- package/dist/translations/sl.js +1 -1
- package/dist/translations/sl.umd.js +1 -1
- package/dist/translations/sq.js +1 -1
- package/dist/translations/sq.umd.js +1 -1
- package/dist/translations/sr-latn.js +1 -1
- package/dist/translations/sr-latn.umd.js +1 -1
- package/dist/translations/sr.js +1 -1
- package/dist/translations/sr.umd.js +1 -1
- package/dist/translations/sv.js +1 -1
- package/dist/translations/sv.umd.js +1 -1
- package/dist/translations/th.js +1 -1
- package/dist/translations/th.umd.js +1 -1
- package/dist/translations/ti.js +1 -1
- package/dist/translations/ti.umd.js +1 -1
- package/dist/translations/tk.js +1 -1
- package/dist/translations/tk.umd.js +1 -1
- package/dist/translations/tr.js +1 -1
- package/dist/translations/tr.umd.js +1 -1
- package/dist/translations/tt.js +1 -1
- package/dist/translations/tt.umd.js +1 -1
- package/dist/translations/ug.js +1 -1
- package/dist/translations/ug.umd.js +1 -1
- package/dist/translations/uk.js +1 -1
- package/dist/translations/uk.umd.js +1 -1
- package/dist/translations/ur.js +1 -1
- package/dist/translations/ur.umd.js +1 -1
- package/dist/translations/uz.js +1 -1
- package/dist/translations/uz.umd.js +1 -1
- package/dist/translations/vi.js +1 -1
- package/dist/translations/vi.umd.js +1 -1
- package/dist/translations/zh-cn.js +1 -1
- package/dist/translations/zh-cn.umd.js +1 -1
- package/dist/translations/zh.js +1 -1
- package/dist/translations/zh.umd.js +1 -1
- package/lang/contexts.json +4 -3
- package/lang/translations/af.po +10 -6
- package/lang/translations/ar.po +11 -7
- package/lang/translations/ast.po +10 -6
- package/lang/translations/az.po +10 -6
- package/lang/translations/be.po +68 -0
- package/lang/translations/bg.po +11 -7
- package/lang/translations/bn.po +11 -7
- package/lang/translations/bs.po +10 -6
- package/lang/translations/ca.po +11 -7
- package/lang/translations/cs.po +11 -7
- package/lang/translations/da.po +11 -7
- package/lang/translations/de-ch.po +10 -6
- package/lang/translations/de.po +11 -7
- package/lang/translations/el.po +11 -7
- package/lang/translations/en-au.po +11 -7
- package/lang/translations/en-gb.po +11 -7
- package/lang/translations/en.po +11 -7
- package/lang/translations/eo.po +10 -6
- package/lang/translations/es-co.po +10 -6
- package/lang/translations/es.po +11 -7
- package/lang/translations/et.po +11 -7
- package/lang/translations/eu.po +10 -6
- package/lang/translations/fa.po +10 -6
- package/lang/translations/fi.po +11 -7
- package/lang/translations/fr.po +11 -7
- package/lang/translations/gl.po +10 -6
- package/lang/translations/gu.po +10 -6
- package/lang/translations/he.po +11 -7
- package/lang/translations/hi.po +11 -7
- package/lang/translations/hr.po +10 -6
- package/lang/translations/hu.po +11 -7
- package/lang/translations/hy.po +10 -6
- package/lang/translations/id.po +11 -7
- package/lang/translations/it.po +11 -7
- package/lang/translations/ja.po +11 -7
- package/lang/translations/jv.po +10 -6
- package/lang/translations/kk.po +10 -6
- package/lang/translations/km.po +10 -6
- package/lang/translations/kn.po +10 -6
- package/lang/translations/ko.po +11 -7
- package/lang/translations/ku.po +10 -6
- package/lang/translations/lt.po +11 -7
- package/lang/translations/lv.po +11 -7
- package/lang/translations/ms.po +11 -7
- package/lang/translations/nb.po +10 -6
- package/lang/translations/ne.po +10 -6
- package/lang/translations/nl.po +11 -7
- package/lang/translations/no.po +11 -7
- package/lang/translations/oc.po +10 -6
- package/lang/translations/pl.po +11 -7
- package/lang/translations/pt-br.po +11 -7
- package/lang/translations/pt.po +11 -7
- package/lang/translations/ro.po +11 -7
- package/lang/translations/ru.po +11 -7
- package/lang/translations/si.po +10 -6
- package/lang/translations/sk.po +11 -7
- package/lang/translations/sl.po +10 -6
- package/lang/translations/sq.po +10 -6
- package/lang/translations/sr-latn.po +10 -6
- package/lang/translations/sr.po +11 -7
- package/lang/translations/sv.po +11 -7
- package/lang/translations/th.po +11 -7
- package/lang/translations/ti.po +10 -6
- package/lang/translations/tk.po +10 -6
- package/lang/translations/tr.po +11 -7
- package/lang/translations/tt.po +10 -6
- package/lang/translations/ug.po +10 -6
- package/lang/translations/uk.po +11 -7
- package/lang/translations/ur.po +10 -6
- package/lang/translations/uz.po +10 -6
- package/lang/translations/vi.po +11 -7
- package/lang/translations/zh-cn.po +11 -7
- package/lang/translations/zh.po +11 -7
- package/package.json +12 -12
- package/src/autolink.js +3 -0
- package/src/index.d.ts +1 -2
- package/src/index.js +0 -1
- package/src/linkcommand.d.ts +17 -10
- package/src/linkcommand.js +212 -82
- package/src/linkconfig.d.ts +28 -0
- package/src/linkediting.d.ts +18 -0
- package/src/linkediting.js +19 -9
- package/src/linkimageui.d.ts +1 -1
- package/src/linkimageui.js +4 -4
- package/src/linkui.d.ts +215 -24
- package/src/linkui.js +517 -109
- package/src/ui/linkbuttonview.d.ts +31 -0
- package/src/ui/linkbuttonview.js +54 -0
- package/src/ui/linkformview.d.ts +34 -49
- package/src/ui/linkformview.js +163 -134
- package/src/ui/linkpreviewbuttonview.d.ts +35 -0
- package/src/ui/linkpreviewbuttonview.js +43 -0
- package/src/ui/linkpropertiesview.d.ts +88 -0
- package/src/ui/linkpropertiesview.js +170 -0
- package/src/ui/linkprovideritemsview.d.ts +114 -0
- package/src/ui/linkprovideritemsview.js +207 -0
- package/src/utils/automaticdecorators.js +5 -7
- package/src/utils/manualdecorator.js +27 -0
- package/src/utils.d.ts +5 -5
- package/src/utils.js +12 -32
- package/theme/linkform.css +11 -33
- package/theme/linkproperties.css +4 -0
- package/theme/linkprovideritems.css +18 -0
- package/theme/linktoolbar.css +12 -0
- package/src/ui/linkactionsview.d.ts +0 -117
- package/src/ui/linkactionsview.js +0 -173
- package/theme/icons/link.svg +0 -1
- package/theme/icons/unlink.svg +0 -1
- package/theme/linkactions.css +0 -32
package/src/linkui.js
CHANGED
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
* @module link/linkui
|
|
7
7
|
*/
|
|
8
8
|
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
+
import { IconLink, IconPencil, IconUnlink, IconSettings } from 'ckeditor5/src/icons.js';
|
|
9
10
|
import { ClickObserver } from 'ckeditor5/src/engine.js';
|
|
10
|
-
import { ButtonView, ContextualBalloon, clickOutsideHandler, CssTransitionDisablerMixin, MenuBarMenuListItemButtonView } from 'ckeditor5/src/ui.js';
|
|
11
|
+
import { ButtonView, SwitchButtonView, ContextualBalloon, clickOutsideHandler, CssTransitionDisablerMixin, MenuBarMenuListItemButtonView, ToolbarView } from 'ckeditor5/src/ui.js';
|
|
12
|
+
import { Collection } from 'ckeditor5/src/utils.js';
|
|
11
13
|
import { isWidget } from 'ckeditor5/src/widget.js';
|
|
14
|
+
import LinkEditing from './linkediting.js';
|
|
15
|
+
import LinkPreviewButtonView from './ui/linkpreviewbuttonview.js';
|
|
12
16
|
import LinkFormView from './ui/linkformview.js';
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
17
|
+
import LinkProviderItemsView from './ui/linkprovideritemsview.js';
|
|
18
|
+
import LinkPropertiesView from './ui/linkpropertiesview.js';
|
|
19
|
+
import LinkButtonView from './ui/linkbuttonview.js';
|
|
20
|
+
import { addLinkProtocolIfApplicable, ensureSafeUrl, isLinkElement, extractTextFromLinkRange, LINK_KEYSTROKE } from './utils.js';
|
|
21
|
+
import '../theme/linktoolbar.css';
|
|
16
22
|
const VISUAL_SELECTION_MARKER_NAME = 'link-ui';
|
|
17
23
|
/**
|
|
18
24
|
* The link UI plugin. It introduces the `'link'` and `'unlink'` buttons and support for the <kbd>Ctrl+K</kbd> keystroke.
|
|
@@ -21,22 +27,35 @@ const VISUAL_SELECTION_MARKER_NAME = 'link-ui';
|
|
|
21
27
|
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
|
|
22
28
|
*/
|
|
23
29
|
export default class LinkUI extends Plugin {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The toolbar view displayed inside of the balloon.
|
|
32
|
+
*/
|
|
33
|
+
toolbarView = null;
|
|
34
|
+
/**
|
|
35
|
+
* The form view displayed inside the balloon.
|
|
36
|
+
*/
|
|
37
|
+
formView = null;
|
|
38
|
+
/**
|
|
39
|
+
* The view displaying links list.
|
|
40
|
+
*/
|
|
41
|
+
linkProviderItemsView = null;
|
|
42
|
+
/**
|
|
43
|
+
* The form view displaying properties link settings.
|
|
44
|
+
*/
|
|
45
|
+
propertiesView = null;
|
|
46
|
+
/**
|
|
47
|
+
* The contextual balloon plugin instance.
|
|
48
|
+
*/
|
|
49
|
+
_balloon;
|
|
50
|
+
/**
|
|
51
|
+
* The collection of the link providers.
|
|
52
|
+
*/
|
|
53
|
+
_linksProviders = new Collection();
|
|
35
54
|
/**
|
|
36
55
|
* @inheritDoc
|
|
37
56
|
*/
|
|
38
57
|
static get requires() {
|
|
39
|
-
return [ContextualBalloon];
|
|
58
|
+
return [ContextualBalloon, LinkEditing];
|
|
40
59
|
}
|
|
41
60
|
/**
|
|
42
61
|
* @inheritDoc
|
|
@@ -56,10 +75,12 @@ export default class LinkUI extends Plugin {
|
|
|
56
75
|
init() {
|
|
57
76
|
const editor = this.editor;
|
|
58
77
|
const t = this.editor.t;
|
|
78
|
+
this.set('selectedLinkableText', undefined);
|
|
59
79
|
editor.editing.view.addObserver(ClickObserver);
|
|
60
80
|
this._balloon = editor.plugins.get(ContextualBalloon);
|
|
61
81
|
// Create toolbar buttons.
|
|
62
|
-
this.
|
|
82
|
+
this._registerComponents();
|
|
83
|
+
this._registerEditingOpeners();
|
|
63
84
|
this._enableBalloonActivators();
|
|
64
85
|
// Renders a fake visual selection marker on an expanded selection.
|
|
65
86
|
editor.conversion.for('editingDowncast').markerToHighlight({
|
|
@@ -103,73 +124,104 @@ export default class LinkUI extends Plugin {
|
|
|
103
124
|
destroy() {
|
|
104
125
|
super.destroy();
|
|
105
126
|
// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
|
|
127
|
+
if (this.propertiesView) {
|
|
128
|
+
this.propertiesView.destroy();
|
|
129
|
+
}
|
|
106
130
|
if (this.formView) {
|
|
107
131
|
this.formView.destroy();
|
|
108
132
|
}
|
|
109
|
-
if (this.
|
|
110
|
-
this.
|
|
133
|
+
if (this.toolbarView) {
|
|
134
|
+
this.toolbarView.destroy();
|
|
135
|
+
}
|
|
136
|
+
if (this.linkProviderItemsView) {
|
|
137
|
+
this.linkProviderItemsView.destroy();
|
|
111
138
|
}
|
|
112
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Registers list of buttons below the link form view that
|
|
142
|
+
* open a list of links provided by the clicked provider.
|
|
143
|
+
*/
|
|
144
|
+
registerLinksListProvider(provider) {
|
|
145
|
+
const insertIndex = this._linksProviders
|
|
146
|
+
.filter(existing => (existing.order || 0) <= (provider.order || 0))
|
|
147
|
+
.length;
|
|
148
|
+
this._linksProviders.add(provider, insertIndex);
|
|
149
|
+
}
|
|
113
150
|
/**
|
|
114
151
|
* Creates views.
|
|
115
152
|
*/
|
|
116
153
|
_createViews() {
|
|
117
|
-
|
|
154
|
+
const linkCommand = this.editor.commands.get('link');
|
|
155
|
+
this.toolbarView = this._createToolbarView();
|
|
118
156
|
this.formView = this._createFormView();
|
|
157
|
+
if (linkCommand.manualDecorators.length) {
|
|
158
|
+
this.propertiesView = this._createPropertiesView();
|
|
159
|
+
}
|
|
119
160
|
// Attach lifecycle actions to the the balloon.
|
|
120
161
|
this._enableUserBalloonInteractions();
|
|
121
162
|
}
|
|
122
163
|
/**
|
|
123
|
-
* Creates the
|
|
164
|
+
* Creates the ToolbarView instance.
|
|
124
165
|
*/
|
|
125
|
-
|
|
166
|
+
_createToolbarView() {
|
|
126
167
|
const editor = this.editor;
|
|
127
|
-
const
|
|
168
|
+
const toolbarView = new ToolbarView(editor.locale);
|
|
128
169
|
const linkCommand = editor.commands.get('link');
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.listenTo(actionsView, 'unlink', () => {
|
|
139
|
-
editor.execute('unlink');
|
|
140
|
-
this._hideUI();
|
|
141
|
-
});
|
|
142
|
-
// Close the panel on esc key press when the **actions have focus**.
|
|
143
|
-
actionsView.keystrokes.set('Esc', (data, cancel) => {
|
|
170
|
+
toolbarView.class = 'ck-link-toolbar';
|
|
171
|
+
// Remove the linkProperties button if there are no manual decorators, as it would be useless.
|
|
172
|
+
let toolbarItems = editor.config.get('link.toolbar');
|
|
173
|
+
if (!linkCommand.manualDecorators.length) {
|
|
174
|
+
toolbarItems = toolbarItems.filter(item => item !== 'linkProperties');
|
|
175
|
+
}
|
|
176
|
+
toolbarView.fillFromConfig(toolbarItems, editor.ui.componentFactory);
|
|
177
|
+
// Close the panel on esc key press when the **link toolbar have focus**.
|
|
178
|
+
toolbarView.keystrokes.set('Esc', (data, cancel) => {
|
|
144
179
|
this._hideUI();
|
|
145
180
|
cancel();
|
|
146
181
|
});
|
|
147
|
-
// Open the form view on Ctrl+K when the **
|
|
148
|
-
|
|
182
|
+
// Open the form view on Ctrl+K when the **link toolbar have focus**..
|
|
183
|
+
toolbarView.keystrokes.set(LINK_KEYSTROKE, (data, cancel) => {
|
|
149
184
|
this._addFormView();
|
|
150
185
|
cancel();
|
|
151
186
|
});
|
|
152
|
-
|
|
187
|
+
// Register the toolbar, so it becomes available for Alt+F10 and Esc navigation.
|
|
188
|
+
// TODO this should be registered earlier to be able to open this toolbar without previously opening it by click or Ctrl+K
|
|
189
|
+
editor.ui.addToolbar(toolbarView, {
|
|
190
|
+
isContextual: true,
|
|
191
|
+
beforeFocus: () => {
|
|
192
|
+
if (this._getSelectedLinkElement() && !this._isToolbarVisible) {
|
|
193
|
+
this._showUI(true);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
afterBlur: () => {
|
|
197
|
+
this._hideUI(false);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return toolbarView;
|
|
153
201
|
}
|
|
154
202
|
/**
|
|
155
203
|
* Creates the {@link module:link/ui/linkformview~LinkFormView} instance.
|
|
156
204
|
*/
|
|
157
205
|
_createFormView() {
|
|
158
206
|
const editor = this.editor;
|
|
207
|
+
const t = editor.locale.t;
|
|
159
208
|
const linkCommand = editor.commands.get('link');
|
|
160
209
|
const defaultProtocol = editor.config.get('link.defaultProtocol');
|
|
161
|
-
const formView = new (CssTransitionDisablerMixin(LinkFormView))(editor.locale,
|
|
162
|
-
formView.
|
|
210
|
+
const formView = new (CssTransitionDisablerMixin(LinkFormView))(editor.locale, getFormValidators(editor));
|
|
211
|
+
formView.displayedTextInputView.bind('isEnabled').to(this, 'selectedLinkableText', value => value !== undefined);
|
|
163
212
|
// Form elements should be read-only when corresponding commands are disabled.
|
|
164
213
|
formView.urlInputView.bind('isEnabled').to(linkCommand, 'isEnabled');
|
|
165
214
|
// Disable the "save" button if the command is disabled.
|
|
166
215
|
formView.saveButtonView.bind('isEnabled').to(linkCommand, 'isEnabled');
|
|
216
|
+
// Change the "Save" button label depending on the command state.
|
|
217
|
+
formView.saveButtonView.bind('label').to(linkCommand, 'value', value => value ? t('Update') : t('Insert'));
|
|
167
218
|
// Execute link command after clicking the "Save" button.
|
|
168
219
|
this.listenTo(formView, 'submit', () => {
|
|
169
220
|
if (formView.isValid()) {
|
|
170
|
-
const
|
|
171
|
-
const parsedUrl = addLinkProtocolIfApplicable(
|
|
172
|
-
|
|
221
|
+
const url = formView.urlInputView.fieldView.element.value;
|
|
222
|
+
const parsedUrl = addLinkProtocolIfApplicable(url, defaultProtocol);
|
|
223
|
+
const displayedText = formView.displayedTextInputView.fieldView.element.value;
|
|
224
|
+
editor.execute('link', parsedUrl, this._getDecoratorSwitchesState(), displayedText !== this.selectedLinkableText ? displayedText : undefined);
|
|
173
225
|
this._closeFormView();
|
|
174
226
|
}
|
|
175
227
|
});
|
|
@@ -186,13 +238,124 @@ export default class LinkUI extends Plugin {
|
|
|
186
238
|
this._closeFormView();
|
|
187
239
|
cancel();
|
|
188
240
|
});
|
|
241
|
+
// Watch adding new link providers and add them to the buttons list.
|
|
242
|
+
formView.providersListChildren.bindTo(this._linksProviders).using(provider => this._createLinksListProviderButton(provider));
|
|
189
243
|
return formView;
|
|
190
244
|
}
|
|
191
245
|
/**
|
|
192
|
-
* Creates a
|
|
193
|
-
|
|
246
|
+
* Creates a sorted array of buttons with link names.
|
|
247
|
+
*/
|
|
248
|
+
_createLinkProviderListView(provider) {
|
|
249
|
+
return provider.getListItems().map(({ href, label, icon }) => {
|
|
250
|
+
const buttonView = new ButtonView();
|
|
251
|
+
buttonView.set({
|
|
252
|
+
label,
|
|
253
|
+
icon,
|
|
254
|
+
tooltip: false,
|
|
255
|
+
withText: true
|
|
256
|
+
});
|
|
257
|
+
buttonView.on('execute', () => {
|
|
258
|
+
this.formView.resetFormStatus();
|
|
259
|
+
this.formView.urlInputView.fieldView.value = href;
|
|
260
|
+
// Set focus to the editing view to prevent from losing it while current view is removed.
|
|
261
|
+
this.editor.editing.view.focus();
|
|
262
|
+
this._removeLinksProviderView();
|
|
263
|
+
// Set the focus to the URL input field.
|
|
264
|
+
this.formView.focus();
|
|
265
|
+
});
|
|
266
|
+
return buttonView;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Creates a view for links provider.
|
|
194
271
|
*/
|
|
195
|
-
|
|
272
|
+
_createLinkProviderItemsView(provider) {
|
|
273
|
+
const editor = this.editor;
|
|
274
|
+
const t = editor.locale.t;
|
|
275
|
+
const view = new LinkProviderItemsView(editor.locale);
|
|
276
|
+
const { emptyListPlaceholder, label } = provider;
|
|
277
|
+
view.emptyListPlaceholder = emptyListPlaceholder || t('No links available');
|
|
278
|
+
view.title = label;
|
|
279
|
+
// Hide the panel after clicking the "Cancel" button.
|
|
280
|
+
this.listenTo(view, 'cancel', () => {
|
|
281
|
+
// Set focus to the editing view to prevent from losing it while current view is removed.
|
|
282
|
+
editor.editing.view.focus();
|
|
283
|
+
this._removeLinksProviderView();
|
|
284
|
+
// Set the focus to the URL input field.
|
|
285
|
+
this.formView.focus();
|
|
286
|
+
});
|
|
287
|
+
return view;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Creates the {@link module:link/ui/linkpropertiesview~LinkPropertiesView} instance.
|
|
291
|
+
*/
|
|
292
|
+
_createPropertiesView() {
|
|
293
|
+
const editor = this.editor;
|
|
294
|
+
const linkCommand = this.editor.commands.get('link');
|
|
295
|
+
const view = new (CssTransitionDisablerMixin(LinkPropertiesView))(editor.locale);
|
|
296
|
+
// Hide the panel after clicking the back button.
|
|
297
|
+
this.listenTo(view, 'back', () => {
|
|
298
|
+
// Move focus back to the editing view to prevent from losing it while current view is removed.
|
|
299
|
+
editor.editing.view.focus();
|
|
300
|
+
this._removePropertiesView();
|
|
301
|
+
});
|
|
302
|
+
view.listChildren.bindTo(linkCommand.manualDecorators).using(manualDecorator => {
|
|
303
|
+
const button = new SwitchButtonView(editor.locale);
|
|
304
|
+
button.set({
|
|
305
|
+
label: manualDecorator.label,
|
|
306
|
+
withText: true
|
|
307
|
+
});
|
|
308
|
+
button.bind('isOn').toMany([manualDecorator, linkCommand], 'value', (decoratorValue, commandValue) => {
|
|
309
|
+
return commandValue === undefined && decoratorValue === undefined ?
|
|
310
|
+
!!manualDecorator.defaultValue :
|
|
311
|
+
!!decoratorValue;
|
|
312
|
+
});
|
|
313
|
+
button.on('execute', () => {
|
|
314
|
+
manualDecorator.set('value', !button.isOn);
|
|
315
|
+
editor.execute('link', linkCommand.value, this._getDecoratorSwitchesState());
|
|
316
|
+
});
|
|
317
|
+
return button;
|
|
318
|
+
});
|
|
319
|
+
return view;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Obtains the state of the manual decorators.
|
|
323
|
+
*/
|
|
324
|
+
_getDecoratorSwitchesState() {
|
|
325
|
+
const linkCommand = this.editor.commands.get('link');
|
|
326
|
+
return Array
|
|
327
|
+
.from(linkCommand.manualDecorators)
|
|
328
|
+
.reduce((accumulator, manualDecorator) => {
|
|
329
|
+
const value = linkCommand.value === undefined && manualDecorator.value === undefined ?
|
|
330
|
+
manualDecorator.defaultValue :
|
|
331
|
+
manualDecorator.value;
|
|
332
|
+
return {
|
|
333
|
+
...accumulator,
|
|
334
|
+
[manualDecorator.id]: !!value
|
|
335
|
+
};
|
|
336
|
+
}, {});
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Registers listeners used in editing plugin, used to open links.
|
|
340
|
+
*/
|
|
341
|
+
_registerEditingOpeners() {
|
|
342
|
+
const linkEditing = this.editor.plugins.get(LinkEditing);
|
|
343
|
+
linkEditing._registerLinkOpener(href => {
|
|
344
|
+
const match = this._getLinkProviderLinkByHref(href);
|
|
345
|
+
if (!match) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
const { item, provider } = match;
|
|
349
|
+
if (provider.navigate) {
|
|
350
|
+
return provider.navigate(item);
|
|
351
|
+
}
|
|
352
|
+
return false;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Registers components in the ComponentFactory.
|
|
357
|
+
*/
|
|
358
|
+
_registerComponents() {
|
|
196
359
|
const editor = this.editor;
|
|
197
360
|
editor.ui.componentFactory.add('link', () => {
|
|
198
361
|
const button = this._createButton(ButtonView);
|
|
@@ -208,6 +371,113 @@ export default class LinkUI extends Plugin {
|
|
|
208
371
|
});
|
|
209
372
|
return button;
|
|
210
373
|
});
|
|
374
|
+
editor.ui.componentFactory.add('linkPreview', locale => {
|
|
375
|
+
const button = new LinkPreviewButtonView(locale);
|
|
376
|
+
const allowedProtocols = editor.config.get('link.allowedProtocols');
|
|
377
|
+
const linkCommand = editor.commands.get('link');
|
|
378
|
+
const t = locale.t;
|
|
379
|
+
button.bind('isEnabled').to(linkCommand, 'value', href => !!href);
|
|
380
|
+
button.bind('href').to(linkCommand, 'value', href => {
|
|
381
|
+
return href && ensureSafeUrl(href, allowedProtocols);
|
|
382
|
+
});
|
|
383
|
+
const setHref = (href) => {
|
|
384
|
+
if (!href) {
|
|
385
|
+
button.label = undefined;
|
|
386
|
+
button.icon = undefined;
|
|
387
|
+
button.tooltip = t('Open link in new tab');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const selectedLinksProviderLink = this._getLinkProviderLinkByHref(href);
|
|
391
|
+
if (selectedLinksProviderLink) {
|
|
392
|
+
const { label, tooltip, icon } = selectedLinksProviderLink.item;
|
|
393
|
+
button.label = label;
|
|
394
|
+
button.tooltip = tooltip || false;
|
|
395
|
+
button.icon = icon;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
button.label = href;
|
|
399
|
+
button.icon = undefined;
|
|
400
|
+
button.tooltip = t('Open link in new tab');
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
setHref(linkCommand.value);
|
|
404
|
+
this.listenTo(linkCommand, 'change:value', (evt, name, href) => {
|
|
405
|
+
setHref(href);
|
|
406
|
+
});
|
|
407
|
+
this.listenTo(button, 'navigate', (evt, href, cancel) => {
|
|
408
|
+
const selectedLinksProviderLink = this._getLinkProviderLinkByHref(href);
|
|
409
|
+
if (!selectedLinksProviderLink) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const { provider, item } = selectedLinksProviderLink;
|
|
413
|
+
const { navigate } = provider;
|
|
414
|
+
if (navigate && navigate(item)) {
|
|
415
|
+
evt.stop();
|
|
416
|
+
cancel();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
return button;
|
|
420
|
+
});
|
|
421
|
+
editor.ui.componentFactory.add('unlink', locale => {
|
|
422
|
+
const unlinkCommand = editor.commands.get('unlink');
|
|
423
|
+
const button = new ButtonView(locale);
|
|
424
|
+
const t = locale.t;
|
|
425
|
+
button.set({
|
|
426
|
+
label: t('Unlink'),
|
|
427
|
+
icon: IconUnlink,
|
|
428
|
+
tooltip: true
|
|
429
|
+
});
|
|
430
|
+
button.bind('isEnabled').to(unlinkCommand);
|
|
431
|
+
this.listenTo(button, 'execute', () => {
|
|
432
|
+
editor.execute('unlink');
|
|
433
|
+
this._hideUI();
|
|
434
|
+
});
|
|
435
|
+
return button;
|
|
436
|
+
});
|
|
437
|
+
editor.ui.componentFactory.add('editLink', locale => {
|
|
438
|
+
const linkCommand = editor.commands.get('link');
|
|
439
|
+
const button = new ButtonView(locale);
|
|
440
|
+
const t = locale.t;
|
|
441
|
+
button.set({
|
|
442
|
+
label: t('Edit link'),
|
|
443
|
+
icon: IconPencil,
|
|
444
|
+
tooltip: true
|
|
445
|
+
});
|
|
446
|
+
button.bind('isEnabled').to(linkCommand);
|
|
447
|
+
this.listenTo(button, 'execute', () => {
|
|
448
|
+
this._addFormView();
|
|
449
|
+
});
|
|
450
|
+
return button;
|
|
451
|
+
});
|
|
452
|
+
editor.ui.componentFactory.add('linkProperties', locale => {
|
|
453
|
+
const linkCommand = editor.commands.get('link');
|
|
454
|
+
const button = new ButtonView(locale);
|
|
455
|
+
const t = locale.t;
|
|
456
|
+
button.set({
|
|
457
|
+
label: t('Link properties'),
|
|
458
|
+
icon: IconSettings,
|
|
459
|
+
tooltip: true
|
|
460
|
+
});
|
|
461
|
+
button.bind('isEnabled').to(linkCommand, 'isEnabled', linkCommand, 'value', linkCommand, 'manualDecorators', (isEnabled, href, manualDecorators) => isEnabled && !!href && manualDecorators.length > 0);
|
|
462
|
+
this.listenTo(button, 'execute', () => {
|
|
463
|
+
this._addPropertiesView();
|
|
464
|
+
});
|
|
465
|
+
return button;
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Creates a links button view.
|
|
470
|
+
*/
|
|
471
|
+
_createLinksListProviderButton(linkProvider) {
|
|
472
|
+
const locale = this.editor.locale;
|
|
473
|
+
const linksButton = new LinkButtonView(locale);
|
|
474
|
+
linksButton.set({
|
|
475
|
+
label: linkProvider.label
|
|
476
|
+
});
|
|
477
|
+
this.listenTo(linksButton, 'execute', () => {
|
|
478
|
+
this._showLinksProviderView(linkProvider);
|
|
479
|
+
});
|
|
480
|
+
return linksButton;
|
|
211
481
|
}
|
|
212
482
|
/**
|
|
213
483
|
* Creates a button for link command to use either in toolbar or in menu bar.
|
|
@@ -220,14 +490,21 @@ export default class LinkUI extends Plugin {
|
|
|
220
490
|
const t = locale.t;
|
|
221
491
|
view.set({
|
|
222
492
|
label: t('Link'),
|
|
223
|
-
icon:
|
|
493
|
+
icon: IconLink,
|
|
224
494
|
keystroke: LINK_KEYSTROKE,
|
|
225
495
|
isToggleable: true
|
|
226
496
|
});
|
|
227
497
|
view.bind('isEnabled').to(command, 'isEnabled');
|
|
228
498
|
view.bind('isOn').to(command, 'value', value => !!value);
|
|
229
499
|
// Show the panel on button click.
|
|
230
|
-
this.listenTo(view, 'execute', () =>
|
|
500
|
+
this.listenTo(view, 'execute', () => {
|
|
501
|
+
this._showUI(true);
|
|
502
|
+
// Open the form view on-top of the toolbar view if it's already visible.
|
|
503
|
+
// It should be visible every time the link is selected.
|
|
504
|
+
if (this._getSelectedLinkElement()) {
|
|
505
|
+
this._addFormView();
|
|
506
|
+
}
|
|
507
|
+
});
|
|
231
508
|
return view;
|
|
232
509
|
}
|
|
233
510
|
/**
|
|
@@ -262,8 +539,8 @@ export default class LinkUI extends Plugin {
|
|
|
262
539
|
_enableUserBalloonInteractions() {
|
|
263
540
|
// Focus the form if the balloon is visible and the Tab key has been pressed.
|
|
264
541
|
this.editor.keystrokes.set('Tab', (data, cancel) => {
|
|
265
|
-
if (this.
|
|
266
|
-
this.
|
|
542
|
+
if (this._isToolbarVisible && !this.toolbarView.focusTracker.isFocused) {
|
|
543
|
+
this.toolbarView.focus();
|
|
267
544
|
cancel();
|
|
268
545
|
}
|
|
269
546
|
}, {
|
|
@@ -288,20 +565,21 @@ export default class LinkUI extends Plugin {
|
|
|
288
565
|
});
|
|
289
566
|
}
|
|
290
567
|
/**
|
|
291
|
-
* Adds the {@link #
|
|
568
|
+
* Adds the {@link #toolbarView} to the {@link #_balloon}.
|
|
292
569
|
*
|
|
293
570
|
* @internal
|
|
294
571
|
*/
|
|
295
|
-
|
|
296
|
-
if (!this.
|
|
572
|
+
_addToolbarView() {
|
|
573
|
+
if (!this.toolbarView) {
|
|
297
574
|
this._createViews();
|
|
298
575
|
}
|
|
299
|
-
if (this.
|
|
576
|
+
if (this._isToolbarInPanel) {
|
|
300
577
|
return;
|
|
301
578
|
}
|
|
302
579
|
this._balloon.add({
|
|
303
|
-
view: this.
|
|
304
|
-
position: this._getBalloonPositionData()
|
|
580
|
+
view: this.toolbarView,
|
|
581
|
+
position: this._getBalloonPositionData(),
|
|
582
|
+
balloonClassName: 'ck-toolbar-container'
|
|
305
583
|
});
|
|
306
584
|
}
|
|
307
585
|
/**
|
|
@@ -314,20 +592,22 @@ export default class LinkUI extends Plugin {
|
|
|
314
592
|
if (this._isFormInPanel) {
|
|
315
593
|
return;
|
|
316
594
|
}
|
|
317
|
-
const
|
|
318
|
-
const linkCommand = editor.commands.get('link');
|
|
595
|
+
const linkCommand = this.editor.commands.get('link');
|
|
319
596
|
this.formView.disableCssTransitions();
|
|
320
597
|
this.formView.resetFormStatus();
|
|
598
|
+
this.formView.backButtonView.isVisible = linkCommand.isEnabled && !!linkCommand.value;
|
|
321
599
|
this._balloon.add({
|
|
322
600
|
view: this.formView,
|
|
323
601
|
position: this._getBalloonPositionData()
|
|
324
602
|
});
|
|
325
|
-
// Make sure that each time the panel shows up, the
|
|
603
|
+
// Make sure that each time the panel shows up, the fields remains in sync with the value of
|
|
326
604
|
// the command. If the user typed in the input, then canceled the balloon (`urlInputView.fieldView#value` stays
|
|
327
605
|
// unaltered) and re-opened it without changing the value of the link command (e.g. because they
|
|
328
606
|
// clicked the same link), they would see the old value instead of the actual value of the command.
|
|
329
607
|
// https://github.com/ckeditor/ckeditor5-link/issues/78
|
|
330
608
|
// https://github.com/ckeditor/ckeditor5-link/issues/123
|
|
609
|
+
this.selectedLinkableText = this._getSelectedLinkableText();
|
|
610
|
+
this.formView.displayedTextInputView.fieldView.value = this.selectedLinkableText || '';
|
|
331
611
|
this.formView.urlInputView.fieldView.value = linkCommand.value || '';
|
|
332
612
|
// Select input when form view is currently visible.
|
|
333
613
|
if (this._balloon.visibleView === this.formView) {
|
|
@@ -335,18 +615,55 @@ export default class LinkUI extends Plugin {
|
|
|
335
615
|
}
|
|
336
616
|
this.formView.enableCssTransitions();
|
|
337
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Adds the {@link #propertiesView} to the {@link #_balloon}.
|
|
620
|
+
*/
|
|
621
|
+
_addPropertiesView() {
|
|
622
|
+
if (!this.propertiesView) {
|
|
623
|
+
this._createViews();
|
|
624
|
+
}
|
|
625
|
+
if (this._arePropertiesInPanel) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
this.propertiesView.disableCssTransitions();
|
|
629
|
+
this._balloon.add({
|
|
630
|
+
view: this.propertiesView,
|
|
631
|
+
position: this._getBalloonPositionData()
|
|
632
|
+
});
|
|
633
|
+
this.propertiesView.enableCssTransitions();
|
|
634
|
+
this.propertiesView.focus();
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Shows the view with links provided by the given provider.
|
|
638
|
+
*/
|
|
639
|
+
_showLinksProviderView(provider) {
|
|
640
|
+
if (this.linkProviderItemsView) {
|
|
641
|
+
this._removeLinksProviderView();
|
|
642
|
+
}
|
|
643
|
+
this.linkProviderItemsView = this._createLinkProviderItemsView(provider);
|
|
644
|
+
this._addLinkProviderItemsView(provider);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Adds the {@link #linkProviderItemsView} to the {@link #_balloon}.
|
|
648
|
+
*/
|
|
649
|
+
_addLinkProviderItemsView(provider) {
|
|
650
|
+
// Clear the collection of links.
|
|
651
|
+
this.linkProviderItemsView.listChildren.clear();
|
|
652
|
+
// Add links to the collection.
|
|
653
|
+
this.linkProviderItemsView.listChildren.addMany(this._createLinkProviderListView(provider));
|
|
654
|
+
this._balloon.add({
|
|
655
|
+
view: this.linkProviderItemsView,
|
|
656
|
+
position: this._getBalloonPositionData()
|
|
657
|
+
});
|
|
658
|
+
this.linkProviderItemsView.focus();
|
|
659
|
+
}
|
|
338
660
|
/**
|
|
339
661
|
* Closes the form view. Decides whether the balloon should be hidden completely or if the action view should be shown. This is
|
|
340
662
|
* decided upon the link command value (which has a value if the document selection is in the link).
|
|
341
|
-
*
|
|
342
|
-
* Additionally, if any {@link module:link/linkconfig~LinkConfig#decorators} are defined in the editor configuration, the state of
|
|
343
|
-
* switch buttons responsible for manual decorator handling is restored.
|
|
344
663
|
*/
|
|
345
664
|
_closeFormView() {
|
|
346
665
|
const linkCommand = this.editor.commands.get('link');
|
|
347
|
-
|
|
348
|
-
// when the user cancels the editing form.
|
|
349
|
-
linkCommand.restoreManualDecoratorStates();
|
|
666
|
+
this.selectedLinkableText = undefined;
|
|
350
667
|
if (linkCommand.value !== undefined) {
|
|
351
668
|
this._removeFormView();
|
|
352
669
|
}
|
|
@@ -354,6 +671,22 @@ export default class LinkUI extends Plugin {
|
|
|
354
671
|
this._hideUI();
|
|
355
672
|
}
|
|
356
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Removes the {@link #propertiesView} from the {@link #_balloon}.
|
|
676
|
+
*/
|
|
677
|
+
_removePropertiesView() {
|
|
678
|
+
if (this._arePropertiesInPanel) {
|
|
679
|
+
this._balloon.remove(this.propertiesView);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Removes the {@link #linkProviderItemsView} from the {@link #_balloon}.
|
|
684
|
+
*/
|
|
685
|
+
_removeLinksProviderView() {
|
|
686
|
+
if (this._isLinksListInPanel) {
|
|
687
|
+
this._balloon.remove(this.linkProviderItemsView);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
357
690
|
/**
|
|
358
691
|
* Removes the {@link #formView} from the {@link #_balloon}.
|
|
359
692
|
*/
|
|
@@ -362,7 +695,8 @@ export default class LinkUI extends Plugin {
|
|
|
362
695
|
// Blur the input element before removing it from DOM to prevent issues in some browsers.
|
|
363
696
|
// See https://github.com/ckeditor/ckeditor5/issues/1501.
|
|
364
697
|
this.formView.saveButtonView.focus();
|
|
365
|
-
// Reset
|
|
698
|
+
// Reset fields to update the state of the submit button.
|
|
699
|
+
this.formView.displayedTextInputView.fieldView.reset();
|
|
366
700
|
this.formView.urlInputView.fieldView.reset();
|
|
367
701
|
this._balloon.remove(this.formView);
|
|
368
702
|
// Because the form has an input which has focus, the focus must be brought back
|
|
@@ -372,7 +706,7 @@ export default class LinkUI extends Plugin {
|
|
|
372
706
|
}
|
|
373
707
|
}
|
|
374
708
|
/**
|
|
375
|
-
* Shows the correct UI type. It is either {@link #formView} or {@link #
|
|
709
|
+
* Shows the correct UI type. It is either {@link #formView} or {@link #toolbarView}.
|
|
376
710
|
*
|
|
377
711
|
* @internal
|
|
378
712
|
*/
|
|
@@ -385,7 +719,7 @@ export default class LinkUI extends Plugin {
|
|
|
385
719
|
// Show visual selection on a text without a link when the contextual balloon is displayed.
|
|
386
720
|
// See https://github.com/ckeditor/ckeditor5/issues/4721.
|
|
387
721
|
this._showFakeVisualSelection();
|
|
388
|
-
this.
|
|
722
|
+
this._addToolbarView();
|
|
389
723
|
// Be sure panel with link is visible.
|
|
390
724
|
if (forceVisible) {
|
|
391
725
|
this._balloon.showStack('main');
|
|
@@ -394,13 +728,13 @@ export default class LinkUI extends Plugin {
|
|
|
394
728
|
}
|
|
395
729
|
// If there's a link under the selection...
|
|
396
730
|
else {
|
|
397
|
-
// Go to the editing UI if
|
|
398
|
-
if (this.
|
|
731
|
+
// Go to the editing UI if toolbar is already visible.
|
|
732
|
+
if (this._isToolbarVisible) {
|
|
399
733
|
this._addFormView();
|
|
400
734
|
}
|
|
401
|
-
// Otherwise display just the
|
|
735
|
+
// Otherwise display just the toolbar.
|
|
402
736
|
else {
|
|
403
|
-
this.
|
|
737
|
+
this._addToolbarView();
|
|
404
738
|
}
|
|
405
739
|
// Be sure panel with link is visible.
|
|
406
740
|
if (forceVisible) {
|
|
@@ -413,22 +747,30 @@ export default class LinkUI extends Plugin {
|
|
|
413
747
|
/**
|
|
414
748
|
* Removes the {@link #formView} from the {@link #_balloon}.
|
|
415
749
|
*
|
|
416
|
-
* See {@link #_addFormView}, {@link #
|
|
750
|
+
* See {@link #_addFormView}, {@link #_addToolbarView}.
|
|
417
751
|
*/
|
|
418
|
-
_hideUI() {
|
|
752
|
+
_hideUI(updateFocus = true) {
|
|
753
|
+
const editor = this.editor;
|
|
419
754
|
if (!this._isUIInPanel) {
|
|
420
755
|
return;
|
|
421
756
|
}
|
|
422
|
-
const editor = this.editor;
|
|
423
757
|
this.stopListening(editor.ui, 'update');
|
|
424
758
|
this.stopListening(this._balloon, 'change:visibleView');
|
|
425
759
|
// Make sure the focus always gets back to the editable _before_ removing the focused form view.
|
|
426
760
|
// Doing otherwise causes issues in some browsers. See https://github.com/ckeditor/ckeditor5-link/issues/193.
|
|
427
|
-
|
|
428
|
-
|
|
761
|
+
if (updateFocus) {
|
|
762
|
+
editor.editing.view.focus();
|
|
763
|
+
}
|
|
764
|
+
// If the links view is visible, remove it because it can be on top of the stack.
|
|
765
|
+
this._removeLinksProviderView();
|
|
766
|
+
// If the properties form view is visible, remove it because it can be on top of the stack.
|
|
767
|
+
this._removePropertiesView();
|
|
768
|
+
// Then remove the form view because it's beneath the properties form.
|
|
429
769
|
this._removeFormView();
|
|
430
|
-
//
|
|
431
|
-
|
|
770
|
+
// Finally, remove the link toolbar view because it's last in the stack.
|
|
771
|
+
if (this._isToolbarInPanel) {
|
|
772
|
+
this._balloon.remove(this.toolbarView);
|
|
773
|
+
}
|
|
432
774
|
this._hideFakeVisualSelection();
|
|
433
775
|
}
|
|
434
776
|
/**
|
|
@@ -451,7 +793,7 @@ export default class LinkUI extends Plugin {
|
|
|
451
793
|
// of the link,
|
|
452
794
|
// * the selection went to a different parent when creating a NEW link. E.g. someone
|
|
453
795
|
// else modified the document.
|
|
454
|
-
// * the selection has expanded (e.g. displaying link
|
|
796
|
+
// * the selection has expanded (e.g. displaying link toolbar then pressing SHIFT+Right arrow).
|
|
455
797
|
//
|
|
456
798
|
// Note: #_getSelectedLinkElement will return a link for a non-collapsed selection only
|
|
457
799
|
// when fully selected.
|
|
@@ -480,6 +822,18 @@ export default class LinkUI extends Plugin {
|
|
|
480
822
|
this.listenTo(editor.ui, 'update', update);
|
|
481
823
|
this.listenTo(this._balloon, 'change:visibleView', update);
|
|
482
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* Returns `true` when {@link #propertiesView} is in the {@link #_balloon}.
|
|
827
|
+
*/
|
|
828
|
+
get _arePropertiesInPanel() {
|
|
829
|
+
return !!this.propertiesView && this._balloon.hasView(this.propertiesView);
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Returns `true` when {@link #linkProviderItemsView} is in the {@link #_balloon}.
|
|
833
|
+
*/
|
|
834
|
+
get _isLinksListInPanel() {
|
|
835
|
+
return !!this.linkProviderItemsView && this._balloon.hasView(this.linkProviderItemsView);
|
|
836
|
+
}
|
|
483
837
|
/**
|
|
484
838
|
* Returns `true` when {@link #formView} is in the {@link #_balloon}.
|
|
485
839
|
*/
|
|
@@ -487,31 +841,45 @@ export default class LinkUI extends Plugin {
|
|
|
487
841
|
return !!this.formView && this._balloon.hasView(this.formView);
|
|
488
842
|
}
|
|
489
843
|
/**
|
|
490
|
-
* Returns `true` when {@link #
|
|
844
|
+
* Returns `true` when {@link #toolbarView} is in the {@link #_balloon}.
|
|
491
845
|
*/
|
|
492
|
-
get
|
|
493
|
-
return !!this.
|
|
846
|
+
get _isToolbarInPanel() {
|
|
847
|
+
return !!this.toolbarView && this._balloon.hasView(this.toolbarView);
|
|
494
848
|
}
|
|
495
849
|
/**
|
|
496
|
-
* Returns `true` when {@link #
|
|
850
|
+
* Returns `true` when {@link #propertiesView} is in the {@link #_balloon} and it is
|
|
497
851
|
* currently visible.
|
|
498
852
|
*/
|
|
499
|
-
get
|
|
500
|
-
return !!this.
|
|
853
|
+
get _isPropertiesVisible() {
|
|
854
|
+
return !!this.propertiesView && this._balloon.visibleView === this.propertiesView;
|
|
501
855
|
}
|
|
502
856
|
/**
|
|
503
|
-
* Returns `true` when {@link #
|
|
857
|
+
* Returns `true` when {@link #formView} is in the {@link #_balloon} and it is
|
|
858
|
+
* currently visible.
|
|
504
859
|
*/
|
|
505
|
-
get
|
|
506
|
-
return this.
|
|
860
|
+
get _isFormVisible() {
|
|
861
|
+
return !!this.formView && this._balloon.visibleView == this.formView;
|
|
507
862
|
}
|
|
508
863
|
/**
|
|
509
|
-
* Returns `true` when {@link #
|
|
864
|
+
* Returns `true` when {@link #toolbarView} is in the {@link #_balloon} and it is
|
|
510
865
|
* currently visible.
|
|
511
866
|
*/
|
|
867
|
+
get _isToolbarVisible() {
|
|
868
|
+
return !!this.toolbarView && this._balloon.visibleView === this.toolbarView;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Returns `true` when {@link #propertiesView}, {@link #toolbarView}, {@link #linkProviderItemsView}
|
|
872
|
+
* or {@link #formView} is in the {@link #_balloon}.
|
|
873
|
+
*/
|
|
874
|
+
get _isUIInPanel() {
|
|
875
|
+
return this._arePropertiesInPanel || this._isLinksListInPanel || this._isFormInPanel || this._isToolbarInPanel;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Returns `true` when {@link #propertiesView}, {@link #linkProviderItemsView}, {@link #toolbarView}
|
|
879
|
+
* or {@link #formView} is in the {@link #_balloon} and it is currently visible.
|
|
880
|
+
*/
|
|
512
881
|
get _isUIVisible() {
|
|
513
|
-
|
|
514
|
-
return !!this.formView && visibleView == this.formView || this._areActionsVisible;
|
|
882
|
+
return this._isPropertiesVisible || this._isLinksListInPanel || this._isFormVisible || this._isToolbarVisible;
|
|
515
883
|
}
|
|
516
884
|
/**
|
|
517
885
|
* Returns positioning options for the {@link #_balloon}. They control the way the balloon is attached
|
|
@@ -522,30 +890,34 @@ export default class LinkUI extends Plugin {
|
|
|
522
890
|
*/
|
|
523
891
|
_getBalloonPositionData() {
|
|
524
892
|
const view = this.editor.editing.view;
|
|
525
|
-
const model = this.editor.model;
|
|
526
893
|
const viewDocument = view.document;
|
|
527
|
-
|
|
894
|
+
const model = this.editor.model;
|
|
528
895
|
if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
|
|
529
896
|
// There are cases when we highlight selection using a marker (#7705, #4721).
|
|
530
|
-
const markerViewElements =
|
|
531
|
-
|
|
532
|
-
|
|
897
|
+
const markerViewElements = this.editor.editing.mapper.markerNameToElements(VISUAL_SELECTION_MARKER_NAME);
|
|
898
|
+
// Marker could be removed by link text override and end up in the graveyard.
|
|
899
|
+
if (markerViewElements) {
|
|
900
|
+
const markerViewElementsArray = Array.from(markerViewElements);
|
|
901
|
+
const newRange = view.createRange(view.createPositionBefore(markerViewElementsArray[0]), view.createPositionAfter(markerViewElementsArray[markerViewElementsArray.length - 1]));
|
|
902
|
+
return {
|
|
903
|
+
target: view.domConverter.viewRangeToDom(newRange)
|
|
904
|
+
};
|
|
905
|
+
}
|
|
533
906
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
target
|
|
907
|
+
// Make sure the target is calculated on demand at the last moment because a cached DOM range
|
|
908
|
+
// (which is very fragile) can desynchronize with the state of the editing view if there was
|
|
909
|
+
// any rendering done in the meantime. This can happen, for instance, when an inline widget
|
|
910
|
+
// gets unlinked.
|
|
911
|
+
return {
|
|
912
|
+
target: () => {
|
|
540
913
|
const targetLink = this._getSelectedLinkElement();
|
|
541
914
|
return targetLink ?
|
|
542
915
|
// When selection is inside link element, then attach panel to this element.
|
|
543
916
|
view.domConverter.mapViewToDom(targetLink) :
|
|
544
917
|
// Otherwise attach panel to the selection.
|
|
545
918
|
view.domConverter.viewRangeToDom(viewDocument.selection.getFirstRange());
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return { target };
|
|
919
|
+
}
|
|
920
|
+
};
|
|
549
921
|
}
|
|
550
922
|
/**
|
|
551
923
|
* Returns the link {@link module:engine/view/attributeelement~AttributeElement} under
|
|
@@ -582,6 +954,42 @@ export default class LinkUI extends Plugin {
|
|
|
582
954
|
}
|
|
583
955
|
}
|
|
584
956
|
}
|
|
957
|
+
/**
|
|
958
|
+
* Returns selected link text content.
|
|
959
|
+
* If link is not selected it returns the selected text.
|
|
960
|
+
* If selection or link includes non text node (inline object or block) then returns undefined.
|
|
961
|
+
*/
|
|
962
|
+
_getSelectedLinkableText() {
|
|
963
|
+
const model = this.editor.model;
|
|
964
|
+
const editing = this.editor.editing;
|
|
965
|
+
const selectedLink = this._getSelectedLinkElement();
|
|
966
|
+
if (!selectedLink) {
|
|
967
|
+
return extractTextFromLinkRange(model.document.selection.getFirstRange());
|
|
968
|
+
}
|
|
969
|
+
const viewLinkRange = editing.view.createRangeOn(selectedLink);
|
|
970
|
+
const linkRange = editing.mapper.toModelRange(viewLinkRange);
|
|
971
|
+
return extractTextFromLinkRange(linkRange);
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Returns a provider by its URL.
|
|
975
|
+
*
|
|
976
|
+
* @param href URL of the link.
|
|
977
|
+
* @returns Link provider and item or `null` if not found.
|
|
978
|
+
*/
|
|
979
|
+
_getLinkProviderLinkByHref(href) {
|
|
980
|
+
if (!href) {
|
|
981
|
+
return null;
|
|
982
|
+
}
|
|
983
|
+
for (const provider of this._linksProviders) {
|
|
984
|
+
const item = provider.getItem ?
|
|
985
|
+
provider.getItem(href) :
|
|
986
|
+
provider.getListItems().find(item => item.href === href);
|
|
987
|
+
if (item) {
|
|
988
|
+
return { provider, item };
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
585
993
|
/**
|
|
586
994
|
* Displays a fake visual selection when the contextual balloon is displayed.
|
|
587
995
|
*
|