PageSourceSearch

https://erdingtonelim.org/wp-content/plugins/gatherova/js/dist/v2_shell.js?ver=1790807369

js erdingtonelim.org collected 2026-10-02 02:04:13 UTC 29,754 bytes, 485 lines download raw bytes

1(function () {
2    var root = document.documentElement;
3    if (window.gwp_design3) root.classList.add('gwp_v2', 'gwp_v3');
4    if (!root.classList.contains('gwp_v2')) return;
5
6    var GNAME = { content: 'Content', work: 'Work', comms: 'Communication', people: 'People', live: 'Live', family: 'Family' };
7
8    function el(tag, cls, html) {
9        var e = document.createElement(tag);
10        if (cls) e.className = cls;
11        if (html) e.innerHTML = html;
12        return e;
13    }
14
15    /* Decorate the existing hamburger menu (#gwp_chooseapp): brand header + group headings. Same items, same behaviour, same full-text labels. */
16    function decorateMenu() {
17        var menu = document.getElementById('gwp_chooseapp');
18        var box = document.getElementById('gwp_accontent');
19        if (!menu || !box) return false;
20        if (!menu.querySelector('.v2m_brand')) {
21            var brand = el('div', 'v2m_brand');
22            var mark = el('span', 'v2m_mark');
23            if (typeof gwp_pluginurl !== 'undefined') mark.style.setProperty('--v2-logo', 'url("' + gwp_pluginurl + 'images/gatherova-icon.png")');
24            brand.appendChild(mark);
25            brand.appendChild(el('span', 'v2m_word', 'Gatherova'));
26            menu.insertBefore(brand, menu.firstChild);
27        }
28        var links = box.querySelectorAll(':scope > a');
29        if (!links.length) return false;
30        if (box.getAttribute('data-v2n') === String(links.length) && box.querySelector(':scope > .v2m_grp')) return true;
31        box.setAttribute('data-v2n', String(links.length));
32        Array.prototype.forEach.call(box.querySelectorAll(':scope > .v2m_grp'), function (n) { n.parentNode.removeChild(n); });
33        var last = null;
34        Array.prototype.forEach.call(links, function (a) {
35            var g = a.getAttribute('data-tool') || '';
36            if (g && g !== last && g !== 'home' && GNAME[g]) {
37                var h = el('div', 'v2m_grp');
38                h.setAttribute('data-tool', g);
39                h.innerHTML = '<i></i><span></span>';
40                h.lastChild.textContent = GNAME[g];
41                box.insertBefore(h, a);
42            }
43            if (g) last = g;
44        });
45        return true;
46    }
47
48    /* Remember how far the hamburger menu was scrolled, across opens and page loads. */
49    var menuScrollBound = false;
50    function bindMenuScroll() {
51        var box = document.getElementById('gwp_accontent');
52        var menu = document.getElementById('gwp_chooseapp');
53        if (!box || !menu || menuScrollBound) return;
54        menuScrollBound = true;
55        function restore() { var v = 0; try { v = parseInt(sessionStorage.getItem('gwp_menu_scroll') || '0', 10) || 0; } catch (e) {} if (v) box.scrollTop = v; }
56        box.addEventListener('scroll', function () { try { sessionStorage.setItem('gwp_menu_scroll', String(box.scrollTop)); } catch (e) {} });
57        if (window.MutationObserver) new MutationObserver(function () { if (menu.offsetParent !== null || getComputedStyle(menu).display !== 'none') setTimeout(restore, 30); }).observe(menu, { attributes: true, attributeFilter: ['class', 'style'] });
58        restore();
59    }
60
61    function currentPageLabel() {
62        var label = '';
63        try { (gwp_getNavItems().items || []).forEach(function (it) { if (it.active && !label) label = it.label; }); } catch (e) { return ''; }
64        var TL = { 'Store': 'Content Store', 'Teams & Groups': 'Teams & Groups', 'Taxonomies': 'Classifications', 'Expenses': 'Expenses', 'Compliance': 'Compliance', 'Workspace': 'Workspace', 'Tickets': 'Tickets', 'Blogs': 'Blog / News', 'Blog': 'Blog / News', 'Meetings': 'Meetings', 'Broadcasts': 'Broadcasts', 'Curriculum': 'Curriculum', 'Database': 'Database', 'Schedules': 'Schedules', 'Members': 'Members', 'Settings': 'Settings', 'KioskAdmin': 'Kiosk Mode', 'ParentPortal': 'Parent Portal', 'Labels': 'Labels' };
65        if (!label && typeof gwp_templatename !== 'undefined' && TL[gwp_templatename]) label = TL[gwp_templatename];
66        return label;
67    }
68
69    function buildGenericHead() {
70        var existing = document.getElementById('gwp_v2_head');
71        if (existing) {
72            /* The heading can be built before the page has set its final template name, so keep its page label current. */
73            if (existing.classList.contains('v2_generic') && typeof gwp_getNavItems === 'function' && !document.querySelector('#gwp_navigation .gwp_nav_pagetitle')) {
74                var lbl = currentPageLabel();
75                if (lbl && existing.getAttribute('data-page') !== lbl) existing.setAttribute('data-page', lbl);
76            }
77            return true;
78        }
79        var bar = document.getElementById('gwp_topbuttonbar');
80        if (!bar || !bar.parentNode || typeof gwp_getNavItems !== 'function') return false;
81        var anchor = bar;
82        if (bar.parentNode.id !== 'gwp_frame') {
83            if (typeof gwp_templatename !== 'undefined' && (gwp_templatename === 'Blogs' || gwp_templatename === 'Settings') && bar.parentNode.parentNode && bar.parentNode.parentNode.id === 'gwp_frame') anchor = bar.parentNode;
84            else return true;
85        }
86        if (typeof gwp_templatename !== 'undefined' && (gwp_templatename === 'Diary' || gwp_templatename === 'Messages')) return true;
87        var label = '';
88        try { (gwp_getNavItems().items || []).forEach(function (it) { if (it.active && !label) label = it.label; }); } catch (e) { return false; }
89        var TL = { 'Store': 'Content Store', 'Teams & Groups': 'Teams & Groups', 'Taxonomies': 'Classifications', 'Expenses': 'Expenses', 'Compliance': 'Compliance', 'Workspace': 'Workspace', 'Tickets': 'Tickets', 'Blogs': 'Blog / News', 'Blog': 'Blog / News', 'Meetings': 'Meetings', 'Broadcasts': 'Broadcasts', 'Curriculum': 'Curriculum', 'Database': 'Database', 'Schedules': 'Schedules', 'Members': 'Members', 'Settings': 'Settings', 'KioskAdmin': 'Kiosk Mode', 'ParentPortal': 'Parent Portal', 'Labels': 'Labels' };
90        if (!label && typeof gwp_templatename !== 'undefined' && TL[gwp_templatename]) label = TL[gwp_templatename];
91        if (!label) return true;
92        var h = el('div', 'v2_generic');
93        h.id = 'gwp_v2_head';
94        h.setAttribute('data-page', label);
95        var t = document.createElement('h1');
96        t.textContent = label;
97        h.appendChild(t);
98        anchor.parentNode.insertBefore(h, anchor.nextSibling);
99        if (getComputedStyle(bar).position === 'fixed') bar.parentNode.classList.add('v2_fixedbar');
100        return true;
101    }
102
103    /* Pages that put their own title in the bar (Meetings: "Meeting Minutes"): show it as the page heading instead, and hide it in the bar. */
104    function syncTitle() {
105        var head = document.getElementById('gwp_v2_head');
106        if (!head || !head.classList.contains('v2_generic')) return;
107        var h1 = head.querySelector('h1');
108        if (!h1) return;
109        var pt = document.querySelector('#gwp_navigation .gwp_nav_pagetitle');
110        if (pt) {
111            var txt = (pt.textContent || '').trim();
112            if (txt) { head.setAttribute('data-page', txt); if (h1.textContent !== txt) h1.textContent = txt; }
113        }
114        var page = head.getAttribute('data-page') || h1.textContent;
115        var sw = document.querySelector('#gwp_navigation .gwp_lib_type_dropdown .gwp_lib_type_current > span');
116        var want = page, sub = '';
117        if (sw) {
118            var t = (sw.textContent || '').trim();
119            if (t && t !== 'All' && t !== page) { want = t; sub = page; }
120        }
121        if (h1.textContent !== want) h1.textContent = want;
122        var p = head.querySelector('p');
123        if (sub) { if (!p) { p = document.createElement('p'); head.appendChild(p); } if (p.textContent !== sub) p.textContent = sub; }
124        else if (p) { head.removeChild(p); }
125    }
126    document.addEventListener('click', function () { setTimeout(syncTitle, 120); setTimeout(syncTitle, 500); });
127
128    /* Content Store: its tabs (Browse / My Imported / My Published) live in the page body. Mirror them in the top bar as the usual ‹ label › switcher and drive the same gwp_so_switch_tab(). */
129    function buildStoreSwitcher() {
130        var nav = document.getElementById('gwp_navigation');
131        if (!nav || document.getElementById('gwp_v2_so_dd') || !document.querySelector('.gwp_so_tab') || typeof gwp_so_switch_tab !== 'function') return;
132        var tabs = [['browse', 'Browse', 'la-search'], ['imported', 'My Imported', 'la-download'], ['published', 'My Published', 'la-upload']];
133        var cur = 0;
134        var wrap = el('div', 'gwp_lib_type_dropdown');
135        wrap.id = 'gwp_v2_so_dd';
136        wrap.innerHTML = '<div class="gwp_lib_type_current"><i class="las la-caret-left gwp_lib_type_arrow gwp_lib_type_arrow_disabled"></i><span></span><i class="las la-caret-right gwp_lib_type_arrow"></i></div><div class="gwp_lib_type_list" style="display:none"></div>';
137        var current = wrap.firstChild, list = wrap.lastChild, label = current.children[1], left = current.children[0], right = current.children[2];
138        function select(i) {
139            if (i < 0 || i >= tabs.length) return;
140            cur = i;
141            gwp_so_switch_tab(tabs[i][0]);
142            label.textContent = tabs[i][1];
143            left.classList.toggle('gwp_lib_type_arrow_disabled', i === 0);
144            right.classList.toggle('gwp_lib_type_arrow_disabled', i === tabs.length - 1);
145            Array.prototype.forEach.call(list.children, function (c, k) { c.classList.toggle('gwp_lib_type_item_active', k === i); });
146            list.style.display = 'none';
147        }
148        tabs.forEach(function (t, i) {
149            var it = el('div', 'gwp_lib_type_item', '<i class="las ' + t[2] + '"></i> ');
150            it.appendChild(document.createTextNode(t[1]));
151            it.addEventListener('click', function () { select(i); });
152            list.appendChild(it);
153        });
154        current.addEventListener('click', function () { list.style.display = list.style.display === 'block' ? 'none' : 'block'; });
155        left.addEventListener('click', function (e) { e.stopPropagation(); select(cur - 1); });
156        right.addEventListener('click', function (e) { e.stopPropagation(); select(cur + 1); });
157        document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) list.style.display = 'none'; });
158        nav.insertBefore(wrap, nav.firstChild);
159        select(0);
160    }
161
162    /* Builds the usual ‹ label › switcher. items = [{key,label,icon,dot}], onPick(key) is called on any change.
163       Extra item kinds: {heading:true,label} is a non-clickable group label; {key,label,action:true} is a button-style entry that is never "selected" and is skipped by the arrows.
164       A list of more than 10 selectable entries gets a search box. */
165    function makeSwitcher(id, items, cur, onPick, sig) {
166        var wrap = el('div', 'gwp_lib_type_dropdown');
167        wrap.id = id;
168        wrap.setAttribute('data-sig', sig);
169        wrap.innerHTML = '<div class="gwp_lib_type_current"><i class="las la-caret-left gwp_lib_type_arrow"></i><span></span><i class="las la-caret-right gwp_lib_type_arrow"></i></div><div class="gwp_lib_type_list" style="display:none"></div>';
170        var current = wrap.firstChild, list = wrap.lastChild, label = current.children[1], left = current.children[0], right = current.children[2];
171        var order = [];
172        items.forEach(function (t, i) { if (!t.heading && !t.action) order.push(i); });
173        var pos = order.indexOf(cur);
174        label.textContent = items[cur].label;
175        left.classList.toggle('gwp_lib_type_arrow_disabled', pos <= 0);
176        right.classList.toggle('gwp_lib_type_arrow_disabled', pos < 0 || pos === order.length - 1);
177        var search = null, empty = null, rows = [];
178        if (order.length > 10) {
179            search = el('input', 'gwp_lib_type_search');
180            search.type = 'text';
181            search.placeholder = 'Search\u2026';
182            search.setAttribute('aria-label', 'Search');
183            list.appendChild(search);
184        }
185        items.forEach(function (t, i) {
186            var it;
187            if (t.heading) {
188                it = el('div', 'gwp_lib_type_heading');
189                it.appendChild(document.createTextNode(t.label));
190            } else {
191                it = el('div', 'gwp_lib_type_item' + (i === cur ? ' gwp_lib_type_item_active' : '') + (t.action ? ' gwp_lib_type_action' : ''), t.dot ? '<span class="gwp_v2_dot" style="background:' + t.dot + '"></span>' : (t.icon ? '<i class="' + t.icon + '"></i> ' : ''));
192                it.appendChild(document.createTextNode(t.label));
193                it.addEventListener('click', function () { close(); onPick(t.key); });
194            }
195            rows.push({ node: it, item: t });
196            list.appendChild(it);
197        });
198        if (search) {
199            empty = el('div', 'gwp_lib_type_empty');
200            empty.textContent = 'No matches';
201            empty.style.display = 'none';
202            list.appendChild(empty);
203            search.addEventListener('input', function () {
204                var q = search.value.trim().toLowerCase(), shown = 0;
205                rows.forEach(function (r) {
206                    var ok = r.item.action || (!r.item.heading && r.item.label.toLowerCase().indexOf(q) !== -1) || (r.item.heading && !q);
207                    r.node.style.display = ok ? '' : 'none';
208                    if (ok && !r.item.heading && !r.item.action) shown++;
209                });
210                empty.style.display = shown || !q ? 'none' : '';
211            });
212            search.addEventListener('keydown', function (e) {
213                if (e.key === 'Escape') { close(); return; }
214                if (e.key !== 'Enter') return;
215                e.preventDefault();
216                var first = rows.filter(function (r) { return !r.item.heading && !r.item.action && r.node.style.display !== 'none'; })[0];
217                if (first) first.node.click();
218            });
219        }
220        function close() {
221            list.style.display = 'none';
222            if (search && search.value) { search.value = ''; search.dispatchEvent(new Event('input')); }
223        }
224        current.addEventListener('click', function () {
225            if (list.style.display === 'block') { close(); return; }
226            list.style.display = 'block';
227            if (search) setTimeout(function () { search.focus(); }, 0);
228        });
229        left.addEventListener('click', function (e) { e.stopPropagation(); if (pos > 0) onPick(items[order[pos - 1]].key); });
230        right.addEventListener('click', function (e) { e.stopPropagation(); if (pos >= 0 && pos < order.length - 1) onPick(items[order[pos + 1]].key); });
231        document.addEventListener('click', function (e) { if (!wrap.contains(e.target) && list.style.display === 'block') close(); });
232        return wrap;
233    }
234
235    /* A round "..." button with a small menu of actions. items = [{label,icon,danger,run}] */
236    function makeMenu(id, items, title) {
237        var wrap = el('div', 'gwp_v2_menu');
238        wrap.id = id;
239        var btn = el('button', 'gwp_v2_menu_btn', '<i class="las la-ellipsis-h"></i>');
240        btn.type = 'button';
241        btn.title = title || 'More';
242        btn.setAttribute('aria-label', title || 'More');
243        var list = el('div', 'gwp_lib_type_list gwp_v2_menu_list');
244        list.style.display = 'none';
245        items.forEach(function (t) {
246            var it = el('div', 'gwp_lib_type_item' + (t.danger ? ' gwp_lib_type_danger' : ''), '<i class="' + t.icon + '"></i> ');
247            it.appendChild(document.createTextNode(t.label));
248            it.addEventListener('click', function () { list.style.display = 'none'; t.run(); });
249            list.appendChild(it);
250        });
251        btn.addEventListener('click', function () { list.style.display = list.style.display === 'block' ? 'none' : 'block'; });
252        document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) list.style.display = 'none'; });
253        wrap.appendChild(btn);
254        wrap.appendChild(list);
255        return wrap;
256    }
257
258    window.gwp_v2_makeSwitcher = makeSwitcher;
259
260    function wsSection() {
261        var st = window.gwp_tasks_state;
262        if (!st) return '';
263        if (st.active_project) return 'projects';
264        if (st.view === 'notes') return 'notes';
265        if (st.view === 'master') return 'agenda';
266        return '';
267    }
268
269    var wsLastProject = null;
270    function wsPickSection(key) {
271        var st = gwp_tasks_state;
272        if (key === 'agenda') gwp_tasks_open_master();
273        else if (key === 'notes') gwp_tasks_open_notes();
274        else {
275            var all = (st.projects || []).concat(st.completed_projects || []);
276            var p = wsLastProject && all.filter(function (x) { return x.id == wsLastProject; })[0] || all[0];
277            if (p) gwp_tasks_open_project(p);
278        }
279    }
280
281    /* Workspace: the top-bar switcher chooses the section (My Agenda / Notes / Projects); what each section needs (project + view, group filter) is built under the title by syncWorkspaceControls(). */
282    function syncWorkspaceSwitcher() {
283        var tb = document.getElementById('gwp_workspace_toolbar');
284        var nav = document.getElementById('gwp_navigation');
285        if (!tb || !nav || typeof gwp_tasks_switch_view !== 'function') return;
286        var old = document.getElementById('gwp_v2_ws_dd');
287        var sec = wsSection();
288        if (!sec) { if (old) old.parentNode.removeChild(old); return; }
289        var sig = sec;
290        if (old && old.getAttribute('data-sig') === sig) return;
291        if (old) old.parentNode.removeChild(old);
292        var items = [
293            { key: 'agenda', label: 'My Agenda', icon: 'las la-th-list' },
294            { key: 'notes', label: 'Notes', icon: 'las la-sticky-note' },
295            { key: 'projects', label: 'Projects', icon: 'las la-columns' }
296        ];
297        var cur = 0;
298        items.forEach(function (t, i) { if (t.key === sec) cur = i; });
299        nav.insertBefore(makeSwitcher('gwp_v2_ws_dd', items, cur, wsPickSection, sig), nav.firstChild);
300    }
301
302    function syncWorkspaceControls() {
303        var tb = document.getElementById('gwp_workspace_toolbar');
304        if (!tb || typeof gwp_tasks_switch_view !== 'function') return;
305        var sec = wsSection();
306        var wrapEl = document.querySelector('.gwp_workspace_wrap');
307        if (wrapEl && wrapEl.getAttribute('data-v2sec') !== sec) wrapEl.setAttribute('data-v2sec', sec);
308        var old = document.getElementById('gwp_v2_ws_ctl');
309        if (old && old.parentNode !== tb) { old.parentNode.removeChild(old); old = null; }
310        var ctl = null, sig = '', build = null;
311        var st = window.gwp_tasks_state || {};
312        if (sec === 'projects') {
313            var done = st.completed_projects || [];
314            var ap = st.active_project;
315            wsLastProject = ap.id;
316            var isDone = done.some(function (p) { return p.id == ap.id; });
317            var tabs = Array.prototype.map.call(tb.querySelectorAll('.gwp_ts_view_tabs .gwp_ts_tab'), function (b) {
318                var m = /gwp_tasks_switch_view\('([a-z]+)'\)/.exec(b.getAttribute('onclick') || '');
319                var ic = b.querySelector('i');
320                return { key: m ? m[1] : '', label: (b.textContent || '').trim(), icon: ic ? ic.className : '', active: b.classList.contains('gwp_ts_tab_active') };
321            }).filter(function (t) { return t.key; });
322            var all = (st.projects || []).concat(done);
323            sig = 'p|' + ap.id + (isDone ? 'd' : '') + '|' + all.map(function (p) { return p.id + ':' + p.title + ':' + p.colour; }).join('|') + '|' + tabs.map(function (t) { return t.key + (t.active ? '*' : ''); }).join('|');
324            build = function (box) {
325                var pitems = [], pi = 0;
326                function add(p) { if (p.id == ap.id) pi = pitems.length; pitems.push({ key: String(p.id), label: p.title, dot: p.colour || '#2a9d90' }); }
327                (st.projects || []).forEach(add);
328                if (done.length) { pitems.push({ heading: true, label: 'Completed' }); done.forEach(add); }
329                pitems.push({ key: '__new', label: 'New project', icon: 'las la-plus', action: true });
330                box.appendChild(makeSwitcher('gwp_v2_ws_proj', pitems, pi, function (k) {
331                    if (k === '__new') gwp_tasks_new_project_modal(); else gwp_tasks_open_project_by_id(+k);
332                }, sig));
333                if (tabs.length) {
334                    var vi = 0;
335                    tabs.forEach(function (t, i) { if (t.active) vi = i; });
336                    box.appendChild(makeSwitcher('gwp_v2_ws_view', tabs, vi, gwp_tasks_switch_view, sig));
337                }
338                var acts = isDone
339                    ? [{ label: 'Reopen project', icon: 'las la-undo', run: function () { gwp_tasks_reopen_project(ap.id); } }]
340                    : [{ label: 'Edit project', icon: 'las la-pen', run: function () { gwp_tasks_edit_project_modal(ap.id); } },
341                       { label: 'Complete project', icon: 'las la-check', run: function () { gwp_tasks_complete_project(ap.id); } }];
342                acts.push({ label: 'Delete project', icon: 'las la-trash-alt', danger: true, run: function () { gwp_tasks_delete_project(ap.id); } });
343                box.appendChild(makeMenu('gwp_v2_ws_menu', acts, 'Project actions'));
344            };
345        } else if (sec === 'notes') {
346            var src = tb.querySelector('select.gwp_ts_panel_select');
347            if (src) {
348                sig = 'n|' + Array.prototype.map.call(src.options, function (o) { return o.value + ':' + o.text; }).join('|') + '#' + src.value;
349                build = function (box) {
350                    var gi = 0;
351                    var gitems = Array.prototype.map.call(src.options, function (o, i) { if (o.selected) gi = i; return { key: o.value, label: o.text, icon: '' }; });
352                    box.appendChild(makeSwitcher('gwp_v2_ws_grp', gitems, gi, function (k) { if (typeof gwp_ts_note_filter_by_group === 'function') gwp_ts_note_filter_by_group(+k); }, sig));
353                };
354            }
355        }
356        if (!build) { if (old) old.parentNode.removeChild(old); tb.removeAttribute('data-v2ctl'); return; }
357        tb.setAttribute('data-v2ctl', '1');
358        if (old && old.getAttribute('data-sig') === sig) return;
359        if (old) old.parentNode.removeChild(old);
360        ctl = el('div', 'gwp_v2_ws_ctl');
361        ctl.id = 'gwp_v2_ws_ctl';
362        ctl.setAttribute('data-sig', sig);
363        build(ctl);
364        tb.insertBefore(ctl, tb.firstChild);
365    }
366
367    /* Workspace: the Create button follows the current view (Board -> New List, Notes -> New Note, otherwise the page default).
368       This mirrors a control the toolbar renders, so the underlying page code is unchanged. */
369    var wsLast = '';
370    function syncWorkspaceExtras() {
371        var tb = document.getElementById('gwp_workspace_toolbar');
372        var nav = document.getElementById('gwp_navigation');
373        if (!tb || !nav) return;
374        if (typeof gwp_addbtn_set === 'function') {
375            var cfg = { label: 'New List', onclick: 'gwp_open_popup(`gwp_addlist`);' };
376            var b = null;
377            Array.prototype.forEach.call(tb.querySelectorAll('.gwp_ts_toolbar_right button'), function (x) {
378                var oc = x.getAttribute('onclick') || '';
379                if (!b && /gwp_tasks_new_list_modal|gwp_ts_open_note_editor/.test(oc)) b = x;
380            });
381            if (b) cfg = { label: (b.textContent || '').trim(), onclick: b.getAttribute('onclick') };
382            else if (wsSection() === 'agenda' && typeof gwp_tasks_new_agenda_task === 'function') cfg = { label: 'New Task', onclick: 'gwp_tasks_new_agenda_task();' };
383            else if (wsSection() === 'projects' && gwp_tasks_state.view === 'linked' && typeof gwp_ts_open_any_picker === 'function') cfg = { label: 'Link Item', onclick: 'gwp_ts_open_any_picker();' };
384            else if (wsSection() === 'projects' && typeof gwp_tasks_quick_add_agenda === 'function') cfg = { label: 'New Task', onclick: 'gwp_tasks_quick_add_agenda();' };
385            var key = cfg.label + '|' + cfg.onclick;
386            if (key !== wsLast) { wsLast = key; gwp_addbtn_set(cfg); }
387            if (b) tb.setAttribute('data-v2mirror', '1'); else tb.removeAttribute('data-v2mirror');
388        }
389    }
390
391    function buildHead() {
392        if (document.getElementById('gwp_v2_head')) return true;
393        var mf = document.getElementById('gwp_mediaframe');
394        if (!mf) return buildGenericHead();
395        if (!mf.parentNode) return false;
396        var h = el('div', '');
397        h.id = 'gwp_v2_head';
398        h.innerHTML = '<div class="v2h_text"><h1>Library</h1><p id="gwp_v2_sub"></p></div><div class="v2h_tools" id="gwp_v2_tools"></div>';
399        mf.parentNode.insertBefore(h, mf);
400        var lab = document.getElementById('gwp_med_type_label');
401        var sub = document.getElementById('gwp_v2_sub');
402        function sync() {
403            var l = document.getElementById('gwp_med_type_label');
404            sub.textContent = l && l.textContent ? 'Showing: ' + l.textContent.trim() : '';
405        }
406        sync();
407        if (lab && window.MutationObserver) new MutationObserver(sync).observe(lab, { childList: true, characterData: true, subtree: true });
408    }
409
410    /* Each page switcher is only as wide as its widest option needs, so every page ends up with its own width. */
411    var fitCtx = null;
412    function fitSwitchers() {
413        var dds = document.querySelectorAll('#gwp_navigation .gwp_lib_type_dropdown');
414        if (!dds.length) return;
415        if (!fitCtx) fitCtx = document.createElement('canvas').getContext('2d');
416        Array.prototype.forEach.call(dds, function (dd) {
417            var cur = dd.querySelector('.gwp_lib_type_current > span'), list = dd.querySelector('.gwp_lib_type_list');
418            if (!cur || !list) return;
419            var cs = getComputedStyle(cur);
420            fitCtx.font = cs.fontWeight + ' ' + cs.fontSize + ' ' + cs.fontFamily;
421            var max = fitCtx.measureText((cur.textContent || '').trim()).width;
422            Array.prototype.forEach.call(list.children, function (it) {
423                var w = fitCtx.measureText((it.textContent || '').trim()).width;
424                if (w > max) max = w;
425            });
426            if (window.innerWidth <= 768) { cur.style.removeProperty('min-width'); cur.style.removeProperty('flex'); dd.removeAttribute('data-fitw'); return; }
427            var px = Math.ceil(max) + 4;
428            if (dd.getAttribute('data-fitw') !== String(px)) { dd.setAttribute('data-fitw', String(px)); cur.style.setProperty('min-width', px + 'px', 'important'); cur.style.setProperty('flex', '0 0 auto', 'important'); }
429        });
430    }
431
432    /* Some pages rebuild their switcher on every change (Database swaps it via outerHTML), so re-fit whenever the bar's DOM changes. */
433    var fitObs = false;
434    function watchSwitchers() {
435        var nav = document.getElementById('gwp_navigation');
436        if (fitObs || !nav || !window.MutationObserver) return;
437        fitObs = true;
438        new MutationObserver(function () { fitSwitchers(); }).observe(nav.parentNode || nav, { childList: true, subtree: true, characterData: true });
439    }
440
441    /* Layout-affecting pieces (page title, switchers, workspace extras) are built the moment their host element is added to the page.
442       A MutationObserver callback runs before the browser paints, so nothing visibly jumps; the timer loop below stays as a fallback. */
443    var passing = false;
444    function layoutPass() {
445        if (passing) return;
446        passing = true;
447        try {
448            [buildHead, buildGenericHead, syncTitle, buildStoreSwitcher, syncWorkspaceSwitcher, syncWorkspaceControls, syncWorkspaceExtras, fitSwitchers, watchSwitchers].forEach(function (f) {
449                try { f(); } catch (e) { if (window.console) console.warn('v2 layout', f.name, e); }
450            });
451            /* The page stays hidden until its top bar has been built, so the title, bar and switcher appear together instead of the title showing first. */
452            if (window.gwp_v3_reveal && document.getElementById('gwp_addtb')) window.gwp_v3_reveal();
453        } catch (e) {}
454        passing = false;
455    }
456    if (window.MutationObserver) {
457        var pageObs = new MutationObserver(layoutPass);
458        pageObs.observe(document.documentElement, { childList: true, subtree: true });
459        setTimeout(function () { pageObs.disconnect(); }, 15000);
460    }
461    layoutPass();
462
463    var tries = 0;
464    (function boot() {
465        var ok = decorateMenu();
466        fitSwitchers();
467        watchSwitchers();
468        bindMenuScroll();
469        buildHead();
470        syncTitle();
471        buildStoreSwitcher();
472        var wtb = document.getElementById('gwp_workspace_toolbar');
473        if (wtb && !wtb.getAttribute('data-v2obs') && window.MutationObserver) { wtb.setAttribute('data-v2obs', '1'); new MutationObserver(function () { syncWorkspaceSwitcher(); syncWorkspaceControls(); syncWorkspaceExtras(); }).observe(wtb, { childList: true }); }
474        syncWorkspaceSwitcher();
475        syncWorkspaceControls();
476        syncWorkspaceExtras();
477        if (ok && window.MutationObserver && !document.getElementById('gwp_accontent').getAttribute('data-v2obs')) {
478            var box = document.getElementById('gwp_accontent');
479            box.setAttribute('data-v2obs', '1');
480            var busy = false;
481            new MutationObserver(function () { if (busy) return; busy = true; setTimeout(function () { decorateMenu(); busy = false; }, 50); }).observe(box, { childList: true });
482        }
483        if (tries++ < 48) setTimeout(boot, 250);
484    })();
485})();

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.