File: //home/arjun/projects/buyercall/node_modules/@ckeditor/ckeditor5-link/src/utils.js
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module link/utils
*/
/* global window */
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;
// Simplified email test - should be run over previously found URL.
const EMAIL_REG_EXP = /^[\S]+@((?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.))+(?:[a-z\u00a1-\uffff]{2,})$/i;
// The regex checks for the protocol syntax ('xxxx://' or 'xxxx:')
// or non-word characters at the beginning of the link ('/', '#' etc.).
const PROTOCOL_REG_EXP = /^((\w+:(\/{2,})?)|(\W))/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 a 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` can be linked (the element allows the `linkHref` attribute).
*
* @params {module:engine/model/element~Element|null} element
* @params {module:engine/model/schema~Schema} schema
* @returns {Boolean}
*/
export function isLinkableElement( element, schema ) {
if ( !element ) {
return false;
}
return schema.checkAttribute( element.name, 'linkHref' );
}
/**
* Returns `true` if the specified `value` is an email.
*
* @params {String} value
* @returns {Boolean}
*/
export function isEmail( value ) {
return EMAIL_REG_EXP.test( value );
}
/**
* Adds the protocol prefix to the specified `link` when:
*
* * it does not contain it already, and there is a {@link module:link/link~LinkConfig#defaultProtocol `defaultProtocol` }
* configuration value provided,
* * or the link is an email address.
*
*
* @params {String} link
* @params {String} defaultProtocol
* @returns {Boolean}
*/
export function addLinkProtocolIfApplicable( link, defaultProtocol ) {
const protocol = isEmail( link ) ? 'mailto:' : defaultProtocol;
const isProtocolNeeded = !!protocol && !PROTOCOL_REG_EXP.test( link );
return link && isProtocolNeeded ? protocol + link : link;
}
/**
* Opens the link in a new browser tab.
*
* @param {String} link
*/
export function openLink( link ) {
window.open( link, '_blank', 'noopener' );
}