/*!
* molybdenum v1.0.250619.1428
* Copyright 2025 PT Nusa Angkasa Siber
* Released under the MIT License
*/
class molybdenum{
version = '1.0.250619.1428';
_alerts = [];
_confirms = [];
_loads = [];
_dialogs = [];
lastDialog()
{
return this._dialogs.at(-1);
}
lastLoadScreenId()
{
if(this._loads.length > 0)
{
return this._loads.at(-1).id;
}
return null;
}
newElement(name = "div", options = {}){
const ele = document.createElement(name);
options.style ? ele.style = options.style : null;
options.className ? ele.className = options.className : null;
typeof options.append === 'string' ? ele.append(options.append) : options.append instanceof Element ? ele.append(options.append) : null;
typeof options.href === 'string' && ele.nodeName === "A" ? ele.setAttribute("href",options.href) : null;
return ele;
}
customElement(name = "div", options = {})
{
const par = document.createElement("div");
par.innerHTML=`<${name}>${name}>`;
const ele = par.firstChild;
options.style ? ele.style = options.style : null;
options.className ? ele.className = options.className : null;
typeof options.append === 'string' ? ele.append(options.append) : options.append instanceof Element ? ele.append(options.append) : null;
typeof options.href === 'string' && ele.nodeName === "A" ? ele.setAttribute("href",options.href) : null;
return ele;
}
loadScreen = class {
static clear()
{
$('load-screen').remove();
moly._loads = [];
}
static show(message = "", style = "sonar", target = "body")
{
if (target != "body" && ($(target).length != 1 || $(target).prevObject))
{
console.error("Target Invalid: " + JSON.stringify($(target)));
return -1;
}
let sid = Date.now() + Math.random().toString(16).slice(2);
let ls = moly.newElement("load-screen");
let lm = moly.newElement("load-message");
lm.append(message);
if (style == "dots")
{
let p = moly.newElement("chat-load")
ls.append(p);
p.append(moly.newElement("span"));
p.append(moly.newElement("span"));
p.append(moly.newElement("span"));
}
else if (style == "bar")
{
ls.append(moly.newElement("progress-loop"));
}
else
{
ls.append(moly.newElement("sonar-ping"));
}
ls.append(lm);
moly._loads.push({id: sid, screen: ls});
$(target).append(ls);
return sid;
}
static update(id=0, message = "")
{
let i = moly._loads.findIndex(e=> e.id == id)
if(i >= 0 && message.length > 0)
{
$(moly._loads[i].screen).children("load-message").html(message);
}
}
static close(id=0)
{
let i = moly._loads.findIndex(e=> e.id == id)
if(i >= 0)
{
moly._loads[i].screen.remove();
moly._loads.splice(i,1);
}
}
}
dialog = class {
static clear()
{
$('dialog-box').remove();
moly._dialogs = [];
}
static async show({ title = "Dialog Box", content = "", fetching = false, data = {} } = {})
{
let dialog =
{
id: Date.now() + Math.random().toString(16).slice(2),
screen: moly.newElement("dialog-screen"),
box: moly.newElement("dialog-box"),
titlet: moly.newElement("span"),
content: moly.newElement("div"),
beforeResolve: [],
resolve: undefined,
data: data
}
moly._dialogs.push(dialog);
dialog.screen.append(dialog.box)
let titlebar = moly.newElement("dialog-title");
let closebtn = moly.newElement("a-button");
let closeicn = moly.newElement("m-icon");
closebtn.append(closeicn)
closeicn.append("close");
closebtn.setAttribute("type","negative");
dialog.box.append(titlebar);
$(dialog.box).css("width",data.width);
$(dialog.box).css("height",data.height);
titlebar.append(dialog.titlet);
$(dialog.titlet).html(title);
titlebar.append(closebtn);
dialog.box.append(dialog.content);
dialog.content.setAttribute("id",`db${dialog.id}`);
dialog.content.style = "overflow-y: auto;"
$('body').append(dialog.screen);
setTimeout(()=>{
$(dialog.box).css("scale",1);
},5);
let loadid = moly.loadScreen.show("Memuat Konten...","sonar",`#db${dialog.id}`);
$(dialog.content).html(fetching ? await fetch(content).then(r => r.text()) : content);
moly.loadScreen.close(loadid);
return new Promise((resolve,reject)=>{
dialog.resolve = function(out)
{
moly.dialog.close(dialog.id);
resolve(out);
}
$(closebtn).click(()=>{
moly.dialog.close(dialog.id);
resolve(false);
});
});
}
static close(id="")
{
let i = moly._dialogs.findIndex(e => e.id == id); //this.screens.findIndex(e=> e.id == id)
if(i >= 0)
{
$(moly._dialogs[i].box).css("scale",0);
setTimeout(() => {
$.each(moly._dialogs[i].beforeResolve, (i,v)=>
{
typeof v == "function" ? v() : false;
});
moly._dialogs[i].screen.remove();
moly._dialogs.splice(i,1);
}, 250);
}
}
static resolve(id = 0, data)
{
let i = moly._dialogs.findIndex(e => e.id == id); //this.screens.findIndex(e=> e.id == id)
if(i >= 0)
{
moly._dialogs[i].resolve(data);
}
}
}
alert = class {
static show(title = "Perhatian", messageHTML = "", timeOut = 0)
{
let alert =
{
id: Date.now() + Math.random().toString(16).slice(2),
screen: moly.newElement("alert-screen"),
box: moly.newElement("alert-box"),
title: moly.newElement("alert-title"),
message: moly.newElement("alert-text"),
handle: moly.newElement("alert-handle")
}
alert.screen.append(alert.box);
alert.box.append(alert.title);
alert.box.append(alert.message);
alert.box.append(alert.handle);
alert.title.innerText = title;
alert.message.innerHTML = messageHTML;
alert.message.setAttribute("title", alert.message.innerText);
if (timeOut >0 ) {alert.handle.innerText = `Tunggu ${timeOut} detik.`;} else {alert.handle.innerText = "Tutup";}
moly._alerts.push(alert);
$('body').append(alert.screen);
setTimeout(() => {
$(alert.box).css("scale",1);
}, 5);
setTimeout(() => {
if ( timeOut > 0)
{
let ms = timeOut * 1000;
let intv = setInterval(()=>{
ms -= 1000;
if (ms <= 0)
{
moly.alert.close(alert.id);
clearInterval(intv);
return;
}
$(moly._alerts.findLast(()=>true).screen).find("alert-handle")[0].innerText = `Tunggu ${ms/1000} detik`;
},1000);
}
else
{
$(alert.handle).click(()=>
{
moly.alert.close(alert.id);
})
}
}, 250);
}
static close(id = "")
{
let ix = moly._alerts.findIndex(e=> e.id == id)
if (ix >= 0)
{
$(moly._alerts[ix].box).css("scale",0);
setTimeout(()=>{
moly._alerts[ix].screen.remove();
moly._alerts.splice(ix,1);
}, 250);
}
}
}
confirm = class {
static show(title = "Anda Yakin?", messageHTML = "", yesLabel = "Yes", noLabel = "No", positiveYes = null, positiveNo = null)
{
let confirm =
{
id: Date.now() + Math.random().toString(16).slice(2),
screen: moly.newElement("confirm-screen"),
box: moly.newElement("confirm-box"),
title: moly.newElement("confirm-title"),
message: moly.newElement("confirm-text"),
handle: moly.newElement("confirm-handle"),
resolve: undefined
}
let yes = moly.newElement("a-button");
let no = moly.newElement("a-button");
confirm.screen.append(confirm.box);
confirm.box.append(confirm.title);
confirm.box.append(confirm.message);
confirm.box.append(confirm.handle);
confirm.title.innerText = title;
confirm.message.innerHTML = messageHTML;
confirm.message.setAttribute("title", confirm.message.innerText);
confirm.handle.append(yes);
confirm.handle.append(no);
yes.innerText = yesLabel;
no.innerText = noLabel;
if (positiveYes == true)
{
yes.setAttribute("type","positive");
}
else if (positiveYes == false)
{
yes.setAttribute("type","negative");
}
else
{
yes.setAttribute("type","blend");
}
if (positiveNo == true)
{
no.setAttribute("type","positive");
}
else if (positiveNo == false)
{
no.setAttribute("type","negative");
}
else
{
no.setAttribute("type","blend");
}
moly._confirms.push(confirm);
$('body').append(confirm.screen);
setTimeout(() => {
$(confirm.box).css("scale",1);
}, 5);
return new Promise((resolve,reject)=>{
confirm.resolve = function(out)
{
moly.confirm.close(confirm.id);
resolve(out);
}
$(no).click(()=>{
moly.confirm.close(confirm.id);
resolve(false);
});
$(yes).click(()=>{
moly.confirm.close(confirm.id);
resolve(true);
});
});
}
static close(id ="")
{
let i = moly._confirms.findIndex(e => e.id == id); //this.screens.findIndex(e=> e.id == id)
if(i >= 0)
{
$(moly._confirms[i].box).css("scale",0);
setTimeout(() => {
moly._confirms[i].screen.remove();
moly._confirms.splice(i,1);
}, 250);
}
}
}
file = class {
static async crc32(file){
let crc32Table = new Uint32Array(256).map((t, c) => {
for (let k = 0; k < 8; k++) {
c = c & 1 ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
return c >>> 0;
});
const arrayBuffer = await file.arrayBuffer();
const buf = new Uint8Array(arrayBuffer);
let crc = 0 ^ (-1);
for (let i = 0; i < buf.length; i++) {
crc = (crc >>> 8) ^ crc32Table[(crc ^ buf[i]) & 0xFF];
}
return ((crc ^ (-1)) >>> 0).toString(16).padStart(8, '0').toUpperCase();
};
static async serialise(file = new File([],"")){
let hash = await this.crc32(file);
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onload = function(event) {
const base64Data = event.target.result.split(',')[1];
const fileData = {
name: file.name,
type: file.type,
data: base64Data,
crc32: hash
};
resolve(fileData);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
static deserialise(fileData){
const byteCharacters = atob(fileData.data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: fileData.type });
const file = new File([blob], fileData.name, { type: fileData.type });
return file;
}
}
AButton = class extends HTMLElement
{
static observedAttributes = ["disabled"];
constructor() {
super();
this.attachShadow({ mode: "open" }); // Attach Shadow DOM
this.shadowRoot.innerHTML = `