-
Notifications
You must be signed in to change notification settings - Fork 6
/
common-scripts.js
142 lines (135 loc) · 4.41 KB
/
common-scripts.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* 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')));
}
if (params.get('isRandomized') !== null) {
settings.isRandomized = Boolean(parseInt(params.get('isRandomized')));
}
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;
}
function setContentEditablePlaintext(element) {
element.setAttribute('contenteditable', 'PLAINTEXT-ONLY');
const isPlaintextSupported = element.contentEditable === 'plaintext-only';
element.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
element.blur();
event.preventDefault();
}
});
element.addEventListener('blur', function(event) {
// Combine text nodes into one text node
element.textContent = element.textContent;
});
if (isPlaintextSupported) {
return;
}
// Polyfill for contenteditable="plaintext-only"
element.contentEditable = true;
const observer = new MutationObserver(plaintextOnlyMutationHandler);
observer.observe(element, {childList: true, subtree: true});
}
function plaintextOnlyMutationHandler(mutationList, observer) {
for (const mutation of mutationList) {
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType == Node.ELEMENT_NODE) {
addedNode.replaceWith(addedNode.textContent);
}
}
}
}