PageSourceSearch

https://thai-mecc.go.th/share/js/utils/Utils.js

js thai-mecc.go.th collected 2026-10-09 03:31:42 UTC 47,508 bytes, 1,437 lines download raw bytes

1
2async function getMustacheTemplate(branch, callback) {
3	let response = await GET('mustache/get/'+branch, callback, 'json');
4	if (callback != undefined) callback(response.results);
5	return response.results;
6}
7
8async function getMustacheIcon(callback) {
9	let response = await GET('mustache/icon/get', callback, 'json');
10	if (callback != undefined) callback(response.results);
11	return response.results;
12}
13
14async function getExtensionMustacheTemplate(extension, branch, callback) {
15	let response = await GET(`mustache/extension/get/${extension}/${branch}`, callback, 'json');
16	if (callback != undefined) callback(response.results);
17	return response.results;
18}
19
20async function getAllCountry(callback) {
21	let response = await GET('country/all', callback, 'json');
22	if (callback != undefined) callback(response);
23	return response;
24}
25
26async function getLocale(language, oldLanguage, callback) {
27	let response = await GET('locale/'+language+'/'+oldLanguage, callback, 'json', true);
28	if (response == undefined) response = {results: {}}
29	if (callback != undefined) callback(response.results);
30	return response.results;
31}
32
33async function setTextLocale(text, callback) {
34	let response = await GET('locale/set/'+text.encodeHex(), callback, 'json');
35	if (callback != undefined) callback(response);
36	return response;
37}
38
39async function setChunkTextLocale(data, callback) {
40	let response = await GET('locale/chunk/set', data, callback, 'json');
41	if (callback != undefined) callback(response);
42	return response;
43}
44
45async function START(page) {
46	await main.init();
47}
48
49String.prototype.encodeHex = function(){
50	let encoder = new TextEncoder();
51	let array = encoder.encode(this)
52	let result = "";
53	for (let i=0; i < array.length; i++) {
54		result += array[i].toString(16);
55	}
56	return result
57}
58
59String.prototype.decodeHex = function(){
60	let str = '';
61	for (let n = 0; n < this.length; n += 2) {
62		str += String.fromCharCode(parseInt(this.substr(n, 2), 16));
63	}
64	return str;
65}
66
67// async function sha256(str) {
68//     return crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(str)).then(buf => {
69//       return Array.prototype.map.call(new Uint8Array(buf), x=>(('00'+x.toString(16)).slice(-2))).join('');
70//     });
71// }
72
73async function sha1(str) {
74	const enc = new TextEncoder();
75	const hash = await crypto.subtle.digest('SHA-1', enc.encode(str));
76	return Array.from(new Uint8Array(hash))
77		.map(v => v.toString(16).padStart(2, '0'))
78		.join('');
79}
80
81async function sha512(str) {
82	let hasher = new Sha512();
83	let bytes = new TextEncoder().encode(str);
84	hasher.process(bytes);
85	hasher.finish();
86	let hashedArray = Array.from(new Uint8Array(hasher.result));
87	return hashedArray.map(b => b.toString(16).padStart(2, '0')).join('');
88	// return crypto.subtle.digest("SHA-512", new TextEncoder("utf-8").encode(str)).then(buf => {
89	//   return Array.prototype.map.call(new Uint8Array(buf), x=>(('00'+x.toString(16)).slice(-2))).join('');
90	// });
91}
92
93function randomString(length) {
94	let result = '';
95	let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
96	let charactersLength = characters.length;
97	for (let i = 0; i < length; i++ ) {
98		result += characters.charAt(Math.floor(Math.random() * charactersLength));
99	}
100	return result;
101}
102
103async function GET_TEMPLATE(name, isExtension) {
104	if (!isExtension) return eval(`TEMPLATE.${name}`);
105	if (window.EXTENSION_TEMPLATE == undefined) window.EXTENSION_TEMPLATE = {};
106	if (window.EXTENSION_TEMPLATE[name] != undefined) return window.EXTENSION_TEMPLATE[name];
107	let response = await GET('mustache/each/get/'+name);
108	window.EXTENSION_TEMPLATE[name] = response.results;
109	return response.results;
110}
111
112async function LOAD_CSS(url, callback) {
113	if (window.LOADED_CSS == undefined) window.LOADED_CSS = {};
114	if (window.LOADED_CSS[url] != undefined) return;
115	return new Promise(function(resolve, reject) {
116		let script = document.createElement("link");
117		script.onload = function() {
118			window.LOADED_CSS[url] = url;
119			resolve();
120			if (callback != undefined) callback();
121		}
122		script.setAttribute("rel", "stylesheet");
123		script.setAttribute("type", "text/css");
124		script.setAttribute("href", `${rootURL}${url}?${Date.now()}`);
125		document.getElementsByTagName("head")[0].appendChild(script);
126	});
127}
128
129async function APPEND_LINK_TO_HEAD(rel, url) {
130	if (window.HEAD_LINK == undefined) window.HEAD_LINK = {};
131	if (window.HEAD_LINK[url] != undefined) return;
132	return new Promise(function(resolve, reject) {
133		let script = document.createElement("link");
134		script.onload = function() {
135			window.HEAD_LINK[url] = url;
136			resolve();
137		}
138		script.setAttribute("rel", rel);
139		script.setAttribute("href", `${rootURL}${url}?${Date.now()}`);
140		document.getElementsByTagName("head")[0].appendChild(script);
141	});
142}
143
144async function LOAD_JS(url, callback) {
145	if (window.LOADED_JS == undefined) window.LOADED_JS = {};
146	if (window.LOADED_JS[url] != undefined) return;
147	return new Promise(function(resolve, reject) {
148		let script = document.createElement("script");
149		script.onload = function(event) {
150			window.LOADED_JS[url] = url;
151			resolve();
152			if (callback != undefined) callback();
153		}
154
155		script.onerror = function(event){
156			reject(event);
157		}
158		script.setAttribute("type", "text/javascript");
159		script.setAttribute("src", `${rootURL}${url}?${Date.now()}`);
160		document.getElementsByTagName("head")[0].appendChild(script);
161	});
162}
163
164async function LOAD_JS_EXTENSION(extension, url, callback) {
165	if (window.LOADED_JS == undefined) window.LOADED_JS = {};
166	if (window.LOADED_JS[url] != undefined) return;
167	try {
168		let temp = url.split('/');
169		temp = temp[temp.length-1]
170		let name = temp.split('.');
171		name = name[0];
172		eval(name);
173		return;
174	} catch {
175	}
176	return new Promise(function(resolve, reject) {
177		let script = document.createElement("script");
178		script.onload = function() {
179			window.LOADED_JS[url] = url;
180			resolve();
181			if (callback != undefined) callback();
182		}
183		script.setAttribute("type", "text/javascript");
184		script.setAttribute("src", `${rootURL}share/${extension}/js/${url}?${Date.now()}`);
185		document.getElementsByTagName("head")[0].appendChild(script);
186	});
187}
188
189async function GET_JS_EXTENSION(extension, name, callback) {
190	try {
191		let temp = eval(name);
192		if (typeof temp == 'function') return temp;
193	} catch (error) {
194	}
195	return new Promise(function(resolve, reject) {
196		let script = document.createElement("script");
197		let urls = JS_EXTENSION[extension];
198		if (urls == undefined) return;
199		let url = JS_EXTENSION[extension][name];
200		if (url == undefined) return;
201		if (window.LOADED_JS == undefined) window.LOADED_JS = {};
202		let path = `share/${extension}/js/${url}`;
203		script.onload = function() {
204			window.LOADED_JS[path] = path;
205			resolve(eval(name));
206			if (callback != undefined) callback();
207		}
208		script.onerror = function() {
209			resolve(undefined);
210		}
211		script.setAttribute("type", "text/javascript");
212		script.setAttribute("src", `${rootURL}${path}?${Date.now()}`);
213		document.getElementsByTagName("head")[0].appendChild(script);
214	});
215}
216
217async function INIT_STATE() {
218	window.addEventListener('popstate', async (event) => {
219		await RENDER_STATE(event);
220	});
221}
222
223async function GET_CURRENT_PAGE(event) {
224	if (event == undefined) event = history;
225	let page = main.pageIDDict[event.state.PAGE];
226	return page
227}
228
229
230async function RENDER_SARFUNKEL_STATE(event) {
231	if (event == undefined) event = history;
232	let query = new URLSearchParams(window.location.search);
233	let pageID = query.get('p');
234	let page = main.pageIDDict[pageID];
235	let isDynamic = query.get('dynamic');
236	if (page == undefined && isDynamic == undefined) return;
237	if (page == undefined) {
238		page = new DynamicModelPage(main, undefined, query.get('m'), query.get('e'));
239		await page.onCreate();
240	}
241	if (page.menu) {
242		await page.menu.handleHighlight()
243	} else if (page.menu == undefined) {
244		let tabPage = main.pageIDDict[page.tabPageID];
245		if (tabPage && tabPage.menu) {
246			await tabPage.menu.handleHighlight();
247		}
248	}
249
250	await SHOW_LOADING_DIALOG(async () => {
251		await page.renderByURL(query);
252	});
253}
254
255function CLEAR_HIGHLIGHT_MENU() {
256	let menuItems = main.home.dom.menuBar.querySelectorAll('.menuItem');
257	for (let item of menuItems) {
258		item.classList.remove('highlightMenu');
259	}
260}
261
262async function RENDER_STATE(event) {
263	if (event == undefined) event = history;
264	let query = new URLSearchParams(window.location.search);
265	let state = event.state;
266	CLEAR_HIGHLIGHT_MENU();
267	if (query.get('sfk') != null) return await RENDER_SARFUNKEL_STATE(event);
268	let pageID = query.get('pageID');
269	if (pageID != null) {
270		state = query.get('state');
271		state= JSON.parse(state.b64decode());
272	}
273	let pageName = query.get('p');
274	if ((pageName == null && pageID == null) || state == null) {
275		if (main.homeMenu != undefined) {
276			await main.homeMenu.dom.menu.click();
277		} else {
278			await main.render();
279		}
280		return;
281	}
282	let page = main.pageIDDict[state.PAGE];
283	if (page == undefined) {
284		REDIRECT('backend');
285		return;
286	}
287	try {
288		await RENDER_NAVIGATOR(state);
289	} catch (error) {
290		console.error(error);
291	}
292	if (state.isInit != undefined && state.isInit) {
293		let isSubMenu = false;
294		let menuDetail = main.menuDict[page.pageID];
295		let parentID;
296		if (menuDetail && menuDetail.parent) {
297			parentID = menuDetail.parent;
298			isSubMenu = true;
299		}
300		if (state.isRenderFromTab) {
301			menuDetail = main.menuDict[state.tabPageID];
302			if (menuDetail && menuDetail.parent) {
303				parentID = menuDetail.parent;
304				isSubMenu = true;
305			}
306		}
307		let subMenu = main.subMenu[page.pageID];
308		if (subMenu != undefined) subMenu.isSubMenu = true;
309
310		if (menuDetail && !isSubMenu && subMenu != undefined) {
311			for (let item of subMenu) {
312				if (!item.html.classList.contains("hidden")) {
313					let subPage = main.pageIDDict[item.data.pageID];
314					await subPage.highlightMenu(item.dom.menu, isSubMenu, false);
315					await page.highlightMenu(menuDetail.menu.dom.menu, false, false);
316					main.selectedSubMenu.push(item.dom.menu);
317					await subPage.setPageState();
318					SHOW_LOADING_DIALOG(async function(){
319						await subPage.onPrepareState();
320						await subPage.render();
321					});
322					return;
323				}
324			}
325		}
326
327		if (menuDetail && isSubMenu) {
328			let parent = main.menuDict[parentID];
329			if (parent) await page.highlightMenu(parent.menu.dom.menu, false, true);
330			await page.highlightMenu(menuDetail.menu.dom.menu, isSubMenu, !isSubMenu);
331			if (parent) main.selectedMenu.push(parent.menu.dom.menu);
332			main.selectedSubMenu.push(menuDetail.menu.dom.menu);
333		} else if (menuDetail) {
334			await page.highlightMenu(menuDetail.menu.dom.menu, false, false);
335			main.selectedMenu.push(menuDetail.menu.dom.menu);
336		}
337
338		SHOW_LOADING_DIALOG(async function(){
339			await page.onPrepareState();
340			await page.render(state);
341		});
342	} else {
343		SHOW_LOADING_DIALOG(async function(){
344			await page.onPrepareState();
345			await page.renderState(state);
346			await page.renderOtherState(state);
347		});
348	}
349	// await RENDER_MOST_USED(page);
350}
351
352async function GET_STATE_URL(page, data) {
353	data.PAGE = page.pageID;
354	data.PAGE_NAME = page.title;
355	state = JSON.stringify(data);
356	state = state.b64encode();
357	// let url = await sha1(`${randomString(10)}_${Date.now()}`);
358	// localStorage.setItem(url, state);
359	return window.location.pathname + `?pageID=${page.pageID}&title=${page.title}&state=${state}`;
360	// return rootURL + `?pageID=${page.pageID}&title=${page.title}&state=${state}`;
361	// return rootURL + `?p=${url}`;
362}
363
364function PUSH_SARFUNKEL_STATE(url) {
365	let current = window.location.search;
366	if (url == current) return;
367	history.pushState(undefined, '', window.location.pathname+url);
368}
369
370function REPLACE_SARFUNKEL_STATE(url) {
371	let current = window.location.search;
372	if (url == current) return;
373	history.replaceState(undefined, '', window.location.pathname+url);
374}
375
376async function PUSH_STATE(page, data, url) {
377	if(history.state){
378		let state = history.state;
379		let dataPAGE = url.split('/')[0];
380		let dataState = url.split('/')[1];
381		if(dataState == undefined && data.state == undefined) dataState = '';
382		if(state.PAGE == dataPAGE && state.state == dataState) return;
383		else if(state.PAGE == dataPAGE && dataState == undefined) return;
384	}
385	data.PAGE = page.pageID;
386	data.PAGE_NAME = page.title;
387	state = JSON.stringify(data)
388	state = state.b64encode()
389
390	let pathName = "";
391	// localStorage.setItem(url, state);
392	if (!data.isLegacy) {
393		let parameter = `?pageID=${page.pageID}&title=${page.title}&state=${state}`
394		pathName = window.location.pathname + parameter;
395	} else {
396		// url = await sha1(`${randomString(10)}_${Date.now()}`);
397		url = `${randomString(10)}_${Date.now()}`.hashCode();
398		pathName = window.location.pathname + '?p='+url;
399	}
400	history.pushState(data, '', pathName);
401	await RENDER_NAVIGATOR(data);
402}
403
404async function PUSH_SIMPLE_STATE(data, url = '/'){
405	let oldURL = GET_PARAMS_FROM_URL();
406	let length = Object.keys(data).length;
407	let count = 0;
408	for(let i in data){
409		if(oldURL[i] != undefined && oldURL[i] == data[i]) count = count + 1;
410	}
411	if(count == length) return;
412	let params = [];
413	for (let i in data) {
414		params.push(`${i}=${data[i]}`);
415	}
416	url = url + '?' + params.join('&');
417	history.pushState(data, '', url);
418}
419
420function GET_PARAMS_FROM_URL(){
421	const params = new URLSearchParams(location.search);
422	let data = {};
423	let keys = params.keys();
424	for (let key of keys) {
425		data[key] = params.get(key);
426	}
427	return data;
428}
429
430async function CREATE_MENU(pageID, name, icon, isSubMenu, isImage, hasAdd=false, addCallBack=undefined) {
431	if (isImage == undefined) isImage = false;
432	let iconDict = {};
433	if (!isImage) iconDict = await CREATE_SVG_ICON(icon);
434	else iconDict = await CREATE_IMAGE_ICON(icon);
435	let menu = {'name': name, 'isSVG': iconDict.isSVG, 'icon': iconDict.icon, 'hasAdd': hasAdd, 'isMobile': isMobile()};
436	let tag;
437	if (isSubMenu == undefined) isSubMenu = false;
438	let page = main.pageIDDict[pageID];
439	if (page == undefined) return;
440	let url = await page.getPageStateURL();
441	menu.url = url;
442	menu.pageID = pageID;
443	if (isSubMenu) tag = new DOMObject(TEMPLATE.SubMenuItem, menu);
444	else  tag = new DOMObject(TEMPLATE.MenuItem, menu);
445
446	main.home.dom.container.onclick = function(){
447		main.home.dom.subMenuContainer.hide();
448	}
449	tag.dom.link.onclick = async function(e) {
450		e.preventDefault();
451	}
452	if (!isSubMenu) {
453		tag.dom.menu.addEventListener('click', async function(e) {
454			e.preventDefault();
455			if (tag.hasChild) {
456				// await page.highlightMenu(tag.dom.menu, isSubMenu, true);
457				main.selectedMenu.push(tag.dom.menu);
458				main.home.dom.menuName.html(name);
459				await APPEND_SUB_MENU(main.subMenu[pageID]);
460				main.home.dom.subMenuContainer.show();
461
462			} else {
463				await page.highlightMenu(tag.dom.menu, isSubMenu, false);
464				main.selectedMenu.push(tag.dom.menu);
465				if (isMobile()) main.home.dom.menuContainer.classList.add('hidden');
466				await page.setPageState();
467				SHOW_LOADING_DIALOG(async function(){
468					await page.onPrepareState();
469					await page.render();
470				});
471				main.home.dom.subMenuContainer.hide();
472				await setMostUsed(page);
473				// await RENDER_MOST_USED(page);
474			}
475			await RENDER_NAVIGATOR();
476		});
477	} else {
478		tag.dom.menuButton.addEventListener('click', async function(e) {
479			e.preventDefault();
480			if (isMobile()) main.home.dom.menuContainer.classList.add('hidden');
481			await page.highlightMenu(tag.dom.menu, isSubMenu, false);
482			main.selectedSubMenu.push(tag.dom.menu);
483			await page.setPageState();
484			SHOW_LOADING_DIALOG(async function(){
485				await page.onPrepareState();
486				await page.render();
487			});
488			main.home.dom.subMenuContainer.hide();
489			await RENDER_NAVIGATOR();
490			await setMostUsed(page);
491			// await RENDER_MOST_USED(page);
492		});
493		if(tag.dom.add){
494			if(!CHECK_PERMISSION_USER(page.extension, page.role, ['WRITE'])) tag.dom.add.remove();
495			else{
496				tag.dom.add.addEventListener('click', async function(e) {
497					if (isMobile()) main.home.dom.menuContainer.classList.add('hidden');
498					await page.highlightMenu(tag.dom.menu, isSubMenu, false);
499					main.selectedSubMenu.push(tag.dom.menu);
500					SHOW_LOADING_DIALOG(async function(){
501						await page.onPrepareState();
502						if(addCallBack == undefined) await page.renderView(page.model);
503						else await addCallBack(page);
504					});
505					main.home.dom.subMenuContainer.hide();
506					await setMostUsed(page);
507				});
508			}
509		}
510	}
511
512	async function setMostUsed(page){
513		if(localStorage.getItem('mostUsed') == null) localStorage.setItem('mostUsed', JSON.stringify({}));
514		let mostUsed = JSON.parse(localStorage.getItem('mostUsed'));
515		if(mostUsed[page.pageID] == undefined){
516			mostUsed[page.pageID] = {};
517			mostUsed[page.pageID].count = 0;
518			mostUsed[page.pageID].pageID = page.pageID;
519		}
520		mostUsed[page.pageID].title = page.title;
521		mostUsed[page.pageID].extension = page.extension;
522		mostUsed[page.pageID].count += 1;
523		localStorage.setItem('mostUsed', JSON.stringify(mostUsed));
524	}
525	return tag;
526}
527
528async function RENDER_NAVIGATOR(config){
529	let isSetNavigator = true;
530	if(config != undefined && main.pageIDDict[config.PAGE]){
531		if(main.pageIDDict[config.PAGE].isSetNavigator == undefined) main.pageIDDict[config.PAGE].isSetNavigator = true;
532		isSetNavigator = main.pageIDDict[config.PAGE].isSetNavigator;
533		if(!isSetNavigator){
534			let navigator = main.personalBar.home.dom.navigator;
535			navigator.html('');
536			return;
537		}
538	}
539	let state = history.state;
540	if (config != undefined) state = config;
541	if (main.personalBar != undefined && state != undefined && state.PAGE_NAME != undefined) {
542		let navigator = main.personalBar.home.dom.navigator;
543		navigator.html('');
544		let parent = main.pageIDDict[state.PAGE].parent;
545		if(parent.parentID == undefined && parent.menu != undefined){
546			let name = parent.menu.data.name;
547			if(main.pageIDDict[state.PAGE].referencePage != undefined){
548				let referencePage = main.pageIDDict[state.PAGE].referencePage;
549				let page = main.pageIDDict[referencePage];
550				name = page.parent.menu.data.name;
551			}
552			navigator.html(new DOMObject(`<div class="" localize>${name}</div>`));
553			let icon = await CREATE_SVG_ICON('ChevronRight');
554			navigator.append(new DOMObject(icon.icon));
555		}
556		if(main.pageIDDict[state.PAGE].referencePage != undefined){
557			let referencePage = main.pageIDDict[state.PAGE].referencePage;
558			let page = main.pageIDDict[referencePage];
559			let button = new DOMObject(`<div class="item" localize>${page.title}</div>`);
560			navigator.append(button);
561			button.html.onclick = async function(){
562				await page.render(state);
563				if (state.state) await page.setPageState();
564				else page.setPageState(state);
565			}
566		}else{
567			let button = new DOMObject(`<div class="item" localize>${state.PAGE_NAME}</div>`);
568			navigator.append(button);
569			button.html.onclick = async function(){
570				let page = main.pageIDDict[state.PAGE];
571				// if(page.referencePage){
572				// 	page = main.pageIDDict[page.referencePage];
573				// 	await page.render();
574				// 	page.home.dom.menuList[event.state.PAGE].onclick();
575				// }else
576				await page.render(state);
577				if (state.state) await page.setPageState();
578				else page.setPageState(state);
579				// RENDER_STATE();
580				// main.pageIDDict[state.PAGE].render();
581			}
582		}
583
584		// if(parent.parentPageID != undefined && parent.parentID == undefined && parent.menu != undefined){
585		// 	navigator.html(new DOMObject(`<div class="" localize>${parent.parentPageID}</div>`));
586		// 	let icon = await CREATE_SVG_ICON('ChevronRight');
587		// 	navigator.append(new DOMObject(icon.icon));
588		// 	if(parent.parentID == undefined && parent.menu != undefined){
589		// 		let button = new DOMObject(`<div class="item" localize>${parent.menu.data.name}</div>`)
590		// 		navigator.append(button);
591		// 		button.html.onclick = async function(){
592		// 			console.log(config);
593		// 			await parent.render();
594		// 			await parent.setPageState();
595		// 		}
596		// 		let icon = await CREATE_SVG_ICON('ChevronRight');
597		// 		navigator.append(new DOMObject(icon.icon));
598		// 	}
599		// 	let button = new DOMObject(`<div class="" localize>${state.PAGE_NAME}</div>`);
600		// 	navigator.append(button);
601		// }else{
602		// 	if(parent.parentID == undefined && parent.menu != undefined){
603		// 		navigator.html(new DOMObject(`<div class="" localize>${parent.menu.data.name}</div>`));
604		// 		let icon = await CREATE_SVG_ICON('ChevronRight');
605		// 		navigator.append(new DOMObject(icon.icon));
606		// 	}
607		// 	let button = new DOMObject(`<div class="item" localize>${state.PAGE_NAME}</div>`);
608		// 	navigator.append(button);
609		// 	button.html.onclick = async function(){
610		// 		let page = main.pageIDDict[state.PAGE];
611		// 		if(page.referencePage){
612		// 			page = main.pageIDDict[page.referencePage];
613		// 			await page.render();
614		// 			page.home.dom.menuList[state.PAGE].onclick();
615		// 		}else await page.render();
616		// 		await page.setPageState();
617		// 		// RENDER_STATE();
618		// 		// main.pageIDDict[state.PAGE].render();
619		// 	}
620		// }
621	}
622	if(state == undefined) return;
623	if (main.personalBar != undefined && state != undefined && state.title != undefined){
624		let navigator = main.personalBar.home.dom.navigator;
625		let icon = await CREATE_SVG_ICON('ChevronRight');
626		navigator.append(new DOMObject(icon.icon));
627		if(state.data == undefined){
628			navigator.append(new DOMObject(`<div class="" localize>Add</div>`));
629		}else{
630			if(state.isView) navigator.append(new DOMObject(`<div class="" localize>View</div>`));
631			else navigator.append(new DOMObject(`<div class="" localize>Edit</div>`));
632		}
633	} else if (main.pageIDDict[state.PAGE] != undefined && main.pageIDDict[state.PAGE].referencePage != undefined){
634		let navigator = main.personalBar.home.dom.navigator;
635		let icon = await CREATE_SVG_ICON('ChevronRight');
636		navigator.append(new DOMObject(icon.icon));
637		navigator.append(new DOMObject(`<div class="" localize>${main.pageIDDict[state.PAGE].title}</div>`));
638	}
639}
640
641async function RENDER_MOST_USED(page){
642	if(localStorage.getItem('mostUsed') == null) return;
643	let mostUsed = JSON.parse(localStorage.getItem('mostUsed'));
644	let mostUsedList = Object.keys(mostUsed).map(function(key) {
645		return [key, mostUsed[key]];
646	});
647	mostUsedList.sort(function(a, b) {
648		return b[1].count - a[1].count;
649	});
650	if(page.home == undefined) return;
651	page.home.dom.mostUsed.html('');
652	let extension = {};
653	for(let i in mostUsedList){
654		if(extension[mostUsedList[i][1].extension] == undefined) extension[mostUsedList[i][1].extension] = [];
655		extension[mostUsedList[i][1].extension].push(mostUsedList[i][1]);
656		if(i == 4) break;
657	}
658	for(let i in extension){
659		let title = `${i.charAt(0).toUpperCase()}${i.substring(1, i.length)}`;
660		title = `<div class="bold" style="padding:10px 15px;user-select:none;text-decoration:underline;">${title}</div>`;
661		if(i != 'undefined') page.home.dom.mostUsed.append(title);
662		for(let j in extension[i]){
663			let onclickEvent = `RENDER_MOST_USED_PAGE(main.pageIDDict['${extension[i][j].pageID}'])`;
664			let menu = {
665				onclickEvent: onclickEvent,
666				name: main.pageIDDict[extension[i][j].pageID].title,
667			}
668			let domObject = new DOMObject(TEMPLATE.MostUsedItem, menu);
669			if(i == 'undefined') domObject.html.style = 'padding:10px 15px;';
670			page.home.dom.mostUsed.append(domObject);
671		}
672	}
673}
674
675async function RENDER_MOST_USED_PAGE(page){
676	await page.setPageState();
677	await SHOW_LOADING_DIALOG(async () => {
678		await page.onPrepareState();
679		await page.render();
680	});
681	// await RENDER_MOST_USED(page);
682}
683
684let LOADING_DIALOG_DEPTH = 0;
685async function SHOW_LOADING_DIALOG(callback){
686	LOADING_DIALOG_DEPTH += 1;
687	main.home.dom.loading.classList.add('!z-[99999]');
688	main.home.dom.loading.show();
689	try{
690		if(callback != undefined) await callback();
691	}catch(e){
692		console.error(e);
693		SHOW_ALERT_DIALOG(e);
694	}finally{
695		LOADING_DIALOG_DEPTH -= 1;
696		if(LOADING_DIALOG_DEPTH <= 0){
697			LOADING_DIALOG_DEPTH = 0;
698			main.home.dom.loading.hide();
699		}
700	}
701}
702
703function FORCE_HIDE_LOADING_DIALOG(){
704	LOADING_DIALOG_DEPTH = 0;
705	main.home.dom.loading.hide();
706}
707
708async function SHOW_PRE_LOADING_DIALOG(callback){
709	let body = document.querySelector('body');
710	let content = `<div class="abstract_dialog hidden" rel="loading">
711			<div class="abstract_dialog_container abstract_loading">
712				<div class="flex-column gap-20px">
713					<div class="flex center"><img src="{{{rootURL}}}share/icon/loading.gif"></div>
714					<div localize>Loading</div>
715				</div>
716			</div>
717		</div>`;
718	let dialog = new DOMObject(content, rootURL);
719	body.appendChild(dialog.html);
720	dialog.dom.loading.show();
721	if(callback != undefined) await callback();
722	dialog.dom.loading.hide();
723}
724
725async function CREATE_IMAGE_ICON(path) {
726	return {'isSVG': false, 'icon': path};
727}
728
729async function CREATE_SVG_ICON(name) {
730	if (Object.keys(ICON).length == 0) ICON = await getMustacheIcon();
731	if (name == undefined) return {'isSVG': true, 'icon': ''};
732	let items = name.split('.');
733	let template = JSON.parse(JSON.stringify(ICON));
734	for (let item of items) {
735		if (template[item] != undefined) template = template[item]
736		else break
737	}
738	if (typeof template == 'object') template = '';
739	return {'isSVG': true, 'icon': template};
740}
741
742async function APPEND_SUB_MENU(subMenu) {
743	main.home.dom.subMenuBar.html('');
744	for(let i in subMenu){
745		subMenu[i].isSubMenu = true;
746		main.home.dom.subMenuBar.append(subMenu[i]);
747		subMenu[i].renderLocalize();
748	}
749}
750
751function GET_PAGE_NAME(label) {
752	return label.replace(/ /g, '') + 'Page';
753}
754
755function isMobile() {
756	return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase());
757}
758
759function isTablet() {
760	return /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))
760/.test(navigator.userAgent.toLowerCase());
761}
762
763function swapArrayIndex(array, origin, destination) {
764	let temp = JSON.parse(JSON.stringify(array));
765	let value = temp[origin];
766	array.splice(origin, 1)
767	array.splice(destination, 0, value);
768}
769
770function getScheduleEventType(eventType) {
771	if (GLOBAL.SCHEDULE_EVENT_TYPE == undefined) return;
772	return GLOBAL.SCHEDULE_EVENT_TYPE[eventType];
773}
774
775function getNotificationEventType(eventType) {
776	if (GLOBAL.NOTIFICATION_EVENT_TYPE == undefined) return;
777	return GLOBAL.NOTIFICATION_EVENT_TYPE[eventType];
778}
779
780async function OPEN_FILE(blob, fileName) {
781	let objectURL = window.URL.createObjectURL(blob);
782	window.open(objectURL);
783}
784
785async function LOAD_FILE(filePath) {
786	let result = null;
787	let xmlhttp = new XMLHttpRequest();
788	xmlhttp.open("GET", filePath, false);
789	xmlhttp.send();
790	if (xmlhttp.status==200) {
791		result = xmlhttp.responseText;
792	}
793	return result;
794}
795
796function arrayBufferToBase64(buffer) {
797	let binary = '';
798	let bytes = new Uint8Array(buffer);
799	let len = bytes.byteLength;
800	for (let i = 0; i < len; i++) {
801		binary += String.fromCharCode(bytes[ i ]);
802	}
803	return btoa(binary);
804}
805
806function uint8ArrayToBase64(bytes) {
807	let binary = '';
808	let len = bytes.byteLength;
809	for (let i = 0; i < len; i++) {
810		binary += String.fromCharCode(bytes[ i ]);
811	}
812	return btoa(binary);
813}
814
815function arrayBufferFromBase64(text) {
816	let array = Uint8Array.from(atob(text), c => c.charCodeAt(0));
817	return array.buffer;
818}
819
820async function REDIRECT(url) {
821	location.href = `${rootURL}${url}`;
822}
823
824async function GET_PICTURE_FROM_CAMERA() {
825
826}
827
828async function SHOW_ALERT_DIALOG(text, callback){
829	let dialog = new DOMObject(TEMPLATE.AlertDialog, {text: text});
830	main.home.dom.alertDialog.html(dialog);
831
832	let body = document.getElementsByTagName('body')[0];
833	body.style.overflow = 'hidden';
834	dialog.html.style.top = `${window.pageYOffset}px`;
835
836	dialog.dom.confirm.onclick = async function(){
837		await CLOSE_ALERT_DIALOG();
838		if(callback != undefined) callback();
839	}
840	return dialog;
841}
842
843async function SHOW_CONFIRM_DIALOG(text, confirmCallback, cancelCallback){
844
845	let dialog = new DOMObject(TEMPLATE.ConfirmDialog, {text: text, isForm: false});
846	if (typeof text == 'object') {
847		dialog.dom.body.innerHTML = "";
848		dialog.dom.body.appendChild(text.html);
849	}
850	main.home.dom.alertDialog.html(dialog);
851
852	let body = document.getElementsByTagName('body')[0];
853	body.style.overflow = 'hidden';
854	dialog.html.style.top = `${window.pageYOffset}px`;
855
856	dialog.dom.confirm.onclick = async function(){
857		await CLOSE_ALERT_DIALOG();
858		if(confirmCallback != undefined) confirmCallback();
859	}
860	dialog.dom.cancel.onclick = async function(){
861		await CLOSE_ALERT_DIALOG();
862		if(cancelCallback != undefined) cancelCallback();
863	}
864	return dialog;
865}
866
867async function SHOW_FINISHED_DIALOG(text, callback){
868	let dialog = new DOMObject(TEMPLATE.FinishedDialog, {text: text, isForm: false});
869	main.home.dom.alertDialog.html(dialog);
870
871	let body = document.getElementsByTagName('body')[0];
872	body.style.overflow = 'hidden';
873	dialog.html.style.top = `${window.pageYOffset}px`;
874
875	dialog.dom.confirm.onclick = async function(){
876		await CLOSE_ALERT_DIALOG();
877		if(callback != undefined) callback();
878	}
879	return dialog;
880}
881
882async function SHOW_CONFIRM_DELETE_DIALOG(text, confirmCallback, cancelCallback){
883	let dialog = new DOMObject(TEMPLATE.ConfirmDeleteDialog, {text: text, isForm: false});
884	main.home.dom.alertDialog.html(dialog);
885
886	let body = document.getElementsByTagName('body')[0];
887	body.style.overflow = 'hidden';
888	dialog.html.style.top = `${window.pageYOffset}px`;
889
890	dialog.dom.confirm.onclick = async function(){
891		await CLOSE_ALERT_DIALOG();
892		if(confirmCallback != undefined) confirmCallback();
893	}
894	dialog.dom.cancel.onclick = async function(){
895		await CLOSE_ALERT_DIALOG();
896		if(cancelCallback != undefined) cancelCallback();
897	}
898	return dialog;
899}
900
901async function SHOW_INFORMATION_DIALOG(text, callback){
902	let dialog = new DOMObject(TEMPLATE.InformationDialog, {text: text, isForm: false});
903	main.home.dom.alertDialog.html(dialog);
904
905	let body = document.getElementsByTagName('body')[0];
906	body.style.overflow = 'hidden';
907	dialog.html.style.top = `${window.pageYOffset}px`;
908
909	dialog.dom.confirm.onclick = async function(){
910		await CLOSE_ALERT_DIALOG();
911		if(callback != undefined) callback();
912	}
913	return dialog;
914}
915
916async function SHOW_BLANK_DIALOG(title, text, color, cancelCallback){
917	let dialog = new DOMObject(TEMPLATE.BlankDialog, {title: title, text: text, color: color, isForm: false});
918	main.home.dom.alertDialog.html(dialog);
919
920	let body = document.getElementsByTagName('body')[0];
921	body.style.overflow = 'hidden';
922	dialog.html.style.top = `${window.pageYOffset}px`;
923
924	dialog.dom.cancel.onclick = async function(){
925		await CLOSE_ALERT_DIALOG();
926		if(cancelCallback != undefined) cancelCallback();
927	}
928	return dialog;
929}
930
931async function SHOW_IMAGE_PREVIEW(path){
932	let dialog = new DOMObject(TEMPLATE.ImagePreview, {path});
933	main.home.dom.alertDialog.html(dialog);
934	let body = document.getElementsByTagName('body')[0];
935	body.style.overflow = 'hidden';
936	dialog.html.style.top = `${window.pageYOffset}px`;
937
938	dialog.dom.close.onclick = async function(){
939		await CLOSE_ALERT_DIALOG();
940	}
941	return dialog;
942}
943
944async function CLOSE_ALERT_DIALOG(){
945	main.home.dom.alertDialog.html('');
946	let body = document.getElementsByTagName('body')[0];
947	body.style.overflowY = 'auto';
948}
949
950function START_PAGE_AFTER_LOAD(start){
951	document.addEventListener("DOMContentLoaded", function(event){
952		if(isPreload){
953			preloader = new Preloader();
954			preloader.preload(preloadJS, preloadCSS).then(function(){
955				start();
956			});
957		}else{
958			start();
959		}
960	});
961}
962
963function SHOW_LOCATION_VIEWER(positions, labels, colors, callbacks){
964	function findCenter(positions) {
965		const lats = positions.map(m => m[1]);
966		const lngs = positions.map(m => m[0]);
967		return {
968		  lat: (Math.min(...lats) + Math.max(...lats)) / 2,
969		  lng: (Math.min(...lngs) + Math.max(...lngs)) / 2
970		};
971	}
972
973	let center = findCenter(positions)
974	let dialog = new DOMObject(TEMPLATE.LocationPickerDialog, {text: "Position", isForm: false, height: `calc(90vh - 97px)`, width: "90vw"});
975
976	const map = L.map(dialog.dom.body).setView([center.lat, center.lng], 14);
977	L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
978	map._onResize();
979
980	const markersGroup = L.layerGroup();
981	map.addLayer(markersGroup);
982
983	let template = `<svg width="36" height="36" viewBox="0 0 24 24"><path fill="{{color}}" d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /></svg>`
984
985	markersGroup.clearLayers();
986	for (let i = 0; i < positions.length; i++) {
987		let marker = L.marker([positions[i][1], positions[i][0]], {
988			icon: L.divIcon({
989				className: "marker-icon",
990				html: Mustache.render(template, {color: colors[i]}),
991				iconSize: [36, 36],
992				iconAnchor: [18, 36],
993			})
994		}).addTo(markersGroup);
995		// marker.bindTooltip(labels[i], {permanent: true, direction: 'bottom', opacity: 1}).openTooltip();
996		marker.on('click', function(e) {
997			callbacks[i]();
998			CLOSE_ALERT_DIALOG();
999		});
1000	}
1001
1002	main.home.dom.alertDialog.html(dialog);
1003	dialog.dom.container.style.maxWidth = '90vw';
1004	dialog.dom.container.style.maxHeight = '90vh';
1005	dialog.dom.container.style.top = '5vh';
1006	// dialog.dom.container.style.maxWidth = '80vw';
1007
1008	let body = document.getElementsByTagName('body')[0];
1009	body.style.overflow = 'hidden';
1010	dialog.html.style.top = `${window.pageYOffset}px`;
1011	dialog.dom.title.html('Location Viewer');
1012	dialog.dom.cancel.html('Close');
1013	dialog.dom.confirm.hide();
1014	dialog.dom.cancel.onclick = async function(){
1015		await CLOSE_ALERT_DIALOG();
1016	}
1017	return dialog;
1018}
1019
1020async function getDateRangeInput() {
1021	let timeType = 'date';
1022	let now = new Date();
1023	let view = new DOMObject(await TEMPLATE.get("DateRangeInput"), {rootURL});
1024	let startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6);
1025	let endDate = now;
1026
1027	function setDate(startDate, endDate) {
1028		view.dom.start.value = `${startDate.getFullYear()}-${String(startDate.getMonth()+1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`;
1029		view.dom.end.value = `${endDate.getFullYear()}-${String(endDate.getMonth()+1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`;
1030		let formatter = new Intl.DateTimeFormat(LANGUAGE, {year: "numeric", month: "long", day: '2-digit'});
1031		view.dom.filter.innerHTML = `${formatter.format(startDate)} - ${formatter.format(endDate)}`
1032
1033		view.dom.start.max = view.dom.end.value;
1034		view.dom.end.min = view.dom.start.value;
1035	}
1036
1037	setDate(startDate, endDate);
1038	view.dom.lastSevenDays.classList.add('highlight');
1039
1040	view.dom.filterContainer.onclick = async function() {
1041		view.dom.filterContextMenu.classList.toggle('hideMenuOnMobile');
1042	}
1043
1044	function highlightMenu(tag) {
1045		let contextMenus = view.html.getElementsByClassName("contextMenu");
1046		for (let contextMenu of contextMenus) {
1047			contextMenu.classList.remove('highlight');
1048		}
1049		tag.classList.add('highlight');
1050		view.dom.customRageContainer.classList.add('hidden');
1051		view.dom.filterContextMenu.classList.remove('expanded')
1052	}
1053
1054	function getFilterData() {
1055		let result = {
1056			value: {},
1057			date: {},
1058			month: {},
1059			year: {}
1060		};
1061		let startTime = new Date(view.dom.start.value);
1062		let endTime = new Date(view.dom.end.value);
1063
1064		result.value.start = view.dom.start.value;
1065		result.value.end = view.dom.end.value;
1066
1067		result.date.start = dateToDateID(view.dom.start.value);
1068		result.date.end = dateToDateID(view.dom.end.value);
1069		result.timeType = timeType;
1070
1071		result.month.start = 12 * (startTime.getFullYear() - 1970) + (startTime.getMonth() + 1);
1072		result.month.end = 12 * (endTime.getFullYear() - 1970) + (endTime.getMonth() + 1);
1073
1074		result.year.start = startTime.getFullYear();
1075		result.year.end = endTime.getFullYear();
1076		return result
1077	}
1078
1079	view.dom.today.onclick = async function() {
1080		let now = new Date();
1081		setDate(now, now);
1082		highlightMenu(this);
1083		timeType = 'date';
1084		view.refresh();
1085	}
1086
1087	view.dom.yesterday.onclick = async function() {
1088		let now = new Date();
1089		now.setDate(now.getDate() - 1);
1090		timeType = 'date';
1091		setDate(now, now);
1092		highlightMenu(this);
1093		view.refresh();
1094	}
1095
1096	view.dom.lastSevenDays.onclick = async function() {
1097		let now = new Date();
1098		let start = new Date();
1099		start.setDate(start.getDate() - 6);
1100		timeType = 'date';
1101		setDate(start, now);
1102		highlightMenu(this);
1103		view.refresh();
1104	}
1105
1106	view.dom.lastThirtyDays.onclick = async function() {
1107		let now = new Date();
1108		let start = new Date();
1109		start.setDate(start.getDate() - 29);
1110		timeType = 'date';
1111		setDate(start, now);
1112		highlightMenu(this);
1113		view.refresh();
1114	}
1115
1116	view.dom.thisMonth.onclick = async function() {
1117		let now = new Date();
1118		let start = new Date(now.getFullYear(), now.getMonth(), 1);
1119		let end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
1120		timeType = 'date';
1121		setDate(start, end);
1122		highlightMenu(this);
1123		view.refresh();
1124	}
1125
1126	view.dom.lastMonth.onclick = async function() {
1127		let now = new Date();
1128		let start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
1129		let end = new Date(now.getFullYear(), now.getMonth(), 0);
1130		timeType = 'date';
1131		setDate(start, end);
1132		highlightMenu(this);
1133		view.refresh();
1134	}
1135
1136	view.dom.thisYear.onclick = async function() {
1137		let now = new Date();
1138		let start = new Date(now.getFullYear(), 0, 1);
1139		let end = new Date(now.getFullYear(), 12, 0);
1140		timeType = 'month';
1141		setDate(start, end);
1142		highlightMenu(this);
1143		view.refresh();
1144	}
1145
1146	view.dom.lastYear.onclick = async function() {
1147		let now = new Date();
1148		let start = new Date(now.getFullYear() - 1, 0, 1);
1149		let end = new Date(now.getFullYear() - 1, 12, 0);
1150		timeType = 'month';
1151		setDate(start, end);
1152		highlightMenu(this);
1153		view.refresh();
1154	}
1155
1156	view.dom.lastFiveYears.onclick = async function() {
1157		let now = new Date();
1158		let start = new Date(now.getFullYear() - 4, 0, 1);
1159		let end = new Date(now.getFullYear(), 12, 0);
1160		timeType = 'year';
1161		setDate(start, end);
1162		highlightMenu(this);
1163		view.refresh();
1164	}
1165
1166	view.dom.customRange.onclick = async function() {
1167		highlightMenu(this);
1168		view.dom.customRageContainer.classList.remove('hidden');
1169		view.dom.filterContextMenu.classList.add('expanded')
1170	}
1171
1172	view.dom.start.onchange = async function() {
1173		let start = new Date(view.dom.start.value);
1174		let end = new Date(view.dom.end.value);
1175		setDate(start, end);
1176		view.refresh();
1177	}
1178	view.dom.end.onchange = async function() {
1179		view.refresh();
1180	}
1181
1182	view.refresh = async function() {
1183		if (view.onchange != undefined) {
1184			let value = getFilterData();
1185			view.onchange(value);
1186		}
1187	}
1188	view.getFilterData = getFilterData;
1189	return view;
1190}
1191
1192const isInStandaloneMode = () => (window.matchMedia('(display-mode: standalone)').matches) || (window.navigator.standalone) || document.referrer.includes('android-app://');
1193
1194FormData.prototype.isEmpty = function() {
1195	return this.entries().next().done;
1196}
1197
1198function CHECK_ROOT_USER(){
1199	IS_ROOT = false;
1200	if(GLOBAL.USER.permissions.indexOf('root') != -1) IS_ROOT = true;
1201	return IS_ROOT;
1202}
1203
1204function CHECK_PERMISSION_USER(extension, roles, permissions){
1205	if(CHECK_ROOT_USER()) return true;
1206	for(let i in roles){
1207		let role = roles[i];
1208		for(let j in permissions){
1209			let permission = permissions[j];
1210			if(GLOBAL.USER.permissions.indexOf(`${extension}.${role}.${permission}`) != -1) return true;
1211		}
1212	}
1213	return false;
1214}
1215
1216async function SEND_OPERATION(operation, data) {
1217}
1218
1219
1220function GET_FILE_BY_DATA_URL(dataurl, filename){
1221	let arr = dataurl.split(','),
1222	mime = arr[0].match(/:(.*?);/)[1],
1223	bstr = atob(arr[1]),
1224	n = bstr.length,
1225	u8arr = new Uint8Array(n);
1226	while(n--){
1227		u8arr[n] = bstr.charCodeAt(n);
1228	}
1229	return new File([u8arr], filename, {type:mime});
1230}
1231
1232function GENERATE_RANDOM_HEX(length = 16) {
1233    const arr = new Uint8Array(length / 2);
1234    window.crypto.getRandomValues(arr);
1235    return Array.from(arr, byte => byte.toString(16).padStart(2, '0')).join('');
1236}
1237
1238function SHOW_TOAST(code, message = null) {
1239	message = LOCALE[message] ? LOCALE[message] : message;
1240	let toastContainer = document.getElementById('toast_alert');
1241
1242	if (!toastContainer) {
1243		toastContainer = document.createElement('div');
1244		toastContainer.id = 'toast_alert';
1245		toastContainer.className = 'fixed top-10 left-1/2 -translate-x-1/2 z-[9999] flex flex-col items-center gap-2.5';
1246		document.body.appendChild(toastContainer);
1247	}
1248
1249	const statusCode = String(code);
1250
1251	if (!message || message.trim() === "") {
1252		const statusCodeStr = String(statusCode);
1253
1254		if (['200', '201'].includes(statusCodeStr)) {
1255			message = 'ดำเนินการเสร็จสิ้นสำเร็จ';
1256		} else if (statusCodeStr === '400') {
1257			message = 'คำขอไม่ถูกต้อง กรุณาตรวจสอบข้อมูลอีกครั้ง';
1258		} else if (statusCodeStr === '401') {
1259			message = 'ไม่มีสิทธิ์เข้าถึง กรุณาเข้าสู่ระบบใหม่';
1260		} else if (statusCodeStr === '403') {
1261			message = 'ปฏิเสธการเข้าถึง คุณไม่มีสิทธิ์ในส่วนนี้';
1262		} else if (statusCodeStr === '404') {
1263			message = 'ไม่พบข้อมูลหรือหน้าที่คุณต้องการ';
1264		} else if (['500', '502', '503', '504'].includes(statusCodeStr)) {
1265			message = 'ระบบหลังบ้านเกิดข้อผิดพลาด (Server Error)';
1266		} else if (statusCodeStr === 'warning') {
1267			message = 'กรุณาระมัดระวัง ตรวจสอบความถูกต้อง';
1268		} else {
1269			message = `กำลังประมวลผลรหัสสถานะ: ${statusCodeStr}`;
1270		}
1271	}
1272
1273	let config = {
1274		bg: 'bg-blue-100',
1275		text: 'text-blue-800',
1276		iconColor: 'text-blue-500',
1277		icon: `
1278			<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
1279				<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
1280			</svg>
1281		`
1282	};
1283
1284	if (statusCode === '200') {
1285		config = {
1286			bg: 'bg-green-100',
1287			text: 'text-green-800',
1288			iconColor: 'text-green-600',
1289			icon: `
1290				<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
1291					<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
1292				</svg>
1293			`
1294		};
1295	} else if (['400', '401', '403', '404', '500'].includes(statusCode)) {
1296		config = {
1297			bg: 'bg-red-100',
1298			text: 'text-red-800',
1299			iconColor: 'text-red-600',
1300			icon: `
1301				<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
1302					<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
1303				</svg>
1304			`
1305		};
1306	} else if (statusCode === 'warning') {
1307		config = {
1308			bg: 'bg-amber-100',
1309			text: 'text-amber-800',
1310			iconColor: 'text-amber-600',
1311			icon: `
1312				<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
1313					<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
1314				</svg>
1315			`
1316		};
1317	}
1318
1319	const toastItem = document.createElement('div');
1320
1321	toastItem.className = `
1322		${config.bg} ${config.text}
1323		font-['IBMPlexSansThai'] px-6 py-3 rounded-lg shadow-md
1324		flex items-center gap-3 max-w-[450px]
1325		transition-all duration-300 ease-in-out opacity-0 -translate-y-5
1326		overflow-hidden max-h-40
1327	`;
1328
1329	toastItem.innerHTML = `
1330		<div class="${config.iconColor} flex-shrink-0">${config.icon}</div>
1331		<div class="text-sm font-normal break-words">${message}</div>
1332	`;
1333
1334	toastContainer.appendChild(toastItem);
1335
1336	setTimeout(() => {
1337		toastItem.classList.remove('opacity-0', '-translate-y-5');
1338		toastItem.classList.add('opacity-100', 'translate-y-0');
1339	}, 50);
1340
1341	setTimeout(() => {
1342		toastItem.classList.remove('opacity-100', 'translate-y-0');
1343		toastItem.classList.add('opacity-0', '-translate-y-5');
1344		setTimeout(() => {
1345			toastItem.style.maxHeight = '0px';
1346			toastItem.style.paddingTop = '0px';
1347			toastItem.style.paddingBottom = '0px';
1348			toastItem.style.marginTop = '-10px';
1349			setTimeout(() => {
1350				toastItem.remove();
1351			}, 300);
1352		}, 150);
1353	}, 3000);
1354}
1355
1356String.prototype.b64encode = function() {
1357	let text = this + '';
1358	let result = btoa(unescape(encodeURIComponent(text)));
1359	return result;
1360};
1361String.prototype.b64decode = function() {
1362	let text = this + '';
1363	return decodeURIComponent(escape(atob(text)));
1364};
1365
1366String.prototype.hexEncode = function(){
1367	let hex, i;
1368
1369	let result = "";
1370	for (i=0; i<this.length; i++) {
1371		hex = this.charCodeAt(i).toString(16);
1372		result += ("000"+hex).slice(-4);
1373	}
1374
1375	return result
1376}
1377
1378String.prototype.hexDecode = function(){
1379	let j;
1380	let hexes = this.match(/.{1,4}/g) || [];
1381	let back = "";
1382	for(j = 0; j<hexes.length; j++) {
1383		back += String.fromCharCode(parseInt(hexes[j], 16));
1384	}
1385
1386	return back;
1387}
1388
1389function BACK(callback){
1390	if(callback != undefined) callback();
1391	else history.back();
1392}
1393
1394String.prototype.hashCode = function() {
1395	let hash = 0;
1396	for (let i = 0; i < this.length; i++) {
1397		let char = this.charCodeAt(i);
1398		hash = ((hash<<5)-hash)+char;
1399		hash = hash & hash; // Convert to 32bit integer
1400	}
1401	return hash;
1402}
1403
1404Object.id = function(o) {
1405	if (window.OBJECT_ID == undefined) window.OBJECT_ID = 0;
1406	if ( typeof o.__unique__ != "undefined" ) {
1407		return o.__unique__;
1408	}
1409	Object.defineProperty(o, "__unique__", {
1410		value: ++window.OBJECT_ID,
1411		enumerable: false,
1412		writable: false
1413	});
1414	return o.__unique__;
1415};
1416
1417// Element.prototype._addEventListener = Element.prototype.addEventListener;
1418// Element.prototype.addEventListener = function(a, b, c) {
1419// 	if(!this.event) this.event = {};
1420// 	if(!this.event[a]) this.event[a] = {};
1421// 	if(!this.event[a][b]) this._addEventListener(a, b, c);
1422// 	this.event[a][b] = b;
1423// };
1424
1425function NUMBER_WITH_COMMAS(num){
1426	if(!num) return num;
1427	return num.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
1428}
1429
1430function GET_FRACTION_TO_NUMBER(faction){
1431	return parseFloat(Fraction(faction).toString());
1432}
1433
1434function GET_CURRENCY_FORMAT(value, decimal = 2){
1435	if(typeof(value) == 'string') value = GET_FRACTION_TO_NUMBER(value);
1436	return NUMBER_WITH_COMMAS(value.toFixed(decimal));
1437}

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.