From ede3270fe9269367a24a8fb8a10ae813f03c88d1 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Tue, 23 Aug 2016 17:03:36 +1000 Subject: [PATCH 01/21] Async suggest This PR makes suggest operating asynchroniously, providing _much_ faster performance, much smaller memory use, and allowing to stop a suggestion search midway. suggest : function (word, limit, doneFunc, progressFunc) - returns undefined - doneFunc (if given) will be called (with the results array) once the search is done - progressFunc (if given) will be called (with the new match and the array of all matches so far) whenever a new match is found in the search - if progressFunc returns===false the current search will be aborted - calling suggest without parameters will abort a current search if running See a working sample in https://plnkr.co/edit/0y1wCHXx3k3mZaHFOpHT --- typo/typo.js | 175 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 70 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 9da598d..abf9e04 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -737,11 +737,15 @@ Typo.prototype = { alphabet : "", - suggest : function (word, limit) { + suggest : function (word, limit, doneFunc, progressFunc) { if (!this.loaded) { throw "Dictionary not loaded."; } - + + this.id=(this.id || 0) + 1; // id identify the current async op + // calling suggest with argument will stop the current search if there is now + if (arguments.length===0) return; + limit = limit || 5; if (this.memoized.hasOwnProperty(word)) { @@ -750,11 +754,17 @@ Typo.prototype = { // Only return the cached list if it's big enough or if there weren't enough suggestions // to fill a smaller limit. if (limit <= memoizedLimit || this.memoized[word]['suggestions'].length < memoizedLimit) { - return this.memoized[word]['suggestions'].slice(0, limit); + var res=this.memoized[word]['suggestions'].slice(0, limit); + if (progressFunc) for (var r=0; r 1 && s[1][1] !== s[1][0]) { - rv.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); - } - - if (s[1]) { - for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { - // Eliminate replacement of a letter by itself - if (self.alphabet[j] != s[1].substring(0,1)){ - rv.push(s[0] + self.alphabet[j] + s[1].substring(1)); - } - } - } - - if (s[1]) { - for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { - rv.push(s[0] + self.alphabet[j] + s[1]); - } - } - } - } - - return rv; - } - - function known(words) { - var rv = []; - - for (var i = 0, _len = words.length; i < _len; i++) { - if (self.check(words[i])) { - rv.push(words[i]); - } - } - - return rv; - } - - function correct(word) { - // Get the edit-distance-1 and edit-distance-2 forms of this word. - var ed1 = edits1([word]); - var ed2 = edits1(ed1); - - var corrections = known(ed1.concat(ed2)); - + function sortCorrections(corrections) { var i, _len; // Sort the edits based on how many different ways they were created. @@ -865,7 +818,7 @@ Typo.prototype = { weighted_corrections[corrections[i]] += 1; } } - + var sorted_corrections = []; for (i in weighted_corrections) { @@ -907,16 +860,98 @@ Typo.prototype = { rv.push(sorted_corrections[i][0]); } } + + return rv; + } + + // Get the edit-distance-1 of this word. + function edits(word) { + var rv = []; + var i, j, _len, _jlen; + + for (i = 0, _len = word.length + 1; i < _len; i++) { + var s = [ word.substring(0, i), word.substring(i) ]; + + if (s[1]) { + rv.push(s[0] + s[1].substring(1)); + } + + // Eliminate transpositions of identical letters + if (s[1].length > 1 && s[1][1] !== s[1][0]) { + rv.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); + } + + if (s[1]) { + for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { + // Eliminate replacement of a letter by itself + if (self.alphabet[j] != s[1].substring(0,1)){ + rv.push(s[0] + self.alphabet[j] + s[1].substring(1)); + } + } + } + + if (s[1]) { + for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { + rv.push(s[0] + self.alphabet[j] + s[1]); + } + } + } + return rv; } - this.memoized[word] = { - 'suggestions': correct(word), - 'limit': limit - }; + function known(id) { + // verify we are still in the same operation + if (id!==self.id) { + console.log('different context - aborting'); + return; // another suggest was called so abort + } + + while(true) { + if (ed1.length===0 && ed2.length===0) { + founds=sortCorrections(founds); + self.memoized[word] = { + 'suggestions': founds, + 'limit': limit + } + if (doneFunc) doneFunc(founds); + return; // we're done + } + + var next; + if (ed2.length===0) { + next=ed1.shift(); + ed2=[next].concat(edits(next)); + } + + next=ed2.shift(); + + if (founds.indexOf(next)===-1 && self.check(next)) { + var abort; + founds.push(next); + if (progressFunc) abort=progressFunc(next, founds); + if (abort===false) { + console.log('suggestions aborted'); + return; // aborted + } + if (founds.length===limit) { + ed1=ed2=[]; // finish gracefully + } + } + + // do a sleep(0) every 200 ms + if (Date.now()-timer>200) { + console.log('sleep 0'); + timer=Date.now(); + setTimeout(function(id) { known(id); }, 0, id); // we continue after a sleep + return; + } + } + } - return this.memoized[word]['suggestions']; + ed1=edits(word); + known(self.id); // start the search } }; })(); @@ -924,4 +959,4 @@ Typo.prototype = { // Support for use as a node.js module. if (typeof module !== 'undefined') { module.exports = Typo; -} \ No newline at end of file +} From 4c7d4c0308be6268860519e45cefdc61a72524e6 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Tue, 23 Aug 2016 17:25:39 +1000 Subject: [PATCH 02/21] Update typo.js --- typo/typo.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index abf9e04..0809857 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -904,7 +904,7 @@ Typo.prototype = { function known(id) { // verify we are still in the same operation if (id!==self.id) { - console.log('different context - aborting'); + //console.log('different context - aborting'); return; // another suggest was called so abort } @@ -932,7 +932,7 @@ Typo.prototype = { founds.push(next); if (progressFunc) abort=progressFunc(next, founds); if (abort===false) { - console.log('suggestions aborted'); + //console.log('suggestions aborted'); return; // aborted } if (founds.length===limit) { @@ -942,7 +942,7 @@ Typo.prototype = { // do a sleep(0) every 200 ms if (Date.now()-timer>200) { - console.log('sleep 0'); + //console.log('sleep 0'); timer=Date.now(); setTimeout(function(id) { known(id); }, 0, id); // we continue after a sleep return; From 8704d75d9320c0536ce9ca0039db6c6892b94bdd Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 08:11:04 +1000 Subject: [PATCH 03/21] Update typo.js --- typo/typo.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 0809857..d05de9b 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -755,7 +755,7 @@ Typo.prototype = { // to fill a smaller limit. if (limit <= memoizedLimit || this.memoized[word]['suggestions'].length < memoizedLimit) { var res=this.memoized[word]['suggestions'].slice(0, limit); - if (progressFunc) for (var r=0; r200) { - //console.log('sleep 0'); + console.log('sleep 0'); timer=Date.now(); setTimeout(function(id) { known(id); }, 0, id); // we continue after a sleep return; } } + + founds=sortCorrections(founds); + self.memoized[word] = { + 'suggestions': founds, + 'limit': limit + } + if (doneFunc) doneFunc(founds); } ed1=edits(word); From 9636b40f13908006730b166f202f988f6c89d2a4 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 08:17:47 +1000 Subject: [PATCH 04/21] Update typo.js --- typo/typo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index d05de9b..d1999cf 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -934,7 +934,7 @@ Typo.prototype = { // do a sleep(0) every 200 ms if (Date.now()-timer>200) { - console.log('sleep 0'); + //console.log('sleep 0'); timer=Date.now(); setTimeout(function(id) { known(id); }, 0, id); // we continue after a sleep return; From 67e89a97c83bdf883d3f1a6babf73f9aa0b63b99 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 08:54:21 +1000 Subject: [PATCH 05/21] Update typo.js --- typo/typo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index d1999cf..a0f1b73 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -743,7 +743,7 @@ Typo.prototype = { } this.id=(this.id || 0) + 1; // id identify the current async op - // calling suggest with argument will stop the current search if there is now + // calling suggest with no arguments will stop the current search if there is now if (arguments.length===0) return; limit = limit || 5; From afc51f9254da340af11a40f2a977df5b9ebaeed7 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 09:55:25 +1000 Subject: [PATCH 06/21] Update typo.js --- typo/typo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index a0f1b73..3486c34 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -913,11 +913,11 @@ Typo.prototype = { while(ed1.length!==0 || ed2.length!==0) { var next; if (ed2.length===0) { - next=ed1.shift(); + next=ed1.pop(); ed2=[next].concat(edits(next)); } - next=ed2.shift(); + next=ed2.pop(); if (founds.indexOf(next)===-1 && self.check(next)) { var abort; From 0829f26c94c3cb46bd23db65a5fd098421ddf150 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 10:13:45 +1000 Subject: [PATCH 07/21] Update typo.js --- typo/typo.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index 3486c34..c7b9af1 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -914,7 +914,8 @@ Typo.prototype = { var next; if (ed2.length===0) { next=ed1.pop(); - ed2=[next].concat(edits(next)); + ed2=edits(next); + ed2.push(next); } next=ed2.pop(); From 6b37bf0f8dc6991db2a5dc9d844a10ce42a521cb Mon Sep 17 00:00:00 2001 From: Kofifus Date: Wed, 24 Aug 2016 10:25:46 +1000 Subject: [PATCH 08/21] Update typo.js --- typo/typo.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index c7b9af1..b70e751 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -866,9 +866,9 @@ Typo.prototype = { return rv; } - // Get the edit-distance-1 of this word. - function edits(word) { - var rv = []; + // Get the edit-distance-1 of this word into rv + function edits(rv, word) { + rv.length=0; // clear it var i, j, _len, _jlen; @@ -914,7 +914,7 @@ Typo.prototype = { var next; if (ed2.length===0) { next=ed1.pop(); - ed2=edits(next); + edits(ed2, next); ed2.push(next); } @@ -950,7 +950,7 @@ Typo.prototype = { if (doneFunc) doneFunc(founds); } - ed1=edits(word); + edits(ed1, word); known(self.id); // start the search } }; From 5239f23798b9e98a4bba720bdf75e9e697db393f Mon Sep 17 00:00:00 2001 From: Kofifus Date: Thu, 25 Aug 2016 08:34:08 +1000 Subject: [PATCH 09/21] Update typo.js --- typo/typo.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index b70e751..8c0066b 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -928,16 +928,14 @@ Typo.prototype = { console.log('suggestions aborted'); return; // aborted } - if (founds.length===limit) { - ed1=ed2=[]; // finish gracefully - } + if (founds.length===limit) ed1.length=ed2.length=0; // finish gracefully } // do a sleep(0) every 200 ms if (Date.now()-timer>200) { //console.log('sleep 0'); timer=Date.now(); - setTimeout(function(id) { known(id); }, 0, id); // we continue after a sleep + setTimeout(function(id) { known(id); }, 0, id); return; } } From 5f249c56019a195d9492bafc03faa0dbfb9da095 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Thu, 25 Aug 2016 08:49:36 +1000 Subject: [PATCH 10/21] Update typo.js --- typo/typo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 8c0066b..343a39e 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -774,8 +774,8 @@ Typo.prototype = { var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]); if (this.check(correctedWord)) { - if (progressFunc) progressFunc([correctedWord]); - if (doneFunc) doneFunc([correctedWord]); + if (progressFunc) progressFunc([correctedWord]); + if (doneFunc) doneFunc([correctedWord]); return; } } From d7fbe79daad2ff99cabc6098af8544eb11537272 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Thu, 25 Aug 2016 12:23:01 +1000 Subject: [PATCH 11/21] Update typo.js --- typo/typo.js | 82 +++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 343a39e..69af05c 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -738,53 +738,56 @@ Typo.prototype = { alphabet : "", suggest : function (word, limit, doneFunc, progressFunc) { - if (!this.loaded) { + var self = this; + + if (!self.loaded) { throw "Dictionary not loaded."; } - this.id=(this.id || 0) + 1; // id identify the current async op + // id identify the current async op + if (!self.id || self.id>100000) self.id=0; + self.id++; + // calling suggest with no arguments will stop the current search if there is now if (arguments.length===0) return; limit = limit || 5; - if (this.memoized.hasOwnProperty(word)) { - var memoizedLimit = this.memoized[word]['limit']; + if (self.memoized.hasOwnProperty(word)) { + var memoizedLimit = self.memoized[word]['limit']; // Only return the cached list if it's big enough or if there weren't enough suggestions // to fill a smaller limit. - if (limit <= memoizedLimit || this.memoized[word]['suggestions'].length < memoizedLimit) { - var res=this.memoized[word]['suggestions'].slice(0, limit); + if (limit <= memoizedLimit || self.memoized[word]['suggestions'].length < memoizedLimit) { + var res=self.memoized[word]['suggestions'].slice(0, limit); if (progressFunc) for (var r=0; r200) { + if (Date.now()-startTime>200) { //console.log('sleep 0'); - timer=Date.now(); - setTimeout(function(id) { known(id); }, 0, id); + setTimeout(function(id) { known(); }, 0); // we continue after a sleep return; } } @@ -948,7 +952,7 @@ Typo.prototype = { if (doneFunc) doneFunc(founds); } - edits(ed1, word); + ed1=edits(word); known(self.id); // start the search } }; From a558e679a11bd603875ec5c4ab15bc1a04f24b4b Mon Sep 17 00:00:00 2001 From: Kofifus Date: Thu, 25 Aug 2016 12:32:18 +1000 Subject: [PATCH 12/21] Update typo.js --- typo/typo.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 69af05c..168a755 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -779,9 +779,9 @@ Typo.prototype = { var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]); if (self.check(correctedWord)) { - if (progressFunc) progressFunc([correctedWord]); - if (doneFunc) doneFunc([correctedWord]); - return; + if (progressFunc) progressFunc([correctedWord]); + if (doneFunc) doneFunc([correctedWord]); + return; } } } @@ -809,7 +809,7 @@ Typo.prototype = { } */ - // define the local context for async ops + // define the local context for async ops var localId=self.id, ed1=[], ed2=[], founds=[]; function sortCorrections(corrections) { From eba900f5dac105d20304286a915266caec8cbe6f Mon Sep 17 00:00:00 2001 From: Kofifus Date: Thu, 25 Aug 2016 14:17:55 +1000 Subject: [PATCH 13/21] Update typo.js --- typo/typo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index 168a755..1b0e366 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -939,7 +939,7 @@ Typo.prototype = { // do a sleep(0) every 200 ms if (Date.now()-startTime>200) { //console.log('sleep 0'); - setTimeout(function(id) { known(); }, 0); // we continue after a sleep + setTimeout(known, 0); // we continue after a sleep return; } } From df20791e826f2a4602040e8bfbeb684f42a4639d Mon Sep 17 00:00:00 2001 From: Kofifus Date: Fri, 26 Aug 2016 23:40:29 +1000 Subject: [PATCH 14/21] Update typo.js --- typo/typo.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 1b0e366..c230f77 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -739,6 +739,9 @@ Typo.prototype = { suggest : function (word, limit, doneFunc, progressFunc) { var self = this; + + // define the local context for async ops + var localId, ed1=[], ed2=[], founds=[]; if (!self.loaded) { throw "Dictionary not loaded."; @@ -746,7 +749,7 @@ Typo.prototype = { // id identify the current async op if (!self.id || self.id>100000) self.id=0; - self.id++; + localId=++self.id; // calling suggest with no arguments will stop the current search if there is now if (arguments.length===0) return; @@ -779,13 +782,16 @@ Typo.prototype = { var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]); if (self.check(correctedWord)) { - if (progressFunc) progressFunc([correctedWord]); - if (doneFunc) doneFunc([correctedWord]); - return; + founds.push(correctedWord); + if (progressFunc) progressFunc(correctedWord); + if (founds.length===limit) { + if (doneFunc) doneFunc(founds); + return; + } } } } - + self.alphabet = "abcdefghijklmnopqrstuvwxyz"; /* @@ -809,9 +815,6 @@ Typo.prototype = { } */ - // define the local context for async ops - var localId=self.id, ed1=[], ed2=[], founds=[]; - function sortCorrections(corrections) { var i, _len; @@ -915,16 +918,11 @@ Typo.prototype = { return; } - var startTime=Date.now(); + var next, startTime=Date.now(); while(ed1.length!==0 || ed2.length!==0) { - var next; - if (ed2.length===0) { - next=ed1.pop(); - ed2=edits(next); - } else { - next=ed2.pop(); - } + if (ed2.length===0) ed2=edits(ed1.pop()); + next=ed2.pop(); if (founds.indexOf(next)===-1 && self.check(next)) { if (progressFunc && progressFunc(next)===false) { @@ -939,7 +937,7 @@ Typo.prototype = { // do a sleep(0) every 200 ms if (Date.now()-startTime>200) { //console.log('sleep 0'); - setTimeout(known, 0); // we continue after a sleep + setTimeout(known, 0); return; } } @@ -953,7 +951,8 @@ Typo.prototype = { } ed1=edits(word); - known(self.id); // start the search + ed2=edits(word); + known(); // start the search } }; })(); From 9ab565a6ad62fe897dd87db632a2c7265e9217d7 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sat, 27 Aug 2016 10:05:05 +1000 Subject: [PATCH 15/21] Update typo.js --- typo/typo.js | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index c230f77..bd727a6 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -876,21 +876,33 @@ Typo.prototype = { } // Get the edit-distance-1 of word - function edits(word) { - var rv=[], i, j, _len, _jlen; + function edits1(word) { + var rv=[], i, j, _len, _jlen, s; + // remove a letter for (i = 0, _len = word.length + 1; i < _len; i++) { - var s = [ word.substring(0, i), word.substring(i) ]; + s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { - rv.push(s[0] + s[1].substring(1)); + for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { + rv.push(s[0] + self.alphabet[j] + s[1]); + } } - - // Eliminate transpositions of identical letters - if (s[1].length > 1 && s[1][1] !== s[1][0]) { - rv.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); + } + + // add a letter + for (i = 0, _len = word.length + 1; i < _len; i++) { + s = [ word.substring(0, i), word.substring(i) ]; + + if (s[1]) { + rv.push(s[0] + s[1].substring(1)); } + } + // replace a letter + for (i = 0, _len = word.length + 1; i < _len; i++) { + s = [ word.substring(0, i), word.substring(i) ]; + if (s[1]) { for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { // Eliminate replacement of a letter by itself @@ -899,11 +911,14 @@ Typo.prototype = { } } } + } - if (s[1]) { - for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { - rv.push(s[0] + self.alphabet[j] + s[1]); - } + // Eliminate transpositions of identical letters + for (i = 0, _len = word.length + 1; i < _len; i++) { + s = [ word.substring(0, i), word.substring(i) ]; + + if (s[1].length > 1 && s[1][1] !== s[1][0]) { + rv.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); } } @@ -921,7 +936,7 @@ Typo.prototype = { var next, startTime=Date.now(); while(ed1.length!==0 || ed2.length!==0) { - if (ed2.length===0) ed2=edits(ed1.pop()); + if (ed2.length===0) ed2=edits1(ed1.pop()); next=ed2.pop(); if (founds.indexOf(next)===-1 && self.check(next)) { @@ -950,8 +965,8 @@ Typo.prototype = { if (doneFunc) doneFunc(founds); } - ed1=edits(word); - ed2=edits(word); + ed1=edits1(word); + ed2=ed1.slice(); known(); // start the search } }; From c2a387be5c69f7f8433da94c8556f1367f6aecc9 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sun, 28 Aug 2016 10:41:38 +1000 Subject: [PATCH 16/21] Update typo.js --- typo/typo.js | 76 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index bd727a6..d44dc0d 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -740,9 +740,9 @@ Typo.prototype = { suggest : function (word, limit, doneFunc, progressFunc) { var self = this; - // define the local context for async ops - var localId, ed1=[], ed2=[], founds=[]; - + var async=!!(doneFunc || progressFunc), localId; + var ed1=[], ed2=[], founds=[]; + if (!self.loaded) { throw "Dictionary not loaded."; } @@ -763,15 +763,23 @@ Typo.prototype = { // to fill a smaller limit. if (limit <= memoizedLimit || self.memoized[word]['suggestions'].length < memoizedLimit) { var res=self.memoized[word]['suggestions'].slice(0, limit); - if (progressFunc) for (var r=0; r=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { - for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { + + for (j = self.alphabet.length; j >=0; j--) { rv.push(s[0] + self.alphabet[j] + s[1]); } } } // add a letter - for (i = 0, _len = word.length + 1; i < _len; i++) { + for (i = _len ; i >=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { @@ -900,11 +914,11 @@ Typo.prototype = { } // replace a letter - for (i = 0, _len = word.length + 1; i < _len; i++) { + for (i = _len ; i >=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { - for (j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { + for (j = self.alphabet.length; j>= 0; j--) { // Eliminate replacement of a letter by itself if (self.alphabet[j] != s[1].substring(0,1)){ rv.push(s[0] + self.alphabet[j] + s[1].substring(1)); @@ -914,7 +928,7 @@ Typo.prototype = { } // Eliminate transpositions of identical letters - for (i = 0, _len = word.length + 1; i < _len; i++) { + for (i = _len ; i >=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1].length > 1 && s[1][1] !== s[1][0]) { @@ -949,12 +963,14 @@ Typo.prototype = { if (founds.length===limit) ed1.length=ed2.length=0; // finish gracefully } - // do a sleep(0) every 200 ms - if (Date.now()-startTime>200) { - //console.log('sleep 0'); - setTimeout(known, 0); - return; - } + if (async) { + // do a sleep(0) every 200 ms + if (Date.now()-startTime>200) { + //console.log('sleep 0'); + setTimeout(known, 0); + return; + } + } } founds=sortCorrections(founds); @@ -962,12 +978,18 @@ Typo.prototype = { 'suggestions': founds, 'limit': limit } - if (doneFunc) doneFunc(founds); + + if (async) { + if (doneFunc) doneFunc(founds); + } else { + return founds; + } } ed1=edits1(word); ed2=ed1.slice(); known(); // start the search + if (!async) return founds; } }; })(); From c3a4697f55341f7908e5007c238bedd01b147bda Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sun, 28 Aug 2016 10:42:28 +1000 Subject: [PATCH 17/21] Update typo.js --- typo/typo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index d44dc0d..4bbe4d8 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -751,7 +751,7 @@ Typo.prototype = { if (!self.id || self.id>100000) self.id=0; localId=++self.id; - // calling suggest with no arguments will stop the current search if there is now + // calling suggest with no arguments will stop the current search if there is one if (arguments.length===0) return; limit = limit || 5; From 0f17d9e3bc55748a34f9b51ebeb850c0d1066894 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sun, 4 Sep 2016 18:21:50 +1000 Subject: [PATCH 18/21] Update typo.js --- typo/typo.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 4bbe4d8..4b3b80e 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -890,26 +890,27 @@ Typo.prototype = { // Get the edit-distance-1 of word // we are adding matches in reverse as they will later be popped function edits1(word) { - var rv=[], i, j, _len=word.length+1, s; + var word=word.toLowerCase(), rv=[], i, j, _len=word.length+1, s; - // remove a letter + // add a letter for (i = _len ; i >=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { - - for (j = self.alphabet.length; j >=0; j--) { + for (j = self.alphabet.length-1; j >=0; j--) { rv.push(s[0] + self.alphabet[j] + s[1]); + if (i===0) rv.push(self.alphabet[j].toUpperCase() + s[1]); } } } - // add a letter + // remove a letter for (i = _len ; i >=0; i--) { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { rv.push(s[0] + s[1].substring(1)); + if (i===0) rv.push(s[1][1].toUpperCase() + s[1].substring(2)); } } @@ -918,10 +919,11 @@ Typo.prototype = { s = [ word.substring(0, i), word.substring(i) ]; if (s[1]) { - for (j = self.alphabet.length; j>= 0; j--) { + for (j = self.alphabet.length-1; j>= 0; j--) { // Eliminate replacement of a letter by itself if (self.alphabet[j] != s[1].substring(0,1)){ rv.push(s[0] + self.alphabet[j] + s[1].substring(1)); + if (i===0) rv.push(self.alphabet[j].toUpperCase() + s[1].substring(1)); } } } @@ -933,6 +935,7 @@ Typo.prototype = { if (s[1].length > 1 && s[1][1] !== s[1][0]) { rv.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); + if (i===0) rv.push(s[1][1].toUpperCase() + s[1][0] + s[1].substring(2)); } } @@ -962,6 +965,7 @@ Typo.prototype = { founds.push(next); if (founds.length===limit) ed1.length=ed2.length=0; // finish gracefully } + next=next[0].toUpperCase + next.substring(1); // try capitalized next if (async) { // do a sleep(0) every 200 ms From bd35c2e95b47c6d8f23825997e72723165283b55 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sun, 4 Sep 2016 18:31:19 +1000 Subject: [PATCH 19/21] Update typo.js --- typo/typo.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index 4b3b80e..0321549 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -671,7 +671,9 @@ Typo.prototype = { var ruleCodes = this.dictionaryTable[word]; var i, _len; - + + if (word === word.toUpperCase()) return true; // all uppercase word is ok + if (typeof ruleCodes === 'undefined') { // Check if this might be a compound word. if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) { From c2b79cbdb945b8871931a57e6456471a0c41431f Mon Sep 17 00:00:00 2001 From: Kofifus Date: Sun, 4 Sep 2016 18:33:28 +1000 Subject: [PATCH 20/21] Update typo.js --- typo/typo.js | 1 - 1 file changed, 1 deletion(-) diff --git a/typo/typo.js b/typo/typo.js index 0321549..85b237b 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -967,7 +967,6 @@ Typo.prototype = { founds.push(next); if (founds.length===limit) ed1.length=ed2.length=0; // finish gracefully } - next=next[0].toUpperCase + next.substring(1); // try capitalized next if (async) { // do a sleep(0) every 200 ms From 868c61bed1d1913fae8319e42e61e80fc50e56f3 Mon Sep 17 00:00:00 2001 From: Kofifus Date: Mon, 5 Sep 2016 08:13:27 +1000 Subject: [PATCH 21/21] Update typo.js --- typo/typo.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/typo/typo.js b/typo/typo.js index 85b237b..f703cec 100644 --- a/typo/typo.js +++ b/typo/typo.js @@ -623,24 +623,7 @@ Typo.prototype = { return true; } - // The exact word is not in the dictionary. - if (trimmedWord.toUpperCase() === trimmedWord) { - // The word was supplied in all uppercase. - // Check for a capitalized form of the word. - var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase(); - - if (this.hasFlag(capitalizedWord, "KEEPCASE")) { - // Capitalization variants are not allowed for this word. - return false; - } - - if (this.checkExact(capitalizedWord)) { - return true; - } - } - var lowercaseWord = trimmedWord.toLowerCase(); - if (lowercaseWord !== trimmedWord) { if (this.hasFlag(lowercaseWord, "KEEPCASE")) { // Capitalization variants are not allowed for this word. @@ -958,7 +941,7 @@ Typo.prototype = { if (ed2.length===0) ed2=edits1(ed1.pop()); next=ed2.pop(); - if (founds.indexOf(next)===-1 && self.check(next)) { + if (founds.indexOf(next)===-1 && self.checkExact(next)) { if (progressFunc && progressFunc(next)===false) { // console.log('suggestions aborted'); ed1.length=ed2.length=0; // encourage GC