Working up to log in
This commit is contained in:
parent
5104dd8e2d
commit
18b552308b
53
assets/css/fonts.css
Normal file
53
assets/css/fonts.css
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* latin-ext */
|
||||||
|
/* @font-face {
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300 600;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/font/ss3le.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
} */
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300 600;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/fonts/ss3l.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
/* @font-face {
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300 600;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/font/ss3ile.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
} */
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300 600;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/fonts/ss3il.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* arabic */
|
||||||
|
/* @font-face {
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/font/UthmanicHafs1Ver09.woff2) format('woff2');
|
||||||
|
unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
|
||||||
|
} */
|
||||||
|
/* Material Symbols Outlined */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Material Symbols Outlined';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: block;
|
||||||
|
src: url(/assets/fonts/materials.woff2) format('woff2');
|
||||||
|
}
|
545
assets/css/molybdenum.css
Executable file
545
assets/css/molybdenum.css
Executable file
@ -0,0 +1,545 @@
|
|||||||
|
/*!
|
||||||
|
* molybdenum v1.1.0 Companion CSS
|
||||||
|
* Copyright 2025 PT Nusa Angkasa Siber
|
||||||
|
* Released under the MIT License
|
||||||
|
*/
|
||||||
|
:root {
|
||||||
|
color-scheme: light only;
|
||||||
|
--secondary-table: #1e634a;
|
||||||
|
--primary-table: #1182a4;
|
||||||
|
--primary-foreground: #050607;
|
||||||
|
--secondary-foreground: #aaaaac;
|
||||||
|
--primary-background: #fefdfc;
|
||||||
|
--secondary-background: #d9d9dc;
|
||||||
|
--semi-background: #fdfcf975;
|
||||||
|
--primary-accent: #1b5d76;
|
||||||
|
--semi-primary: #1b5d76bb;
|
||||||
|
--secondary-accent: #4eaacc;
|
||||||
|
--semi-secondary: #4eaaccbb;
|
||||||
|
--negative-accent: #ad0400;
|
||||||
|
--positive-accent: #4ecc89;
|
||||||
|
--neutral-p-accent: #5fc4ff;
|
||||||
|
--neutral-n-accent: #f6c267;
|
||||||
|
--semi-negative: #ad0400bb;
|
||||||
|
--semi-positive: #4ecc89bb;
|
||||||
|
--semi-neutral-p: #5fc4ffbb;
|
||||||
|
--semi-neutral-n: #f6c267bb;
|
||||||
|
--input-background: #ebf2f4;
|
||||||
|
--blend-color: inherit;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
font-size: 13pt;
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
}
|
||||||
|
thead
|
||||||
|
{
|
||||||
|
background-color: var(--primary-accent);
|
||||||
|
color: var(--primary-background);
|
||||||
|
position: sticky;
|
||||||
|
top: 1px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
button
|
||||||
|
{
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
padding: .125em .35em .125em .35em;
|
||||||
|
border-radius: .15em;
|
||||||
|
margin: .15em 0 .15em 0;
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
button.accented
|
||||||
|
{
|
||||||
|
background-color: var(--primary-accent);
|
||||||
|
color: var(--primary-background);
|
||||||
|
}
|
||||||
|
button.p-accent
|
||||||
|
{
|
||||||
|
background-color: var(--neutral-p-accent);
|
||||||
|
}
|
||||||
|
button.s-accent
|
||||||
|
{
|
||||||
|
background-color: var(--secondary-accent);
|
||||||
|
}
|
||||||
|
button.n-accent
|
||||||
|
{
|
||||||
|
background-color: var(--neutral-n-accent);
|
||||||
|
}
|
||||||
|
button.positive
|
||||||
|
{
|
||||||
|
background-color: var(--positive-accent);
|
||||||
|
}
|
||||||
|
button.negative
|
||||||
|
{
|
||||||
|
background-color: var(--negative-accent);
|
||||||
|
color: var(--primary-background);
|
||||||
|
}
|
||||||
|
button:hover:not(:disabled)
|
||||||
|
{
|
||||||
|
filter: brightness(1.15);
|
||||||
|
}
|
||||||
|
button:active:not(:disabled)
|
||||||
|
{
|
||||||
|
border: 1px inset var(--secondary-foreground);
|
||||||
|
filter: brightness(.95);
|
||||||
|
}
|
||||||
|
button:disabled
|
||||||
|
{
|
||||||
|
background-color: var(--secondary-foreground);
|
||||||
|
color: var(--primary-foreground)
|
||||||
|
}
|
||||||
|
table
|
||||||
|
{
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
table, table th, table td {
|
||||||
|
border-collapse: collapse;
|
||||||
|
padding: .2ch .25ch .2ch .25ch;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
table.bordered, table.bordered th , table.bordered td {
|
||||||
|
border: 1px solid var(--primary-background);
|
||||||
|
}
|
||||||
|
table.fullwidth{
|
||||||
|
width: calc(100% - 2ch);
|
||||||
|
}
|
||||||
|
table.selectable tr:not(thead tr){
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
table.selectable tr:hover:not(thead tr)
|
||||||
|
{
|
||||||
|
background-color: var(--semi-neutral-p);
|
||||||
|
}
|
||||||
|
tr:nth-child(odd):not(thead tr){
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
tr:not(thead tr){
|
||||||
|
background-color: var(--input-background);
|
||||||
|
}
|
||||||
|
table.selectable tr.selected:not(thead tr), table.selectable tr.odd.selected:not(thead tr){
|
||||||
|
background-color: var(--semi-positive);
|
||||||
|
}
|
||||||
|
table.selectablecell td:hover:not(.block)
|
||||||
|
{
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--semi-neutral-p);
|
||||||
|
}
|
||||||
|
table.selectablecell td.selected:not(.block)
|
||||||
|
{
|
||||||
|
background-color: var(--semi-positive);
|
||||||
|
}
|
||||||
|
textarea
|
||||||
|
{
|
||||||
|
min-height: 1.85em;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]), textarea, select
|
||||||
|
{
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
border-radius: .15em;
|
||||||
|
font-size: 1em;
|
||||||
|
padding-inline-start: .35em;
|
||||||
|
padding-inline-end: .35em;
|
||||||
|
width: 25ch;
|
||||||
|
outline: none;
|
||||||
|
margin-bottom: .25em;
|
||||||
|
}
|
||||||
|
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):focus-visible, textarea:focus-visible, select:focus-visible
|
||||||
|
{
|
||||||
|
border: 1px solid var(--secondary-background);
|
||||||
|
border-bottom: 1px solid var(--primary-accent);
|
||||||
|
background-color: var(--input-background);
|
||||||
|
}
|
||||||
|
input.icon
|
||||||
|
{
|
||||||
|
padding-left: 3.5ch !important;
|
||||||
|
}
|
||||||
|
input[type="password"].peek
|
||||||
|
{
|
||||||
|
padding-right: 3.5ch !important;
|
||||||
|
}
|
||||||
|
sep-bar{
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
margin-inline-start: .75ch;
|
||||||
|
margin-inline-end: .5ch;
|
||||||
|
}
|
||||||
|
sep-bar::after{
|
||||||
|
content: "|";
|
||||||
|
}
|
||||||
|
background-screen, load-screen, dialog-screen
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--semi-background);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
user-select: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
load-screen:not(body>load-screen)
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
load-screen
|
||||||
|
{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
dialog-box
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
row-gap: .75em;
|
||||||
|
position: relative;
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 90vh;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
border-radius: .25em;
|
||||||
|
padding: .5em;
|
||||||
|
transition: scale .25s;
|
||||||
|
scale: 0;
|
||||||
|
}
|
||||||
|
dialog-title
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
border-bottom: 1px solid var(--secondary-foreground);
|
||||||
|
font-weight: 500;
|
||||||
|
padding-left: .25em;
|
||||||
|
height: 1.65em;
|
||||||
|
}
|
||||||
|
load-screen.opaque
|
||||||
|
{
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
}
|
||||||
|
a, confirm-handle
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--primary-accent);
|
||||||
|
margin-inline-start: .5ch;
|
||||||
|
margin-inline-end: .5ch;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
filter: brightness(1);
|
||||||
|
}
|
||||||
|
a
|
||||||
|
{
|
||||||
|
transition: filter .35s ease, text-shadow .35s ease;
|
||||||
|
}
|
||||||
|
a:hover /*, confirm-handle:hover */
|
||||||
|
{
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .25em var(--semi-primary);
|
||||||
|
}
|
||||||
|
a:active /*, confirm-handle:active*/
|
||||||
|
{
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .75em var(--semi-primary);
|
||||||
|
}
|
||||||
|
button.notifier::after
|
||||||
|
{
|
||||||
|
content: ".";
|
||||||
|
background-color: var(--negative-accent);
|
||||||
|
color: var(--negative-accent);
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
border-radius: 1em;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: -.75em;
|
||||||
|
right: -.75em;
|
||||||
|
font-size: .3em;
|
||||||
|
}
|
||||||
|
a-button.notifier::after
|
||||||
|
{
|
||||||
|
content: ".";
|
||||||
|
background-color: var(--negative-accent);
|
||||||
|
color: var(--negative-accent);
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
border-radius: 1em;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: -.1em;
|
||||||
|
right: -1.25em;
|
||||||
|
font-size: .26em;
|
||||||
|
}
|
||||||
|
/* .ellipsis
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
} */
|
||||||
|
.noscroll
|
||||||
|
{
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
/* ANIMATIONS */
|
||||||
|
load-message
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
margin: .4em;
|
||||||
|
}
|
||||||
|
chat-load { /* POSSIBLE load-anim DEFINITION */
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat-load span { /* ALSO PUT load-anim span IF USED */
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: var(--primary-accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0.3;
|
||||||
|
animation: pulse 1.5s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat-load span:nth-child(1) {
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
chat-load span:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
chat-load span:nth-child(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.3;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sonar-ping
|
||||||
|
{ /* POSSIBLE load-anim DEFINITION */
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
border: 2px solid var(--primary-accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
animation: expandRing 1.5s infinite ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes expandRing {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skeleton-view {
|
||||||
|
background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: skeleton-loading 1.5s infinite linear;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes skeleton-loading {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
progress-loop { /* POSSIBLE load-anim DEFINITION */
|
||||||
|
width: 95%;
|
||||||
|
height: .25em;
|
||||||
|
background: var(--secondary-background);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress-loop::after { /* ALSO PUT load-anim::after IF USED */
|
||||||
|
content: "";
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--primary-accent);
|
||||||
|
position: absolute;
|
||||||
|
left: -50%;
|
||||||
|
animation: progress-loop 2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress-loop {
|
||||||
|
0% { left: -50%; }
|
||||||
|
100% { left: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
m-icon {
|
||||||
|
max-width: 1ch;
|
||||||
|
width: 1ch;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Material Symbols Outlined';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 1.3em;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
m-inline {
|
||||||
|
max-width: 1ch;
|
||||||
|
width: 1ch;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Material Symbols Outlined';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 1.25em;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
padding-bottom: .17em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
button>m-inline
|
||||||
|
{
|
||||||
|
padding-bottom: .07em;
|
||||||
|
}
|
||||||
|
m-inline.icon
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding-top: .25em;
|
||||||
|
padding-left: .2em;
|
||||||
|
max-width: 1.2ch;
|
||||||
|
width: 1.2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ALERT BOX */
|
||||||
|
alert-screen, confirm-screen
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--semi-background);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
user-select: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
alert-box, confirm-box
|
||||||
|
{
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
display: block;
|
||||||
|
width: calc(100vw - .75em);
|
||||||
|
max-width: 450px;
|
||||||
|
position: fixed;
|
||||||
|
height: 12.25em;
|
||||||
|
max-height: 12.25em;
|
||||||
|
padding: .5em;
|
||||||
|
background-color: var(--semi-background);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
-webkit-backdrop-filter: blur(5px);
|
||||||
|
border-radius: .25em;
|
||||||
|
scale: 0;
|
||||||
|
transition: scale .25s;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
confirm-box
|
||||||
|
{
|
||||||
|
height: 14.25em;
|
||||||
|
max-height: 14.25em;
|
||||||
|
}
|
||||||
|
alert-handle
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--primary-accent);
|
||||||
|
margin-inline-start: .5ch;
|
||||||
|
margin-inline-end: .5ch;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
filter: brightness(1);
|
||||||
|
}
|
||||||
|
confirm-handle
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
position: absolute;
|
||||||
|
bottom: .25em;
|
||||||
|
left: -.2em;
|
||||||
|
right: -.2em;
|
||||||
|
text-align: center;
|
||||||
|
height: 3.5em;
|
||||||
|
outline: none;
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
}
|
||||||
|
alert-title, confirm-title
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 600;
|
||||||
|
max-width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: .25em;
|
||||||
|
border-bottom: 1px solid var(--secondary-background);
|
||||||
|
}
|
||||||
|
alert-text, confirm-text
|
||||||
|
{
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 5;
|
||||||
|
line-clamp: 5;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-top: .25em;
|
||||||
|
border-bottom: 1px solid var(--secondary-background);
|
||||||
|
text-align: justify;
|
||||||
|
height: 7.75em;
|
||||||
|
margin-bottom: .25em;
|
||||||
|
}
|
BIN
assets/fonts/materials.woff2
Normal file
BIN
assets/fonts/materials.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/ss3il.woff2
Normal file
BIN
assets/fonts/ss3il.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/ss3l.woff2
Normal file
BIN
assets/fonts/ss3l.woff2
Normal file
Binary file not shown.
BIN
assets/images/bpn.webp
Normal file
BIN
assets/images/bpn.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
@ -1,3 +1,4 @@
|
|||||||
|
vernum = "1.0.250601.1020"
|
||||||
async function getJson(url,headers={}) {
|
async function getJson(url,headers={}) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
2
assets/js/jquery.min.js
vendored
Executable file
2
assets/js/jquery.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
988
assets/js/molybdenum.js
Executable file
988
assets/js/molybdenum.js
Executable file
@ -0,0 +1,988 @@
|
|||||||
|
/*!
|
||||||
|
* molybdenum v1.1.0
|
||||||
|
* Copyright 2025 PT Nusa Angkasa Siber
|
||||||
|
* Released under the MIT License
|
||||||
|
*/
|
||||||
|
class molybdenum{
|
||||||
|
version = '1.1.0';
|
||||||
|
_alerts = [];
|
||||||
|
_confirms = [];
|
||||||
|
_loads = [];
|
||||||
|
_dialogs = [];
|
||||||
|
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 = `
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: inline;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: 'Source Sans 3';
|
||||||
|
color: var(--primary-accent);
|
||||||
|
margin-inline-start: .5ch;
|
||||||
|
margin-inline-end: .5ch;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
filter: brightness(1);
|
||||||
|
transition: filter .35s ease, text-shadow .35s ease;
|
||||||
|
}
|
||||||
|
:host([disabled]) {
|
||||||
|
color: var(--secondary-foreground) !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
:host(:hover){
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .25em var(--semi-primary);
|
||||||
|
}
|
||||||
|
:host(:active){
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .75em var(--semi-primary);
|
||||||
|
}
|
||||||
|
:host([type="blend"]), :host([type="peek"])
|
||||||
|
{
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
:host([type="blend"]:hover)
|
||||||
|
{
|
||||||
|
filter: brightness(4);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
:host([type="blend"]:active)
|
||||||
|
{
|
||||||
|
filter: brightness(8);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
:host([type="negative"])
|
||||||
|
{
|
||||||
|
color: var(--negative-accent);
|
||||||
|
}
|
||||||
|
:host([type="negative"]:hover)
|
||||||
|
{
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .35em var(--semi-negative);
|
||||||
|
}
|
||||||
|
:host([type="negative"]:active)
|
||||||
|
{
|
||||||
|
filter: brightness(1.25);
|
||||||
|
text-shadow: 0 0 .65em var(--semi-negative);
|
||||||
|
}
|
||||||
|
:host([type="peek"])
|
||||||
|
{
|
||||||
|
text-shadow: none;
|
||||||
|
position: absolute !important;
|
||||||
|
padding-top: .25em;
|
||||||
|
transform: translatex(-3.5ch);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<slot></slot>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.hasAttribute("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(value) {
|
||||||
|
if (value) {
|
||||||
|
this.setAttribute("disabled", "");
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileInput = class extends HTMLElement
|
||||||
|
{
|
||||||
|
static observedAttributes = ["disabled", "accept", "multiple"];
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: "open" }); // Attach Shadow DOM
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host
|
||||||
|
{
|
||||||
|
display: inline-flex;
|
||||||
|
width: 25ch;
|
||||||
|
height: 1.7em;
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
}
|
||||||
|
div
|
||||||
|
{
|
||||||
|
font-size: .8em;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
width: calc(100%);
|
||||||
|
padding-inline-start: .75ch;
|
||||||
|
padding-inline-end: .75ch;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
this._input = document.createElement("input");
|
||||||
|
this._input.type = "file";
|
||||||
|
this._input.setAttribute("hidden","");
|
||||||
|
this._handleChange = this._handleChange.bind(this);
|
||||||
|
this._handleClick = this._handleClick.bind(this);
|
||||||
|
this._label = document.createElement("div");
|
||||||
|
}
|
||||||
|
connectedCallback()
|
||||||
|
{
|
||||||
|
this._input.addEventListener("change", this._handleChange);
|
||||||
|
this.shadowRoot.appendChild(this._input);
|
||||||
|
this.shadowRoot.appendChild(this._label);
|
||||||
|
this._label.innerText = "Click to select a file.";
|
||||||
|
this.addEventListener("click", this._handleClick);
|
||||||
|
if (this.getAttribute("accept"))
|
||||||
|
{
|
||||||
|
this._input.setAttribute("accept", this.accept);
|
||||||
|
this.accept = this.getAttribute("accept");
|
||||||
|
}
|
||||||
|
if (this.hasAttribute("multiple"))
|
||||||
|
{
|
||||||
|
this._input.setAttribute("multiple", "");
|
||||||
|
this.multiple = this.hasAttribute("multiple");
|
||||||
|
this._label.innerText = "Click to select files.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnectedCallback() {
|
||||||
|
this._input.removeEventListener("change", this._handleChange);
|
||||||
|
}
|
||||||
|
_handleChange(event)
|
||||||
|
{
|
||||||
|
if (this.hasAttribute("multiple") && this._input.files.length == 1)
|
||||||
|
{
|
||||||
|
this._label.innerText = `${this._input.files[0].name} selected.`;
|
||||||
|
}
|
||||||
|
else if (this.hasAttribute("multiple") && this._input.files.length > 1)
|
||||||
|
{
|
||||||
|
this._label.innerText = `${this._input.files.length} files selected.`
|
||||||
|
}
|
||||||
|
else if (this.hasAttribute("multiple"))
|
||||||
|
{
|
||||||
|
this._label.innerText = "Click to select files.";
|
||||||
|
}
|
||||||
|
else if (this._input.files.length == 1)
|
||||||
|
{
|
||||||
|
this._label.innerText = `${this._input.files[0].name} selected.`;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._label.innerText = "Click to select a file.";
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent("change", {bubble: true, composed: true, detail: this.files}));
|
||||||
|
}
|
||||||
|
_handleClick(event)
|
||||||
|
{
|
||||||
|
this._input.click();
|
||||||
|
}
|
||||||
|
get disabled() {
|
||||||
|
return this.hasAttribute("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(value) {
|
||||||
|
if (value) {
|
||||||
|
this.setAttribute("disabled", "");
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get files() {
|
||||||
|
return this._input.files;
|
||||||
|
}
|
||||||
|
|
||||||
|
set accept(value) {
|
||||||
|
if (value) {
|
||||||
|
this._input.accept = value;
|
||||||
|
this.setAttribute("accept", value); // Keep the attribute in sync
|
||||||
|
} else {
|
||||||
|
this._input.removeAttribute("accept");
|
||||||
|
this.removeAttribute("accept");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get accept() {
|
||||||
|
return this._input.accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
set multiple(value) {
|
||||||
|
if (value) {
|
||||||
|
this._input.setAttribute("multiple", "");
|
||||||
|
this.setAttribute("multiple", "");
|
||||||
|
this._label.innerText = "Click to select files.";
|
||||||
|
} else {
|
||||||
|
this._input.removeAttribute("multiple");
|
||||||
|
this.removeAttribute("multiple");
|
||||||
|
this._label.innerText = "Click to select a file.";
|
||||||
|
}
|
||||||
|
this._input.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
get multiple() {
|
||||||
|
return this._input.hasAttribute("multiple");
|
||||||
|
}
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
this._input.value = "";
|
||||||
|
if (this.hasAttribute("multiple"))
|
||||||
|
{
|
||||||
|
this._label.innerText = "Click to select files.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._label.innerText = "Click to select a file.";
|
||||||
|
}
|
||||||
|
return this._input.files.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Group = class extends HTMLElement
|
||||||
|
{
|
||||||
|
static observedAttributes = ["label", "type", "collapsed"];
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: "open"});
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: .5em .25em .5em .75em;
|
||||||
|
margin: .25em;
|
||||||
|
margin-top: 1em;
|
||||||
|
border-top: 1px solid var(--secondary-foreground);
|
||||||
|
}
|
||||||
|
:host([type="box"])
|
||||||
|
{
|
||||||
|
border: 1px solid var(--secondary-foreground);
|
||||||
|
}
|
||||||
|
:host([collapsed]:not([type="box"]))
|
||||||
|
{
|
||||||
|
padding: .35em;
|
||||||
|
}
|
||||||
|
:host([type="box"][collapsed])
|
||||||
|
{
|
||||||
|
padding: .32em;
|
||||||
|
}
|
||||||
|
group-label
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--primary-background);
|
||||||
|
position: absolute;
|
||||||
|
top: -.85em;
|
||||||
|
left: .5ch;
|
||||||
|
padding-left: .25ch;
|
||||||
|
padding-right: .25ch;
|
||||||
|
}
|
||||||
|
:host([collapsed]) group-content
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
m-inline
|
||||||
|
{
|
||||||
|
max-width: 1ch;
|
||||||
|
width: 1ch;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Material Symbols Outlined';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 1.25em;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
-webkit-font-feature-settings: 'liga';
|
||||||
|
font-feature-settings: 'liga';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
padding-bottom: .1em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
a-button
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<group-label><a-button><span></span> <m-inline></m-inline></a-button></group-label>
|
||||||
|
<group-content><slot></slot></group-content>
|
||||||
|
`;
|
||||||
|
this._labelText = $(this.shadowRoot).children("group-label").children("a-button").children("span");
|
||||||
|
this._labelIcon = $(this.shadowRoot).children("group-label").children("a-button").children("m-inline");
|
||||||
|
}
|
||||||
|
connectedCallback()
|
||||||
|
{
|
||||||
|
if (this.label && this.label.length > 0)
|
||||||
|
{
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").children("span").text(this.label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").children("span").text("Group Label");
|
||||||
|
}
|
||||||
|
if (this.collapsed)
|
||||||
|
{
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").children("m-inline").text("expand_more");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").children("m-inline").text("expand_less");
|
||||||
|
}
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").click(()=>
|
||||||
|
{
|
||||||
|
this.collapsed = !this.collapsed;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
disconnectedCallback()
|
||||||
|
{
|
||||||
|
$(this.shadowRoot).children("group-label").children("a-button").off("click");
|
||||||
|
}
|
||||||
|
attributeChangedCallback(name, oldValue, newValue)
|
||||||
|
{
|
||||||
|
if (name === "label")
|
||||||
|
{
|
||||||
|
if (this.label && this.label.length > 0)
|
||||||
|
{
|
||||||
|
this._labelText.text(this.label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._labelText.text("Group Label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (name === "collapsed")
|
||||||
|
{
|
||||||
|
if (this.collapsed)
|
||||||
|
{
|
||||||
|
this._labelIcon.text("expand_more");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._labelIcon.text("expand_less");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get collapsed() {
|
||||||
|
return this.hasAttribute("collapsed");
|
||||||
|
}
|
||||||
|
set collapsed(value) {
|
||||||
|
if (value) {
|
||||||
|
this.setAttribute("collapsed", "");
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("collapsed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get label() {
|
||||||
|
return this.getAttribute("label");
|
||||||
|
}
|
||||||
|
set label(value) {
|
||||||
|
this.setAttribute("label", value.toString());
|
||||||
|
}
|
||||||
|
get type() {
|
||||||
|
return this.getAttribute("type");
|
||||||
|
}
|
||||||
|
set type(value) {
|
||||||
|
this.setAttribute("type", value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VSpacer = class extends HTMLElement
|
||||||
|
{
|
||||||
|
static observedAttributes = ["lines"];
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: "open"});
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div></div>
|
||||||
|
`;
|
||||||
|
this._div = $(this.shadowRoot).children("div");
|
||||||
|
}
|
||||||
|
connectedCallback(){
|
||||||
|
this._applyHeight();
|
||||||
|
}
|
||||||
|
attributeChangedCallback(name, oldValue, newValue)
|
||||||
|
{
|
||||||
|
if (name === "lines")
|
||||||
|
{
|
||||||
|
this._applyHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_applyHeight()
|
||||||
|
{
|
||||||
|
const height = (!this.lines || isNaN(this.lines)) ? 1 : this.lines
|
||||||
|
this._div.css("height",`${height}lh`);
|
||||||
|
}
|
||||||
|
get lines() {
|
||||||
|
return Number(this.getAttribute("lines"));
|
||||||
|
}
|
||||||
|
set lines(value) {
|
||||||
|
if (isNaN(value))
|
||||||
|
{
|
||||||
|
this.setAttribute("lines", 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.setAttribute("lines", Number(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MultiLineEllipsis = class extends HTMLElement
|
||||||
|
{
|
||||||
|
static observedAttributes = ["lines"];
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: "open"});
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
div
|
||||||
|
{
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div><slot></slot></div>
|
||||||
|
`;
|
||||||
|
this._div = $(this.shadowRoot).children("div");
|
||||||
|
this._slot = $(this.shadowRoot).find("slot");
|
||||||
|
}
|
||||||
|
connectedCallback(){
|
||||||
|
this._applyLineClamp();
|
||||||
|
this.title = this.innerText;
|
||||||
|
this._slot.on("slotchange", ()=>{this.title = this.innerText;});
|
||||||
|
}
|
||||||
|
attributeChangedCallback(name, oldValue, newValue)
|
||||||
|
{
|
||||||
|
if (name === "lines")
|
||||||
|
{
|
||||||
|
this._applyLineClamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_applyLineClamp()
|
||||||
|
{
|
||||||
|
const lines = (!this.lines || isNaN(this.lines)) ? "1" : this.lines.toString();
|
||||||
|
this._div.css({
|
||||||
|
"line-clamp": lines,
|
||||||
|
"-webkit-line-clamp": lines
|
||||||
|
});
|
||||||
|
}
|
||||||
|
get lines() {
|
||||||
|
return Number(this.getAttribute("lines"));
|
||||||
|
}
|
||||||
|
set lines(value) {
|
||||||
|
if (isNaN(value))
|
||||||
|
{
|
||||||
|
this.setAttribute("lines", 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.setAttribute("lines", Number(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MidlineEllipsis = class extends HTMLElement
|
||||||
|
{
|
||||||
|
static observedAttributes = ["chars"];
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.attachShadow({mode: "open"});
|
||||||
|
this.shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<slot></slot>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
connectedCallback(){
|
||||||
|
this.title = this.innerText;
|
||||||
|
this._chars = Number(this.getAttribute("chars"));
|
||||||
|
this._chars > 5 ? 1==1 : this.chars = 5;
|
||||||
|
this._originalNodes = this.cloneNode(true);
|
||||||
|
this._applyEllipsis();
|
||||||
|
this.addEventListener("click",()=>{
|
||||||
|
moly.alert.show("Konten Lengkap",this._originalNodes.innerHTML);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disconnectedCallback()
|
||||||
|
{
|
||||||
|
this.removeEventListener("click");
|
||||||
|
}
|
||||||
|
_applyEllipsis()
|
||||||
|
{
|
||||||
|
let sourceNodes = this._originalNodes.cloneNode(true).childNodes;
|
||||||
|
let skip = ["a-button","button", "img"];
|
||||||
|
let fhalftl = Math.ceil((this.chars-3)/2);
|
||||||
|
let lhalftl = Math.floor((this.chars-3)/2);
|
||||||
|
let fhalfl = 0;
|
||||||
|
let lhalfl = 0;
|
||||||
|
let fsearch = true;
|
||||||
|
let fpart = moly.newElement("span");
|
||||||
|
let lpart = moly.newElement("span");
|
||||||
|
let title = "";
|
||||||
|
$.each(sourceNodes,(i,v)=>{
|
||||||
|
if (skip.includes(v.nodeName.toLowerCase())) return;
|
||||||
|
title += v.textContent;
|
||||||
|
if(fsearch && v.textContent.length > 0 )
|
||||||
|
{
|
||||||
|
if (fhalfl + v.textContent.length < fhalftl)
|
||||||
|
{
|
||||||
|
fpart.append(v.cloneNode(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let remaining = fhalftl - fhalfl ;
|
||||||
|
let snode = v.cloneNode(true);
|
||||||
|
snode.textContent = snode.textContent.substring(0,remaining);
|
||||||
|
if(snode.textContent.length>0) fpart.append(snode);
|
||||||
|
fpart.append("...");
|
||||||
|
fsearch = false;
|
||||||
|
}
|
||||||
|
fhalfl += v.textContent.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.title = title.replace(/\s+/g, ' ').trim();
|
||||||
|
for (let i = sourceNodes.length - 1; i >= 0; i--) {
|
||||||
|
let v = sourceNodes[i];
|
||||||
|
if (skip.includes(v.nodeName.toLowerCase())) continue;
|
||||||
|
if(v.textContent.length > 0 )
|
||||||
|
{
|
||||||
|
if (lhalfl + v.textContent.length < lhalftl)
|
||||||
|
{
|
||||||
|
lpart.prepend(v.cloneNode(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let remaining = lhalftl - lhalfl;
|
||||||
|
let snode = v.cloneNode(true);
|
||||||
|
snode.textContent = snode.textContent.substring(snode.textContent.length-remaining);
|
||||||
|
if(snode.textContent.length>0) lpart.prepend(snode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lhalfl += v.textContent.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fpart.innerHTML = fpart.innerHTML.trim();
|
||||||
|
lpart.innerHTML = lpart.innerHTML.trim()
|
||||||
|
$(this).empty().append(...fpart.childNodes,...lpart.childNodes);
|
||||||
|
}
|
||||||
|
attributeChangedCallback(name, oldValue, newValue)
|
||||||
|
{
|
||||||
|
if (name === "chars")
|
||||||
|
{
|
||||||
|
if (this._originalNodes) this._applyEllipsis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get chars() {
|
||||||
|
let retval;
|
||||||
|
this._chars = Number(this.getAttribute("chars"));
|
||||||
|
this._chars > 5 ? retval = this._chars : retval = 5;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
set chars(value) {
|
||||||
|
if (isNaN(value))
|
||||||
|
{
|
||||||
|
this._chars = 5
|
||||||
|
this.setAttribute("chars", 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._chars = Number(value);
|
||||||
|
this.setAttribute("chars", this._chars > 5 ? this.chars : 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(async ()=>{
|
||||||
|
let time = 0
|
||||||
|
while (typeof window.jQuery === "undefined" && time < 10000) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
time +=50;
|
||||||
|
}
|
||||||
|
if (window.jQuery)
|
||||||
|
{
|
||||||
|
window.moly = new molybdenum();
|
||||||
|
customElements.define("a-button",moly.AButton);
|
||||||
|
customElements.define("file-input",moly.FileInput);
|
||||||
|
customElements.define("group-el",moly.Group);
|
||||||
|
customElements.define("v-spacer",moly.VSpacer);
|
||||||
|
customElements.define("multi-ellipsis",moly.MultiLineEllipsis);
|
||||||
|
customElements.define("midline-ellipsis",moly.MidlineEllipsis);
|
||||||
|
}
|
||||||
|
})();
|
254
index.html
254
index.html
@ -1,18 +1,254 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="en-gb" translate="no">
|
||||||
<head>
|
<head>
|
||||||
<title>Agen Perubahan</title>
|
<meta charset="utf-8">
|
||||||
<script src="/assets/js/app.js"
|
<meta name="viewport" content="width=device-width initial-scale=1.0, maximum-scale=1.0"/>
|
||||||
></script>
|
<link rel="icon" type="image/webp" href="/assets/images/bpn.webp">
|
||||||
|
<title>Agen Perubahan Admin Panel</title>
|
||||||
|
<link rel="preload" href="/assets/fonts/materials.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/ss3l.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/ss3il.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/assets/css/fonts.css">
|
||||||
|
<link rel="stylesheet" href="/assets/css/molybdenum.css">
|
||||||
|
<script src="/assets/js/jquery.min.js" async></script>
|
||||||
|
<script src="/assets/js/molybdenum.js" type="module" async></script>
|
||||||
|
<script src="/assets/js/app.js" async></script>
|
||||||
<style>
|
<style>
|
||||||
html
|
*
|
||||||
{
|
{
|
||||||
background-color: dimgray;
|
user-select: none;
|
||||||
color: lightgray;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
*::-webkit-scrollbar, .main::-webkit-scrollbar {
|
||||||
|
width: .8ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar thumb (the draggable part) */
|
||||||
|
*::-webkit-scrollbar-thumb, .main::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--secondary-background); /* Dark semi-transparent */
|
||||||
|
border-radius: .2ch; /* Rounded edges */
|
||||||
|
}
|
||||||
|
/* body
|
||||||
|
{
|
||||||
|
height: 100%;
|
||||||
|
} */
|
||||||
|
h2, h3, h4
|
||||||
|
{
|
||||||
|
margin: .25em 0;
|
||||||
|
}
|
||||||
|
aside
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
width: 30ch;
|
||||||
|
background-color: var(--primary-table);
|
||||||
|
color: var(--primary-foreground);
|
||||||
|
padding: .5ch;
|
||||||
|
padding-top: 3.5em;
|
||||||
|
box-shadow: var(--primary-foreground) .65ch 0, inset var(--primary-foreground) -13px 0 20px -25px;
|
||||||
|
}
|
||||||
|
.logobox
|
||||||
|
{
|
||||||
|
height: 3em;
|
||||||
|
position: fixed;
|
||||||
|
top: .5ch;
|
||||||
|
left: 0;
|
||||||
|
width: inherit;
|
||||||
|
padding: 0 .5ch .5ch .5ch;
|
||||||
|
border-bottom: 1px solid var(--secondary-background);
|
||||||
|
}
|
||||||
|
.sidebar
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(3em + 1ch);
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% - .25ch);
|
||||||
|
max-height: calc(100% - (3em + 1.5ch));
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.sidebar::-webkit-scrollbar, .main::-webkit-scrollbar {
|
||||||
|
width: .8ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar thumb (the draggable part) */
|
||||||
|
.sidebar::-webkit-scrollbar-thumb, .main::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--secondary-background); /* Dark semi-transparent */
|
||||||
|
border-radius: .2ch; /* Rounded edges */
|
||||||
|
}
|
||||||
|
.item
|
||||||
|
{
|
||||||
|
height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
margin-left: 1ch;
|
||||||
|
margin-right: 1ch;
|
||||||
|
background-color: var(--primary-table);
|
||||||
|
padding-left: 1ch;
|
||||||
|
padding-right: 1ch;
|
||||||
|
border-radius: .25ch;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.item:hover
|
||||||
|
{
|
||||||
|
filter: brightness(1.2);
|
||||||
|
}
|
||||||
|
.item[data-active]
|
||||||
|
{
|
||||||
|
filter: brightness(1.4);
|
||||||
|
}
|
||||||
|
.sub.item
|
||||||
|
{
|
||||||
|
padding-left: 3ch;
|
||||||
|
}
|
||||||
|
#main
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 30ch;
|
||||||
|
padding: .5ch .5ch .5ch 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
backdrop-filter: blur(7px);
|
||||||
|
background-color: var(--semi-background);
|
||||||
|
}
|
||||||
|
nav
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
height: 3em;
|
||||||
|
padding-left: 2ch;
|
||||||
|
padding-right: 2ch;
|
||||||
|
line-height: 3em;
|
||||||
|
border-bottom: 1px solid var(--input-background);
|
||||||
|
}
|
||||||
|
#content
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: calc(100% - 6em);
|
||||||
|
padding: .5ch 1ch;
|
||||||
|
}
|
||||||
|
#footer
|
||||||
|
{
|
||||||
|
height: 3em;
|
||||||
|
padding: .5em;
|
||||||
|
line-height: 2em;
|
||||||
|
border-top: 1px solid var(--input-background);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="margin: 0; height: 100%; background-color: var(--primary-background);">
|
||||||
Agen Perubahan
|
<load-screen id="blocker" style="background-color : #fefdfc ; z-index: 1;"><sonar-ping></sonar-ping><load-message>Memuat Aplikasi...</load-message></load-screen>
|
||||||
|
<aside>
|
||||||
|
<div class="logobox">
|
||||||
|
<img src="/assets/images/logo-text.png" style="width: 100%; height: 100%; object-fit: contain; object-position: 1.5ch center;">
|
||||||
|
</div>
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="item" data-path="dasbor">
|
||||||
|
<m-inline>dashboard</m-inline> Dasbor
|
||||||
|
</div>
|
||||||
|
<div class="item" data-path="kegiatan">
|
||||||
|
<m-inline>supervisor_account</m-inline> Kegiatan dan Dokumentasi
|
||||||
|
</div>
|
||||||
|
<div class="item" data-path="agen">
|
||||||
|
<m-inline>supervisor_account</m-inline> Manajemen Agen & User
|
||||||
|
</div>
|
||||||
|
<div class="item" data-path="laporan">
|
||||||
|
<m-inline>quick_references</m-inline> Laporan
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div id="main">
|
||||||
|
<nav>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
Agen Perubahan ATR/BPN Kanwil Riau
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span id="username">User Name</span>
|
||||||
|
<sep-bar></sep-bar>
|
||||||
|
<a-button type="blend" id="lo"><m-inline>logout</m-inline> Keluar</a-button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<span>Agen Perubahan Admin Panel <span id="vernum"></span></span>
|
||||||
|
|
||||||
|
<span>ATR/BPN Kantor Wilayah Provinsi Riau</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
<script type="module">
|
||||||
|
let detail = await fetch("/auth/me").then(r=>r.json()).catch(e=>{
|
||||||
|
moly.alert.show("Koneksi Gagal","Gagal menyambung ke server. Harap periksa koneksi jaringan.");
|
||||||
|
return {status: 0};
|
||||||
|
});
|
||||||
|
if (detail.status == 401)
|
||||||
|
{
|
||||||
|
location = "/login.html";
|
||||||
|
}
|
||||||
|
window.agents = (await fetch("/api/getagents").then(r=>r.json()).catch(e=>{
|
||||||
|
moly.alert.show("Koneksi Gagal","Gagal menyambung ke server. Harap periksa koneksi jaringan.");
|
||||||
|
return {data: []};
|
||||||
|
})).data;
|
||||||
|
const userDetails = detail.data;
|
||||||
|
const loggedInAgent = agents.find(a=>a.agentID == userDetails.agentID)
|
||||||
|
// if (!window.evtSource) window.evtSource = new EventSource("/events");
|
||||||
|
window.addEventListener("viewChange", async ()=>{
|
||||||
|
if (window.evtSource) evtSource.onmessage = null;
|
||||||
|
});
|
||||||
|
async function domReady()
|
||||||
|
{
|
||||||
|
$('#vernum').text(`v${vernum}`)
|
||||||
|
$('#lo').click(async()=>{
|
||||||
|
const res = await fetch('/auth/logout').catch(e=>{
|
||||||
|
moly.alert.show("Logout: Koneksi Gagal","Gagal menyambung ke server. Harap periksa koneksi jaringan.");
|
||||||
|
return {status: 0};
|
||||||
|
});
|
||||||
|
if(res.status == 200) location = "/login.html";
|
||||||
|
});
|
||||||
|
$('#username').text(loggedInAgent.name)
|
||||||
|
$('.item, .subitem').click(async (e)=>{
|
||||||
|
if (window.shellnavigating) return;
|
||||||
|
window.dispatchEvent(new Event("viewChange"));
|
||||||
|
window.shellnavigating = true;
|
||||||
|
let id = moly.loadScreen.show("Memuat...","sonar","#content");
|
||||||
|
$('.item[data-active]').removeAttr("data-active");
|
||||||
|
$(e.target.closest(".item")).attr("data-active","");
|
||||||
|
let content = e.target.closest(".item").getAttribute("data-path") ? fetch(`/modules/${e.target.closest(".item").getAttribute("data-path")}.html`).then(d => d.text()).catch(e=>{
|
||||||
|
return "Gagal menyambung ke server. Harap periksa koneksi jaringan.";
|
||||||
|
}) : "";
|
||||||
|
$('#content').html(await content);
|
||||||
|
moly.loadScreen.close(id);
|
||||||
|
window.shellnavigating = false;
|
||||||
|
});
|
||||||
|
$('#blocker').remove();
|
||||||
|
$($('.item')[0]).trigger("click");
|
||||||
|
}
|
||||||
|
(async ()=>{
|
||||||
|
let time = 0
|
||||||
|
while (typeof window.moly === "undefined" && time < 10000) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
time += 50;
|
||||||
|
}
|
||||||
|
if (window.moly)
|
||||||
|
{
|
||||||
|
while (typeof window.moly !== "undefined" && document.readyState !== "complete") {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
domReady();
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
112
login.html
Normal file
112
login.html
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en-gb" translate="no">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1.0, maximum-scale=1.0"/>
|
||||||
|
<link rel="icon" type="image/webp" href="/assets/images/bpn.webp">
|
||||||
|
<title>Agen Perubahan ATR/BPN Riau</title>
|
||||||
|
<link rel="preload" href="/assets/fonts/materials.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/ss3l.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="preload" href="/assets/fonts/ss3il.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="/assets/css/fonts.css">
|
||||||
|
<link rel="stylesheet" href="/assets/css/molybdenum.css">
|
||||||
|
<script src="/assets/js/jquery.min.js" async></script>
|
||||||
|
<script src="/assets/js/molybdenum.js" async></script>
|
||||||
|
<script src="/assets/js/app.js" async></script>
|
||||||
|
<style>
|
||||||
|
*
|
||||||
|
{
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
input
|
||||||
|
{
|
||||||
|
user-select: text;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
}
|
||||||
|
*::-webkit-scrollbar, .main::-webkit-scrollbar {
|
||||||
|
width: .8ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar thumb (the draggable part) */
|
||||||
|
*::-webkit-scrollbar-thumb, .main::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--secondary-background); /* Dark semi-transparent */
|
||||||
|
border-radius: .2ch; /* Rounded edges */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; height: 100%;">
|
||||||
|
<load-screen id="blocker" style="background-color : #fefdfc ; z-index: 1;"><sonar-ping></sonar-ping><load-message>Memuat Aplikasi...</load-message></load-screen>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center; width: 100%; height: calc(100% - 2.4em); background-color: var(--secondary-background); flex-direction: column; background-image: url(/assets/images/bpn.webp); background-position-x: center; background-repeat: no-repeat;">
|
||||||
|
<div style="width: 40ch; padding: 1.5ch; background-color: var(--semi-background); backdrop-filter: blur(3px);" id="box">
|
||||||
|
<h3 style="text-align: center;"><span id="mt">Agen Perubahan ATR/BPN<br>Kantor Wilayah Provinsi Riau</span></h3>
|
||||||
|
<div style="margin: auto; width: max-content;">
|
||||||
|
<input id="uname" type="text" class="icon" placeholder="Username"><m-inline class="icon">person</m-inline><br>
|
||||||
|
<input id="pwd" type="password" class="icon peek" placeholder="Password"><m-inline class="icon">password</m-inline><a-button id="peeker" type="peek"><m-inline>visibility_on</m-inline></a-button><br>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<button id="login-btn"><m-inline>key</m-inline> Masuk</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="height: 3em; background-color: var(--secondary-background); text-align: center; font-size: .8em;">
|
||||||
|
Agen Perubahan ATR/BPN Kanwil Riau <span id="vernum"></span><br>
|
||||||
|
Kementerian ATR/BPN Kantor Wilayah Provinsi Riau
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script type="module">
|
||||||
|
async function domReady()
|
||||||
|
{
|
||||||
|
$('#vernum').text("versi " + vernum);
|
||||||
|
window.detail = await fetch("/auth/me");
|
||||||
|
if(detail.status != 401)
|
||||||
|
{
|
||||||
|
location = "/";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#blocker').remove();
|
||||||
|
$('#login-btn').click(async ()=>{
|
||||||
|
await login();
|
||||||
|
});
|
||||||
|
$('#pwd, #uname').on("keypress", async (e)=>{
|
||||||
|
if (e.keyCode == 13) await login();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(async ()=>{
|
||||||
|
let time = 0
|
||||||
|
while (typeof window.moly === "undefined" && time < 10000) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
time += 50;
|
||||||
|
}
|
||||||
|
if (window.moly)
|
||||||
|
{
|
||||||
|
while (typeof window.moly !== "undefined" && document.readyState !== "complete") {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
domReady();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
async function login()
|
||||||
|
{
|
||||||
|
$('#uname').prop("disabled",true);
|
||||||
|
$('#pwd').prop("disabled",true);
|
||||||
|
$('#peeker').prop("disabled",true);
|
||||||
|
$('#login-btn').prop("disabled",true);
|
||||||
|
$('#login-btn').html("<m-inline>hourglass_empty</m-inline> Tunggu");
|
||||||
|
let login = await fetch("/auth/login",{method: "post", headers: {"content-type": "application/json"}, body: JSON.stringify({username:$('#uname').val(), password:$('#pwd').val()})});
|
||||||
|
if(login.status == 200)
|
||||||
|
{
|
||||||
|
location = "/";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$('#uname').prop("disabled",false);
|
||||||
|
$('#pwd').prop("disabled",false);
|
||||||
|
$('#peeker').prop("disabled",false);
|
||||||
|
$('#login-btn').prop("disabled",false);
|
||||||
|
$('#login-btn').html("<m-inline>key</m-inline> Masuk");
|
||||||
|
moly.alert.show("Login Gagal","Tidak dapat login dengan username dan password yang diberikan. Pastikan username dan password sudah benar dan akun dalam keadaan aktif. Hubungi admin untuk informasi lebih lanjut.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
0
modules/agen.html
Normal file
0
modules/agen.html
Normal file
0
modules/dasbor.html
Normal file
0
modules/dasbor.html
Normal file
0
modules/kegiatan.html
Normal file
0
modules/kegiatan.html
Normal file
0
modules/laporan.html
Normal file
0
modules/laporan.html
Normal file
Loading…
x
Reference in New Issue
Block a user