author | manotejmeka <manotejmeka@gmail.com> |
Mon, 20 Mar 2017 16:12:49 -0400 | |
changeset 502476 | 0913c1470ff6e959a260dcee82311968f495fc59 |
parent 501227 | 98fc32dcdfba74192bfdc9e8ecf9129d64134057 |
child 550167 | 13a88c32e2448fb0b7c8f14421c7782ea748824b |
push id | 50289 |
push user | bmo:manotejmeka@gmail.com |
push date | Tue, 21 Mar 2017 20:33:18 +0000 |
bugs | 1335905 |
milestone | 55.0a1 |
--- a/browser/components/preferences/in-content/findInPage.js +++ b/browser/components/preferences/in-content/findInPage.js @@ -7,27 +7,38 @@ var gSearchResultsPane = { findSelection: null, searchResultsCategory: null, searchInput: null, listSearchBubbles: [], + initialSearch: true, + + trie: null, + init() { let controller = this.getSelectionController(); this.findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND); this.searchResultsCategory = document.getElementById("category-search-results"); this.searchInput = document.getElementById("searchInput"); this.searchInput.hidden = !Services.prefs.getBoolPref("browser.preferences.search"); + document.getElementById("searchSuggestions").hidden = this.searchInput.hidden; if (!this.searchInput.hidden) { this.searchInput.addEventListener("command", this); this.searchInput.addEventListener("focus", this); + + // adding datalist to textbos input element + // let temp = this.searchInput.boxObject.firstChild.childNodes[0]; + // temp.setAttribute("list","searchSuggestions"); + // temp.setAttribute("autocomplete","off"); } + this.trie = new Trie(); }, handleEvent(event) { if (event.type === "command") { this.searchFunction(event); } else if (event.type === "focus") { this.initializeCategories(); } @@ -140,18 +151,21 @@ var gSearchResultsPane = { * nodeSizes = "This is an example" * This is used when executing the regular expression * @param String searchPhrase * word or words to search for * @returns boolean * Returns true when atleast one instance of search phrase is found, otherwise false */ highlightMatches(textNodes, nodeSizes, textSearch, searchPhrase) { - let regExpPhrase = this.escapeRegexpSpecialCharacters(searchPhrase); - let regExp = new RegExp(regExpPhrase, "gi"); + if (this.initialSearch) { + this.addToTries(textSearch.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"").removeStopWords().split(" ")); + } + let regExp = new RegExp(searchPhrase.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),"gi"); + //let regExp = new RegExp(this.escapeRegexpSpecialCharacters(searchPhrase), "gi"); let result, indices = []; while ((result = regExp.exec(textSearch))) { indices.push(result.index); } if(indices.length > 0){ let xxx = 0; @@ -206,16 +220,30 @@ var gSearchResultsPane = { return controller; }, get strings() { delete this.strings; return this.strings = document.getElementById("searchResultBundle"); }, + searchSuggestions(listSuggestions) { + let object = document.getElementById("searchSuggestions"); + while (object.firstChild) { + object.removeChild(object.firstChild); + } + for (let word of listSuggestions){ + let option = document.createElementNS('http://www.w3.org/1999/xhtml', 'option'); + option.setAttribute("value",word); + option.setAttribute("data-value",word); + //option.setAttribute("xmlns" ,"http://www.w3.org/1999/xhtml"); + object.appendChild(option); + } + }, + /** * Shows or hides content according to search input * * @param String event * to search for filted query in */ searchFunction(event) { let query = event.target.value.trim(); @@ -224,16 +252,19 @@ var gSearchResultsPane = { [].forEach.call(document.querySelectorAll('.search-bubble'),function(e){ e.parentNode.removeChild(e); }); this.listSearchBubbles = []; let srHeader = document.getElementById("header-searchResults"); if (query) { + // Provide Search Suggestions + console.log("Search Suggestions: ",this.trie.autoComplete(String(query.toLowerCase()))); + this.searchSuggestions(this.trie.autoComplete(String(query.toLowerCase()))); // Showing the Search Results Tag gotoPref("paneSearchResults"); this.searchResultsCategory.hidden = false; let resultsFound = false; // Building the range for highlighted areas @@ -278,16 +309,20 @@ var gSearchResultsPane = { } } } else { this.searchResultsCategory.hidden = true; document.getElementById("sorry-message").textContent = ""; // Going back to General when cleared gotoPref("paneGeneral"); } + + if (this.initialSearch) { + this.initialSearch = false; + } }, /** * Finding leaf nodes and checking their content for words to search, * It is a recursive function * * @param Node nodeObject * DOM Element @@ -363,10 +398,16 @@ var gSearchResultsPane = { var searchContent = document.createElement("div"); searchContent.setAttribute("class", "search-bubble-innards"); searchContent.innerHTML = searchPhrase; searchBubble.appendChild(searchContent); currentNode.parentElement.insertBefore(searchBubble,currentNode); + }, + + addToTries(listOfWords) { + for(i = 0; i < listOfWords.length; i++) { + this.trie.insert(listOfWords[i].toLowerCase()); + } } }
--- a/browser/components/preferences/in-content/jar.mn +++ b/browser/components/preferences/in-content/jar.mn @@ -12,8 +12,10 @@ browser.jar: content/browser/preferences/in-content/containers.js content/browser/preferences/in-content/advanced.js content/browser/preferences/in-content/applications.js content/browser/preferences/in-content/content.js content/browser/preferences/in-content/sync.js content/browser/preferences/in-content/security.js content/browser/preferences/in-content/search.js content/browser/preferences/in-content/findInPage.js + content/browser/preferences/in-content/tries.js + \ No newline at end of file
--- a/browser/components/preferences/in-content/preferences.xul +++ b/browser/components/preferences/in-content/preferences.xul @@ -68,16 +68,17 @@ title="&prefWindow.title;"> #endif <html:link rel="shortcut icon" href="chrome://browser/skin/preferences/in-content/favicon.ico"/> <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/> + <script src="chrome://browser/content/preferences/in-content/tries.js"/> <script src="chrome://browser/content/preferences/in-content/findInPage.js"/> <script type="application/javascript" src="chrome://browser/content/preferences/in-content/preferences.js"/> <script src="chrome://browser/content/preferences/in-content/subdialogs.js"/> <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/> <stringbundle id="bundlePreferences" @@ -195,17 +196,27 @@ <!-- Disable the findbar because it doesn't work properly. Remove this keyset once bug 1094240 ("disablefastfind" attribute broken in e10s mode) is fixed. --> <key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand=";"/> </keyset> <vbox class="main-content" flex="1"> <hbox pack="end"> - <textbox type="search" id="searchInput" placeholder="&searchInput.label;" hidden="true"/> + <textbox type="search" id="searchInput" placeholder="&searchInput.label;" list="searchSuggestions" autocomplete="off" hidden="true" /> + <!--<html:input id="selected" list="browsers" name="browser" /> +<html:datalist id="browsers"> + <html:option data-value="InternetExplorer" value="1"></html:option> + <html:option data-value="Firefox" value="2"></html:option> + <html:option data-value="Chrome" value="3"></html:option> + <html:option data-value="Opera" value="4"></html:option> + <html:option data-value="Safari" value="5"></html:option> +</html:datalist> +<html:input xmlns="http://www.w3.org/1999/xhtml" id="submit" type="submit" />--> + <html:datalist id="searchSuggestions" hidden="true" /> </hbox> <prefpane id="mainPrefPane"> #include searchResults.xul #include main.xul #include search.xul #include privacy.xul #include containers.xul #include advanced.xul
new file mode 100644 --- /dev/null +++ b/browser/components/preferences/in-content/tries.js @@ -0,0 +1,206 @@ +Trie = function() { + this.wordCount = 0; + this.children = []; + this.word = null; +} + +Trie.prototype = { + + insert: function(word){ + let node = this; + if (word === "") { + return; + } + for (let character of word.split("")) { + if (node.children[character] === undefined || node.children[character] === null) { + node.children[character] = new Trie(); + } + node = node.children[character]; + } + node.wordCount++; + node.word = word; + }, + + completeWord: function(partialWord) { + let node = this; + let listOfWords = []; + let child = null; + + if (node.wordCount > 0) { + listOfWords.push(partialWord); + } + for (let childNode in node.children) { + child = node.children[childNode]; + listOfWords = listOfWords.concat(child.completeWord(partialWord+childNode)); + } + return listOfWords; + }, + + listOfWords: function(){ + let node = this; + if (node === undefined || node === null){ + return []; + } + let wordsList = []; + let child = null; + if (node.word != null || node.word != undefined){ + wordsList.push(node.word); + } + for (let char in node.children){ + child = node.children[char]; + wordsList = wordsList.concat(child.listOfWords()); + } + return wordsList; + }, + + autoComplete: function(partialWord) { + let node = this; + let typos = node.levenshteinDistance(partialWord,1); + let arrayTypos = firstElement(typos); + + for (let char of partialWord) { + if (node.children[char] === undefined || node.children[char] === null){ + return new Set([].concat(arrayTypos)); + } + node = node.children[char]; + } + // return node.completeWord(partialWord); + let complete = node.listOfWords(); + return new Set(complete.concat(arrayTypos)); + }, + + levenshteinHelper: function(letter, word, previousRow, results, maxCost) { + let node = this; + let columns = word.length + 1; + let currentRow = [previousRow[0] + 1]; + let insertCost = null, deleteCost = null, replaceCost = null; + let child = null; + + for (let i=1; i<columns; i++) { + insertCost = currentRow[i - 1] + 1; + deleteCost = previousRow[i] + 1; + + if (word[i - 1] != letter) { + replaceCost = previousRow[i - 1] + 1; + } else { + replaceCost = previousRow[i - 1]; + } + + currentRow.push(Math.min(insertCost,deleteCost,replaceCost)); + } + + if (currentRow[currentRow.length - 1] <= maxCost && node.wordCount > 0){ + results.push([node.word, currentRow[currentRow.length - 1]]); + } + + if (Math.min.apply(null,currentRow) <= maxCost) { + for (let character in node.children) { + child = node.children[character]; + child.levenshteinHelper(character, word, currentRow, results, maxCost); + } + } + return results; + }, + + // wrote it with the help of http://stevehanov.ca/blog/index.php?id=114 + levenshteinDistance: function(word, maxCost) { + let node = this; + let currentRow = Array.from(Array(word.length+1).keys()); + let results = []; + let child = null; + + for (let character in node.children){ + child = node.children[character] + child.levenshteinHelper(character, word, currentRow, results, maxCost); + } + return results; + } +} + +function firstElement(arry) { + let temp = []; + for (i=0; i<arry.length; i++){ + temp.push(arry[i][0]); + } + return temp; +} + +/* +Testing out the spell checker +let t = new Trie(); +t.insert("apps"); +t.insert("app"); +t.insert("ppp"); +t.insert("bpp"); +t.insert("acp"); +t.insert("apple"); +t.insert("test"); +t.insert("tasting"); +t.insert("help"); +t.insert("helps"); + +let ans = t.levenshteinDistance("ap",2); +*/ +String.isStopWord = function(word) +{ + var regex = new RegExp("\\b"+word+"\\b","i"); + if(stopWords.search(regex) < 0) + { + return false; + }else + { + return true; + } +} + +String.prototype.removeStopWords = function() +{ + words = new Array(); + + this.replace(/\b[\w]+\b/g, + function($0) + { + if(!String.isStopWord($0)) + { + words[words.length] = $0.trim(); + } + } + ); + return words.join(" "); +} + +var stopWords = "a,able,about,above,abst,accordance,according,accordingly,across,act,actually,added,adj,\ +affected,affecting,affects,after,afterwards,again,against,ah,all,almost,alone,along,already,also,although,\ +always,am,among,amongst,an,and,announce,another,any,anybody,anyhow,anymore,anyone,anything,anyway,anyways,\ +anywhere,apparently,approximately,are,aren,arent,arise,around,as,aside,ask,asking,at,auth,available,away,awfully,\ +b,back,be,became,because,become,becomes,becoming,been,before,beforehand,begin,beginning,beginnings,begins,behind,\ +being,believe,below,beside,besides,between,beyond,biol,both,brief,briefly,but,by,c,ca,came,can,cannot,can't,cause,causes,\ +certain,certainly,co,com,come,comes,contain,containing,contains,could,couldnt,d,date,did,didn't,different,do,does,doesn't,\ +doing,done,don't,down,downwards,due,during,e,each,ed,edu,effect,eg,eight,eighty,either,else,elsewhere,end,ending,enough,\ +especially,et,et-al,etc,even,ever,every,everybody,everyone,everything,everywhere,ex,except,f,far,few,ff,fifth,first,five,fix,\ +followed,following,follows,for,former,formerly,forth,found,four,from,further,furthermore,g,gave,get,gets,getting,give,given,gives,\ +giving,go,goes,gone,got,gotten,h,had,happens,hardly,has,hasn't,have,haven't,having,he,hed,hence,her,here,hereafter,hereby,herein,\ +heres,hereupon,hers,herself,hes,hi,hid,him,himself,his,hither,home,how,howbeit,however,hundred,i,id,ie,if,i'll,im,immediate,\ +immediately,importance,important,in,inc,indeed,index,information,instead,into,invention,inward,is,isn't,it,itd,it'll,its,itself,\ +i've,j,just,k,keep,keeps,kept,kg,km,know,known,knows,l,largely,last,lately,later,latter,latterly,least,less,lest,let,lets,like,\ +liked,likely,line,little,'ll,look,looking,looks,ltd,m,made,mainly,make,makes,many,may,maybe,me,mean,means,meantime,meanwhile,\ +merely,mg,might,million,miss,ml,more,moreover,most,mostly,mr,mrs,much,mug,must,my,myself,n,na,name,namely,nay,nd,near,nearly,\ +necessarily,necessary,need,needs,neither,never,nevertheless,new,next,nine,ninety,no,nobody,non,none,nonetheless,noone,nor,\ +normally,nos,not,noted,nothing,now,nowhere,o,obtain,obtained,obviously,of,off,often,oh,ok,okay,old,omitted,on,once,one,ones,\ +only,onto,or,ord,other,others,otherwise,ought,our,ours,ourselves,out,outside,over,overall,owing,own,p,page,pages,part,\ +particular,particularly,past,per,perhaps,placed,please,plus,poorly,possible,possibly,potentially,pp,predominantly,present,\ +previously,primarily,probably,promptly,proud,provides,put,q,que,quickly,quite,qv,r,ran,rather,rd,re,readily,really,recent,\ +recently,ref,refs,regarding,regardless,regards,related,relatively,research,respectively,resulted,resulting,results,right,run,s,\ +said,same,saw,say,saying,says,sec,section,see,seeing,seem,seemed,seeming,seems,seen,self,selves,sent,seven,several,shall,she,shed,\ +she'll,shes,should,shouldn't,show,showed,shown,showns,shows,significant,significantly,similar,similarly,since,six,slightly,so,\ +some,somebody,somehow,someone,somethan,something,sometime,sometimes,somewhat,somewhere,soon,sorry,specifically,specified,specify,\ +specifying,still,stop,strongly,sub,substantially,successfully,such,sufficiently,suggest,sup,sure,t,take,taken,taking,tell,tends,\ +th,than,thank,thanks,thanx,that,that'll,thats,that've,the,their,theirs,them,themselves,then,thence,there,thereafter,thereby,\ +thered,therefore,therein,there'll,thereof,therere,theres,thereto,thereupon,there've,these,they,theyd,they'll,theyre,they've,\ +think,this,those,thou,though,thoughh,thousand,throug,through,throughout,thru,thus,til,tip,to,together,too,took,toward,towards,\ +tried,tries,truly,try,trying,ts,twice,two,u,un,under,unfortunately,unless,unlike,unlikely,until,unto,up,upon,ups,us,use,used,\ +useful,usefully,usefulness,uses,using,usually,v,value,various,'ve,very,via,viz,vol,vols,vs,w,want,wants,was,wasn't,way,we,wed,\ +welcome,we'll,went,were,weren't,we've,what,whatever,what'll,whats,when,whence,whenever,where,whereafter,whereas,whereby,wherein,\ +wheres,whereupon,wherever,whether,which,while,whim,whither,who,whod,whoever,whole,who'll,whom,whomever,whos,whose,why,widely,\ +willing,wish,with,within,without,won't,words,world,would,wouldn't,www,x,y,yes,yet,you,youd,you'll,your,youre,yours,yourself,\ +yourselves,you've,z,zero"; \ No newline at end of file
--- a/toolkit/content/widgets/textbox.xml +++ b/toolkit/content/widgets/textbox.xml @@ -23,17 +23,17 @@ <stylesheet src="chrome://global/content/textbox.css"/> <stylesheet src="chrome://global/skin/textbox.css"/> </resources> <content> <children/> <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck"> <html:input class="textbox-input" anonid="input" - xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck"/> + xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck,list,autocomplete"/> </xul:hbox> </content> <implementation implements="nsIDOMXULTextBoxElement, nsIDOMXULLabeledControlElement"> <!-- nsIDOMXULLabeledControlElement --> <field name="crop">""</field> <field name="image">""</field> <field name="command">""</field> @@ -313,17 +313,17 @@ </handlers> </binding> <binding id="search-textbox" extends="chrome://global/content/bindings/textbox.xml#textbox"> <content> <children/> <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck" align="center"> <html:input class="textbox-input" anonid="input" mozactionhint="search" - xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,spellcheck"/> + xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,spellcheck,list,autocomplete"/> <xul:deck class="textbox-search-icons" anonid="search-icons"> <xul:image class="textbox-search-icon" anonid="searchbutton-icon" xbl:inherits="src=image,label=searchbuttonlabel,searchbutton,disabled"/> <xul:image class="textbox-search-clear" onclick="document.getBindingParent(this)._clearSearch();" label="&searchTextBox.clear.label;" xbl:inherits="disabled"/> </xul:deck>