@ckeditor/ckeditor5-link 47.6.1-alpha.1 → 48.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/ckeditor5-metadata.json +6 -6
- package/{src → dist}/autolink.d.ts +2 -2
- package/dist/index-editor.css +181 -105
- package/dist/index.css +169 -151
- package/dist/index.css.map +1 -1
- package/{src → dist}/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/{src → dist}/link.d.ts +1 -1
- package/{src → dist}/linkcommand.d.ts +2 -2
- package/{src → dist}/linkconfig.d.ts +1 -1
- package/{src → dist}/linkediting.d.ts +3 -3
- package/{src → dist}/linkimage.d.ts +1 -1
- package/{src → dist}/linkimageediting.d.ts +3 -2
- package/{src → dist}/linkimageui.d.ts +3 -2
- package/{src → dist}/linkui.d.ts +2 -2
- package/{src → dist}/ui/linkbuttonview.d.ts +2 -2
- package/{src → dist}/ui/linkformview.d.ts +2 -4
- package/{src → dist}/ui/linkpreviewbuttonview.d.ts +2 -2
- package/{src → dist}/ui/linkpropertiesview.d.ts +2 -2
- package/{src → dist}/ui/linkprovideritemsview.d.ts +2 -2
- package/{src → dist}/unlinkcommand.d.ts +1 -1
- package/{src → dist}/utils/automaticdecorators.d.ts +2 -2
- package/{src → dist}/utils/manualdecorator.d.ts +4 -4
- package/{src → dist}/utils.d.ts +2 -2
- package/package.json +29 -52
- package/build/link.js +0 -5
- package/build/translations/af.js +0 -1
- package/build/translations/ar.js +0 -1
- package/build/translations/ast.js +0 -1
- package/build/translations/az.js +0 -1
- package/build/translations/be.js +0 -1
- package/build/translations/bg.js +0 -1
- package/build/translations/bn.js +0 -1
- package/build/translations/bs.js +0 -1
- package/build/translations/ca.js +0 -1
- package/build/translations/cs.js +0 -1
- package/build/translations/da.js +0 -1
- package/build/translations/de-ch.js +0 -1
- package/build/translations/de.js +0 -1
- package/build/translations/el.js +0 -1
- package/build/translations/en-au.js +0 -1
- package/build/translations/en-gb.js +0 -1
- package/build/translations/eo.js +0 -1
- package/build/translations/es-co.js +0 -1
- package/build/translations/es.js +0 -1
- package/build/translations/et.js +0 -1
- package/build/translations/eu.js +0 -1
- package/build/translations/fa.js +0 -1
- package/build/translations/fi.js +0 -1
- package/build/translations/fr.js +0 -1
- package/build/translations/gl.js +0 -1
- package/build/translations/gu.js +0 -1
- package/build/translations/he.js +0 -1
- package/build/translations/hi.js +0 -1
- package/build/translations/hr.js +0 -1
- package/build/translations/hu.js +0 -1
- package/build/translations/hy.js +0 -1
- package/build/translations/id.js +0 -1
- package/build/translations/it.js +0 -1
- package/build/translations/ja.js +0 -1
- package/build/translations/jv.js +0 -1
- package/build/translations/kk.js +0 -1
- package/build/translations/km.js +0 -1
- package/build/translations/kn.js +0 -1
- package/build/translations/ko.js +0 -1
- package/build/translations/ku.js +0 -1
- package/build/translations/lt.js +0 -1
- package/build/translations/lv.js +0 -1
- package/build/translations/ms.js +0 -1
- package/build/translations/nb.js +0 -1
- package/build/translations/ne.js +0 -1
- package/build/translations/nl.js +0 -1
- package/build/translations/no.js +0 -1
- package/build/translations/oc.js +0 -1
- package/build/translations/pl.js +0 -1
- package/build/translations/pt-br.js +0 -1
- package/build/translations/pt.js +0 -1
- package/build/translations/ro.js +0 -1
- package/build/translations/ru.js +0 -1
- package/build/translations/si.js +0 -1
- package/build/translations/sk.js +0 -1
- package/build/translations/sl.js +0 -1
- package/build/translations/sq.js +0 -1
- package/build/translations/sr-latn.js +0 -1
- package/build/translations/sr.js +0 -1
- package/build/translations/sv.js +0 -1
- package/build/translations/th.js +0 -1
- package/build/translations/ti.js +0 -1
- package/build/translations/tk.js +0 -1
- package/build/translations/tr.js +0 -1
- package/build/translations/tt.js +0 -1
- package/build/translations/ug.js +0 -1
- package/build/translations/uk.js +0 -1
- package/build/translations/ur.js +0 -1
- package/build/translations/uz.js +0 -1
- package/build/translations/vi.js +0 -1
- package/build/translations/zh-cn.js +0 -1
- package/build/translations/zh.js +0 -1
- package/lang/contexts.json +0 -16
- package/lang/translations/af.po +0 -68
- package/lang/translations/ar.po +0 -68
- package/lang/translations/ast.po +0 -68
- package/lang/translations/az.po +0 -68
- package/lang/translations/be.po +0 -68
- package/lang/translations/bg.po +0 -68
- package/lang/translations/bn.po +0 -70
- package/lang/translations/bs.po +0 -68
- package/lang/translations/ca.po +0 -68
- package/lang/translations/cs.po +0 -68
- package/lang/translations/da.po +0 -68
- package/lang/translations/de-ch.po +0 -68
- package/lang/translations/de.po +0 -68
- package/lang/translations/el.po +0 -68
- package/lang/translations/en-au.po +0 -68
- package/lang/translations/en-gb.po +0 -68
- package/lang/translations/en.po +0 -68
- package/lang/translations/eo.po +0 -68
- package/lang/translations/es-co.po +0 -68
- package/lang/translations/es.po +0 -68
- package/lang/translations/et.po +0 -68
- package/lang/translations/eu.po +0 -68
- package/lang/translations/fa.po +0 -68
- package/lang/translations/fi.po +0 -68
- package/lang/translations/fr.po +0 -68
- package/lang/translations/gl.po +0 -68
- package/lang/translations/gu.po +0 -68
- package/lang/translations/he.po +0 -68
- package/lang/translations/hi.po +0 -68
- package/lang/translations/hr.po +0 -68
- package/lang/translations/hu.po +0 -68
- package/lang/translations/hy.po +0 -68
- package/lang/translations/id.po +0 -68
- package/lang/translations/it.po +0 -68
- package/lang/translations/ja.po +0 -68
- package/lang/translations/jv.po +0 -68
- package/lang/translations/kk.po +0 -68
- package/lang/translations/km.po +0 -68
- package/lang/translations/kn.po +0 -68
- package/lang/translations/ko.po +0 -68
- package/lang/translations/ku.po +0 -68
- package/lang/translations/lt.po +0 -68
- package/lang/translations/lv.po +0 -68
- package/lang/translations/ms.po +0 -68
- package/lang/translations/nb.po +0 -68
- package/lang/translations/ne.po +0 -68
- package/lang/translations/nl.po +0 -68
- package/lang/translations/no.po +0 -68
- package/lang/translations/oc.po +0 -68
- package/lang/translations/pl.po +0 -68
- package/lang/translations/pt-br.po +0 -68
- package/lang/translations/pt.po +0 -68
- package/lang/translations/ro.po +0 -68
- package/lang/translations/ru.po +0 -68
- package/lang/translations/si.po +0 -68
- package/lang/translations/sk.po +0 -68
- package/lang/translations/sl.po +0 -68
- package/lang/translations/sq.po +0 -68
- package/lang/translations/sr-latn.po +0 -68
- package/lang/translations/sr.po +0 -68
- package/lang/translations/sv.po +0 -68
- package/lang/translations/th.po +0 -68
- package/lang/translations/ti.po +0 -68
- package/lang/translations/tk.po +0 -68
- package/lang/translations/tr.po +0 -68
- package/lang/translations/tt.po +0 -68
- package/lang/translations/ug.po +0 -68
- package/lang/translations/uk.po +0 -68
- package/lang/translations/ur.po +0 -68
- package/lang/translations/uz.po +0 -68
- package/lang/translations/vi.po +0 -68
- package/lang/translations/zh-cn.po +0 -68
- package/lang/translations/zh.po +0 -68
- package/src/augmentation.js +0 -5
- package/src/autolink.js +0 -307
- package/src/index.js +0 -25
- package/src/link.js +0 -37
- package/src/linkcommand.js +0 -431
- package/src/linkconfig.js +0 -5
- package/src/linkediting.js +0 -402
- package/src/linkimage.js +0 -37
- package/src/linkimageediting.js +0 -264
- package/src/linkimageui.js +0 -102
- package/src/linkui.js +0 -1072
- package/src/ui/linkbuttonview.js +0 -54
- package/src/ui/linkformview.js +0 -302
- package/src/ui/linkpreviewbuttonview.js +0 -43
- package/src/ui/linkpropertiesview.js +0 -170
- package/src/ui/linkprovideritemsview.js +0 -207
- package/src/unlinkcommand.js +0 -66
- package/src/utils/automaticdecorators.js +0 -181
- package/src/utils/conflictingdecorators.js +0 -80
- package/src/utils/manualdecorator.js +0 -69
- package/src/utils.js +0 -157
- package/theme/link.css +0 -10
- package/theme/linkform.css +0 -24
- package/theme/linkimage.css +0 -16
- package/theme/linkproperties.css +0 -4
- package/theme/linkprovideritems.css +0 -18
- package/theme/linktoolbar.css +0 -12
- /package/{src → dist}/augmentation.d.ts +0 -0
- /package/{src → dist}/utils/conflictingdecorators.d.ts +0 -0
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module link/ui/linkprovideritemsview
|
|
7
|
-
*/
|
|
8
|
-
import { ButtonView, FocusCycler, FormHeaderView, View, ListView, ListItemView, ViewCollection } from 'ckeditor5/src/ui.js';
|
|
9
|
-
import { FocusTracker, KeystrokeHandler } from 'ckeditor5/src/utils.js';
|
|
10
|
-
import { IconPreviousArrow } from '@ckeditor/ckeditor5-icons';
|
|
11
|
-
import '../../theme/linkprovideritems.css';
|
|
12
|
-
/**
|
|
13
|
-
* The link provider items view.
|
|
14
|
-
*/
|
|
15
|
-
export class LinkProviderItemsView extends View {
|
|
16
|
-
/**
|
|
17
|
-
* Tracks information about DOM focus in the form.
|
|
18
|
-
*/
|
|
19
|
-
focusTracker = new FocusTracker();
|
|
20
|
-
/**
|
|
21
|
-
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
|
|
22
|
-
*/
|
|
23
|
-
keystrokes = new KeystrokeHandler();
|
|
24
|
-
/**
|
|
25
|
-
* The Back button view displayed in the header.
|
|
26
|
-
*/
|
|
27
|
-
backButtonView;
|
|
28
|
-
/**
|
|
29
|
-
* The List view of links buttons.
|
|
30
|
-
*/
|
|
31
|
-
listView;
|
|
32
|
-
/**
|
|
33
|
-
* The collection of child views, which is bind with the `listView`.
|
|
34
|
-
*/
|
|
35
|
-
listChildren;
|
|
36
|
-
/**
|
|
37
|
-
* The view displayed when the list is empty.
|
|
38
|
-
*/
|
|
39
|
-
emptyListInformation;
|
|
40
|
-
/**
|
|
41
|
-
* A collection of child views.
|
|
42
|
-
*/
|
|
43
|
-
children;
|
|
44
|
-
/**
|
|
45
|
-
* A collection of views that can be focused in the form.
|
|
46
|
-
*/
|
|
47
|
-
_focusables = new ViewCollection();
|
|
48
|
-
/**
|
|
49
|
-
* Helps cycling over {@link #_focusables} in the form.
|
|
50
|
-
*/
|
|
51
|
-
_focusCycler;
|
|
52
|
-
/**
|
|
53
|
-
* Creates an instance of the {@link module:link/ui/linkprovideritemsview~LinkProviderItemsView} class.
|
|
54
|
-
*
|
|
55
|
-
* Also see {@link #render}.
|
|
56
|
-
*
|
|
57
|
-
* @param locale The localization services instance.
|
|
58
|
-
*/
|
|
59
|
-
constructor(locale) {
|
|
60
|
-
super(locale);
|
|
61
|
-
this.listChildren = this.createCollection();
|
|
62
|
-
this.backButtonView = this._createBackButton();
|
|
63
|
-
this.listView = this._createListView();
|
|
64
|
-
this.emptyListInformation = this._createEmptyLinksListItemView();
|
|
65
|
-
this.children = this.createCollection([
|
|
66
|
-
this._createHeaderView(),
|
|
67
|
-
this.emptyListInformation
|
|
68
|
-
]);
|
|
69
|
-
this.set('title', '');
|
|
70
|
-
this.set('emptyListPlaceholder', '');
|
|
71
|
-
this.set('hasItems', false);
|
|
72
|
-
this.listenTo(this.listChildren, 'change', () => {
|
|
73
|
-
this.hasItems = this.listChildren.length > 0;
|
|
74
|
-
});
|
|
75
|
-
this.on('change:hasItems', (evt, propName, hasItems) => {
|
|
76
|
-
if (hasItems) {
|
|
77
|
-
this.children.remove(this.emptyListInformation);
|
|
78
|
-
this.children.add(this.listView);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
this.children.remove(this.listView);
|
|
82
|
-
this.children.add(this.emptyListInformation);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
// Close the panel on esc key press when the **form has focus**.
|
|
86
|
-
this.keystrokes.set('Esc', (data, cancel) => {
|
|
87
|
-
this.fire('cancel');
|
|
88
|
-
cancel();
|
|
89
|
-
});
|
|
90
|
-
this._focusCycler = new FocusCycler({
|
|
91
|
-
focusables: this._focusables,
|
|
92
|
-
focusTracker: this.focusTracker,
|
|
93
|
-
keystrokeHandler: this.keystrokes,
|
|
94
|
-
actions: {
|
|
95
|
-
// Navigate form fields backwards using the Shift + Tab keystroke.
|
|
96
|
-
focusPrevious: 'shift + tab',
|
|
97
|
-
// Navigate form fields forwards using the Tab key.
|
|
98
|
-
focusNext: 'tab'
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
this.setTemplate({
|
|
102
|
-
tag: 'div',
|
|
103
|
-
attributes: {
|
|
104
|
-
class: [
|
|
105
|
-
'ck',
|
|
106
|
-
'ck-link-providers'
|
|
107
|
-
],
|
|
108
|
-
// https://github.com/ckeditor/ckeditor5-link/issues/90
|
|
109
|
-
tabindex: '-1'
|
|
110
|
-
},
|
|
111
|
-
children: this.children
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* @inheritDoc
|
|
116
|
-
*/
|
|
117
|
-
render() {
|
|
118
|
-
super.render();
|
|
119
|
-
const childViews = [
|
|
120
|
-
this.listView,
|
|
121
|
-
this.backButtonView
|
|
122
|
-
];
|
|
123
|
-
childViews.forEach(v => {
|
|
124
|
-
// Register the view as focusable.
|
|
125
|
-
this._focusables.add(v);
|
|
126
|
-
// Register the view in the focus tracker.
|
|
127
|
-
this.focusTracker.add(v.element);
|
|
128
|
-
});
|
|
129
|
-
// Start listening for the keystrokes coming from #element.
|
|
130
|
-
this.keystrokes.listenTo(this.element);
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* @inheritDoc
|
|
134
|
-
*/
|
|
135
|
-
destroy() {
|
|
136
|
-
super.destroy();
|
|
137
|
-
this.focusTracker.destroy();
|
|
138
|
-
this.keystrokes.destroy();
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Focuses the fist {@link #_focusables} in the form.
|
|
142
|
-
*/
|
|
143
|
-
focus() {
|
|
144
|
-
this._focusCycler.focusFirst();
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Creates a view for the list at the bottom.
|
|
148
|
-
*/
|
|
149
|
-
_createListView() {
|
|
150
|
-
const listView = new ListView(this.locale);
|
|
151
|
-
listView.extendTemplate({
|
|
152
|
-
attributes: {
|
|
153
|
-
class: [
|
|
154
|
-
'ck-link-providers__list'
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
listView.items.bindTo(this.listChildren).using(button => {
|
|
159
|
-
const listItemView = new ListItemView(this.locale);
|
|
160
|
-
listItemView.children.add(button);
|
|
161
|
-
return listItemView;
|
|
162
|
-
});
|
|
163
|
-
return listView;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Creates a back button view that cancels the form.
|
|
167
|
-
*/
|
|
168
|
-
_createBackButton() {
|
|
169
|
-
const t = this.locale.t;
|
|
170
|
-
const backButton = new ButtonView(this.locale);
|
|
171
|
-
backButton.set({
|
|
172
|
-
class: 'ck-button-back',
|
|
173
|
-
label: t('Back'),
|
|
174
|
-
icon: IconPreviousArrow,
|
|
175
|
-
tooltip: true
|
|
176
|
-
});
|
|
177
|
-
backButton.delegate('execute').to(this, 'cancel');
|
|
178
|
-
return backButton;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Creates a header view for the form.
|
|
182
|
-
*/
|
|
183
|
-
_createHeaderView() {
|
|
184
|
-
const header = new FormHeaderView(this.locale);
|
|
185
|
-
header.bind('label').to(this, 'title');
|
|
186
|
-
header.children.add(this.backButtonView, 0);
|
|
187
|
-
return header;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Creates an info view for an empty list.
|
|
191
|
-
*/
|
|
192
|
-
_createEmptyLinksListItemView() {
|
|
193
|
-
const view = new View(this.locale);
|
|
194
|
-
view.setTemplate({
|
|
195
|
-
tag: 'p',
|
|
196
|
-
attributes: {
|
|
197
|
-
class: ['ck', 'ck-link__empty-list-info']
|
|
198
|
-
},
|
|
199
|
-
children: [
|
|
200
|
-
{
|
|
201
|
-
text: this.bindTemplate.to('emptyListPlaceholder')
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
});
|
|
205
|
-
return view;
|
|
206
|
-
}
|
|
207
|
-
}
|
package/src/unlinkcommand.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module link/unlinkcommand
|
|
7
|
-
*/
|
|
8
|
-
import { Command } from 'ckeditor5/src/core.js';
|
|
9
|
-
import { findAttributeRange } from 'ckeditor5/src/typing.js';
|
|
10
|
-
import { isLinkableElement } from './utils.js';
|
|
11
|
-
/**
|
|
12
|
-
* The unlink command. It is used by the {@link module:link/link~Link link plugin}.
|
|
13
|
-
*/
|
|
14
|
-
export class UnlinkCommand extends Command {
|
|
15
|
-
/**
|
|
16
|
-
* @inheritDoc
|
|
17
|
-
*/
|
|
18
|
-
refresh() {
|
|
19
|
-
const model = this.editor.model;
|
|
20
|
-
const selection = model.document.selection;
|
|
21
|
-
const selectedElement = selection.getSelectedElement();
|
|
22
|
-
// A check for any integration that allows linking elements (e.g. `LinkImage`).
|
|
23
|
-
// Currently the selection reads attributes from text nodes only. See #7429 and #7465.
|
|
24
|
-
if (isLinkableElement(selectedElement, model.schema)) {
|
|
25
|
-
this.isEnabled = model.schema.checkAttribute(selectedElement, 'linkHref');
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
this.isEnabled = model.schema.checkAttributeInSelection(selection, 'linkHref');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Executes the command.
|
|
33
|
-
*
|
|
34
|
-
* When the selection is collapsed, it removes the `linkHref` attribute from each node with the same `linkHref` attribute value.
|
|
35
|
-
* When the selection is non-collapsed, it removes the `linkHref` attribute from each node in selected ranges.
|
|
36
|
-
*
|
|
37
|
-
* # Decorators
|
|
38
|
-
*
|
|
39
|
-
* If {@link module:link/linkconfig~LinkConfig#decorators `config.link.decorators`} is specified,
|
|
40
|
-
* all configured decorators are removed together with the `linkHref` attribute.
|
|
41
|
-
*
|
|
42
|
-
* @fires execute
|
|
43
|
-
*/
|
|
44
|
-
execute() {
|
|
45
|
-
const editor = this.editor;
|
|
46
|
-
const model = this.editor.model;
|
|
47
|
-
const selection = model.document.selection;
|
|
48
|
-
const linkCommand = editor.commands.get('link');
|
|
49
|
-
model.change(writer => {
|
|
50
|
-
// Get ranges to unlink.
|
|
51
|
-
const rangesToUnlink = selection.isCollapsed ?
|
|
52
|
-
[findAttributeRange(selection.getFirstPosition(), 'linkHref', selection.getAttribute('linkHref'), model)] :
|
|
53
|
-
model.schema.getValidRanges(selection.getRanges(), 'linkHref');
|
|
54
|
-
// Remove `linkHref` attribute from specified ranges.
|
|
55
|
-
for (const range of rangesToUnlink) {
|
|
56
|
-
writer.removeAttribute('linkHref', range);
|
|
57
|
-
// If there are registered custom attributes, then remove them during unlink.
|
|
58
|
-
if (linkCommand) {
|
|
59
|
-
for (const manualDecorator of linkCommand.manualDecorators) {
|
|
60
|
-
writer.removeAttribute(manualDecorator.id, range);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module link/utils/automaticdecorators
|
|
7
|
-
*/
|
|
8
|
-
import { toMap, priorities } from 'ckeditor5/src/utils.js';
|
|
9
|
-
/**
|
|
10
|
-
* Helper class that ties together all {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition} and provides
|
|
11
|
-
* the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement downcast dispatchers} for them.
|
|
12
|
-
*/
|
|
13
|
-
export class AutomaticLinkDecorators {
|
|
14
|
-
/**
|
|
15
|
-
* Stores the definition of {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators}.
|
|
16
|
-
* This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
|
|
17
|
-
*/
|
|
18
|
-
_definitions = new Set();
|
|
19
|
-
/**
|
|
20
|
-
* A callback that checks if a decorator can be applied to a given element.
|
|
21
|
-
* Returns `true` if there is a conflict preventing the decorator from being applied.
|
|
22
|
-
*/
|
|
23
|
-
_conflictChecker;
|
|
24
|
-
/**
|
|
25
|
-
* Gives information about the number of decorators stored in the {@link module:link/utils/automaticdecorators~AutomaticLinkDecorators}
|
|
26
|
-
* instance.
|
|
27
|
-
*/
|
|
28
|
-
get length() {
|
|
29
|
-
return this._definitions.size;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Sets a callback that checks if a decorator can be applied to a given element.
|
|
33
|
-
*
|
|
34
|
-
* @param checker A function that returns `true` if there is a conflict preventing the decorator from being applied.
|
|
35
|
-
*/
|
|
36
|
-
setConflictChecker(checker) {
|
|
37
|
-
this._conflictChecker = checker;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Adds automatic decorator objects or an array with them to be used during downcasting.
|
|
41
|
-
*
|
|
42
|
-
* @param item A configuration object of automatic rules for decorating links. It might also be an array of such objects.
|
|
43
|
-
*/
|
|
44
|
-
add(item) {
|
|
45
|
-
if (Array.isArray(item)) {
|
|
46
|
-
item.forEach(item => this._definitions.add(item));
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
this._definitions.add(item);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
|
|
54
|
-
*
|
|
55
|
-
* @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
|
|
56
|
-
*/
|
|
57
|
-
getDispatcher() {
|
|
58
|
-
return dispatcher => {
|
|
59
|
-
const elementCreator = (item, viewWriter) => {
|
|
60
|
-
const viewElement = viewWriter.createAttributeElement('a', item.attributes, {
|
|
61
|
-
priority: 5
|
|
62
|
-
});
|
|
63
|
-
if (item.classes) {
|
|
64
|
-
viewWriter.addClass(item.classes, viewElement);
|
|
65
|
-
}
|
|
66
|
-
for (const key in item.styles) {
|
|
67
|
-
viewWriter.setStyle(key, item.styles[key], viewElement);
|
|
68
|
-
}
|
|
69
|
-
viewWriter.setCustomProperty('link', true, viewElement);
|
|
70
|
-
return viewElement;
|
|
71
|
-
};
|
|
72
|
-
const createConverter = (isApplyingConverter) => {
|
|
73
|
-
return (evt, data, conversionApi) => {
|
|
74
|
-
if (!data.attributeKey.startsWith('link')) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
// There is only test as this behavior decorates links and
|
|
78
|
-
// it is run before dispatcher which actually consumes this node.
|
|
79
|
-
// This allows on writing own dispatcher with highest priority,
|
|
80
|
-
// which blocks both native converter and this additional decoration.
|
|
81
|
-
if (data.attributeKey == 'linkHref' && !conversionApi.consumable.test(data.item, 'attribute:linkHref')) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
// Automatic decorators for block links are handled e.g. in LinkImageEditing.
|
|
85
|
-
if (!data.item.is('selection') && !conversionApi.schema.isInline(data.item)) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
for (const decorator of this._definitions) {
|
|
89
|
-
// Check if automatic decorator is matched and does not conflict with any other active manual decorator.
|
|
90
|
-
if (decorator.callback(data.item.getAttribute('linkHref')) &&
|
|
91
|
-
!this._conflictChecker?.(decorator, data.item) &&
|
|
92
|
-
isApplyingConverter) {
|
|
93
|
-
if (data.item.is('selection')) {
|
|
94
|
-
conversionApi.writer.wrap(conversionApi.writer.document.selection.getFirstRange(), elementCreator(decorator, conversionApi.writer));
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
conversionApi.writer.wrap(conversionApi.mapper.toViewRange(data.range), elementCreator(decorator, conversionApi.writer));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
conversionApi.writer.unwrap(conversionApi.mapper.toViewRange(data.range), elementCreator(decorator, conversionApi.writer));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
dispatcher.on('attribute', createConverter(false), { priority: priorities.high - 1 });
|
|
107
|
-
// Apply decorators after all automatic and manual decorators are removed so removing one decorator
|
|
108
|
-
// won't strip part of the other decorator's attributes, classes or styles.
|
|
109
|
-
dispatcher.on('attribute', createConverter(true), { priority: priorities.high - 2 });
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
|
|
114
|
-
* when linking images.
|
|
115
|
-
*
|
|
116
|
-
* @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
|
|
117
|
-
*/
|
|
118
|
-
getDispatcherForLinkedImage() {
|
|
119
|
-
return dispatcher => {
|
|
120
|
-
const createConverter = (isApplyingConverter) => {
|
|
121
|
-
return (evt, data, { writer, mapper }) => {
|
|
122
|
-
if (!data.item.is('element', 'imageBlock') || !data.attributeKey.startsWith('link')) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const viewFigure = mapper.toViewElement(data.item);
|
|
126
|
-
const linkInImage = Array.from(viewFigure.getChildren())
|
|
127
|
-
.find((child) => child.is('element', 'a'));
|
|
128
|
-
// It's not guaranteed that the anchor is present in the image block during execution of this dispatcher.
|
|
129
|
-
// It might have been removed during the execution of unlink command that runs the image link downcast dispatcher
|
|
130
|
-
// that is executed before this one and removes the anchor from the image block.
|
|
131
|
-
if (!linkInImage) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
for (const decorator of this._definitions) {
|
|
135
|
-
const attributes = toMap(decorator.attributes);
|
|
136
|
-
if (decorator.callback(data.item.getAttribute('linkHref')) &&
|
|
137
|
-
!this._conflictChecker?.(decorator, data.item) &&
|
|
138
|
-
isApplyingConverter) {
|
|
139
|
-
for (const [key, val] of attributes) {
|
|
140
|
-
// Left for backward compatibility. Since v30 decorator should
|
|
141
|
-
// accept `classes` and `styles` separately from `attributes`.
|
|
142
|
-
if (key === 'class') {
|
|
143
|
-
writer.addClass(val, linkInImage);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
writer.setAttribute(key, val, false, linkInImage);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (decorator.classes) {
|
|
150
|
-
writer.addClass(decorator.classes, linkInImage);
|
|
151
|
-
}
|
|
152
|
-
for (const key in decorator.styles) {
|
|
153
|
-
writer.setStyle(key, decorator.styles[key], linkInImage);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
for (const [key, val] of attributes) {
|
|
158
|
-
if (key === 'class') {
|
|
159
|
-
writer.removeClass(val, linkInImage);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
writer.removeAttribute(key, val, linkInImage);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (decorator.classes) {
|
|
166
|
-
writer.removeClass(decorator.classes, linkInImage);
|
|
167
|
-
}
|
|
168
|
-
for (const key in decorator.styles) {
|
|
169
|
-
writer.removeStyle(key, linkInImage);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
};
|
|
175
|
-
dispatcher.on('attribute', createConverter(false), { priority: priorities.high - 1 });
|
|
176
|
-
// Apply decorators after all automatic and manual decorators are removed so removing one decorator
|
|
177
|
-
// won't strip part of the other decorator's attributes, classes or styles.
|
|
178
|
-
dispatcher.on('attribute', createConverter(true), { priority: priorities.high - 2 });
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module link/utils/conflictingdecorators
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Checks if two decorators conflict with each other.
|
|
10
|
-
*
|
|
11
|
-
* Decorators conflict when they share the same HTML attribute names (excluding mergeable attributes)
|
|
12
|
-
* or style properties.
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
* @param a The first decorator.
|
|
16
|
-
* @param b The second decorator.
|
|
17
|
-
*/
|
|
18
|
-
export function areDecoratorsConflicting(a, b) {
|
|
19
|
-
if (a.attributes && b.attributes) {
|
|
20
|
-
const hasConflict = Object.keys(a.attributes).some(key => !isMergeableAttribute(key) && key in b.attributes);
|
|
21
|
-
if (hasConflict) {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
// Check for conflicting style properties (same CSS property names).
|
|
26
|
-
if (a.styles && b.styles) {
|
|
27
|
-
const hasConflict = Object.keys(a.styles).some(key => key in b.styles);
|
|
28
|
-
if (hasConflict) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// Classes don't conflict with each other - they can be merged.
|
|
33
|
-
return false;
|
|
34
|
-
function isMergeableAttribute(key) {
|
|
35
|
-
return key === 'class' || key === 'style' || key === 'rel';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Resolves conflicting manual decorators by automatically disabling decorators that share
|
|
40
|
-
* the same HTML attributes with newly enabled decorators.
|
|
41
|
-
*
|
|
42
|
-
* @internal
|
|
43
|
-
* @param options Configuration object.
|
|
44
|
-
* @param options.decoratorStates Initial decorator states.
|
|
45
|
-
* @param options.allDecorators Collection of all manual decorators.
|
|
46
|
-
* @returns Updated decorator states with conflicts resolved.
|
|
47
|
-
*/
|
|
48
|
-
export function resolveConflictingDecorators({ decoratorStates, allDecorators }) {
|
|
49
|
-
const resolved = { ...decoratorStates };
|
|
50
|
-
for (const name in decoratorStates) {
|
|
51
|
-
if (decoratorStates[name] && isNewlyAddedDecorator(name)) {
|
|
52
|
-
const conflicts = getConflictingManualDecorators(name, allDecorators);
|
|
53
|
-
for (const conflict of conflicts) {
|
|
54
|
-
resolved[conflict] = false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function isNewlyAddedDecorator(name) {
|
|
59
|
-
return allDecorators.some(item => item.id === name && !item.value);
|
|
60
|
-
}
|
|
61
|
-
return resolved;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Returns array of decorator names that conflict with the given decorator.
|
|
65
|
-
* Decorators conflict when they share the same HTML attribute names or style properties.
|
|
66
|
-
*
|
|
67
|
-
* @param decoratorId The id/name of the manual decorator to check for conflicts.
|
|
68
|
-
* @param manualDecorators Collection of all manual decorators.
|
|
69
|
-
* @returns Array of conflicting decorator names.
|
|
70
|
-
*/
|
|
71
|
-
function getConflictingManualDecorators(decoratorId, manualDecorators) {
|
|
72
|
-
const decorator = manualDecorators.find(item => item.id === decoratorId);
|
|
73
|
-
/* istanbul ignore next -- @preserve */
|
|
74
|
-
if (!decorator) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
return manualDecorators
|
|
78
|
-
.filter(otherDecorator => otherDecorator.id !== decoratorId && areDecoratorsConflicting(decorator, otherDecorator))
|
|
79
|
-
.map(item => item.id);
|
|
80
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module link/utils/manualdecorator
|
|
7
|
-
*/
|
|
8
|
-
import { ObservableMixin } from 'ckeditor5/src/utils.js';
|
|
9
|
-
/**
|
|
10
|
-
* Helper class that stores manual decorators with observable {@link module:link/utils/manualdecorator~LinkManualDecorator#value}
|
|
11
|
-
* to support integration with the UI state. An instance of this class is a model with the state of individual manual decorators.
|
|
12
|
-
* These decorators are kept as collections in {@link module:link/linkcommand~LinkCommand#manualDecorators}.
|
|
13
|
-
*/
|
|
14
|
-
export class LinkManualDecorator extends /* #__PURE__ */ ObservableMixin() {
|
|
15
|
-
/**
|
|
16
|
-
* An ID of a manual decorator which is the name of the attribute in the model, for example: 'linkManualDecorator0'.
|
|
17
|
-
*/
|
|
18
|
-
id;
|
|
19
|
-
/**
|
|
20
|
-
* The default value of manual decorator.
|
|
21
|
-
*/
|
|
22
|
-
defaultValue;
|
|
23
|
-
/**
|
|
24
|
-
* The label used in the user interface to toggle the manual decorator.
|
|
25
|
-
*/
|
|
26
|
-
label;
|
|
27
|
-
/**
|
|
28
|
-
* A set of attributes added to downcasted data when the decorator is activated for a specific link.
|
|
29
|
-
* Attributes should be added in a form of attributes defined in {@link module:engine/view/elementdefinition~ViewElementDefinition}.
|
|
30
|
-
*/
|
|
31
|
-
attributes;
|
|
32
|
-
/**
|
|
33
|
-
* A set of classes added to downcasted data when the decorator is activated for a specific link.
|
|
34
|
-
* Classes should be added in a form of classes defined in {@link module:engine/view/elementdefinition~ViewElementDefinition}.
|
|
35
|
-
*/
|
|
36
|
-
classes;
|
|
37
|
-
/**
|
|
38
|
-
* A set of styles added to downcasted data when the decorator is activated for a specific link.
|
|
39
|
-
* Styles should be added in a form of styles defined in {@link module:engine/view/elementdefinition~ViewElementDefinition}.
|
|
40
|
-
*/
|
|
41
|
-
styles;
|
|
42
|
-
/**
|
|
43
|
-
* Creates a new instance of {@link module:link/utils/manualdecorator~LinkManualDecorator}.
|
|
44
|
-
*
|
|
45
|
-
* @param options The configuration object.
|
|
46
|
-
*/
|
|
47
|
-
constructor({ id, label, attributes, classes, styles, defaultValue }) {
|
|
48
|
-
super();
|
|
49
|
-
this.id = id;
|
|
50
|
-
this.set('value', undefined);
|
|
51
|
-
this.defaultValue = defaultValue;
|
|
52
|
-
this.label = label;
|
|
53
|
-
this.attributes = attributes;
|
|
54
|
-
this.classes = classes;
|
|
55
|
-
this.styles = styles;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Returns {@link module:engine/view/matcher~MatcherPattern} with decorator attributes.
|
|
59
|
-
*
|
|
60
|
-
* @internal
|
|
61
|
-
*/
|
|
62
|
-
_createPattern() {
|
|
63
|
-
return {
|
|
64
|
-
attributes: this.attributes,
|
|
65
|
-
classes: this.classes,
|
|
66
|
-
styles: this.styles
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|