diff --git a/lib/exe/js.php b/lib/exe/js.php
index 25dc8496a8eb1296f0d27798a145ae433a7754da..28894ffc6035e69ac47a906db044d19d71a08f39 100644
--- a/lib/exe/js.php
+++ b/lib/exe/js.php
@@ -48,7 +48,7 @@ function js_out(){
                 DOKU_INC.'lib/scripts/cookie.js',
                 DOKU_INC.'lib/scripts/script.js',
                 DOKU_INC.'lib/scripts/tw-sack.js',
-                DOKU_INC.'lib/scripts/ajax.js',
+                DOKU_INC.'lib/scripts/qsearch.js',
                 DOKU_INC.'lib/scripts/index.js',
                 DOKU_INC.'lib/scripts/drag.js',
                 DOKU_INC.'lib/scripts/textselection.js',
diff --git a/lib/scripts/ajax.js b/lib/scripts/ajax.js
deleted file mode 100644
index 19d4b86a6832d0ef9b284def16b63e3550e14bd3..0000000000000000000000000000000000000000
--- a/lib/scripts/ajax.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/**
- * AJAX functions for the pagename quicksearch
- *
- * @license  GPL2 (http://www.gnu.org/licenses/gpl.html)
- * @author   Andreas Gohr <andi@splitbrain.org>
- * @author   Adrian Lang <lang@cosmocode.de>
- * @author   Michal Rezler <m.rezler@centrum.cz>
- */
-
-var ajax_quicksearch = {
-
-    inObj: null, // jquery object
-    outObj: null, // jquery object
-    delay: null,
-
-    /**
-     * initialize the quick search
-     *
-     * Attaches the event handlers
-     *
-     * @param input element (JQuery selector/DOM obj)
-     * @param output element (JQuery selector/DOM obj)
-     */
-    init: function(input, output) {
-        ajax_quicksearch.inObj  = jQuery(input);
-        ajax_quicksearch.outObj = jQuery(output);
-
-        // objects found?
-        if (ajax_quicksearch.inObj  === []) return;
-        if (ajax_quicksearch.outObj === []) return;
-
-        // attach eventhandler to search field
-        ajax_quicksearch.delay = new Delay(function () {
-            ajax_quicksearch.clear_results();
-            var value = ajax_quicksearch.inObj.val();
-            if(value === ''){ return; }
-            jQuery.post(
-                DOKU_BASE + 'lib/exe/ajax.php',
-                {
-                    call: 'qsearch',
-                    q: encodeURI(value)
-                },
-                ajax_quicksearch.onCompletion,
-                'html'
-            );
-        });
-
-        ajax_quicksearch.inObj.keyup(
-            function() {
-                ajax_quicksearch.clear_results();
-                ajax_quicksearch.delay.start();
-            }
-        );
-
-        // attach eventhandler to output field
-        ajax_quicksearch.outObj.click(
-             ajax_quicksearch.outObj.hide
-        );
-
-    },
-
-    /**
-     * Empty and hide the output div
-     */
-    clear_results: function(){
-        ajax_quicksearch.outObj.hide();
-        ajax_quicksearch.outObj.text('');
-    },
-
-    /**
-     * Callback. Reformat and display the results.
-     *
-     * Namespaces are shortened here to keep the results from overflowing
-     * or wrapping
-     *
-     * @param data The result HTML
-     */
-    onCompletion: function(data) {
-        if (data === '') { return; }
-
-        var outObj = ajax_quicksearch.outObj;
-
-        outObj.html(data);
-        outObj.show();
-        outObj.css('white-space', 'nowrap');
-
-        // shorten namespaces if too long
-        var width = outObj.clientWidth;
-        var links = outObj.find('a');
-
-        for (var i=0; i<links.length; i++) {
-            var content = links[i].text;
-
-            // maximum allowed width:
-            var max = width - links[i].offsetLeft;
-            var isRTL = (document.documentElement.dir == 'rtl');
-
-            if(!isRTL && links[i].offsetWidth < max) continue;
-            if(isRTL && links[i].offsetLeft > 0) continue;
-
-            var nsL = content.indexOf('(');
-            var nsR = content.indexOf(')');
-            var eli = 0;
-            var runaway = 0;
-
-            while((nsR - nsL > 3) &&
-                    (
-                       (!isRTL && links[i].offsetWidth > max) ||
-                       (isRTL && links[i].offsetLeft < 0)
-                    )
-                 ){
-
-                if(runaway++ > 500) return; // just in case something went wrong
-
-                if(eli){
-                    // elipsis already inserted
-                    if( (eli - nsL) > (nsR - eli) ){
-                        // cut left
-                        content = content.substring(0,eli-2) + content.substring(eli);
-                    }else{
-                        // cut right
-                        content = content.substring(0,eli+1) + content.substring(eli+2);
-                    }
-                }else{
-                    // replace middle with ellipsis
-                    var mid = Math.floor( nsL + ((nsR-nsL)/2) );
-                    content = content.substring(0,mid)+'…' + content.substring(mid+1);
-                }
-
-                eli = content.indexOf('…');
-                nsL = content.indexOf('(');
-                nsR = content.indexOf(')');
-            }
-        }
-    }
-};
-
-jQuery(function () {
-    ajax_quicksearch.init('#qsearch__in','#qsearch__out');
-});
-
diff --git a/lib/scripts/compatibility.js b/lib/scripts/compatibility.js
index 559cc359a3698f6699e01af34be61eddb5f9ed11..54eb07632fb44222b85348622ba82709bbbc634e 100644
--- a/lib/scripts/compatibility.js
+++ b/lib/scripts/compatibility.js
@@ -1,16 +1,14 @@
 /*jslint sloppy: true */
-/*global dw_index, DEPRECATED */
+/*global dw_index, dw_qsearch, DEPRECATED_WRAP */
 
 var index = {
     throbber_delay: dw_index.throbber_delay,
+    toggle: DEPRECATED_WRAP(dw_index.toggle, dw_index),
+    treeattach: DEPRECATED_WRAP(dw_index.treeattach, dw_index)
+};
 
-    toggle: function () {
-        DEPRECATED();
-        dw_index.toggle.apply(dw_index, arguments);
-    },
-
-    treeattach: function () {
-        DEPRECATED();
-        dw_index.treeattach.apply(dw_index, arguments);
-    }
+var ajax_quicksearch = {
+    init: DEPRECATED_WRAP(dw_qsearch.init, dw_qsearch),
+    clear_results: DEPRECATED_WRAP(dw_qsearch.clear_results, dw_qsearch),
+    onCompletion: DEPRECATED_WRAP(dw_qsearch.onCompletion, dw_qsearch)
 };
diff --git a/lib/scripts/helpers.js b/lib/scripts/helpers.js
index 129964d2909950deb6d3471783ab139d77cdd36b..77e7ffc4a307bf0997fac31b7ade8b49bdbcb30b 100644
--- a/lib/scripts/helpers.js
+++ b/lib/scripts/helpers.js
@@ -144,3 +144,22 @@ function hasFlash(version){
     if(ver >= version) return true;
     return false;
 }
+
+/**
+ * A PHP-style substr_replace
+ *
+ * Supports negative start and length and omitting length, but not
+ * str and replace arrays.
+ * See http://php.net/substr-replace for further documentation.
+ */
+function substr_replace(str, replace, start, length) {
+    var a2, b1;
+    a2 = (start < 0 ? str.length : 0) + start;
+    if (typeof length === 'undefined') {
+        length = str.length - a2;
+    } else if (length < 0 && start < 0 && length <= start) {
+        length = 0;
+    }
+    b1 = (length < 0 ? str.length : a2) + length;
+    return str.substring(0, a2) + replace + str.substring(b1);
+}
diff --git a/lib/scripts/qsearch.js b/lib/scripts/qsearch.js
new file mode 100644
index 0000000000000000000000000000000000000000..97eaa7ef06f2325053fdf241dc89d761460827fb
--- /dev/null
+++ b/lib/scripts/qsearch.js
@@ -0,0 +1,144 @@
+/*jslint sloppy: true, plusplus: true, continue: true */
+/*global jQuery, DOKU_BASE, window, document, substr_replace */
+
+/**
+ * AJAX functions for the pagename quicksearch
+ *
+ * @license  GPL2 (http://www.gnu.org/licenses/gpl.html)
+ * @author   Andreas Gohr <andi@splitbrain.org>
+ * @author   Adrian Lang <lang@cosmocode.de>
+ * @author   Michal Rezler <m.rezler@centrum.cz>
+ */
+
+var dw_qsearch = {
+
+    $inObj: null,
+    $outObj: null,
+
+    /**
+     * initialize the quick search
+     *
+     * Attaches the event handlers
+     *
+     * @param input element (JQuery selector/DOM obj)
+     * @param output element (JQuery selector/DOM obj)
+     */
+    init: function (input, output) {
+        var do_qsearch;
+
+        dw_qsearch.$inObj  = jQuery(input);
+        dw_qsearch.$outObj = jQuery(output);
+
+        // objects found?
+        if (dw_qsearch.$inObj.length === 0 ||
+            dw_qsearch.$outObj.length === 0) {
+            return;
+        }
+
+        // attach eventhandler to search field
+        do_qsearch = function () {
+            dw_qsearch.clear_results();
+            var value = dw_qsearch.$inObj.val();
+            if (value === '') {
+                return;
+            }
+            jQuery.post(
+                DOKU_BASE + 'lib/exe/ajax.php',
+                {
+                    call: 'qsearch',
+                    q: encodeURI(value)
+                },
+                dw_qsearch.onCompletion,
+                'html'
+            );
+        };
+
+        dw_qsearch.$inObj.keyup(
+            function() {
+                dw_qsearch.clear_results();
+                window.setTimeout(do_qsearch, 500);
+            }
+        );
+
+        // attach eventhandler to output field
+        dw_qsearch.$outObj.click(dw_qsearch.clear_results);
+    },
+
+    /**
+     * Empty and hide the output div
+     */
+    clear_results: function(){
+        dw_qsearch.$outObj.hide();
+        dw_qsearch.$outObj.text('');
+    },
+
+    /**
+     * Callback. Reformat and display the results.
+     *
+     * Namespaces are shortened here to keep the results from overflowing
+     * or wrapping
+     *
+     * @param data The result HTML
+     */
+    onCompletion: function(data) {
+        var max, $links, too_big;
+
+        if (data === '') { return; }
+
+        dw_qsearch.$outObj
+            .html(data)
+            .show()
+            .css('white-space', 'nowrap');
+
+        // shorten namespaces if too long
+        max = dw_qsearch.$outObj[0].clientWidth;
+        $links = dw_qsearch.$outObj.find('a');
+        too_big = (document.documentElement.dir === 'rtl')
+                  ? function (l) { return l.offsetLeft < 0; }
+                  : function (l) { return l.offsetWidth + l.offsetLeft > max; };
+
+        $links.each(function () {
+            var start, length, replace, nsL, nsR, eli, runaway;
+
+            if (!too_big(this)) {
+                return;
+            }
+
+            nsL = this.innerText.indexOf('(');
+            nsR = this.innerText.indexOf(')');
+            eli = 0;
+            runaway = 0;
+
+            while((nsR - nsL > 3) && too_big(this) && runaway++ < 500) {
+                if(eli !== 0){
+                    // elipsis already inserted
+                    if( (eli - nsL) > (nsR - eli) ){
+                        // cut left
+                        start = eli - 2;
+                        length = 2;
+                    }else{
+                        // cut right
+                        start = eli + 1;
+                        length = 1;
+                    }
+                    replace = '';
+                }else{
+                    // replace middle with ellipsis
+                    start = Math.floor( nsL + ((nsR-nsL)/2) );
+                    length = 1;
+                    replace = '…';
+                }
+                this.innerText = substr_replace(this.innerText,
+                                                replace, start, length);
+
+                eli = this.innerText.indexOf('…');
+                nsL = this.innerText.indexOf('(');
+                nsR = this.innerText.indexOf(')');
+            }
+        });
+    }
+};
+
+jQuery(function () {
+    dw_qsearch.init('#qsearch__in','#qsearch__out');
+});
diff --git a/lib/scripts/script.js b/lib/scripts/script.js
index a673cdc02bc1bebb95c440ec6846cb55c914b440..8adba829ca41423f5c82da4e3af82725d91c91cf 100644
--- a/lib/scripts/script.js
+++ b/lib/scripts/script.js
@@ -28,6 +28,21 @@ function DEPRECATED(msg){
     if(console.trace) console.trace();
 }
 
+/**
+ * Construct a wrapper function for deprecated function names
+ *
+ * This function returns a wrapper function which just calls DEPRECATED
+ * and the new function.
+ *
+ * @param func    The new function
+ * @param context Optional; The context (`this`) of the call
+ */
+function DEPRECATED_WRAP(func, context) {
+    return function () {
+        DEPRECATED();
+        return func.apply(context || this, arguments);
+    }
+}
 
 /**
  * Some of these scripts were taken from wikipedia.org and were modified for DokuWiki