mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	The extracted logic is in linkifier.js. We have decided to name it linkifier.js instead of realm_linkifier.js because in future when we will add stream-level linkifiers, we'll likely want them to be managed by this same file.
		
			
				
	
	
		
			110 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import marked from "../third/marked/lib/marked";
 | 
						|
 | 
						|
import * as blueslip from "./blueslip";
 | 
						|
 | 
						|
const linkifier_map = new Map();
 | 
						|
export let linkifier_list = [];
 | 
						|
 | 
						|
function handleLinkifier(pattern, matches) {
 | 
						|
    let url = linkifier_map.get(pattern);
 | 
						|
 | 
						|
    let current_group = 1;
 | 
						|
 | 
						|
    for (const match of matches) {
 | 
						|
        const back_ref = "\\" + current_group;
 | 
						|
        url = url.replace(back_ref, match);
 | 
						|
        current_group += 1;
 | 
						|
    }
 | 
						|
 | 
						|
    return url;
 | 
						|
}
 | 
						|
 | 
						|
function python_to_js_linkifier(pattern, url) {
 | 
						|
    // Converts a python named-group regex to a javascript-compatible numbered
 | 
						|
    // group regex... with a regex!
 | 
						|
    const named_group_re = /\(?P<([^>]+?)>/g;
 | 
						|
    let match = named_group_re.exec(pattern);
 | 
						|
    let current_group = 1;
 | 
						|
    while (match) {
 | 
						|
        const name = match[1];
 | 
						|
        // Replace named group with regular matching group
 | 
						|
        pattern = pattern.replace("(?P<" + name + ">", "(");
 | 
						|
        // Replace named reference in URL to numbered reference
 | 
						|
        url = url.replace("%(" + name + ")s", "\\" + current_group);
 | 
						|
 | 
						|
        // Reset the RegExp state
 | 
						|
        named_group_re.lastIndex = 0;
 | 
						|
        match = named_group_re.exec(pattern);
 | 
						|
 | 
						|
        current_group += 1;
 | 
						|
    }
 | 
						|
    // Convert any python in-regex flags to RegExp flags
 | 
						|
    let js_flags = "g";
 | 
						|
    const inline_flag_re = /\(\?([Limsux]+)\)/;
 | 
						|
    match = inline_flag_re.exec(pattern);
 | 
						|
 | 
						|
    // JS regexes only support i (case insensitivity) and m (multiline)
 | 
						|
    // flags, so keep those and ignore the rest
 | 
						|
    if (match) {
 | 
						|
        const py_flags = match[1].split("");
 | 
						|
 | 
						|
        for (const flag of py_flags) {
 | 
						|
            if ("im".includes(flag)) {
 | 
						|
                js_flags += flag;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        pattern = pattern.replace(inline_flag_re, "");
 | 
						|
    }
 | 
						|
    // Ideally we should have been checking that linkifiers
 | 
						|
    // begin with certain characters but since there is no
 | 
						|
    // support for negative lookbehind in javascript, we check
 | 
						|
    // for this condition in `contains_backend_only_syntax()`
 | 
						|
    // function. If the condition is satisfied then the message
 | 
						|
    // is rendered locally, otherwise, we return false there and
 | 
						|
    // message is rendered on the backend which has proper support
 | 
						|
    // for negative lookbehind.
 | 
						|
    pattern = pattern + /(?!\w)/.source;
 | 
						|
    let final_regex = null;
 | 
						|
    try {
 | 
						|
        final_regex = new RegExp(pattern, js_flags);
 | 
						|
    } catch (error) {
 | 
						|
        // We have an error computing the generated regex syntax.
 | 
						|
        // We'll ignore this linkifier for now, but log this
 | 
						|
        // failure for debugging later.
 | 
						|
        blueslip.error("python_to_js_linkifier: " + error.message);
 | 
						|
    }
 | 
						|
    return [final_regex, url];
 | 
						|
}
 | 
						|
 | 
						|
export function update_linkifier_rules(linkifiers) {
 | 
						|
    // Update the marked parser with our particular set of linkifiers
 | 
						|
    linkifier_map.clear();
 | 
						|
    linkifier_list = [];
 | 
						|
 | 
						|
    const marked_rules = [];
 | 
						|
 | 
						|
    for (const linkifier of linkifiers) {
 | 
						|
        const [regex, final_url] = python_to_js_linkifier(linkifier.pattern, linkifier.url_format);
 | 
						|
        if (!regex) {
 | 
						|
            // Skip any linkifiers that could not be converted
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        linkifier_map.set(regex, final_url);
 | 
						|
        linkifier_list.push({
 | 
						|
            pattern: regex,
 | 
						|
            url_format: final_url,
 | 
						|
        });
 | 
						|
        marked_rules.push(regex);
 | 
						|
    }
 | 
						|
 | 
						|
    marked.InlineLexer.rules.zulip.linkifiers = marked_rules;
 | 
						|
}
 | 
						|
 | 
						|
export function initialize(linkifiers) {
 | 
						|
    update_linkifier_rules(linkifiers);
 | 
						|
 | 
						|
    marked.setOptions({linkifierHandler: handleLinkifier});
 | 
						|
}
 |