mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-04-26 02:02:29 -05:00
Add webui for BeatStream.
This commit is contained in:
parent
bfdeca09fd
commit
c2e321e298
|
|
@ -1,8 +1,9 @@
|
||||||
# BeatStream
|
# BeatStream
|
||||||
|
|
||||||
Plugin Version: **v0.1.0-beta**
|
Plugin Version: **v1.0.0**
|
||||||
|
|
||||||
Supported Versions:
|
Supported Versions:
|
||||||
|
|
||||||
- BeatStream アニムトライヴ
|
- BeatStream アニムトライヴ
|
||||||
- Back end ✔
|
- Back end ✔
|
||||||
|
- Web UI ✔
|
||||||
|
|
@ -147,7 +147,7 @@ export namespace Bst2HandlersCommon {
|
||||||
let query: Query<IBst2MusicRecord> = { collection: "bst.bst2.playData.musicRecord#userId", userId: stageLog.userId, musicId: stageLog.musicId, chart: stageLog.chart }
|
let query: Query<IBst2MusicRecord> = { collection: "bst.bst2.playData.musicRecord#userId", userId: stageLog.userId, musicId: stageLog.musicId, chart: stageLog.chart }
|
||||||
let oldRecord = await DB.FindOne<IBst2MusicRecord>(query)
|
let oldRecord = await DB.FindOne<IBst2MusicRecord>(query)
|
||||||
|
|
||||||
let time = Date.now() / 1000
|
let time = Date.now()
|
||||||
stageLog.time = time
|
stageLog.time = time
|
||||||
stageLog.isCourseStage = isCourseStage
|
stageLog.isCourseStage = isCourseStage
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ export namespace Bst2HandlersCommon {
|
||||||
let query: Query<IBst2Course> = { collection: "bst.bst2.playData.course#userId", userId: courseLog.userId, courseId: courseLog.courseId }
|
let query: Query<IBst2Course> = { collection: "bst.bst2.playData.course#userId", userId: courseLog.userId, courseId: courseLog.courseId }
|
||||||
let oldRecord = await DB.FindOne<IBst2Course>(query)
|
let oldRecord = await DB.FindOne<IBst2Course>(query)
|
||||||
|
|
||||||
let time = Date.now() / 1000
|
let time = Date.now()
|
||||||
courseLog.time = time
|
courseLog.time = time
|
||||||
|
|
||||||
if (oldRecord == null) {
|
if (oldRecord == null) {
|
||||||
|
|
|
||||||
39
bst@asphyxia/handlers/bst2/webui.ts
Normal file
39
bst@asphyxia/handlers/bst2/webui.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { IBst2Base, IBst2Customization } from "../../models/bst2/profile"
|
||||||
|
import { WebUIMessageType } from "../../models/utility/webui_message"
|
||||||
|
import { DBM } from "../utility/db_manager"
|
||||||
|
import { UtilityHandlersWebUI } from "../utility/webui"
|
||||||
|
|
||||||
|
export namespace Bst2HandlersWebUI {
|
||||||
|
export const UpdateSettings = async (data: {
|
||||||
|
refid: string
|
||||||
|
name: string
|
||||||
|
rippleNote: number
|
||||||
|
sfxNormalNote: number
|
||||||
|
sfxRippleNote: number
|
||||||
|
sfxSlashNote: number
|
||||||
|
sfxStreamNote: number
|
||||||
|
backgroundBrightness: number
|
||||||
|
judgeText: number
|
||||||
|
rippleNoteGuide: number
|
||||||
|
streamNoteGuide: number
|
||||||
|
sfxFine: number
|
||||||
|
sfxStreamNoteTail: number
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
let customization = await DB.FindOne<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" })
|
||||||
|
if (customization == null) throw new Error("No profile for refid=" + data.refid)
|
||||||
|
customization.custom[0] = data.rippleNote
|
||||||
|
customization.custom[2] = data.sfxNormalNote
|
||||||
|
customization.custom[3] = data.sfxRippleNote
|
||||||
|
customization.custom[4] = data.sfxSlashNote
|
||||||
|
customization.custom[5] = data.sfxStreamNote
|
||||||
|
customization.custom[6] = data.backgroundBrightness
|
||||||
|
customization.custom[7] = (data.judgeText << 0) | (data.rippleNoteGuide << 1) | (data.streamNoteGuide << 2) | (data.sfxStreamNoteTail << 3) | (data.sfxFine << 4)
|
||||||
|
customization.custom[9] = data.judgeText
|
||||||
|
DBM.update<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" }, customization)
|
||||||
|
UtilityHandlersWebUI.pushMessage("Save BeatStream Animtribe settings succeeded!", 2, WebUIMessageType.success, data.refid)
|
||||||
|
} catch (e) {
|
||||||
|
UtilityHandlersWebUI.pushMessage("Error while save BeatStream Animtribe settings: " + e.message, 2, WebUIMessageType.error, data.refid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -121,6 +121,7 @@ export namespace DBM {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkData<T extends ICollection<any>>(data: T): Promise<void> {
|
async function checkData<T extends ICollection<any>>(data: T): Promise<void> {
|
||||||
|
for (let k in data) if (k.startsWith("__")) delete data[k]
|
||||||
if (await DB.FindOne<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection }) == null) {
|
if (await DB.FindOne<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection }) == null) {
|
||||||
await DB.Insert<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection })
|
await DB.Insert<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection })
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +167,7 @@ export namespace DBM {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await log(Date.now().toLocaleString() + " Error: " + (e as Error).message)
|
await log(new Date().toLocaleString() + " Error: " + (e as Error).message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@ export namespace UtilityHandlersWebUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removeWebUIMessage = async () => {
|
export const removeWebUIMessage = async () => {
|
||||||
await DB.Remove<IWebUIMessage>({ collection: "utility.webuiMessage" })
|
await DBM.remove<IWebUIMessage>(null, { collection: "utility.webuiMessage" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,18 +2,21 @@ import { UtilityHandlersCommon } from "./handlers/utility/common"
|
||||||
import { UtilityHandlersWebUI } from "./handlers/utility/webui"
|
import { UtilityHandlersWebUI } from "./handlers/utility/webui"
|
||||||
import { initialize } from "./handlers/utility/initialize"
|
import { initialize } from "./handlers/utility/initialize"
|
||||||
import { Bst2HandlersCommon } from "./handlers/bst2/common"
|
import { Bst2HandlersCommon } from "./handlers/bst2/common"
|
||||||
|
import { Bst2HandlersWebUI } from "./handlers/bst2/webui"
|
||||||
|
|
||||||
export function register() {
|
export function register() {
|
||||||
R.GameCode("NBT")
|
R.GameCode("NBT")
|
||||||
|
|
||||||
RouteBst2()
|
routeBst2()
|
||||||
|
|
||||||
|
R.WebUIEvent("removeWebUIMessage", UtilityHandlersWebUI.removeWebUIMessage)
|
||||||
|
|
||||||
R.Unhandled()
|
R.Unhandled()
|
||||||
|
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
function RouteBst2() {
|
function routeBst2() {
|
||||||
R.Route("info2.common", Bst2HandlersCommon.Common)
|
R.Route("info2.common", Bst2HandlersCommon.Common)
|
||||||
R.Route("pcb2.boot", Bst2HandlersCommon.BootPcb)
|
R.Route("pcb2.boot", Bst2HandlersCommon.BootPcb)
|
||||||
R.Route("player2.start", Bst2HandlersCommon.StartPlayer)
|
R.Route("player2.start", Bst2HandlersCommon.StartPlayer)
|
||||||
|
|
@ -24,4 +27,6 @@ function RouteBst2() {
|
||||||
R.Route("player2.stagedata_write", Bst2HandlersCommon.WriteStageLog)
|
R.Route("player2.stagedata_write", Bst2HandlersCommon.WriteStageLog)
|
||||||
R.Route("player2.course_stage_data_write", Bst2HandlersCommon.WriteCourseStageLog)
|
R.Route("player2.course_stage_data_write", Bst2HandlersCommon.WriteCourseStageLog)
|
||||||
R.Route("player2.course_data_write", Bst2HandlersCommon.WriteCourseLog)
|
R.Route("player2.course_data_write", Bst2HandlersCommon.WriteCourseLog)
|
||||||
|
|
||||||
|
R.WebUIEvent("bst2UpdateSettings", Bst2HandlersWebUI.UpdateSettings)
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +107,7 @@ export const Bst2UnlockingInfoMap: KM<IBst2UnlockingInfo> = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBst2Customization extends ICollection<"bst.bst2.player.customization"> {
|
export interface IBst2Customization extends ICollection<"bst.bst2.player.customization"> {
|
||||||
|
// [rippleNote, rippleNoteColor, sfxNormalNote, sfxRippleNote, sfxSlashNote, sfxStreamNote, backgroundBrightnessTimes2, (000{sfxFine}{sfxStreamTail}{streamNoteGuide}{rippleNoteGuide}{judgeText}, ?, ?, ?, ?, ?, ?, ?, ?)]
|
||||||
custom: FixedSizeArray<number, 16>
|
custom: FixedSizeArray<number, 16>
|
||||||
}
|
}
|
||||||
export const Bst2CustomizationMap: KM<IBst2Customization> = {
|
export const Bst2CustomizationMap: KM<IBst2Customization> = {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
export type Game = "bst"
|
export type Game = "bst"
|
||||||
export const game: Game = "bst"
|
export const game: Game = "bst"
|
||||||
export const version: string = "0.1.0"
|
export type PluginVersion = "1.0.0"
|
||||||
|
export const version: PluginVersion = "1.0.0"
|
||||||
|
|
|
||||||
243
bst@asphyxia/webui/css/webui_util.css
Normal file
243
bst@asphyxia/webui/css/webui_util.css
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
#tab-content, .tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab-content.is-active, .tab-content.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr#tab-content.is-active, tr.tab-content.is-active {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tabs li.disabled a {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
border-color: #c0c0c0;
|
||||||
|
color: #7f7f7f;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form-pagination ul.pagination-list {
|
||||||
|
margin: 0!important;
|
||||||
|
}
|
||||||
|
.pagination-link, .pagination-next, .pagination-previous {
|
||||||
|
border-color: transparent;
|
||||||
|
transition: .2s linear;
|
||||||
|
}
|
||||||
|
.pagination-next, .pagination-previous {
|
||||||
|
color: #209CEE;
|
||||||
|
}
|
||||||
|
.pagination-next:not([disabled]):hover, .pagination-previous:not([disabled]):hover {
|
||||||
|
color: #118fe4;
|
||||||
|
}
|
||||||
|
/* Set all link color to Asphyxia CORE blue */
|
||||||
|
::selection {
|
||||||
|
color: white;
|
||||||
|
background-color: #209CEE;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #209CEE;
|
||||||
|
}
|
||||||
|
.tabs.is-toggle li.is-active a {
|
||||||
|
background-color: #209CEE;
|
||||||
|
border-color: #209CEE;
|
||||||
|
}
|
||||||
|
.tabs li.is-active a {
|
||||||
|
color: #209CEE;
|
||||||
|
border-color: #209CEE;
|
||||||
|
}
|
||||||
|
.pagination-link.is-current {
|
||||||
|
background-color: #209CEE;
|
||||||
|
border-color: #209CEE;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.select:not(.is-multiple):not(.is-loading):after {
|
||||||
|
border-color: #209CEE;
|
||||||
|
}
|
||||||
|
.select select:active, .select select:focus {
|
||||||
|
border-color: #209CEE;
|
||||||
|
}
|
||||||
|
.button.is-link {
|
||||||
|
background-color: #209CEE;
|
||||||
|
}
|
||||||
|
.button.is-link.is-active, .button.is-link:active, .button.is-link.is-hovered, .button.is-link:hover {
|
||||||
|
background-color: #118fe4;
|
||||||
|
}
|
||||||
|
.input:active, .input:focus {
|
||||||
|
border-color: #209CEE;
|
||||||
|
}
|
||||||
|
.table tr.is-selected {
|
||||||
|
background-color: #209CEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#card-content.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#card-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.marquee-label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.marquee-label-container {
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* from Bulma */
|
||||||
|
.button.is-danger.is-light {
|
||||||
|
background-color: #feecf0;
|
||||||
|
color: #cc0f35;
|
||||||
|
}
|
||||||
|
.button.is-link.is-light {
|
||||||
|
background-color: #edf8ff;
|
||||||
|
color: #209CEE;
|
||||||
|
}
|
||||||
|
.button.is-danger.is-light.is-hovered, .button.is-danger.is-light:hover {
|
||||||
|
background-color: #fde0e6;
|
||||||
|
color: #cc0f35;
|
||||||
|
}
|
||||||
|
.button.is-link.is-light.is-hovered, .button.is-link.is-light:hover {
|
||||||
|
background-color: #e0f1fc;
|
||||||
|
color: #209CEE;
|
||||||
|
}
|
||||||
|
.tag.is-link.is-light {
|
||||||
|
background-color: #edf8ff;
|
||||||
|
color: #0D7DC6;
|
||||||
|
}
|
||||||
|
.tag.is-link.is-light:hover {
|
||||||
|
background-color: #209CEE;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.tag.is-delete:hover {
|
||||||
|
background-color: #FF3860!important;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.pagination {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pagination-list {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
list-style: none!important;
|
||||||
|
margin-top: 0.25em!important;
|
||||||
|
margin-bottom: 0.25em!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content li + li {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-quarter#forwide, .one-third#forwide {
|
||||||
|
display: block;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
.one-quarter#fornarrow, .one-third#fornarrow {
|
||||||
|
display: none;
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1023px) {
|
||||||
|
.one-quarter#forwide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.one-quarter#fornarrow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 700px) {
|
||||||
|
.one-third#forwide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.one-third#fornarrow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes notification-fadeout {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
99.99% {
|
||||||
|
opacity: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
animation: notification-fadeout 8s forwards;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
.notification:hover {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
.modal {
|
||||||
|
padding-bottom: 13px;
|
||||||
|
}
|
||||||
|
@media screen and (max-width:1024px) {
|
||||||
|
.modal {
|
||||||
|
transition: padding-left .2s ease-in-out 50ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width:1023px) {
|
||||||
|
.modal {
|
||||||
|
padding-left: 256px;
|
||||||
|
transition: padding-left .2s ease-in-out 50ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
transition: linear .2s;
|
||||||
|
}
|
||||||
|
.tags .tag:not(:last-child) {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal table tr {
|
||||||
|
border: solid #dbdbdb;
|
||||||
|
border-width: 0 0 1px;
|
||||||
|
}
|
||||||
|
.modal table tbody tr:last-child {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
.hidden-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.hidden-x-wrapper {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.hidden-y-wrapper {
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
.scrolling-wrapper {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.scrolling-x-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.scrolling-y-wrapper {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
a.pagination-previous {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
a.pagination-next {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.button.checkbox, .button.checkbox .checkmark {
|
||||||
|
transition: linear .2s;
|
||||||
|
}
|
||||||
618
bst@asphyxia/webui/js/webui_util.js
Normal file
618
bst@asphyxia/webui/js/webui_util.js
Normal file
|
|
@ -0,0 +1,618 @@
|
||||||
|
function initializePaginatedContent() {
|
||||||
|
let containers = document.querySelectorAll(".paginated-container")
|
||||||
|
|
||||||
|
for (let container of containers) {
|
||||||
|
let pageSizeInput = container.querySelector("input.page-size")
|
||||||
|
let paginations = container.querySelectorAll(".pagination")
|
||||||
|
let contents = container.querySelectorAll(".paginated-content")
|
||||||
|
let group = container.getAttribute("pagination-group")
|
||||||
|
let flags = { isFirst: true }
|
||||||
|
let refreshEllipsis = (param) => {
|
||||||
|
if (flags.isFirst) return
|
||||||
|
let maxWidth = container.offsetWidth / 2
|
||||||
|
for (let pagination of paginations) {
|
||||||
|
let buttons = pagination.querySelector("ul.pagination-list")
|
||||||
|
if (buttons.childElementCount == 0) return
|
||||||
|
let show = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "block"
|
||||||
|
let hide = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "none"
|
||||||
|
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||||
|
let nextButton = pagination.querySelector("a.pagination-next")
|
||||||
|
let leftEllipsis = buttons.querySelector("li.ellipsis-left")
|
||||||
|
let rightEllipsis = buttons.querySelector("li.ellipsis-right")
|
||||||
|
let width = buttons.firstChild.offsetWidth.toString()
|
||||||
|
leftEllipsis.style.width = width + "px"
|
||||||
|
rightEllipsis.style.width = width + "px"
|
||||||
|
let count = buttons.childElementCount - 2
|
||||||
|
let maxButtonCount = Math.max((buttons.firstChild.offsetWidth == 0) ? 5 : Math.trunc(maxWidth / buttons.firstChild.offsetWidth), 5)
|
||||||
|
let current = (param instanceof HTMLElement) ? param : buttons.querySelector("li.is-active")
|
||||||
|
let index = parseInt((current == null) ? 0 : current.getAttribute("tab-index"))
|
||||||
|
if (index == 0) previousButton.setAttribute("disabled", "")
|
||||||
|
else previousButton.removeAttribute("disabled")
|
||||||
|
if (index == (count - 1)) nextButton.setAttribute("disabled", "")
|
||||||
|
else nextButton.removeAttribute("disabled")
|
||||||
|
if (count <= maxButtonCount) {
|
||||||
|
for (let i = 0; i < count; i++) buttons.querySelector("li[tab-index=\"" + i + "\"]").style.display = "block"
|
||||||
|
leftEllipsis.style.display = "none"
|
||||||
|
rightEllipsis.style.display = "none"
|
||||||
|
} else {
|
||||||
|
maxButtonCount = Math.trunc((maxButtonCount - 1) / 2) * 2 + 1
|
||||||
|
let maxSurroundingButtonCount = (maxButtonCount - 5) / 2
|
||||||
|
let maxNoEllipsisIndex = maxButtonCount - 2 - maxSurroundingButtonCount - 1
|
||||||
|
|
||||||
|
if (index <= maxNoEllipsisIndex) {
|
||||||
|
for (let i = 0; i <= (maxNoEllipsisIndex + maxSurroundingButtonCount); i++) show(i)
|
||||||
|
for (let i = (maxNoEllipsisIndex + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||||
|
show(count - 1)
|
||||||
|
leftEllipsis.style.display = "none"
|
||||||
|
rightEllipsis.style.display = "block"
|
||||||
|
} else if (index >= (count - maxNoEllipsisIndex - 1)) {
|
||||||
|
for (let i = 1; i < (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i++) hide(i)
|
||||||
|
for (let i = (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i < count; i++) show(i)
|
||||||
|
show(0)
|
||||||
|
leftEllipsis.style.display = "block"
|
||||||
|
rightEllipsis.style.display = "none"
|
||||||
|
} else {
|
||||||
|
for (let i = 1; i < (index - maxSurroundingButtonCount); i++) hide(i)
|
||||||
|
for (let i = (index - maxSurroundingButtonCount); i <= (index + maxSurroundingButtonCount); i++) show(i)
|
||||||
|
for (let i = (index + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||||
|
show(0)
|
||||||
|
show(count - 1)
|
||||||
|
leftEllipsis.style.display = "block"
|
||||||
|
rightEllipsis.style.display = "block"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let refresh = () => {
|
||||||
|
if ((pageSizeInput == null) || (parseInt(pageSizeInput.value) <= 0)) {
|
||||||
|
for (let pagination of paginations) pagination.style.display = "none"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let pageSize = parseInt(pageSizeInput.value)
|
||||||
|
let pageCount = Math.ceil(contents.length / pageSize)
|
||||||
|
if (!flags.isFirst && (flags.pageSize == pageSize) && (flags.pageCount == pageCount)) return
|
||||||
|
for (let pagination of paginations) {
|
||||||
|
let buttons = pagination.querySelector("ul.pagination-list")
|
||||||
|
buttons.innerHTML = ""
|
||||||
|
buttons.id = "tabs"
|
||||||
|
}
|
||||||
|
for (let i = 0; i < pageCount; i++) {
|
||||||
|
for (let j = i * pageSize; j < (i + 1) * pageSize; j++) {
|
||||||
|
if (contents[j] == null) break
|
||||||
|
contents[j].classList.add("tab-content")
|
||||||
|
contents[j].setAttribute("tab-group", group)
|
||||||
|
contents[j].setAttribute("tab-index", i)
|
||||||
|
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) contents[j].classList.add("is-active")
|
||||||
|
if (j == ((i + 1) * pageSize - 1)) for (let td of contents[j].querySelectorAll("td")) td.style.borderBottom = "0"
|
||||||
|
}
|
||||||
|
if (pageCount > 1) for (let pagination of paginations) {
|
||||||
|
let buttons = pagination.querySelector("ul.pagination-list")
|
||||||
|
let a = document.createElement("a")
|
||||||
|
a.classList.add("pagination-link")
|
||||||
|
a.innerText = i + 1
|
||||||
|
let li = document.createElement("li")
|
||||||
|
li.appendChild(a)
|
||||||
|
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) {
|
||||||
|
li.classList.add("is-active")
|
||||||
|
a.classList.add("is-current")
|
||||||
|
}
|
||||||
|
li.setAttribute("tab-group", group)
|
||||||
|
li.setAttribute("tab-index", i)
|
||||||
|
buttons.appendChild(li)
|
||||||
|
li.addEventListener("click", () => {
|
||||||
|
refreshEllipsis(li)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pageCount > 1) for (let pagination of paginations) {
|
||||||
|
pagination.style.display = "flex"
|
||||||
|
let buttons = pagination.querySelector("ul.pagination-list")
|
||||||
|
let leftEllipsis = document.createElement("li")
|
||||||
|
leftEllipsis.style.display = "none"
|
||||||
|
leftEllipsis.classList.add("ellipsis-left", "ignore")
|
||||||
|
leftEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||||
|
let rightEllipsis = document.createElement("li")
|
||||||
|
rightEllipsis.style.display = "none"
|
||||||
|
rightEllipsis.classList.add("ellipsis-right", "ignore")
|
||||||
|
rightEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||||
|
buttons.firstChild.after(leftEllipsis)
|
||||||
|
buttons.lastChild.before(rightEllipsis)
|
||||||
|
|
||||||
|
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||||
|
let nextButton = pagination.querySelector("a.pagination-next")
|
||||||
|
previousButton.addEventListener("click", () => {
|
||||||
|
let current = buttons.querySelector("li.is-active")
|
||||||
|
let index = parseInt(current.getAttribute("tab-index"))
|
||||||
|
if (index <= 0) return
|
||||||
|
let prev = buttons.querySelector("li[tab-index=\"" + (index - 1) + "\"]")
|
||||||
|
prev.dispatchEvent(new Event("click"))
|
||||||
|
})
|
||||||
|
nextButton.addEventListener("click", () => {
|
||||||
|
let current = buttons.querySelector("li.is-active")
|
||||||
|
let index = parseInt(current.getAttribute("tab-index"))
|
||||||
|
if (index >= (buttons.childElementCount - 3)) return // includes left & right ellipsis
|
||||||
|
let next = buttons.querySelector("li[tab-index=\"" + (index + 1) + "\"]")
|
||||||
|
next.dispatchEvent(new Event("click"))
|
||||||
|
})
|
||||||
|
} else for (let pagination of paginations) pagination.style.display = "none"
|
||||||
|
flags.pageCount = pageCount
|
||||||
|
flags.pageSize = pageSize
|
||||||
|
flags.isFirst = false
|
||||||
|
}
|
||||||
|
refresh()
|
||||||
|
pageSizeInput.addEventListener("change", refresh)
|
||||||
|
let o = new ResizeObserver(refreshEllipsis)
|
||||||
|
o.observe(container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeTabs() {
|
||||||
|
let tabs = document.querySelectorAll("#tabs li")
|
||||||
|
let tabContents = document.querySelectorAll("#tab-content, .tab-content")
|
||||||
|
let updateActiveTab = (tabGroup, tabIndex) => {
|
||||||
|
for (let t of tabs) if (t && (t.getAttribute("tab-group") == tabGroup)) {
|
||||||
|
if (t.getAttribute("tab-index") != tabIndex) {
|
||||||
|
t.classList.remove("is-active")
|
||||||
|
for (let a of t.querySelectorAll("a")) a.classList.remove("is-current")
|
||||||
|
} else {
|
||||||
|
t.classList.add("is-active")
|
||||||
|
for (let a of t.querySelectorAll("a")) a.classList.add("is-current")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let updateActiveContent = (tabGroup, tabIndex) => {
|
||||||
|
for (let item of tabContents) {
|
||||||
|
let group = item.getAttribute("tab-group")
|
||||||
|
let index = item.getAttribute("tab-index")
|
||||||
|
if (item && (group == tabGroup)) item.classList.remove("is-active")
|
||||||
|
if ((index == tabIndex) && (group == tabGroup)) item.classList.add("is-active")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let t of tabs) {
|
||||||
|
if (!t.classList.contains("disabled") && !t.classList.contains("ignore")) t.addEventListener("click", () => {
|
||||||
|
let group = t.getAttribute("tab-group")
|
||||||
|
let index = t.getAttribute("tab-index")
|
||||||
|
updateActiveTab(group, index)
|
||||||
|
updateActiveContent(group, index)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeToggles() {
|
||||||
|
let toggles = document.querySelectorAll(".card-header .card-toggle")
|
||||||
|
let contents = document.querySelectorAll(".card-content")
|
||||||
|
|
||||||
|
for (let t of toggles) {
|
||||||
|
let card = t.getAttribute("card")
|
||||||
|
if (card == null) continue
|
||||||
|
let cc = []
|
||||||
|
for (let c of contents) if (c.getAttribute("card") == card) cc.push(c)
|
||||||
|
t.style.transition = "0.2s linear"
|
||||||
|
t.addEventListener("click", (e) => {
|
||||||
|
if (e.currentTarget.style.transform == "rotate(180deg)") {
|
||||||
|
e.currentTarget.style.transform = ""
|
||||||
|
for (let c of cc) c.classList.remove("is-hidden")
|
||||||
|
} else {
|
||||||
|
e.currentTarget.style.transform = "rotate(180deg)"
|
||||||
|
for (let c of cc) c.classList.add("is-hidden")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeModals() {
|
||||||
|
let modaltriggers = $(".modal-trigger")
|
||||||
|
for (let t of modaltriggers) {
|
||||||
|
let m = t.querySelector(".modal")
|
||||||
|
let c = m.querySelectorAll("#close")
|
||||||
|
t.addEventListener("click", (e) => { m.style.display = "flex" })
|
||||||
|
for (let v of c) v.addEventListener("click", (e) => {
|
||||||
|
m.style.display = "none"
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeFormSelects() {
|
||||||
|
let formSelects = document.querySelectorAll("#form-select")
|
||||||
|
for (let s of formSelects) {
|
||||||
|
let input = s.querySelector("input#form-select-input")
|
||||||
|
let select = s.querySelector("select#form-select-select")
|
||||||
|
let options = select.querySelectorAll("option")
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
let o = options[i]
|
||||||
|
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||||
|
let enabled = (o.getAttribute("disabled") == null) ? true : false
|
||||||
|
if (value == input.value) select.selectedIndex = i
|
||||||
|
if (!enabled) o.style.display = "none"
|
||||||
|
}
|
||||||
|
select.addEventListener("change", () => {
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
let o = options[i]
|
||||||
|
if (o.selected) {
|
||||||
|
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||||
|
input.dispatchEvent(new Event("change"))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeFormPaginations() {
|
||||||
|
let formPags = document.querySelectorAll("#form-pagination")
|
||||||
|
for (let p of formPags) {
|
||||||
|
let input = p.querySelector("input#form-pagination-input")
|
||||||
|
let options = p.querySelectorAll("ul.pagination-list li a.pagination-link")
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
let o = options[i]
|
||||||
|
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||||
|
if (value == input.value) {
|
||||||
|
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||||
|
} else o.classList.remove("is-current")
|
||||||
|
o.addEventListener("click", () => {
|
||||||
|
for (let i = 0; i < options.length; i++) options[i].classList.remove("is-current")
|
||||||
|
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||||
|
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeFormValidation() {
|
||||||
|
let forms = document.querySelectorAll("form#validatable")
|
||||||
|
for (let f of forms) {
|
||||||
|
let validatableFields = f.querySelectorAll(".field#validatable")
|
||||||
|
let validatableButtons = f.querySelectorAll("button#validatable")
|
||||||
|
|
||||||
|
let getParams = (input) => {
|
||||||
|
return {
|
||||||
|
minLength: input.getAttribute("min-length"),
|
||||||
|
maxLength: input.getAttribute("max-length"),
|
||||||
|
recommendedLength: input.getAttribute("recommended-length"),
|
||||||
|
minPattern: input.getAttribute("min-pattern"),
|
||||||
|
recommendedPattern: input.getAttribute("recommended-pattern"),
|
||||||
|
isNumeric: (input.getAttribute("numeric") != null) ? true : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let isValid = (value, params) => {
|
||||||
|
let t = value.trim()
|
||||||
|
if (params.minLength != null) if (t.length < parseInt(params.minLength)) return false
|
||||||
|
if (params.maxLength != null) if (t.length > parseInt(params.maxLength)) return false
|
||||||
|
if (params.minPattern != null) if (!(new RegExp(params.minPattern).test(t))) return false
|
||||||
|
if (params.isNumeric == true) if (parseInt(t).toString() != t) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let isFormValid = () => {
|
||||||
|
for (let field of validatableFields) for (let i of field.querySelectorAll("input#validatable")) if (!isValid(i.value, getParams(i))) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let field of validatableFields) {
|
||||||
|
let inputs = field.querySelectorAll("input#validatable")
|
||||||
|
let tips = field.querySelectorAll(".help")
|
||||||
|
for (let i of inputs) i.addEventListener("change", () => {
|
||||||
|
let params = getParams(i)
|
||||||
|
// inputs
|
||||||
|
if (isValid(i.value, params)) {
|
||||||
|
i.classList.remove("is-danger")
|
||||||
|
for (let t of tips) t.classList.remove("is-danger")
|
||||||
|
} else if (!i.classList.contains("is-danger")) {
|
||||||
|
i.classList.add("is-danger")
|
||||||
|
for (let t of tips) t.classList.add("is-danger")
|
||||||
|
}
|
||||||
|
// buttons
|
||||||
|
if (isFormValid()) {
|
||||||
|
for (let b of validatableButtons) b.removeAttribute("disabled")
|
||||||
|
} else {
|
||||||
|
for (let b of validatableButtons) if (b.getAttribute("disabled") == null) b.setAttribute("disabled", "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeFormCollections() {
|
||||||
|
let collections = document.querySelectorAll("#form-collection")
|
||||||
|
for (let c of collections) {
|
||||||
|
let maxLength = parseInt(c.getAttribute("max-length"))
|
||||||
|
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||||
|
let input = c.querySelector("#form-collection-input")
|
||||||
|
let tags = c.querySelectorAll("#form-collection-tag")
|
||||||
|
let modButton = c.querySelector("#form-collection-modify")
|
||||||
|
let modTable = c.querySelector("table#multi-select")
|
||||||
|
let modInput = modTable.querySelector("input#multi-select-input")
|
||||||
|
let modTitle = modTable.querySelector("input#multi-select-title")
|
||||||
|
let deleteButtonClickEventListener = (tag) => () => {
|
||||||
|
let tvalue = JSON.parse(tag.getAttribute("value"))
|
||||||
|
let value = JSON.parse(input.value)
|
||||||
|
value.splice(value.indexOf(tvalue), 1)
|
||||||
|
if (fallbackValue != null) value.push(fallbackValue)
|
||||||
|
input.value = JSON.stringify(value)
|
||||||
|
modInput.value = input.value
|
||||||
|
modInput.dispatchEvent(new Event("change"))
|
||||||
|
tag.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let t of tags) {
|
||||||
|
let d = t.querySelector(".delete, .is-delete")
|
||||||
|
d.addEventListener("click", deleteButtonClickEventListener(t))
|
||||||
|
}
|
||||||
|
modInput.value = input.value
|
||||||
|
modInput.setAttribute("max-length", maxLength)
|
||||||
|
modInput.setAttribute("fallback", JSON.stringify(fallbackValue))
|
||||||
|
modInput.addEventListener("change", () => {
|
||||||
|
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||||
|
let oldValue = JSON.parse(input.value)
|
||||||
|
let newValue = JSON.parse(modInput.value)
|
||||||
|
let tags = c.querySelectorAll("#form-collection-tag")
|
||||||
|
for (let o of oldValue) if (!newValue.includes(o) && (o != fallbackValue)) {
|
||||||
|
for (let t of tags) if (JSON.parse(t.getAttribute("value")) == o) t.remove()
|
||||||
|
}
|
||||||
|
for (let n = 0; n < newValue.length; n++) if (!oldValue.includes(newValue[n]) && (newValue[n] != fallbackValue)) {
|
||||||
|
let tag = document.createElement("div")
|
||||||
|
tag.classList.add("control")
|
||||||
|
tag.id = "form-collection-tag"
|
||||||
|
tag.setAttribute("value", newValue[n])
|
||||||
|
tag.innerHTML = "<span class=\"tags has-addons\"><span class=\"tag is-link is-light\" id=\"form-collection-tag-title\">" + JSON.parse(modTitle.value)[n] + "</span><a class=\"tag is-delete\" /></span>"
|
||||||
|
tag.querySelector("a.is-delete").addEventListener("click", deleteButtonClickEventListener(tag))
|
||||||
|
modButton.before(tag)
|
||||||
|
}
|
||||||
|
input.value = modInput.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeMultiSelectTables() {
|
||||||
|
let tables = document.querySelectorAll("table#multi-select")
|
||||||
|
for (let table of tables) {
|
||||||
|
let valueInput = table.querySelector("input#multi-select-input")
|
||||||
|
let titleInput = table.querySelector("input#multi-select-title")
|
||||||
|
let trimValues = (values, fallback) => {
|
||||||
|
while (values.includes(fallback)) values.splice(values.indexOf(fallback), 1)
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
let fillValues = (values, fallback) => {
|
||||||
|
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||||
|
while (values.length < maxLength) values.push(fallback)
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
let lines = table.querySelectorAll("tbody tr")
|
||||||
|
let refresh = () => {
|
||||||
|
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||||
|
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||||
|
let title = []
|
||||||
|
for (let l of lines) {
|
||||||
|
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||||
|
if (value.includes(lvalue)) {
|
||||||
|
if (!l.classList.contains("is-selected")) l.classList.add("is-selected")
|
||||||
|
title[value.indexOf(lvalue)] = l.getAttribute("multi-select-title")
|
||||||
|
l.style.fontWeight = "bold"
|
||||||
|
} else {
|
||||||
|
l.classList.remove("is-selected")
|
||||||
|
l.style.fontWeight = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
titleInput.value = JSON.stringify(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let l of lines) {
|
||||||
|
l.onclick = () => {
|
||||||
|
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||||
|
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||||
|
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||||
|
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||||
|
if (value.includes(lvalue)) value.splice(value.indexOf(lvalue), 1)
|
||||||
|
else if (maxLength >= 0) {
|
||||||
|
if (value.length < maxLength) value.push(lvalue)
|
||||||
|
else alert("Cannot add more items, items are up to " + maxLength + ".")
|
||||||
|
} else value.push(lvalue)
|
||||||
|
valueInput.value = JSON.stringify(fillValues(value, fallbackValue))
|
||||||
|
refresh()
|
||||||
|
valueInput.dispatchEvent(new Event("change"))
|
||||||
|
}
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
valueInput.addEventListener("change", refresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeFormNumerics() {
|
||||||
|
let numerics = document.querySelectorAll("#form-numeric")
|
||||||
|
for (let n of numerics) {
|
||||||
|
let add = n.querySelector("#form-numeric-add")
|
||||||
|
let sub = n.querySelector("#form-numeric-sub")
|
||||||
|
let inputs = n.querySelectorAll("#form-numeric-input")
|
||||||
|
add.addEventListener("click", (e) => {
|
||||||
|
for (let i of inputs) {
|
||||||
|
let maxValue = parseFloat(i.getAttribute("max-value"))
|
||||||
|
let step = parseFloat(i.getAttribute("step"))
|
||||||
|
|
||||||
|
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||||
|
let value = (parseFloat(i.value) * 10 + step * 10) / 10
|
||||||
|
if (value * Math.sign(step) <= maxValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||||
|
}
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
sub.addEventListener("click", (e) => {
|
||||||
|
for (let i of inputs) {
|
||||||
|
let minValue = parseFloat(i.getAttribute("min-value"))
|
||||||
|
let step = parseFloat(i.getAttribute("step"))
|
||||||
|
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||||
|
let value = (parseFloat(i.value) * 10 - step * 10) / 10
|
||||||
|
if (value * Math.sign(step) >= minValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||||
|
}
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
for (let i of inputs) {
|
||||||
|
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||||
|
let value = parseFloat(i.value)
|
||||||
|
i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeUploader() {
|
||||||
|
let uploaders = document.querySelectorAll("div#uploader")
|
||||||
|
for (let uploader of uploaders) {
|
||||||
|
let input = uploader.querySelector("input#uploader-input")
|
||||||
|
let text = uploader.querySelector("input#uploader-text")
|
||||||
|
let placeholder = uploader.querySelector("#uploader-placeholder")
|
||||||
|
let remove = uploader.querySelector("#uploader-delete")
|
||||||
|
let reader = new FileReader()
|
||||||
|
input.addEventListener("change", () => {
|
||||||
|
if (input.files.length > 0) {
|
||||||
|
remove.style.display = "block"
|
||||||
|
placeholder.innerText = input.files[0].name
|
||||||
|
reader.readAsText(input.files[0])
|
||||||
|
reader.onload = () => text.value = reader.result
|
||||||
|
} else {
|
||||||
|
placeholder.innerText = ""
|
||||||
|
remove.style.display = "none"
|
||||||
|
text.value = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
remove.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
input.value = null
|
||||||
|
input.dispatchEvent(new Event("change"))
|
||||||
|
})
|
||||||
|
|
||||||
|
remove.style.display = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkImg() {
|
||||||
|
let imgs = document.querySelectorAll("#exist-or-not")
|
||||||
|
for (let img of imgs) {
|
||||||
|
let general = img.querySelector("img#general")
|
||||||
|
let specified = img.querySelector("img#specified")
|
||||||
|
|
||||||
|
if (specified.width == 0) specified.style.display = "none"
|
||||||
|
else general.style.display = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeMarqueeLabels() {
|
||||||
|
let marqueeContainers = document.querySelectorAll(".marquee-label-container")
|
||||||
|
for (let c of marqueeContainers) {
|
||||||
|
let marquees = c.querySelectorAll(".marquee-label")
|
||||||
|
for (let marquee of marquees) {
|
||||||
|
if (marquee.closest(".marquee-label-container") != c) continue
|
||||||
|
let refresh = () => {
|
||||||
|
let lpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-left"))
|
||||||
|
if (lpad == NaN) lpad = 0
|
||||||
|
let rpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-right"))
|
||||||
|
if (rpad == NaN) rpad = 20
|
||||||
|
let hpad = lpad + rpad
|
||||||
|
let speed = marquee.getAttribute("speed")
|
||||||
|
if (speed == null) speed = 1
|
||||||
|
let stopingTime = 0.5
|
||||||
|
let duration = (20 * (marquee.offsetWidth - c.offsetWidth + hpad)) / speed + 2 * stopingTime
|
||||||
|
if ((marquee.offsetWidth > 0) && (marquee.offsetWidth > c.offsetWidth - hpad)) {
|
||||||
|
marquee.animate([
|
||||||
|
{ transform: "translateX(0)", offset: 0 },
|
||||||
|
{ transform: "translateX(0)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: stopingTime / duration },
|
||||||
|
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: 1 - stopingTime / duration },
|
||||||
|
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", offset: 1 }
|
||||||
|
], { duration: (20 * (marquee.offsetWidth - c.offsetWidth) + 1000) / speed, direction: "alternate-reverse", iterations: Infinity })
|
||||||
|
} else marquee.style.animation = "none"
|
||||||
|
}
|
||||||
|
let o = new ResizeObserver(refresh)
|
||||||
|
o.observe(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeNotificatioAnimation() {
|
||||||
|
let notifications = document.querySelectorAll(".notification.temporary")
|
||||||
|
for (let n of notifications) {
|
||||||
|
let remove = n.querySelector(".delete")
|
||||||
|
let startSubmitter = n.querySelector("form.start")
|
||||||
|
let startPath = startSubmitter.getAttribute("action")
|
||||||
|
let startRequest = new XMLHttpRequest()
|
||||||
|
startRequest.open("POST", startPath, true)
|
||||||
|
startRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
let endSubmitter = n.querySelector("form.end")
|
||||||
|
let endPath = startSubmitter.getAttribute("action")
|
||||||
|
let endRequest = new XMLHttpRequest()
|
||||||
|
endRequest.open("POST", endPath, true)
|
||||||
|
endRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
if (startSubmitter != null) startRequest.send()
|
||||||
|
let end = () => {
|
||||||
|
n.style.display = "none"
|
||||||
|
if (endSubmitter != null) endRequest.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
n.style.animationPlayState = "running"
|
||||||
|
remove.addEventListener("click", end)
|
||||||
|
n.addEventListener("animationend", end)
|
||||||
|
n.addEventListener("webkitAnimationEnd", end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeCheckBoxes() {
|
||||||
|
let checks = document.querySelectorAll(".checkbox")
|
||||||
|
for (let c of checks) {
|
||||||
|
let input = c.querySelector("input[type=checkbox]")
|
||||||
|
let mark = c.querySelector(".checkmark")
|
||||||
|
let refresh = (value) => {
|
||||||
|
value = input.getAttribute("checked")
|
||||||
|
if (value == null) {
|
||||||
|
input.removeAttribute("checked")
|
||||||
|
mark.style.opacity = 0
|
||||||
|
if (!c.classList.contains("is-light")) c.classList.add("is-light")
|
||||||
|
} else {
|
||||||
|
input.setAttribute("checked", "checked")
|
||||||
|
mark.style.opacity = 100
|
||||||
|
c.classList.remove("is-light")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.addEventListener("click", () => {
|
||||||
|
let value = input.getAttribute("checked")
|
||||||
|
if (value == null) input.setAttribute("checked", "checked")
|
||||||
|
else input.removeAttribute("checked")
|
||||||
|
refresh()
|
||||||
|
})
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLoadingModal() {
|
||||||
|
let loading = document.querySelector(".loading")
|
||||||
|
setTimeout(() => (loading == null) ? null : loading.remove(), 505)
|
||||||
|
try {
|
||||||
|
let a = loading.animate([
|
||||||
|
{ offset: 0, opacity: 1 },
|
||||||
|
{ offset: 0.25, opacity: 0 },
|
||||||
|
{ offset: 1, opacity: 0 }
|
||||||
|
], { duration: 2000 })
|
||||||
|
a.onfinish = loading.remove
|
||||||
|
a.play()
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(() => {
|
||||||
|
initializeNotificatioAnimation()
|
||||||
|
initializePaginatedContent()
|
||||||
|
initializeTabs()
|
||||||
|
initializeToggles()
|
||||||
|
initializeModals()
|
||||||
|
initializeFormSelects()
|
||||||
|
initializeFormNumerics()
|
||||||
|
initializeFormPaginations()
|
||||||
|
initializeFormValidation()
|
||||||
|
initializeFormCollections()
|
||||||
|
initializeMultiSelectTables()
|
||||||
|
initializeUploader()
|
||||||
|
checkImg()
|
||||||
|
initializeMarqueeLabels()
|
||||||
|
initializeCheckBoxes()
|
||||||
|
|
||||||
|
removeLoadingModal()
|
||||||
|
})
|
||||||
|
|
||||||
750
bst@asphyxia/webui/profile_detail.pug
Normal file
750
bst@asphyxia/webui/profile_detail.pug
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user