1/**
2 * The Link Wizard
3 *
4 * @author Andreas Gohr <gohr@cosmocode.de>
5 * @author Pierre Spring <pierre.spring@caillou.ch>
6 */
7var dw_linkwiz = {
8    $wiz: null,
9    $entry: null,
10    result: null,
11    timer: null,
12    textArea: null,
13    selected: null,
14    selection: null,
15
16    /**
17     * Initialize the dw_linkwizard by creating the needed HTML
18     * and attaching the eventhandlers
19     */
20    init: function($editor){
21        // position relative to the text area
22        var pos = $editor.position();
23
24        // create HTML Structure
25        if(dw_linkwiz.$wiz)
26            return;
27        dw_linkwiz.$wiz = jQuery(document.createElement('div'))
28               .dialog({
29                   autoOpen: false,
30                   draggable: true,
31                   title: LANG.linkwiz,
32                   resizable: false
33               })
34               .html(
35                    '<div>'+LANG.linkto+' <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+
36                    '<div id="link__wiz_result"></div>'
37                    )
38               .parent()
39               .attr('id','link__wiz')
40               .css({
41                    'position':    'absolute',
42                    'top':         (pos.top+20)+'px',
43                    'left':        (pos.left+80)+'px'
44                   })
45               .hide()
46               .appendTo('.dokuwiki:first');
47
48        dw_linkwiz.textArea = $editor[0];
49        dw_linkwiz.result = jQuery('#link__wiz_result')[0];
50
51        // scrollview correction on arrow up/down gets easier
52        jQuery(dw_linkwiz.result).css('position', 'relative');
53
54        dw_linkwiz.$entry = jQuery('#link__wiz_entry');
55        if(JSINFO.namespace){
56            dw_linkwiz.$entry.val(JSINFO.namespace+':');
57        }
58
59        // attach event handlers
60        jQuery('#link__wiz .ui-dialog-titlebar-close').on('click', dw_linkwiz.hide);
61        dw_linkwiz.$entry.keyup(dw_linkwiz.onEntry);
62        jQuery(dw_linkwiz.result).on('click', 'a', dw_linkwiz.onResultClick);
63    },
64
65    /**
66     * handle all keyup events in the entry field
67     */
68    onEntry: function(e){
69        if(e.keyCode == 37 || e.keyCode == 39){ //left/right
70            return true; //ignore
71        }
72        if(e.keyCode == 27){ //Escape
73            dw_linkwiz.hide();
74            e.preventDefault();
75            e.stopPropagation();
76            return false;
77        }
78        if(e.keyCode == 38){ //Up
79            dw_linkwiz.select(dw_linkwiz.selected -1);
80            e.preventDefault();
81            e.stopPropagation();
82            return false;
83        }
84        if(e.keyCode == 40){ //Down
85            dw_linkwiz.select(dw_linkwiz.selected +1);
86            e.preventDefault();
87            e.stopPropagation();
88            return false;
89        }
90        if(e.keyCode == 13){ //Enter
91            if(dw_linkwiz.selected > -1){
92                var $obj = dw_linkwiz.$getResult(dw_linkwiz.selected);
93                if($obj.length > 0){
94                    dw_linkwiz.resultClick($obj.find('a')[0]);
95                }
96            }else if(dw_linkwiz.$entry.val()){
97                dw_linkwiz.insertLink(dw_linkwiz.$entry.val());
98            }
99
100            e.preventDefault();
101            e.stopPropagation();
102            return false;
103        }
104        dw_linkwiz.autocomplete();
105    },
106
107    /**
108     * Get one of the results by index
109     *
110     * @param   num int result div to return
111     * @returns DOMObject or null
112     */
113    getResult: function(num){
114        DEPRECATED('use dw_linkwiz.$getResult()[0] instead');
115        return dw_linkwiz.$getResult()[0] || null;
116    },
117
118    /**
119     * Get one of the results by index
120     *
121     * @param   num int result div to return
122     * @returns jQuery object
123     */
124    $getResult: function(num) {
125        return jQuery(dw_linkwiz.result).find('div').eq(num);
126    },
127
128    /**
129     * Select the given result
130     */
131    select: function(num){
132        if(num < 0){
133            dw_linkwiz.deselect();
134            return;
135        }
136
137        var $obj = dw_linkwiz.$getResult(num);
138        if ($obj.length === 0) {
139            return;
140        }
141
142        dw_linkwiz.deselect();
143        $obj.addClass('selected');
144
145        // make sure the item is viewable in the scroll view
146
147        //getting child position within the parent
148        var childPos = $obj.position().top;
149        //getting difference between the childs top and parents viewable area
150        var yDiff = childPos + $obj.outerHeight() - jQuery(dw_linkwiz.result).innerHeight();
151
152        if (childPos < 0) {
153            //if childPos is above viewable area (that's why it goes negative)
154            jQuery(dw_linkwiz.result)[0].scrollTop += childPos;
155        } else if(yDiff > 0) {
156            // if difference between childs top and parents viewable area is
157            // greater than the height of a childDiv
158            jQuery(dw_linkwiz.result)[0].scrollTop += yDiff;
159        }
160
161        dw_linkwiz.selected = num;
162    },
163
164    /**
165     * deselect a result if any is selected
166     */
167    deselect: function(){
168        if(dw_linkwiz.selected > -1){
169            dw_linkwiz.$getResult(dw_linkwiz.selected).removeClass('selected');
170        }
171        dw_linkwiz.selected = -1;
172    },
173
174    /**
175     * Handle clicks in the result set an dispatch them to
176     * resultClick()
177     */
178    onResultClick: function(e){
179        if(!jQuery(this).is('a')) {
180            return;
181        }
182        e.stopPropagation();
183        e.preventDefault();
184        dw_linkwiz.resultClick(this);
185        return false;
186    },
187
188    /**
189     * Handles the "click" on a given result anchor
190     */
191    resultClick: function(a){
192        dw_linkwiz.$entry.val(a.title);
193        if(a.title == '' || a.title.substr(a.title.length-1) == ':'){
194            dw_linkwiz.autocomplete_exec();
195        }else{
196            if (jQuery(a.nextSibling).is('span')) {
197                dw_linkwiz.insertLink(a.nextSibling.innerHTML);
198            }else{
199                dw_linkwiz.insertLink('');
200            }
201        }
202    },
203
204    /**
205     * Insert the id currently in the entry box to the textarea,
206     * replacing the current selection or at the cursor position.
207     * When no selection is available the given title will be used
208     * as link title instead
209     */
210    insertLink: function(title){
211        var link = dw_linkwiz.$entry.val(),
212            sel, stxt;
213        if(!link) {
214            return;
215        }
216
217        sel = DWgetSelection(dw_linkwiz.textArea);
218        if(sel.start == 0 && sel.end == 0) {
219            sel = dw_linkwiz.selection;
220        }
221
222        stxt = sel.getText();
223
224        // don't include trailing space in selection
225        if(stxt.charAt(stxt.length - 1) == ' '){
226            sel.end--;
227            stxt = sel.getText();
228        }
229
230        if(!stxt && !DOKU_UHC) {
231            stxt=title;
232        }
233
234        // prepend colon inside namespaces for non namespace pages
235        if(dw_linkwiz.textArea.form.id.value.indexOf(':') != -1 &&
236           link.indexOf(':') == -1){
237           link = ':' + link;
238        }
239
240        var so = link.length;
241        var eo = 0;
242        if(dw_linkwiz.val){
243            if(dw_linkwiz.val.open) {
244                so += dw_linkwiz.val.open.length;
245                link = dw_linkwiz.val.open+link;
246            }
247            link += '|';
248            so += 1;
249            if(stxt) {
250                link += stxt;
251            }
252            if(dw_linkwiz.val.close) {
253                link += dw_linkwiz.val.close;
254                eo = dw_linkwiz.val.close.length;
255            }
256        }
257
258        pasteText(sel,link,{startofs: so, endofs: eo});
259        dw_linkwiz.hide();
260
261        // reset the entry to the parent namespace
262        var externallinkpattern = new RegExp('^((f|ht)tps?:)?//', 'i'),
263            entry_value;
264        if (externallinkpattern.test(dw_linkwiz.$entry.val())) {
265            if (JSINFO.namespace) {
266                entry_value = JSINFO.namespace + ':';
267            } else {
268                entry_value = ''; //reset whole external links
269            }
270        } else {
271            entry_value = dw_linkwiz.$entry.val().replace(/[^:]*$/, '')
272        }
273        dw_linkwiz.$entry.val(entry_value);
274    },
275
276    /**
277     * Start the page/namespace lookup timer
278     *
279     * Calls autocomplete_exec when the timer runs out
280     */
281    autocomplete: function(){
282        if(dw_linkwiz.timer !== null){
283            window.clearTimeout(dw_linkwiz.timer);
284            dw_linkwiz.timer = null;
285        }
286
287        dw_linkwiz.timer = window.setTimeout(dw_linkwiz.autocomplete_exec,350);
288    },
289
290    /**
291     * Executes the AJAX call for the page/namespace lookup
292     */
293    autocomplete_exec: function(){
294        var $res = jQuery(dw_linkwiz.result);
295        dw_linkwiz.deselect();
296        $res.html('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />')
297            .load(
298            DOKU_BASE + 'lib/exe/ajax.php',
299            {
300                call: 'linkwiz',
301                q: dw_linkwiz.$entry.val()
302            }
303        );
304    },
305
306    /**
307     * Show the link wizard
308     */
309    show: function(){
310        dw_linkwiz.selection  = DWgetSelection(dw_linkwiz.textArea);
311        dw_linkwiz.$wiz.show();
312        dw_linkwiz.$entry.focus();
313        dw_linkwiz.autocomplete();
314
315        // Move the cursor to the end of the input
316        var temp = dw_linkwiz.$entry.val();
317        dw_linkwiz.$entry.val('');
318        dw_linkwiz.$entry.val(temp);
319    },
320
321    /**
322     * Hide the link wizard
323     */
324    hide: function(){
325        dw_linkwiz.$wiz.hide();
326        dw_linkwiz.textArea.focus();
327    },
328
329    /**
330     * Toggle the link wizard
331     */
332    toggle: function(){
333        if(dw_linkwiz.$wiz.css('display') == 'none'){
334            dw_linkwiz.show();
335        }else{
336            dw_linkwiz.hide();
337        }
338    }
339};
340