475.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import ClassicEditor from '/ckeditor5/editor-classic/classic.js';
  6. import Feature from '/ckeditor5/feature.js';
  7. import TreeWalker from '/ckeditor5/engine/model/treewalker.js';
  8. import Position from '/ckeditor5/engine/model/position.js';
  9. import Range from '/ckeditor5/engine/model/range.js';
  10. import LivePosition from '/ckeditor5/engine/model/liveposition.js';
  11. import buildModelConverter from '/ckeditor5/engine/conversion/buildmodelconverter.js';
  12. import BuildViewConverterFor from '/ckeditor5/engine/conversion/view-converter-builder.js';
  13. import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
  14. export default class Link extends Feature {
  15. init() {
  16. const editor = this.editor;
  17. const data = editor.data;
  18. const editing = editor.editing;
  19. // Allow bold attribute on all inline nodes.
  20. editor.document.schema.allow( { name: '$inline', attributes: [ 'link' ] } );
  21. // Build converter from model to view for data and editing pipelines.
  22. buildModelConverter().for( data.modelToView, editing.modelToView )
  23. .fromAttribute( 'link' )
  24. .toElement( ( href ) => new AttributeElement( 'a', { href } ) );
  25. // Build converter from view to model for data pipeline.
  26. BuildViewConverterFor( data.viewToModel )
  27. .fromElement( 'a' )
  28. .toAttribute( ( viewElement ) => ( { key: 'link', value: viewElement.getAttribute( 'href' ) } ) );
  29. }
  30. }
  31. const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;
  32. export default class AutoLinker extends Feature {
  33. init() {
  34. this.editor.document.on( 'change', ( event, type, changes, batch ) => {
  35. if ( type != 'insert' ) {
  36. return;
  37. }
  38. for ( let value of changes.range.getItems( { singleCharacters: true } ) ) {
  39. const walker = new TreeWalker( {
  40. direction: 'backward',
  41. startPosition: Position.createAfter( value )
  42. } );
  43. const currentValue = walker.next().value;
  44. const text = currentValue.item.data;
  45. if ( !text ) {
  46. return;
  47. }
  48. let matchedUrl = urlRegex.exec( text );
  49. if ( !matchedUrl ) {
  50. return;
  51. }
  52. const doc = this.editor.document;
  53. const url = matchedUrl[ 0 ];
  54. const offset = _getLastPathPart( currentValue.nextPosition.path ) + matchedUrl.index;
  55. const livePos = LivePosition.createFromParentAndOffset( currentValue.item.parent, offset );
  56. doc.enqueueChanges( () => {
  57. const urlRange = Range.createFromPositionAndShift( livePos, url.length );
  58. batch.setAttribute( urlRange, 'link', url );
  59. } );
  60. }
  61. } );
  62. }
  63. }
  64. function _getLastPathPart( path ) {
  65. return path[ path.length - 1 ];
  66. }
  67. ClassicEditor.create( document.querySelector( '#editor' ), {
  68. features: [ 'enter', 'typing', 'paragraph', 'undo', Link, AutoLinker ],
  69. toolbar: [ 'undo', 'redo' ]
  70. } );