From 5e7a292691951a0fa0a18f06c8b9bcfb509a032d Mon Sep 17 00:00:00 2001
From: Adrian Lang <mail@adrianlang.de>
Date: Fri, 9 Sep 2011 22:26:16 +0200
Subject: [PATCH] Various JavaScript improvements, JSLint, jQuery

---
 lib/exe/js.php               |   6 +-
 lib/plugins/acl/script.js    |   2 +-
 lib/scripts/compatibility.js |   2 +-
 lib/scripts/cookie.js        |  15 +--
 lib/scripts/edit.js          | 222 ++++++++++++++++-------------------
 lib/scripts/editor.js        | 101 ++++++++--------
 lib/scripts/index.js         |   3 -
 lib/scripts/linkwiz.js       | 199 +++++++++++++++----------------
 lib/scripts/media.js         |   3 -
 lib/scripts/qsearch.js       |   3 -
 lib/scripts/script.js        |   7 +-
 lib/scripts/textselection.js |   7 +-
 lib/scripts/toolbar.js       | 146 ++++++++++-------------
 lib/scripts/tree.js          |   3 -
 lib/styles/screen.css        |   5 +
 15 files changed, 337 insertions(+), 387 deletions(-)

diff --git a/lib/exe/js.php b/lib/exe/js.php
index 33f8c695d..b7f2fd222 100644
--- a/lib/exe/js.php
+++ b/lib/exe/js.php
@@ -88,6 +88,7 @@ function js_out(){
     // add some global variables
     print "var DOKU_BASE   = '".DOKU_BASE."';";
     print "var DOKU_TPL    = '".DOKU_TPL."';";
+    // FIXME: Move those to JSINFO
     print "var DOKU_UHN    = ".((int) useHeading('navigation')).";";
     print "var DOKU_UHC    = ".((int) useHeading('content')).";";
 
@@ -106,10 +107,7 @@ function js_out(){
         echo "\n\n/* XXXXXXXXXX end of " . str_replace(DOKU_INC, '', $file) . " XXXXXXXXXX */\n\n";
     }
 
-
     // init stuff
-    js_runonstart("addEvent(document,'click',closePopups)");
-    js_runonstart("initToolbar('tool__bar','wiki__text',toolbar)");
     if($conf['locktime'] != 0){
         js_runonstart("dw_locktimer.init(".($conf['locktime'] - 60).",".$conf['usedraft'].")");
     }
@@ -220,7 +218,7 @@ function js_escape($string){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function js_runonstart($func){
-    echo "addInitEvent(function(){ $func; });".NL;
+    echo "jQuery(function(){ $func; });".NL;
 }
 
 /**
diff --git a/lib/plugins/acl/script.js b/lib/plugins/acl/script.js
index 2598bcef8..d912a2407 100644
--- a/lib/plugins/acl/script.js
+++ b/lib/plugins/acl/script.js
@@ -33,7 +33,7 @@ var dw_acl = {
                                show_sublist,
                                'html'
                            );
-    },
+                       },
 
                        toggle_display: function ($clicky, opening) {
                            $clicky.attr('src',
diff --git a/lib/scripts/compatibility.js b/lib/scripts/compatibility.js
index 39f703c71..3b027f016 100644
--- a/lib/scripts/compatibility.js
+++ b/lib/scripts/compatibility.js
@@ -38,7 +38,7 @@ function DEPRECATED_WRAP(func, context) {
     return function () {
         DEPRECATED();
         return func.apply(context || this, arguments);
-    }
+    };
 }
 
 /**
diff --git a/lib/scripts/cookie.js b/lib/scripts/cookie.js
index 4dd77beea..c70d0bb56 100644
--- a/lib/scripts/cookie.js
+++ b/lib/scripts/cookie.js
@@ -9,7 +9,7 @@
 * @author Andreas Gohr <andi@splitbrain.org>
 * @author Michal Rezler <m.rezler@centrum.cz>
 */
-DokuCookie = {
+var DokuCookie = {
     data: {},
     name: 'DOKU_PREFS',
 
@@ -19,17 +19,18 @@ DokuCookie = {
      * @author Andreas Gohr <andi@splitbrain.org>
      */
     setValue: function(key,val){
-        var text = '';
+        var text = [],
+            _this = this;
         this.init();
         this.data[key] = val;
 
         //save the whole data array
-        jQuery.each(this.data, function (key, val) {
-            if (DokuCookie.data.hasOwnProperty(key)) {
-                text += '#'+encodeURIComponent(key)+'#'+encodeURIComponent(val);
-        }
+        jQuery.each(_this.data, function (key, val) {
+            if (_this.data.hasOwnProperty(key)) {
+                text.push(encodeURIComponent(key)+'#'+encodeURIComponent(val));
+            }
         });
-        jQuery.cookie(this.name,text.substr(1), {expires: 365, path: DOKU_BASE});
+        jQuery.cookie(this.name, text.join('#'), {expires: 365, path: DOKU_BASE});
     },
 
     /**
diff --git a/lib/scripts/edit.js b/lib/scripts/edit.js
index 816568e92..bfbd698f1 100644
--- a/lib/scripts/edit.js
+++ b/lib/scripts/edit.js
@@ -14,38 +14,36 @@
  * @author Michal Rezler <m.rezler@centrum.cz>
  */
 function createToolButton(icon,label,key,id,classname){
-    var $ = jQuery;
-    var btn = $('<button>');
-    var ico = $('<img />');
+    var $btn = jQuery(document.createElement('button')),
+        $ico = jQuery(document.createElement('img'));
 
-    // preapare the basic button stuff
-    btn.attr('class', 'toolbutton');
+    // prepare the basic button stuff
+    $btn.addClass('toolbutton');
     if(classname){
-        btn.attr('class', 'toolbutton '+classname);
+        $btn.addClass(classname);
     }
 
-    btn.attr('title', label);
+    $btn.attr('title', label);
     if(key){
-        btn.attr('title', label + ' ['+key.toUpperCase()+']')
+        $btn.attr('title', label + ' ['+key.toUpperCase()+']')
             .attr('accessKey', key);
     }
 
     // set IDs if given
     if(id){
-        btn.attr('id', id);
-        ico.attr('id', id+'_ico');
+        $btn.attr('id', id);
+        $ico.attr('id', id+'_ico');
     }
 
     // create the icon and add it to the button
-    if(icon.substr(0,1) == '/'){
-        ico.attr('src', icon);
-    }else{
-        ico.attr('src', DOKU_BASE+'lib/images/toolbar/'+icon);
+    if(icon.substr(0,1) !== '/'){
+        icon = DOKU_BASE + 'lib/images/toolbar/' + icon;
     }
-    btn.append(ico);
+    $ico.attr('src', icon);
+    $btn.append($ico);
 
-    // we have to return a javascript object (for compatibility reasons)
-    return btn[0];
+    // we have to return a DOM object (for compatibility reasons)
+    return $btn[0];
 }
 
 /**
@@ -63,69 +61,51 @@ function createToolButton(icon,label,key,id,classname){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function createPicker(id,props,edid){
-    var icobase = props['icobase'];
-    var list = props['list'];
-    var $ = jQuery;
-
     // create the wrapping div
-    var picker = $('<div></div>');
+    var $picker = jQuery(document.createElement('div'));
 
-    var className = 'picker';
+    $picker.addClass('picker hidden_with_access_keys');
     if(props['class']){
-        className += ' '+props['class'];
+        $picker.addClass(props['class']);
     }
 
-    picker.attr('class', className)
-        .attr('id', id)
-        .css('position', 'absolute')
-        .css('marginLeft', '-10000px') // no display:none, to keep access keys working
-        .css('marginTop', '-10000px');
+    $picker.attr('id', id).css('position', 'absolute');
+
+    function $makebutton(title) {
+        var $btn = jQuery(document.createElement('button'))
+            .addClass('pickerbutton').attr('title', title)
+            .bind('click', bind(pickerInsert, title, edid))
+            .appendTo($picker);
+        return $btn;
+    }
 
-    for(var key in list){
-        if (!list.hasOwnProperty(key)) continue;
+    jQuery.each(props.list, function (key, item) {
+        if (!props.list.hasOwnProperty(key)) {
+            return;
+        }
 
         if(isNaN(key)){
-            // associative array -> treat as image/value pairs
-            var btn = $('<button>');
-            btn.attr('class', 'pickerbutton')
-                .attr('title', key);
-
-            var ico = $('<img>');
-            if (list[key].substr(0,1) == '/') {
-                var src = list[key];
-            } else {
-                var src = DOKU_BASE+'lib/images/'+icobase+'/'+list[key];
+            // associative array -> treat as text => image pairs
+            if (item.substr(0,1) !== '/') {
+                item = DOKU_BASE+'lib/images/'+props.icobase+'/'+item;
             }
-
-            ico.attr('src', src);
-            btn.append(ico);
-
-            btn.bind('click', bind(pickerInsert, key, edid));
-            picker.append(btn);
-        }else if (typeof (list[key]) == 'string'){
+            jQuery(document.createElement('img'))
+                .attr('src', item)
+                .appendTo($makebutton(key));
+        }else if (typeof item == 'string'){
             // a list of text -> treat as text picker
-            var btn = $('<button>');
-            btn.attr('class', 'pickerbutton')
-                .attr('title', list[key]);
-
-            var txt = $(document.createTextNode(list[key]));
-            btn.append(txt);
-
-            btn.bind('click', bind(pickerInsert, list[key], edid));
-
-            picker.append(btn);
+            $makebutton(item).text(item);
         }else{
             // a list of lists -> treat it as subtoolbar
-            initToolbar(picker,edid,list);
-            break; // all buttons handled already
+            initToolbar($picker,edid,props.list);
+            return false; // all buttons handled already
         }
 
-    }
-    var body = $('body');
-    body.append(picker);
+    });
+    jQuery('body').append($picker);
 
-    // we have to return a javascript object (for compatibility reasons)
-    return picker[0];
+    // we have to return a DOM object (for compatibility reasons)
+    return $picker[0];
 }
 
 /**
@@ -147,9 +127,9 @@ function pickerInsert(text,edid){
  * @return boolean    If button should be appended
  * @author Gabriel Birke <birke@d-scribe.de>
  */
-function addBtnActionSignature(btn, props, edid) {
-    if(typeof(SIG) != 'undefined' && SIG != ''){
-        btn.bind('click', bind(insertAtCarret,edid,SIG));
+function addBtnActionSignature($btn, props, edid) {
+    if(typeof SIG != 'undefined' && SIG != ''){
+        $btn.bind('click', bind(insertAtCarret,edid,SIG));
         return true;
     }
     return false;
@@ -161,24 +141,27 @@ function addBtnActionSignature(btn, props, edid) {
  * @author Andreas Gohr <gohr@cosmocode.de>
  */
 function currentHeadlineLevel(textboxId){
-    var field     = $(textboxId);
-    var selection = getSelection(field);
-    var search    = "\n"+field.value.substr(0,selection.start);
-    var lasthl    = search.lastIndexOf("\n==");
-    if(lasthl == -1 && field.form.prefix){
+    var field = jQuery('#' + textboxId)[0],
+        s = false,
+        opts = [field.value.substr(0,getSelection(field).start)];
+    if (field.form.prefix) {
         // we need to look in prefix context
-        search = field.form.prefix.value;
-        lasthl    = search.lastIndexOf("\n==");
+        opts.push(field.form.prefix.value);
     }
-    search    = search.substr(lasthl+1,6);
 
-    if(search == '======') return 1;
-    if(search.substr(0,5) == '=====') return 2;
-    if(search.substr(0,4) == '====') return 3;
-    if(search.substr(0,3) == '===') return 4;
-    if(search.substr(0,2) == '==') return 5;
-
-    return 0;
+    jQuery.each(opts, function (_, opt) {
+        // Check whether there is a headline in the given string
+        var str = "\n" + opt,
+            lasthl = str.lastIndexOf("\n==");
+        if (lasthl !== -1) {
+            s = str.substr(lasthl+1,6);
+            return false;
+        }
+    });
+    if (s === false) {
+        return 0;
+    }
+    return 7 - s.match(/^={2,6}/)[0].length;
 }
 
 
@@ -191,21 +174,23 @@ window.textChanged = false;
  * Delete the draft before leaving the page
  */
 function deleteDraft() {
-    if (is_opera) return;
-    if (window.keepDraft) return;
+    if (is_opera || window.keepDraft) {
+        return;
+    }
 
-    // remove a possibly saved draft using ajax
-    var dwform = jQuery('#dw__editform');
-    if(dwform.length != 0) {
-
-        jQuery.post(
-            DOKU_BASE + 'lib/exe/ajax.php',
-            {
-                call: 'draftdel',
-                id: jQuery('#dw__editform input[name=id]').val()
-            }
-        );
+    var $dwform = jQuery('#dw__editform');
+
+    if($dwform.length === 0) {
+        return;
     }
+
+    // remove a possibly saved draft using ajax
+    jQuery.post(DOKU_BASE + 'lib/exe/ajax.php',
+        {
+            call: 'draftdel',
+            id: $dwform.find('input[name=id]').val()
+        }
+    );
 }
 
 /**
@@ -214,21 +199,24 @@ function deleteDraft() {
  *
  * Sets focus to the editbox as well
  */
-addInitEvent(function () {
-    var $ = jQuery;
-    var editform = $('#dw__editform');
-    if (editform.length == 0) return;
+jQuery(function () {
+    var $editform = jQuery('#dw__editform');
+    if ($editform.length == 0) {
+        return;
+    }
 
-    var edit_text = $('#wiki__text');
-    if (edit_text.length > 0) {
-        if(edit_text.attr('readOnly')) return;
+    var $edit_text = jQuery('#wiki__text');
+    if ($edit_text.length > 0) {
+        if($edit_text.attr('readOnly')) {
+            return;
+        }
 
         // set focus and place cursor at the start
-        var sel = getSelection(edit_text.get(0));
+        var sel = getSelection($edit_text[0]);
         sel.start = 0;
         sel.end   = 0;
         setSelection(sel);
-        edit_text.focus();
+        $edit_text.focus();
     }
 
     var checkfunc = function() {
@@ -236,8 +224,8 @@ addInitEvent(function () {
         summaryCheck();
     };
 
-    editform.change(checkfunc);
-    editform.keydown(checkfunc);
+    $editform.change(checkfunc);
+    $editform.keydown(checkfunc);
 
     window.onbeforeunload = function(){
         if(window.textChanged) {
@@ -247,13 +235,13 @@ addInitEvent(function () {
     window.onunload = deleteDraft;
 
     // reset change memory var on submit
-    $('#edbtn__save').click(
+    jQuery('#edbtn__save').click(
         function() {
             window.onbeforeunload = '';
             textChanged = false;
         }
     );
-    $('#edbtn__preview').click(
+    jQuery('#edbtn__preview').click(
         function() {
             window.onbeforeunload = '';
             textChanged = false;
@@ -261,9 +249,9 @@ addInitEvent(function () {
         }
     );
 
-    var summary = $('#edit__summary');
-    summary.change(summaryCheck);
-    summary.keyup(summaryCheck);
+    var $summary = jQuery('#edit__summary');
+    $summary.change(summaryCheck);
+    $summary.keyup(summaryCheck);
 
     if (textChanged) summaryCheck();
 });
@@ -274,11 +262,7 @@ addInitEvent(function () {
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function summaryCheck(){
-    var sum = jQuery('#edit__summary');
-
-    if (sum.val() === '') {
-        sum.attr('class', 'missing');
-    } else{
-        sum.attr('class', 'edit');
-    }
+    var $sum = jQuery('#edit__summary'),
+        missing = $sum.val() === '';
+    $sum.toggleClass('missing', missing).toggleClass('edit', !missing);
 }
diff --git a/lib/scripts/editor.js b/lib/scripts/editor.js
index 6d7f9f4a8..2009ce6af 100644
--- a/lib/scripts/editor.js
+++ b/lib/scripts/editor.js
@@ -15,11 +15,15 @@ var dw_editor = {
      */
     init: function(){
         var $editor = jQuery('#wiki__text');
-        if(!$editor.length) return;
+        if($editor.length === 0) {
+            return;
+        }
 
         dw_editor.initSizeCtl('#size__ctl',$editor);
 
-        if($editor.attr('readOnly')) return;
+        if($editor.attr('readOnly')) {
+            return;
+        }
 
         // in Firefox, keypress doesn't send the correct keycodes,
         // in Opera, the default of keydown can't be prevented
@@ -40,34 +44,30 @@ var dw_editor = {
      * @param selector editor  the textarea to control
      */
     initSizeCtl: function(ctlarea,editor){
-        var $ctl      = jQuery(ctlarea);
-        var $textarea = jQuery(editor);
-        if(!$ctl.length || !$textarea.length) return;
-
-        var hgt = DokuCookie.getValue('sizeCtl');
-        if(hgt){
-            $textarea.css('height', hgt);
-        }else{
-            $textarea.css('height', '300px');
+        var $ctl      = jQuery(ctlarea),
+            $textarea = jQuery(editor);
+
+        if($ctl.length === 0 || $textarea.length === 0) {
+            return;
         }
 
+        $textarea.css('height', DokuCookie.getValue('sizeCtl') || '300px');
+
         var wrp = DokuCookie.getValue('wrapCtl');
         if(wrp){
             dw_editor.setWrap($textarea[0], wrp);
         } // else use default value
 
-        var l = document.createElement('img');
-        var s = document.createElement('img');
-        var w = document.createElement('img');
-        l.src = DOKU_BASE+'lib/images/larger.gif';
-        s.src = DOKU_BASE+'lib/images/smaller.gif';
-        w.src = DOKU_BASE+'lib/images/wrap.gif';
-        jQuery(l).click(function(){dw_editor.sizeCtl(editor,100);});
-        jQuery(s).click(function(){dw_editor.sizeCtl(editor,-100);});
-        jQuery(w).click(function(){dw_editor.toggleWrap(editor);});
-        $ctl.append(l);
-        $ctl.append(s);
-        $ctl.append(w);
+        jQuery.each([
+            ['larger', function(){dw_editor.sizeCtl(editor,100);}],
+            ['smaller', function(){dw_editor.sizeCtl(editor,-100);}],
+            ['wrap', function(){dw_editor.toggleWrap(editor);}],
+        ], function (_, img) {
+            jQuery(document.createElement('img'))
+                .attr('src', DOKU_BASE+'lib/images/' + img[0] + '.gif')
+                .click(img[1])
+                .appendTo($ctl);
+        });
     },
 
     /**
@@ -77,9 +77,8 @@ var dw_editor = {
      * @param int val          the relative value to resize in pixel
      */
     sizeCtl: function(editor,val){
-        var $textarea = jQuery(editor);
-        var height = parseInt($textarea.css('height'));
-        height += val;
+        var $textarea = jQuery(editor),
+            height = parseInt($textarea.css('height')) + val;
         $textarea.css('height', height+'px');
         DokuCookie.setValue('sizeCtl',$textarea.css('height'));
     },
@@ -91,13 +90,10 @@ var dw_editor = {
      * @param selector editor  the textarea to control
      */
     toggleWrap: function(editor){
-        var $textarea = jQuery(editor);
-        var wrap = textarea.attr('wrap');
-        if(wrap && wrap.toLowerCase() == 'off'){
-            dw_editor.setWrap(textarea[0], 'soft');
-        }else{
-            dw_editor.setWrap(textarea[0], 'off');
-        }
+        var $textarea = jQuery(editor),
+            wrap = $textarea.attr('wrap');
+        dw_editor.setWrap($textarea[0],
+                          (wrap && wrap.toLowerCase() == 'off') ? 'soft' : 'off');
         DokuCookie.setValue('wrapCtl',$textarea.attr('wrap'));
     },
 
@@ -134,13 +130,14 @@ var dw_editor = {
      * @param event e - the key press event object
      */
     keyHandler: function(e){
-        if(e.keyCode != 13 &&
-           e.keyCode != 8  &&
-           e.keyCode != 32) return;
-        var field     = e.target;
-        var selection = getSelection(field);
-        if(selection.getLength()) return; //there was text selected, keep standard behavior
-        var search    = "\n"+field.value.substr(0,selection.start);
+        if([8, 13, 32].indexOf(e.keyCode) === -1) {
+            return;
+        }
+        var selection = getSelection(this);
+        if(selection.getLength() > 0) {
+            return; //there was text selected, keep standard behavior
+        }
+        var search    = "\n"+this.value.substr(0,selection.start);
         var linestart = Math.max(search.lastIndexOf("\n"),
                                  search.lastIndexOf("\r")); //IE workaround
         search = search.substr(linestart);
@@ -149,19 +146,19 @@ var dw_editor = {
             // keep current indention for lists and code
             var match = search.match(/(\n  +([\*-] ?)?)/);
             if(match){
-                var scroll = field.scrollHeight;
+                var scroll = this.scrollHeight;
                 var match2 = search.match(/^\n  +[\*-]\s*$/);
                 // Cancel list if the last item is empty (i. e. two times enter)
-                if (match2 && field.value.substr(selection.start).match(/^($|\r?\n)/)) {
-                    field.value = field.value.substr(0, linestart) + "\n" +
-                                  field.value.substr(selection.start);
+                if (match2 && this.value.substr(selection.start).match(/^($|\r?\n)/)) {
+                    this.value = this.value.substr(0, linestart) + "\n" +
+                                 this.value.substr(selection.start);
                     selection.start = linestart + 1;
                     selection.end = linestart + 1;
                     setSelection(selection);
                 } else {
-                    insertAtCarret(field.id,match[1]);
+                    insertAtCarret(this.id,match[1]);
                 }
-                field.scrollTop += (field.scrollHeight - scroll);
+                this.scrollTop += (this.scrollHeight - scroll);
                 e.preventDefault(); // prevent enter key
                 return false;
             }
@@ -172,13 +169,13 @@ var dw_editor = {
                 var spaces = match[1].length-1;
 
                 if(spaces > 3){ // unindent one level
-                    field.value = field.value.substr(0,linestart)+
-                                  field.value.substr(linestart+2);
+                    this.value = this.value.substr(0,linestart)+
+                                 this.value.substr(linestart+2);
                     selection.start = selection.start - 2;
                     selection.end   = selection.start;
                 }else{ // delete list point
-                    field.value = field.value.substr(0,linestart)+
-                                  field.value.substr(selection.start);
+                    this.value = this.value.substr(0,linestart)+
+                                 this.value.substr(selection.start);
                     selection.start = linestart;
                     selection.end   = linestart;
                 }
@@ -190,8 +187,8 @@ var dw_editor = {
             // intend list item
             var match = search.match(/(\n  +)([*-] )$/);
             if(match){
-                field.value = field.value.substr(0,linestart)+'  '+
-                              field.value.substr(linestart);
+                this.value = this.value.substr(0,linestart)+'  '+
+                             this.value.substr(linestart);
                 selection.start = selection.start + 2;
                 selection.end   = selection.start;
                 setSelection(selection);
diff --git a/lib/scripts/index.js b/lib/scripts/index.js
index 96d4e2fb9..4b67a0b12 100644
--- a/lib/scripts/index.js
+++ b/lib/scripts/index.js
@@ -1,6 +1,3 @@
-/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: false, newcap: true, immed: true */
-/*global jQuery, window, DOKU_BASE, DEPRECATED, bind*/
-
 var dw_index = jQuery('#index__tree').dw_tree({deferInit: true,
     load_data: function  (show_sublist, $clicky) {
         jQuery.post(
diff --git a/lib/scripts/linkwiz.js b/lib/scripts/linkwiz.js
index 83653c9bb..6e0a00183 100644
--- a/lib/scripts/linkwiz.js
+++ b/lib/scripts/linkwiz.js
@@ -1,7 +1,3 @@
-/*jslint sloppy: true, indent: 4, white: true, browser: true, eqeq: true */
-/*global jQuery, DOKU_BASE, LANG, DOKU_UHC, getSelection, pasteText */
-
-
 /**
  * The Link Wizard
  *
@@ -10,7 +6,7 @@
  */
 var dw_linkwiz = {
     $wiz: null,
-    entry: null,
+    $entry: null,
     result: null,
     timer: null,
     textArea: null,
@@ -28,12 +24,11 @@ var dw_linkwiz = {
         // create HTML Structure
         dw_linkwiz.$wiz = jQuery(document.createElement('div'))
                .attr('id','link__wiz')
+               .addClass('hidden_with_access_keys')
                .css({
                     'position':    'absolute',
                     'top':         (pos.top+20)+'px',
-                    'left':        (pos.left+80)+'px',
-                    'margin-left': '-10000px',
-                    'margin-top':  '-10000px'
+                    'left':        (pos.left+80)+'px'
                    })
                .html(
                     '<div id="link__wiz_header">'+
@@ -44,15 +39,19 @@ var dw_linkwiz = {
                     )
                .addClass('picker');
 
-        $editor[0].form.parentNode.appendChild(dw_linkwiz.$wiz[0]);
+        $editor.parents('form').parent().append(dw_linkwiz.$wiz);
         dw_linkwiz.textArea = $editor[0];
         dw_linkwiz.result = jQuery('#link__wiz_result')[0];
-        dw_linkwiz.entry = jQuery('#link__wiz_entry')[0];
+
+        // scrollview correction on arrow up/down gets easier
+        jQuery(dw_linkwiz.result).css('position', 'relative');
+
+        dw_linkwiz.$entry = jQuery('#link__wiz_entry');
 
         // attach event handlers
         jQuery('#link__wiz_close').click(dw_linkwiz.hide);
-        jQuery(dw_linkwiz.entry).keyup(dw_linkwiz.onEntry);
-        jQuery(dw_linkwiz.result).click(dw_linkwiz.onResultClick);
+        dw_linkwiz.$entry.keyup(dw_linkwiz.onEntry);
+        jQuery(dw_linkwiz.result).delegate('a', 'click', dw_linkwiz.onResultClick);
 
         dw_linkwiz.$wiz.draggable({handle: '#link__wiz_header'});
     },
@@ -84,13 +83,12 @@ var dw_linkwiz = {
         }
         if(e.keyCode == 13){ //Enter
             if(dw_linkwiz.selected > -1){
-                var obj = dw_linkwiz.getResult(dw_linkwiz.selected);
-                if(obj){
-                    var a = jQuery(obj).find('a')[0];
-                    dw_linkwiz.resultClick(a);
+                var $obj = dw_linkwiz.$getResult(dw_linkwiz.selected);
+                if($obj.length > 0){
+                    dw_linkwiz.resultClick($obj.find('a')[0]);
                 }
-            }else if(dw_linkwiz.entry.value){
-                dw_linkwiz.insertLink(dw_linkwiz.entry.value);
+            }else if(dw_linkwiz.$entry.val()){
+                dw_linkwiz.insertLink(dw_linkwiz.$entry.val());
             }
 
             e.preventDefault();
@@ -107,13 +105,18 @@ var dw_linkwiz = {
      * @returns DOMObject or null
      */
     getResult: function(num){
-        var childs = jQuery(dw_linkwiz.result).find('div');
-        var obj = childs[num];
-        if(obj){
-            return obj;
-        }else{
-            return null;
-        }
+        DEPRECATED('use dw_linkwiz.$getResult()[0] instead');
+        return dw_linkwiz.$getResult()[0] || null;
+    },
+
+    /**
+     * Get one of the results by index
+     *
+     * @param int result div to return
+     * @returns jQuery object
+     */
+    $getResult: function(num) {
+        return jQuery(dw_linkwiz.result).find('div').eq(num);
     },
 
     /**
@@ -125,26 +128,31 @@ var dw_linkwiz = {
             return;
         }
 
-        var obj = dw_linkwiz.getResult(num);
-        if(obj){
-            dw_linkwiz.deselect();
-            obj.className += ' selected';
-
-            // make sure the item is viewable in the scroll view
-            // FIXME check IE compatibility
-            if(obj.offsetTop > dw_linkwiz.result.scrollTop + dw_linkwiz.result.clientHeight){
-                dw_linkwiz.result.scrollTop += obj.clientHeight;
-            }else if(obj.offsetTop - dw_linkwiz.result.clientHeight < dw_linkwiz.result.scrollTop){ // this works but isn't quite right, fixes welcome
-                dw_linkwiz.result.scrollTop -= obj.clientHeight;
-            }
-            // now recheck - if still not in view, the user used the mouse to scroll
-            if( (obj.offsetTop > dw_linkwiz.result.scrollTop + dw_linkwiz.result.clientHeight) ||
-                (obj.offsetTop < dw_linkwiz.result.scrollTop) ){
-                obj.scrollIntoView();
-            }
+        var $obj = dw_linkwiz.$getResult(num);
+        if ($obj.length === 0) {
+            return;
+        }
 
-            dw_linkwiz.selected = num;
+        dw_linkwiz.deselect();
+        $obj.addClass('selected');
+
+        // make sure the item is viewable in the scroll view
+
+        //getting child position within the parent
+        var childPos = $obj.position().top;
+        //getting difference between the childs top and parents viewable area
+        var yDiff = childPos + $obj.outerHeight() - jQuery(dw_linkwiz.result).innerHeight();
+
+        if (childPos < 0) {
+            //if childPos is above viewable area (that's why it goes negative)
+            jQuery(dw_linkwiz.result)[0].scrollTop += childPos;
+        } else if(yDiff > 0) {
+            // if difference between childs top and parents viewable area is
+            // greater than the height of a childDiv
+            jQuery(dw_linkwiz.result)[0].scrollTop += yDiff;
         }
+
+        dw_linkwiz.selected = num;
     },
 
     /**
@@ -152,10 +160,7 @@ var dw_linkwiz = {
      */
     deselect: function(){
         if(dw_linkwiz.selected > -1){
-            var obj = dw_linkwiz.getResult(dw_linkwiz.selected);
-            if(obj){
-                obj.className = obj.className.replace(/ ?selected/,'');
-            }
+            dw_linkwiz.$getResult(dw_linkwiz.selected).removeClass('selected');
         }
         dw_linkwiz.selected = -1;
     },
@@ -165,10 +170,12 @@ var dw_linkwiz = {
      * resultClick()
      */
     onResultClick: function(e){
-        if(e.target.tagName != 'A') return;
+        if(!jQuery(this).is('a')) {
+            return;
+        }
         e.stopPropagation();
         e.preventDefault();
-        dw_linkwiz.resultClick(e.target);
+        dw_linkwiz.resultClick(this);
         return false;
     },
 
@@ -176,17 +183,14 @@ var dw_linkwiz = {
      * Handles the "click" on a given result anchor
      */
     resultClick: function(a){
-        var L = dw_linkwiz;
-        var id = a.title;
-        if(id == '' || id.substr(id.length-1) == ':'){
-            L.entry.value = id;
-            L.autocomplete_exec();
+        dw_linkwiz.$entry.val(a.title);
+        if(a.title == '' || a.title.substr(-1) == ':'){
+            dw_linkwiz.autocomplete_exec();
         }else{
-            L.entry.value = id;
-            if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){
-                L.insertLink(a.nextSibling.innerHTML);
+            if (jQuery(a.nextSibling).is('span')) {
+                dw_linkwiz.insertLink(a.nextSibling.innerHTML);
             }else{
-                L.insertLink('');
+                dw_linkwiz.insertLink('');
             }
         }
     },
@@ -198,14 +202,18 @@ var dw_linkwiz = {
      * as link title instead
      */
     insertLink: function(title){
-        var L = dw_linkwiz;
-        var E = L.entry;
-        if(!E.value) return;
+        var link = dw_linkwiz.$entry.val(),
+            sel, stxt;
+        if(!link) {
+            return;
+        }
 
-        var sel = getSelection(L.textArea);
-        if(sel.start == 0 && sel.end == 0) sel = L.selection;
+        sel = getSelection(dw_linkwiz.textArea);
+        if(sel.start == 0 && sel.end == 0) {
+            sel = dw_linkwiz.selection;
+        }
 
-        var stxt = sel.getText();
+        stxt = sel.getText();
 
         // don't include trailing space in selection
         if(stxt.charAt(stxt.length - 1) == ' '){
@@ -213,25 +221,29 @@ var dw_linkwiz = {
             stxt = sel.getText();
         }
 
-        if(!stxt && !DOKU_UHC) stxt=title;
+        if(!stxt && !DOKU_UHC) {
+            stxt=title;
+        }
 
         // prepend colon inside namespaces for non namespace pages
-        if(L.textArea.form['id'].value.indexOf(':') != -1 &&
-           E.value.indexOf(':') == -1){
-            E.value = ':'+E.value;
+        if(dw_linkwiz.textArea.form.id.value.indexOf(':') != -1 &&
+           link.indexOf(':') == -1){
+           link = ':' + link;
         }
 
-        var link = '[['+E.value+'|';
-        if(stxt) link += stxt;
+        var so = link.length+3;
+
+        link = '[['+link+'|';
+        if(stxt) {
+            link += stxt;
+        }
         link += ']]';
 
-        var so = E.value.length+3;
-        var eo = 2;
+        pasteText(sel,link,{startofs: so, endofs: 2});
+        dw_linkwiz.hide();
 
-        pasteText(sel,link,{startofs: so, endofs: eo});
-        L.hide();
-        // reset the entry to the parent namespace and remove : at the beginning
-        E.value = E.value.replace(/(^:)?[^:]*$/, '');
+        // reset the entry to the parent namespace
+        dw_linkwiz.$entry.val(dw_linkwiz.$entry.val().replace(/[^:]*$/, ''));
     },
 
     /**
@@ -252,21 +264,15 @@ var dw_linkwiz = {
      * Executes the AJAX call for the page/namespace lookup
      */
     autocomplete_exec: function(){
+        var $res = jQuery(dw_linkwiz.result);
         dw_linkwiz.deselect();
-        dw_linkwiz.result.innerHTML = '<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />';
-
-        // because we need to use POST, we
-        // can not use the .load() function.
-        jQuery.post(
+        $res.html('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />')
+            .load(
             DOKU_BASE + 'lib/exe/ajax.php',
             {
                 call: 'linkwiz',
-                q: dw_linkwiz.entry.value
-            },
-            function (data) {
-                dw_linkwiz.result.innerHTML = data;
-            },
-            'html'
+                q: dw_linkwiz.$entry.val()
+            }
         );
     },
 
@@ -274,33 +280,28 @@ var dw_linkwiz = {
      * Show the link wizard
      */
     show: function(){
-        var L = dw_linkwiz;
-        L.selection  = getSelection(dw_linkwiz.textArea);
-        L.$wiz.css('marginLeft', '0');
-        L.$wiz.css('marginTop', '0');
-        L.entry.focus();
-        L.autocomplete();
+        dw_linkwiz.selection  = getSelection(dw_linkwiz.textArea);
+        dw_linkwiz.$wiz.removeClass('hidden_with_access_keys');
+        dw_linkwiz.$entry.focus();
+        dw_linkwiz.autocomplete();
     },
 
     /**
      * Hide the link wizard
      */
     hide: function(){
-        var L = dw_linkwiz;
-        L.$wiz.css('marginLeft', '-10000px');
-        L.$wiz.css('marginTop', '-10000px');
-        L.textArea.focus();
+        dw_linkwiz.$wiz.addClass('hidden_with_access_keys');
+        dw_linkwiz.textArea.focus();
     },
 
     /**
      * Toggle the link wizard
      */
     toggle: function(){
-        if(dw_linkwiz.$wiz.css('marginLeft') == '-10000px'){
+        if(dw_linkwiz.$wiz.hasClass('hidden_with_access_keys')){
             dw_linkwiz.show();
         }else{
             dw_linkwiz.hide();
         }
     }
-
 };
diff --git a/lib/scripts/media.js b/lib/scripts/media.js
index f4064efd5..e3a000e99 100644
--- a/lib/scripts/media.js
+++ b/lib/scripts/media.js
@@ -1,6 +1,3 @@
-/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: false, strict: true, newcap: true, immed: true, sloppy: true, browser: true */
-/*global jQuery, DOKU_BASE, LANG, bind, DokuCookie, opener, confirm*/
-
 /**
  * JavaScript functionality for the media management popup
  *
diff --git a/lib/scripts/qsearch.js b/lib/scripts/qsearch.js
index f83b7a5a1..c7128b9e3 100644
--- a/lib/scripts/qsearch.js
+++ b/lib/scripts/qsearch.js
@@ -1,6 +1,3 @@
-/*jslint sloppy: true, plusplus: true, continue: true */
-/*global jQuery, DOKU_BASE, window, document, substr_replace */
-
 /**
  * AJAX functions for the pagename quicksearch
  *
diff --git a/lib/scripts/script.js b/lib/scripts/script.js
index 8db223d61..5fddb0431 100644
--- a/lib/scripts/script.js
+++ b/lib/scripts/script.js
@@ -48,8 +48,7 @@ function showLoadBar(){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function hideLoadBar(id){
-  obj = $(id);
-  if(obj) obj.style.display="none";
+  jQuery('#' + id).hide();
 }
 
 /**
@@ -59,4 +58,6 @@ function closePopups(){
     jQuery('div.JSpopup').hide();
 }
 
-
+jQuery(function () {
+    jQuery(document).click(closePopups);
+});
diff --git a/lib/scripts/textselection.js b/lib/scripts/textselection.js
index 742338785..bd80e9341 100644
--- a/lib/scripts/textselection.js
+++ b/lib/scripts/textselection.js
@@ -22,8 +22,7 @@ function selection_class(){
     };
 
     this.getText = function(){
-        if(!this.obj) return '';
-        return this.obj.value.substring(this.start,this.end);
+        return (!this.obj) ? '' : this.obj.value.substring(this.start,this.end);
     };
 }
 
@@ -188,7 +187,7 @@ function pasteText(selection,text,opts){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function insertTags(textAreaID, tagOpen, tagClose, sampleText){
-    var txtarea = $(textAreaID);
+    var txtarea = jQuery('#' + textAreaID)[0];
 
     var selection = getSelection(txtarea);
     var text = selection.getText();
@@ -227,7 +226,7 @@ function insertTags(textAreaID, tagOpen, tagClose, sampleText){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function insertAtCarret(textAreaID, text){
-    var txtarea = $(textAreaID);
+    var txtarea = jQuery('#' + textAreaID)[0];
     var selection = getSelection(txtarea);
     pasteText(selection,text,{nosel: true});
 }
diff --git a/lib/scripts/toolbar.js b/lib/scripts/toolbar.js
index 2306ef5db..6cae9455a 100644
--- a/lib/scripts/toolbar.js
+++ b/lib/scripts/toolbar.js
@@ -1,4 +1,3 @@
-
 // used to identify pickers
 var pickercounter=0;
 
@@ -12,62 +11,55 @@ var pickercounter=0;
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function initToolbar(tbid,edid,tb, allowblock){
-    var $ = jQuery;
+    var $toolbar, $edit;
     if (typeof tbid == 'string') {
-        var toolbar = $('#' + tbid);
+        $toolbar = jQuery('#' + tbid);
     } else {
-        var toolbar = $(tbid);
+        $toolbar = jQuery(tbid);
     }
 
-    if(toolbar.length == 0) return;
-
-    var edit = $('#' + edid);
-    if(edit.length == 0) return;
+    $edit = jQuery('#' + edid);
 
-    if(edit.attr('readOnly')) return;
+    if ($toolbar.length == 0 || $edit.length == 0 || $edit.attr('readOnly')) {
+        return;
+    }
 
     if (typeof allowblock === 'undefined') {
         allowblock = true;
     }
 
     //empty the toolbar area:
-    toolbar.html('');
-
-    var cnt = tb.length;
+    $toolbar.html('');
 
-    for(var i=0; i<cnt; i++){
-        if (!allowblock && tb[i].block === true) {
-            continue;
+    jQuery.each(tb, function (k, val) {
+        if (!tb.hasOwnProperty(k) || (!allowblock && val.block === true)) {
+            return;
         }
-        var actionFunc;
+        var actionFunc, $btn;
 
         // create new button (jQuery object)
-        var btn = $(createToolButton(tb[i]['icon'],
-                                   tb[i]['title'],
-                                   tb[i]['key'],
-                                   tb[i]['id'],
-                                   tb[i]['class']));
+        $btn = jQuery(createToolButton(val.icon, val.title, val.key, val.id,
+                                       val['class']));
 
         // type is a tb function -> assign it as onclick
-        actionFunc = 'tb_'+tb[i]['type'];
-        if( $.isFunction(window[actionFunc]) ){
-            btn.bind('click', bind(window[actionFunc],btn,tb[i],edid) );
-            toolbar.append(btn);
-            continue;
+        actionFunc = 'tb_'+val.type;
+        if( jQuery.isFunction(window[actionFunc]) ){
+            $btn.bind('click', bind(window[actionFunc],$btn,val,edid) );
+            $toolbar.append($btn);
+            return;
         }
 
         // type is a init function -> execute it
-        actionFunc = 'addBtnAction'+tb[i]['type'].charAt(0).toUpperCase()+tb[i]['type'].substring(1);
-        if( $.isFunction(window[actionFunc]) ){
-            if(window[actionFunc](btn, tb[i], edid)){
-                toolbar.append(btn);
+        actionFunc = 'addBtnAction'+val.type.charAt(0).toUpperCase()+val.type.substring(1);
+        if( jQuery.isFunction(window[actionFunc]) ){
+            if(window[actionFunc]($btn, val, edid)){
+                $toolbar.append($btn);
             }
-            continue;
+            return;
         }
 
-        alert('unknown toolbar type: '+tb[i]['type']+'  '+actionFunc);
-    } // end for
-
+        alert('unknown toolbar type: '+val.type+'  '+actionFunc);
+    });
 }
 
 /**
@@ -80,13 +72,10 @@ function initToolbar(tbid,edid,tb, allowblock){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function tb_format(btn, props, edid) {
-    var sample = props['title'];
-    if(props['sample']){
-        sample = props['sample'];
-    }
+    var sample = props.title || props.sample;
     insertTags(edid,
-               fixtxt(props['open']),
-               fixtxt(props['close']),
+               fixtxt(props.open),
+               fixtxt(props.close),
                fixtxt(sample));
     pickerClose();
     return false;
@@ -105,30 +94,27 @@ function tb_format(btn, props, edid) {
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function tb_formatln(btn, props, edid) {
-    var sample = props['title'];
-    if(props['sample']){
-        sample = props['sample'];
-    }
-    sample = fixtxt(sample);
+    var sample = props.title || props.sample,
+        opts,
+        selection = getSelection(jQuery('#'+edid)[0]);
 
-    props['open']  = fixtxt(props['open']);
-    props['close'] = fixtxt(props['close']);
+    sample = fixtxt(sample);
+    props.open  = fixtxt(props.open);
+    props.close = fixtxt(props.close);
 
     // is something selected?
-    var opts;
-    var selection = getSelection($(edid));
     if(selection.getLength()){
         sample = selection.getText();
         opts = {nosel: true};
     }else{
         opts = {
-            startofs: props['open'].length,
-            endofs: props['close'].length
+            startofs: props.open.length,
+            endofs: props.close.length
         };
     }
 
-    sample = sample.split("\n").join(props['close']+"\n"+props['open']);
-    sample = props['open']+sample+props['close'];
+    sample = sample.split("\n").join(props.close+"\n"+props.open);
+    sample = props.open+sample+props.close;
 
     pasteText(selection,sample,opts);
 
@@ -146,7 +132,7 @@ function tb_formatln(btn, props, edid) {
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function tb_insert(btn, props, edid) {
-    insertAtCarret(edid,fixtxt(props['insert']));
+    insertAtCarret(edid,fixtxt(props.insert));
     pickerClose();
     return false;
 }
@@ -161,9 +147,9 @@ function tb_insert(btn, props, edid) {
  */
 function tb_mediapopup(btn, props, edid) {
     window.open(
-        DOKU_BASE+props['url']+encodeURIComponent(NS)+'&edid='+encodeURIComponent(edid),
-        props['name'],
-        props['options']);
+        DOKU_BASE+props.url+encodeURIComponent(NS)+'&edid='+encodeURIComponent(edid),
+        props.name,
+        props.options);
     return false;
 }
 
@@ -178,16 +164,16 @@ function tb_mediapopup(btn, props, edid) {
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function tb_autohead(btn, props, edid){
-    var lvl = currentHeadlineLevel(edid);
+    var lvl = currentHeadlineLevel(edid),
+        tags;
 
     // determine new level
-    lvl += props['mod'];
+    lvl += props.mod;
     if(lvl < 1) lvl = 1;
     if(lvl > 5) lvl = 5;
 
-    var tags = '=';
-    for(var i=0; i<=5-lvl; i++) tags += '=';
-    insertTags(edid, tags+' ', ' '+tags+"\n", props['text']);
+    tags = (new Array(8 - lvl)).join('=');
+    insertTags(edid, tags+' ', ' '+tags+"\n", props.text);
     pickerClose();
     return false;
 }
@@ -196,19 +182,19 @@ function tb_autohead(btn, props, edid){
 /**
  * Add button action for picker buttons and create picker element
  *
- * @param  DOMElement btn   Button element to add the action to
+ * @param  jQuery      btn   Button element to add the action to
  * @param  array      props Associative array of button properties
  * @param  string     edid  ID of the editor textarea
  * @return boolean    If button should be appended
  * @author Gabriel Birke <birke@d-scribe.de>
  */
-function addBtnActionPicker(btn, props, edid) {
+function addBtnActionPicker($btn, props, edid) {
     var pickerid = 'picker'+(pickercounter++);
     createPicker(pickerid, props, edid);
 
-    btn.click(
+    $btn.click(
         function() {
-            pickerToggle(pickerid,btn);
+            pickerToggle(pickerid,$btn);
             return false;
         }
     );
@@ -240,20 +226,11 @@ function addBtnActionLinkwiz(btn, props, edid) {
  *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
-function pickerToggle(pickerid,btn){
-    var picker = jQuery('#' + pickerid);
-    if (picker.css('marginLeft') == '-10000px'){
-        var x = findPosX(btn[0]);
-        var y = findPosY(btn[0]);
-
-        picker.css('left',(x+3)+'px')
-            .css('top', (y+btn[0].offsetHeight+3)+'px')
-            .css('marginLeft', '0px')
-            .css('marginTop', '0px');
-    } else {
-        picker.css('marginLeft', '-10000px')
-            .css('marginTop', '-10000px');
-    }
+function pickerToggle(pickerid,$btn){
+    var $picker = jQuery('#' + pickerid),
+        pos = $btn.offset();
+    $picker.toggleClass('hidden_with_access_keys')
+           .offset({left: pos.left+3, top: pos.top+$btn[0].offsetHeight+3});
 }
 
 /**
@@ -262,11 +239,7 @@ function pickerToggle(pickerid,btn){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function pickerClose(){
-    var pobjs = jQuery('#picker');
-    for(var i=0; i<pobjs.length; i++){
-        pobjs[i].css('marginLeft', '-10000px')
-            .css('marginTop', '-10000px');
-    }
+    jQuery('.picker').addClass('hidden_with_access_keys');
 }
 
 
@@ -277,3 +250,6 @@ function fixtxt(str){
     return str.replace(/\\n/g,"\n");
 }
 
+jQuery(function () {
+    initToolbar('tool__bar','wiki__text',toolbar);
+});
diff --git a/lib/scripts/tree.js b/lib/scripts/tree.js
index 98d3f55d4..96763053d 100644
--- a/lib/scripts/tree.js
+++ b/lib/scripts/tree.js
@@ -1,6 +1,3 @@
-/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: false, newcap: true, immed: true, sloppy: true */
-/*global jQuery, window, DOKU_BASE, DEPRECATED, bind*/
-
 jQuery.fn.dw_tree = function(overrides) {
     var dw_tree = {
 
diff --git a/lib/styles/screen.css b/lib/styles/screen.css
index 80a161f19..6664c2102 100644
--- a/lib/styles/screen.css
+++ b/lib/styles/screen.css
@@ -87,3 +87,8 @@ div.notify {
 .code .st0  { color: #ff0000; }
 .code .sy0  { color: #66cc66; }
 
+.hidden_with_access_keys {
+    /* No display: none to keep accesskeys working */
+    margin-left: -10000px !important;
+    margin-top: -10000px !important;
+}
-- 
GitLab