utils.js 4.3 KB

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