const Endpoint = '/scopes/status'; const macs = new Map(); const Interval = 1000; let updateTimeoutId = null; const StorageGroup = Object.freeze({ SHOW: 'show', CHECK: 'check', }); class ogStorage { static STORAGE_VERSION = Object.freeze(1); static store(group, context, elemId, value) { const key = `${group}-${context}-${elemId}`; localStorage.setItem(key, value); } static remove(group, context, elemId) { const key = `${group}-${context}-${elemId}`; localStorage.removeItem(key); } static hasKey(group, context, elemId) { const key = `${group}-${context}-${elemId}`; return localStorage.getItem(key) !== null; } static deleteInvalidStorage(items, group, context) { if (localStorage.getItem('storageVersion') < ogStorage.STORAGE_VERSION) { localStorage.clear(); localStorage.setItem('storageVersion', ogStorage.STORAGE_VERSION); return } const prefix = `${group}-${context}`; const existingKeys = items.map(function() { return `${group}-${context}-${this.id}`; }).get(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (!key.startsWith(prefix)) { continue; } if (!existingKeys.includes(key)) { localStorage.removeItem(key); } } } static storeCheckboxStatus(checkbox, context) { if (checkbox.checked) ogStorage.store(StorageGroup.CHECK, context, checkbox.id, ""); else ogStorage.remove(StorageGroup.CHECK, context, checkbox.id); } } async function show_client_mac(pill_id) { const pill = $('#' +pill_id); if (!pill.length) { return } const ip = $('[name="ip"]', pill).html() if (!macs.get(ip)) { const resp = await fetch('/client/mac?ip=' + ip); const resp_mac = await resp.json(); macs.set(ip, resp_mac) } const mac = macs.get(ip) pill.append('
' + mac + '
'); } function showSelectedClient(client_checkbox) { const container = $('#selected-clients'); const pill_id = 'pill-' + client_checkbox.name.replaceAll(/[. ()]/g, '_'); const client_name = client_checkbox.name.replaceAll(/_\d+$/g, ''); if (client_name === 'scope-room' || client_name === 'scope-center' || client_name === 'folder' || client_name === 'scope-server') { return; } if (client_checkbox.checked) { if (!($('#' + pill_id).length)) { $(container).append('
' + '
' + client_name + '
' + '
' + client_checkbox.value + '
'+ '
'); show_client_mac(pill_id); } return; } $('#' + pill_id, container).remove(); } function showSelectedClientsOnEvents() { const checkboxes = $('#sidebar input:checkbox'); checkboxes.on('change show-client', function () { showSelectedClient(this); }); } function findParentCheckboxes(element) { const $element = $(element); const parents = $element.parentsUntil('#scopes').not('ul'); const checkboxes = parents .map(function() { return $(this).children('input[type="checkbox"][form="scopesForm"]').toArray(); }) .get() .flat(); return $(checkboxes).not($element);; } function setParentStatus(checkboxes) { checkboxes.each(function() { const checkboxChildren = $(this).parent().find('input:checkbox[form="scopesForm"]').not(this); if (checkboxChildren.length == 0) return; const unCheckedChildren = checkboxChildren.filter(":not(:checked)"); this.indeterminate = unCheckedChildren.length > 0 && unCheckedChildren.length < checkboxChildren.length; this.checked = unCheckedChildren.length === 0; }); } function configureCommandCheckboxes(context) { const checkboxes = $('input:checkbox[form="scopesForm"]'); ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); // Ensure the form fields are sent $('#scopesForm').on('submit', function() { checkboxes.each(function() { if (this.indeterminate) { this.checked = true; this.disabled = false; } }); }); // disable checkboxes outside of scope-center branches $(window).on('pageshow', function(event) { const enabledCheckboxes = checkboxes.filter('[name="scope-center"]').parent().find('input:checkbox[form="scopesForm"]'); checkboxes.not(enabledCheckboxes).prop('disabled', true); setParentStatus(checkboxes) }); checkboxes.on('change', function () { const checked = this.checked; const childrenCheckboxes = $('input[type="checkbox"][form="scopesForm"]', this.parentNode); // Uncheck all other checkboxes outside of the actual center branch if (checked) { const centerBranch = findParentCheckboxes(this).add(childrenCheckboxes) .filter('[name="scope-center"]') .parent().find('input:checkbox[form="scopesForm"]'); checkboxes.not(centerBranch).each(function () { this.checked = false; this.indeterminate = false; }); } childrenCheckboxes.each(function () { this.checked = checked; }); setParentStatus(checkboxes); checkboxes.each(function() { showSelectedClient(this); ogStorage.storeCheckboxStatus(this, context); }); }); } function keepSelectedClients(context) { const checkboxes = $('#sidebar input:checkbox') checkboxes.on('change', function (event) { ogStorage.storeCheckboxStatus(this, context); }); ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); checkboxes.each(function () { if (ogStorage.hasKey(StorageGroup.CHECK, context, this.id)) { this.checked = true; $(this).trigger('show-client'); } }); } function keepTreeState(selector, context) { const tree_items = $(selector + ' .collapse'); ogStorage.deleteInvalidStorage(tree_items, StorageGroup.SHOW, context); tree_items.on('hidden.bs.collapse', function (event) { event.stopPropagation(); ogStorage.remove(StorageGroup.SHOW, context, this.id) }); tree_items.on('shown.bs.collapse', function (event) { event.stopPropagation(); ogStorage.store(StorageGroup.SHOW, context, this.id, "") }); tree_items.each(function () { if (ogStorage.hasKey(StorageGroup.SHOW, context, this.id)) { $(this).collapse('show'); } else { $(this).siblings('a').addClass('collapsed'); } }); } function updateScopeState() { if (updateTimeoutId) { clearTimeout(updateTimeoutId); } updateTimeoutId = setTimeout(() => { updateTimeoutId = null; fetch(Endpoint) .then(response => response.json()) .then((data) => { updateScopes(data.scope); }) .catch((error) => { console.error(error); }) .finally(() => { updateScopeState(); }); }, Interval); } function updatePillStatus(scope, pill) { const state = scope.state let link = scope.link let units = 'Mb/s' const pillCls = ['badge-danger', 'badge-success', 'badge-warning', 'badge-wol', 'badge-light', 'badge-linux', 'badge-windows']; pill.classList.remove(...pillCls); if (state === 'OPG') { pill.classList.add('badge-warning'); } else if (state === 'LNX') { pill.classList.add('badge-linux'); } else if (state === 'LNXS') { pill.classList.add('badge-linux'); } else if (state === 'WIN') { pill.classList.add('badge-windows'); } else if (state === 'WINS') { pill.classList.add('badge-windows'); } else if (state === 'BSY') { pill.classList.add('badge-danger'); } else if (state === 'VDI') { pill.classList.add('badge-success'); } else if (state === 'WOL_SENT') { pill.classList.add('badge-wol'); } else { pill.classList.add('badge-light'); } $('[name="link"]', pill).remove() if (link) { let linkClasses = 'class="badge-danger badge-pill"'; if (link >= 1000) { link = link / 1000 units = 'Gb/s' linkClasses = ''; } $(pill).append('
' + link + ' ' + units + '
'); } } function updateScopes(scopes) { let hasLiveChildren = false; scopes.forEach((scope) => { const scopeName = `${scope.name}`.replaceAll(/[.]|[ ]/g, '_'); if (scope.state) { const scopeId = `${scopeName}_${scope.id}`; const iconEl = document.querySelector(`#${scopeId} .nav-icon`); const iconCls = ['fas', 'far', 'fa-circle', 'fa-check-circle', 'fa-times-circle', 'fa-user-circle', 'text-danger', 'text-success', 'text-warning', 'text-wol', 'text-linux', 'text-windows']; iconEl.classList.remove(...iconCls); let newIconCls = []; if (scope.state === 'OPG') { hasLiveChildren = true; newIconCls.push('fas', 'text-warning'); if (scope.last_cmd.result === 'failure') newIconCls.push('fa-times-circle'); else newIconCls.push('fa-circle'); } else if (scope.state === 'LNX') { newIconCls.push('fas', 'fa-circle', 'text-linux'); } else if (scope.state === 'LNXS') { newIconCls.push('fas', 'fa-user-circle', 'text-linux'); } else if (scope.state === 'WIN') { newIconCls.push('fas', 'fa-circle', 'text-windows'); } else if (scope.state === 'WINS') { newIconCls.push('fas', 'fa-user-circle', 'text-windows'); } else if (scope.state === 'BSY') { hasLiveChildren = true; newIconCls.push('fas', 'fa-circle', 'text-danger'); } else if (scope.state === 'VDI') { newIconCls.push('fas', 'fa-circle', 'text-success'); } else if (scope.state === 'WOL_SENT') { newIconCls.push('fas', 'fa-circle', 'text-wol'); } else { newIconCls.push('far', 'fa-circle'); } iconEl.classList.add(...newIconCls); const pillScopeId = `pill-${scopeId}`; const pillEl = document.querySelector(`#${pillScopeId}`); if (pillEl) updatePillStatus(scope, pillEl); } if (scope.scope) { // This is a level so we should update all childs let hasLocalLiveChildren = updateScopes(scope.scope); if (hasLocalLiveChildren) { hasLiveChildren = true; } const disclosureWidgetId = scope.id ? `${scope.type}-${scope.id}` : `${scope.type}-${scopeName}`; const disclosureWidget = document.querySelector(`#${disclosureWidgetId}`); disclosureWidget.classList.remove('live-report'); if (hasLocalLiveChildren) { disclosureWidget.classList.add('live-report'); } } }); return hasLiveChildren; } function checkFolderParent(context) { const folder = $('#sidebar input:checkbox[name="folder"]') folder.on('change', function() { const folder_parent = $('#' + $.escapeSelector(this.dataset.parentInput)); folder_parent.prop('checked', this.checked); ogStorage.storeCheckboxStatus(folder_parent.get(0), context); }); } function limitCheckboxes(context) { const checkboxes = $('#sidebar input:checkbox'); ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); checkboxes.on('change', function () { const currentCheckbox = $(this); const currentParentRoom = currentCheckbox.attr('data-parent-room'); checkboxes.each(function () { const checkbox = $(this); const checkboxParentRoom = checkbox.attr('data-parent-room'); if (currentCheckbox.is(checkbox)) { return; } const isSibling = currentParentRoom && checkboxParentRoom && checkboxParentRoom === currentParentRoom; if (!isSibling) { checkbox.prop('checked', false); } }); checkCheckbox('scope-server'); checkboxes.each(function() { ogStorage.storeCheckboxStatus(this, context); showSelectedClient(this); }); }); } function checkCheckbox(inputName) { const checkboxes = $('#sidebar input:checkbox[name="' + inputName + '"]'); checkboxes.each(function() { const checkbox = this; const checkboxChildren = $('input:checkbox', this.parentNode).not(this); if (checkboxChildren.length == 0) return; const checkedChildren = checkboxChildren.filter(":checked"); checkbox.checked = checkedChildren.length > 0; }); } function checkOnChange(inputName) { const checkboxes = $('#sidebar input:checkbox') checkboxes.on('change', function (event) { checkCheckbox(inputName); }); checkboxes.each(function () { checkCheckbox(inputName) }); }