1/* Catalog filters â select-all for every filter group. 2 * 3 * Ticking a whole group one box at a time is slow and fires one results request per click. This 4 * adds a control checkbox per group that applies the whole group in a single pass, then issues 5 * exactly one request. Groups opt in with data-filter-group (a unique key), data-group-name and 6 * data-all-label on their .choicelist. 7 * 8 * When a group is fully selected its individual tags collapse into one group tag: selecting all of 9 * פ×ת×× ×××©× ×××××× ×××ת would otherwise push 16 tags into the active-filter row at once. 10 * 11 * Depends on common.js for filterCheckboxTag, countAllClickedChecboxes, updateWindowParams and 12 * filterResultsAjax; this file must load after it. 13 */ 14(function () { 15 'use strict'; 16 17 var GROUP = '[data-filter-group]'; 18 19 function groups() { 20 return Array.prototype.slice.call(document.querySelectorAll(GROUP)); 21 } 22 23 function boxesIn(group) { 24 return Array.prototype.slice.call(group.querySelectorAll('input.inputbox')); 25 } 26 27 function checkedIn(group) { 28 return boxesIn(group).filter(function (b) { return b.checked; }); 29 } 30 31 function groupTagId(group) { 32 return 'fgroup' + group.getAttribute('data-filter-group') + 'x'; 33 } 34 35 /** Per-group count shown on the collapsed group button. */ 36 function syncCount(group) { 37 var boxes = boxesIn(group); 38 if (!boxes.length) return; 39 var target = document.getElementById(boxes[0].getAttribute('data-count')); 40 if (!target) return; 41 var n = checkedIn(group).length; 42 target.innerHTML = n === 0 ? '' : String(n); 43 } 44 45 /** Control checkbox reflects the group: checked when all, indeterminate when some. */ 46 function syncControl(group) { 47 var control = group.querySelector('input.inputboxAll'); 48 if (!control) return; 49 var boxes = boxesIn(group), checked = checkedIn(group).length; 50 control.checked = boxes.length > 0 && checked === boxes.length; 51 control.indeterminate = checked > 0 && checked < boxes.length; 52 } 53 54 /** 55 * Rebuild this group's tags. Rebuilt rather than patched because the two shapes are mutually 56 * exclusive â one group tag, or one tag per term â and a partial update would leave both. 57 */ 58 function syncTags(group) { 59 var list = document.getElementById('catalogSelectedFiltersItems'); 60 if (!list) return; 61 62 var existing = document.getElementById(groupTagId(group)); 63 if (existing) existing.parentNode.removeChild(existing); 64 65 var boxes = boxesIn(group), checked = checkedIn(group); 66 var whole = boxes.length > 0 && checked.length === boxes.length; 67 68 boxes.forEach(function (box) { 69 var li = document.getElementById(box.id + 'x'); 70 // A term's own tag is wrong while the whole group is represented by one tag. 71 if (whole && li) li.parentNode.removeChild(li); 72 if (!whole && typeof window.filterCheckboxTag === 'function') window.filterCheckboxTag(box); 73 }); 74 75 if (!whole) return; 76 77 var li = document.createElement('li'); 78 li.id = groupTagId(group); 79 li.appendChild(document.createTextNode( 80 group.getAttribute('data-group-name') + ': ' + group.getAttribute('data-all-label'))); 81 var remove = document.createElement('a'); 82 remove.href = 'javascript:;'; 83 remove.textContent = 'x'; 84 remove.addEventListener('click', function () { applyGroup(group, false, true); }); 85 li.appendChild(remove); 86 list.appendChild(li); 87 } 88 89 function syncGroup(group) { 90 syncCount(group); 91 syncControl(group); 92 syncTags(group); 93 } 94 95 /** Apply a state to every term in the group, then refresh results once. */ 96 function applyGroup(group, state, refresh) { 97 boxesIn(group).forEach(function (box) { box.checked = state; }); 98 syncGroup(group); 99 100 if (typeof window.countAllClickedChecboxes === 'function') window.countAllClickedChecboxes(); 101 if (typeof window.updateWindowParams === 'function') window.updateWindowParams(); 102 if (refresh && typeof window.filterResultsAjax === 'function') window.filterResultsAjax(); 103 } 104 105 function init() { 106 var all = groups(); 107 if (!all.length) return; 108 109 wrapLegacyTagRebuild(); 110 111 all.forEach(function (group) { 112 var control = group.querySelector('input.inputboxAll'); 113 if (control) { 114 control.addEventListener('change', function () { applyGroup(group, this.checked, true); }); 115 } 116 // Runs after common.js's own click handler, so the per-term tag it just added is already in 117 // place and this only has to reconcile the group-level view.
118 boxesIn(group).forEach(function (box) { 119 box.addEventListener('click', function () { syncGroup(group); }); 120 }); 121 syncGroup(group); 122 }); 123 124 // Reset clears .inputbox but knows nothing about the control boxes or the group tags. 125 var reset = document.getElementById('resetFilters'); 126 if (reset) { 127 reset.addEventListener('click', function () { 128 all.forEach(function (group) { 129 var control = group.querySelector('input.inputboxAll'); 130 if (control) { control.checked = false; control.indeterminate = false; } 131 var tag = document.getElementById(groupTagId(group)); 132 if (tag) tag.parentNode.removeChild(tag); 133 }); 134 }); 135 } 136 } 137 138 /** Reconcile every group's tags and controls. */ 139 function syncAll() { 140 groups().forEach(syncGroup); 141 } 142 143 /** 144 * common.js rebuilds the whole tag list from the raw checkboxes in updateFilterTags(), which the 145 * sort handler calls on every sort-driven reload. It knows nothing about groups, so it re-adds a 146 * tag per checked term â leaving a group tag and all its term tags showing together, the two 147 * shapes syncTags() treats as mutually exclusive. 148 * 149 * Wrapped here rather than edited there: the group concept belongs to this module, and common.js 150 * is a shared monolith this fix has no reason to touch. The original still runs first; this only 151 * collapses the per-term tags a fully selected group should not have. 152 */ 153 function wrapLegacyTagRebuild() { 154 var original = window.updateFilterTags; 155 if (typeof original !== 'function' || original.cilGroupAware) return; 156 157 var wrapped = function () { 158 var result = original.apply(this, arguments); 159 syncAll(); 160 return result; 161 }; 162 wrapped.cilGroupAware = true; // guards against double-wrapping on a re-init 163 window.updateFilterTags = wrapped; 164 } 165 166 if (document.readyState === 'loading') { 167 document.addEventListener('DOMContentLoaded', init); 168 } else { 169 init(); 170 } 171})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.