1/**
2 * PyCode plugin: it embeds a Python script hosted in a remote repository.
3 *
4 * script.js: it defines the PyCode Wizard.
5 *
6 * @author Torpedo <dgtorpedo@gmail.com>
7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @package script
9 */
10
11var pycode_wiz = {
12    $wiz: null,
13    timer: null,
14    textArea: null,
15
16    /**
17     * Initialize the pycode_wiz 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        pycode_wiz.$wiz = jQuery(document.createElement("div"))
26            .dialog({
27                autoOpen: false,
28                draggable: true,
29                title: LANG.plugins.pycode.wizard,
30                resizable: false
31            })
32            .html(
33                '<fieldset class="index">' +
34                    '<legend>'+LANG.plugins.pycode.repository+'</legend>' +
35                    '<div class="pycode__repo-src-url">' +
36                        '<button id="pycode__b" type="button">' +
37                            '<img src="'+DOKU_BASE+'lib/plugins/pycode/images/b.png"></img>' +
38                         '</button>' +
39                        '<button id="pycode__g" type="button">' +
40                            '<img  src="'+DOKU_BASE+'lib/plugins/pycode/images/g.png"></img>' +
41                         '</button>' +
42                        '<input id="pycode__src-url" type="text" disabled>' +
43                    '</div>' +
44                '</fieldset>' +
45
46                '<fieldset class="options">' +
47                    '<legend>'+LANG.plugins.pycode.options+'</legend>' +
48                    '<div>' +
49                        '<strong>'+LANG.plugins.pycode.embed+'</strong><br />' +
50
51                        '<div class="pycode__embed-lns" id="pycode__left">' +
52                            '<label><input id="pycode__opt-lns" type="checkbox">' +
53                            LANG.plugins.pycode.lns+'</label>' +
54                        '</div>' +
55                        '<div class="pycode__embed-lns" id="pycode__right">' +
56                            '<input id="pycode__lns-from" type="text" disabled>' +
57                            ' : ' +
58                            '<input id="pycode__lns-to" type="text" disabled>' +
59                        '</div>' +
60
61                        '<div class="pycode__embed-def" id="pycode__left">' +
62                            '<label><input id="pycode__opt-def" type="checkbox">' +
63                            LANG.plugins.pycode.def+'</label>' +
64                        '</div>' +
65                        '<div class="pycode__embed-def" id="pycode__right">' +
66                            '<input id="pycode__def" type="text" disabled>' +
67                        '</div>' +
68
69                        '<div class="pycode__embed-cls" id="pycode__left">' +
70                            '<label><input id="pycode__opt-cls" type="checkbox">' +
71                            LANG.plugins.pycode.cls+'</label>' +
72                        '</div>' +
73                        '<div class="pycode__embed-cls" id="pycode__right">' +
74                            '<input id="pycode__cls" type="text" disabled>' +
75                        '</div>' +
76                    '</div>' +
77
78                    '<div>' +
79                        '<strong>'+LANG.plugins.pycode.nums+'</strong><br />' +
80
81                        '<div class="pycode__code-nums" id="pycode__left">' +
82                            '<label><input id="pycode__opt-nums" type="checkbox">' +
83                            LANG.plugins.pycode.change+'</label>' +
84                        '</div>' +
85                        '<div class="pycode__code-nums" id="pycode__right">' +
86                            '<label><input type="radio" name="radio-nums" value="show" disabled>' +
87                            LANG.plugins.pycode.show+'</label>' +
88                            '<label><input type="radio" name="radio-nums" value="hide" disabled>' +
89                            LANG.plugins.pycode.hide+'</label>' +
90                        '</div>' +
91                    '</div>' +
92
93                    '<div>' +
94                        '<strong>'+LANG.plugins.pycode.title+'</strong><br />' +
95
96                        '<div class="pycode__code-title" id="pycode__left">' +
97                            '<label><input id="pycode__opt-title" type="checkbox">' +
98                            LANG.plugins.pycode.change+'</label>' +
99                        '</div>' +
100                        '<div class="pycode__code-title" id="pycode__right">' +
101                            '<input id="pycode__title" type="text" disabled>' +
102                        '</div>' +
103                    '</div>' +
104
105                    '<div>' +
106                        '<strong>'+LANG.plugins.pycode.docstr+'</strong><br />' +
107
108                        '<div class="pycode__code-docstr" id="pycode__left">' +
109                            '<label><input id="pycode__opt-docstr" type="checkbox">' +
110                            LANG.plugins.pycode.change+'</label>' +
111                        '</div>' +
112                        '<div class="pycode__code-docstr" id="pycode__right">' +
113                            '<label><input type="radio" name="radio-docstr" value="show" disabled>' +
114                            LANG.plugins.pycode.show+'</label>' +
115                            '<label><input type="radio" name="radio-docstr" value="hide" disabled>' +
116                            LANG.plugins.pycode.hide+'</label>' +
117                        '</div>' +
118                    '</div>' +
119                '</fieldset>' +
120                '<input type="submit" value="'+LANG.plugins.pycode.insert+'" id="pycode__insert">'
121            )
122            .parent()
123            .attr("id","pycode__wiz")
124            .css({
125                "position": "absolute",
126                "top": (pos.top+20)+"px",
127                "left": (pos.left+80)+"px"
128            })
129            .hide()
130            .appendTo(".dokuwiki:first");
131
132        pycode_wiz.textArea = $editor[0];
133
134        // start to attach event handler
135
136        // the following is the table of all possibiles combinations,
137        // depending on flag, file extension and options
138        //
139        //         |        .py        ||        .*         |
140        //         --------------------||--------------------
141        //         |   | l | f |f+c| c ||   | l | f |f+c| c |
142        // ----------------------------||--------------------
143        // nums    | V | V | V | V | X || V | V | X | X | X |
144        // ----------------------------||--------------------
145        // title   | V | V | V | V | X || V | V | X | X | X |
146        // ----------------------------||--------------------
147        // docstr  | X | X | X | X | V || X | X | X | X | X |
148        // ----------------------------||--------------------
149
150        // handle buttons for the repository
151        jQuery("#pycode__b").click(function() {
152            pycode_wiz.reset(".pycode__repo-src-url");
153            jQuery("#pycode__b").fadeTo(0, 1);
154            jQuery("#pycode__g").fadeTo(0, 0.5);
155            if (jQuery("#pycode__src-url").prop("disabled") == true) {
156                jQuery("#pycode__src-url").prop("disabled", false);
157            }
158            jQuery("#pycode__src-url")
159            .val("https://bitbucket.org/<user>/<repo>/src/<branch>/<file>")
160            .click(function() {pycode_wiz.reset(".pycode__repo-src-url");});
161        });
162        jQuery("#pycode__g").click(function() {
163            pycode_wiz.reset(".pycode__repo-src-url");
164            jQuery("#pycode__g").fadeTo(0, 1);
165            jQuery("#pycode__b").fadeTo(0, 0.5);
166            if (jQuery('#pycode__src-url').prop("disabled") == true) {
167                jQuery('#pycode__src-url').prop("disabled", false);
168            }
169            jQuery("#pycode__src-url")
170            .val("https://github.com/<user>/<repo>/blob/<branch>/<file>")
171            .click(function() {pycode_wiz.reset(".pycode__repo-src-url");});
172        });
173
174        // handle checkbox for embed code between two line numbers
175        jQuery("#pycode__opt-lns").change(function() {
176            if (jQuery("#pycode__opt-lns").prop("checked") == true) {
177                jQuery(".pycode__embed-lns input:text").prop("disabled", false);
178                pycode_wiz.disable(".pycode__embed-def");
179                pycode_wiz.disable(".pycode__embed-cls");
180                pycode_wiz.disable(".pycode__code-docstr");
181                pycode_wiz.reset(".pycode__embed-def");
182                pycode_wiz.reset(".pycode__embed-cls");
183                pycode_wiz.reset(".pycode__code-docstr");
184            }
185            else {
186                pycode_wiz.disable(".pycode__embed-lns");
187            }
188        });
189
190        // handle checkbox for embed code from function name
191        jQuery("#pycode__opt-def").change(function() {
192            if (jQuery("#pycode__opt-def").prop("checked") == true) {
193                jQuery("#pycode__def")
194                .prop("disabled", false)
195                .click(function() {pycode_wiz.reset(".pycode__embed-def");});
196                pycode_wiz.disable(".pycode__embed-lns");
197                pycode_wiz.disable(".pycode__code-docstr");
198                pycode_wiz.reset(".pycode__code-docstr");
199            }
200            else {
201                pycode_wiz.disable(".pycode__embed-def");
202                pycode_wiz.reset(".pycode__embed-def");
203                if (jQuery("#pycode__opt-cls").prop("checked") == true) {
204                    pycode_wiz.disable(".pycode__code-nums");
205                    pycode_wiz.disable(".pycode__code-title");
206                    pycode_wiz.reset(".pycode__code-nums");
207                }
208            }
209        });
210
211        // handle checkbox for embed code from class name
212        jQuery("#pycode__opt-cls").change(function() {
213            if (jQuery("#pycode__opt-cls").prop("checked") == true) {
214                jQuery("#pycode__cls")
215                .prop("disabled", false)
216                .click(function() {pycode_wiz.reset(".pycode__embed-cls");});
217                pycode_wiz.disable(".pycode__embed-lns");
218                if (jQuery("#pycode__opt-def").prop("checked") == false) {
219                    pycode_wiz.disable(".pycode__code-nums");
220                    pycode_wiz.disable(".pycode__code-title");
221                    pycode_wiz.reset(".pycode__code-nums");
222                }
223            }
224            else {
225                pycode_wiz.disable(".pycode__embed-cls");
226                pycode_wiz.disable(".pycode__code-docstr");
227                pycode_wiz.reset(".pycode__embed-cls");
228                pycode_wiz.reset(".pycode__code-docstr");
229            }
230        });
231
232        // handle checkbox for show/hide line numbers
233        jQuery("#pycode__opt-nums").change(function() {
234            if (jQuery("#pycode__opt-nums").prop("checked") == true) {
235                jQuery(".pycode__code-nums input:radio")
236                .prop("disabled", false)
237                .click(function() {pycode_wiz.reset('.pycode__code-nums');});
238                if (jQuery("#pycode__opt-def").prop("checked") == false &&
239                    jQuery("#pycode__opt-cls").prop("checked") == true) {
240                    pycode_wiz.disable(".pycode__embed-cls");
241                    pycode_wiz.disable(".pycode__code-docstr");
242                    pycode_wiz.reset(".pycode__embed-cls");
243                    pycode_wiz.reset(".pycode__code-docstr");
244                }
245            }
246            else {
247                pycode_wiz.disable(".pycode__code-nums");
248                pycode_wiz.reset(".pycode__code-nums");
249            }
250        });
251
252        // handle checkbox for define new title for the code
253        jQuery("#pycode__opt-title").change(function() {
254            if (jQuery("#pycode__opt-title").prop("checked") == true) {
255                jQuery("#pycode__title").prop("disabled", false);
256                if (jQuery("#pycode__opt-def").prop("checked") == false &&
257                    jQuery("#pycode__opt-cls").prop("checked") == true) {
258                    pycode_wiz.disable(".pycode__embed-cls");
259                    pycode_wiz.disable(".pycode__code-docstr");
260                    pycode_wiz.reset(".pycode__embed-cls");
261                    pycode_wiz.reset(".pycode__code-docstr");
262                }
263
264            }
265            else {
266                pycode_wiz.disable(".pycode__code-title");
267            }
268        });
269
270        // handle checkbox for show/hide docstring
271        jQuery("#pycode__opt-docstr").change(function() {
272            if (jQuery("#pycode__opt-docstr").prop("checked") == true) {
273                jQuery(".pycode__code-docstr input:radio")
274                .prop("disabled", false)
275                .click(function() {pycode_wiz.reset('.pycode__code-docstr');});
276                pycode_wiz.disable(".pycode__embed-lns");
277                pycode_wiz.disable(".pycode__embed-def");
278                pycode_wiz.disable(".pycode__code-nums");
279                pycode_wiz.disable(".pycode__code-title");
280                pycode_wiz.reset(".pycode__embed-def");
281                pycode_wiz.reset(".pycode__code-nums");
282            }
283            else {
284                pycode_wiz.disable(".pycode__code-docstr");
285                pycode_wiz.reset(".pycode__code-docstr");
286            }
287        });
288
289        // handle button for insert the syntax
290        jQuery("#pycode__insert").click(pycode_wiz.insertPycode);
291
292        // handle button for close the wizard
293        jQuery("#pycode__wiz").find(".ui-dialog-titlebar-close").click(pycode_wiz.hide);
294    },
295
296    /**
297     * Disable input objects belong to a given div.
298     *
299     * @param (obj) obj class name of a div
300     */
301    disable: function(obj) {
302        jQuery(obj+" input:checkbox").prop("checked", false);
303        jQuery(obj+" input:radio").prop("disabled", true);
304        jQuery(obj+" input:text").prop("disabled", true);
305    },
306
307    /**
308     * Set css rules defined for required fields missing.
309     *
310     * @param (obj) obj class name of a div
311     */
312    require: function(obj) {
313        jQuery(obj+" input:radio").css("outline", "2px solid #FCC");
314        jQuery(obj+" input:text").css("background-color", "#FCC");
315    },
316
317    /**
318     * Reset css rules previously defined for required fields missing.
319     *
320     * @param (obj) obj class name of a div
321     */
322    reset: function(obj) {
323        jQuery(obj+" input:radio").css("outline", "");
324        jQuery(obj+" input:text").css("background-color", "");
325    },
326
327    /**
328     * Get the extension of a given src-url.
329     *
330     * @param (str) src_url the url to the source code
331     */
332    getExtension: function(src_url) {
333        var ext = src_url.slice(-3);  // get .py
334        if (ext == ".py") {
335            return "py";
336        }
337        else {
338            return "no-py";
339        }
340    },
341
342    /**
343     * Insert the syntax into the textarea,
344     * replacing the current selection or at the cursor position.
345     */
346    insertPycode: function() {
347        var data = {
348            "src_url": null,
349            "flag": null,
350            "name": null,
351            "nums": null,
352            "title": null,
353            "docstr": null
354        };
355        var error = new Array;
356
357        // get the values from the fields
358        // and check if some data has been missed
359
360        // get value from src-url field
361        if (jQuery("#pycode__src-url").val() == "") {
362            error.push("no-src-url");
363        }
364        else {
365            data["src_url"] = jQuery("#pycode__src-url").val();
366            if (pycode_wiz.getExtension(data["src_url"]) == "no-py" &&
367                (jQuery("#pycode__opt-def").prop("checked") == true ||
368                jQuery("#pycode__opt-cls").prop("checked") == true)) {
369                error.push("no-py");
370            }
371        }
372
373        // get value from name lines field
374        if (jQuery("#pycode__opt-lns").prop("checked") == true) {
375            data["flag"] = "l";
376            data["name"] = jQuery("#pycode__lns-from").val()+":"+jQuery("#pycode__lns-to").val();
377        }
378        // get value from name function field
379        else if ((jQuery("#pycode__opt-def").prop("checked") == true) &&
380                 (jQuery("#pycode__opt-cls").prop("checked") == false)) {
381            data["flag"] = "f";
382            if (jQuery("#pycode__def").val() == "") {
383                error.push("no-def");
384            }
385            else {
386                data["name"] = jQuery("#pycode__def").val();
387            }
388        }
389        // get value from names method and class fields
390        else if ((jQuery("#pycode__opt-def").prop("checked") == true) &&
391                 (jQuery("#pycode__opt-cls").prop("checked") == true)) {
392            data["flag"] = "f";
393            if (jQuery("#pycode__def").val() == "" &&
394                jQuery("#pycode__cls").val() == "") {
395                error.push("no-def-cls");
396            }
397            else if (jQuery("#pycode__def").val() != "" &&
398                     jQuery("#pycode__cls").val() == "") {
399                error.push("no-cls");
400            }
401            else if (jQuery("#pycode__def").val() == "" &&
402                     jQuery("#pycode__cls").val() != "") {
403                error.push("no-def");
404            }
405            else {
406                data["name"] = jQuery("#pycode__def").val()+" "+jQuery("#pycode__cls").val();
407            }
408        }
409        // get value from name class field
410        else if ((jQuery("#pycode__opt-def").prop("checked") == false) &&
411                 (jQuery("#pycode__opt-cls").prop("checked") == true)) {
412            data["flag"] = "c";
413            if (jQuery("#pycode__cls").val() != "") {
414                data["name"] = jQuery("#pycode__cls").val();
415            }
416            else {
417                error.push("no-cls");
418            }
419        }
420        // embed the whole file
421        else {
422            data["flag"] = "";
423        }
424
425
426        // get value from line numbers option
427        if (jQuery("#pycode__opt-nums").prop("checked") == true) {
428            if (jQuery(".pycode__code-nums input:radio:checked").val() == "show") {
429                data["nums"] = "-nums = 1";
430            }
431            else if (jQuery(".pycode__code-nums input:radio:checked").val() == "hide") {
432                data["nums"] = "-nums = 0";
433            }
434            else {
435                error.push("no-nums");
436            }
437        }
438
439        // get value from title option
440        if (jQuery("#pycode__opt-title").prop("checked") == true) {
441            title = jQuery("#pycode__title").val();
442            data["title"] = '-title = "'+title+'"';
443        }
444
445        // get value from docstring option
446        if (jQuery("#pycode__opt-docstr").prop("checked") == true &&
447            jQuery("#pycode__opt-cls").prop("checked") == true) {
448            if (jQuery(".pycode__code-docstr input:radio:checked").val() == "show") {
449                data["docstr"] = "-docstr = 1";
450            }
451            else if (jQuery(".pycode__code-docstr input:radio:checked").val() == "hide") {
452                data["docstr"] = "-docstr = 0";
453            }
454            else {
455                error.push("no-docstr");
456            }
457        }
458        else if (jQuery("#pycode__opt-docstr").prop("checked") == true &&
459                 jQuery("#pycode__opt-cls").prop("checked") == false) {
460            error.push("no-cls");
461        }
462
463        if (error.length == 0) {
464            // depending on the choices made, print the right syntax
465            var syntax = "<pycode";
466            for (var k in data) {
467                if (data[k] != null) {
468                    if (data[k] == "") {
469                        syntax += data[k];
470                    }
471                    else {
472                        syntax += " " + data[k];
473                    }
474                }
475            }
476            syntax += ">";
477            // get current selection/cursor position in a given textArea
478            // with DWgetSelection() defined in lib/scripts/textselection.js
479            sel = DWgetSelection(pycode_wiz.textArea);
480            // insert/replace given text at the current cursor position
481            // with pasteText() defined in lib/scripts/textselection.js
482            pasteText(sel, syntax, 0);
483            // finally close the wizard
484            pycode_wiz.hide();
485        }
486        else {
487            // highlight missing fields
488            error.forEach(function(item, index, array) {
489                if (item == "no-src-url") {
490                    pycode_wiz.require(".pycode__repo-src-url");
491                }
492                else if (item == "no-py") {
493                    pycode_wiz.disable(".pycode__embed-def");
494                    pycode_wiz.disable(".pycode__embed-cls");
495                    pycode_wiz.reset(".pycode__embed-def");
496                    pycode_wiz.reset(".pycode__embed-cls");
497                }
498                else if (item == "no-def") {
499                    pycode_wiz.require(".pycode__embed-def");
500                }
501                else if (item == "no-def-cls") {
502                    pycode_wiz.require(".pycode__embed-def");
503                    pycode_wiz.require(".pycode__embed-cls");
504                }
505                else if (item == "no-cls") {
506                    pycode_wiz.require(".pycode__embed-cls");
507                }
508                else if (item == "no-nums") {
509                    pycode_wiz.require(".pycode__code-nums");
510                }
511                else if (item == "no-docstr") {
512                    pycode_wiz.require(".pycode__code-docstr");
513                }
514            })
515        }
516    },
517
518    /**
519     * Show the pycode wizard
520     */
521    show: function() {
522        pycode_wiz.selection = DWgetSelection(pycode_wiz.textArea);
523        pycode_wiz.$wiz.show();
524    },
525
526    /**
527     * Hide the pycode wizard
528     */
529    hide: function() {
530        pycode_wiz.$wiz.hide();
531        pycode_wiz.textArea.focus();
532    },
533
534    /**
535     * Toggle the pycode wizard
536     */
537    toggle: function() {
538        if (pycode_wiz.$wiz.css("display") == "none") {
539            pycode_wiz.show();
540        }
541        else {
542            pycode_wiz.hide();
543        }
544    }
545};
546
547/**
548 * It adds button action for the toolbar button
549 *
550 * @param (obj) $btn jQuery button element to add the action to.
551 * @param (arr) props Associative array of button properties.
552 * @param (str) edid  ID of the editor textarea.
553 * @return (str) If button should be appended, return the id for in
554 *               aria-controls, otherwise an empty string.
555 */
556function addBtnActionPyCode($btn, props, edid) {
557    pycode_wiz.init(jQuery('#' + edid));
558    $btn.click(function () {
559        pycode_wiz.toggle();
560        return false;
561    });
562    return true;
563}
564