1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/chrome/content/Linkify.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,108 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const LINKIFY_TIMEOUT = 0; 1.9 + 1.10 +function Linkifier() { 1.11 + this._linkifyTimer = null; 1.12 + this._phoneRegex = /(?:\s|^)[\+]?(\(?\d{1,8}\)?)?([- ]+\(?\d{1,8}\)?)+( ?(x|ext) ?\d{1,3})?(?:\s|$)/g; 1.13 +} 1.14 + 1.15 +Linkifier.prototype = { 1.16 + _buildAnchor : function(aDoc, aNumberText) { 1.17 + let anchorNode = aDoc.createElement("a"); 1.18 + let cleanedText = ""; 1.19 + for (let i = 0; i < aNumberText.length; i++) { 1.20 + let c = aNumberText.charAt(i); 1.21 + if ((c >= '0' && c <= '9') || c == '+') //assuming there is only the leading '+'. 1.22 + cleanedText += c; 1.23 + } 1.24 + anchorNode.setAttribute("href", "tel:" + cleanedText); 1.25 + let nodeText = aDoc.createTextNode(aNumberText); 1.26 + anchorNode.appendChild(nodeText); 1.27 + return anchorNode; 1.28 + }, 1.29 + 1.30 + _linkifyNodeNumbers : function(aNodeToProcess, aDoc) { 1.31 + let parent = aNodeToProcess.parentNode; 1.32 + let nodeText = aNodeToProcess.nodeValue; 1.33 + 1.34 + // Replacing the original text node with a sequence of 1.35 + // |text before number|anchor with number|text after number nodes. 1.36 + // Each step a couple of (optional) text node and anchor node are appended. 1.37 + let anchorNode = null; 1.38 + let m = null; 1.39 + let startIndex = 0; 1.40 + let prevNode = null; 1.41 + while (m = this._phoneRegex.exec(nodeText)) { 1.42 + anchorNode = this._buildAnchor(aDoc, nodeText.substr(m.index, m[0].length)); 1.43 + 1.44 + let textExistsBeforeNumber = (m.index > startIndex); 1.45 + let nodeToAdd = null; 1.46 + if (textExistsBeforeNumber) 1.47 + nodeToAdd = aDoc.createTextNode(nodeText.substr(startIndex, m.index - startIndex)); 1.48 + else 1.49 + nodeToAdd = anchorNode; 1.50 + 1.51 + if (!prevNode) // first time, need to replace the whole node with the first new one. 1.52 + parent.replaceChild(nodeToAdd, aNodeToProcess); 1.53 + else 1.54 + parent.insertBefore(nodeToAdd, prevNode.nextSibling); //inserts after. 1.55 + 1.56 + if (textExistsBeforeNumber) // if we added the text node before the anchor, we still need to add the anchor node. 1.57 + parent.insertBefore(anchorNode, nodeToAdd.nextSibling); 1.58 + 1.59 + // next nodes need to be appended to this node. 1.60 + prevNode = anchorNode; 1.61 + startIndex = m.index + m[0].length; 1.62 + } 1.63 + 1.64 + // if some text is remaining after the last anchor. 1.65 + if (startIndex > 0 && startIndex < nodeText.length) { 1.66 + let lastNode = aDoc.createTextNode(nodeText.substr(startIndex)); 1.67 + parent.insertBefore(lastNode, prevNode.nextSibling); 1.68 + return lastNode; 1.69 + } 1.70 + return anchorNode; 1.71 + }, 1.72 + 1.73 + linkifyNumbers: function(aDoc) { 1.74 + // Removing any installed timer in case the page has changed and a previous timer is still running. 1.75 + if (this._linkifyTimer) { 1.76 + clearTimeout(this._linkifyTimer); 1.77 + this._linkifyTimer = null; 1.78 + } 1.79 + 1.80 + let filterNode = function (node) { 1.81 + if (node.parentNode.tagName != 'A' && 1.82 + node.parentNode.tagName != 'SCRIPT' && 1.83 + node.parentNode.tagName != 'NOSCRIPT' && 1.84 + node.parentNode.tagName != 'STYLE' && 1.85 + node.parentNode.tagName != 'APPLET' && 1.86 + node.parentNode.tagName != 'TEXTAREA') 1.87 + return NodeFilter.FILTER_ACCEPT; 1.88 + else 1.89 + return NodeFilter.FILTER_REJECT; 1.90 + } 1.91 + 1.92 + let nodeWalker = aDoc.createTreeWalker(aDoc.body, NodeFilter.SHOW_TEXT, filterNode, false); 1.93 + let parseNode = function() { 1.94 + let node = nodeWalker.nextNode(); 1.95 + if (!node) { 1.96 + this._linkifyTimer = null; 1.97 + return; 1.98 + } 1.99 + let lastAddedNode = this._linkifyNodeNumbers(node, aDoc); 1.100 + // we assign a different timeout whether the node was processed or not. 1.101 + if (lastAddedNode) { 1.102 + nodeWalker.currentNode = lastAddedNode; 1.103 + this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT); 1.104 + } else { 1.105 + this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT); 1.106 + } 1.107 + }.bind(this); 1.108 + 1.109 + this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT); 1.110 + } 1.111 +};