forked from mirrors/gecko-dev
# ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D35914 --HG-- extra : source : eafb1052afc7712c969e57552da5affc63093e9e
127 lines
4.1 KiB
JavaScript
127 lines
4.1 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const LINKIFY_TIMEOUT = 0;
|
|
|
|
function Linkifier() {
|
|
this._linkifyTimer = null;
|
|
this._phoneRegex = /(?:\s|^)[\+]?(\(?\d{1,8}\)?)?([- ]+\(?\d{1,8}\)?)+( ?(x|ext) ?\d{1,3})?(?:\s|$)/g;
|
|
}
|
|
|
|
Linkifier.prototype = {
|
|
_buildAnchor: function(aDoc, aNumberText) {
|
|
let anchorNode = aDoc.createElement("a");
|
|
let cleanedText = "";
|
|
for (let i = 0; i < aNumberText.length; i++) {
|
|
let c = aNumberText.charAt(i);
|
|
if ((c >= "0" && c <= "9") || c == "+") {
|
|
// assuming there is only the leading '+'.
|
|
cleanedText += c;
|
|
}
|
|
}
|
|
anchorNode.setAttribute("href", "tel:" + cleanedText);
|
|
let nodeText = aDoc.createTextNode(aNumberText);
|
|
anchorNode.appendChild(nodeText);
|
|
return anchorNode;
|
|
},
|
|
|
|
_linkifyNodeNumbers: function(aNodeToProcess, aDoc) {
|
|
let parent = aNodeToProcess.parentNode;
|
|
let nodeText = aNodeToProcess.nodeValue;
|
|
|
|
// Replacing the original text node with a sequence of
|
|
// |text before number|anchor with number|text after number nodes.
|
|
// Each step a couple of (optional) text node and anchor node are appended.
|
|
let anchorNode = null;
|
|
let m = null;
|
|
let startIndex = 0;
|
|
let prevNode = null;
|
|
while ((m = this._phoneRegex.exec(nodeText))) {
|
|
anchorNode = this._buildAnchor(
|
|
aDoc,
|
|
nodeText.substr(m.index, m[0].length)
|
|
);
|
|
|
|
let textExistsBeforeNumber = m.index > startIndex;
|
|
let nodeToAdd = null;
|
|
if (textExistsBeforeNumber) {
|
|
nodeToAdd = aDoc.createTextNode(
|
|
nodeText.substr(startIndex, m.index - startIndex)
|
|
);
|
|
} else {
|
|
nodeToAdd = anchorNode;
|
|
}
|
|
|
|
if (!prevNode) {
|
|
// first time, need to replace the whole node with the first new one.
|
|
parent.replaceChild(nodeToAdd, aNodeToProcess);
|
|
} else {
|
|
parent.insertBefore(nodeToAdd, prevNode.nextSibling);
|
|
} // inserts after.
|
|
|
|
if (textExistsBeforeNumber) {
|
|
// if we added the text node before the anchor, we still need to add the anchor node.
|
|
parent.insertBefore(anchorNode, nodeToAdd.nextSibling);
|
|
}
|
|
|
|
// next nodes need to be appended to this node.
|
|
prevNode = anchorNode;
|
|
startIndex = m.index + m[0].length;
|
|
}
|
|
|
|
// if some text is remaining after the last anchor.
|
|
if (startIndex > 0 && startIndex < nodeText.length) {
|
|
let lastNode = aDoc.createTextNode(nodeText.substr(startIndex));
|
|
parent.insertBefore(lastNode, prevNode.nextSibling);
|
|
return lastNode;
|
|
}
|
|
return anchorNode;
|
|
},
|
|
|
|
linkifyNumbers: function(aDoc) {
|
|
// Removing any installed timer in case the page has changed and a previous timer is still running.
|
|
if (this._linkifyTimer) {
|
|
clearTimeout(this._linkifyTimer);
|
|
this._linkifyTimer = null;
|
|
}
|
|
|
|
let filterNode = function(node) {
|
|
if (
|
|
node.parentNode.tagName != "A" &&
|
|
node.parentNode.tagName != "SCRIPT" &&
|
|
node.parentNode.tagName != "NOSCRIPT" &&
|
|
node.parentNode.tagName != "STYLE" &&
|
|
node.parentNode.tagName != "APPLET" &&
|
|
node.parentNode.tagName != "TEXTAREA"
|
|
) {
|
|
return NodeFilter.FILTER_ACCEPT;
|
|
}
|
|
return NodeFilter.FILTER_REJECT;
|
|
};
|
|
|
|
let nodeWalker = aDoc.createTreeWalker(
|
|
aDoc.body,
|
|
NodeFilter.SHOW_TEXT,
|
|
filterNode,
|
|
false
|
|
);
|
|
let parseNode = () => {
|
|
let node = nodeWalker.nextNode();
|
|
if (!node) {
|
|
this._linkifyTimer = null;
|
|
return;
|
|
}
|
|
let lastAddedNode = this._linkifyNodeNumbers(node, aDoc);
|
|
// we assign a different timeout whether the node was processed or not.
|
|
if (lastAddedNode) {
|
|
nodeWalker.currentNode = lastAddedNode;
|
|
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
|
|
} else {
|
|
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
|
|
}
|
|
};
|
|
|
|
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
|
|
},
|
|
};
|