Wednesday, November 23, 2022

javascript translate urls into links, but skipping things that are already links...

A while ago I posted a snippet of linky, and explaining how "changing the current highlighted text into a link" (heuristically figuring out if text to be made clickable or the URL itself) made more sense to me (for use on the backend of my blog) than either markdown-like approaches or dialogs with two input boxes.

But with my work flow, sometimes it was annoying to have to highlight the link in order to linkify it, and I realized a better UX would be "if I click "linky" and there is no text selected, change every https:// starting URL looking thing into a link." 

This stackoverflow was a good start, but assumed any URL wasn't already in a link - like if you ran it twice (or there were links elsewhere) you'd get <a href="<a href=" crap. 

But the regular expression to do it properly was tough. I got some help and ended up with (with a few tester function to start... and pardon the bad pre code ;-) )

function testLinkify(){

   console.log('starting test'); 

   test(`https://foo.com`, `<a href="https://foo.com"></a>`);

   test(`\nhttps://foo.com`,`\n<a href="https://foo.com"></a>`);

   test(`<a href="https://foo.com"></a>`,`<a href="https://foo.com"></a>`);

   test(`<a href="https://foo.com"></a> https://bar.com`,`<a href="https://foo.com"></a> <a href="https://bar.com"></a>`);

     test(`https://foo.com\nhttps://bar.com <a href="https://baz.com">BAZ</a>`,`<a href="https://foo.com"></a>\n<a href="https://bar.com"></a> <a href="https://baz.com">BAZ</a>`);

}

function test(input,expect){

  const testFunction = linkifyBareHttp;

  const output = testFunction(input);

  console.log (output === expect ? 'PASS':'FAIL');

      console.log(` INPUT: ${input}`);

  if(output !== expect) {

    console.log(`EXPECT: ${expect}`);

    console.log(`OUTPUT: ${output}`)

  }

}

 


function linkifyBareHttp(inputText){

    //URLs starting with http://, https://, or ftp://

    const replacePattern1 = /\b(?<!(\'|\"))(((https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]))/gim;

    return inputText.replace(replacePattern1, '<a href="$2"></a>');

}

    

testLinkify();


You can play with that at a codepen

(UPDATE: it was funny playing with my admittedly idiosyncratic UI, starting to use it for real. Turns out I had a workflow that expected to be able to click "linky" before typing anything and just get a place holder "<a href>". Also in theory I might hit linky before any textarea has the focus (it uses the rule of "less touched textarea is the 'current' one") so I special cased a few in the offchance I had started focusing yet... anyway...)     

No comments:

Post a Comment