From: Jens Frank Date: Sun, 26 Mar 2006 19:03:14 +0000 (+0000) Subject: Ajax based suggest feature for the search box X-Git-Tag: 1.6.0~123 X-Git-Url: http://git.cyclocoop.org/url?a=commitdiff_plain;h=86c655d7abce201fd68f0e73e413d583f20e9161;p=lhc%2Fweb%2Fwiklou.git Ajax based suggest feature for the search box --- diff --git a/README b/README index d487a2f341..051b947371 100644 --- a/README +++ b/README @@ -49,6 +49,16 @@ not funded any of the development work. also released into the public domain, which does not impair the obligations of users under the GPL for use of the whole code or other sections thereof. +[2] MediaWiki makes use of the Sajax Toolkit by modernmethod, + http://www.modernmethod.com/sajax/ + which has the following license: + + 'This work is licensed under the Creative Commons Attribution + License. To view a copy of this license, visit + http://creativecommons.org/licenses/by/2.0/ or send a letter + to Creative Commons, 559 Nathan Abbott Way, + Stanford, California 94305, USA.' + Many thanks to the Wikipedia regulars for testing and suggestions. The official website for mediawiki is located at: diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ed1c919643..7097d2cc26 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -717,6 +717,8 @@ fully support the editing toolbar, but was found to be too confusing. * (bug 4729) Add user preference that marks a user's edits as patrolled if user is able to * (bug 4497,4704,5010) Added some new language codes. * (bug 4630) Add user preference to prompt users when entering blank edit summaries +* Added optional suggest feature for the search box. Set wgUseAjax to true to + enable it. === Caveats === diff --git a/ajax.php b/ajax.php new file mode 100644 index 0000000000..933134f210 --- /dev/null +++ b/ajax.php @@ -0,0 +1,71 @@ +writeHeader(); + echo $result; +} +exit; + +?> diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php new file mode 100644 index 0000000000..5d9582045c --- /dev/null +++ b/includes/AjaxFunctions.php @@ -0,0 +1,153 @@ +>6)+192).chr(($num&63)+128); + if ( $num<65536 ) + return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128); + if ( $num<2097152 ) + return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128) .chr(($num&63)+128); + return ''; +} + +class AjaxCachePolicy { + var $policy; + + function AjaxCachePolicy( $policy = null ) { + $this->policy = $policy; + } + + function setPolicy( $policy ) { + $this->policy = $policy; + } + + function writeHeader() { + header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + if ( is_null( $this->policy ) ) { + // Bust cache in the head + header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past + // always modified + header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 + header ("Pragma: no-cache"); // HTTP/1.0 + } else { + header ("Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->policy ) . " GMT"); + header ("Cache-Control: public,max-age={$this->policy}"); + } + } +} + + +function wfSajaxSearch( $term ) { + global $wgContLang, $wgUser, $wgRequest, $wgAjaxCachePolicy; + $limit = 16; + + $l = new Linker; + + $term = str_replace( ' ', '_', $wgContLang->ucfirst( + $wgContLang->checkTitleEncoding( $wgContLang->recodeInput( js_unescape( $term ) ) ) + ) ); + + if ( strlen( str_replace( '_', '', $term ) )<3 ) + return; + + $wgAjaxCachePolicy->setPolicy( 30*60 ); + + $db =& wfGetDB( DB_SLAVE ); + $res = $db->select( 'page', 'page_title', + array( 'page_namespace' => 0, + "page_title LIKE '". $db->strencode( $term) ."%'" ), + "wfSajaxSearch", + array( 'LIMIT' => $limit+1 ) + ); + + $r = ""; + + $i=0; + while ( ( $row = $db->fetchObject( $res ) ) && ( ++$i <= $limit ) ) { + $nt = Title::newFromDBkey( $row->page_title ); + $r .= '
  • ' . $l->makeKnownLinkObj( $nt ) . "
  • \n"; + } + if ( $i > $limit ) { + $more = '' . $l->makeKnownLink( $wgContLang->specialPage( "Allpages" ), + wfMsg('moredotdotdot'), + "namespace=0&from=" . wfUrlEncode ( $term ) ) . + ''; + } else { + $more = ''; + } + + $term = htmlspecialchars( $term ); + return '
    ' + . wfMsg( 'hideresults' ) . '
    ' + . '

    '.wfMsg('search') + . '

    '.wfMsg('searchquery', $term) . '

    " . wfMsg( 'articletitles', $term ) . "

    " + . ''.$more; +} + +?> diff --git a/includes/Article.php b/includes/Article.php index 2d00bf1c38..210f775566 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -490,7 +490,7 @@ class Article { } $revision = Revision::newFromId( $this->mLatest ); if( is_null( $revision ) ) { - wfDebug( "$fname failed to retrieve current page, rev_id $data->page_latest\n" ); + wfDebug( "$fname failed to retrieve current page, rev_id {$data->page_latest}\n" ); return false; } } diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index e63ff14208..43d0c57f04 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1897,5 +1897,10 @@ $wgJobRunRate = 1; */ $wgJobLogFile = false; +/** + * Enable use of AJAX features, currently auto suggestion for the search bar + */ +$wgUseAjax = false; + ?> diff --git a/includes/OutputPage.php b/includes/OutputPage.php index b90faf87cb..aea3fefd1d 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -460,6 +460,7 @@ class OutputPage { function output() { global $wgUser, $wgOutputEncoding; global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType, $wgProfiler; + global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgScriptPath, $wgServer; if( $this->mDoNothing ){ return; @@ -468,6 +469,14 @@ class OutputPage { wfProfileIn( $fname ); $sk = $wgUser->getSkin(); + if ( $wgUseAjax ) { + $this->addScript( "" ); + $this->addScript( "\n" ); + } + if ( '' != $this->mRedirect ) { if( substr( $this->mRedirect, 0, 4 ) != 'http' ) { # Standards require redirect URLs to be absolute diff --git a/languages/Messages.php b/languages/Messages.php index 4c587c06ee..ad14e2c6e8 100644 --- a/languages/Messages.php +++ b/languages/Messages.php @@ -1986,6 +1986,10 @@ Please confirm that really want to recreate this article.', 'youhavenewmessagesmulti' => "You have new messages on $1", 'newtalkseperator' => ',_', +'searchcontaining' => "Search for articles containing ''$1''.", +'searchnamed' => "Search for articles named ''$1''.", +'articletitles' => "Articles starting with ''$1''", +'hideresults' => 'Hide results', ); diff --git a/languages/MessagesDe.php b/languages/MessagesDe.php index 46f35e6723..76f041ce7a 100644 --- a/languages/MessagesDe.php +++ b/languages/MessagesDe.php @@ -1127,9 +1127,14 @@ $3 Wenn Sie *nicht* $2 sind, folgen Sie dem Link bitte nicht. Der Bestätigungskode läuft am $4 ab. -" +", +'searchcontaining' => "Suche nach Artikeln, in denen ''$1'' vorkommt.", +'searchnamed' => "Suche nach Artikeln, deren Name ''$1'' enthält.", +'articletitles' => "Artikel, die mit ''$1'' beginnen", +'hideresults' => 'Verbergen', + ); -?> \ No newline at end of file +?> diff --git a/skins/MonoBook.php b/skins/MonoBook.php index 72b6006ccc..2200f559c3 100644 --- a/skins/MonoBook.php +++ b/skins/MonoBook.php @@ -55,7 +55,6 @@ class MonoBookTemplate extends QuickTemplate { html('headlinks') ?> - html('headscripts') ?> <?php $this->text('pagetitle') ?> data['printable']) ) { ?>media="print" href="text('stylepath') ?>/common/commonPrint.css" /> @@ -82,6 +81,8 @@ class MonoBookTemplate extends QuickTemplate { data['trackbackhtml']) print $this->data['trackbackhtml']; ?> + + html('headscripts') ?> data['body_ondblclick']) { ?>ondblclick="text('body_ondblclick') ?>" data['body_onload' ]) { ?>onload="text('body_onload') ?>" diff --git a/skins/common/ajax.js b/skins/common/ajax.js new file mode 100644 index 0000000000..b1f3e0574b --- /dev/null +++ b/skins/common/ajax.js @@ -0,0 +1,177 @@ +// remote scripting library +// (c) copyright 2005 modernmethod, inc +var sajax_debug_mode = false; +var sajax_request_type = "GET"; + +var started; +var typing; +var memory=null; +var body=null; +var oldbody=null; + +function sajax_debug(text) { + if (sajax_debug_mode) + alert("RSD: " + text) +} + + +function sajax_init_object() { + sajax_debug("sajax_init_object() called..") + var A; + try { + A=new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) { + try { + A=new ActiveXObject("Microsoft.XMLHTTP"); + } catch (oc) { + A=null; + } + } + if(!A && typeof XMLHttpRequest != "undefined") + A = new XMLHttpRequest(); + if (!A) + sajax_debug("Could not create connection object."); + return A; +} + + +function sajax_do_call(func_name, args) { + var i, x, n; + var uri; + var post_data; + uri = wgServer + "/" + wgScriptPath + "/ajax.php"; + if (sajax_request_type == "GET") { + if (uri.indexOf("?") == -1) + uri = uri + "?rs=" + escape(func_name); + else + uri = uri + "&rs=" + escape(func_name); + for (i = 0; i < args.length-1; i++) + uri = uri + "&rsargs[]=" + escape(args[i]); + uri = uri + "&rsrnd=" + new Date().getTime(); + post_data = null; + } else { + post_data = "rs=" + escape(func_name); + for (i = 0; i < args.length-1; i++) + post_data = post_data + "&rsargs[]=" + escape(args[i]); + } + x = sajax_init_object(); + x.open(sajax_request_type, uri, true); + if (sajax_request_type == "POST") { + x.setRequestHeader("Method", "POST " + uri + " HTTP/1.1"); + x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + } + x.setRequestHeader("Pragma", "cache=yes"); + x.setRequestHeader("Cache-Control", "no-transform"); + x.onreadystatechange = function() { + if (x.readyState != 4) + return; + sajax_debug("received " + x.responseText); + var status; + var data; + status = x.responseText.charAt(0); + data = x.responseText.substring(2); + if (status == "-") + alert("Error: " + data); + else + args[args.length-1](data); + } + x.send(post_data); + sajax_debug(func_name + " uri = " + uri + "/post = " + post_data); + sajax_debug(func_name + " waiting.."); + delete x; +} + +// Remove the typing barrier to allow call() to complete +function Search_doneTyping() +{ + typing=false; +} + +// Wait 500ms to run call() +function Searching_Go() +{ + setTimeout("Searching_Call()", 500); +} + +// If the user is typing wait until they are done. +function Search_Typing() { + started=true; + typing=true; + window.status = "Waiting until you're done typing..."; + setTimeout("Search_doneTyping()", 500); + + // I believe these are needed by IE for when the users press return? + if (window.event) + { + if (event.keyCode == 13) + { + event.cancelBubble = true; + event.returnValue = false; + } + } +} + +// Set the body div to the results +function Searching_SetResult(result) +{ + //body.innerHTML = result; + t = document.getElementById("searchTarget"); + if ( t == null ) { + oldbody=body.innerHTML; + body.innerHTML= '
    ' ; + t = document.getElementById("searchTarget"); + } + t.innerHTML = result; + t.style.display='block'; +} + +function Searching_Hide_Results() +{ + t = document.getElementById("searchTarget"); + t.style.display='none'; + body.innerHTML = oldbody; +} + + +// This will call the php function that will eventually +// return a results table +function Searching_Call() +{ + var x; + Searching_Go(); + + //Don't proceed if user is typing + if (typing) + return; + + x = document.getElementById("searchInput").value; + + // Don't search again if the query is the same + if (x==memory) + return; + + memory=x; + if (started) { + // Don't search for blank or < 3 chars. + if ((x=="") || (x.length < 3)) + { + return; + } + x_wfSajaxSearch(x, Searching_SetResult); + } +} + +function x_wfSajaxSearch() { + sajax_do_call( "wfSajaxSearch", x_wfSajaxSearch.arguments ); +} + + +//Initialize +function sajax_onload() { + x = document.getElementById( 'searchInput' ); + x.onkeypress= function() { Search_Typing(); }; + Searching_Go(); + body = document.getElementById("content"); +} + +hookEvent("load", sajax_onload);