1/* DOKUWIKI:include scripts/tagselector.js */
2
3jQuery(function(){
4    // TODO: make this configurable by the admin
5    var currentInterval = 1000; // Starting interval in milliseconds
6    var maxInterval = 10000; // Maximum interval is 10 seconds
7
8    const BuildmanagerStates = {
9        STATE_NON_EXISTENT: 1,
10        STATE_RUNNING: 2,
11        STATE_SCHEDULED: 3,
12        STATE_FINISHED: 4,
13        STATE_ERROR: 5,
14    };
15
16    var isLoadingSnippet = false;
17
18    /**
19     * Scan the document for doxycode markers that represent dynamically loaded code snippets.
20     *
21     * The doxycode_markers contain custom data fields that represent the cache files.
22     * We extract all markers so the state of the associated doxygen builds can be obtained in a single request.
23     *
24     * When the code snippets are successfully loaded the markers are removed later. If no more markers
25     * are present we return an empty array which indicates that dynamic loading is finished for this page.
26     *
27     * @returns {Array} contains the xml and html hash names that represent the doxycode cache files
28     * @author Lukas Probsthain <lukas.probsthain@gmail.com>
29     */
30    function scanAndPrepareData() {
31        var dataToSend = [];
32
33        jQuery('.doxycode_marker').each(function() {
34            var xmlHash = jQuery(this).data('doxycode-xml-hash');
35            var htmlHash = jQuery(this).data('doxycode-html-hash');
36            if (xmlHash || htmlHash) {
37                dataToSend.push({xmlHash: xmlHash, htmlHash: htmlHash});
38            }
39        });
40        return dataToSend;
41    }
42
43    /**
44     * Handles the state response for the dynamically loaded code snippets.
45     *
46     * This is called from requestJobStatus.
47     * The function will place an appropriate state message obtained from the
48     * global LANG variable inside the doxycode_marker container.
49     *
50     * If a successful build was reported for a dynamically loaded code snippet
51     * the function will request the parsed code snippet HTML from the server via AJAX.
52     *
53     * @param {Array} response Each entry contains the xml and html hash from the request with the reported state
54     * @author Lukas Probsthain <lukas.probsthain@gmail.com>
55     */
56    function handleStatusResponse(response) {
57        response.forEach(function(hashInfo) {
58            $markers = jQuery('.doxycode_marker').filter(function() {
59                return jQuery(this).data('doxycode-xml-hash') === hashInfo.xmlHash &&
60                       jQuery(this).data('doxycode-html-hash') === hashInfo.htmlHash;
61            });
62
63            var $loadingAnimation;
64            if(parseInt(hashInfo.state) != BuildmanagerStates.STATE_FINISHED) {
65                $loadingAnimation = jQuery('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />');
66            }
67
68            $markers.each(function() {
69                var $currentMarker = jQuery(this);
70                $currentMarker.empty();
71
72                var message;
73                switch(parseInt(hashInfo.state)) {
74                    case BuildmanagerStates.STATE_NON_EXISTENT:
75                        message = LANG.plugins.doxycode.msg_not_existent;
76                        break;
77                    case BuildmanagerStates.STATE_SCHEDULED:
78                        message = LANG.plugins.doxycode.msg_scheduled;
79                        break;
80                    case BuildmanagerStates.STATE_RUNNING:
81                        message = LANG.plugins.doxycode.msg_running;
82                        break;
83                    case BuildmanagerStates.STATE_ERROR:
84                        message = LANG.plugins.doxycode.msg_error;
85                        break;
86                    case BuildmanagerStates.STATE_FINISHED:
87                        loadSnippet({
88                            xmlHash: hashInfo.xmlHash,
89                            htmlHash: hashInfo.htmlHash
90                        });
91                        return; // Skip appending for finished state
92                }
93
94                if ($loadingAnimation) {
95                    $currentMarker.append($loadingAnimation.clone());
96                }
97
98                if (message) {
99                    $currentMarker.append(message);
100                }
101            });
102        });
103    }
104
105    /**
106     * Handles the insertion of dynamically loaded code snippet HTML into the code container.
107     *
108     * @param {Array} response Contains the xml and html hashes along with the parsed code snippet HTML
109     * @author Lukas Probsthain <lukas.probsthain@gmail.com>
110     */
111    function loadSnippetHtml(response) {
112
113        $markers = jQuery('.doxycode_marker').filter(function() {
114            return jQuery(this).data('doxycode-xml-hash') === response.hashes.xmlHash &&
115                   jQuery(this).data('doxycode-html-hash') === response.hashes.htmlHash;
116        });
117
118        $markers.each(function() {
119            // dynamically load the HTML for this snippet!
120            var parent_element = jQuery(this).parent();
121            jQuery(this).remove();
122            jQuery(parent_element).append(response.html);
123        });
124    }
125
126    /**
127     * Function to request the build status of dynamically loaded code snippets via AJAX.
128     *
129     * It obtains the xml and html hashes (which represent the cache files) of the code snippets with scanAndPrepareData and sends a request
130     * for all dynamically loaded code snippets to plugin_doxycode_check_status.
131     *
132     * If dynamically loaded code snippets are present in the page it will call itself again.
133     * The time period after which the request is repeated increases with each request to reduce load on the server.
134     *
135     * @author Lukas Probsthain <lukas.probsthain@gmail.com>
136     */
137    function requestJobStatus() {
138        var data = scanAndPrepareData();
139        if (data.length === 0) {
140            return;
141        }
142
143        jQuery.post(
144            DOKU_BASE + 'lib/exe/ajax.php',
145            {
146                call: 'plugin_doxycode_check_status',
147                hashes: data
148            },
149            function(response) {
150                handleStatusResponse(response);
151                currentInterval = Math.min(currentInterval + 100, maxInterval);
152                setTimeout(requestJobStatus, currentInterval);
153            },
154            'json'
155        ).fail(function(jqXHR, textStatus, errorThrown) {
156            console.error("AJAX error:", textStatus, errorThrown);
157            currentInterval = Math.min(currentInterval + 1000, maxInterval);
158            setTimeout(requestJobStatus, currentInterval);
159        });
160    }
161
162    /**
163     * Request the parsed code snippet HTML from the server via AJAX.
164     *
165     * @param {Array} data Contains the xml and html hashes for the code snippet cache files
166     * @author Lukas Probsthain <lukas.probsthain@gmail.com>
167     */
168    function loadSnippet(data) {
169        if (isLoadingSnippet) {
170            return;
171        }
172
173        isLoadingSnippet = true;
174
175        jQuery.post(
176            DOKU_BASE + 'lib/exe/ajax.php',
177            {
178                call: 'plugin_doxycode_get_snippet_html',
179                hashes: data
180            },
181            function(response) {
182                isLoadingSnippet = false;
183                loadSnippetHtml(response);
184            },
185            'json'
186        ).fail(function(jqXHR, textStatus, errorThrown) {
187            console.error("AJAX error:", textStatus, errorThrown);
188            isLoadingSnippet = false;
189        });
190    }
191
192    requestJobStatus();
193});
194
195/**
196 * Add button action for the doxycode tag selector button
197 *
198 * Adapted from the linkwiz dialog by Andreas Gohr (see {@link https://github.com/dokuwiki/dokuwiki/blob/8985cadc85d51290e4908456c2afc923fd0f0332/lib/scripts/toolbar.js#L221-L230}).
199 *
200 * @param  DOMElement btn   Button element to add the action to
201 * @param  array      props Associative array of button properties
202 * @param  string     edid  ID of the editor textarea
203 * @return boolean    If button should be appended
204 * @author Lukas Probsthain <lukas.probsthain@gmail.com>
205 */
206function addBtnActionDoxycodeTagSelector($btn, props, edid) {
207    doxycode_tagselector.init(jQuery('#'+edid));
208    jQuery($btn).click(function(e){
209        doxycode_tagselector.val = props;
210        doxycode_tagselector.toggle();
211        e.preventDefault();
212        return '';
213    });
214    return 'doxycode__tagselector';
215}