From baa8a5945e29594a3678ce446a643e6a9b56fd93 Mon Sep 17 00:00:00 2001 From: x2001corpet Date: Sat, 31 Mar 2007 15:03:05 +0000 Subject: [PATCH] autocompletion ameliorations git-svn-id: svn+ssh://murphy/home/svn/platal/trunk@1636 839d8a87-29fc-0310-9880-83ba4fa771e5 --- Makefile | 2 - htdocs/css/base.css | 15 +- htdocs/javascript/jquery.autocomplete.js | 326 +++++++++++++++++++++++++++++++ modules/search.php | 4 +- templates/search/adv.form.tpl | 2 +- 5 files changed, 341 insertions(+), 8 deletions(-) create mode 100644 htdocs/javascript/jquery.autocomplete.js diff --git a/Makefile b/Makefile index 047d482..3859605 100644 --- a/Makefile +++ b/Makefile @@ -109,8 +109,6 @@ htdocs/css/banana.css: jquery: htdocs/javascript/jquery.js htdocs/javascript/jquery.autocomplete.js htdocs/javascript/jquery.js: wget http://jquery.com/src/jquery-latest.pack.js -O htdocs/javascript/jquery.js -q -htdocs/javascript/jquery.autocomplete.js: - wget http://www.dyve.net/jquery/js/jquery.autocomplete.js -O htdocs/javascript/jquery.autocomplete.js -q ################################################################################ diff --git a/htdocs/css/base.css b/htdocs/css/base.css index 1e7cc4c..c305aec 100644 --- a/htdocs/css/base.css +++ b/htdocs/css/base.css @@ -18,6 +18,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ***************************************************************************/ +.ac_results { + background-color: window; + border: 1px solid; + overflow: hidden; + padding:0px; +} + .ac_results ul { padding:0px; margin:0px; @@ -26,12 +33,14 @@ .ac_results li { display:block; padding: 2px; - background: white; - color: black; cursor:pointer; } .ac_results .over { - background: yellow; + background: highlight; + color: highlighttext; } +.ac_loading { + background:window url(images/wait.gif) no-repeat scroll right center; +} /* vim: set et ts=4 sts=4 sw=4: */ diff --git a/htdocs/javascript/jquery.autocomplete.js b/htdocs/javascript/jquery.autocomplete.js new file mode 100644 index 0000000..18d1fe5 --- /dev/null +++ b/htdocs/javascript/jquery.autocomplete.js @@ -0,0 +1,326 @@ +/* jQuery autocomplete Copyright Dylan Verheul + * Licensed like jQuery, see http://docs.jquery.com/License + */ + +$.autocomplete = function(input, options) { + // Create a link to self + var me = this; + + // Create jQuery object for input element + var $input = $(input).attr("autocomplete", "off");; + + // Apply inputClass if necessary + if (options.inputClass) $input.addClass(options.inputClass); + + // Create results + var results = document.createElement("div"); + // Create jQuery object for results + var $results = $(results); + // Set default values for results + var pos = findPos(input); + $results.hide().addClass(options.resultsClass).css({ + position: "absolute", + top: (pos.y + input.offsetHeight) + "px", + left: pos.x + "px", + }); + // Add to body element + $(input).parent().append(results); + + input.autocompleter = me; + input.lastSelected = $input.val(); + + var timeout = null; + var prev = ""; + var active = -1; + var cache = {}; + var keyb = false; + + $input + .keydown(function(e) { + switch(e.keyCode) { + case 38: // up + e.preventDefault(); + moveSelect(-1); + break; + case 40: // down + e.preventDefault(); + moveSelect(1); + break; + case 9: // tab + case 13: // return + if (selectCurrent()) { + e.preventDefault(); + } + break; + default: + active = -1; + if (timeout) clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }) + .blur(function() { + hideResults(); + }); + + hideResultsNow(); + + function onChange() { + var v = $input.val(); + if (v == prev) return; + prev = v; + if (v.length >= options.minChars) { + $input.addClass(options.loadingClass); + requestData(v); + } else { + $input.removeClass(options.loadingClass); + $results.hide(); + } + }; + + function moveSelect(step) { + + var lis = $("li", results); + if (!lis) return; + + active += step; + + if (active < 0) { + active = 0; + } else if (active >= lis.size()) { + active = lis.size() - 1; + } + + lis.removeClass("over"); + + $(lis[active]).addClass("over"); + + // Weird behaviour in IE + // if (lis[active] && lis[active].scrollIntoView) { + // lis[active].scrollIntoView(false); + // } + + }; + + function selectCurrent() { + var li = $("li.over", results)[0]; + if (!li) { + var $li = $("li", results); + if (options.selectOnly) { + if ($li.length == 1) li = $li[0]; + } else if (options.selectFirst) { + li = $li[0]; + } + } + if (li) { + selectItem(li); + return true; + } else { + return false; + } + }; + + function selectItem(li) { + if (!li) { + li = document.createElement("li"); + li.extra = []; + li.selectValue = ""; + } + var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML); + input.lastSelected = v; + prev = v; + $results.html(""); + $input.val(v); + hideResultsNow(); + if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1); + }; + + function hideResults() { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + if (timeout) clearTimeout(timeout); + $input.removeClass(options.loadingClass); + if ($results.is(":visible")) { + $results.hide(); + } + if (options.mustMatch) { + var v = $input.val(); + if (v != input.lastSelected) { + selectItem(null); + } + } + }; + + function receiveData(q, data) { + if (data) { + $input.removeClass(options.loadingClass); + results.innerHTML = ""; + if ($.browser.msie) { + // we put a styled iframe behind the calendar so HTML SELECT elements don't show through + $results.append(document.createElement('iframe')); + } + results.appendChild(dataToDom(data)); + $results.show(); + } else { + hideResultsNow(); + } + }; + + function parseData(data) { + if (!data) return null; + var parsed = []; + var rows = data.split(options.lineSeparator); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + parsed[parsed.length] = row.split(options.cellSeparator); + } + } + return parsed; + }; + + function dataToDom(data) { + var ul = document.createElement("ul"); + var num = data.length; + for (var i=0; i < num; i++) { + var row = data[i]; + if (!row) continue; + var li = document.createElement("li"); + if (options.formatItem) { + li.innerHTML = options.formatItem(row, i, num); + li.selectValue = row[0]; + } else { + li.innerHTML = row[0]; + } + var extra = null; + if (row.length > 1) { + extra = []; + for (var j=1; j < row.length; j++) { + extra[extra.length] = row[j]; + } + } + li.extra = extra; + ul.appendChild(li); + $(li).hover( + function() { $("li", ul).removeClass("over"); $(this).addClass("over"); }, + function() { $(this).removeClass("over"); } + ).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) }); + } + return ul; + }; + + function requestData(q) { + if (!options.matchCase) q = q.toLowerCase(); + var data = options.cacheLength ? loadFromCache(q) : null; + if (data) { + receiveData(q, data); + } else { + $.get(makeUrl(q), function(data) { + data = parseData(data) + addToCache(q, data); + receiveData(q, data); + }); + } + }; + + function makeUrl(q) { + var url = options.url + "?q=" + q; + for (var i in options.extraParams) { + url += "&" + i + "=" + options.extraParams[i]; + } + return url; + }; + + function loadFromCache(q) { + if (!q) return null; + if (cache[q]) return cache[q]; + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var qs = q.substr(0, i); + var c = cache[qs]; + if (c) { + var csub = []; + for (var j = 0; j < c.length; j++) { + var x = c[j]; + var x0 = x[0]; + if (matchSubset(x0, q)) { + csub[csub.length] = x; + } + } + return csub; + } + } + } + return null; + }; + + function matchSubset(s, sub) { + if (!options.matchCase) s = s.toLowerCase(); + var i = s.indexOf(sub); + if (i == -1) return false; + return i == 0 || options.matchContains; + }; + + this.flushCache = function() { + cache = {}; + }; + + this.setExtraParams = function(p) { + options.extraParams = p; + }; + + function addToCache(q, data) { + if (!data || !q || !options.cacheLength) return; + if (!cache.length || cache.length > options.cacheLength) { + cache = {}; + cache.length = 1; // we know we're adding something + } else if (!cache[q]) { + cache.length++; + } + cache[q] = data; + }; + + function findPos(obj) { + var curleft = obj.offsetLeft || 0; + var curtop = obj.offsetTop || 0; + while (obj = obj.offsetParent) { + curleft += obj.offsetLeft + curtop += obj.offsetTop + } + return {x:curleft,y:curtop}; + } +} + +$.fn.autocomplete = function(url, options) { + // Make sure options exists + options = options || {}; + // Set url as option + options.url = url; + // Set default values for required options + options.inputClass = options.inputClass || "ac_input"; + options.resultsClass = options.resultsClass || "ac_results"; + options.lineSeparator = options.lineSeparator || "\n"; + options.cellSeparator = options.cellSeparator || "|"; + options.minChars = options.minChars || 1; + options.delay = options.delay || 400; + options.matchCase = options.matchCase || 0; + options.matchSubset = typeof(options.matchSubset) != 'undefined' ? options.matchSubset : 1; + options.matchContains = options.matchContains || 0; + options.cacheLength = options.cacheLength || 1; + options.mustMatch = options.mustMatch || 0; + options.extraParams = options.extraParams || {}; + options.loadingClass = options.loadingClass || "ac_loading"; + options.selectFirst = options.selectFirst || false; + options.selectOnly = options.selectOnly || false; + + this.each(function() { + var input = this; + new $.autocomplete(input, options); + }); + + // Don't break the chain + return this; +} diff --git a/modules/search.php b/modules/search.php index 300b65b..89104be 100644 --- a/modules/search.php +++ b/modules/search.php @@ -247,7 +247,7 @@ class SearchModule extends PLModule $page->assign('url_search_form', $search->make_url(Array('rechercher'=>0))); if (!Env::i('with_soundex')) { - $page->assign('with_soundex', $search->make_url(Array()) . "&with_soundex=1"); + $page->assign('with_soundex', $search->make_url(Array()) . "&with_soundex=1"); } $nb_tot = $search->show(); @@ -295,7 +295,7 @@ class SearchModule extends PLModule switch ($type) { case 'firstname': $field = 'prenom'; break; case 'name': $field = 'nom'; break; - case 'nickname': $field = 'profile_nick'; break; + case 'nickname': $field = 'profile_nick'; $db = 'auth_user_quick'; break; case 'entreprise': $db = 'entreprises'; $field = 'entreprise'; $unique='uid'; break; default: exit(); } diff --git a/templates/search/adv.form.tpl b/templates/search/adv.form.tpl index 99ed048..6027b17 100644 --- a/templates/search/adv.form.tpl +++ b/templates/search/adv.form.tpl @@ -39,7 +39,7 @@ } $(document).ready(function() { $(".autocomplete").each(function() { - $(this).autocomplete("search/autocomplete/"+this.name,{selectOnly:1,formatItem:format_autocomplete}); + $(this).autocomplete("search/autocomplete/"+this.name,{selectOnly:1,formatItem:format_autocomplete,matchSubset:0,width:$(this).width()}); }); }); {/literal} -- 2.1.4