Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
|
|
@ -0,0 +1,279 @@
|
|||
'use strict';
|
||||
|
||||
/* globals utils */
|
||||
/* exported TelIndexer */
|
||||
|
||||
// This object implements a prefix tree (aka trie) for FB tel numbers
|
||||
|
||||
function TrieNode(number) {
|
||||
this.keys = Object.create(null);
|
||||
this.leaves = null;
|
||||
this.value = number;
|
||||
}
|
||||
|
||||
var TelIndexer = {
|
||||
_MIN_TEL_LENGTH: 3,
|
||||
_INSERT_MODE: 'insert',
|
||||
|
||||
_indexOf: function(array, value, byPrefix) {
|
||||
var out = -1;
|
||||
|
||||
if (!Array.isArray(array)) {
|
||||
return out;
|
||||
}
|
||||
|
||||
for (var j = 0, l = array.length; j < l; j++) {
|
||||
if (array[j].value === value || (byPrefix &&
|
||||
array[j].value.startsWith(value))) {
|
||||
out = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
_getStringVariants: function(number) {
|
||||
var out = [];
|
||||
|
||||
var limit = number.length - this._MIN_TEL_LENGTH + 1;
|
||||
for (var j = 0; j < limit; j++) {
|
||||
out.push(number.substr(j));
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Allows to index the number passed as parameter
|
||||
index: function(tree, number, dsId) {
|
||||
var self = this;
|
||||
var variants = this._getStringVariants(number);
|
||||
|
||||
variants.forEach(function(aVariant) {
|
||||
self._insert(tree, aVariant, dsId);
|
||||
});
|
||||
},
|
||||
|
||||
// Inserts a substring in a node. dsId is the object id (uid) associated
|
||||
_insertInNode: function(node, str, dsId) {
|
||||
if (str.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStr = str;
|
||||
var insertPointFound = false;
|
||||
var insertObj;
|
||||
var pointerOriginal = str.length;
|
||||
while (!insertPointFound && currentStr.length > 0) {
|
||||
var inextObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (inextObj !== -1) {
|
||||
insertPointFound = true;
|
||||
insertObj = node.leaves[inextObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (insertPointFound) {
|
||||
if (insertObj.value === str) {
|
||||
// Exact insertion point
|
||||
insertObj.keys[dsId] = true;
|
||||
}
|
||||
else {
|
||||
var keys = Object.keys(insertObj.keys);
|
||||
// Let's check what's the gap
|
||||
var newString = insertObj.value.substring(0, currentStr.length);
|
||||
var restString = insertObj.value.substring(currentStr.length);
|
||||
|
||||
insertObj.value = newString;
|
||||
|
||||
if (restString.length > 0) {
|
||||
var newNode = new TrieNode(restString);
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
newNode.keys[keys[j]] = true;
|
||||
}
|
||||
if (Array.isArray(insertObj.leaves)) {
|
||||
newNode.leaves = [];
|
||||
insertObj.leaves.forEach(function(aLeave) {
|
||||
newNode.leaves.push(aLeave);
|
||||
});
|
||||
}
|
||||
insertObj.leaves = [];
|
||||
insertObj.leaves.push(newNode);
|
||||
}
|
||||
insertObj.keys[dsId] = true;
|
||||
|
||||
var restOriginal = str.substring(pointerOriginal + 1);
|
||||
// And the rest of the original string is inserted in the insertObj
|
||||
this._insertInNode(insertObj, restOriginal, dsId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var nextObj = new TrieNode(str);
|
||||
nextObj.keys[dsId] = true;
|
||||
node.leaves = node.leaves || [];
|
||||
node.leaves.push(nextObj);
|
||||
}
|
||||
},
|
||||
|
||||
// Compare function used for binary search
|
||||
_compare: function(a, b) {
|
||||
return a.localeCompare(b);
|
||||
},
|
||||
|
||||
// Find the first level node (corresponding to the 3 first numbers)
|
||||
_getFirstLevelNode: function(tree, str, mode) {
|
||||
var out = null;
|
||||
|
||||
var index;
|
||||
if (mode === this._INSERT_MODE) {
|
||||
index = this._indexOf(tree, str);
|
||||
if (index !== -1) {
|
||||
out = tree[index];
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = utils.binarySearch(str, tree, {
|
||||
compareFunction: this._compare,
|
||||
arrayField: 'value'
|
||||
});
|
||||
|
||||
if (index.length > 0) {
|
||||
out = tree[index[0]];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Orders the tree enabling binary search on the first levels
|
||||
orderTree: function(tree) {
|
||||
tree.sort(function(a, b) {
|
||||
return a.value.localeCompare(b.value);
|
||||
});
|
||||
},
|
||||
|
||||
// Inserts a number on the tree
|
||||
_insert: function(tree, str, dsId) {
|
||||
var firstThreeStr = str.substring(0, this._MIN_TEL_LENGTH);
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr,
|
||||
this._INSERT_MODE);
|
||||
if (!rootObj) {
|
||||
rootObj = new TrieNode(firstThreeStr);
|
||||
tree.push(rootObj);
|
||||
}
|
||||
rootObj.keys[dsId] = true;
|
||||
|
||||
this._insertInNode(rootObj, str.substring(this._MIN_TEL_LENGTH), dsId);
|
||||
},
|
||||
|
||||
// Search a substring in a node
|
||||
_searchInNode: function(node, str) {
|
||||
var currentStr = str;
|
||||
var found = false;
|
||||
var pointerOriginal = str.length;
|
||||
var searchObj;
|
||||
while (!found && currentStr.length > 0) {
|
||||
var idxObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (idxObj !== -1) {
|
||||
found = true;
|
||||
searchObj = node.leaves[idxObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (searchObj.value === str) {
|
||||
return searchObj;
|
||||
}
|
||||
// Let's calculate what is the gap
|
||||
var restString = str.substr(pointerOriginal + 1);
|
||||
if (restString.length > 0) {
|
||||
return this._searchInNode(searchObj, restString);
|
||||
}
|
||||
else {
|
||||
return searchObj;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// Search for a number (which can be partial) on the tree
|
||||
search: function(tree, number) {
|
||||
var out = [];
|
||||
var totalLength = number.length;
|
||||
var MIN_TEL_LENGTH = this._MIN_TEL_LENGTH;
|
||||
|
||||
if (totalLength >= MIN_TEL_LENGTH) {
|
||||
var firstThreeStr = number.substring(0, MIN_TEL_LENGTH);
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr);
|
||||
|
||||
if (rootObj) {
|
||||
if (totalLength === MIN_TEL_LENGTH) {
|
||||
out = Object.keys(rootObj.keys);
|
||||
}
|
||||
else {
|
||||
var node = this._searchInNode(rootObj,
|
||||
number.substring(MIN_TEL_LENGTH));
|
||||
if (node) {
|
||||
out = Object.keys(node.keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Removes a number from the tree
|
||||
remove: function(tree, number, dsId) {
|
||||
var self = this;
|
||||
var variants = this._getStringVariants(number);
|
||||
variants.forEach(function(aVariant) {
|
||||
self._unIndex(tree, aVariant, dsId);
|
||||
});
|
||||
},
|
||||
|
||||
// Removes a partial string from a node
|
||||
_removeInNode: function(node, str, dsId) {
|
||||
if (str.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStr = str;
|
||||
var found = false;
|
||||
var pointerOriginal = str.length;
|
||||
var searchObj;
|
||||
while (!found && currentStr.length > 0) {
|
||||
var idxObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (idxObj !== -1) {
|
||||
found = true;
|
||||
searchObj = node.leaves[idxObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
delete searchObj.keys[dsId];
|
||||
this._removeInNode(searchObj, str.substr(pointerOriginal + 1), dsId);
|
||||
}
|
||||
},
|
||||
|
||||
// Removes a number from the tree
|
||||
// TODO: Compact the tree when a number is removed
|
||||
_unIndex: function(tree, number, dsId) {
|
||||
var firstThreeStr = number.substring(0, this._MIN_TEL_LENGTH);
|
||||
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr);
|
||||
if (rootObj) {
|
||||
delete rootObj.keys[dsId];
|
||||
this._removeInNode(rootObj, number.substr(this._MIN_TEL_LENGTH), dsId);
|
||||
}
|
||||
}
|
||||
};
|
||||
Loading…
Reference in a new issue