@ckeditor/ckeditor5-link 41.4.1 → 42.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/README.md +6 -0
- package/build/link.js +1 -1
- package/dist/index.js +634 -531
- package/dist/index.js.map +1 -1
- package/dist/types/utils/manualdecorator.d.ts +1 -1
- package/package.json +3 -3
- package/src/ui/linkformview.js +1 -0
- package/src/utils/manualdecorator.d.ts +1 -1
- package/src/utils/manualdecorator.js +1 -1
package/dist/index.js
CHANGED
|
@@ -5,24 +5,31 @@
|
|
|
5
5
|
import { Command, Plugin, icons } from '@ckeditor/ckeditor5-core/dist/index.js';
|
|
6
6
|
import { findAttributeRange, TwoStepCaretMovement, Input, inlineHighlight, Delete, TextWatcher, getLastTextLine } from '@ckeditor/ckeditor5-typing/dist/index.js';
|
|
7
7
|
import { ClipboardPipeline } from '@ckeditor/ckeditor5-clipboard/dist/index.js';
|
|
8
|
-
import { toMap,
|
|
8
|
+
import { toMap, Collection, first, ObservableMixin, env, keyCodes, FocusTracker, KeystrokeHandler } from '@ckeditor/ckeditor5-utils/dist/index.js';
|
|
9
9
|
import { upperFirst } from 'lodash-es';
|
|
10
10
|
import { ClickObserver, Matcher } from '@ckeditor/ckeditor5-engine/dist/index.js';
|
|
11
|
-
import { View, submitHandler, LabeledFieldView, createLabeledInputText, ButtonView, SwitchButtonView,
|
|
11
|
+
import { View, ViewCollection, FocusCycler, submitHandler, LabeledFieldView, createLabeledInputText, ButtonView, SwitchButtonView, ContextualBalloon, CssTransitionDisablerMixin, MenuBarMenuListItemButtonView, clickOutsideHandler } from '@ckeditor/ckeditor5-ui/dist/index.js';
|
|
12
12
|
import { isWidget } from '@ckeditor/ckeditor5-widget/dist/index.js';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Helper class that ties together all {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition} and provides
|
|
16
|
+
* the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement downcast dispatchers} for them.
|
|
17
|
+
*/ class AutomaticDecorators {
|
|
18
|
+
/**
|
|
19
|
+
* Stores the definition of {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators}.
|
|
20
|
+
* This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
|
|
21
|
+
*/ _definitions = new Set();
|
|
15
22
|
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
* Gives information about the number of decorators stored in the {@link module:link/utils/automaticdecorators~AutomaticDecorators}
|
|
24
|
+
* instance.
|
|
25
|
+
*/ get length() {
|
|
19
26
|
return this._definitions.size;
|
|
20
27
|
}
|
|
21
28
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
* Adds automatic decorator objects or an array with them to be used during downcasting.
|
|
30
|
+
*
|
|
31
|
+
* @param item A configuration object of automatic rules for decorating links. It might also be an array of such objects.
|
|
32
|
+
*/ add(item) {
|
|
26
33
|
if (Array.isArray(item)) {
|
|
27
34
|
item.forEach((item)=>this._definitions.add(item));
|
|
28
35
|
} else {
|
|
@@ -30,10 +37,10 @@ class AutomaticDecorators {
|
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
* Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
|
|
41
|
+
*
|
|
42
|
+
* @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
|
|
43
|
+
*/ getDispatcher() {
|
|
37
44
|
return (dispatcher)=>{
|
|
38
45
|
dispatcher.on('attribute:linkHref', (evt, data, conversionApi)=>{
|
|
39
46
|
// There is only test as this behavior decorates links and
|
|
@@ -76,11 +83,11 @@ class AutomaticDecorators {
|
|
|
76
83
|
};
|
|
77
84
|
}
|
|
78
85
|
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
* Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
|
|
87
|
+
* when linking images.
|
|
88
|
+
*
|
|
89
|
+
* @returns A dispatcher function used as conversion helper in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
|
|
90
|
+
*/ getDispatcherForLinkedImage() {
|
|
84
91
|
return (dispatcher)=>{
|
|
85
92
|
dispatcher.on('attribute:linkHref:imageBlock', (evt, data, { writer, mapper })=>{
|
|
86
93
|
const viewFigure = mapper.toViewElement(data.item);
|
|
@@ -122,12 +129,6 @@ class AutomaticDecorators {
|
|
|
122
129
|
});
|
|
123
130
|
};
|
|
124
131
|
}
|
|
125
|
-
constructor(){
|
|
126
|
-
/**
|
|
127
|
-
* Stores the definition of {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators}.
|
|
128
|
-
* This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
|
|
129
|
-
*/ this._definitions = new Set();
|
|
130
|
-
}
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
const ATTRIBUTE_WHITESPACES = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
|
|
@@ -255,17 +256,29 @@ const DEFAULT_LINK_PROTOCOLS = [
|
|
|
255
256
|
window.open(link, '_blank', 'noopener');
|
|
256
257
|
}
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
/**
|
|
260
|
+
* The link command. It is used by the {@link module:link/link~Link link feature}.
|
|
261
|
+
*/ class LinkCommand extends Command {
|
|
259
262
|
/**
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
* A collection of {@link module:link/utils/manualdecorator~ManualDecorator manual decorators}
|
|
264
|
+
* corresponding to the {@link module:link/linkconfig~LinkConfig#decorators decorator configuration}.
|
|
265
|
+
*
|
|
266
|
+
* You can consider it a model with states of manual decorators added to the currently selected link.
|
|
267
|
+
*/ manualDecorators = new Collection();
|
|
268
|
+
/**
|
|
269
|
+
* An instance of the helper that ties together all {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition}
|
|
270
|
+
* that are used by the {@glink features/link link} and the {@glink features/images/images-linking linking images} features.
|
|
271
|
+
*/ automaticDecorators = new AutomaticDecorators();
|
|
272
|
+
/**
|
|
273
|
+
* Synchronizes the state of {@link #manualDecorators} with the currently present elements in the model.
|
|
274
|
+
*/ restoreManualDecoratorStates() {
|
|
262
275
|
for (const manualDecorator of this.manualDecorators){
|
|
263
276
|
manualDecorator.value = this._getDecoratorStateFromModel(manualDecorator.id);
|
|
264
277
|
}
|
|
265
278
|
}
|
|
266
279
|
/**
|
|
267
|
-
|
|
268
|
-
|
|
280
|
+
* @inheritDoc
|
|
281
|
+
*/ refresh() {
|
|
269
282
|
const model = this.editor.model;
|
|
270
283
|
const selection = model.document.selection;
|
|
271
284
|
const selectedElement = selection.getSelectedElement() || first(selection.getSelectedBlocks());
|
|
@@ -283,70 +296,70 @@ class LinkCommand extends Command {
|
|
|
283
296
|
}
|
|
284
297
|
}
|
|
285
298
|
/**
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
299
|
+
* Executes the command.
|
|
300
|
+
*
|
|
301
|
+
* When the selection is non-collapsed, the `linkHref` attribute will be applied to nodes inside the selection, but only to
|
|
302
|
+
* those nodes where the `linkHref` attribute is allowed (disallowed nodes will be omitted).
|
|
303
|
+
*
|
|
304
|
+
* When the selection is collapsed and is not inside the text with the `linkHref` attribute, a
|
|
305
|
+
* new {@link module:engine/model/text~Text text node} with the `linkHref` attribute will be inserted in place of the caret, but
|
|
306
|
+
* only if such element is allowed in this place. The `_data` of the inserted text will equal the `href` parameter.
|
|
307
|
+
* The selection will be updated to wrap the just inserted text node.
|
|
308
|
+
*
|
|
309
|
+
* When the selection is collapsed and inside the text with the `linkHref` attribute, the attribute value will be updated.
|
|
310
|
+
*
|
|
311
|
+
* # Decorators and model attribute management
|
|
312
|
+
*
|
|
313
|
+
* There is an optional argument to this command that applies or removes model
|
|
314
|
+
* {@glink framework/architecture/editing-engine#text-attributes text attributes} brought by
|
|
315
|
+
* {@link module:link/utils/manualdecorator~ManualDecorator manual link decorators}.
|
|
316
|
+
*
|
|
317
|
+
* Text attribute names in the model correspond to the entries in the {@link module:link/linkconfig~LinkConfig#decorators
|
|
318
|
+
* configuration}.
|
|
319
|
+
* For every decorator configured, a model text attribute exists with the "link" prefix. For example, a `'linkMyDecorator'` attribute
|
|
320
|
+
* corresponds to `'myDecorator'` in the configuration.
|
|
321
|
+
*
|
|
322
|
+
* To learn more about link decorators, check out the {@link module:link/linkconfig~LinkConfig#decorators `config.link.decorators`}
|
|
323
|
+
* documentation.
|
|
324
|
+
*
|
|
325
|
+
* Here is how to manage decorator attributes with the link command:
|
|
326
|
+
*
|
|
327
|
+
* ```ts
|
|
328
|
+
* const linkCommand = editor.commands.get( 'link' );
|
|
329
|
+
*
|
|
330
|
+
* // Adding a new decorator attribute.
|
|
331
|
+
* linkCommand.execute( 'http://example.com', {
|
|
332
|
+
* linkIsExternal: true
|
|
333
|
+
* } );
|
|
334
|
+
*
|
|
335
|
+
* // Removing a decorator attribute from the selection.
|
|
336
|
+
* linkCommand.execute( 'http://example.com', {
|
|
337
|
+
* linkIsExternal: false
|
|
338
|
+
* } );
|
|
339
|
+
*
|
|
340
|
+
* // Adding multiple decorator attributes at the same time.
|
|
341
|
+
* linkCommand.execute( 'http://example.com', {
|
|
342
|
+
* linkIsExternal: true,
|
|
343
|
+
* linkIsDownloadable: true,
|
|
344
|
+
* } );
|
|
345
|
+
*
|
|
346
|
+
* // Removing and adding decorator attributes at the same time.
|
|
347
|
+
* linkCommand.execute( 'http://example.com', {
|
|
348
|
+
* linkIsExternal: false,
|
|
349
|
+
* linkFoo: true,
|
|
350
|
+
* linkIsDownloadable: false,
|
|
351
|
+
* } );
|
|
352
|
+
* ```
|
|
353
|
+
*
|
|
354
|
+
* **Note**: If the decorator attribute name is not specified, its state remains untouched.
|
|
355
|
+
*
|
|
356
|
+
* **Note**: {@link module:link/unlinkcommand~UnlinkCommand#execute `UnlinkCommand#execute()`} removes all
|
|
357
|
+
* decorator attributes.
|
|
358
|
+
*
|
|
359
|
+
* @fires execute
|
|
360
|
+
* @param href Link destination.
|
|
361
|
+
* @param manualDecoratorIds The information about manual decorator attributes to be applied or removed upon execution.
|
|
362
|
+
*/ execute(href, manualDecoratorIds = {}) {
|
|
350
363
|
const model = this.editor.model;
|
|
351
364
|
const selection = model.document.selection;
|
|
352
365
|
// Stores information about manual decorators to turn them on/off when command is applied.
|
|
@@ -442,11 +455,11 @@ class LinkCommand extends Command {
|
|
|
442
455
|
});
|
|
443
456
|
}
|
|
444
457
|
/**
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
458
|
+
* Provides information whether a decorator with a given name is present in the currently processed selection.
|
|
459
|
+
*
|
|
460
|
+
* @param decoratorName The name of the manual decorator used in the model
|
|
461
|
+
* @returns The information whether a given decorator is currently present in the selection.
|
|
462
|
+
*/ _getDecoratorStateFromModel(decoratorName) {
|
|
450
463
|
const model = this.editor.model;
|
|
451
464
|
const selection = model.document.selection;
|
|
452
465
|
const selectedElement = selection.getSelectedElement();
|
|
@@ -458,11 +471,11 @@ class LinkCommand extends Command {
|
|
|
458
471
|
return selection.getAttribute(decoratorName);
|
|
459
472
|
}
|
|
460
473
|
/**
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
474
|
+
* Checks whether specified `range` is inside an element that accepts the `linkHref` attribute.
|
|
475
|
+
*
|
|
476
|
+
* @param range A range to check.
|
|
477
|
+
* @param allowedRanges An array of ranges created on elements where the attribute is accepted.
|
|
478
|
+
*/ _isRangeToUpdate(range, allowedRanges) {
|
|
466
479
|
for (const allowedRange of allowedRanges){
|
|
467
480
|
// A range is inside an element that will have the `linkHref` attribute. Do not modify its nodes.
|
|
468
481
|
if (allowedRange.containsRange(range)) {
|
|
@@ -472,31 +485,18 @@ class LinkCommand extends Command {
|
|
|
472
485
|
return true;
|
|
473
486
|
}
|
|
474
487
|
/**
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
488
|
+
* Updates selected link with a new value as its content and as its href attribute.
|
|
489
|
+
*
|
|
490
|
+
* @param model Model is need to insert content.
|
|
491
|
+
* @param writer Writer is need to create text element in model.
|
|
492
|
+
* @param range A range where should be inserted content.
|
|
493
|
+
* @param href A link value which should be in the href attribute and in the content.
|
|
494
|
+
*/ _updateLinkContent(model, writer, range, href) {
|
|
482
495
|
const text = writer.createText(href, {
|
|
483
496
|
linkHref: href
|
|
484
497
|
});
|
|
485
498
|
return model.insertContent(text, range);
|
|
486
499
|
}
|
|
487
|
-
constructor(){
|
|
488
|
-
super(...arguments);
|
|
489
|
-
/**
|
|
490
|
-
* A collection of {@link module:link/utils/manualdecorator~ManualDecorator manual decorators}
|
|
491
|
-
* corresponding to the {@link module:link/linkconfig~LinkConfig#decorators decorator configuration}.
|
|
492
|
-
*
|
|
493
|
-
* You can consider it a model with states of manual decorators added to the currently selected link.
|
|
494
|
-
*/ this.manualDecorators = new Collection();
|
|
495
|
-
/**
|
|
496
|
-
* An instance of the helper that ties together all {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition}
|
|
497
|
-
* that are used by the {@glink features/link link} and the {@glink features/images/images-linking linking images} features.
|
|
498
|
-
*/ this.automaticDecorators = new AutomaticDecorators();
|
|
499
|
-
}
|
|
500
500
|
}
|
|
501
501
|
// Returns a text of a link under the collapsed selection or a selection that contains the entire link.
|
|
502
502
|
function extractTextFromSelection(selection) {
|
|
@@ -516,10 +516,12 @@ function extractTextFromSelection(selection) {
|
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
|
|
519
|
+
/**
|
|
520
|
+
* The unlink command. It is used by the {@link module:link/link~Link link plugin}.
|
|
521
|
+
*/ class UnlinkCommand extends Command {
|
|
520
522
|
/**
|
|
521
|
-
|
|
522
|
-
|
|
523
|
+
* @inheritDoc
|
|
524
|
+
*/ refresh() {
|
|
523
525
|
const model = this.editor.model;
|
|
524
526
|
const selection = model.document.selection;
|
|
525
527
|
const selectedElement = selection.getSelectedElement();
|
|
@@ -532,18 +534,18 @@ class UnlinkCommand extends Command {
|
|
|
532
534
|
}
|
|
533
535
|
}
|
|
534
536
|
/**
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
537
|
+
* Executes the command.
|
|
538
|
+
*
|
|
539
|
+
* When the selection is collapsed, it removes the `linkHref` attribute from each node with the same `linkHref` attribute value.
|
|
540
|
+
* When the selection is non-collapsed, it removes the `linkHref` attribute from each node in selected ranges.
|
|
541
|
+
*
|
|
542
|
+
* # Decorators
|
|
543
|
+
*
|
|
544
|
+
* If {@link module:link/linkconfig~LinkConfig#decorators `config.link.decorators`} is specified,
|
|
545
|
+
* all configured decorators are removed together with the `linkHref` attribute.
|
|
546
|
+
*
|
|
547
|
+
* @fires execute
|
|
548
|
+
*/ execute() {
|
|
547
549
|
const editor = this.editor;
|
|
548
550
|
const model = this.editor.model;
|
|
549
551
|
const selection = model.document.selection;
|
|
@@ -567,28 +569,42 @@ class UnlinkCommand extends Command {
|
|
|
567
569
|
}
|
|
568
570
|
}
|
|
569
571
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
572
|
+
/**
|
|
573
|
+
* Helper class that stores manual decorators with observable {@link module:link/utils/manualdecorator~ManualDecorator#value}
|
|
574
|
+
* to support integration with the UI state. An instance of this class is a model with the state of individual manual decorators.
|
|
575
|
+
* These decorators are kept as collections in {@link module:link/linkcommand~LinkCommand#manualDecorators}.
|
|
576
|
+
*/ class ManualDecorator extends /* #__PURE__ */ ObservableMixin() {
|
|
577
|
+
/**
|
|
578
|
+
* An ID of a manual decorator which is the name of the attribute in the model, for example: 'linkManualDecorator0'.
|
|
579
|
+
*/ id;
|
|
580
|
+
/**
|
|
581
|
+
* The default value of manual decorator.
|
|
582
|
+
*/ defaultValue;
|
|
583
|
+
/**
|
|
584
|
+
* The label used in the user interface to toggle the manual decorator.
|
|
585
|
+
*/ label;
|
|
586
|
+
/**
|
|
587
|
+
* A set of attributes added to downcasted data when the decorator is activated for a specific link.
|
|
588
|
+
* Attributes should be added in a form of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
|
|
589
|
+
*/ attributes;
|
|
590
|
+
/**
|
|
591
|
+
* A set of classes added to downcasted data when the decorator is activated for a specific link.
|
|
592
|
+
* Classes should be added in a form of classes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
|
|
593
|
+
*/ classes;
|
|
594
|
+
/**
|
|
595
|
+
* A set of styles added to downcasted data when the decorator is activated for a specific link.
|
|
596
|
+
* Styles should be added in a form of styles defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
|
|
597
|
+
*/ styles;
|
|
598
|
+
/**
|
|
599
|
+
* Creates a new instance of {@link module:link/utils/manualdecorator~ManualDecorator}.
|
|
600
|
+
*
|
|
601
|
+
* @param config.id The name of the attribute used in the model that represents a given manual decorator.
|
|
602
|
+
* For example: `'linkIsExternal'`.
|
|
603
|
+
* @param config.label The label used in the user interface to toggle the manual decorator.
|
|
604
|
+
* @param config.attributes A set of attributes added to output data when the decorator is active for a specific link.
|
|
605
|
+
* Attributes should keep the format of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
|
|
606
|
+
* @param [config.defaultValue] Controls whether the decorator is "on" by default.
|
|
607
|
+
*/ constructor({ id, label, attributes, classes, styles, defaultValue }){
|
|
592
608
|
super();
|
|
593
609
|
this.id = id;
|
|
594
610
|
this.set('value', undefined);
|
|
@@ -598,21 +614,37 @@ class ManualDecorator extends ObservableMixin() {
|
|
|
598
614
|
this.classes = classes;
|
|
599
615
|
this.styles = styles;
|
|
600
616
|
}
|
|
617
|
+
/**
|
|
618
|
+
* Returns {@link module:engine/view/matcher~MatcherPattern} with decorator attributes.
|
|
619
|
+
*
|
|
620
|
+
* @internal
|
|
621
|
+
*/ _createPattern() {
|
|
622
|
+
return {
|
|
623
|
+
attributes: this.attributes,
|
|
624
|
+
classes: this.classes,
|
|
625
|
+
styles: this.styles
|
|
626
|
+
};
|
|
627
|
+
}
|
|
601
628
|
}
|
|
602
629
|
|
|
603
630
|
const HIGHLIGHT_CLASS = 'ck-link_selected';
|
|
604
631
|
const DECORATOR_AUTOMATIC = 'automatic';
|
|
605
632
|
const DECORATOR_MANUAL = 'manual';
|
|
606
633
|
const EXTERNAL_LINKS_REGEXP = /^(https?:)?\/\//;
|
|
607
|
-
|
|
634
|
+
/**
|
|
635
|
+
* The link engine feature.
|
|
636
|
+
*
|
|
637
|
+
* It introduces the `linkHref="url"` attribute in the model which renders to the view as a `<a href="url">` element
|
|
638
|
+
* as well as `'link'` and `'unlink'` commands.
|
|
639
|
+
*/ class LinkEditing extends Plugin {
|
|
608
640
|
/**
|
|
609
|
-
|
|
610
|
-
|
|
641
|
+
* @inheritDoc
|
|
642
|
+
*/ static get pluginName() {
|
|
611
643
|
return 'LinkEditing';
|
|
612
644
|
}
|
|
613
645
|
/**
|
|
614
|
-
|
|
615
|
-
|
|
646
|
+
* @inheritDoc
|
|
647
|
+
*/ static get requires() {
|
|
616
648
|
// Clipboard is required for handling cut and paste events while typing over the link.
|
|
617
649
|
return [
|
|
618
650
|
TwoStepCaretMovement,
|
|
@@ -621,8 +653,17 @@ class LinkEditing extends Plugin {
|
|
|
621
653
|
];
|
|
622
654
|
}
|
|
623
655
|
/**
|
|
624
|
-
|
|
625
|
-
|
|
656
|
+
* @inheritDoc
|
|
657
|
+
*/ constructor(editor){
|
|
658
|
+
super(editor);
|
|
659
|
+
editor.config.define('link', {
|
|
660
|
+
allowCreatingEmptyLinks: false,
|
|
661
|
+
addTargetToExternalLinks: false
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* @inheritDoc
|
|
666
|
+
*/ init() {
|
|
626
667
|
const editor = this.editor;
|
|
627
668
|
const allowedProtocols = this.editor.config.get('link.allowedProtocols');
|
|
628
669
|
// Allow link attribute on all inline nodes.
|
|
@@ -670,14 +711,14 @@ class LinkEditing extends Plugin {
|
|
|
670
711
|
this._enableClipboardIntegration();
|
|
671
712
|
}
|
|
672
713
|
/**
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
714
|
+
* Processes an array of configured {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators}
|
|
715
|
+
* and registers a {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}
|
|
716
|
+
* for each one of them. Downcast dispatchers are obtained using the
|
|
717
|
+
* {@link module:link/utils/automaticdecorators~AutomaticDecorators#getDispatcher} method.
|
|
718
|
+
*
|
|
719
|
+
* **Note**: This method also activates the automatic external link decorator if enabled with
|
|
720
|
+
* {@link module:link/linkconfig~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}.
|
|
721
|
+
*/ _enableAutomaticDecorators(automaticDecoratorDefinitions) {
|
|
681
722
|
const editor = this.editor;
|
|
682
723
|
// Store automatic decorators in the command instance as we do the same with manual decorators.
|
|
683
724
|
// Thanks to that, `LinkImageEditing` plugin can re-use the same definitions.
|
|
@@ -701,14 +742,14 @@ class LinkEditing extends Plugin {
|
|
|
701
742
|
}
|
|
702
743
|
}
|
|
703
744
|
/**
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
745
|
+
* Processes an array of configured {@link module:link/linkconfig~LinkDecoratorManualDefinition manual decorators},
|
|
746
|
+
* transforms them into {@link module:link/utils/manualdecorator~ManualDecorator} instances and stores them in the
|
|
747
|
+
* {@link module:link/linkcommand~LinkCommand#manualDecorators} collection (a model for manual decorators state).
|
|
748
|
+
*
|
|
749
|
+
* Also registers an {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement attribute-to-element}
|
|
750
|
+
* converter for each manual decorator and extends the {@link module:engine/model/schema~Schema model's schema}
|
|
751
|
+
* with adequate model attributes.
|
|
752
|
+
*/ _enableManualDecorators(manualDecoratorDefinitions) {
|
|
712
753
|
if (!manualDecoratorDefinitions.length) {
|
|
713
754
|
return;
|
|
714
755
|
}
|
|
@@ -756,9 +797,9 @@ class LinkEditing extends Plugin {
|
|
|
756
797
|
});
|
|
757
798
|
}
|
|
758
799
|
/**
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
800
|
+
* Attaches handlers for {@link module:engine/view/document~Document#event:enter} and
|
|
801
|
+
* {@link module:engine/view/document~Document#event:click} to enable link following.
|
|
802
|
+
*/ _enableLinkOpen() {
|
|
762
803
|
const editor = this.editor;
|
|
763
804
|
const view = editor.editing.view;
|
|
764
805
|
const viewDocument = view.document;
|
|
@@ -797,10 +838,10 @@ class LinkEditing extends Plugin {
|
|
|
797
838
|
});
|
|
798
839
|
}
|
|
799
840
|
/**
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
841
|
+
* Watches the DocumentSelection attribute changes and removes link decorator attributes when the linkHref attribute is removed.
|
|
842
|
+
*
|
|
843
|
+
* This is to ensure that there is no left-over link decorator attributes on the document selection that is no longer in a link.
|
|
844
|
+
*/ _enableSelectionAttributesFixer() {
|
|
804
845
|
const editor = this.editor;
|
|
805
846
|
const model = editor.model;
|
|
806
847
|
const selection = model.document.selection;
|
|
@@ -814,8 +855,8 @@ class LinkEditing extends Plugin {
|
|
|
814
855
|
});
|
|
815
856
|
}
|
|
816
857
|
/**
|
|
817
|
-
|
|
818
|
-
|
|
858
|
+
* Enables URL fixing on pasting.
|
|
859
|
+
*/ _enableClipboardIntegration() {
|
|
819
860
|
const editor = this.editor;
|
|
820
861
|
const model = editor.model;
|
|
821
862
|
const defaultProtocol = this.editor.config.get('link.defaultProtocol');
|
|
@@ -834,15 +875,6 @@ class LinkEditing extends Plugin {
|
|
|
834
875
|
});
|
|
835
876
|
});
|
|
836
877
|
}
|
|
837
|
-
/**
|
|
838
|
-
* @inheritDoc
|
|
839
|
-
*/ constructor(editor){
|
|
840
|
-
super(editor);
|
|
841
|
-
editor.config.define('link', {
|
|
842
|
-
allowCreatingEmptyLinks: false,
|
|
843
|
-
addTargetToExternalLinks: false
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
878
|
}
|
|
847
879
|
/**
|
|
848
880
|
* Make the selection free of link-related model attributes.
|
|
@@ -861,22 +893,105 @@ class LinkEditing extends Plugin {
|
|
|
861
893
|
return textAttributes.filter((attribute)=>attribute.startsWith('link'));
|
|
862
894
|
}
|
|
863
895
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
896
|
+
/**
|
|
897
|
+
* The link form view controller class.
|
|
898
|
+
*
|
|
899
|
+
* See {@link module:link/ui/linkformview~LinkFormView}.
|
|
900
|
+
*/ class LinkFormView extends View {
|
|
901
|
+
/**
|
|
902
|
+
* Tracks information about DOM focus in the form.
|
|
903
|
+
*/ focusTracker = new FocusTracker();
|
|
904
|
+
/**
|
|
905
|
+
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
|
|
906
|
+
*/ keystrokes = new KeystrokeHandler();
|
|
907
|
+
/**
|
|
908
|
+
* The URL input view.
|
|
909
|
+
*/ urlInputView;
|
|
910
|
+
/**
|
|
911
|
+
* The Save button view.
|
|
912
|
+
*/ saveButtonView;
|
|
913
|
+
/**
|
|
914
|
+
* The Cancel button view.
|
|
915
|
+
*/ cancelButtonView;
|
|
916
|
+
/**
|
|
917
|
+
* A collection of {@link module:ui/button/switchbuttonview~SwitchButtonView},
|
|
918
|
+
* which corresponds to {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators}
|
|
919
|
+
* configured in the editor.
|
|
920
|
+
*/ _manualDecoratorSwitches;
|
|
921
|
+
/**
|
|
922
|
+
* A collection of child views in the form.
|
|
923
|
+
*/ children;
|
|
924
|
+
/**
|
|
925
|
+
* An array of form validators used by {@link #isValid}.
|
|
926
|
+
*/ _validators;
|
|
927
|
+
/**
|
|
928
|
+
* A collection of views that can be focused in the form.
|
|
929
|
+
*/ _focusables = new ViewCollection();
|
|
930
|
+
/**
|
|
931
|
+
* Helps cycling over {@link #_focusables} in the form.
|
|
932
|
+
*/ _focusCycler;
|
|
933
|
+
/**
|
|
934
|
+
* Creates an instance of the {@link module:link/ui/linkformview~LinkFormView} class.
|
|
935
|
+
*
|
|
936
|
+
* Also see {@link #render}.
|
|
937
|
+
*
|
|
938
|
+
* @param locale The localization services instance.
|
|
939
|
+
* @param linkCommand Reference to {@link module:link/linkcommand~LinkCommand}.
|
|
940
|
+
* @param validators Form validators used by {@link #isValid}.
|
|
941
|
+
*/ constructor(locale, linkCommand, validators){
|
|
942
|
+
super(locale);
|
|
943
|
+
const t = locale.t;
|
|
944
|
+
this._validators = validators;
|
|
945
|
+
this.urlInputView = this._createUrlInput();
|
|
946
|
+
this.saveButtonView = this._createButton(t('Save'), icons.check, 'ck-button-save');
|
|
947
|
+
this.saveButtonView.type = 'submit';
|
|
948
|
+
this.cancelButtonView = this._createButton(t('Cancel'), icons.cancel, 'ck-button-cancel', 'cancel');
|
|
949
|
+
this._manualDecoratorSwitches = this._createManualDecoratorSwitches(linkCommand);
|
|
950
|
+
this.children = this._createFormChildren(linkCommand.manualDecorators);
|
|
951
|
+
this._focusCycler = new FocusCycler({
|
|
952
|
+
focusables: this._focusables,
|
|
953
|
+
focusTracker: this.focusTracker,
|
|
954
|
+
keystrokeHandler: this.keystrokes,
|
|
955
|
+
actions: {
|
|
956
|
+
// Navigate form fields backwards using the Shift + Tab keystroke.
|
|
957
|
+
focusPrevious: 'shift + tab',
|
|
958
|
+
// Navigate form fields forwards using the Tab key.
|
|
959
|
+
focusNext: 'tab'
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
const classList = [
|
|
963
|
+
'ck',
|
|
964
|
+
'ck-link-form',
|
|
965
|
+
'ck-responsive-form'
|
|
966
|
+
];
|
|
967
|
+
if (linkCommand.manualDecorators.length) {
|
|
968
|
+
classList.push('ck-link-form_layout-vertical', 'ck-vertical-form');
|
|
969
|
+
}
|
|
970
|
+
this.setTemplate({
|
|
971
|
+
tag: 'form',
|
|
972
|
+
attributes: {
|
|
973
|
+
class: classList,
|
|
974
|
+
// https://github.com/ckeditor/ckeditor5-link/issues/90
|
|
975
|
+
tabindex: '-1'
|
|
976
|
+
},
|
|
977
|
+
children: this.children
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Obtains the state of the {@link module:ui/button/switchbuttonview~SwitchButtonView switch buttons} representing
|
|
982
|
+
* {@link module:link/linkcommand~LinkCommand#manualDecorators manual link decorators}
|
|
983
|
+
* in the {@link module:link/ui/linkformview~LinkFormView}.
|
|
984
|
+
*
|
|
985
|
+
* @returns Key-value pairs, where the key is the name of the decorator and the value is its state.
|
|
986
|
+
*/ getDecoratorSwitchesState() {
|
|
872
987
|
return Array.from(this._manualDecoratorSwitches).reduce((accumulator, switchButton)=>{
|
|
873
988
|
accumulator[switchButton.name] = switchButton.isOn;
|
|
874
989
|
return accumulator;
|
|
875
990
|
}, {});
|
|
876
991
|
}
|
|
877
992
|
/**
|
|
878
|
-
|
|
879
|
-
|
|
993
|
+
* @inheritDoc
|
|
994
|
+
*/ render() {
|
|
880
995
|
super.render();
|
|
881
996
|
submitHandler({
|
|
882
997
|
view: this
|
|
@@ -897,20 +1012,20 @@ class LinkFormView extends View {
|
|
|
897
1012
|
this.keystrokes.listenTo(this.element);
|
|
898
1013
|
}
|
|
899
1014
|
/**
|
|
900
|
-
|
|
901
|
-
|
|
1015
|
+
* @inheritDoc
|
|
1016
|
+
*/ destroy() {
|
|
902
1017
|
super.destroy();
|
|
903
1018
|
this.focusTracker.destroy();
|
|
904
1019
|
this.keystrokes.destroy();
|
|
905
1020
|
}
|
|
906
1021
|
/**
|
|
907
|
-
|
|
908
|
-
|
|
1022
|
+
* Focuses the fist {@link #_focusables} in the form.
|
|
1023
|
+
*/ focus() {
|
|
909
1024
|
this._focusCycler.focusFirst();
|
|
910
1025
|
}
|
|
911
1026
|
/**
|
|
912
|
-
|
|
913
|
-
|
|
1027
|
+
* Validates the form and returns `false` when some fields are invalid.
|
|
1028
|
+
*/ isValid() {
|
|
914
1029
|
this.resetFormStatus();
|
|
915
1030
|
for (const validator of this._validators){
|
|
916
1031
|
const errorText = validator(this);
|
|
@@ -924,32 +1039,33 @@ class LinkFormView extends View {
|
|
|
924
1039
|
return true;
|
|
925
1040
|
}
|
|
926
1041
|
/**
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1042
|
+
* Cleans up the supplementary error and information text of the {@link #urlInputView}
|
|
1043
|
+
* bringing them back to the state when the form has been displayed for the first time.
|
|
1044
|
+
*
|
|
1045
|
+
* See {@link #isValid}.
|
|
1046
|
+
*/ resetFormStatus() {
|
|
932
1047
|
this.urlInputView.errorText = null;
|
|
933
1048
|
}
|
|
934
1049
|
/**
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1050
|
+
* Creates a labeled input view.
|
|
1051
|
+
*
|
|
1052
|
+
* @returns Labeled field view instance.
|
|
1053
|
+
*/ _createUrlInput() {
|
|
939
1054
|
const t = this.locale.t;
|
|
940
1055
|
const labeledInput = new LabeledFieldView(this.locale, createLabeledInputText);
|
|
1056
|
+
labeledInput.fieldView.inputMode = 'url';
|
|
941
1057
|
labeledInput.label = t('Link URL');
|
|
942
1058
|
return labeledInput;
|
|
943
1059
|
}
|
|
944
1060
|
/**
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1061
|
+
* Creates a button view.
|
|
1062
|
+
*
|
|
1063
|
+
* @param label The button label.
|
|
1064
|
+
* @param icon The button icon.
|
|
1065
|
+
* @param className The additional button CSS class name.
|
|
1066
|
+
* @param eventName An event name that the `ButtonView#execute` event will be delegated to.
|
|
1067
|
+
* @returns The button view instance.
|
|
1068
|
+
*/ _createButton(label, icon, className, eventName) {
|
|
953
1069
|
const button = new ButtonView(this.locale);
|
|
954
1070
|
button.set({
|
|
955
1071
|
label,
|
|
@@ -967,12 +1083,12 @@ class LinkFormView extends View {
|
|
|
967
1083
|
return button;
|
|
968
1084
|
}
|
|
969
1085
|
/**
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1086
|
+
* Populates {@link module:ui/viewcollection~ViewCollection} of {@link module:ui/button/switchbuttonview~SwitchButtonView}
|
|
1087
|
+
* made based on {@link module:link/linkcommand~LinkCommand#manualDecorators}.
|
|
1088
|
+
*
|
|
1089
|
+
* @param linkCommand A reference to the link command.
|
|
1090
|
+
* @returns ViewCollection of switch buttons.
|
|
1091
|
+
*/ _createManualDecoratorSwitches(linkCommand) {
|
|
976
1092
|
const switches = this.createCollection();
|
|
977
1093
|
for (const manualDecorator of linkCommand.manualDecorators){
|
|
978
1094
|
const switchButton = new SwitchButtonView(this.locale);
|
|
@@ -995,16 +1111,16 @@ class LinkFormView extends View {
|
|
|
995
1111
|
return switches;
|
|
996
1112
|
}
|
|
997
1113
|
/**
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1114
|
+
* Populates the {@link #children} collection of the form.
|
|
1115
|
+
*
|
|
1116
|
+
* If {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators} are configured in the editor, it creates an
|
|
1117
|
+
* additional `View` wrapping all {@link #_manualDecoratorSwitches} switch buttons corresponding
|
|
1118
|
+
* to these decorators.
|
|
1119
|
+
*
|
|
1120
|
+
* @param manualDecorators A reference to
|
|
1121
|
+
* the collection of manual decorators stored in the link command.
|
|
1122
|
+
* @returns The children of link form view.
|
|
1123
|
+
*/ _createFormChildren(manualDecorators) {
|
|
1008
1124
|
const children = this.createCollection();
|
|
1009
1125
|
children.add(this.urlInputView);
|
|
1010
1126
|
if (manualDecorators.length) {
|
|
@@ -1038,81 +1154,89 @@ class LinkFormView extends View {
|
|
|
1038
1154
|
return children;
|
|
1039
1155
|
}
|
|
1040
1156
|
/**
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1157
|
+
* The native DOM `value` of the {@link #urlInputView} element.
|
|
1158
|
+
*
|
|
1159
|
+
* **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
|
|
1160
|
+
* which works one way only and may not represent the actual state of the component in the DOM.
|
|
1161
|
+
*/ get url() {
|
|
1046
1162
|
const { element } = this.urlInputView.fieldView;
|
|
1047
1163
|
if (!element) {
|
|
1048
1164
|
return null;
|
|
1049
1165
|
}
|
|
1050
1166
|
return element.value.trim();
|
|
1051
1167
|
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
var unlinkIcon = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184zm4.919 10.562-1.414 1.414a.75.75 0 1 1-1.06-1.06l1.414-1.415-1.415-1.414a.75.75 0 0 1 1.061-1.06l1.414 1.414 1.414-1.415a.75.75 0 0 1 1.061 1.061l-1.414 1.414 1.414 1.415a.75.75 0 0 1-1.06 1.06l-1.415-1.414z\"/></svg>";
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* The link actions view class. This view displays the link preview, allows
|
|
1174
|
+
* unlinking or editing the link.
|
|
1175
|
+
*/ class LinkActionsView extends View {
|
|
1176
|
+
/**
|
|
1177
|
+
* Tracks information about DOM focus in the actions.
|
|
1178
|
+
*/ focusTracker = new FocusTracker();
|
|
1179
|
+
/**
|
|
1180
|
+
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
|
|
1181
|
+
*/ keystrokes = new KeystrokeHandler();
|
|
1052
1182
|
/**
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1183
|
+
* The href preview view.
|
|
1184
|
+
*/ previewButtonView;
|
|
1185
|
+
/**
|
|
1186
|
+
* The unlink button view.
|
|
1187
|
+
*/ unlinkButtonView;
|
|
1188
|
+
/**
|
|
1189
|
+
* The edit link button view.
|
|
1190
|
+
*/ editButtonView;
|
|
1191
|
+
/**
|
|
1192
|
+
* A collection of views that can be focused in the view.
|
|
1193
|
+
*/ _focusables = new ViewCollection();
|
|
1194
|
+
/**
|
|
1195
|
+
* Helps cycling over {@link #_focusables} in the view.
|
|
1196
|
+
*/ _focusCycler;
|
|
1197
|
+
_linkConfig;
|
|
1198
|
+
/**
|
|
1199
|
+
* @inheritDoc
|
|
1200
|
+
*/ constructor(locale, linkConfig = {}){
|
|
1061
1201
|
super(locale);
|
|
1062
|
-
/**
|
|
1063
|
-
* Tracks information about DOM focus in the form.
|
|
1064
|
-
*/ this.focusTracker = new FocusTracker();
|
|
1065
|
-
/**
|
|
1066
|
-
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
|
|
1067
|
-
*/ this.keystrokes = new KeystrokeHandler();
|
|
1068
|
-
/**
|
|
1069
|
-
* A collection of views that can be focused in the form.
|
|
1070
|
-
*/ this._focusables = new ViewCollection();
|
|
1071
1202
|
const t = locale.t;
|
|
1072
|
-
this.
|
|
1073
|
-
this.
|
|
1074
|
-
this.
|
|
1075
|
-
this.
|
|
1076
|
-
this.
|
|
1077
|
-
this._manualDecoratorSwitches = this._createManualDecoratorSwitches(linkCommand);
|
|
1078
|
-
this.children = this._createFormChildren(linkCommand.manualDecorators);
|
|
1203
|
+
this.previewButtonView = this._createPreviewButton();
|
|
1204
|
+
this.unlinkButtonView = this._createButton(t('Unlink'), unlinkIcon, 'unlink');
|
|
1205
|
+
this.editButtonView = this._createButton(t('Edit link'), icons.pencil, 'edit');
|
|
1206
|
+
this.set('href', undefined);
|
|
1207
|
+
this._linkConfig = linkConfig;
|
|
1079
1208
|
this._focusCycler = new FocusCycler({
|
|
1080
1209
|
focusables: this._focusables,
|
|
1081
1210
|
focusTracker: this.focusTracker,
|
|
1082
1211
|
keystrokeHandler: this.keystrokes,
|
|
1083
1212
|
actions: {
|
|
1084
|
-
// Navigate
|
|
1213
|
+
// Navigate fields backwards using the Shift + Tab keystroke.
|
|
1085
1214
|
focusPrevious: 'shift + tab',
|
|
1086
|
-
// Navigate
|
|
1215
|
+
// Navigate fields forwards using the Tab key.
|
|
1087
1216
|
focusNext: 'tab'
|
|
1088
1217
|
}
|
|
1089
1218
|
});
|
|
1090
|
-
const classList = [
|
|
1091
|
-
'ck',
|
|
1092
|
-
'ck-link-form',
|
|
1093
|
-
'ck-responsive-form'
|
|
1094
|
-
];
|
|
1095
|
-
if (linkCommand.manualDecorators.length) {
|
|
1096
|
-
classList.push('ck-link-form_layout-vertical', 'ck-vertical-form');
|
|
1097
|
-
}
|
|
1098
1219
|
this.setTemplate({
|
|
1099
|
-
tag: '
|
|
1220
|
+
tag: 'div',
|
|
1100
1221
|
attributes: {
|
|
1101
|
-
class:
|
|
1222
|
+
class: [
|
|
1223
|
+
'ck',
|
|
1224
|
+
'ck-link-actions',
|
|
1225
|
+
'ck-responsive-form'
|
|
1226
|
+
],
|
|
1102
1227
|
// https://github.com/ckeditor/ckeditor5-link/issues/90
|
|
1103
1228
|
tabindex: '-1'
|
|
1104
1229
|
},
|
|
1105
|
-
children:
|
|
1230
|
+
children: [
|
|
1231
|
+
this.previewButtonView,
|
|
1232
|
+
this.editButtonView,
|
|
1233
|
+
this.unlinkButtonView
|
|
1234
|
+
]
|
|
1106
1235
|
});
|
|
1107
1236
|
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
var unlinkIcon = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184zm4.919 10.562-1.414 1.414a.75.75 0 1 1-1.06-1.06l1.414-1.415-1.415-1.414a.75.75 0 0 1 1.061-1.06l1.414 1.414 1.414-1.415a.75.75 0 0 1 1.061 1.061l-1.414 1.414 1.414 1.415a.75.75 0 0 1-1.06 1.06l-1.415-1.414z\"/></svg>";
|
|
1111
|
-
|
|
1112
|
-
class LinkActionsView extends View {
|
|
1113
1237
|
/**
|
|
1114
|
-
|
|
1115
|
-
|
|
1238
|
+
* @inheritDoc
|
|
1239
|
+
*/ render() {
|
|
1116
1240
|
super.render();
|
|
1117
1241
|
const childViews = [
|
|
1118
1242
|
this.previewButtonView,
|
|
@@ -1129,25 +1253,25 @@ class LinkActionsView extends View {
|
|
|
1129
1253
|
this.keystrokes.listenTo(this.element);
|
|
1130
1254
|
}
|
|
1131
1255
|
/**
|
|
1132
|
-
|
|
1133
|
-
|
|
1256
|
+
* @inheritDoc
|
|
1257
|
+
*/ destroy() {
|
|
1134
1258
|
super.destroy();
|
|
1135
1259
|
this.focusTracker.destroy();
|
|
1136
1260
|
this.keystrokes.destroy();
|
|
1137
1261
|
}
|
|
1138
1262
|
/**
|
|
1139
|
-
|
|
1140
|
-
|
|
1263
|
+
* Focuses the fist {@link #_focusables} in the actions.
|
|
1264
|
+
*/ focus() {
|
|
1141
1265
|
this._focusCycler.focusFirst();
|
|
1142
1266
|
}
|
|
1143
1267
|
/**
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1268
|
+
* Creates a button view.
|
|
1269
|
+
*
|
|
1270
|
+
* @param label The button label.
|
|
1271
|
+
* @param icon The button icon.
|
|
1272
|
+
* @param eventName An event name that the `ButtonView#execute` event will be delegated to.
|
|
1273
|
+
* @returns The button view instance.
|
|
1274
|
+
*/ _createButton(label, icon, eventName) {
|
|
1151
1275
|
const button = new ButtonView(this.locale);
|
|
1152
1276
|
button.set({
|
|
1153
1277
|
label,
|
|
@@ -1158,10 +1282,10 @@ class LinkActionsView extends View {
|
|
|
1158
1282
|
return button;
|
|
1159
1283
|
}
|
|
1160
1284
|
/**
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1285
|
+
* Creates a link href preview button.
|
|
1286
|
+
*
|
|
1287
|
+
* @returns The button view instance.
|
|
1288
|
+
*/ _createPreviewButton() {
|
|
1165
1289
|
const button = new ButtonView(this.locale);
|
|
1166
1290
|
const bind = this.bindTemplate;
|
|
1167
1291
|
const t = this.t;
|
|
@@ -1188,75 +1312,41 @@ class LinkActionsView extends View {
|
|
|
1188
1312
|
button.template.eventListeners = {};
|
|
1189
1313
|
return button;
|
|
1190
1314
|
}
|
|
1191
|
-
/**
|
|
1192
|
-
* @inheritDoc
|
|
1193
|
-
*/ constructor(locale, linkConfig = {}){
|
|
1194
|
-
super(locale);
|
|
1195
|
-
/**
|
|
1196
|
-
* Tracks information about DOM focus in the actions.
|
|
1197
|
-
*/ this.focusTracker = new FocusTracker();
|
|
1198
|
-
/**
|
|
1199
|
-
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
|
|
1200
|
-
*/ this.keystrokes = new KeystrokeHandler();
|
|
1201
|
-
/**
|
|
1202
|
-
* A collection of views that can be focused in the view.
|
|
1203
|
-
*/ this._focusables = new ViewCollection();
|
|
1204
|
-
const t = locale.t;
|
|
1205
|
-
this.previewButtonView = this._createPreviewButton();
|
|
1206
|
-
this.unlinkButtonView = this._createButton(t('Unlink'), unlinkIcon, 'unlink');
|
|
1207
|
-
this.editButtonView = this._createButton(t('Edit link'), icons.pencil, 'edit');
|
|
1208
|
-
this.set('href', undefined);
|
|
1209
|
-
this._linkConfig = linkConfig;
|
|
1210
|
-
this._focusCycler = new FocusCycler({
|
|
1211
|
-
focusables: this._focusables,
|
|
1212
|
-
focusTracker: this.focusTracker,
|
|
1213
|
-
keystrokeHandler: this.keystrokes,
|
|
1214
|
-
actions: {
|
|
1215
|
-
// Navigate fields backwards using the Shift + Tab keystroke.
|
|
1216
|
-
focusPrevious: 'shift + tab',
|
|
1217
|
-
// Navigate fields forwards using the Tab key.
|
|
1218
|
-
focusNext: 'tab'
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
this.setTemplate({
|
|
1222
|
-
tag: 'div',
|
|
1223
|
-
attributes: {
|
|
1224
|
-
class: [
|
|
1225
|
-
'ck',
|
|
1226
|
-
'ck-link-actions',
|
|
1227
|
-
'ck-responsive-form'
|
|
1228
|
-
],
|
|
1229
|
-
// https://github.com/ckeditor/ckeditor5-link/issues/90
|
|
1230
|
-
tabindex: '-1'
|
|
1231
|
-
},
|
|
1232
|
-
children: [
|
|
1233
|
-
this.previewButtonView,
|
|
1234
|
-
this.editButtonView,
|
|
1235
|
-
this.unlinkButtonView
|
|
1236
|
-
]
|
|
1237
|
-
});
|
|
1238
|
-
}
|
|
1239
1315
|
}
|
|
1240
1316
|
|
|
1241
1317
|
var linkIcon = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z\"/></svg>";
|
|
1242
1318
|
|
|
1243
1319
|
const VISUAL_SELECTION_MARKER_NAME = 'link-ui';
|
|
1244
|
-
|
|
1320
|
+
/**
|
|
1321
|
+
* The link UI plugin. It introduces the `'link'` and `'unlink'` buttons and support for the <kbd>Ctrl+K</kbd> keystroke.
|
|
1322
|
+
*
|
|
1323
|
+
* It uses the
|
|
1324
|
+
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
|
|
1325
|
+
*/ class LinkUI extends Plugin {
|
|
1326
|
+
/**
|
|
1327
|
+
* The actions view displayed inside of the balloon.
|
|
1328
|
+
*/ actionsView = null;
|
|
1245
1329
|
/**
|
|
1246
|
-
|
|
1247
|
-
|
|
1330
|
+
* The form view displayed inside the balloon.
|
|
1331
|
+
*/ formView = null;
|
|
1332
|
+
/**
|
|
1333
|
+
* The contextual balloon plugin instance.
|
|
1334
|
+
*/ _balloon;
|
|
1335
|
+
/**
|
|
1336
|
+
* @inheritDoc
|
|
1337
|
+
*/ static get requires() {
|
|
1248
1338
|
return [
|
|
1249
1339
|
ContextualBalloon
|
|
1250
1340
|
];
|
|
1251
1341
|
}
|
|
1252
1342
|
/**
|
|
1253
|
-
|
|
1254
|
-
|
|
1343
|
+
* @inheritDoc
|
|
1344
|
+
*/ static get pluginName() {
|
|
1255
1345
|
return 'LinkUI';
|
|
1256
1346
|
}
|
|
1257
1347
|
/**
|
|
1258
|
-
|
|
1259
|
-
|
|
1348
|
+
* @inheritDoc
|
|
1349
|
+
*/ init() {
|
|
1260
1350
|
const editor = this.editor;
|
|
1261
1351
|
const t = this.editor.t;
|
|
1262
1352
|
editor.editing.view.addObserver(ClickObserver);
|
|
@@ -1308,8 +1398,8 @@ class LinkUI extends Plugin {
|
|
|
1308
1398
|
});
|
|
1309
1399
|
}
|
|
1310
1400
|
/**
|
|
1311
|
-
|
|
1312
|
-
|
|
1401
|
+
* @inheritDoc
|
|
1402
|
+
*/ destroy() {
|
|
1313
1403
|
super.destroy();
|
|
1314
1404
|
// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
|
|
1315
1405
|
if (this.formView) {
|
|
@@ -1320,16 +1410,16 @@ class LinkUI extends Plugin {
|
|
|
1320
1410
|
}
|
|
1321
1411
|
}
|
|
1322
1412
|
/**
|
|
1323
|
-
|
|
1324
|
-
|
|
1413
|
+
* Creates views.
|
|
1414
|
+
*/ _createViews() {
|
|
1325
1415
|
this.actionsView = this._createActionsView();
|
|
1326
1416
|
this.formView = this._createFormView();
|
|
1327
1417
|
// Attach lifecycle actions to the the balloon.
|
|
1328
1418
|
this._enableUserBalloonInteractions();
|
|
1329
1419
|
}
|
|
1330
1420
|
/**
|
|
1331
|
-
|
|
1332
|
-
|
|
1421
|
+
* Creates the {@link module:link/ui/linkactionsview~LinkActionsView} instance.
|
|
1422
|
+
*/ _createActionsView() {
|
|
1333
1423
|
const editor = this.editor;
|
|
1334
1424
|
const actionsView = new LinkActionsView(editor.locale, editor.config.get('link'));
|
|
1335
1425
|
const linkCommand = editor.commands.get('link');
|
|
@@ -1359,8 +1449,8 @@ class LinkUI extends Plugin {
|
|
|
1359
1449
|
return actionsView;
|
|
1360
1450
|
}
|
|
1361
1451
|
/**
|
|
1362
|
-
|
|
1363
|
-
|
|
1452
|
+
* Creates the {@link module:link/ui/linkformview~LinkFormView} instance.
|
|
1453
|
+
*/ _createFormView() {
|
|
1364
1454
|
const editor = this.editor;
|
|
1365
1455
|
const linkCommand = editor.commands.get('link');
|
|
1366
1456
|
const defaultProtocol = editor.config.get('link.defaultProtocol');
|
|
@@ -1395,9 +1485,9 @@ class LinkUI extends Plugin {
|
|
|
1395
1485
|
return formView;
|
|
1396
1486
|
}
|
|
1397
1487
|
/**
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1488
|
+
* Creates a toolbar Link button. Clicking this button will show
|
|
1489
|
+
* a {@link #_balloon} attached to the selection.
|
|
1490
|
+
*/ _createToolbarLinkButton() {
|
|
1401
1491
|
const editor = this.editor;
|
|
1402
1492
|
const linkCommand = editor.commands.get('link');
|
|
1403
1493
|
editor.ui.componentFactory.add('link', ()=>{
|
|
@@ -1414,8 +1504,8 @@ class LinkUI extends Plugin {
|
|
|
1414
1504
|
});
|
|
1415
1505
|
}
|
|
1416
1506
|
/**
|
|
1417
|
-
|
|
1418
|
-
|
|
1507
|
+
* Creates a button for link command to use either in toolbar or in menu bar.
|
|
1508
|
+
*/ _createButton(ButtonClass) {
|
|
1419
1509
|
const editor = this.editor;
|
|
1420
1510
|
const locale = editor.locale;
|
|
1421
1511
|
const command = editor.commands.get('link');
|
|
@@ -1432,9 +1522,9 @@ class LinkUI extends Plugin {
|
|
|
1432
1522
|
return view;
|
|
1433
1523
|
}
|
|
1434
1524
|
/**
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1525
|
+
* Attaches actions that control whether the balloon panel containing the
|
|
1526
|
+
* {@link #formView} should be displayed.
|
|
1527
|
+
*/ _enableBalloonActivators() {
|
|
1438
1528
|
const editor = this.editor;
|
|
1439
1529
|
const viewDocument = editor.editing.view.document;
|
|
1440
1530
|
// Handle click on view document and show panel when selection is placed inside the link element.
|
|
@@ -1456,9 +1546,9 @@ class LinkUI extends Plugin {
|
|
|
1456
1546
|
});
|
|
1457
1547
|
}
|
|
1458
1548
|
/**
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1549
|
+
* Attaches actions that control whether the balloon panel containing the
|
|
1550
|
+
* {@link #formView} is visible or not.
|
|
1551
|
+
*/ _enableUserBalloonInteractions() {
|
|
1462
1552
|
// Focus the form if the balloon is visible and the Tab key has been pressed.
|
|
1463
1553
|
this.editor.keystrokes.set('Tab', (data, cancel)=>{
|
|
1464
1554
|
if (this._areActionsVisible && !this.actionsView.focusTracker.isFocused) {
|
|
@@ -1489,10 +1579,10 @@ class LinkUI extends Plugin {
|
|
|
1489
1579
|
});
|
|
1490
1580
|
}
|
|
1491
1581
|
/**
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1582
|
+
* Adds the {@link #actionsView} to the {@link #_balloon}.
|
|
1583
|
+
*
|
|
1584
|
+
* @internal
|
|
1585
|
+
*/ _addActionsView() {
|
|
1496
1586
|
if (!this.actionsView) {
|
|
1497
1587
|
this._createViews();
|
|
1498
1588
|
}
|
|
@@ -1505,8 +1595,8 @@ class LinkUI extends Plugin {
|
|
|
1505
1595
|
});
|
|
1506
1596
|
}
|
|
1507
1597
|
/**
|
|
1508
|
-
|
|
1509
|
-
|
|
1598
|
+
* Adds the {@link #formView} to the {@link #_balloon}.
|
|
1599
|
+
*/ _addFormView() {
|
|
1510
1600
|
if (!this.formView) {
|
|
1511
1601
|
this._createViews();
|
|
1512
1602
|
}
|
|
@@ -1535,12 +1625,12 @@ class LinkUI extends Plugin {
|
|
|
1535
1625
|
this.formView.enableCssTransitions();
|
|
1536
1626
|
}
|
|
1537
1627
|
/**
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1628
|
+
* Closes the form view. Decides whether the balloon should be hidden completely or if the action view should be shown. This is
|
|
1629
|
+
* decided upon the link command value (which has a value if the document selection is in the link).
|
|
1630
|
+
*
|
|
1631
|
+
* Additionally, if any {@link module:link/linkconfig~LinkConfig#decorators} are defined in the editor configuration, the state of
|
|
1632
|
+
* switch buttons responsible for manual decorator handling is restored.
|
|
1633
|
+
*/ _closeFormView() {
|
|
1544
1634
|
const linkCommand = this.editor.commands.get('link');
|
|
1545
1635
|
// Restore manual decorator states to represent the current model state. This case is important to reset the switch buttons
|
|
1546
1636
|
// when the user cancels the editing form.
|
|
@@ -1552,8 +1642,8 @@ class LinkUI extends Plugin {
|
|
|
1552
1642
|
}
|
|
1553
1643
|
}
|
|
1554
1644
|
/**
|
|
1555
|
-
|
|
1556
|
-
|
|
1645
|
+
* Removes the {@link #formView} from the {@link #_balloon}.
|
|
1646
|
+
*/ _removeFormView() {
|
|
1557
1647
|
if (this._isFormInPanel) {
|
|
1558
1648
|
// Blur the input element before removing it from DOM to prevent issues in some browsers.
|
|
1559
1649
|
// See https://github.com/ckeditor/ckeditor5/issues/1501.
|
|
@@ -1568,10 +1658,10 @@ class LinkUI extends Plugin {
|
|
|
1568
1658
|
}
|
|
1569
1659
|
}
|
|
1570
1660
|
/**
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1661
|
+
* Shows the correct UI type. It is either {@link #formView} or {@link #actionsView}.
|
|
1662
|
+
*
|
|
1663
|
+
* @internal
|
|
1664
|
+
*/ _showUI(forceVisible = false) {
|
|
1575
1665
|
if (!this.formView) {
|
|
1576
1666
|
this._createViews();
|
|
1577
1667
|
}
|
|
@@ -1602,10 +1692,10 @@ class LinkUI extends Plugin {
|
|
|
1602
1692
|
this._startUpdatingUI();
|
|
1603
1693
|
}
|
|
1604
1694
|
/**
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1695
|
+
* Removes the {@link #formView} from the {@link #_balloon}.
|
|
1696
|
+
*
|
|
1697
|
+
* See {@link #_addFormView}, {@link #_addActionsView}.
|
|
1698
|
+
*/ _hideUI() {
|
|
1609
1699
|
if (!this._isUIInPanel) {
|
|
1610
1700
|
return;
|
|
1611
1701
|
}
|
|
@@ -1622,11 +1712,11 @@ class LinkUI extends Plugin {
|
|
|
1622
1712
|
this._hideFakeVisualSelection();
|
|
1623
1713
|
}
|
|
1624
1714
|
/**
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1715
|
+
* Makes the UI react to the {@link module:ui/editorui/editorui~EditorUI#event:update} event to
|
|
1716
|
+
* reposition itself when the editor UI should be refreshed.
|
|
1717
|
+
*
|
|
1718
|
+
* See: {@link #_hideUI} to learn when the UI stops reacting to the `update` event.
|
|
1719
|
+
*/ _startUpdatingUI() {
|
|
1630
1720
|
const editor = this.editor;
|
|
1631
1721
|
const viewDocument = editor.editing.view.document;
|
|
1632
1722
|
let prevSelectedLink = this._getSelectedLinkElement();
|
|
@@ -1662,40 +1752,40 @@ class LinkUI extends Plugin {
|
|
|
1662
1752
|
this.listenTo(this._balloon, 'change:visibleView', update);
|
|
1663
1753
|
}
|
|
1664
1754
|
/**
|
|
1665
|
-
|
|
1666
|
-
|
|
1755
|
+
* Returns `true` when {@link #formView} is in the {@link #_balloon}.
|
|
1756
|
+
*/ get _isFormInPanel() {
|
|
1667
1757
|
return !!this.formView && this._balloon.hasView(this.formView);
|
|
1668
1758
|
}
|
|
1669
1759
|
/**
|
|
1670
|
-
|
|
1671
|
-
|
|
1760
|
+
* Returns `true` when {@link #actionsView} is in the {@link #_balloon}.
|
|
1761
|
+
*/ get _areActionsInPanel() {
|
|
1672
1762
|
return !!this.actionsView && this._balloon.hasView(this.actionsView);
|
|
1673
1763
|
}
|
|
1674
1764
|
/**
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1765
|
+
* Returns `true` when {@link #actionsView} is in the {@link #_balloon} and it is
|
|
1766
|
+
* currently visible.
|
|
1767
|
+
*/ get _areActionsVisible() {
|
|
1678
1768
|
return !!this.actionsView && this._balloon.visibleView === this.actionsView;
|
|
1679
1769
|
}
|
|
1680
1770
|
/**
|
|
1681
|
-
|
|
1682
|
-
|
|
1771
|
+
* Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon}.
|
|
1772
|
+
*/ get _isUIInPanel() {
|
|
1683
1773
|
return this._isFormInPanel || this._areActionsInPanel;
|
|
1684
1774
|
}
|
|
1685
1775
|
/**
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1776
|
+
* Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon} and it is
|
|
1777
|
+
* currently visible.
|
|
1778
|
+
*/ get _isUIVisible() {
|
|
1689
1779
|
const visibleView = this._balloon.visibleView;
|
|
1690
1780
|
return !!this.formView && visibleView == this.formView || this._areActionsVisible;
|
|
1691
1781
|
}
|
|
1692
1782
|
/**
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1783
|
+
* Returns positioning options for the {@link #_balloon}. They control the way the balloon is attached
|
|
1784
|
+
* to the target element or selection.
|
|
1785
|
+
*
|
|
1786
|
+
* If the selection is collapsed and inside a link element, the panel will be attached to the
|
|
1787
|
+
* entire link element. Otherwise, it will be attached to the selection.
|
|
1788
|
+
*/ _getBalloonPositionData() {
|
|
1699
1789
|
const view = this.editor.editing.view;
|
|
1700
1790
|
const model = this.editor.model;
|
|
1701
1791
|
const viewDocument = view.document;
|
|
@@ -1722,14 +1812,14 @@ class LinkUI extends Plugin {
|
|
|
1722
1812
|
};
|
|
1723
1813
|
}
|
|
1724
1814
|
/**
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1815
|
+
* Returns the link {@link module:engine/view/attributeelement~AttributeElement} under
|
|
1816
|
+
* the {@link module:engine/view/document~Document editing view's} selection or `null`
|
|
1817
|
+
* if there is none.
|
|
1818
|
+
*
|
|
1819
|
+
* **Note**: For a non–collapsed selection, the link element is returned when **fully**
|
|
1820
|
+
* selected and the **only** element within the selection boundaries, or when
|
|
1821
|
+
* a linked widget is selected.
|
|
1822
|
+
*/ _getSelectedLinkElement() {
|
|
1733
1823
|
const view = this.editor.editing.view;
|
|
1734
1824
|
const selection = view.document.selection;
|
|
1735
1825
|
const selectedElement = selection.getSelectedElement();
|
|
@@ -1754,10 +1844,10 @@ class LinkUI extends Plugin {
|
|
|
1754
1844
|
}
|
|
1755
1845
|
}
|
|
1756
1846
|
/**
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1847
|
+
* Displays a fake visual selection when the contextual balloon is displayed.
|
|
1848
|
+
*
|
|
1849
|
+
* This adds a 'link-ui' marker into the document that is rendered as a highlight on selected text fragment.
|
|
1850
|
+
*/ _showFakeVisualSelection() {
|
|
1761
1851
|
const model = this.editor.model;
|
|
1762
1852
|
model.change((writer)=>{
|
|
1763
1853
|
const range = model.document.selection.getFirstRange();
|
|
@@ -1786,8 +1876,8 @@ class LinkUI extends Plugin {
|
|
|
1786
1876
|
});
|
|
1787
1877
|
}
|
|
1788
1878
|
/**
|
|
1789
|
-
|
|
1790
|
-
|
|
1879
|
+
* Hides the fake visual selection created in {@link #_showFakeVisualSelection}.
|
|
1880
|
+
*/ _hideFakeVisualSelection() {
|
|
1791
1881
|
const model = this.editor.model;
|
|
1792
1882
|
if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
|
|
1793
1883
|
model.change((writer)=>{
|
|
@@ -1795,15 +1885,6 @@ class LinkUI extends Plugin {
|
|
|
1795
1885
|
});
|
|
1796
1886
|
}
|
|
1797
1887
|
}
|
|
1798
|
-
constructor(){
|
|
1799
|
-
super(...arguments);
|
|
1800
|
-
/**
|
|
1801
|
-
* The actions view displayed inside of the balloon.
|
|
1802
|
-
*/ this.actionsView = null;
|
|
1803
|
-
/**
|
|
1804
|
-
* The form view displayed inside the balloon.
|
|
1805
|
-
*/ this.formView = null;
|
|
1806
|
-
}
|
|
1807
1888
|
}
|
|
1808
1889
|
/**
|
|
1809
1890
|
* Returns a link element if there's one among the ancestors of the provided `Position`.
|
|
@@ -1851,23 +1932,25 @@ const URL_REG_EXP = new RegExp(// Group 1: Line start or after a space.
|
|
|
1851
1932
|
'((?![-_])(?:[-_a-z0-9\\u00a1-\\uffff]{1,63}\\.))+' + // TLD identifier name.
|
|
1852
1933
|
'(?:[a-z\\u00a1-\\uffff]{2,63})' + ')' + ')$', 'i');
|
|
1853
1934
|
const URL_GROUP_IN_MATCH = 2;
|
|
1854
|
-
|
|
1935
|
+
/**
|
|
1936
|
+
* The autolink plugin.
|
|
1937
|
+
*/ class AutoLink extends Plugin {
|
|
1855
1938
|
/**
|
|
1856
|
-
|
|
1857
|
-
|
|
1939
|
+
* @inheritDoc
|
|
1940
|
+
*/ static get requires() {
|
|
1858
1941
|
return [
|
|
1859
1942
|
Delete,
|
|
1860
1943
|
LinkEditing
|
|
1861
1944
|
];
|
|
1862
1945
|
}
|
|
1863
1946
|
/**
|
|
1864
|
-
|
|
1865
|
-
|
|
1947
|
+
* @inheritDoc
|
|
1948
|
+
*/ static get pluginName() {
|
|
1866
1949
|
return 'AutoLink';
|
|
1867
1950
|
}
|
|
1868
1951
|
/**
|
|
1869
|
-
|
|
1870
|
-
|
|
1952
|
+
* @inheritDoc
|
|
1953
|
+
*/ init() {
|
|
1871
1954
|
const editor = this.editor;
|
|
1872
1955
|
const selection = editor.model.document.selection;
|
|
1873
1956
|
selection.on('change:range', ()=>{
|
|
@@ -1877,17 +1960,17 @@ class AutoLink extends Plugin {
|
|
|
1877
1960
|
this._enableTypingHandling();
|
|
1878
1961
|
}
|
|
1879
1962
|
/**
|
|
1880
|
-
|
|
1881
|
-
|
|
1963
|
+
* @inheritDoc
|
|
1964
|
+
*/ afterInit() {
|
|
1882
1965
|
this._enableEnterHandling();
|
|
1883
1966
|
this._enableShiftEnterHandling();
|
|
1884
1967
|
this._enablePasteLinking();
|
|
1885
1968
|
}
|
|
1886
1969
|
/**
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1970
|
+
* For given position, returns a range that includes the whole link that contains the position.
|
|
1971
|
+
*
|
|
1972
|
+
* If position is not inside a link, returns `null`.
|
|
1973
|
+
*/ _expandLinkRange(model, position) {
|
|
1891
1974
|
if (position.textNode && position.textNode.hasAttribute('linkHref')) {
|
|
1892
1975
|
return findAttributeRange(position, 'linkHref', position.textNode.getAttribute('linkHref'), model);
|
|
1893
1976
|
} else {
|
|
@@ -1895,8 +1978,8 @@ class AutoLink extends Plugin {
|
|
|
1895
1978
|
}
|
|
1896
1979
|
}
|
|
1897
1980
|
/**
|
|
1898
|
-
|
|
1899
|
-
|
|
1981
|
+
* Extends the document selection to includes all links that intersects with given `selectedRange`.
|
|
1982
|
+
*/ _selectEntireLinks(writer, selectedRange) {
|
|
1900
1983
|
const editor = this.editor;
|
|
1901
1984
|
const model = editor.model;
|
|
1902
1985
|
const selection = model.document.selection;
|
|
@@ -1912,8 +1995,8 @@ class AutoLink extends Plugin {
|
|
|
1912
1995
|
}
|
|
1913
1996
|
}
|
|
1914
1997
|
/**
|
|
1915
|
-
|
|
1916
|
-
|
|
1998
|
+
* Enables autolinking on pasting a URL when some content is selected.
|
|
1999
|
+
*/ _enablePasteLinking() {
|
|
1917
2000
|
const editor = this.editor;
|
|
1918
2001
|
const model = editor.model;
|
|
1919
2002
|
const selection = model.document.selection;
|
|
@@ -1948,8 +2031,8 @@ class AutoLink extends Plugin {
|
|
|
1948
2031
|
});
|
|
1949
2032
|
}
|
|
1950
2033
|
/**
|
|
1951
|
-
|
|
1952
|
-
|
|
2034
|
+
* Enables autolinking on typing.
|
|
2035
|
+
*/ _enableTypingHandling() {
|
|
1953
2036
|
const editor = this.editor;
|
|
1954
2037
|
const watcher = new TextWatcher(editor.model, (text)=>{
|
|
1955
2038
|
// 1. Detect <kbd>Space</kbd> after a text with a potential link.
|
|
@@ -1977,8 +2060,8 @@ class AutoLink extends Plugin {
|
|
|
1977
2060
|
watcher.bind('isEnabled').to(this);
|
|
1978
2061
|
}
|
|
1979
2062
|
/**
|
|
1980
|
-
|
|
1981
|
-
|
|
2063
|
+
* Enables autolinking on the <kbd>Enter</kbd> key.
|
|
2064
|
+
*/ _enableEnterHandling() {
|
|
1982
2065
|
const editor = this.editor;
|
|
1983
2066
|
const model = editor.model;
|
|
1984
2067
|
const enterCommand = editor.commands.get('enter');
|
|
@@ -1995,8 +2078,8 @@ class AutoLink extends Plugin {
|
|
|
1995
2078
|
});
|
|
1996
2079
|
}
|
|
1997
2080
|
/**
|
|
1998
|
-
|
|
1999
|
-
|
|
2081
|
+
* Enables autolinking on the <kbd>Shift</kbd>+<kbd>Enter</kbd> keyboard shortcut.
|
|
2082
|
+
*/ _enableShiftEnterHandling() {
|
|
2000
2083
|
const editor = this.editor;
|
|
2001
2084
|
const model = editor.model;
|
|
2002
2085
|
const shiftEnterCommand = editor.commands.get('shiftEnter');
|
|
@@ -2010,8 +2093,8 @@ class AutoLink extends Plugin {
|
|
|
2010
2093
|
});
|
|
2011
2094
|
}
|
|
2012
2095
|
/**
|
|
2013
|
-
|
|
2014
|
-
|
|
2096
|
+
* Checks if the passed range contains a linkable text.
|
|
2097
|
+
*/ _checkAndApplyAutoLinkOnRange(rangeToCheck) {
|
|
2015
2098
|
const model = this.editor.model;
|
|
2016
2099
|
const { text, range } = getLastTextLine(rangeToCheck, model);
|
|
2017
2100
|
const url = getUrlAtTextEnd(text);
|
|
@@ -2021,11 +2104,11 @@ class AutoLink extends Plugin {
|
|
|
2021
2104
|
}
|
|
2022
2105
|
}
|
|
2023
2106
|
/**
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2107
|
+
* Applies a link on a given range if the link should be applied.
|
|
2108
|
+
*
|
|
2109
|
+
* @param url The URL to link.
|
|
2110
|
+
* @param range The text range to apply the link attribute to.
|
|
2111
|
+
*/ _applyAutoLink(url, range) {
|
|
2029
2112
|
const model = this.editor.model;
|
|
2030
2113
|
const defaultProtocol = this.editor.config.get('link.defaultProtocol');
|
|
2031
2114
|
const fullUrl = addLinkProtocolIfApplicable(url, defaultProtocol);
|
|
@@ -2035,11 +2118,11 @@ class AutoLink extends Plugin {
|
|
|
2035
2118
|
this._persistAutoLink(fullUrl, range);
|
|
2036
2119
|
}
|
|
2037
2120
|
/**
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2121
|
+
* Enqueues autolink changes in the model.
|
|
2122
|
+
*
|
|
2123
|
+
* @param url The URL to link.
|
|
2124
|
+
* @param range The text range to apply the link attribute to.
|
|
2125
|
+
*/ _persistAutoLink(url, range) {
|
|
2043
2126
|
const model = this.editor.model;
|
|
2044
2127
|
const deletePlugin = this.editor.plugins.get('Delete');
|
|
2045
2128
|
// Enqueue change to make undo step.
|
|
@@ -2067,10 +2150,15 @@ function linkIsAlreadySet(range) {
|
|
|
2067
2150
|
return !!item && item.hasAttribute('linkHref');
|
|
2068
2151
|
}
|
|
2069
2152
|
|
|
2070
|
-
|
|
2153
|
+
/**
|
|
2154
|
+
* The link plugin.
|
|
2155
|
+
*
|
|
2156
|
+
* This is a "glue" plugin that loads the {@link module:link/linkediting~LinkEditing link editing feature}
|
|
2157
|
+
* and {@link module:link/linkui~LinkUI link UI feature}.
|
|
2158
|
+
*/ class Link extends Plugin {
|
|
2071
2159
|
/**
|
|
2072
|
-
|
|
2073
|
-
|
|
2160
|
+
* @inheritDoc
|
|
2161
|
+
*/ static get requires() {
|
|
2074
2162
|
return [
|
|
2075
2163
|
LinkEditing,
|
|
2076
2164
|
LinkUI,
|
|
@@ -2078,16 +2166,21 @@ class Link extends Plugin {
|
|
|
2078
2166
|
];
|
|
2079
2167
|
}
|
|
2080
2168
|
/**
|
|
2081
|
-
|
|
2082
|
-
|
|
2169
|
+
* @inheritDoc
|
|
2170
|
+
*/ static get pluginName() {
|
|
2083
2171
|
return 'Link';
|
|
2084
2172
|
}
|
|
2085
2173
|
}
|
|
2086
2174
|
|
|
2087
|
-
|
|
2175
|
+
/**
|
|
2176
|
+
* The link image engine feature.
|
|
2177
|
+
*
|
|
2178
|
+
* It accepts the `linkHref="url"` attribute in the model for the {@link module:image/image~Image `<imageBlock>`} element
|
|
2179
|
+
* which allows linking images.
|
|
2180
|
+
*/ class LinkImageEditing extends Plugin {
|
|
2088
2181
|
/**
|
|
2089
|
-
|
|
2090
|
-
|
|
2182
|
+
* @inheritDoc
|
|
2183
|
+
*/ static get requires() {
|
|
2091
2184
|
return [
|
|
2092
2185
|
'ImageEditing',
|
|
2093
2186
|
'ImageUtils',
|
|
@@ -2095,13 +2188,13 @@ class LinkImageEditing extends Plugin {
|
|
|
2095
2188
|
];
|
|
2096
2189
|
}
|
|
2097
2190
|
/**
|
|
2098
|
-
|
|
2099
|
-
|
|
2191
|
+
* @inheritDoc
|
|
2192
|
+
*/ static get pluginName() {
|
|
2100
2193
|
return 'LinkImageEditing';
|
|
2101
2194
|
}
|
|
2102
2195
|
/**
|
|
2103
|
-
|
|
2104
|
-
|
|
2196
|
+
* @inheritDoc
|
|
2197
|
+
*/ afterInit() {
|
|
2105
2198
|
const editor = this.editor;
|
|
2106
2199
|
const schema = editor.model.schema;
|
|
2107
2200
|
if (editor.plugins.has('ImageBlockEditing')) {
|
|
@@ -2118,9 +2211,9 @@ class LinkImageEditing extends Plugin {
|
|
|
2118
2211
|
this._enableManualDecorators();
|
|
2119
2212
|
}
|
|
2120
2213
|
/**
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2214
|
+
* Processes {@link module:link/linkconfig~LinkDecoratorAutomaticDefinition automatic decorators} definitions and
|
|
2215
|
+
* attaches proper converters that will work when linking an image.`
|
|
2216
|
+
*/ _enableAutomaticDecorators() {
|
|
2124
2217
|
const editor = this.editor;
|
|
2125
2218
|
const command = editor.commands.get('link');
|
|
2126
2219
|
const automaticDecorators = command.automaticDecorators;
|
|
@@ -2129,9 +2222,9 @@ class LinkImageEditing extends Plugin {
|
|
|
2129
2222
|
}
|
|
2130
2223
|
}
|
|
2131
2224
|
/**
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2225
|
+
* Processes transformed {@link module:link/utils/manualdecorator~ManualDecorator} instances and attaches proper converters
|
|
2226
|
+
* that will work when linking an image.
|
|
2227
|
+
*/ _enableManualDecorators() {
|
|
2135
2228
|
const editor = this.editor;
|
|
2136
2229
|
const command = editor.commands.get('link');
|
|
2137
2230
|
for (const decorator of command.manualDecorators){
|
|
@@ -2324,10 +2417,15 @@ class LinkImageEditing extends Plugin {
|
|
|
2324
2417
|
};
|
|
2325
2418
|
}
|
|
2326
2419
|
|
|
2327
|
-
|
|
2420
|
+
/**
|
|
2421
|
+
* The link image UI plugin.
|
|
2422
|
+
*
|
|
2423
|
+
* This plugin provides the `'linkImage'` button that can be displayed in the {@link module:image/imagetoolbar~ImageToolbar}.
|
|
2424
|
+
* It can be used to wrap images in links.
|
|
2425
|
+
*/ class LinkImageUI extends Plugin {
|
|
2328
2426
|
/**
|
|
2329
|
-
|
|
2330
|
-
|
|
2427
|
+
* @inheritDoc
|
|
2428
|
+
*/ static get requires() {
|
|
2331
2429
|
return [
|
|
2332
2430
|
LinkEditing,
|
|
2333
2431
|
LinkUI,
|
|
@@ -2335,13 +2433,13 @@ class LinkImageUI extends Plugin {
|
|
|
2335
2433
|
];
|
|
2336
2434
|
}
|
|
2337
2435
|
/**
|
|
2338
|
-
|
|
2339
|
-
|
|
2436
|
+
* @inheritDoc
|
|
2437
|
+
*/ static get pluginName() {
|
|
2340
2438
|
return 'LinkImageUI';
|
|
2341
2439
|
}
|
|
2342
2440
|
/**
|
|
2343
|
-
|
|
2344
|
-
|
|
2441
|
+
* @inheritDoc
|
|
2442
|
+
*/ init() {
|
|
2345
2443
|
const editor = this.editor;
|
|
2346
2444
|
const viewDocument = editor.editing.view.document;
|
|
2347
2445
|
this.listenTo(viewDocument, 'click', (evt, data)=>{
|
|
@@ -2358,12 +2456,12 @@ class LinkImageUI extends Plugin {
|
|
|
2358
2456
|
this._createToolbarLinkImageButton();
|
|
2359
2457
|
}
|
|
2360
2458
|
/**
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2459
|
+
* Creates a `LinkImageUI` button view.
|
|
2460
|
+
*
|
|
2461
|
+
* Clicking this button shows a {@link module:link/linkui~LinkUI#_balloon} attached to the selection.
|
|
2462
|
+
* When an image is already linked, the view shows {@link module:link/linkui~LinkUI#actionsView} or
|
|
2463
|
+
* {@link module:link/linkui~LinkUI#formView} if it is not.
|
|
2464
|
+
*/ _createToolbarLinkImageButton() {
|
|
2367
2465
|
const editor = this.editor;
|
|
2368
2466
|
const t = editor.t;
|
|
2369
2467
|
editor.ui.componentFactory.add('linkImage', (locale)=>{
|
|
@@ -2393,27 +2491,32 @@ class LinkImageUI extends Plugin {
|
|
|
2393
2491
|
});
|
|
2394
2492
|
}
|
|
2395
2493
|
/**
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2494
|
+
* Returns true if a linked image (either block or inline) is the only selected element
|
|
2495
|
+
* in the model document.
|
|
2496
|
+
*/ _isSelectedLinkedImage(selection) {
|
|
2399
2497
|
const selectedModelElement = selection.getSelectedElement();
|
|
2400
2498
|
const imageUtils = this.editor.plugins.get('ImageUtils');
|
|
2401
2499
|
return imageUtils.isImage(selectedModelElement) && selectedModelElement.hasAttribute('linkHref');
|
|
2402
2500
|
}
|
|
2403
2501
|
}
|
|
2404
2502
|
|
|
2405
|
-
|
|
2503
|
+
/**
|
|
2504
|
+
* The `LinkImage` plugin.
|
|
2505
|
+
*
|
|
2506
|
+
* This is a "glue" plugin that loads the {@link module:link/linkimageediting~LinkImageEditing link image editing feature}
|
|
2507
|
+
* and {@link module:link/linkimageui~LinkImageUI link image UI feature}.
|
|
2508
|
+
*/ class LinkImage extends Plugin {
|
|
2406
2509
|
/**
|
|
2407
|
-
|
|
2408
|
-
|
|
2510
|
+
* @inheritDoc
|
|
2511
|
+
*/ static get requires() {
|
|
2409
2512
|
return [
|
|
2410
2513
|
LinkImageEditing,
|
|
2411
2514
|
LinkImageUI
|
|
2412
2515
|
];
|
|
2413
2516
|
}
|
|
2414
2517
|
/**
|
|
2415
|
-
|
|
2416
|
-
|
|
2518
|
+
* @inheritDoc
|
|
2519
|
+
*/ static get pluginName() {
|
|
2417
2520
|
return 'LinkImage';
|
|
2418
2521
|
}
|
|
2419
2522
|
}
|