1/**
2 * The Namespace picker dialog
3 *
4 * Based on the code from "The Link Wizard"/linkwiz.js
5 * from Andreas Gohr <gohr@cosmocode.de> and Pierre Spring <pierre.spring@caillou.ch>
6 *
7 * @author LarsDW223
8 */
9var bc_nspicker = {
10    $picker: null,
11    $entry: null,
12    result: null,
13    timer: null,
14    textArea: null,
15    selected: null,
16    selection: null,
17
18    /**
19     * Initialize the bc_nspicker by creating the needed HTML
20     * and attaching the eventhandlers
21     */
22    init: function ($editor) {
23        "use strict";
24        // position relative to the text area
25        var pos = $editor.position();
26
27        // create HTML Structure
28        if (bc_nspicker.$picker) {
29            return;
30        }
31        bc_nspicker.$picker = jQuery(document.createElement('div'))
32            .dialog({
33                autoOpen: false,
34                draggable: true,
35                title: LANG.plugins.bookcreator.namespace_picker,
36                resizable: false
37            })
38            .html(
39                '<div>' + LANG.plugins.bookcreator.select_namespace + ' <input type="text" class="edit" id="bc__nspicker_entry" autocomplete="off" />' +
40                        '<input type="button" value="' + LANG.plugins.bookcreator.select + '" id="bc__nspicker_select">' +
41                        '<input type="button" value="' + LANG.plugins.bookcreator.cancel + '" id="bc__nspicker_cancel">' +
42                        '<br><input type="checkbox" value="Recursive" id="bc__nspicker_recursive">' + LANG.plugins.bookcreator.add_subns_too +
43                    '</div>' +
44                    '<div id="bc__nspicker_result"></div>'
45            )
46            .parent()
47            .attr('id', 'bc__nspicker')
48            .css({
49                'position': 'absolute',
50                'top': (pos.top + 20) + 'px', //overwritten later with position of Add Namespace button.
51                'left': (pos.left + 80) + 'px'
52            })
53            .hide()
54            .appendTo('.dokuwiki:first');
55
56        bc_nspicker.textArea = $editor[0];
57        bc_nspicker.result = jQuery('#bc__nspicker_result')[0];
58
59        // scrollview correction on arrow up/down gets easier
60        jQuery(bc_nspicker.result).css('position', 'relative');
61
62        bc_nspicker.$entry = jQuery('#bc__nspicker_entry');
63        if (JSINFO.namespace) {
64            bc_nspicker.$entry.val(JSINFO.namespace + ':');
65        }
66
67        // attach event handlers
68        jQuery('#bc__nspicker .ui-dialog-titlebar-close').on('click', bc_nspicker.hide);
69        jQuery('#bc__nspicker_select').on('click', bc_nspicker.selectNamespace_exec);
70        jQuery('#bc__nspicker_cancel').on('click', bc_nspicker.hide);
71        bc_nspicker.$entry.keyup(bc_nspicker.onEntry);
72        jQuery(bc_nspicker.result).on('click', 'a', bc_nspicker.onResultClick);
73    },
74
75    /**
76     * handle all keyup events in the entry field
77     */
78    onEntry: function (e) {
79        "use strict";
80        if (e.keyCode === 37 || e.keyCode === 39) { //left/right
81            return true; //ignore
82        }
83        if (e.keyCode === 27) { //Escape
84            bc_nspicker.hide();
85            e.preventDefault();
86            e.stopPropagation();
87            return false;
88        }
89        if (e.keyCode === 38) { //Up
90            bc_nspicker.select(bc_nspicker.selected - 1);
91            e.preventDefault();
92            e.stopPropagation();
93            return false;
94        }
95        if (e.keyCode === 40) { //Down
96            bc_nspicker.select(bc_nspicker.selected + 1);
97            e.preventDefault();
98            e.stopPropagation();
99            return false;
100        }
101        if (e.keyCode === 13) { //Enter
102            if (bc_nspicker.selected > -1) {
103                var $obj = bc_nspicker.$getResult(bc_nspicker.selected);
104                if ($obj.length > 0) {
105                    bc_nspicker.resultClick($obj.find('a')[0]);
106                }
107            } else if (bc_nspicker.$entry.val()) {
108                bc_nspicker.selectNamespace_exec();
109            }
110
111            e.preventDefault();
112            e.stopPropagation();
113            return false;
114        }
115        bc_nspicker.autocomplete();
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        "use strict";
126        return jQuery(bc_nspicker.result).find('div').eq(num);
127    },
128
129    /**
130     * Select the given result
131     */
132    select: function (num) {
133        "use strict";
134        if (num < 0) {
135            bc_nspicker.deselect();
136            return;
137        }
138
139        var $obj = bc_nspicker.$getResult(num);
140        if ($obj.length === 0) {
141            return;
142        }
143
144        bc_nspicker.deselect();
145        $obj.addClass('selected');
146
147        // make sure the item is viewable in the scroll view
148
149        //getting child position within the parent
150        var childPos = $obj.position().top;
151        //getting difference between the childs top and parents viewable area
152        var yDiff = childPos + $obj.outerHeight() - jQuery(bc_nspicker.result).innerHeight();
153
154        if (childPos < 0) {
155            //if childPos is above viewable area (that's why it goes negative)
156            jQuery(bc_nspicker.result)[0].scrollTop += childPos;
157        } else if (yDiff > 0) {
158            // if difference between childs top and parents viewable area is
159            // greater than the height of a childDiv
160            jQuery(bc_nspicker.result)[0].scrollTop += yDiff;
161        }
162
163        bc_nspicker.selected = num;
164    },
165
166    /**
167     * deselect a result if any is selected
168     */
169    deselect: function () {
170        "use strict";
171        if (bc_nspicker.selected > -1) {
172            bc_nspicker.$getResult(bc_nspicker.selected).removeClass('selected');
173        }
174        bc_nspicker.selected = -1;
175    },
176
177    /**
178     * Handle clicks in the result set an dispatch them to
179     * resultClick()
180     */
181    onResultClick: function (e) {
182        "use strict";
183        if (!jQuery(this).is('a')) {
184            return;
185        }
186        e.stopPropagation();
187        e.preventDefault();
188        bc_nspicker.resultClick(this);
189        return false;
190    },
191
192    /**
193     * Handles the "click" on a given result anchor
194     */
195    resultClick: function (a) {
196        "use strict";
197        if (a.title === '' || a.title.substr(a.title.length - 1) === ':') {
198            bc_nspicker.$entry.val(a.title);
199            bc_nspicker.autocomplete_exec();
200        }
201    },
202
203    /**
204     * Start the page/namespace lookup timer
205     *
206     * Calls autocomplete_exec when the timer runs out
207     */
208    autocomplete: function () {
209        "use strict";
210        if (bc_nspicker.timer !== null) {
211            window.clearTimeout(bc_nspicker.timer);
212            bc_nspicker.timer = null;
213        }
214
215        bc_nspicker.timer = window.setTimeout(bc_nspicker.autocomplete_exec, 350);
216    },
217
218    /**
219     * Executes the AJAX call for the page/namespace lookup
220     */
221    autocomplete_exec: function () {
222        "use strict";
223        var $res = jQuery(bc_nspicker.result);
224        bc_nspicker.deselect();
225        $res.html('<img src="' + DOKU_BASE + 'lib/images/throbber.gif" alt="" width="16" height="16" />')
226            .load(
227                DOKU_BASE + 'lib/exe/ajax.php',
228                {
229                    call: 'linkwiz',
230                    q: bc_nspicker.$entry.val()
231                }
232            );
233    },
234
235    /**
236     * Show the link wizard
237     */
238    show: function () {
239        "use strict";
240        bc_nspicker.selection = DWgetSelection(bc_nspicker.textArea);
241        bc_nspicker.$picker.show();
242        bc_nspicker.$entry.focus();
243        bc_nspicker.autocomplete();
244
245        // Move the cursor to the end of the input
246        var temp = bc_nspicker.$entry.val();
247        bc_nspicker.$entry.val('');
248        bc_nspicker.$entry.val(temp);
249    },
250
251    /**
252     * Hide the link wizard
253     */
254    hide: function () {
255        "use strict";
256        bc_nspicker.$picker.hide();
257        bc_nspicker.textArea.focus();
258    },
259
260    /**
261     * Toggle the link wizard
262     */
263    toggle: function () {
264        "use strict";
265        if (bc_nspicker.$picker.css('display') === 'none') {
266            bc_nspicker.show();
267        } else {
268            bc_nspicker.hide();
269        }
270    },
271
272    /**
273     * Executes the AJAX call for the selected namespace lookup.
274     * Parameter "ns" is the namespace to search through. Parameter
275     * "r" specifies if the search should be recursive or not.
276     */
277    selectNamespace_exec: function () {
278        "use strict";
279        var $recursive = jQuery('#bc__nspicker_recursive');
280
281        jQuery.post(
282            DOKU_BASE + 'lib/exe/ajax.php',
283            {
284                call: 'plugin_bookcreator_call',
285                action: 'searchPages',
286                ns: bc_nspicker.$entry.val(),
287                r: $recursive.is(':checked'),
288                sectok: jQuery('input[name="sectok"]').val()
289            },
290            bc_nspicker.selectNamespace,
291            'json'
292        );
293    },
294
295    /**
296     * Select Namespace.
297     * Add all pages in the selected Namespace to the book, show
298     * window with added pages and then close/hide the Namespace
299     * picker.
300     */
301    selectNamespace: function (data) {
302        "use strict";
303        var content;
304        var pages;
305        var name;
306
307        // Go through the array of pages, add them and prepare
308        // a message for the user
309        pages = 0;
310        content = LANG.plugins.bookcreator.added_pages + "\n\n";
311        if (data.hasOwnProperty('pages')) {
312            jQuery(data.pages).each(function (index) {
313                name = data.pages [index];
314                Bookcreator.selectedpages.addPage (name);
315                content += name + "\n";
316                pages += 1;
317            });
318        }
319        if (pages === 0) {
320            content += LANG.plugins.bookcreator.no_pages_selected + "\n";
321        }
322
323        BookManager.updateListsFromStorage();
324        window.alert(content);
325        bc_nspicker.hide();
326    }
327};
328