mobile/android/chrome/content/Linkify.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:2b036a0cac11
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 const LINKIFY_TIMEOUT = 0;
6
7 function Linkifier() {
8 this._linkifyTimer = null;
9 this._phoneRegex = /(?:\s|^)[\+]?(\(?\d{1,8}\)?)?([- ]+\(?\d{1,8}\)?)+( ?(x|ext) ?\d{1,3})?(?:\s|$)/g;
10 }
11
12 Linkifier.prototype = {
13 _buildAnchor : function(aDoc, aNumberText) {
14 let anchorNode = aDoc.createElement("a");
15 let cleanedText = "";
16 for (let i = 0; i < aNumberText.length; i++) {
17 let c = aNumberText.charAt(i);
18 if ((c >= '0' && c <= '9') || c == '+') //assuming there is only the leading '+'.
19 cleanedText += c;
20 }
21 anchorNode.setAttribute("href", "tel:" + cleanedText);
22 let nodeText = aDoc.createTextNode(aNumberText);
23 anchorNode.appendChild(nodeText);
24 return anchorNode;
25 },
26
27 _linkifyNodeNumbers : function(aNodeToProcess, aDoc) {
28 let parent = aNodeToProcess.parentNode;
29 let nodeText = aNodeToProcess.nodeValue;
30
31 // Replacing the original text node with a sequence of
32 // |text before number|anchor with number|text after number nodes.
33 // Each step a couple of (optional) text node and anchor node are appended.
34 let anchorNode = null;
35 let m = null;
36 let startIndex = 0;
37 let prevNode = null;
38 while (m = this._phoneRegex.exec(nodeText)) {
39 anchorNode = this._buildAnchor(aDoc, nodeText.substr(m.index, m[0].length));
40
41 let textExistsBeforeNumber = (m.index > startIndex);
42 let nodeToAdd = null;
43 if (textExistsBeforeNumber)
44 nodeToAdd = aDoc.createTextNode(nodeText.substr(startIndex, m.index - startIndex));
45 else
46 nodeToAdd = anchorNode;
47
48 if (!prevNode) // first time, need to replace the whole node with the first new one.
49 parent.replaceChild(nodeToAdd, aNodeToProcess);
50 else
51 parent.insertBefore(nodeToAdd, prevNode.nextSibling); //inserts after.
52
53 if (textExistsBeforeNumber) // if we added the text node before the anchor, we still need to add the anchor node.
54 parent.insertBefore(anchorNode, nodeToAdd.nextSibling);
55
56 // next nodes need to be appended to this node.
57 prevNode = anchorNode;
58 startIndex = m.index + m[0].length;
59 }
60
61 // if some text is remaining after the last anchor.
62 if (startIndex > 0 && startIndex < nodeText.length) {
63 let lastNode = aDoc.createTextNode(nodeText.substr(startIndex));
64 parent.insertBefore(lastNode, prevNode.nextSibling);
65 return lastNode;
66 }
67 return anchorNode;
68 },
69
70 linkifyNumbers: function(aDoc) {
71 // Removing any installed timer in case the page has changed and a previous timer is still running.
72 if (this._linkifyTimer) {
73 clearTimeout(this._linkifyTimer);
74 this._linkifyTimer = null;
75 }
76
77 let filterNode = function (node) {
78 if (node.parentNode.tagName != 'A' &&
79 node.parentNode.tagName != 'SCRIPT' &&
80 node.parentNode.tagName != 'NOSCRIPT' &&
81 node.parentNode.tagName != 'STYLE' &&
82 node.parentNode.tagName != 'APPLET' &&
83 node.parentNode.tagName != 'TEXTAREA')
84 return NodeFilter.FILTER_ACCEPT;
85 else
86 return NodeFilter.FILTER_REJECT;
87 }
88
89 let nodeWalker = aDoc.createTreeWalker(aDoc.body, NodeFilter.SHOW_TEXT, filterNode, false);
90 let parseNode = function() {
91 let node = nodeWalker.nextNode();
92 if (!node) {
93 this._linkifyTimer = null;
94 return;
95 }
96 let lastAddedNode = this._linkifyNodeNumbers(node, aDoc);
97 // we assign a different timeout whether the node was processed or not.
98 if (lastAddedNode) {
99 nodeWalker.currentNode = lastAddedNode;
100 this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
101 } else {
102 this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
103 }
104 }.bind(this);
105
106 this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
107 }
108 };

mercurial