Bug 1335905 - Experimenting datalists draft
authormanotejmeka <manotejmeka@gmail.com>
Mon, 20 Mar 2017 16:12:49 -0400
changeset 502476 0913c1470ff6e959a260dcee82311968f495fc59
parent 501227 98fc32dcdfba74192bfdc9e8ecf9129d64134057
child 550167 13a88c32e2448fb0b7c8f14421c7782ea748824b
push id50289
push userbmo:manotejmeka@gmail.com
push dateTue, 21 Mar 2017 20:33:18 +0000
bugs1335905
milestone55.0a1
Bug 1335905 - Experimenting datalists MozReview-Commit-ID: LPOxxj3qUN5
browser/components/preferences/in-content/findInPage.js
browser/components/preferences/in-content/jar.mn
browser/components/preferences/in-content/preferences.xul
browser/components/preferences/in-content/tries.js
toolkit/content/widgets/textbox.xml
--- 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>