@ckeditor/ckeditor5-link 47.6.1 → 48.0.0-alpha.1
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
package/src/linkediting.js
DELETED
|
@@ -1,402 +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/linkediting
|
|
7
|
-
*/
|
|
8
|
-
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
-
import { Input, TwoStepCaretMovement, inlineHighlight } from 'ckeditor5/src/typing.js';
|
|
10
|
-
import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
|
|
11
|
-
import { keyCodes, env, priorities } from 'ckeditor5/src/utils.js';
|
|
12
|
-
import { LinkCommand } from './linkcommand.js';
|
|
13
|
-
import { UnlinkCommand } from './unlinkcommand.js';
|
|
14
|
-
import { areDecoratorsConflicting } from './utils/conflictingdecorators.js';
|
|
15
|
-
import { LinkManualDecorator } from './utils/manualdecorator.js';
|
|
16
|
-
import { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators, addLinkProtocolIfApplicable, openLink } from './utils.js';
|
|
17
|
-
import '../theme/link.css';
|
|
18
|
-
const HIGHLIGHT_CLASS = 'ck-link_selected';
|
|
19
|
-
const DECORATOR_AUTOMATIC = 'automatic';
|
|
20
|
-
const DECORATOR_MANUAL = 'manual';
|
|
21
|
-
const EXTERNAL_LINKS_REGEXP = /^(https?:)?\/\//;
|
|
22
|
-
/**
|
|
23
|
-
* The link engine feature.
|
|
24
|
-
*
|
|
25
|
-
* It introduces the `linkHref="url"` attribute in the model which renders to the view as a `<a href="url">` element
|
|
26
|
-
* as well as `'link'` and `'unlink'` commands.
|
|
27
|
-
*/
|
|
28
|
-
export class LinkEditing extends Plugin {
|
|
29
|
-
/**
|
|
30
|
-
* A list of functions that handles opening links. If any of them returns `true`, the link is considered to be opened.
|
|
31
|
-
*/
|
|
32
|
-
_linkOpeners = [];
|
|
33
|
-
/**
|
|
34
|
-
* @inheritDoc
|
|
35
|
-
*/
|
|
36
|
-
static get pluginName() {
|
|
37
|
-
return 'LinkEditing';
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* @inheritDoc
|
|
41
|
-
*/
|
|
42
|
-
static get isOfficialPlugin() {
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* @inheritDoc
|
|
47
|
-
*/
|
|
48
|
-
static get requires() {
|
|
49
|
-
// Clipboard is required for handling cut and paste events while typing over the link.
|
|
50
|
-
return [TwoStepCaretMovement, Input, ClipboardPipeline];
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* @inheritDoc
|
|
54
|
-
*/
|
|
55
|
-
constructor(editor) {
|
|
56
|
-
super(editor);
|
|
57
|
-
editor.config.define('link', {
|
|
58
|
-
allowCreatingEmptyLinks: false,
|
|
59
|
-
addTargetToExternalLinks: false,
|
|
60
|
-
toolbar: ['linkPreview', '|', 'editLink', 'linkProperties', 'unlink']
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* @inheritDoc
|
|
65
|
-
*/
|
|
66
|
-
init() {
|
|
67
|
-
const editor = this.editor;
|
|
68
|
-
const allowedProtocols = this.editor.config.get('link.allowedProtocols');
|
|
69
|
-
// Allow link attribute on all inline nodes.
|
|
70
|
-
editor.model.schema.extend('$text', { allowAttributes: 'linkHref' });
|
|
71
|
-
editor.conversion.for('dataDowncast')
|
|
72
|
-
.attributeToElement({ model: 'linkHref', view: createLinkElement });
|
|
73
|
-
editor.conversion.for('editingDowncast')
|
|
74
|
-
.attributeToElement({ model: 'linkHref', view: (href, conversionApi) => {
|
|
75
|
-
return createLinkElement(ensureSafeUrl(href, allowedProtocols), conversionApi);
|
|
76
|
-
} });
|
|
77
|
-
editor.conversion.for('upcast')
|
|
78
|
-
.elementToAttribute({
|
|
79
|
-
view: {
|
|
80
|
-
name: 'a',
|
|
81
|
-
attributes: {
|
|
82
|
-
href: true
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
model: {
|
|
86
|
-
key: 'linkHref',
|
|
87
|
-
value: (viewElement) => viewElement.getAttribute('href')
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
// Create linking commands.
|
|
91
|
-
editor.commands.add('link', new LinkCommand(editor));
|
|
92
|
-
editor.commands.add('unlink', new UnlinkCommand(editor));
|
|
93
|
-
const linkDecorators = getLocalizedDecorators(editor.t, normalizeDecorators(editor.config.get('link.decorators')));
|
|
94
|
-
this._enableAutomaticDecorators(linkDecorators
|
|
95
|
-
.filter((item) => item.mode === DECORATOR_AUTOMATIC));
|
|
96
|
-
this._enableManualDecorators(linkDecorators
|
|
97
|
-
.filter((item) => item.mode === DECORATOR_MANUAL));
|
|
98
|
-
// Enable two-step caret movement for `linkHref` attribute.
|
|
99
|
-
const twoStepCaretMovementPlugin = editor.plugins.get(TwoStepCaretMovement);
|
|
100
|
-
twoStepCaretMovementPlugin.registerAttribute('linkHref');
|
|
101
|
-
// Setup highlight over selected link.
|
|
102
|
-
inlineHighlight(editor, 'linkHref', 'a', HIGHLIGHT_CLASS);
|
|
103
|
-
// Handle link following by CTRL+click or ALT+ENTER
|
|
104
|
-
this._enableLinkOpen();
|
|
105
|
-
// Clears the ModelDocumentSelection decorator attributes if the selection is no longer in a link (for example while using 2-SCM).
|
|
106
|
-
this._enableSelectionAttributesFixer();
|
|
107
|
-
// Handle adding default protocol to pasted links.
|
|
108
|
-
this._enableClipboardIntegration();
|
|
109
|
-
// Register postfixer that resolves conflicting decorator attributes.
|
|
110
|
-
this._enableDecoratorConflictPostfixer();
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Registers a function that opens links in a new browser tab.
|
|
114
|
-
*
|
|
115
|
-
* @param linkOpener The function that opens a link in a new browser tab.
|
|
116
|
-
* @internal
|
|
117
|
-
*/
|
|
118
|
-
_registerLinkOpener(linkOpener) {
|
|
119
|
-
this._linkOpeners.push(linkOpener);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Processes an array of configured {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators}
|
|
123
|
-
* and registers a {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}
|
|
124
|
-
* for each one of them. Downcast dispatchers are obtained using the
|
|
125
|
-
* {@link module:link/utils/automaticdecorators~AutomaticLinkDecorators#getDispatcher} method.
|
|
126
|
-
*
|
|
127
|
-
* **Note**: This method also activates the automatic external link decorator if enabled with
|
|
128
|
-
* {@link module:link/linkconfig~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}.
|
|
129
|
-
*/
|
|
130
|
-
_enableAutomaticDecorators(automaticDecoratorDefinitions) {
|
|
131
|
-
const editor = this.editor;
|
|
132
|
-
// Store automatic decorators in the command instance as we do the same with manual decorators.
|
|
133
|
-
// Thanks to that, `LinkImageEditing` plugin can re-use the same definitions.
|
|
134
|
-
const command = editor.commands.get('link');
|
|
135
|
-
const automaticDecorators = command.automaticDecorators;
|
|
136
|
-
// Adds a default decorator for external links.
|
|
137
|
-
if (editor.config.get('link.addTargetToExternalLinks')) {
|
|
138
|
-
automaticDecorators.add({
|
|
139
|
-
id: 'linkIsExternal',
|
|
140
|
-
mode: DECORATOR_AUTOMATIC,
|
|
141
|
-
callback: url => !!url && EXTERNAL_LINKS_REGEXP.test(url),
|
|
142
|
-
attributes: {
|
|
143
|
-
target: '_blank',
|
|
144
|
-
rel: 'noopener noreferrer'
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
automaticDecorators.add(automaticDecoratorDefinitions);
|
|
149
|
-
automaticDecorators.setConflictChecker((automaticDecorator, modelItem) => {
|
|
150
|
-
for (const manualDecorator of command.manualDecorators) {
|
|
151
|
-
// If manual decorator is not applied, skip it.
|
|
152
|
-
if (!modelItem.hasAttribute(manualDecorator.id)) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
// If it conflicts with manual decorator that was applied, return true
|
|
156
|
-
// to prevent the automatic decorator from being applied.
|
|
157
|
-
if (areDecoratorsConflicting(automaticDecorator, manualDecorator)) {
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
if (automaticDecorators.length) {
|
|
163
|
-
editor.conversion.for('downcast').add(automaticDecorators.getDispatcher());
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Processes an array of configured {@link module:link/linkconfig~LinkDecoratorManualDefinition manual decorators},
|
|
168
|
-
* transforms them into {@link module:link/utils/manualdecorator~LinkManualDecorator} instances and stores them in the
|
|
169
|
-
* {@link module:link/linkcommand~LinkCommand#manualDecorators} collection (a model for manual decorators state).
|
|
170
|
-
*
|
|
171
|
-
* Also registers an {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement attribute-to-element}
|
|
172
|
-
* converter for each manual decorator and extends the {@link module:engine/model/schema~ModelSchema model's schema}
|
|
173
|
-
* with adequate model attributes.
|
|
174
|
-
*/
|
|
175
|
-
_enableManualDecorators(manualDecoratorDefinitions) {
|
|
176
|
-
if (!manualDecoratorDefinitions.length) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const editor = this.editor;
|
|
180
|
-
const command = editor.commands.get('link');
|
|
181
|
-
const manualDecorators = command.manualDecorators;
|
|
182
|
-
manualDecoratorDefinitions.forEach(decoratorDefinition => {
|
|
183
|
-
editor.model.schema.extend('$text', { allowAttributes: decoratorDefinition.id });
|
|
184
|
-
// Keeps reference to manual decorator to decode its name to attributes during downcast.
|
|
185
|
-
const decorator = new LinkManualDecorator(decoratorDefinition);
|
|
186
|
-
manualDecorators.add(decorator);
|
|
187
|
-
editor.conversion.for('downcast').add(dispatcher => {
|
|
188
|
-
const elementCreator = (writer) => {
|
|
189
|
-
const element = writer.createAttributeElement('a', decorator.attributes, { priority: 5 });
|
|
190
|
-
if (decorator.classes) {
|
|
191
|
-
writer.addClass(decorator.classes, element);
|
|
192
|
-
}
|
|
193
|
-
for (const key in decorator.styles) {
|
|
194
|
-
writer.setStyle(key, decorator.styles[key], element);
|
|
195
|
-
}
|
|
196
|
-
writer.setCustomProperty('link', true, element);
|
|
197
|
-
return element;
|
|
198
|
-
};
|
|
199
|
-
const createConverter = (isApplyingConverter) => {
|
|
200
|
-
return (evt, data, conversionApi) => {
|
|
201
|
-
// Manual decorators for block links are handled e.g. in LinkImageEditing.
|
|
202
|
-
if (!data.item.is('selection') && !conversionApi.schema.isInline(data.item)) {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
if (!isApplyingConverter && data.attributeOldValue) {
|
|
206
|
-
// Only test while removing the decorator as this is triggered before the applying converter.
|
|
207
|
-
if (!conversionApi.consumable.test(data.item, evt.name)) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
conversionApi.writer.unwrap(conversionApi.mapper.toViewRange(data.range), elementCreator(conversionApi.writer));
|
|
211
|
-
}
|
|
212
|
-
if (isApplyingConverter && data.attributeNewValue) {
|
|
213
|
-
// Consume while applying the decorator.
|
|
214
|
-
if (!conversionApi.consumable.consume(data.item, evt.name)) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
if (data.item.is('selection')) {
|
|
218
|
-
conversionApi.writer.wrap(conversionApi.writer.document.selection.getFirstRange(), elementCreator(conversionApi.writer));
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
conversionApi.writer.wrap(conversionApi.mapper.toViewRange(data.range), elementCreator(conversionApi.writer));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
};
|
|
226
|
-
dispatcher.on(`attribute:${decorator.id}`, createConverter(false), {
|
|
227
|
-
priority: priorities.high - 1
|
|
228
|
-
});
|
|
229
|
-
// Apply decorators after all automatic and manual decorators are removed so removing one decorator
|
|
230
|
-
// won't strip part of the other decorator's attributes, classes or styles.
|
|
231
|
-
dispatcher.on(`attribute:${decorator.id}`, createConverter(true), {
|
|
232
|
-
priority: priorities.high - 2
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
editor.conversion.for('upcast').elementToAttribute({
|
|
236
|
-
view: {
|
|
237
|
-
name: 'a',
|
|
238
|
-
...decorator._createPattern()
|
|
239
|
-
},
|
|
240
|
-
model: {
|
|
241
|
-
key: decorator.id
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Attaches handlers for {@link module:engine/view/document~ViewDocument#event:enter} and
|
|
248
|
-
* {@link module:engine/view/document~ViewDocument#event:click} to enable link following.
|
|
249
|
-
*/
|
|
250
|
-
_enableLinkOpen() {
|
|
251
|
-
const editor = this.editor;
|
|
252
|
-
const view = editor.editing.view;
|
|
253
|
-
const viewDocument = view.document;
|
|
254
|
-
const handleLinkOpening = (url) => {
|
|
255
|
-
if (!this._linkOpeners.some(opener => opener(url))) {
|
|
256
|
-
openLink(url);
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
this.listenTo(viewDocument, 'click', (evt, data) => {
|
|
260
|
-
const shouldOpen = env.isMac ? data.domEvent.metaKey : data.domEvent.ctrlKey;
|
|
261
|
-
if (!shouldOpen) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
let clickedElement = data.domTarget;
|
|
265
|
-
if (clickedElement.tagName.toLowerCase() != 'a') {
|
|
266
|
-
clickedElement = clickedElement.closest('a');
|
|
267
|
-
}
|
|
268
|
-
if (!clickedElement) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const url = clickedElement.getAttribute('href');
|
|
272
|
-
if (!url) {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
evt.stop();
|
|
276
|
-
data.preventDefault();
|
|
277
|
-
handleLinkOpening(url);
|
|
278
|
-
}, { context: '$capture' });
|
|
279
|
-
// Open link on Alt+Enter.
|
|
280
|
-
this.listenTo(viewDocument, 'keydown', (evt, data) => {
|
|
281
|
-
const linkCommand = editor.commands.get('link');
|
|
282
|
-
const url = linkCommand.value;
|
|
283
|
-
const shouldOpen = !!url && data.keyCode === keyCodes.enter && data.altKey;
|
|
284
|
-
if (!shouldOpen) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
evt.stop();
|
|
288
|
-
handleLinkOpening(url);
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Watches the ModelDocumentSelection attribute changes and removes link decorator attributes when the linkHref attribute is removed.
|
|
293
|
-
*
|
|
294
|
-
* This is to ensure that there is no left-over link decorator attributes on the document selection that is no longer in a link.
|
|
295
|
-
*/
|
|
296
|
-
_enableSelectionAttributesFixer() {
|
|
297
|
-
const editor = this.editor;
|
|
298
|
-
const model = editor.model;
|
|
299
|
-
const selection = model.document.selection;
|
|
300
|
-
this.listenTo(selection, 'change:attribute', (evt, { attributeKeys }) => {
|
|
301
|
-
if (!attributeKeys.includes('linkHref') || selection.hasAttribute('linkHref')) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
model.change(writer => {
|
|
305
|
-
removeLinkAttributesFromSelection(writer, getLinkAttributesAllowedOnText(model.schema));
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Enables URL fixing on pasting.
|
|
311
|
-
*/
|
|
312
|
-
_enableClipboardIntegration() {
|
|
313
|
-
const editor = this.editor;
|
|
314
|
-
const model = editor.model;
|
|
315
|
-
const defaultProtocol = this.editor.config.get('link.defaultProtocol');
|
|
316
|
-
if (!defaultProtocol) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
this.listenTo(editor.plugins.get('ClipboardPipeline'), 'contentInsertion', (evt, data) => {
|
|
320
|
-
model.change(writer => {
|
|
321
|
-
const range = writer.createRangeIn(data.content);
|
|
322
|
-
for (const item of range.getItems()) {
|
|
323
|
-
if (item.hasAttribute('linkHref')) {
|
|
324
|
-
const newLink = addLinkProtocolIfApplicable(item.getAttribute('linkHref'), defaultProtocol);
|
|
325
|
-
writer.setAttribute('linkHref', newLink, item);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Registers a postfixer that resolves conflicting decorator attributes on elements.
|
|
333
|
-
*/
|
|
334
|
-
_enableDecoratorConflictPostfixer() {
|
|
335
|
-
const editor = this.editor;
|
|
336
|
-
const model = editor.model;
|
|
337
|
-
const linkCommand = editor.commands.get('link');
|
|
338
|
-
model.document.registerPostFixer(writer => {
|
|
339
|
-
let hasChanged = false;
|
|
340
|
-
const changes = model.document.differ.getChanges();
|
|
341
|
-
const elementsToCheck = new Set();
|
|
342
|
-
const manualDecoratorAttributeKeys = new Set(linkCommand.manualDecorators.map(decorator => decorator.id));
|
|
343
|
-
for (const change of changes) {
|
|
344
|
-
if (change.type === 'attribute') {
|
|
345
|
-
// Only react to link-related attribute changes to reduce work.
|
|
346
|
-
if (change.attributeKey !== 'linkHref' && !manualDecoratorAttributeKeys.has(change.attributeKey)) {
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
// Use range items instead of nodeAfter to avoid issues with text node merging.
|
|
350
|
-
for (const item of change.range.getItems()) {
|
|
351
|
-
if (item.hasAttribute('linkHref')) {
|
|
352
|
-
elementsToCheck.add(item);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// Check if newly inserted nodes have link attributes. This is to cover the case of pasting content
|
|
357
|
-
// with link attributes or typing over a link.
|
|
358
|
-
if (change.type === 'insert' && change.attributes.has('linkHref') && change.position.nodeAfter) {
|
|
359
|
-
elementsToCheck.add(change.position.nodeAfter);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
// Check each element for conflicting decorators.
|
|
363
|
-
for (const item of elementsToCheck) {
|
|
364
|
-
const appliedDecorators = [];
|
|
365
|
-
for (const manualDecorator of linkCommand.manualDecorators) {
|
|
366
|
-
if (!item.hasAttribute(manualDecorator.id)) {
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
// Resolve conflicts on the fly, remove the earlier decorator (later wins).
|
|
370
|
-
for (let i = appliedDecorators.length - 1; i >= 0; i--) {
|
|
371
|
-
const appliedDecorator = appliedDecorators[i];
|
|
372
|
-
if (areDecoratorsConflicting(appliedDecorator, manualDecorator)) {
|
|
373
|
-
writer.removeAttribute(appliedDecorator.id, item);
|
|
374
|
-
appliedDecorators.splice(i, 1);
|
|
375
|
-
hasChanged = true;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
appliedDecorators.push(manualDecorator);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return hasChanged;
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Make the selection free of link-related model attributes.
|
|
387
|
-
* All link-related model attributes start with "link". That includes not only "linkHref"
|
|
388
|
-
* but also all decorator attributes (they have dynamic names), or even custom plugins.
|
|
389
|
-
*/
|
|
390
|
-
function removeLinkAttributesFromSelection(writer, linkAttributes) {
|
|
391
|
-
writer.removeSelectionAttribute('linkHref');
|
|
392
|
-
for (const attribute of linkAttributes) {
|
|
393
|
-
writer.removeSelectionAttribute(attribute);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Returns an array containing names of the attributes allowed on `$text` that describes the link item.
|
|
398
|
-
*/
|
|
399
|
-
function getLinkAttributesAllowedOnText(schema) {
|
|
400
|
-
const textAttributes = schema.getDefinition('$text').allowAttributes;
|
|
401
|
-
return textAttributes.filter(attribute => attribute.startsWith('link'));
|
|
402
|
-
}
|
package/src/linkimage.js
DELETED
|
@@ -1,37 +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/linkimage
|
|
7
|
-
*/
|
|
8
|
-
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
-
import { LinkImageEditing } from './linkimageediting.js';
|
|
10
|
-
import { LinkImageUI } from './linkimageui.js';
|
|
11
|
-
import '../theme/linkimage.css';
|
|
12
|
-
/**
|
|
13
|
-
* The `LinkImage` plugin.
|
|
14
|
-
*
|
|
15
|
-
* This is a "glue" plugin that loads the {@link module:link/linkimageediting~LinkImageEditing link image editing feature}
|
|
16
|
-
* and {@link module:link/linkimageui~LinkImageUI link image UI feature}.
|
|
17
|
-
*/
|
|
18
|
-
export class LinkImage extends Plugin {
|
|
19
|
-
/**
|
|
20
|
-
* @inheritDoc
|
|
21
|
-
*/
|
|
22
|
-
static get requires() {
|
|
23
|
-
return [LinkImageEditing, LinkImageUI];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* @inheritDoc
|
|
27
|
-
*/
|
|
28
|
-
static get pluginName() {
|
|
29
|
-
return 'LinkImage';
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* @inheritDoc
|
|
33
|
-
*/
|
|
34
|
-
static get isOfficialPlugin() {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
}
|