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