xref: /dokuwiki/lib/scripts/linkwiz.js (revision 47583ff0428eaa0d97e26f61f81ac6d199e9b446)
1/**
2 * The Link Wizard
3 *
4 * @author Andreas Gohr <gohr@cosmocode.de>
5 */
6var linkwiz = {
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             LANG['linkwiz']+'</div>'+
37             '<div>'+LANG['linkto']+' <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 == 27){
63            linkwiz.hide();
64            e.preventDefault();
65            e.stopPropagation();
66            return false;
67        }
68        if(e.keyCode == 38){ //Up
69            linkwiz.select(linkwiz.selected -1);
70            e.preventDefault();
71            e.stopPropagation();
72            return false;
73        }
74        if(e.keyCode == 40){ //Down
75            linkwiz.select(linkwiz.selected +1);
76            e.preventDefault();
77            e.stopPropagation();
78            return false;
79        }
80        if(e.keyCode == 13){ //Enter
81            if(linkwiz.selected > -1){
82                var obj = linkwiz.getResult(linkwiz.selected);
83                if(obj){
84                    var a = obj.getElementsByTagName('A')[0];
85                    linkwiz.resultClick(a);
86                }
87            }else if(linkwiz.entry.value){
88                linkwiz.insertLink(linkwiz.entry.value);
89            }
90
91            e.preventDefault();
92            e.stopPropagation();
93            return false;
94        }
95        linkwiz.autocomplete();
96    },
97
98    /**
99     * Get one of the result by index
100     *
101     * @param int result div to return
102     * @returns DOMObject or null
103     */
104    getResult: function(num){
105        var obj;
106        var childs = linkwiz.result.getElementsByTagName('DIV');
107        obj = childs[num];
108        if(obj){
109            return obj;
110        }else{
111            return null;
112        }
113    },
114
115    /**
116     * Select the given result
117     */
118    select: function(num){
119        if(num < 0){
120            linkwiz.deselect();
121            return;
122        }
123
124        var obj = linkwiz.getResult(num);
125        if(obj){
126            linkwiz.deselect();
127            obj.className += ' selected';
128
129            // make sure the item is viewable in the scroll view
130            // FIXME check IE compatibility
131            if(obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight){
132                linkwiz.result.scrollTop += obj.clientHeight;
133            }else if(obj.offsetTop - linkwiz.result.clientHeight < linkwiz.result.scrollTop){ // this works but isn't quite right, fixes welcome
134                linkwiz.result.scrollTop -= obj.clientHeight;
135            }
136            // now recheck - if still not in view, the user used the mouse to scroll
137            if( (obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight) ||
138                (obj.offsetTop < linkwiz.result.scrollTop) ){
139                obj.scrollIntoView();
140            }
141
142            linkwiz.selected = num;
143        }
144    },
145
146    /**
147     * deselect a result if any is selected
148     */
149    deselect: function(){
150        if(linkwiz.selected > -1){
151            var obj = linkwiz.getResult(linkwiz.selected);
152            if(obj){
153                obj.className = obj.className.replace(/ ?selected/,'');
154            }
155        }
156        linkwiz.selected = -1;
157    },
158
159    /**
160     * Handle clicks in the result set an dispatch them to
161     * resultClick()
162     */
163    onResultClick: function(e){
164        if(e.target.tagName != 'A') return;
165        e.stopPropagation();
166        e.preventDefault();
167        linkwiz.resultClick(e.target);
168        return false;
169    },
170
171    /**
172     * Handles the "click" on a given result anchor
173     */
174    resultClick: function(a){
175        var id = a.title;
176        if(id == '' || id.substr(id.length-1) == ':'){
177            linkwiz.entry.value = id;
178            linkwiz.autocomplete_exec();
179        }else{
180            linkwiz.entry.value = id;
181            if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){
182                linkwiz.insertLink(a.nextSibling.innerHTML);
183            }else{
184                linkwiz.insertLink('');
185            }
186        }
187    },
188
189    /**
190     * Insert the id currently in the entry box to the textarea,
191     * replacing the current selection or at the curso postion.
192     * When no selection is available the given title will be used
193     * as link title instead
194     */
195    insertLink: function(title){
196        if(!linkwiz.entry.value) return;
197        var sel  = getSelection(linkwiz.textArea);
198        var stxt = sel.getText();
199        if(!stxt) stxt=title;
200
201        var link = '[['+linkwiz.entry.value+'|';
202        if(stxt) link += stxt;
203        link += ']]';
204
205        var so = linkwiz.entry.value.length+3;
206        var eo = 2;
207
208        pasteText(sel,link,{startofs: so, endofs: eo});
209        linkwiz.hide();
210    },
211
212    /**
213     * Start the page/namespace lookup timer
214     *
215     * Calls autocomplete_exec when the timer runs out
216     */
217    autocomplete: function(){
218        if(linkwiz.timer !== null){
219            window.clearTimeout(linkwiz.timer);
220            linkwiz.timer = null;
221        }
222
223        linkwiz.timer = window.setTimeout(linkwiz.autocomplete_exec,350);
224    },
225
226    /**
227     * Executes the AJAX call for the page/namespace lookup
228     */
229    autocomplete_exec: function(){
230        linkwiz.deselect();
231        linkwiz.result.innerHTML = '<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />';
232        linkwiz.sack.runAJAX('call=linkwiz&q='+encodeURI(linkwiz.entry.value));
233    },
234
235    /**
236     * Clears the result area
237     */
238    clear: function(){
239        linkwiz.result.innerHTML = 'Search for a matching page name above, or browse through the pages on the right';
240        linkwiz.entry.value = '';
241    },
242
243    /**
244     * Show the linkwizard
245     */
246    show: function(){
247        linkwiz.wiz.style['display'] = '';
248        linkwiz.entry.focus();
249        linkwiz.autocomplete();
250    },
251
252    /**
253     * Hide the link wizard
254     */
255    hide: function(){
256        linkwiz.wiz.style['display'] = 'none';
257        linkwiz.textArea.focus();
258    },
259
260    /**
261     * Toggle the link wizard
262     */
263    toggle: function(){
264        if(linkwiz.wiz.style['display'] == 'none'){
265            linkwiz.show();
266        }else{
267            linkwiz.hide();
268        }
269    }
270};
271
272