From b9b85843572bf283f48285001e276ba7e61b63f6 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sun, 22 Feb 2009 13:37:51 +0100 Subject: updated to MediaWiki 1.14.0 --- skins/common/mwsuggest.js | 248 ++++++++++++++++++++++++++++++---------------- 1 file changed, 162 insertions(+), 86 deletions(-) (limited to 'skins/common/mwsuggest.js') diff --git a/skins/common/mwsuggest.js b/skins/common/mwsuggest.js index 52470060..061a6451 100644 --- a/skins/common/mwsuggest.js +++ b/skins/common/mwsuggest.js @@ -1,13 +1,13 @@ /* * OpenSearch ajax suggestion engine for MediaWiki - * + * * uses core MediaWiki open search support to fetch suggestions * and show them below search boxes and other inputs * * by Robert Stojnic (April 2008) */ - -// search_box_id -> Results object + +// search_box_id -> Results object var os_map = {}; // cached data, url -> json_text var os_cache = {}; @@ -31,16 +31,38 @@ var os_autoload_forms = new Array('searchform', 'searchform2', 'powersearch', 's var os_is_stopped = false; // max lines to show in suggest table var os_max_lines_per_suggest = 7; - -/** Timeout timer class that will fetch the results */ +// number of steps to animate expansion/contraction of container width +var os_animation_steps = 6; +// num of pixels of smallest step +var os_animation_min_step = 2; +// delay between steps (in ms) +var os_animation_delay = 30; +// max width of container in percent of normal size (1 == 100%) +var os_container_max_width = 2; +// currently active animation timer +var os_animation_timer = null; + +/** Timeout timer class that will fetch the results */ function os_Timer(id,r,query){ this.id = id; this.r = r; - this.query = query; + this.query = query; +} + +/** Timer user to animate expansion/contraction of container width */ +function os_AnimationTimer(r, target){ + this.r = r; + var current = document.getElementById(r.container).offsetWidth; + this.inc = Math.round((target-current) / os_animation_steps); + if(this.inc < os_animation_min_step && this.inc >=0) + this.inc = os_animation_min_step; // minimal animation step + if(this.inc > -os_animation_min_step && this.inc <0) + this.inc = -os_animation_min_step; + this.target = target; } /** Property class for single search box */ -function os_Results(name, formname){ +function os_Results(name, formname){ this.searchform = formname; // id of the searchform this.searchbox = name; // id of the searchbox this.container = name+"Suggest"; // div that holds results @@ -50,9 +72,9 @@ function os_Results(name, formname){ this.query = null; // last processed query this.results = null; // parsed titles this.resultCount = 0; // number of results - this.original = null; // query that user entered + this.original = null; // query that user entered this.selected = -1; // which result is selected - this.containerCount = 0; // number of results visible in container + this.containerCount = 0; // number of results visible in container this.containerRow = 0; // height of result field in the container this.containerTotal = 0; // total height of the container will all results this.visible = false; // if container is visible @@ -78,15 +100,15 @@ function os_showResults(r){ c.scrollTop = 0; c.style.visibility = "visible"; r.visible = true; - } + } } function os_operaWidthFix(x){ // TODO: better css2 incompatibility detection here if(is_opera || is_khtml || navigator.userAgent.toLowerCase().indexOf('firefox/1')!=-1){ - return x - 30; // opera&konqueror & old firefox don't understand overflow-x, estimate scrollbar width - } - return x; + return 30; // opera&konqueror & old firefox don't understand overflow-x, estimate scrollbar width + } + return 0; } function os_encodeQuery(value){ @@ -175,20 +197,20 @@ function os_getElementPosition(elemID){ function os_createContainer(r){ var c = document.createElement("div"); var s = document.getElementById(r.searchbox); - var pos = os_getElementPosition(r.searchbox); + var pos = os_getElementPosition(r.searchbox); var left = pos.left; var top = pos.top + s.offsetHeight; c.className = "os-suggest"; - c.setAttribute("id", r.container); - document.body.appendChild(c); - - // dynamically generated style params + c.setAttribute("id", r.container); + document.body.appendChild(c); + + // dynamically generated style params // IE workaround, cannot explicitely set "style" attribute c = document.getElementById(r.container); c.style.top = top+"px"; c.style.left = left+"px"; c.style.width = s.offsetWidth+"px"; - + // mouse event handlers c.onmouseover = function(event) { os_eventMouseover(r.searchbox, event); }; c.onmousemove = function(event) { os_eventMousemove(r.searchbox, event); }; @@ -198,13 +220,13 @@ function os_createContainer(r){ } /** change container height to fit to screen */ -function os_fitContainer(r){ +function os_fitContainer(r){ var c = document.getElementById(r.container); var h = os_availableHeight(r) - 20; var inc = r.containerRow; h = parseInt(h/inc) * inc; if(h < (2 * inc) && r.resultCount > 1) // min: two results - h = 2 * inc; + h = 2 * inc; if((h/inc) > os_max_lines_per_suggest ) h = inc * os_max_lines_per_suggest; if(h < r.containerTotal){ @@ -217,11 +239,41 @@ function os_fitContainer(r){ } /** If some entries are longer than the box, replace text with "..." */ function os_trimResultText(r){ + // find max width, first see if we could expand the container to fit it + var maxW = 0; + for(var i=0;i maxW) + maxW = e.offsetWidth; + } var w = document.getElementById(r.container).offsetWidth; - if(r.containerCount < r.resultCount){ - w -= 20; // give 20px for scrollbar + var fix = 0; + if(r.containerCount < r.resultCount){ + fix = 20; // give 20px for scrollbar } else - w = os_operaWidthFix(w); + fix = os_operaWidthFix(w); + if(fix < 4) + fix = 4; // basic padding + maxW += fix; + + // resize container to fit more data if permitted + var normW = document.getElementById(r.searchbox).offsetWidth; + var prop = maxW / normW; + if(prop > os_container_max_width) + prop = os_container_max_width; + else if(prop < 1) + prop = 1; + var newW = Math.round( normW * prop ); + if( w != newW ){ + w = newW; + if( os_animation_timer != null ) + clearInterval(os_animation_timer.id) + os_animation_timer = new os_AnimationTimer(r,w); + os_animation_timer.id = setInterval("os_animateChangeWidth()",os_animation_delay); + w -= fix; // this much is reserved + } + + // trim results if(w < 10) return; for(var i=0;i w && (e.offsetWidth < lastW || iteration<2)){ changedText = true; lastW = e.offsetWidth; - var l = e.innerHTML; + var l = e.innerHTML; e.innerHTML = l.substring(0,l.length-replace)+"..."; iteration++; replace = 4; // how many chars to replace @@ -245,8 +297,31 @@ function os_trimResultText(r){ } } +/** Invoked on timer to animate change in container width */ +function os_animateChangeWidth(){ + var r = os_animation_timer.r; + var c = document.getElementById(r.container); + var w = c.offsetWidth; + var normW = document.getElementById(r.searchbox).offsetWidth; + var normL = os_getElementPosition(r.searchbox).left; + var inc = os_animation_timer.inc; + var target = os_animation_timer.target; + var nw = w + inc; + if( (inc > 0 && nw >= target) || (inc <= 0 && nw <= target) ){ + // finished ! + c.style.width = target+"px"; + clearInterval(os_animation_timer.id) + os_animation_timer = null; + } else{ + // in-progress + c.style.width = nw+"px"; + if(document.documentElement.dir == "rtl") + c.style.left = (normL + normW + (target - nw) - os_animation_timer.target - 1)+"px"; + } +} + /** Handles data from XMLHttpRequest, and updates the suggest results */ -function os_updateResults(r, query, text, cacheKey){ +function os_updateResults(r, query, text, cacheKey){ os_cache[cacheKey] = text; r.query = query; r.original = query; @@ -254,7 +329,7 @@ function os_updateResults(r, query, text, cacheKey){ r.results = null; r.resultCount = 0; os_hideResults(r); - } else{ + } else{ try { var p = eval('('+text+')'); // simple json parse, could do a safer one if(p.length<2 || p[1].length == 0){ @@ -262,29 +337,30 @@ function os_updateResults(r, query, text, cacheKey){ r.resultCount = 0; os_hideResults(r); return; - } + } var c = document.getElementById(r.container); if(c == null) - c = os_createContainer(r); + c = os_createContainer(r); c.innerHTML = os_createResultTable(r,p[1]); // init container table sizes - var t = document.getElementById(r.resultTable); - r.containerTotal = t.offsetHeight; + var t = document.getElementById(r.resultTable); + r.containerTotal = t.offsetHeight; r.containerRow = t.offsetHeight / r.resultCount; - os_trimResultText(r); + os_fitContainer(r); + os_trimResultText(r); os_showResults(r); } catch(e){ // bad response from server or such - os_hideResults(r); + os_hideResults(r); os_cache[cacheKey] = null; } - } + } } /** Create the result table to be placed in the container div */ function os_createResultTable(r, results){ var c = document.getElementById(r.container); - var width = os_operaWidthFix(c.offsetWidth); + var width = c.offsetWidth - os_operaWidthFix(c.offsetWidth); var html = ""; r.results = new Array(); r.resultCount = results.length; @@ -299,14 +375,14 @@ function os_createResultTable(r, results){ /** Fetch namespaces from checkboxes or hidden fields in the search form, if none defined use wgSearchNamespaces global */ -function os_getNamespaces(r){ +function os_getNamespaces(r){ var namespaces = ""; var elements = document.forms[r.searchform].elements; for(i=0; i < elements.length; i++){ var name = elements[i].name; - if(typeof name != 'undefined' && name.length > 2 - && name[0]=='n' && name[1]=='s' - && ((elements[i].type=='checkbox' && elements[i].checked) + if(typeof name != 'undefined' && name.length > 2 + && name[0]=='n' && name[1]=='s' + && ((elements[i].type=='checkbox' && elements[i].checked) || (elements[i].type=='hidden' && elements[i].value=="1")) ){ if(namespaces!="") namespaces+="|"; @@ -321,7 +397,7 @@ function os_getNamespaces(r){ /** Update results if user hasn't already typed something else */ function os_updateIfRelevant(r, query, text, cacheKey){ var t = document.getElementById(r.searchbox); - if(t != null && t.value == query){ // check if response is still relevant + if(t != null && t.value == query){ // check if response is still relevant os_updateResults(r, query, text, cacheKey); } r.query = query; @@ -337,22 +413,22 @@ function os_delayedFetch(){ var path = wgMWSuggestTemplate.replace("{namespaces}",os_getNamespaces(r)) .replace("{dbname}",wgDBname) .replace("{searchTerms}",os_encodeQuery(query)); - + // try to get from cache, if not fetch using ajax var cached = os_cache[path]; if(cached != null){ os_updateIfRelevant(r, query, cached, path); - } else{ + } else{ var xmlhttp = sajax_init_object(); if(xmlhttp){ - try { + try { xmlhttp.open("GET", path, true); xmlhttp.onreadystatechange=function(){ - if (xmlhttp.readyState==4 && typeof os_updateIfRelevant == 'function') { + if (xmlhttp.readyState==4 && typeof os_updateIfRelevant == 'function') { os_updateIfRelevant(r, query, xmlhttp.responseText, path); } }; - xmlhttp.send(null); + xmlhttp.send(null); } catch (e) { if (window.location.hostname == "localhost") { alert("Your browser blocks XMLHttpRequest to 'localhost', try using a real hostname for development/testing."); @@ -370,23 +446,23 @@ function os_fetchResults(r, query, timeout){ return; } else if(query == r.query) return; // no change - + os_is_stopped = false; // make sure we're running - - /* var cacheKey = wgDBname+":"+query; + + /* var cacheKey = wgDBname+":"+query; var cached = os_cache[cacheKey]; if(cached != null){ os_updateResults(r,wgDBname,query,cached); return; } */ - + // cancel any pending fetches if(os_timer != null && os_timer.id != null) clearTimeout(os_timer.id); - // schedule delayed fetching of results + // schedule delayed fetching of results if(timeout != 0){ os_timer = new os_Timer(setTimeout("os_delayedFetch()",timeout),r,query); - } else{ + } else{ os_timer = new os_Timer(null,r,query); os_delayedFetch(); // do it now! } @@ -397,11 +473,11 @@ function os_changeHighlight(r, cur, next, updateSearchBox){ if (next >= r.resultCount) next = r.resultCount-1; if (next < -1) - next = -1; + next = -1; r.selected = next; if (cur == next) return; // nothing to do. - + if(cur >= 0){ var curRow = document.getElementById(r.resultTable + cur); if(curRow != null) @@ -415,7 +491,7 @@ function os_changeHighlight(r, cur, next, updateSearchBox){ newText = r.results[next]; } else newText = r.original; - + // adjust the scrollbar if any if(r.containerCount < r.resultCount){ var c = document.getElementById(r.container); @@ -426,10 +502,10 @@ function os_changeHighlight(r, cur, next, updateSearchBox){ else if(next >= vEnd) c.scrollTop = (next - r.containerCount + 1) * r.containerRow; } - + // update the contents of the search box if(updateSearchBox){ - os_updateSearchQuery(r,newText); + os_updateSearchQuery(r,newText); } } @@ -463,8 +539,8 @@ function os_getTarget(e){ /******************** - * Keyboard events - ********************/ + * Keyboard events + ********************/ /** Event handler that will fetch results on keyup */ function os_eventKeyup(e){ @@ -472,8 +548,8 @@ function os_eventKeyup(e){ var r = os_map[targ.id]; if(r == null) return; // not our event - - // some browsers won't generate keypressed for arrow keys, catch it + + // some browsers won't generate keypressed for arrow keys, catch it if(os_keypressed_count == 0){ os_processKey(r,os_cur_keypressed,targ); } @@ -484,8 +560,8 @@ function os_eventKeyup(e){ /** catch arrows up/down and escape to hide the suggestions */ function os_processKey(r,keypressed,targ){ if (keypressed == 40){ // Arrow Down - if (r.visible) { - os_changeHighlight(r, r.selected, r.selected+1, true); + if (r.visible) { + os_changeHighlight(r, r.selected, r.selected+1, true); } else if(os_timer == null){ // user wants to get suggestions now r.query = ""; @@ -505,12 +581,12 @@ function os_processKey(r,keypressed,targ){ } /** When keys is held down use a timer to output regular events */ -function os_eventKeypress(e){ +function os_eventKeypress(e){ var targ = os_getTarget(e); var r = os_map[targ.id]; if(r == null) return; // not our event - + var keypressed = os_cur_keypressed; if(keypressed == 38 || keypressed == 40){ var d = new Date() @@ -520,7 +596,7 @@ function os_eventKeypress(e){ return; } } - + os_keypressed_count++; os_processKey(r,keypressed,targ); } @@ -532,21 +608,21 @@ function os_eventKeydown(e){ var r = os_map[targ.id]; if(r == null) return; // not our event - + os_mouse_moved = false; - os_cur_keypressed = (window.Event) ? e.which : e.keyCode; + os_cur_keypressed = (e.keyCode == undefined) ? e.which : e.keyCode; os_last_keypress = 0; os_keypressed_count = 0; } /** Event: loss of focus of input box */ -function os_eventBlur(e){ +function os_eventBlur(e){ var targ = os_getTarget(e); var r = os_map[targ.id]; if(r == null) return; // not our event - if(!os_mouse_pressed) + if(!os_mouse_pressed) os_hideResults(r); } @@ -558,19 +634,19 @@ function os_eventFocus(e){ /******************** - * Mouse events - ********************/ + * Mouse events + ********************/ /** Mouse over the container */ function os_eventMouseover(srcId, e){ - var targ = os_getTarget(e); + var targ = os_getTarget(e); var r = os_map[srcId]; if(r == null || !os_mouse_moved) return; // not our event var num = os_getNumberSuffix(targ.id); if(num >= 0) os_changeHighlight(r,r.selected,num,false); - + } /* Get row where the event occured (from its id) */ @@ -596,7 +672,7 @@ function os_eventMousedown(srcId, e){ if(r == null) return; // not our event var num = os_getNumberSuffix(targ.id); - + os_mouse_pressed = true; if(num >= 0){ os_mouse_num = num; @@ -604,7 +680,7 @@ function os_eventMousedown(srcId, e){ } // keep the focus on the search field document.getElementById(r.searchbox).focus(); - + return false; // prevents selection } @@ -615,7 +691,7 @@ function os_eventMouseup(srcId, e){ if(r == null) return; // not our event var num = os_getNumberSuffix(targ.id); - + if(num >= 0 && os_mouse_num == num){ os_updateSearchQuery(r,r.results[num]); os_hideResults(r); @@ -654,10 +730,10 @@ function os_eventOnsubmit(e){ var r = os_map[os_autoload_inputs[i]]; if(r != null){ var b = document.getElementById(r.searchform); - if(b != null && b == targ){ + if(b != null && b == targ){ // set query value so the handler won't try to fetch additional results r.query = document.getElementById(r.searchbox).value; - } + } os_hideResults(r); } } @@ -674,7 +750,7 @@ function os_hookEvent(element, hookName, hookFunct) { /** Init Result objects and event handlers */ function os_initHandlers(name, formname, element){ - var r = new os_Results(name, formname); + var r = new os_Results(name, formname); // event handler os_hookEvent(element, "keyup", function(event) { os_eventKeyup(event); }); os_hookEvent(element, "keydown", function(event) { os_eventKeydown(event); }); @@ -684,10 +760,10 @@ function os_initHandlers(name, formname, element){ element.setAttribute("autocomplete","off"); // stopping handler os_hookEvent(document.getElementById(formname), "submit", function(event){ return os_eventOnsubmit(event); }); - os_map[name] = r; + os_map[name] = r; // toggle link if(document.getElementById(r.toggle) == null){ - // TODO: disable this while we figure out a way for this to work in all browsers + // TODO: disable this while we figure out a way for this to work in all browsers /* if(name=='searchInput'){ // special case: place above the main search box var t = os_createToggle(r,"os-suggest-toggle"); @@ -708,7 +784,7 @@ function os_initHandlers(name, formname, element){ t.style.visibility = "visible"; } */ } - + } /** Return the span element that contains the toggle link */ @@ -722,7 +798,7 @@ function os_createToggle(r,className){ var msg = document.createTextNode(wgMWSuggestMessages[0]); link.appendChild(msg); t.appendChild(link); - return t; + return t; } /** Call when user clicks on some of the toggle links */ @@ -732,7 +808,7 @@ function os_toggle(inputId,formName){ if(r == null){ os_enableSuggestionsOn(inputId,formName); r = os_map[inputId]; - msg = wgMWSuggestMessages[0]; + msg = wgMWSuggestMessages[0]; } else{ os_disableSuggestionsOn(inputId,formName); msg = wgMWSuggestMessages[1]; @@ -756,7 +832,7 @@ function os_disableSuggestionsOn(inputId){ os_hideResults(r); // turn autocomplete on ! document.getElementById(inputId).setAttribute("autocomplete","on"); - // remove descriptor + // remove descriptor os_map[inputId] = null; } } @@ -769,7 +845,7 @@ function os_MWSuggestInit() { element = document.getElementById( id ); if(element != null) os_initHandlers(id,form,element); - } + } } hookEvent("load", os_MWSuggestInit); -- cgit v1.2.3-54-g00ecf