diff options
Diffstat (limited to 'mobile/android/chrome/content/Linkify.js')
-rw-r--r-- | mobile/android/chrome/content/Linkify.js | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/mobile/android/chrome/content/Linkify.js b/mobile/android/chrome/content/Linkify.js new file mode 100644 index 000000000..3c757cc18 --- /dev/null +++ b/mobile/android/chrome/content/Linkify.js @@ -0,0 +1,108 @@ +/* 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; + else + return NodeFilter.FILTER_REJECT; + } + + let nodeWalker = aDoc.createTreeWalker(aDoc.body, NodeFilter.SHOW_TEXT, filterNode, false); + let parseNode = function() { + 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); + } + }.bind(this); + + this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT); + } +}; |