[IMP] web_editor: remove global flag from URL regex
A Regex object with the 'g' flag is stateful [1]. As the URL_REGEX and URL_REGEX_WITH_INFOS objects are exported, code that calls their `exec` method would be changing their `lastIndex` property, affecting future `exec` calls in different code sections. This commit removes the 'g' flag from the URL regexes, in order to allow us to: - avoid side effects of one call to `exec` to subsequent calls, - remove the `getUrlsInfosInString` utility function, - use `match` instead of `exec` for having results containing the capturing groups (see [2]). [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex#avoiding_side_effects [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match#return_value task-3520513 Part-of: odoo/odoo#136497
This commit is contained in:
@@ -46,7 +46,6 @@ import {
|
||||
isUnremovable,
|
||||
fillEmpty,
|
||||
isEmptyBlock,
|
||||
getUrlsInfosInString,
|
||||
URL_REGEX,
|
||||
URL_REGEX_WITH_INFOS,
|
||||
isSelectionFormat,
|
||||
@@ -4594,16 +4593,15 @@ export class OdooEditor extends EventTarget {
|
||||
) {
|
||||
const textSliced = selection.anchorNode.textContent.slice(0, selection.anchorOffset);
|
||||
const textNodeSplitted = textSliced.split(/\s/);
|
||||
let potentialUrl = textNodeSplitted.pop();
|
||||
const lastWordMatch = potentialUrl.match(URL_REGEX_WITH_INFOS) && !potentialUrl.match(EMAIL_REGEX);
|
||||
const potentialUrl = textNodeSplitted.pop();
|
||||
const match = potentialUrl.match(URL_REGEX_WITH_INFOS);
|
||||
|
||||
if (lastWordMatch) {
|
||||
const matches = getUrlsInfosInString(textSliced);
|
||||
const match = matches[matches.length - 1];
|
||||
if (match && match[0] === potentialUrl && !EMAIL_REGEX.test(potentialUrl)) {
|
||||
const url = match[2] ? match[0] : 'http://' + match[0];
|
||||
const range = this.document.createRange();
|
||||
range.setStart(selection.anchorNode, match.index);
|
||||
range.setEnd(selection.anchorNode, match.index + match.length);
|
||||
const link = this._createLink(range.extractContents().textContent, match.url);
|
||||
range.setStart(selection.anchorNode, selection.anchorOffset - match[0].length);
|
||||
range.setEnd(selection.anchorNode, selection.anchorOffset);
|
||||
const link = this._createLink(range.extractContents().textContent, url);
|
||||
range.insertNode(link);
|
||||
const container = link.parentElement;
|
||||
const offset = childNodeIndex(link) + 1;
|
||||
|
||||
@@ -94,11 +94,7 @@ export function deduceURLfromText(text, link) {
|
||||
return match[1] ? match[0] : 'mailto:' + match[0];
|
||||
}
|
||||
// Check for http link.
|
||||
// Regex with 'g' flag is stateful, reset lastIndex before and after using
|
||||
// exec.
|
||||
URL_REGEX_WITH_INFOS.lastIndex = 0;
|
||||
match = URL_REGEX_WITH_INFOS.exec(label);
|
||||
URL_REGEX_WITH_INFOS.lastIndex = 0;
|
||||
match = label.match(URL_REGEX_WITH_INFOS);
|
||||
if (match && match[0] === label) {
|
||||
const currentHttpProtocol = (link?.href.match(/^http(s)?:\/\//gi) || [])[0];
|
||||
if (match[2]) {
|
||||
|
||||
@@ -51,8 +51,8 @@ const urlRegexBase = `|(?:[-a-zA-Z0-9@:%._\\+~#=]{1,64}\\.))[-a-zA-Z0-9@:%._\\+~
|
||||
const httpRegex = `(?:https?:\\/\\/)`;
|
||||
const httpCapturedRegex= `(https?:\\/\\/)`;
|
||||
|
||||
export const URL_REGEX = new RegExp(`((?:(?:${httpRegex}${urlRegexBase})`, 'gi');
|
||||
export const URL_REGEX_WITH_INFOS = new RegExp(`((?:(?:${httpCapturedRegex}${urlRegexBase})`, 'gi');
|
||||
export const URL_REGEX = new RegExp(`((?:(?:${httpRegex}${urlRegexBase})`, 'i');
|
||||
export const URL_REGEX_WITH_INFOS = new RegExp(`((?:(?:${httpCapturedRegex}${urlRegexBase})`, 'i');
|
||||
export const YOUTUBE_URL_GET_VIDEO_ID =
|
||||
/^(?:(?:https?:)?\/\/)?(?:(?:www|m)\.)?(?:youtube\.com|youtu\.be)(?:\/(?:[\w-]+\?v=|embed\/|v\/)?)([^\s?&#]+)(?:\S+)?$/i;
|
||||
export const EMAIL_REGEX = /^(mailto:)?[\w-.]+@(?:[\w-]+\.)+[\w-]{2,4}$/i;
|
||||
@@ -1520,26 +1520,6 @@ export function makeContentsInline(node) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of url infos for url matched in the given string.
|
||||
*
|
||||
* @param {String} string
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function getUrlsInfosInString(string) {
|
||||
let infos = [],
|
||||
match;
|
||||
while ((match = URL_REGEX_WITH_INFOS.exec(string))) {
|
||||
infos.push({
|
||||
url: match[2] ? match[0] : 'http://' + match[0],
|
||||
label: match[0],
|
||||
index: match.index,
|
||||
length: match[0].length,
|
||||
});
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
// optimize: use the parent Oid to speed up detection
|
||||
export function getOuid(node, optimize = false) {
|
||||
while (node && !isUnbreakable(node)) {
|
||||
|
||||
Reference in New Issue
Block a user