PageSourceSearch

https://www.vscht.cz/v1787844072/static/js/csc/util/util.js

js vscht.cz collected 2026-09-24 08:55:21 UTC 11,137 bytes, 368 lines download raw bytes

1window.Util = {
2	isElement(element) {
3		return typeof HTMLElement === "object" ? element instanceof HTMLElement :
4			element && typeof element === "object" && element !== null && element.nodeType === 1 && typeof element.nodeName === "string";
5	},
6
7	defaults(options = null, defaults = null) {
8		options = options ?? {};
9		defaults = defaults ?? {};
10		Object.keys(defaults).forEach((option) => {
11			if(typeof options[option] === 'object' && options[option] !== null && options[option].constructor === ({}).constructor
12					&& typeof defaults[option] === 'object' && defaults[option] !== null && defaults[option].constructor === ({}).constructor) {
13				options[option] = this.defaults(options[option], defaults[option]);
14			} else if(typeof options[option] === 'undefined') {
15				options[option] = defaults[option];
16			}
17		});
18		return options;
19	},
20
21	overrides(options = null, overrides = null) {
22		options = options ?? {};
23		overrides = overrides ?? {};
24		Object.keys(overrides).forEach((option) => {
25			if(typeof options[option] === 'object' && options[option] !== null && options[option].constructor === ({}).constructor
26					&& typeof overrides[option] === 'object' && overrides[option] !== null && overrides[option].constructor === ({}).constructor) {
27				options[option] = this.overrides(options[option], overrides[option]);
28			} else {
29				options[option] = overrides[option];
30			}
31		});
32		return options;
33	},
34
35	wrapHtml(html, wrapper) {
36		if(this.isElement(wrapper)) {
37			wrapper = wrapper.cloneNode();
38		} else {
39			wrapper = document.createElement(wrapper);
40		}
41
42		wrapper.innerHTML = html;
43		return wrapper;
44	},
45
46	wrapElement(element, wrapper) {
47		if(this.isElement(wrapper)) {
48			wrapper = wrapper.cloneNode();
49		} else {
50			wrapper = document.createElement(wrapper);
51		}
52
53		element.replaceWith(wrapper);
54		wrapper.appendChild(element);
55		return element;
56	},
57
58	replaceHtmlTag(element, new_tag) {
59		let replacement = document.createElement(new_tag);
60		replacement.innerHTML = element.innerHTML;
61
62		// Copy the attributes
63		Array.from(element.attributes).forEach(a => replacement.attributes.setNamedItem(a.cloneNode()));
64
65		// Replace source element
66		element.parentNode.replaceChild(replacement, element);
67		return element;
68	},
69
70	stop() {
71		if(window.stop)
72			window.stop();
73	},
74
75	humanByteSize(bytes, si = false, decimal_places = 0, short_units = false) {
76		const thresh = si ? 1000 : 1024;
77
78		if(isNaN(bytes))
79			return null;
80		if(Math.abs(bytes) < thresh)
81			return bytes + ' B';
82
83		const units = short_units ? ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] : (si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']);
84		let u = -1;
85
86		do {
87			bytes /= thresh;
88			++u;
89		} while (Math.round(Math.abs(bytes) * (10**decimal_places)) / (10**decimal_places) >= thresh && u < units.length - 1);
90
91		return bytes.toFixed(decimal_places) + ' ' + units[u];
92	},
93
94	humanSecondInterval(seconds) {
95		if(isNaN(seconds))
96			return null;
97		if(Math.abs(seconds) < 60)
98			return seconds + ' s';
99
100		const units = [ 'm', 'h', 'd' ];
101		const thresh = [ 60, 60, 24 ]
102		let u = -1;
103
104		do {
105			seconds /= thresh[u+1];
106			++u;
107		} while (Math.round(Math.abs(seconds) * 10) / 10 >= thresh[u+1] && u < units.length - 1);
108
109		return seconds.toFixed(0) + ' ' + units[u];
110	},
111
112	submitForm(form, detail = null) {
113		if(form instanceof Event)
114			form = form.currentTarget || form.target;
115		if(!this.isElement(form))
116			form = document.body.querySelector(form);
117
118		form.dispatchEvent(new Event('submit', {
119			cancelable: true,
120			detail: detail,
121		}));
122	},
123
124	cookieGet(name) {
125		var cookies = document.cookie.split(';');
126
127		for(var i = 0; i < cookies.length; i++) {
128			var cookie = cookies[i].split('=');
129			if(name === cookie[0].trim()) return decodeURIComponent(cookie[1]);
130		}
131
132		return null;
133	},
134
135	cookieSet(name, value, timeout = null, type = 'core') {
136		if(type === null || window.config.cookie_types.includes(type)) {
137			timeout = timeout === null ? '' : ' Max-Age=' + (timeout * 1000) + ';';
138			document.cookie = name + '=' + encodeURIComponent(value) + ';' + timeout + ' Path=' + config.root + '; ' + (window.config.https ? 'Secure; ' : '') + 'SameSite=Lax';
139		} else {
140			console.warn(`Attempted to set cookie '${name}' of type '${type}', but it was not allowed by the user`);
141			// TODO: Notify the user
142		}
143	},
144
145	cookieUnset(name) {
146		if(this.cookieGet(name))
147			this.cookieSet(name, '');
148	},
149
150	cookieConsent(types = []) {
151		this.cookieSet('CscAllowedCookieTypes', types.join(','), 365 * 24 * 60 * 60, null);
152		window.config.cookie_types = types;
153	},
154
155	dispatchEvent(element, name, detail = undefined, options = {}, overrides = {}) {
156		if(Array.isArray(element))
157			return element.map(e => this.dispatchEvent(e, name, detail, options, overrides));
158		if(typeof element.elements === 'function')
159			return this.dispatchEvent(element.elements(), name, detail, options, { ...overrides, component: element });
160
161		options = this.defaults(options, { bubbles: true, detail });
162		return element.dispatchEvent(this.overrides(new CustomEvent(name, options), overrides));
163	},
164
165	functionOverride(_super, _override) {
166		if(typeof _super !== 'function')
167			return _super;
168
169		let tmp = {
170			[_super.name]: function() {
171				let args = Array.from(arguments);
172				args.unshift(_super);
173				return _override.apply(this, args);
174			},
175		};
176
177		let definedProps = Object.getOwnPropertyNames(tmp[_super.name]);
178		let props = Object.getOwnPropertyNames(_super).filter(p => !definedProps.includes(p));
179
180		props.forEach(prop => {
181			Object.defineProperty(tmp[_super.name], prop, {
182				get: () => _super[prop],
183				set: v => _super[prop] = v,
184			});
185		});
186
187		return tmp[_super.name];
188	},
189
190	findDotKey(obj, key) {
191		return key.split('.').reduce((o, i) => o === undefined ? undefined : o[i], obj);
192	},
193
194	delayedTimeoutMap: new Map(),
195	delayed(previous, callback, timeout = 0, ignore_previous = false) {
196		clearTimeout(previous);
197		let new_callback;
198		let new_timeout = setTimeout(() => new_callback(true), timeout);
199
200		new_callback = (last) => {
201			if(!ignore_previous && this.delayedTimeoutMap.has(previous))
202				this.delayedTimeoutMap.get(previous)(false);
203			callback(last);
204			this.delayedTimeoutMap.delete(new_timeout);
205		};
206
207		this.delayedTimeoutMap.set(new_timeout, new_callback)
208		return new_timeout;
209	},
210
211	objectProxy(options, ...objects) {
212		const callOnFirst = (key = undefined, callback = () => {}) => {
213			for(obj of objects)
214				if(key === undefined || Reflect.has(obj, key))
215					return callback(obj);
216		};
217
218		return new Proxy(objects[0], {
219			deleteProperty: (target, prop) => options.readonlyFallbacks ? Reflect.deleteProperty(target, prop) : callOnFirst(prop, obj => Reflect.deleteProperty(obj, prop)),
220			get: (target, prop, receiver) => callOnFirst(prop, obj => Reflect.get(obj, prop, receiver)),
221			getOwnPropertyDescriptor: (target, prop) => callOnFirst(prop, obj => Reflect.getOwnPropertyDescriptor(obj, prop)),
222			has: (target, key) => callOnFirst(key, obj => true) === true,
223			ownKeys: (target) => {
224				let keys = new Set();
225				for(obj of objects)
226					keys = new Set([...keys, ...Reflect.ownKeys(obj)]);
227				return [...keys];
228			},
229			set: (target, prop, value) => options.readonlyFallbacks ? Reflect.set(target, prop, value) : callOnFirst(prop, obj => Reflect.set(obj, prop, value)),
230		});
231	},
232
233	bindEventDebugger(...events) {
234		function debug(event) {
235			let component = event.target && event.target.$component || null;
236			let id = component && component.$attrs.id || event.target;
237			console.debug(id, event.type, event.detail);
238		};
239
240		return events.reduce((obj, ev) => ({ ...obj, [ev]: e => debug(e) }), {});
241	},
242
243	reactiveStorageMap: new Map(),
244	reactiveStorage(key, initialValue = null) {
245		if(this.reactiveStorageMap.has(key))
246			return this.reactiveStorageMap.get(key);
247
248		try {
249			if(typeof localStorage.getItem(key) === "string")
250				initialValue = JSON.parse(localStorage.getItem(key));
251		} catch (SyntaxError) {}
252
253		const value = Vue.reactive({
254			value: initialValue,
255		});
256
257		let recursionBlock = false;
258		Vue.watchEffect(() => {
259			if(recursionBlock)
260				return;
261
262			recursionBlock = true;
263			if(localStorage.getItem(key) !== JSON.stringify(value.value))
264				localStorage.setItem(key, JSON.stringify(value.value));
265			recursionBlock = false;
266		});
267
268		window.addEventListener('storage', event => {
269			if(recursionBlock)
270				return;
271
272			recursionBlock = true;
273			try {
274				if(event.key === key)
275					value.value = JSON.parse(localStorage.getItem(key));
276			} catch (e) {
277				console.error(e)
278			}
279			recursionBlock = false;
280		});
281
282		Storage.prototype.setItem = this.functionOverride(Storage.prototype.setItem, function(_super, setKey, setValue) {
283			if(recursionBlock || this !== window.localStorage)
284				return _super.call(this, setKey, setValue);
285
286			recursionBlock = true;
287			try {
288				if(setKey === key)
289					value.value = JSON.parse(setValue);
290			} catch (e) {
291				console.error(e);
292			}
293			const result = _super.call(this, setKey, setValue);
294			recursionBlock = false;
295			return result;
296		});
297
298		this.reactiveStorageMap.set(key, value);
299		return value;
300	},
301
302	objectToFormData(formData, data, parentKey) {
303		if(data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
304			Object.keys(data).forEach(key => {
305				buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
306			});
307		} else if(parentKey && parentKey.substr(-1) === ']') {
308			formData.append(parentKey, data == null ? '' : data);
309		} else {
310			formData.set(parentKey, data == null ? '' : data);
311		}
312	},
313
314	scrollToElement(element, offset = 0, options = {}) {
315		window.scrollTo({
316			top: element.getBoundingClientRect().top + window.pageYOffset + offset,
317			...options,
318		});
319	},
320
321	debounce(root, fn, timeout = 100) {
322		if(root.cscDebounceTimer) {
323			clearTimeout(root.cscDebounceTimer);
324			root.cscDebounceTimer = null;
325		}
326
327		return root.cscDebounceTimer = setTimeout(() => {
328			root.cscDebounceTimer = null;
329			fn();
330		}, timeout);
331	},
332
333	youtubeLinkToId(youtube_link) {
334		const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch))(\?v=)?([^#&?]*).*/;
335		const match = youtube_link.match(regExp);
336		return match ? match[8] : null;
337	},
338
339	cssUrl(href) {
340		return config.root + 'v' + config.version + '/css' + href;
341	},
342
343	toggleClass(target, options = {}) {
344		options = this.defaults(options, { class: "toggle", hideRest: false });
345
346		if(target instanceof Event)
347			target = target.currentTarget;
348		if(!this.isElement(target))
349			target = document.querySelector(target);
350
351		if(options.hideRest && !target.classList.contains(options.class))
352			Array.from(document.getElementsByClassName(options.class))
353				.forEach(e => e.classList.remove(options.class));
354
355		target.classList.toggle(options.class);
356	},
357
358	currentGridBreakpoint() {
359		const breakpoints = ["xs", "sm", "md", "lg", "xl"];
360		const style = getComputedStyle(document.body);
361		const width = window.innerWidth;
362
363		for(const breakpoint of breakpoints)
364			if(width < parseInt(style.getPropertyValue(`--grid-break-${breakpoint}`)))
365				return breakpoint;
366		return breakpoints[breakpoints.length - 1];
367	},
368};

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.