xref: /dokuwiki/lib/scripts/linkwiz.js (revision 7fe97caa1ee1d0e4eb27139a9cb86aac0087b3a0)
1/**
2 * The Link Wizard
3 *
4 * @author Andreas Gohr <gohr@cosmocode.de>
5 */
6linkwiz = {
7    wiz:    null,
8    entry:  null,
9    result: null,
10    timer:  null,
11    sack:   null,
12    textArea: null,
13    selected: -1,
14
15    /**
16     * Initialize the linkwizard by creating the needed HTML
17     * and attaching the eventhandlers
18     */
19    init: function(textArea){
20        // prepare AJAX object
21        linkwiz.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php');
22        linkwiz.sack.AjaxFailedAlert = '';
23        linkwiz.sack.encodeURIString = false;
24
25        // create HTML Structure
26        linkwiz.wiz = document.createElement('div');
27        linkwiz.wiz.id = 'link__wiz';
28        linkwiz.wiz.className     = 'picker';
29        linkwiz.wiz.style.top  = (findPosY(textArea)+20)+'px';
30        linkwiz.wiz.style.left = (findPosX(textArea)+80)+'px';
31        linkwiz.wiz.style.display = 'none';
32
33        linkwiz.wiz.innerHTML =
34             '<div id="link__wiz_header">'+
35             '<img src="'+DOKU_BASE+'lib/images/close.png" width="16" height="16" align="right" alt="" id="link__wiz_close" />'+
36             'Link Wizard</div>'+
37             '<div>Link: <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+
38             '<div id="link__wiz_result"></div>';
39        textArea.form.parentNode.appendChild(linkwiz.wiz);
40        linkwiz.textArea = textArea;
41        linkwiz.result = $('link__wiz_result');
42        linkwiz.entry = $('link__wiz_entry');
43
44        // attach event handlers
45        var obj;
46        obj = $('link__wiz_close');
47        obj.onclick = linkwiz.hide;
48
49        linkwiz.sack.elementObj = linkwiz.result;
50        addEvent(linkwiz.entry,'keyup',linkwiz.onEntry);
51        addEvent(linkwiz.result,'click',linkwiz.onResultClick);
52        drag.attach(linkwiz.wiz,$('link__wiz_header'));
53    },
54
55    /**
56     * handle all keyup events in the entry field
57     */
58    onEntry: function(e){
59        if(e.keyCode == 37 || e.keyCode == 39){ //left/right
60            return true; //ignore
61        }
62        if(e.keyCode == 38){ //Up
63            linkwiz.select(linkwiz.selected -1);
64            e.preventDefault();
65            e.stopPropagation();
66            return false;
67        }
68        if(e.keyCode == 40){ //Down
69            linkwiz.select(linkwiz.selected +1);
70            e.preventDefault();
71            e.stopPropagation();
72            return false;
73        }
74        if(e.keyCode == 13){ //Enter
75            if(linkwiz.selected > -1){
76                var obj = linkwiz.getResult(linkwiz.selected);
77                if(obj){
78                    var a = obj.getElementsByTagName('A')[0];
79                    linkwiz.resultClick(a);
80                }
81            }else if(linkwiz.entry.value){
82                linkwiz.insertLink(linkwiz.entry.value);
83            }
84
85            e.preventDefault();
86            e.stopPropagation();
87            return false;
88        }
89        linkwiz.autocomplete();
90    },
91
92    /**
93     * Get one of the result by index
94     *
95     * @param int result div to return
96     * @returns DOMObject or null
97     */
98    getResult: function(num){
99        var obj;
100        var childs = linkwiz.result.getElementsByTagName('DIV');
101        obj = childs[num];
102        if(obj){
103            return obj;
104        }else{
105            return null;
106        }
107    },
108
109    /**
110     * Select the given result
111     */
112    select: function(num){
113        if(num < 0){
114            linkwiz.deselect();
115            return;
116        }
117
118        var obj = linkwiz.getResult(num);
119        if(obj){
120            linkwiz.deselect();
121            obj.className += ' selected';
122
123            // make sure the item is viewable in the scroll view
124            // FIXME check IE compatibility
125            if(obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight){
126                linkwiz.result.scrollTop += obj.clientHeight;
127            }else if(obj.offsetTop - linkwiz.result.clientHeight < linkwiz.result.scrollTop){ // this works but isn't quite right, fixes welcome
128                linkwiz.result.scrollTop -= obj.clientHeight;
129            }
130            // now recheck - if still not in view, the user used the mouse to scroll
131            if( (obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight) ||
132                (obj.offsetTop < linkwiz.result.scrollTop) ){
133                obj.scrollIntoView();
134            }
135
136            linkwiz.selected = num;
137        }
138    },
139
140    /**
141     * deselect a result if any is selected
142     */
143    deselect: function(){
144        if(linkwiz.selected > -1){
145            var obj = linkwiz.getResult(linkwiz.selected);
146            if(obj){
147                obj.className = obj.className.replace(/ ?selected/,'');
148            }
149        }
150        linkwiz.selected = -1;
151    },
152
153    /**
154     * Handle clicks in the result set an dispatch them to
155     * resultClick()
156     */
157    onResultClick: function(e){
158        if(e.target.tagName != 'A') return;
159        e.stopPropagation();
160        e.preventDefault();
161        linkwiz.resultClick(e.target);
162        return false;
163    },
164
165    /**
166     * Handles the "click" on a given result anchor
167     */
168    resultClick: function(a){
169        var id = a.title;
170        if(id == '' || id.substr(id.length-1) == ':'){
171            linkwiz.entry.value = id;
172            linkwiz.autocomplete_exec();
173        }else{
174            linkwiz.entry.value = id;
175            if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){
176                linkwiz.insertLink(a.nextSibling.innerHTML);
177            }else{
178                linkwiz.insertLink('');
179            }
180        }
181    },
182
183    /**
184     * Insert the id currently in the entry box to the textarea,
185     * replacing the current selection or at the curso postion.
186     * When no selection is available the given title will be used
187     * as link title instead
188     */
189    insertLink: function(title){
190        if(!linkwiz.entry.value) return;
191        var sel  = getSelection(linkwiz.textArea);
192        var stxt = sel.getText();
193        if(!stxt) stxt=title;
194
195        var link = '[['+linkwiz.entry.value;
196        if(stxt) link += '|'+stxt;
197        link += ']]';
198
199        var so = linkwiz.entry.value.length+3;
200        var eo = 2;
201
202        pasteText(sel,link,{startofs: so, endofs: eo});
203        linkwiz.hide();
204    },
205
206    /**
207     * Start the page/namespace lookup timer
208     *
209     * Calls autocomplete_exec when the timer runs out
210     */
211    autocomplete: function(){
212        if(linkwiz.timer !== null){
213            window.clearTimeout(linkwiz.timer);
214            linkwiz.timer = null;
215        }
216
217        linkwiz.timer = window.setTimeout(linkwiz.autocomplete_exec,350);
218    },
219
220    /**
221     * Executes the AJAX call for the page/namespace lookup
222     */
223    autocomplete_exec: function(){
224        linkwiz.deselect();
225        linkwiz.result.innerHTML = '<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />';
226        linkwiz.sack.runAJAX('call=linkwiz&q='+encodeURI(linkwiz.entry.value));
227    },
228
229    /**
230     * Clears the result area
231     */
232    clear: function(){
233        linkwiz.result.innerHTML = 'Search for a matching page name above, or browse through the pages on the right';
234        linkwiz.entry.value = '';
235    },
236
237    /**
238     * Show the linkwizard
239     */
240    show: function(){
241        linkwiz.wiz.style['display'] = '';
242        linkwiz.entry.focus();
243        linkwiz.autocomplete();
244    },
245
246    /**
247     * Hide the link wizard
248     */
249    hide: function(){
250        linkwiz.wiz.style['display'] = 'none';
251    },
252
253    /**
254     * Toggle the link wizard
255     */
256    toggle: function(){
257        if(linkwiz.wiz.style['display'] == 'none'){
258            linkwiz.show();
259        }else{
260            linkwiz.hide();
261        }
262    },
263};
264
265