/
utils.js
137 lines (118 loc) · 4.34 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module link/utils
*/
import { upperFirst } from 'lodash-es';
const ATTRIBUTE_WHITESPACES = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
const SAFE_URL = /^(?:(?:https?|ftps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.:-]|$))/i;
/**
* A keystroke used by the {@link module:link/linkui~LinkUI link UI feature}.
*/
export const LINK_KEYSTROKE = 'Ctrl+K';
/**
* Returns `true` if a given view node is the link element.
*
* @param {module:engine/view/node~Node} node
* @returns {Boolean}
*/
export function isLinkElement( node ) {
return node.is( 'attributeElement' ) && !!node.getCustomProperty( 'link' );
}
/**
* Creates link {@link module:engine/view/attributeelement~AttributeElement} with the provided `href` attribute.
*
* @param {String} href
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
* @returns {module:engine/view/attributeelement~AttributeElement}
*/
export function createLinkElement( href, { writer } ) {
// Priority 5 - https://github.com/ckeditor/ckeditor5-link/issues/121.
const linkElement = writer.createAttributeElement( 'a', { href }, { priority: 5 } );
writer.setCustomProperty( 'link', true, linkElement );
return linkElement;
}
/**
* Returns a safe URL based on a given value.
*
* A URL is considered safe if it is safe for the user (does not contain any malicious code).
*
* If a URL is considered unsafe, a simple `"#"` is returned.
*
* @protected
* @param {*} url
* @returns {String} Safe URL.
*/
export function ensureSafeUrl( url ) {
url = String( url );
return isSafeUrl( url ) ? url : '#';
}
// Checks whether the given URL is safe for the user (does not contain any malicious code).
//
// @param {String} url URL to check.
function isSafeUrl( url ) {
const normalizedUrl = url.replace( ATTRIBUTE_WHITESPACES, '' );
return normalizedUrl.match( SAFE_URL );
}
/**
* Returns the {@link module:link/link~LinkConfig#decorators `config.link.decorators`} configuration processed
* to respect the locale of the editor, i.e. to display the {@link module:link/link~LinkDecoratorManualDefinition label}
* in the correct language.
*
* **Note**: Only the few most commonly used labels are translated automatically. Other labels should be manually
* translated in the {@link module:link/link~LinkConfig#decorators `config.link.decorators`} configuration.
*
* @param {module:utils/locale~Locale#t} t shorthand for {@link module:utils/locale~Locale#t Locale#t}
* @param {Array.<module:link/link~LinkDecoratorDefinition>} The decorator reference
* where the label values should be localized.
* @returns {Array.<module:link/link~LinkDecoratorDefinition>}
*/
export function getLocalizedDecorators( t, decorators ) {
const localizedDecoratorsLabels = {
'Open in a new tab': t( 'Open in a new tab' ),
'Downloadable': t( 'Downloadable' )
};
decorators.forEach( decorator => {
if ( decorator.label && localizedDecoratorsLabels[ decorator.label ] ) {
decorator.label = localizedDecoratorsLabels[ decorator.label ];
}
return decorator;
} );
return decorators;
}
/**
* Converts an object with defined decorators to a normalized array of decorators. The `id` key is added for each decorator and
* is used as the attribute's name in the model.
*
* @param {Object.<String, module:link/link~LinkDecoratorDefinition>} decorators
* @returns {Array.<module:link/link~LinkDecoratorDefinition>}
*/
export function normalizeDecorators( decorators ) {
const retArray = [];
if ( decorators ) {
for ( const [ key, value ] of Object.entries( decorators ) ) {
const decorator = Object.assign(
{},
value,
{ id: `link${ upperFirst( key ) }` }
);
retArray.push( decorator );
}
}
return retArray;
}
/**
* Returns `true` if the specified `element` is an image and it can be linked (the element allows having the `linkHref` attribute).
*
* @params {module:engine/model/element~Element|null} element
* @params {module:engine/model/schema~Schema} schema
* @returns {Boolean}
*/
export function isImageAllowed( element, schema ) {
if ( !element ) {
return false;
}
return element.is( 'element', 'image' ) && schema.checkAttribute( 'image', 'linkHref' );
}