Skip to content

Instantly share code, notes, and snippets.

@javan
Last active April 10, 2024 21:17
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javan/7b0c99f43e67080c2380e8d30707f773 to your computer and use it in GitHub Desktop.
Save javan/7b0c99f43e67080c2380e8d30707f773 to your computer and use it in GitHub Desktop.
Trix auto-linker. Demo: http://codepen.io/javan/pen/YqeQdG
addEventListener "trix-initialize", (event) ->
new TrixAutoLinker event.target
class TrixAutoLinker
constructor: (@element) ->
{@editor} = @element
@element.addEventListener("trix-render", @autoLink)
@autoLink()
autoLink: =>
for {url, range} in @getURLsWithRanges()
unless @getHrefAtRange(range) is url
currentRange = @editor.getSelectedRange()
@editor.setSelectedRange(range)
if @editor.canActivateAttribute("href")
@editor.activateAttribute("href", url)
@editor.setSelectedRange(currentRange)
getDocument: ->
@editor.getDocument()
getDocumentString: ->
@getDocument().toString()
getHrefAtRange: (range) ->
@getDocument().getCommonAttributesAtRange(range).href
PATTERN = ///(https?://\S+\.\S+)\s///ig
getURLsWithRanges: ->
results = []
string = @getDocumentString()
while match = PATTERN.exec(string)
url = match[1]
if isValidURL(url)
position = match.index
range = [position, position + url.length]
results.push({url, range})
results
INPUT = document.createElement("input")
INPUT.type = "url"
INPUT.required = true
isValidURL = (value) ->
INPUT.value = value
INPUT.checkValidity()
@shabbir-ahmed
Copy link

It's working! But currently working for the only https but I want to work with http or www or .com anyone.

How to do this?

Thanks

@jarommadsen
Copy link

Converted this to a React hook:

import { useCallback, useEffect } from 'react';

const getHrefAtRange = (editor, range) => editor.getDocument().getCommonAttributesAtRange(range).href;

export const getURLsWithRanges = (string) => {
  const results = [];
  const pattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi;
  let match = pattern.exec(string);
  while (match !== null) {
    const url = match[1];
    if (isValidURL(url)) {
      const position = match.index;
      const range = [position, position + url.length];
      results.push({ url, range });
    }
    match = pattern.exec(string);
  }
  return results;
};

const isValidURL = (value) => {
  const input = document.createElement('input');
  input.type = 'url';
  input.required = true;
  input.value = value;
  return input.checkValidity();
};

const useTrixAutoLinker = (trixElement) => {
  useEffect(() => {
    if (!trixElement) return;
    trixElement.addEventListener('trix-render', autoLink);
    autoLink();

    // eslint-disable-next-line consistent-return
    return () => {
      trixElement.removeEventListener('trix-render', autoLink);
    };
  }, [trixElement]);

  const autoLink = useCallback(() => {
    setTimeout(() => {
      const urlsAndRanges = getURLsWithRanges(
        trixElement.editor.getDocument().toString()
      );
      urlsAndRanges.forEach(({ url, range }) => {
        const href = getHrefAtRange(trixElement.editor, range);
        if (href !== url) {
          const currentRange = trixElement.editor.getSelectedRange();
          trixElement.editor.setSelectedRange(range);
          if (trixElement.editor.canActivateAttribute('href')) {
            trixElement.editor.activateAttribute('href', url);
          }
          trixElement.editor.setSelectedRange(currentRange);
        }
      });
    }, 0);
  }, [trixElement, getURLsWithRanges, getHrefAtRange]);
};

export default useTrixAutoLinker;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment