From 9af30a1eda807a6269bbd921b5d2f2a71e2d362d Mon Sep 17 00:00:00 2001 From: Alexander Butenko Date: Sat, 19 Dec 2009 10:45:39 +0100 Subject: [PATCH] Optimize and clean-up form history Switch from g_file_test() to g_access() Initialize suggestions once the DOM is ready and only once No need to handle WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED [JS] Hide suggestions if search pattern was cleared [JS] Human usable up/ down navigation in the suggestion window Reusing a single suggestion window introduced a regression. Suggestions were filling into the wrong editbox if there was more than 1 on the page. Some comments fixed and style clean-ups Removed dead code --- data/autosuggestcontrol.css | 5 +- data/autosuggestcontrol.js | 108 ++++++++++++++++-------------------- extensions/formhistory.c | 9 ++- 3 files changed, 52 insertions(+), 70 deletions(-) diff --git a/data/autosuggestcontrol.css b/data/autosuggestcontrol.css index db1b1006..a34bc268 100644 --- a/data/autosuggestcontrol.css +++ b/data/autosuggestcontrol.css @@ -1,9 +1,6 @@ div.suggestions { - -moz-box-sizing: border-box; - box-sizing: border-box; - border: 1px solid #cccccc; + -webkit-appearance: listbox; text-align: left; - background-color: #ffffff; position: absolute; z-index: 999; } diff --git a/data/autosuggestcontrol.js b/data/autosuggestcontrol.js index fc82cbf1..23619bcc 100644 --- a/data/autosuggestcontrol.js +++ b/data/autosuggestcontrol.js @@ -38,7 +38,6 @@ function AutoSuggestControl(oTextbox /*:HTMLInputElement*/, * @param bTypeAhead If the control should provide a type ahead suggestion. */ AutoSuggestControl.prototype.autosuggest = function (aSuggestions /*:Array*/) { - //make sure theres at least one suggestion if (aSuggestions.length > 0) { this.showSuggestions(aSuggestions); } else { @@ -51,35 +50,16 @@ AutoSuggestControl.prototype.autosuggest = function (aSuggestions /*:Array*/) { * @scope private */ AutoSuggestControl.prototype.createDropDown = function () { - var oThis = this; - var sDiv = document.getElementById("suggestions_box"); - - if (sDiv) + if (sDiv) { this.layer = sDiv; - else - { - this.layer = document.createElement("div"); - this.layer.className = "suggestions"; - this.layer.id = "suggestions_box"; - this.layer.style.visibility = "hidden"; - this.layer.style.width = this.textbox.offsetWidth; + return; } - this.layer.onmousedown = - this.layer.onmouseup = - this.layer.onmouseover = function (oEvent) { - oEvent = oEvent || window.event; - oTarget = oEvent.target || oEvent.srcElement; - - if (oEvent.type == "mousedown") { - oThis.textbox.value = oTarget.firstChild.nodeValue; - oThis.hideSuggestions(); - } else if (oEvent.type == "mouseover") { - oThis.highlightSuggestion(oTarget); - } else { - oThis.textbox.focus(); - } - }; + this.layer = document.createElement("div"); + this.layer.className = "suggestions"; + this.layer.id = "suggestions_box"; + this.layer.style.visibility = "hidden"; + this.layer.style.width = this.textbox.offsetWidth; document.body.appendChild(this.layer); }; @@ -91,12 +71,10 @@ AutoSuggestControl.prototype.createDropDown = function () { AutoSuggestControl.prototype.getLeft = function () /*:int*/ { var oNode = this.textbox; var iLeft = 0; - - while(oNode.tagName != "BODY") { + while (oNode.tagName != "BODY") { iLeft += oNode.offsetLeft; oNode = oNode.offsetParent; } - return iLeft; }; @@ -141,17 +119,15 @@ AutoSuggestControl.prototype.handleKeyDown = function (oEvent /*:Event*/) { */ AutoSuggestControl.prototype.handleKeyUp = function (oEvent /*:Event*/) { var iKeyCode = oEvent.keyCode; - //for backspace (8) and delete (46), shows suggestions without typeahead if (iKeyCode == 8 || iKeyCode == 46) { this.provider.requestSuggestions(this); //make sure not to interfere with non-character keys } else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode < 46) || (iKeyCode >= 112 && iKeyCode <= 123) ) { //ignore } else if (oEvent.ctrlKey) { - iKeyCode = 0; this.hideSuggestions(); } else { - //request suggestions from the suggestion provider with typeahead + //request suggestions from the suggestion provider this.provider.requestSuggestions(this); } }; @@ -227,11 +203,15 @@ AutoSuggestControl.prototype.init = function () { AutoSuggestControl.prototype.nextSuggestion = function () { var cSuggestionNodes = this.layer.childNodes; - if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) { - var oNode = cSuggestionNodes[++this.cur]; - this.highlightSuggestion(oNode); - this.textbox.value = oNode.firstChild.nodeValue; - } + if (!cSuggestionNodes.length) + return; + + if (this.cur == cSuggestionNodes.length-1) + this.cur = -1; + + var oNode = cSuggestionNodes[++this.cur]; + this.highlightSuggestion(oNode); + this.textbox.value = oNode.firstChild.nodeValue; }; /** @@ -242,24 +222,15 @@ AutoSuggestControl.prototype.nextSuggestion = function () { AutoSuggestControl.prototype.previousSuggestion = function () { var cSuggestionNodes = this.layer.childNodes; - if (cSuggestionNodes.length > 0 && this.cur > 0) { - var oNode = cSuggestionNodes[--this.cur]; - this.highlightSuggestion(oNode); - this.textbox.value = oNode.firstChild.nodeValue; - } -}; + if (!cSuggestionNodes.length) + return; -/** - * Selects a range of text in the textbox. - * @scope public - * @param iStart The start index (base 0) of the selection. - * @param iLength The number of characters to select. - */ -AutoSuggestControl.prototype.selectRange = function (iStart /*:int*/, iLength /*:int*/) { - if (this.textbox.setSelectionRange) { - this.textbox.setSelectionRange(iStart, iLength); - } - this.textbox.focus(); + if (this.cur == -1 || this.cur == 0) + this.cur = cSuggestionNodes.length; + + var oNode = cSuggestionNodes[--this.cur]; + this.highlightSuggestion(oNode); + this.textbox.value = oNode.firstChild.nodeValue; }; /** @@ -269,6 +240,7 @@ AutoSuggestControl.prototype.selectRange = function (iStart /*:int*/, iLength /* * @param aSuggestions An array of suggestions for the control. */ AutoSuggestControl.prototype.showSuggestions = function (aSuggestions /*:Array*/) { + var oThis = this; var oDiv = null; this.layer.innerHTML = ""; //clear contents of the layer @@ -282,6 +254,21 @@ AutoSuggestControl.prototype.showSuggestions = function (aSuggestions /*:Array*/ this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px"; this.layer.style.visibility = "visible"; this.layer.style.position = "absolute"; + this.layer.onmousedown = + this.layer.onmouseup = + this.layer.onmouseover = function (oEvent) { + var oEvent = oEvent || window.event; + var oTarget = oEvent.target || oEvent.srcElement; + + if (oEvent.type == "mousedown") { + oThis.textbox.value = oTarget.firstChild.nodeValue; + oThis.hideSuggestions(); + } else if (oEvent.type == "mouseover") { + oThis.highlightSuggestion(oTarget); + } else { + oThis.textbox.focus(); + } + }; }; /** @@ -295,14 +282,13 @@ FormSuggestions.prototype.requestSuggestions = function (oAutoSuggestControl /*: if (!this.suggestions) return; - if (!sTextboxValue.length) - return; //search for matching suggestions - for (var i=0; i < this.suggestions.length; i++) { - if (this.suggestions[i].toLowerCase().indexOf(sTextboxValue) == 0) { - aSuggestions.push(this.suggestions[i]); - } + if (sTextboxValue.length) + for (var i=0; i < this.suggestions.length; i++) { + if (this.suggestions[i].toLowerCase().indexOf(sTextboxValue) == 0) { + aSuggestions.push(this.suggestions[i]); + } } //provide suggestions to the control oAutoSuggestControl.autosuggest(aSuggestions); diff --git a/extensions/formhistory.c b/extensions/formhistory.c index b4ec3787..3594746c 100644 --- a/extensions/formhistory.c +++ b/extensions/formhistory.c @@ -38,13 +38,13 @@ formhistory_prepare_js () gchar* data_path = g_build_filename (MDATADIR, PACKAGE_NAME, "res", NULL); file = g_build_filename (data_path,"/autosuggestcontrol.js",NULL); - if (!g_file_test (file, G_FILE_TEST_EXISTS)) + if (g_access (file, F_OK) != 0) return FALSE; g_file_get_contents (file, &autosuggest, NULL, NULL); g_strchomp (autosuggest); file = g_build_filename (data_path,"/autosuggestcontrol.css",NULL); - if (!g_file_test (file, G_FILE_TEST_EXISTS)) + if (g_access (file, F_OK) != 0) return FALSE; g_file_get_contents (file, &style, NULL, NULL); g_strchomp (style); @@ -58,7 +58,6 @@ formhistory_prepare_js () jsforms = g_strdup_printf ( "%s" - "window.addEventListener ('load', function () { initSuggestions (); }, true);" "window.addEventListener ('DOMContentLoaded'," "function () {" " var styles = document.getElementsByTagName('style');" @@ -66,6 +65,7 @@ formhistory_prepare_js () " if (styles[i].getAttribute('title') == 'formhistory')" " return;" " }" + " initSuggestions ();" " var mystyle = document.createElement('style');" " mystyle.setAttribute('type', 'text/css');" " mystyle.setAttribute('title', 'formhistory');" @@ -215,8 +215,7 @@ formhistory_navigation_decision_cb (WebKitWebView* web_view, "}" "dumpForm (document.getElementsByTagName('input'))"; - if (webkit_web_navigation_action_get_reason (action) == WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED - || webkit_web_navigation_action_get_reason (action) == WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED) + if (webkit_web_navigation_action_get_reason (action) == WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED) { gchar* value = sokoke_js_script_eval (js_context, script, NULL); if (value)