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.