/* global $ $$ API debounce $create t */ 'use strict'; /* exported hotkeys */ const hotkeys = (() => { const entries = document.getElementsByClassName('entry'); let togglablesShown; let togglables; let enabled = false; let ready = false; window.addEventListener('showStyles:done', () => { togglablesShown = true; togglables = getTogglables(); ready = true; setState(true); initHotkeyInfo(); }, {once: true}); window.addEventListener('resize', adjustInfoPosition); return {setState}; function setState(newState = !enabled) { if (!ready) { throw new Error('hotkeys no ready'); } if (newState !== enabled) { window[`${newState ? 'add' : 'remove'}EventListener`]('keydown', onKeyDown); enabled = newState; } } function onKeyDown(event) { if (event.ctrlKey || event.altKey || event.metaKey || !enabled || /^(text|search)$/.test((document.activeElement || {}).type)) { return; } let entry; let {key, code, shiftKey} = event; if (key >= '0' && key <= '9') { entry = entries[(Number(key) || 10) - 1]; } else if (code >= 'Digit0' && code <= 'Digit9') { entry = entries[(Number(code.slice(-1)) || 10) - 1]; } else if (key === '`' || key === '*' || code === 'Backquote' || code === 'NumpadMultiply') { invertTogglables(); } else if (key === '-' || code === 'NumpadSubtract') { toggleState(entries, 'enabled', false); } else if (key === '+' || code === 'NumpadAdd') { toggleState(entries, 'disabled', true); } else if (key.length === 1) { shiftKey = false; // typing ':' etc. needs Shift so we hide it here to avoid opening editor key = key.toLocaleLowerCase(); entry = [...entries].find(e => e.innerText.toLocaleLowerCase().startsWith(key)); } if (!entry) { return; } const target = $(shiftKey ? '.style-edit-link' : 'input', entry); target.dispatchEvent(new MouseEvent('click', {cancelable: true})); } function getTogglables() { const enabledOrAll = $('.entry.enabled') ? $$('.entry.enabled') : [...entries]; return enabledOrAll.map(entry => entry.id); } function countEnabledTogglables() { let num = 0; for (const id of togglables) { num += $(`#${id}`).classList.contains('enabled'); } return num; } function invertTogglables() { togglables = togglables.length ? togglables : getTogglables(); togglablesShown = countEnabledTogglables() > togglables.length / 2; toggleState(togglables, null, !togglablesShown); togglablesShown = !togglablesShown; } function toggleState(list, match, enable) { const results = []; let task = Promise.resolve(); for (let entry of list) { entry = typeof entry === 'string' ? $('#' + entry) : entry; if (!match && $('input', entry).checked !== enable || entry.classList.contains(match)) { results.push(entry.id); task = task .then(() => API.toggleStyle(entry.styleId, enable)) .then(() => { entry.classList.toggle('enabled', enable); entry.classList.toggle('disabled', !enable); $('input', entry).checked = enable; }); } } if (results.length) task.then(API.refreshAllTabs); return results; } function initHotkeyInfo() { const container = $('#hotkey-info'); let title; container.onclick = ({target}) => { if (target.localName === 'button') { close(); } else if (!container.dataset.active) { open(); } }; function close() { delete container.dataset.active; document.body.style.height = ''; container.title = title; window.addEventListener('resize', adjustInfoPosition); } function open() { window.removeEventListener('resize', adjustInfoPosition); debounce.unregister(adjustInfoPosition); title = container.title; container.title = ''; container.style = ''; container.dataset.active = true; if (!container.firstElementChild) { buildElement(); } const height = 3 + container.firstElementChild.scrollHeight + container.lastElementChild.scrollHeight; if (height > document.body.clientHeight) { document.body.style.height = height + 'px'; } } function buildElement() { const keysToElements = line => line .split(/(<.*?>)/) .map(s => (!s.startsWith('<') ? s : $create('mark', s.slice(1, -1)))); const linesToElements = text => text .trim() .split('\n') .map((line, i, array) => $create(i < array.length - 1 ? { tag: 'p', appendChild: keysToElements(line), } : { tag: 'a', target: '_blank', href: 'https://github.com/openstyles/stylus/wiki/Popup', textContent: line, })); [ linesToElements(t('popupHotkeysInfo')), $create('button', t('confirmOK')), ].forEach(child => { container.appendChild($create('div', child)); }); } } function adjustInfoPosition(debounced) { if (debounced !== true) { debounce(adjustInfoPosition, 100, true); return; } } })();