/* Settings flags */ const settingsFlagsKey = '?'; const settingsFlagsEnum = { allowCategoryEditing: 1, isRandomized: 2, }; const settingsDefaults = { allowCategoryEditing: true, isRandomized: false, }; function loadSettings(data) { const settings = Object.assign({}, settingsDefaults); const flags = data[settingsFlagsKey]; for (const settingName of Object.keys(settingsFlagsEnum)) { settings[settingName] = Boolean(settingsFlagsEnum[settingName] & flags); } return settings; } function saveSettings(settings) { let flags = 0; for (const [settingName, isEnabled] of Object.entries(settings)) { if (isEnabled) { flags |= settingsFlagsEnum[settingName]; } } return flags; } /* Saving and loading */ const uncategorizedKey = '#'; const reservedKeys = [ settingsFlagsKey, uncategorizedKey, ]; const regexRemoveBasenameFromUrl = /\/[^\/]*?$/; async function uint8ArrayToBase64(data) { const base64url = await new Promise((resolve) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.readAsDataURL(new Blob([data])); }); return base64url.split(',', 2)[1]; } async function base64ToUint8Array(base64string) { const response = await fetch('data:;base64,' + base64string); const blob = await response.blob(); return new Uint8Array(await blob.arrayBuffer()); } async function saveToString(data) { const jsonString = JSON.stringify(data); const compressedData = pako.deflate(new TextEncoder().encode(jsonString)); const base64String = await uint8ArrayToBase64(compressedData); const webSafeBase64String = base64String.replaceAll('+', '-').replaceAll('/', '_'); return webSafeBase64String; } async function loadFromString(webSafeBase64String) { const base64String = webSafeBase64String.replaceAll('-', '+').replaceAll('_', '/'); const compressedData = await base64ToUint8Array(base64String); const jsonString = new TextDecoder('utf8').decode(pako.inflate(compressedData)); const data = JSON.parse(jsonString); return data; } function loadFromQueryParameters() { const data = {}; const settings = Object.assign({}, settingsDefaults); const params = new URLSearchParams(window.location.search); const cardTexts = (params.get('cards') || '').split(',').filter(item => !!item); const categories = (params.get('categories') || '').split(',').filter(item => !!item); if (params.get('allowCategoryEditing') !== null) { settings.allowCategoryEditing = Boolean(parseInt(params.get('allowCategoryEditing'))); } data[uncategorizedKey] = cardTexts; data[settingsFlagsKey] = saveSettings(settings); for (const categoryName of categories) { data[categoryName] = []; } return data; } /* Utilities */ function debounce(func, wait, immediate) { let timeout; return function debouncedFunc() { const context = this; const args = arguments; const later = function () { timeout = null; if ( ! immediate) { func.apply(context, args); } }; const callNow = immediate && ! timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { func.apply(context, args); } }; } function asTextContent(input) { const tempElement = document.createElement('span'); tempElement.textContent = input; return tempElement.innerHTML; }