Doccano - Convert URLs to Clickable Hyperlinks

For the Doccano text annotator application: replace non-clickable urls with hyperlinks.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Doccano - Convert URLs to Clickable Hyperlinks
// @version      1.1.1
// @namespace    http://tampermonkey.net/
// @description  For the Doccano text annotator application: replace non-clickable urls with hyperlinks.
// @author       Kyle Nakamura
// @license      MIT
// @match        */projects/1/text-classification*
// @icon         https://doccano.herokuapp.com/static/_nuxt/img/icon.c360b38.png
// ==/UserScript==


// Global constants
const urlRegStr = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
    urlRegExp = new RegExp(urlRegStr, 'i'),
    urlMaxLength = 2083,
    loadtimeDelay = 200;


/**
 *  Check if a string is a valid URL/URI.
 *
 *  @param  {string}    str
 *  @return {boolean}
 */
function isURL(str) {
    return str.length < urlMaxLength && urlRegExp.test(str);
}


/**
 *  Wrap a string in an HTML <a> tag to create a clickable hyperlink.
 *
 *  @param  {string}    url
 *  @return {string}
 */
function makeHyperlink(url) {
    return `<a href="${url}" target="_blank">${url}</a>`;
}


function findAndReplaceUrl() {
    let hyperlinkCreated = false;

    // Iterate over all elements with the relevant classnames
    // (in case multiple such elements exist).
    document.querySelectorAll('.v-card__text.title.highlight').forEach(el => {
        // Extract first word from the element and trim extra whitespace.
        const url = el.innerHTML.split(',')[0].trim();

        // Insert the clickable hyperlink in place within `el`,
        // replacing only the non-clickable url.
        if (isURL(url)) {
            el.innerHTML = el.innerHTML.replace(url, makeHyperlink(url));
            hyperlinkCreated = true;
        }
    });

    return hyperlinkCreated;
}


// Repeat every 500 ms because the page never refreshes and
// therefore the script never executes twice within the same session.
const interval = setInterval(() => {
    // Cancel interval after a url was replaced on this page load.
    if (findAndReplaceUrl()) {
        clearInterval(interval);

        // Additionally, add event listeners to repeat
        // on every page navigation done via button presses.
        document.querySelectorAll('button').forEach(el => {
            el.addEventListener('click', () => {
                let eventInterval = setInterval(() => {
                    if (findAndReplaceUrl()) {
                        clearInterval(eventInterval);
                    }
                }, parseInt(loadtimeDelay / 5));
            });
        });
    };
}, loadtimeDelay);