1jQuery(function () {
2    /**
3     * Add tag search parameter to all links in the advanced search tools
4     *
5     * This duplicates the solution from the watchcycle plugin, and should also be replaced
6     * with a DokuWiki event, which does not exist yet, but should handle extending search tools.
7     */
8    const $advancedOptions = jQuery('.search-results-form .advancedOptions');
9    if (!$advancedOptions.length) {
10        return;
11    }
12
13    /**
14     * Extracts the value of a given parameter from the URL querystring
15     *
16     * taken via watchcycle from https://stackoverflow.com/a/31412050/3293343
17     * @param param
18     * @returns {*}
19     */
20    function getQueryParam(param) {
21        location.search.substr(1)
22            .split("&")
23            .some(function(item) { // returns first occurence and stops
24                return item.split("=")[0] === param && (param = item.split("=")[1])
25            });
26        return param
27    }
28
29    if (getQueryParam('tagging-logic') === 'and') {
30        $advancedOptions.find('a').each(function (index, element) {
31            const $link = jQuery(element);
32            // do not override parameters in our own links
33            if ($link.attr('href').indexOf('tagging-logic') === -1) {
34                $link.attr('href', $link.attr('href') + '&tagging-logic=and');
35            }
36        });
37    }
38
39
40    /* **************************************************************************
41     * Search filter
42     * ************************************************************************ */
43
44    const $filterContainer = jQuery('#plugin__tagging-tags');
45    const $resultLinks = jQuery('div.search_fullpage_result dt a:not([class])');
46
47    /**
48     * Returns the filter ul
49     *
50     * @param {*} tags
51     * @param {string[]} filters
52     * @returns {jQuery}
53     */
54    function buildFilter(tags, filters) {
55        const lis = [];
56        let i = 0;
57
58        // when tag search has no results, build the filter dropdown anyway but from tags in query
59        if (Object.keys(tags).length === 0 && filters.length > 0) {
60            for (const key of filters) {
61                tags[key] = 0;
62            }
63        }
64
65        for (const tag in tags) {
66            let checked = filters.includes(tag) ? 'checked="checked"' : '';
67
68            lis.push(` <li>
69                <input name="tagging[]" type="checkbox" value="${tag}" id="__tagging-${i}" ${checked}>
70                <label for="__tagging-${i}" title="${tag}">
71                    ${tag} (${tags[tag]})
72                </label>
73            </li>`);
74            i++;
75        }
76
77        if (lis.length) {
78            $filterContainer.find('div.current').addClass('changed');
79        } else {
80            lis.push(`<li>${LANG.plugins.tagging.search_nofilter}</li>`);
81        }
82
83        return jQuery('<ul aria-expanded="false">' + lis.join('') + '</ul>');
84    }
85
86    /**
87     * Collects tags from results list
88     *
89     * @returns {*}
90     */
91    function getTagsFromResults() {
92        const tags = [];
93        $resultLinks.toArray().forEach(function(link) {
94            const text = jQuery(link).text();
95            if (text.charAt(0) === '#') {
96                const tag = text.replace('#', '');
97                tags.push(tag);
98            }
99        });
100
101        return tags.sort().reduce(function (allTags, tag) {
102            if (tag in allTags) {
103                allTags[tag]++;
104            }
105            else {
106                allTags[tag] = 1;
107            }
108            return allTags;
109        }, {});
110    }
111
112    /**
113     * Returns query from the main search form, ignoring quicksearch.
114     *
115     * @returns {jQuery}
116     */
117    function getQueryElement() {
118        return jQuery('#dokuwiki__content input[name="q"]');
119    }
120
121    /**
122     * Returns an array of all tags found in search form input
123     *
124     * @returns {string[]}
125     */
126    function getFiltersFromQuery() {
127        const parts = getQueryElement().val().split(' ');
128        let filters = parts.filter(function (part) {
129            return part.charAt(0) === '#';
130        });
131
132        return filters.map(function (tag) {
133            return tag.replace('#', '');
134        });
135    }
136
137    /**
138     * Called when a tag filter is updated. Manipulates query by adding or removing the selected tag.
139     *
140     * @param {string} tag
141     */
142    function toggleTag(tag) {
143        tag = '#' + tag;
144        const $q = getQueryElement();
145        const q = $q.val();
146        const isFilter = q.indexOf(tag) > -1;
147
148        if (isFilter) {
149            $q.val(q.replace(tag, ''));
150        } else {
151            $q.val(q.trim() + ' ' + tag);
152        }
153    }
154
155    /**
156     * Restore tags in search links
157     *
158     * @param {jQuery} $searchLinks
159     */
160    function addTagsToSearchLinks($searchLinks) {
161        const tags = getFiltersFromQuery();
162        if (tags.length === 0) {
163            return;
164        }
165
166        $searchLinks.each(function () {
167            $link = jQuery(this);
168            const qParam = $link[0]['href'].match(/q=[^&]*/)[0];
169            $link[0]['href'] = $link[0]['href'].replace(qParam, qParam + encodeURIComponent(' #' + tags.join(' #')));
170        });
171    }
172
173    // tag filter
174    let $ul;
175    $ul = $filterContainer.find('ul');
176    if ($ul.length === 0) {
177        $ul = buildFilter(getTagsFromResults(), getFiltersFromQuery());
178        $filterContainer.append($ul);
179    }
180
181    $inputs = $ul.find('input');
182    $inputs.change(function () {
183        toggleTag(this.value);
184    });
185
186    // tags in other search filters
187    addTagsToSearchLinks(jQuery('.advancedOptions a'));
188
189});
190