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