3194 lines
120 KiB
JavaScript
3194 lines
120 KiB
JavaScript
/******/ (() => { // webpackBootstrap
|
|
/******/ "use strict";
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
|
|
// UNUSED EXPORTS: default
|
|
|
|
;// ./static/js/lib/validation.js
|
|
class Validation {
|
|
/*
|
|
isNullOrWhitespace(v) {
|
|
let txt = JSON.stringify(v).replace('/\s\g', '');
|
|
return (txt == '' || 'null');
|
|
}
|
|
*/
|
|
|
|
static isEmpty(object) {
|
|
let isEmpty = true;
|
|
if (object !== null && object !== "null" && object !== undefined && object !== "undefined") {
|
|
if (object.length == undefined) {
|
|
isEmpty = false; // object exists but isn't a collection
|
|
} else if (typeof object === "function") {
|
|
isEmpty = false; // object is reference
|
|
} else {
|
|
// string or collection
|
|
|
|
let isString = typeof object == "string";
|
|
if (isString) object = object.trim();
|
|
if (object.length > 0) {
|
|
if (isString) {
|
|
isEmpty = false; // String greater than length 0
|
|
} else {
|
|
if (typeof object[0] != "string") {
|
|
isEmpty = false;
|
|
} else {
|
|
for (let i = 0; i < object.length; i++) {
|
|
if (object[i] != "") {
|
|
isEmpty = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return isEmpty;
|
|
}
|
|
static isValidNumber(value, positiveOnly) {
|
|
return !Validation.isEmpty(value) && !isNaN(value) && (!positiveOnly || parseFloat(value) > 0);
|
|
}
|
|
static getDataContentType(params) {
|
|
var data = null;
|
|
var contentType = '';
|
|
if (!Validation.isEmpty(params)) {
|
|
if (typeof params === "string") {
|
|
data = params;
|
|
contentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
|
} else {
|
|
data = JSON.stringify(params);
|
|
contentType = "application/json; charset=UTF-8";
|
|
}
|
|
}
|
|
return {
|
|
Data: data,
|
|
ContentType: contentType
|
|
};
|
|
}
|
|
static arrayContainsItem(array, itemValue) {
|
|
var hasItem = false;
|
|
if (!Validation.isEmpty(array) && !Validation.isEmpty(itemValue)) {
|
|
var isJQueryElementArray = array[0] instanceof jQuery;
|
|
if (isJQueryElementArray) {
|
|
for (let i = 0; i < array.length; i++) {
|
|
if (document.querySelectorAll(array[i]).is(itemValue)) {
|
|
hasItem = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
var isDate = array[0] instanceof Date;
|
|
if (isDate) {
|
|
for (let i = 0; i < array.length; i++) {
|
|
if (array[i].getTime() === itemValue.getTime()) {
|
|
hasItem = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (let i = 0; i < array.length; i++) {
|
|
if (array[i] == itemValue) {
|
|
hasItem = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hasItem;
|
|
}
|
|
static dictHasKey(d, k) {
|
|
return k in d;
|
|
}
|
|
static areEqualDicts(dict1, dict2) {
|
|
const keys1 = Object.keys(dict1);
|
|
const keys2 = Object.keys(dict2);
|
|
if (keys1.length !== keys2.length) {
|
|
return false;
|
|
}
|
|
for (let key of keys1) {
|
|
if (dict1[key] !== dict2[key]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
static imageExists(url, callback) {
|
|
var img = new Image();
|
|
img.onload = function () {
|
|
callback(true);
|
|
};
|
|
img.onerror = function () {
|
|
callback(false);
|
|
};
|
|
img.src = url;
|
|
}
|
|
static toFixedOrDefault(value, decimalPlaces, defaultValue = null) {
|
|
return Validation.isValidNumber(value) ? parseFloat(value).toFixed(decimalPlaces) : defaultValue;
|
|
}
|
|
}
|
|
;// ./static/js/dom.js
|
|
|
|
|
|
class DOM {
|
|
static setElementAttributesValuesCurrentAndPrevious(element, data) {
|
|
DOM.setElementAttributeValueCurrent(element, data);
|
|
DOM.setElementAttributeValuePrevious(element, data);
|
|
}
|
|
static setElementAttributeValueCurrent(element, data) {
|
|
element.setAttribute(attrValueCurrent, data);
|
|
}
|
|
static setElementAttributeValuePrevious(element, data) {
|
|
element.setAttribute(attrValuePrevious, data);
|
|
}
|
|
static setElementValuesCurrentAndPrevious(element, data) {
|
|
DOM.setElementValueCurrent(element, data);
|
|
DOM.setElementAttributeValuePrevious(element, data);
|
|
}
|
|
static setElementValueCurrent(element, data) {
|
|
DOM.setElementAttributeValueCurrent(element, data);
|
|
let tagName = element.tagName.toUpperCase();
|
|
if (element.type === "checkbox") {
|
|
element.checked = data;
|
|
} else if (tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
|
|
element.value = data;
|
|
} else {
|
|
element.textContent = data;
|
|
}
|
|
}
|
|
static setElementValueCurrentIfEmpty(element, data) {
|
|
if (Validation.isEmpty(DOM.getElementValueCurrent(element))) {
|
|
DOM.setElementValueCurrent(element, data);
|
|
}
|
|
}
|
|
static getCellFromElement(element) {
|
|
return element.closest('td');
|
|
}
|
|
static getRowFromElement(element, flagRow) {
|
|
let selector = Validation.isEmpty(flagRow) ? 'tr' : 'tr.' + flagRow;
|
|
return element.closest(selector);
|
|
}
|
|
static getClosestParent(element, parentSelector) {
|
|
let parent = element.parentElement;
|
|
while (parent) {
|
|
if (parent.matches(parentSelector)) {
|
|
return parent;
|
|
}
|
|
parent = parent.parentElement;
|
|
}
|
|
return null;
|
|
}
|
|
static convertForm2JSON(elementForm) {
|
|
let dataForm = {};
|
|
if (Validation.isEmpty(elementForm)) {
|
|
return dataForm;
|
|
}
|
|
let containersFilter = elementForm.querySelectorAll('.' + flagContainerInput + '.' + flagFilter);
|
|
let containerFilter, labelFilter, keyFilter, filter;
|
|
for (let indexFilter = 0; indexFilter < containersFilter.length; indexFilter++) {
|
|
containerFilter = containersFilter[indexFilter];
|
|
labelFilter = containerFilter.querySelector('label');
|
|
keyFilter = labelFilter.getAttribute('for');
|
|
filter = containerFilter.querySelector(`#${keyFilter}`);
|
|
dataForm[keyFilter] = DOM.getElementValueCurrent(filter);
|
|
}
|
|
return dataForm;
|
|
}
|
|
static loadPageBody(contentNew) {
|
|
let pageBody = document.querySelector(idPageBody);
|
|
pageBody.innerHTML = contentNew;
|
|
}
|
|
static getHashPageCurrent() {
|
|
const hashPageCurrent = document.body.dataset.page;
|
|
return hashPageCurrent;
|
|
}
|
|
static updateAndCheckIsElementDirty(element) {
|
|
element.setAttribute(attrValueCurrent, DOM.getElementValueCurrent(element));
|
|
return DOM.isElementDirty(element);
|
|
}
|
|
static isElementDirty(element) {
|
|
let isDirty = element.getAttribute(attrValuePrevious) != element.getAttribute(attrValueCurrent);
|
|
DOM.handleDirtyElement(element, isDirty);
|
|
return isDirty;
|
|
}
|
|
static handleDirtyElement(element, isDirty) {
|
|
DOM.toggleElementHasClassnameFlag(element, isDirty, flagDirty);
|
|
}
|
|
static toggleElementHasClassnameFlag(element, elementHasFlag, flag) {
|
|
let elementAlreadyHasFlag = element.classList.contains(flag);
|
|
if (elementHasFlag == elementAlreadyHasFlag) return;
|
|
if (elementHasFlag) {
|
|
element.classList.add(flag);
|
|
} else {
|
|
element.classList.remove(flag);
|
|
}
|
|
}
|
|
static hasDirtyChildrenContainer(container) {
|
|
if (container == null) return false;
|
|
return container.querySelector('.' + flagDirty) != null;
|
|
}
|
|
static hasDirtyChildrenNotDeletedContainer(container) {
|
|
if (container == null || container.classList.contains(flagDelete)) return false;
|
|
return container.querySelector('.' + flagDirty + ':not(.' + flagDelete + ', .' + flagDelete + ' *)') != null;
|
|
}
|
|
static getElementValueCurrent(element) {
|
|
let returnVal = '';
|
|
if (!Validation.isEmpty(element)) {
|
|
let tagName = element.tagName.toUpperCase();
|
|
if (element.type === "checkbox") {
|
|
returnVal = element.checked;
|
|
}
|
|
/*
|
|
else if (element.classList.contains(flagIsDatePicker)) {
|
|
returnVal = getDatePickerDate(element, adjust4DayLightSavings);
|
|
}
|
|
*/else if (tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
|
|
returnVal = element.value;
|
|
} else if (element.classList.contains(flagButton) && element.classList.contains(flagActive)) {
|
|
// tagName === 'BUTTON'
|
|
returnVal = element.classList.contains(flagDelete);
|
|
} else if (tagName === 'TD') {
|
|
returnVal = DOM.getElementAttributeValueCurrent(element);
|
|
} else if (tagName == 'SVG' && element.classList.contains(flagCheckbox)) {
|
|
returnVal = element.classList.contains(flagIsChecked);
|
|
} else {
|
|
returnVal = element.textContent;
|
|
}
|
|
}
|
|
if (Validation.isEmpty(returnVal)) returnVal = '';
|
|
return returnVal;
|
|
}
|
|
static getElementAttributeValueCurrent(element) {
|
|
if (Validation.isEmpty(element)) return null;
|
|
return element.getAttribute(attrValueCurrent);
|
|
}
|
|
static getElementAttributeValuePrevious(element) {
|
|
if (Validation.isEmpty(element)) return null;
|
|
return element.getAttribute(attrValuePrevious);
|
|
}
|
|
/* base_table.handleChangeElementCellTable
|
|
static updateAndCheckIsTableElementDirty(element) {
|
|
let wasDirty = DOM.isElementDirty(element);
|
|
let row = DOM.getRowFromElement(element);
|
|
let wasDirtyRow = DOM.hasDirtyChildrenNotDeletedContainer(row);
|
|
let isDirty = DOM.updateAndCheckIsElementDirty(element);
|
|
let cell = DOM.getCellFromElement(element);
|
|
Utils.consoleLogIfNotProductionEnvironment({element, row, cell, isDirty, wasDirty});
|
|
if (isDirty != wasDirty) {
|
|
DOM.handleDirtyElement(cell, isDirty);
|
|
let isDirtyRow = DOM.hasDirtyChildrenNotDeletedContainer(row);
|
|
Utils.consoleLogIfNotProductionEnvironment({isDirtyRow, wasDirtyRow});
|
|
if (isDirtyRow != wasDirtyRow) {
|
|
DOM.handleDirtyElement(row, isDirtyRow);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
static scrollToElement(parent, element) {
|
|
// REQUIRED: parent has scroll-bar
|
|
parent.scrollTop(parent.scrollTop() + (element.offset().top - parent.offset().top));
|
|
}
|
|
static isElementInContainer(container, element) {
|
|
if (typeof jQuery === 'function') {
|
|
if (container instanceof jQuery) container = container[0];
|
|
if (element instanceof jQuery) element = element[0];
|
|
}
|
|
var containerBounds = container.getBoundingClientRect();
|
|
var elementBounds = element.getBoundingClientRect();
|
|
return containerBounds.top <= elementBounds.top && containerBounds.left <= elementBounds.left && elementBounds.top + elementBounds.height <= containerBounds.top + containerBounds.height && elementBounds.left + elementBounds.width <= containerBounds.left + containerBounds.width;
|
|
}
|
|
static alertError(errorType, errorText) {
|
|
alert(errorType + '\n' + errorText);
|
|
}
|
|
static createOptionUnselectedProductVariation() {
|
|
return {
|
|
[flagProductVariationType]: {
|
|
[flagNameAttrOptionText]: [flagName],
|
|
[flagNameAttrOptionValue]: [attrIdProductVariationType],
|
|
[flagName]: 'Select Variation Type',
|
|
[attrIdProductVariationType]: 0
|
|
},
|
|
[flagProductVariation]: {
|
|
[flagNameAttrOptionText]: [flagName],
|
|
[flagNameAttrOptionValue]: [attrIdProductVariation],
|
|
[flagName]: 'Select Variation',
|
|
[attrIdProductVariation]: 0
|
|
}
|
|
};
|
|
}
|
|
static createOption(optionJson) {
|
|
if (Validation.isEmpty(optionJson)) optionJson = {
|
|
text: 'Select',
|
|
value: 0
|
|
};
|
|
let option = document.createElement('option');
|
|
option.value = optionJson.value;
|
|
option.textContent = optionJson.text;
|
|
option.selected = optionJson.selected;
|
|
return option;
|
|
}
|
|
static escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
static unescapeHtml(html) {
|
|
const div = document.createElement('div');
|
|
div.innerHTML = html;
|
|
return div.textContent || div.innerText || '';
|
|
}
|
|
}
|
|
;// ./static/js/api.js
|
|
|
|
class API {
|
|
static getCsrfToken() {
|
|
return document.querySelector(idCSRFToken).getAttribute('content');
|
|
}
|
|
static async request(hashEndpoint, method = 'GET', data = null, params = null) {
|
|
const url = API.getUrlFromHash(hashEndpoint, params);
|
|
const csrfToken = API.getCsrfToken();
|
|
const options = {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
[flagCsrfToken]: csrfToken
|
|
}
|
|
};
|
|
if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
|
data = {
|
|
...data,
|
|
[flagCsrfToken]: csrfToken
|
|
};
|
|
options.body = JSON.stringify(data);
|
|
}
|
|
try {
|
|
const response = await fetch(url, options);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('API request failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
static getUrlFromHash(hash, params = null) {
|
|
if (hash == null) hash = hashPageHome;
|
|
let url = API.parameteriseUrl(_pathHost + hash, params);
|
|
return url;
|
|
}
|
|
static parameteriseUrl(url, params) {
|
|
if (params) {
|
|
url += '?' + new URLSearchParams(params).toString();
|
|
}
|
|
return url;
|
|
}
|
|
static goToUrl(url) {
|
|
window.location.href = url;
|
|
}
|
|
static goToHash(hash, params = null) {
|
|
const url = API.getUrlFromHash(hash, params);
|
|
API.goToUrl(url);
|
|
}
|
|
|
|
// specific api calls
|
|
/* Example:
|
|
getUsers: () => request('/users'),
|
|
getUserById: (id) => request(`/users/${id}`),
|
|
createUser: (userData) => request('/users', 'POST', userData),
|
|
updateUser: (id, userData) => request(`/users/${id}`, 'PUT', userData),
|
|
deleteUser: (id) => request(`/users/${id}`, 'DELETE'),
|
|
*/
|
|
|
|
// User
|
|
// user
|
|
static async loginUser() {
|
|
let callback = {};
|
|
callback[flagCallback] = DOM.getHashPageCurrent();
|
|
return await API.request(hashPageUserLogin, 'POST', callback);
|
|
}
|
|
static async saveUsers(users, formFilters, comment) {
|
|
let dataRequest = {};
|
|
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
|
|
dataRequest[flagUser] = users;
|
|
dataRequest[flagComment] = comment;
|
|
return await API.request(hashSaveUserUser, 'POST', dataRequest);
|
|
}
|
|
|
|
// MTG Game API methods
|
|
static async saveGame(game, formFilters, comment) {
|
|
let dataRequest = {};
|
|
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
|
|
dataRequest[flagGame] = game;
|
|
dataRequest[flagComment] = comment;
|
|
return await API.request(hashSaveGame, 'POST', dataRequest);
|
|
}
|
|
static async getGamePlayers(gameId) {
|
|
const url = `/mtg/api/game/${gameId}/players`;
|
|
return await API.request(url, 'GET');
|
|
}
|
|
static async saveGamePlayers(players, formFilters, comment) {
|
|
let dataRequest = {};
|
|
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
|
|
dataRequest[flagPlayer] = players;
|
|
dataRequest[flagComment] = comment;
|
|
return await API.request(hashSaveGamePlayer, 'POST', dataRequest);
|
|
}
|
|
static async getGameRounds(gameId) {
|
|
const url = `/mtg/api/game/${gameId}/rounds`;
|
|
return await API.request(url, 'GET');
|
|
}
|
|
static async getGameDamageRecords(gameId) {
|
|
const url = `/mtg/api/game/${gameId}/damage-records`;
|
|
return await API.request(url, 'GET');
|
|
}
|
|
static async saveGameRoundPlayerDamages(rounds, damages, formFilters, comment) {
|
|
let dataRequest = {};
|
|
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
|
|
dataRequest[flagDamage] = damages;
|
|
dataRequest[flagRound] = rounds;
|
|
dataRequest[flagComment] = comment;
|
|
return await API.request(hashSaveGameRoundPlayerDamage, 'POST', dataRequest);
|
|
}
|
|
}
|
|
;// ./static/js/lib/business_objects/business_objects.js
|
|
|
|
class BusinessObjects {
|
|
static getOptionJsonFromObjectJsonAndKeys(objectJson, keyText, keyValue, valueSelected = null) {
|
|
return {
|
|
text: objectJson[keyText],
|
|
value: objectJson[keyValue],
|
|
selected: objectJson[keyValue] == valueSelected
|
|
};
|
|
}
|
|
static getOptionJsonFromObjectJson(objectJson, valueSelected = null) {
|
|
let keyText = objectJson[flagNameAttrOptionText];
|
|
let keyValue = objectJson[flagNameAttrOptionValue];
|
|
// Utils.consoleLogIfNotProductionEnvironment({objectJson, keyText, keyValue});
|
|
return BusinessObjects.getOptionJsonFromObjectJsonAndKeys(objectJson, keyText, keyValue, valueSelected);
|
|
}
|
|
static getObjectText(objectJson) {
|
|
return objectJson == null ? '' : objectJson[objectJson[flagNameAttrOptionText]];
|
|
}
|
|
static getListObjectsFromIdDictAndCsv(idDict, idCsv) {
|
|
let listObjects = [];
|
|
let ids = idCsv.split(',');
|
|
for (let id of ids) {
|
|
listObjects.push(idDict[id]);
|
|
}
|
|
return listObjects;
|
|
}
|
|
}
|
|
;// ./static/js/lib/events.js
|
|
class Events {
|
|
static initialiseEventHandler(selectorElement, classInitialised, eventHandler) {
|
|
document.querySelectorAll(selectorElement).forEach(function (element) {
|
|
if (element.classList.contains(classInitialised)) return;
|
|
eventHandler(element);
|
|
element.classList.add(classInitialised);
|
|
});
|
|
}
|
|
static hookupEventHandler(eventType, selector, callback) {
|
|
Events.initialiseEventHandler(selector, flagInitialised, element => {
|
|
element.addEventListener(eventType, event => {
|
|
event.stopPropagation();
|
|
callback(event, element);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
;// ./static/js/lib/local_storage.js
|
|
|
|
class LocalStorage {
|
|
/*
|
|
function getPageLocalStorage(pageHash) {
|
|
|
|
let ls;
|
|
try {
|
|
ls = JSON.parse(localStorage.getItem(pageHash));
|
|
} catch {
|
|
|
|
}
|
|
|
|
if (Validation.isEmpty(ls)) return {}
|
|
|
|
return ls;
|
|
}
|
|
function getPageLocalStorageCurrent() {
|
|
|
|
return JSON.parse(localStorage.getItem(hashPageCurrent));
|
|
}
|
|
|
|
function setPageLocalStorage(pageHash, newLS) {
|
|
|
|
localStorage.setItem(pageHash, JSON.stringify(newLS));
|
|
}
|
|
|
|
function clearPageLocalStorage(pageHash) {
|
|
localStorage.removeItem(pageHash);
|
|
}
|
|
|
|
function setupPageLocalStorage(pageHash) {
|
|
|
|
let ls = getPageLocalStorage(pageHash);
|
|
|
|
if (Validation.isEmpty(ls)) ls = {};
|
|
|
|
setPageLocalStorage(pageHash, ls);
|
|
}
|
|
*/
|
|
|
|
static getLocalStorage(key) {
|
|
return JSON.parse(localStorage.getItem(key));
|
|
}
|
|
static setLocalStorage(key, newLS) {
|
|
localStorage.setItem(key, JSON.stringify(newLS));
|
|
}
|
|
|
|
/*
|
|
function setupPageLocalStorageNext(pageHashNext) {
|
|
let lsOld = getPageLocalStorage(hashPageCurrent);
|
|
hashPageCurrent = pageHashNext;
|
|
clearPageLocalStorage(hashPageCurrent);
|
|
setupPageLocalStorage(hashPageCurrent);
|
|
let lsNew = getPageLocalStorage(hashPageCurrent);
|
|
lsNew[keyBasket] = (keyBasket in lsOld) ? lsOld[keyBasket] : {'items': []};
|
|
setPageLocalStorage(hashPageCurrent, lsNew);
|
|
}
|
|
*/
|
|
}
|
|
;// ./static/js/lib/utils.js
|
|
// Utility functions
|
|
/*
|
|
function $(selector) {
|
|
return document.querySelector(selector);
|
|
}
|
|
|
|
function $$(selector) {
|
|
return document.querySelectorAll(selector);
|
|
}
|
|
*/
|
|
class utils_Utils {
|
|
static getListFromDict(dict) {
|
|
let list = [];
|
|
for (let key in dict) {
|
|
list.push(dict[key]);
|
|
}
|
|
return list;
|
|
}
|
|
static consoleLogIfNotProductionEnvironment(message) {
|
|
if (environment.is_production != "true") {
|
|
console.log(message);
|
|
}
|
|
}
|
|
}
|
|
;// ./static/js/components/common/temporary/overlay_confirm.js
|
|
|
|
class OverlayConfirm {
|
|
static hookup(callbackSuccess) {
|
|
Events.initialiseEventHandler(idOverlayConfirm + ' button.' + flagCancel, flagInitialised, buttonCancel => {
|
|
buttonCancel.addEventListener('click', () => {
|
|
let overlay = document.querySelector(idOverlayConfirm);
|
|
overlay.style.visibility = 'hidden';
|
|
});
|
|
});
|
|
Events.initialiseEventHandler(idOverlayConfirm + ' button.' + flagSubmit, flagInitialised, buttonConfirm => {
|
|
buttonConfirm.addEventListener('click', () => {
|
|
let overlay = document.querySelector(idOverlayConfirm);
|
|
let textarea = overlay.querySelector('textarea');
|
|
overlay.style.visibility = 'hidden';
|
|
callbackSuccess(textarea.value);
|
|
});
|
|
});
|
|
}
|
|
static show() {
|
|
let overlay = document.querySelector(idOverlayConfirm);
|
|
overlay.classList.remove(flagIsCollapsed);
|
|
overlay.style.visibility = 'visible';
|
|
}
|
|
}
|
|
;// ./static/js/pages/base.js
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BasePage {
|
|
constructor(router) {
|
|
if (!router) {
|
|
throw new Error("Router is required");
|
|
} else {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("initialising with router: ", router);
|
|
}
|
|
this.router = router;
|
|
this.title = titlePageCurrent;
|
|
if (this.constructor === BasePage) {
|
|
throw new Error("Cannot instantiate abstract class");
|
|
}
|
|
if (!this.constructor.hash) {
|
|
throw new Error(`Class ${this.constructor.name} must have a static hash attribute.`);
|
|
}
|
|
}
|
|
initialize() {
|
|
throw new Error("Method 'initialize()' must be implemented.");
|
|
}
|
|
sharedInitialize() {
|
|
this.logInitialisation();
|
|
this.hookupCommonElements();
|
|
}
|
|
logInitialisation() {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Initialising ' + this.title + ' page');
|
|
}
|
|
hookupCommonElements() {
|
|
// hookupVideos();
|
|
this.hookupLogos();
|
|
this.hookupNavigation();
|
|
this.hookupOverlays();
|
|
}
|
|
hookupLogos() {
|
|
Events.hookupEventHandler("click", "." + flagImageLogo + "," + "." + flagLogo, (event, element) => {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('clicking logo');
|
|
this.router.navigateToHash(hashPageHome);
|
|
});
|
|
}
|
|
/*
|
|
hookupEventHandler(eventType, selector, callback) {
|
|
Events.initialiseEventHandler(selector, flagInitialised, (element) => {
|
|
element.addEventListener(eventType, (event) => {
|
|
event.stopPropagation();
|
|
callback(event, element);
|
|
});
|
|
});
|
|
}
|
|
*/
|
|
hookupNavigation() {
|
|
Events.hookupEventHandler("click", idButtonHamburger, (event, element) => {
|
|
let overlayHamburger = document.querySelector(idOverlayHamburger);
|
|
if (overlayHamburger.classList.contains(flagIsCollapsed)) {
|
|
overlayHamburger.classList.remove(flagIsCollapsed);
|
|
overlayHamburger.classList.add(flagExpanded);
|
|
} else {
|
|
overlayHamburger.classList.remove(flagExpanded);
|
|
overlayHamburger.classList.add(flagIsCollapsed);
|
|
}
|
|
});
|
|
this.hookupButtonsNavUserAccount();
|
|
this.hookupButtonsNavUserLogout();
|
|
this.hookupButtonsNavUserLogin();
|
|
}
|
|
hookupButtonsNav(buttonSelector) {
|
|
Events.hookupEventHandler("click", buttonSelector, (event, button) => {
|
|
let pageHash = buttonSelector.getAttribute('href');
|
|
this.router.navigateToHash(pageHash);
|
|
});
|
|
}
|
|
hookupButtonsNavUserAccount() {
|
|
// this.hookupButtonsNav('.' + flagNavUserAccount);
|
|
}
|
|
hookupButtonsNavUserLogout() {
|
|
// this.hookupButtonsNav('.' + flagNavUserLogout);
|
|
}
|
|
hookupButtonsNavUserLogin() {
|
|
Events.hookupEventHandler("click", '.' + flagNavUserLogin, (event, navigator) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
this.leave();
|
|
API.loginUser().then(response => {
|
|
if (response.Success) {
|
|
window.location.href = response[flagCallback];
|
|
} else {
|
|
DOM.alertError("Error", response.Message);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
hookupOverlays() {
|
|
this.hookupOverlayFromId(idOverlayConfirm);
|
|
this.hookupOverlayFromId(idOverlayError);
|
|
}
|
|
hookupOverlayFromId(idOverlay) {
|
|
Events.initialiseEventHandler(idOverlay, flagInitialised, overlay => {
|
|
overlay.querySelector('button.' + flagCancel).addEventListener("click", event => {
|
|
event.stopPropagation();
|
|
overlay.style.display = 'none';
|
|
});
|
|
});
|
|
}
|
|
hookupButtonSave() {
|
|
Events.initialiseEventHandler('.' + flagContainer + '.' + flagSave + '.' + flagCancel + ' button.' + flagSave, flagInitialised, button => {
|
|
button.addEventListener("click", event => {
|
|
event.stopPropagation();
|
|
button = event.target;
|
|
if (button.classList.contains(flagIsCollapsed)) return;
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('saving page: ', this.title);
|
|
OverlayConfirm.show();
|
|
});
|
|
});
|
|
}
|
|
leave() {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Leaving ' + this.title + ' page');
|
|
if (this.constructor === BasePage) {
|
|
throw new Error("Must implement leave() method.");
|
|
}
|
|
}
|
|
setLocalStoragePage(dataPage) {
|
|
LocalStorage.setLocalStorage(this.hash, dataPage);
|
|
}
|
|
getLocalStoragePage() {
|
|
return LocalStorage.getLocalStorage(this.hash);
|
|
}
|
|
toggleShowButtonsSaveCancel(show, buttonContainerSelector = null) {
|
|
// , buttonSave = null, buttonCancel = null
|
|
if (Validation.isEmpty(buttonContainerSelector)) buttonContainerSelector = '.' + flagContainer + '.' + flagSave + '.' + flagCancel;
|
|
let buttonSave = document.querySelector(buttonContainerSelector + ' ' + idButtonSave);
|
|
if (buttonSave == null) return;
|
|
let buttonCancel = document.querySelector(buttonContainerSelector + ' ' + idButtonCancel);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
show,
|
|
buttonContainerSelector,
|
|
buttonCancel,
|
|
buttonSave
|
|
});
|
|
if (show) {
|
|
buttonCancel.classList.remove(flagIsCollapsed);
|
|
buttonSave.classList.remove(flagIsCollapsed);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('showing buttons');
|
|
} else {
|
|
buttonCancel.classList.add(flagIsCollapsed);
|
|
buttonSave.classList.add(flagIsCollapsed);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('hiding buttons');
|
|
}
|
|
}
|
|
static isDirtyFilter(filter) {
|
|
let isDirty = DOM.updateAndCheckIsElementDirty(filter);
|
|
if (isDirty) document.querySelectorAll(idTableMain + ' tbody tr').remove();
|
|
return isDirty;
|
|
}
|
|
}
|
|
;// ./static/js/components/common/temporary/overlay_error.js
|
|
|
|
class OverlayError {
|
|
static hookup() {
|
|
Events.initialiseEventHandler(idOverlayError + ' button.' + flagCancel, flagInitialised, buttonCancel => {
|
|
buttonCancel.addEventListener('click', () => {
|
|
let overlay = document.querySelector(idOverlayError);
|
|
overlay.style.visibility = 'hidden';
|
|
});
|
|
});
|
|
}
|
|
static show(msgError) {
|
|
let overlay = document.querySelector(idOverlayError);
|
|
let labelError = overlay.querySelector(idLabelError);
|
|
labelError.innerText = msgError;
|
|
overlay.style.visibility = 'visible';
|
|
}
|
|
}
|
|
;// ./static/js/pages/base_table.js
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TableBasePage extends BasePage {
|
|
// static hash
|
|
// static attrIdRowObject
|
|
// callSaveTableContent
|
|
|
|
constructor(router) {
|
|
super(router);
|
|
this.cursorYInitial = null;
|
|
this.rowInitial = null;
|
|
this.placeholder = null;
|
|
this.dragSrcEl = null;
|
|
this.dragSrcRow = null;
|
|
this.hookupTableCellDdls = this.hookupTableCellDdls.bind(this);
|
|
}
|
|
initialize(isPopState = false) {
|
|
throw new Error("Must implement initialize() method.");
|
|
}
|
|
sharedInitialize(isPopState = false, isSinglePageApp = false) {
|
|
if (!isPopState) {
|
|
super.sharedInitialize();
|
|
this.hookupFilters();
|
|
this.hookupButtonsSaveCancel();
|
|
this.hookupTableMain();
|
|
OverlayConfirm.hookup(() => {
|
|
if (isSinglePageApp) {
|
|
this.saveRecordsTableDirtySinglePageApp();
|
|
} else {
|
|
this.saveRecordsTableDirty();
|
|
}
|
|
});
|
|
} else {
|
|
let dataPage = this.getLocalStoragePage();
|
|
let filters = dataPage[flagFormFilters];
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let filtersDefault = DOM.convertForm2JSON(formFilters);
|
|
if (!Validation.areEqualDicts(filters, filtersDefault)) {
|
|
this.callFilterTableContent();
|
|
}
|
|
}
|
|
}
|
|
hookupFilters() {
|
|
if (this.constructor === TableBasePage) {
|
|
throw new Error("Subclass of TableBasePage must implement method hookupFilters().");
|
|
}
|
|
}
|
|
sharedHookupFilters() {
|
|
this.hookupButtonApplyFilters();
|
|
this.hookupSearchTextFilter();
|
|
}
|
|
hookupFilterActive() {
|
|
let filterSelector = idFormFilters + ' #' + flagActiveOnly;
|
|
let filterActiveOld = document.querySelector(filterSelector);
|
|
filterActiveOld.removeAttribute('id');
|
|
let parentDiv = filterActiveOld.parentElement;
|
|
let isChecked = DOM.getElementAttributeValuePrevious(parentDiv) == "True";
|
|
let filterActiveNew = document.querySelector(idFormFilters + ' div.' + flagActiveOnly + '.' + flagContainerInput + ' svg.' + flagActiveOnly);
|
|
filterActiveNew.setAttribute('id', flagActiveOnly);
|
|
if (isChecked) filterActiveNew.classList.add(flagIsChecked);
|
|
Events.hookupEventHandler("click", filterSelector, (event, filterActive) => {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
filterActive
|
|
});
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
[filterActive.tagName]: filterActive.tagName
|
|
});
|
|
let svgElement = filterActive.tagName.toUpperCase() == 'SVG' ? filterActive : filterActive.parentElement;
|
|
let wasChecked = svgElement.classList.contains(flagIsChecked);
|
|
if (wasChecked) {
|
|
svgElement.classList.remove(flagIsChecked);
|
|
} else {
|
|
svgElement.classList.add(flagIsChecked);
|
|
}
|
|
return this.handleChangeFilter(event, filterActive);
|
|
});
|
|
let filter = document.querySelector(filterSelector);
|
|
let filterValuePrevious = DOM.getElementValueCurrent(filter);
|
|
filter.setAttribute(attrValueCurrent, filterValuePrevious);
|
|
filter.setAttribute(attrValuePrevious, filterValuePrevious);
|
|
}
|
|
hookupFilter(filterFlag, handler = (event, filter) => {
|
|
return this.handleChangeFilter(event, filter);
|
|
}) {
|
|
let filterSelector = idFormFilters + ' #' + filterFlag;
|
|
Events.hookupEventHandler("change", filterSelector, handler);
|
|
let filter = document.querySelector(filterSelector);
|
|
let filterValuePrevious = DOM.getElementValueCurrent(filter);
|
|
filter.setAttribute(attrValueCurrent, filterValuePrevious);
|
|
filter.setAttribute(attrValuePrevious, filterValuePrevious);
|
|
}
|
|
handleChangeFilter(event, filter) {
|
|
let isDirtyFilter = DOM.updateAndCheckIsElementDirty(filter);
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let areDirtyFilters = isDirtyFilter || DOM.hasDirtyChildrenContainer(formFilters);
|
|
let tbody = document.querySelector(idTableMain + ' tbody');
|
|
let rows = tbody.querySelectorAll(':scope > tr');
|
|
rows.forEach(row => {
|
|
if (areDirtyFilters && !row.classList.contains(flagIsCollapsed)) row.classList.add(flagIsCollapsed);
|
|
if (!areDirtyFilters && row.classList.contains(flagIsCollapsed)) {
|
|
row.classList.remove(flagIsCollapsed);
|
|
let dirtyInputs = row.querySelectorAll('input.' + flagDirty);
|
|
dirtyInputs.forEach(dirtyInput => {
|
|
dirtyInput.value = DOM.getElementAttributeValueCurrent(dirtyInput);
|
|
});
|
|
}
|
|
});
|
|
if (areDirtyFilters) {
|
|
/*
|
|
tbody.querySelectorAll('tr').forEach((tr) => {
|
|
if (!DOM.hasDirtyChildrenContainer(tr)) tr.remove();
|
|
});
|
|
*/
|
|
tbody.innerHTML = '<div>Press "Apply Filters" to refresh the table.</div>' + tbody.innerHTML;
|
|
if (!tbody.classList.contains(flagIsCollapsed)) tbody.classList.add(flagIsCollapsed);
|
|
} else {
|
|
let isDirtyLabel = tbody.querySelector(":scope > div");
|
|
if (isDirtyLabel != null) isDirtyLabel.remove();
|
|
if (tbody.classList.contains(flagIsCollapsed)) tbody.classList.remove(flagIsCollapsed);
|
|
let initialisedElements = tbody.querySelectorAll('.' + flagInitialised);
|
|
initialisedElements.forEach(initialisedElement => {
|
|
initialisedElement.classList.remove(flagInitialised);
|
|
});
|
|
this.hookupTableMain();
|
|
}
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
hookupFilterIsNotEmpty() {
|
|
this.hookupFilter(flagIsNotEmpty);
|
|
}
|
|
hookupButtonApplyFilters() {
|
|
Events.hookupEventHandler("click", idButtonApplyFilters, (event, button) => {
|
|
event.stopPropagation();
|
|
this.callFilterTableContent();
|
|
});
|
|
}
|
|
hookupSearchTextFilter() {
|
|
this.hookupFilter(flagSearch);
|
|
}
|
|
hookupFilterCommandCategory() {
|
|
this.hookupFilter(attrIdCommandCategory, (event, filterCommandCategory) => {
|
|
this.handleChangeFilter();
|
|
let isDirtyFilter = filterCommandCategory.classList.contains(flagDirty);
|
|
let idCommandCategory = DOM.getElementValueCurrent(filterCommandCategory);
|
|
console.log("filter commands unsorted");
|
|
console.log(utils_Utils.getListFromDict(filterCommands));
|
|
let commandsInCategory = utils_Utils.getListFromDict(filterCommands).filter(command => command[attrIdCommandCategory] == idCommandCategory);
|
|
let sortedCommands = commandsInCategory.sort((a, b) => a[flagName].localeCompare(b[flagName]));
|
|
let filterCommand = document.querySelector(idFormFilters + ' .' + flagCommand);
|
|
let idCommandPrevious = DOM.getElementAttributeValuePrevious(filterCommand);
|
|
filterCommand.innerHTML = '';
|
|
let optionJson, option;
|
|
option = DOM.createOption(null);
|
|
filterCommand.appendChild(option);
|
|
sortedCommands.forEach(command => {
|
|
optionJson = BusinessObjects.getOptionJsonFromObjectJson(command, idCommandPrevious);
|
|
option = DOM.createOption(optionJson);
|
|
filterCommand.appendChild(option);
|
|
});
|
|
filterCommand.dispatchEvent(new Event('change'));
|
|
return isDirtyFilter;
|
|
});
|
|
}
|
|
hookupFilterCommand() {
|
|
this.hookupFilter(attrIdCommand);
|
|
}
|
|
hookupFilterLocation() {
|
|
this.hookupFilter(attrIdLocation);
|
|
}
|
|
/*
|
|
getAndLoadFilteredTableContent = () => {
|
|
this.callFilterTableContent()
|
|
.catch(error => console.error('Error:', error));
|
|
}
|
|
*/
|
|
static getFormFilters() {
|
|
return document.querySelector(idFormFilters);
|
|
}
|
|
callFilterTableContent() {
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let filtersJson = DOM.convertForm2JSON(formFilters);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("callFilterTableContent");
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("formFilters");
|
|
utils_Utils.consoleLogIfNotProductionEnvironment(formFilters);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("filtersJson");
|
|
utils_Utils.consoleLogIfNotProductionEnvironment(filtersJson);
|
|
this.leave();
|
|
API.goToHash(this.constructor.hash, filtersJson);
|
|
}
|
|
callbackLoadTableContent(response) {
|
|
let table = TableBasePage.getTableMain();
|
|
let bodyTable = table.querySelector('tbody');
|
|
bodyTable.querySelectorAll('tr').forEach(function (row) {
|
|
row.remove();
|
|
});
|
|
let rowsJson = response.data[flagRows];
|
|
if (!Validation.isEmpty(rowsJson) && rowsJson.every(row => row.hasOwnProperty('display_order'))) {
|
|
rowsJson = rowsJson.sort((a, b) => a.display_order - b.display_order);
|
|
}
|
|
rowsJson.forEach(this.loadRowTable.bind(this));
|
|
this.hookupTableMain();
|
|
}
|
|
static getTableMain() {
|
|
return document.querySelector(idTableMain);
|
|
}
|
|
loadRowTable(rowJson) {
|
|
throw new Error("Subclass of TableBasePage must implement method loadRowTable().");
|
|
}
|
|
getAndLoadFilteredTableContentSinglePageApp() {
|
|
this.callFilterTableContent().then(data => {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Table data received:', data);
|
|
this.callbackLoadTableContent(data);
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
hookupButtonsSaveCancel() {
|
|
this.hookupButtonSave();
|
|
this.hookupButtonCancel();
|
|
this.toggleShowButtonsSaveCancel(false);
|
|
}
|
|
saveRecordsTableDirty() {
|
|
let records = this.getTableRecords(true);
|
|
if (records.length == 0) {
|
|
OverlayError.show('No records to save');
|
|
return;
|
|
}
|
|
let formElement = TableBasePage.getFormFilters();
|
|
let comment = DOM.getElementValueCurrent(document.querySelector(idTextareaConfirm));
|
|
this.callSaveTableContent(records, formElement, comment).then(data => {
|
|
if (data[flagStatus] == flagSuccess) {
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Records saved!');
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Data received:', data);
|
|
}
|
|
this.callFilterTableContent();
|
|
} else {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("error: ", data[flagMessage]);
|
|
OverlayError.show(data[flagMessage]);
|
|
}
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
getTableRecords(dirtyOnly = false) {
|
|
let records = [];
|
|
let record;
|
|
document.querySelectorAll(idTableMain + ' > tbody > tr').forEach(row => {
|
|
if (dirtyOnly && !DOM.hasDirtyChildrenContainer(row)) return;
|
|
record = this.getJsonRow(row);
|
|
records.push(record);
|
|
});
|
|
return records;
|
|
}
|
|
getJsonRow(row) {
|
|
throw new Error("Subclass of TableBasePage must implement method getJsonRow().");
|
|
}
|
|
saveRecordsTableDirtySinglePageApp() {
|
|
let records = this.getTableRecords(true);
|
|
if (records.length == 0) {
|
|
OverlayError.show('No records to save');
|
|
return;
|
|
}
|
|
let formElement = TableBasePage.getFormFilters();
|
|
let comment = DOM.getElementValueCurrent(document.querySelector(idTextareaConfirm));
|
|
this.callSaveTableContent(records, formElement, comment).then(data => {
|
|
if (data[flagStatus] == flagSuccess) {
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Records saved!');
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Data received:', data);
|
|
}
|
|
this.callbackLoadTableContent(data);
|
|
} else {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("error: ", data[flagMessage]);
|
|
OverlayError.show(data[flagMessage]);
|
|
}
|
|
}).catch(error => console.error('Error:', error));
|
|
}
|
|
hookupButtonCancel() {
|
|
Events.initialiseEventHandler('.' + flagContainer + '.' + flagSave + '.' + flagCancel + ' button.' + flagCancel, flagInitialised, button => {
|
|
button.addEventListener("click", event => {
|
|
event.stopPropagation();
|
|
button = event.target;
|
|
if (button.classList.contains(flagIsCollapsed)) return;
|
|
this.callFilterTableContent();
|
|
});
|
|
button.classList.add(flagIsCollapsed);
|
|
});
|
|
}
|
|
handleClickAddRowTable(event, button) {
|
|
event.stopPropagation();
|
|
_rowBlank.setAttribute(this.constructor.attrIdRowObject, -1 - _rowBlank.getAttribute(this.constructor.attrIdRowObject));
|
|
let tbody = document.querySelector(idTableMain + ' tbody');
|
|
if (tbody.classList.contains(flagIsCollapsed)) return;
|
|
let row = _rowBlank.cloneNode(true);
|
|
row.classList.remove(flagInitialised);
|
|
row.querySelectorAll('.' + flagInitialised).forEach(function (element) {
|
|
element.classList.remove(flagInitialised);
|
|
});
|
|
let countRows = document.querySelectorAll(idTableMain + ' > tbody > tr').length;
|
|
row.setAttribute(this.constructor.attrIdRowObject, -1 - countRows);
|
|
this.initialiseRowNew(tbody, row);
|
|
tbody.prepend(row);
|
|
tbody.scrollTop = 0;
|
|
this.hookupTableMain();
|
|
this.postInitialiseRowNewCallback(tbody);
|
|
}
|
|
initialiseRowNew(tbody, row) {
|
|
if (this.constructor === TableBasePage) {
|
|
throw new Error("Subclass of TableBasePage must implement method initialiseRowNew().");
|
|
}
|
|
// row.classList.remove(flagRowNew);
|
|
}
|
|
hookupTableMain() {
|
|
if (this.constructor === TableBasePage) {
|
|
throw new Error("Must implement hookupTableMain() method.");
|
|
}
|
|
Events.initialiseEventHandler(idTableMain, flagInitialised, table => {
|
|
this.cacheRowBlank();
|
|
});
|
|
}
|
|
cacheRowBlank() {
|
|
let selectorRowNew = idTableMain + ' tbody tr.' + flagRowNew;
|
|
let rowBlankTemp = document.querySelector(selectorRowNew);
|
|
if (rowBlankTemp == null) return;
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("row blank temp: ", rowBlankTemp);
|
|
let countRows = document.querySelectorAll(idTableMain + ' > tbody > tr').length;
|
|
_rowBlank = rowBlankTemp.cloneNode(true);
|
|
document.querySelectorAll(selectorRowNew).forEach(function (row) {
|
|
row.remove();
|
|
});
|
|
_rowBlank.setAttribute(this.constructor.attrIdRowObject, -1 - countRows);
|
|
}
|
|
postInitialiseRowNewCallback(tbody) {
|
|
if (this.constructor === TableBasePage) {
|
|
throw new Error("Subclass of TableBasePage must implement method postInitialiseRowNewCallback(tbody).");
|
|
}
|
|
}
|
|
initialiseSliderDisplayOrderRowNew(tbody, row) {
|
|
// let tdSelector = ':scope > tr > td.' + flagDisplayOrder;
|
|
// let tbody = document.querySelector('table' + (Validation.isEmpty(flagTable) ? '' : '.' + flagTable) + ' > tbody');
|
|
let slidersDisplayOrder = tbody.querySelectorAll(':scope > tr > td.' + flagDisplayOrder + ' input.' + flagSlider);
|
|
let maxDisplayOrder = 0;
|
|
slidersDisplayOrder.forEach(slider => {
|
|
maxDisplayOrder = Math.max(maxDisplayOrder, parseFloat(DOM.getElementValueCurrent(slider)));
|
|
});
|
|
let sliderDisplayOrder = row.querySelector('td.' + flagDisplayOrder + ' .' + flagSlider);
|
|
DOM.setElementValuesCurrentAndPrevious(sliderDisplayOrder, maxDisplayOrder + 1);
|
|
}
|
|
hookupSlidersDisplayOrderTable() {
|
|
let selectorDisplayOrder = idTableMain + ' tbody tr td.' + flagDisplayOrder + ' input.' + flagSlider + '.' + flagDisplayOrder;
|
|
this.hookupChangeHandlerTableCells(selectorDisplayOrder);
|
|
}
|
|
hookupChangeHandlerTableCells(inputSelector, handler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
Events.initialiseEventHandler(inputSelector, flagInitialised, input => {
|
|
input.addEventListener("change", event => {
|
|
handler(event, input);
|
|
});
|
|
handler(null, input);
|
|
});
|
|
}
|
|
handleChangeNestedElementCellTable(event, element) {
|
|
let wasDirtyParentRows = this.getAllIsDirtyRowsInParentTree(element);
|
|
let wasDirtyElement = element.classList.contains(flagDirty);
|
|
let isDirtyElement = DOM.updateAndCheckIsElementDirty(element);
|
|
// Utils.consoleLogIfNotProductionEnvironment({isDirtyElement, wasDirtyElement, wasDirtyParentRows});
|
|
// let td = DOM.getCellFromElement(element);
|
|
// DOM.setElementAttributeValueCurrent(td, DOM.getElementAttributeValueCurrent(element));
|
|
if (isDirtyElement != wasDirtyElement) {
|
|
// DOM.handleDirtyElement(td, isDirtyElement);
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
this.cascadeChangedIsDirtyNestedElementCellTable(element, isDirtyElement, wasDirtyParentRows);
|
|
}
|
|
}
|
|
getAllIsDirtyRowsInParentTree(element) {
|
|
let rows = [];
|
|
let parent = element;
|
|
let isDirty;
|
|
while (parent) {
|
|
if (parent.tagName.toUpperCase() == 'TR') {
|
|
isDirty = parent.classList.contains(flagDirty);
|
|
rows.push(isDirty);
|
|
}
|
|
parent = parent.parentElement;
|
|
}
|
|
return rows;
|
|
}
|
|
cascadeChangedIsDirtyNestedElementCellTable(element, isDirtyElement, wasDirtyParentRows) {
|
|
if (Validation.isEmpty(wasDirtyParentRows)) return;
|
|
let tr = DOM.getRowFromElement(element);
|
|
let isDirtyRow = isDirtyElement || DOM.hasDirtyChildrenContainer(tr);
|
|
let wasDirtyRow = wasDirtyParentRows.shift();
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
isDirtyRow,
|
|
wasDirtyRow
|
|
});
|
|
if (isDirtyRow != wasDirtyRow) {
|
|
DOM.handleDirtyElement(tr, isDirtyRow);
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
this.cascadeChangedIsDirtyNestedElementCellTable(tr.parentElement, isDirtyRow, wasDirtyParentRows);
|
|
}
|
|
}
|
|
hookupChangeHandlerTableCellsWhenNotCollapsed(inputSelector, handler = (event, element) => {
|
|
if (!element.classList.contains(flagIsCollapsed)) this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
Events.hookupEventHandler("change", inputSelector, handler);
|
|
}
|
|
hookupFieldsCodeTable() {
|
|
this.hookupChangeHandlerTableCells(idTableMain + ' > tbody > tr > td.' + flagCode + ' > .' + flagCode);
|
|
}
|
|
hookupFieldsNameTable() {
|
|
this.hookupChangeHandlerTableCells(idTableMain + ' > tbody > tr > td.' + flagName + ' > .' + flagName);
|
|
}
|
|
hookupFieldsDescriptionTable() {
|
|
this.hookupChangeHandlerTableCells(idTableMain + ' > tbody > tr > td.' + flagDescription + ' > .' + flagDescription);
|
|
}
|
|
hookupFieldsNotesTable() {
|
|
this.hookupChangeHandlerTableCells(idTableMain + ' > tbody > tr > td.' + flagNotes + ' > .' + flagNotes);
|
|
}
|
|
hookupFieldsActive(flagTable = '', handleClickRowNew = (event, element) => {
|
|
this.handleClickAddRowTable(event, element);
|
|
}) {
|
|
let selectorButton = 'table.table-main' + (Validation.isEmpty(flagTable) ? '' : '.' + flagTable) + ' > tbody > tr > td.' + flagActive + ' .' + flagButton + '.' + flagActive;
|
|
let selectorButtonDelete = selectorButton + '.' + flagDelete;
|
|
let selectorButtonUndelete = selectorButton + ':not(.' + flagDelete + ')';
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("hookupFieldsActive: ", selectorButtonDelete, selectorButtonUndelete);
|
|
this.hookupButtonsRowDelete(selectorButtonDelete, selectorButtonUndelete);
|
|
this.hookupButtonsRowUndelete(selectorButtonDelete, selectorButtonUndelete);
|
|
Events.hookupEventHandler("click", 'table.table-main' + (Validation.isEmpty(flagTable) ? '' : '.' + flagTable) + ' > thead > tr > th.' + flagActive + ' .' + flagButton + '.' + flagActive, (event, button) => {
|
|
handleClickRowNew(event, button);
|
|
});
|
|
}
|
|
hookupButtonsRowDelete(selectorButtonDelete, selectorButtonUndelete, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
Events.hookupEventHandler("click", selectorButtonDelete, (event, element) => {
|
|
this.handleClickButtonRowDelete(event, element, selectorButtonDelete, selectorButtonUndelete, (changeEvent, changeElement) => {
|
|
changeHandler(changeEvent, changeElement);
|
|
});
|
|
});
|
|
}
|
|
handleClickButtonRowDelete(event, element, selectorButtonDelete, selectorButtonUndelete, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
if (element.tagName.toUpperCase() != 'SVG') element = element.parentElement;
|
|
let valuePrevious = DOM.getElementAttributeValuePrevious(element);
|
|
let wasDirty = element.classList.contains(flagDirty);
|
|
let row = DOM.getRowFromElement(element);
|
|
if (row.classList.contains(flagRowNew) && !DOM.hasDirtyChildrenContainer(row)) {
|
|
row.parentNode.removeChild(row);
|
|
} else {
|
|
let buttonAddTemplate = document.querySelector(idContainerTemplateElements + ' .' + flagButton + '.' + flagActive + '.' + flagAdd);
|
|
let buttonAdd = buttonAddTemplate.cloneNode(true);
|
|
DOM.setElementAttributeValuePrevious(buttonAdd, valuePrevious);
|
|
DOM.setElementAttributeValueCurrent(buttonAdd, false);
|
|
if (wasDirty) buttonAdd.classList.add(flagDirty);
|
|
element.replaceWith(buttonAdd);
|
|
changeHandler(null, buttonAdd);
|
|
this.hookupButtonsRowUndelete(selectorButtonDelete, selectorButtonUndelete, (changeEvent, changeElement) => {
|
|
changeHandler(changeEvent, changeElement);
|
|
});
|
|
}
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
hookupButtonsRowUndelete(selectorButtonDelete, selectorButtonUndelete, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
Events.hookupEventHandler("click", selectorButtonUndelete, (event, element) => {
|
|
this.handleClickButtonRowUndelete(event, element, selectorButtonDelete, selectorButtonUndelete, (changeEvent, changeElement) => {
|
|
changeHandler(changeEvent, changeElement);
|
|
});
|
|
});
|
|
}
|
|
handleClickButtonRowUndelete(event, element, selectorButtonDelete, selectorButtonUndelete, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
if (element.tagName.toUpperCase() != 'SVG') element = element.parentElement;
|
|
let valuePrevious = DOM.getElementAttributeValuePrevious(element);
|
|
let wasDirty = DOM.isElementDirty(element);
|
|
let buttonDeleteTemplate = document.querySelector(idContainerTemplateElements + ' .' + flagButton + '.' + flagActive + '.' + flagDelete);
|
|
let buttonDelete = buttonDeleteTemplate.cloneNode(true);
|
|
DOM.setElementAttributeValuePrevious(buttonDelete, valuePrevious);
|
|
DOM.setElementAttributeValueCurrent(buttonDelete, true);
|
|
if (wasDirty) buttonDelete.classList.add(flagDirty);
|
|
element.replaceWith(buttonDelete);
|
|
changeHandler(null, buttonDelete);
|
|
this.hookupButtonsRowDelete(selectorButtonDelete, selectorButtonUndelete, (changeEvent, changeElement) => {
|
|
changeHandler(changeEvent, changeElement);
|
|
});
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
hookupTdsAccessLevel() {
|
|
this.hookupTableCellDdlPreviews(flagAccessLevel, utils_Utils.getListFromDict(accessLevels));
|
|
}
|
|
hookupTableCellDdlPreviews(fieldFlag, optionList, cellSelector = null, ddlHookup = ddlSelector => {
|
|
this.hookupTableCellDdls(ddlSelector);
|
|
}, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
if (cellSelector == null) cellSelector = idTableMain + ' > tbody > tr > td.' + fieldFlag;
|
|
Events.hookupEventHandler("click", cellSelector + ' div.' + fieldFlag, (event, div) => {
|
|
this.handleClickTableCellDdlPreview(event, div, fieldFlag, optionList, cellSelector, ddlSelector => {
|
|
ddlHookup(ddlSelector, (event, element) => {
|
|
changeHandler(event, element);
|
|
});
|
|
});
|
|
});
|
|
ddlHookup(cellSelector + ' select.' + fieldFlag);
|
|
}
|
|
hookupTableCellDdls(ddlSelector, changeHandler = (event, element) => {
|
|
this.handleChangeNestedElementCellTable(event, element);
|
|
}) {
|
|
this.hookupChangeHandlerTableCells(ddlSelector, (event, element) => {
|
|
changeHandler(event, element);
|
|
});
|
|
}
|
|
handleClickTableCellDdlPreview(event, div, fieldFlag, optionObjectList, cellSelector = null, ddlHookup = cellSelector => {
|
|
this.hookupTableCellDdls(cellSelector);
|
|
}) {
|
|
if (Validation.isEmpty(cellSelector)) cellSelector = idTableMain + ' > tbody > tr > td.' + fieldFlag;
|
|
let idSelected = DOM.getElementAttributeValueCurrent(div);
|
|
let td = DOM.getCellFromElement(div);
|
|
td.innerHTML = '';
|
|
let ddl = document.createElement('select');
|
|
ddl.classList.add(fieldFlag);
|
|
DOM.setElementValuesCurrentAndPrevious(ddl, idSelected);
|
|
let optionJson, option;
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("click table cell ddl preview");
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
optionObjectList,
|
|
cellSelector
|
|
});
|
|
}
|
|
option = DOM.createOption(null);
|
|
ddl.appendChild(option);
|
|
optionObjectList.forEach(optionObjectJson => {
|
|
optionJson = BusinessObjects.getOptionJsonFromObjectJson(optionObjectJson, idSelected);
|
|
option = DOM.createOption(optionJson);
|
|
ddl.appendChild(option);
|
|
});
|
|
td.appendChild(ddl);
|
|
let ddlSelector = cellSelector + ' select.' + fieldFlag;
|
|
ddlHookup(ddlSelector);
|
|
}
|
|
/*
|
|
hookupTableCellDDlPreviewsWhenNotCollapsed(cellSelector, optionList, ddlHookup = (event, element) => { this.hookupTableCellDdls(event, element); }) {
|
|
Events.hookupEventHandler("click", cellSelector + ' div', (event, div) => {
|
|
this.handleClickTableCellDdlPreview(event, div, optionList, cellSelector, (event, element) => { ddlHookup(event, element); });
|
|
});
|
|
}
|
|
*/
|
|
toggleColumnCollapsed(flagColumn, isCollapsed) {
|
|
this.toggleColumnHasClassnameFlag(flagColumn, isCollapsed, flagIsCollapsed);
|
|
}
|
|
toggleColumnHeaderCollapsed(flagColumn, isCollapsed) {
|
|
this.toggleColumnHasClassnameFlag(flagColumn, isCollapsed, flagIsCollapsed);
|
|
}
|
|
hookupFieldsCommandCategory(idTable = null) {
|
|
if (idTable == null) idTable = idTableMain;
|
|
this.hookupTableCellDdlPreviews(flagCommandCategory, utils_Utils.getListFromDict(filterCommandCategories).sort((a, b) => a[flagName].localeCompare(b[flagName])), idTable + ' > tbody > tr > td.' + flagCommandCategory // + ' .' + flagCommandCategory
|
|
, cellSelector => {
|
|
this.hookupCommandCategoryDdls(cellSelector);
|
|
});
|
|
}
|
|
hookupCommandCategoryDdls(ddlSelector) {
|
|
this.hookupChangeHandlerTableCells(ddlSelector, (event, element) => {
|
|
this.handleChangeCommandCategoryDdl(event, element);
|
|
});
|
|
}
|
|
handleChangeCommandCategoryDdl(event, ddlCategory) {
|
|
let row = DOM.getRowFromElement(ddlCategory);
|
|
let idCommandCategoryRowOld = this.getIdCommandCategoryRow(row); // DOM.getElementAttributeValueCurrent(ddlCategory);
|
|
this.handleChangeNestedElementCellTable(event, ddlCategory);
|
|
let idCommandCategoryRowNew = this.getIdCommandCategoryRow(row); // DOM.getElementAttributeValueCurrent(ddlCategory);
|
|
if (idCommandCategoryRowOld == idCommandCategoryRowNew || idCommandCategoryRowNew == 0) return;
|
|
console.log({
|
|
idCommandCategoryRowNew,
|
|
idCommandCategoryRowOld
|
|
});
|
|
let idCommandCategoryFilter = this.getIdCommandCategoryFilter();
|
|
let tdCommand = row.querySelector('td.' + flagCommand);
|
|
tdCommand.dispatchEvent(new Event('click'));
|
|
let ddlCommand = row.querySelector('td.' + flagCommand + ' select.' + flagCommand);
|
|
ddlCommand.innerHTML = '';
|
|
ddlCommand.appendChild(DOM.createOption(null));
|
|
let optionJson, option;
|
|
let commandsInCategory = utils_Utils.getListFromDict(filterCommands).filter(command => (command[attrIdCommandCategory] == idCommandCategoryRowNew || idCommandCategoryRowNew == 0) && (command[attrIdCommandCategory] == idCommandCategoryFilter || idCommandCategoryFilter == 0));
|
|
let sortedCommands = commandsInCategory.sort((a, b) => a[flagName].localeCompare(b[flagName]));
|
|
sortedCommands.forEach(command => {
|
|
optionJson = BusinessObjects.getOptionJsonFromObjectJson(command);
|
|
option = DOM.createOption(optionJson);
|
|
ddlCommand.appendChild(option);
|
|
});
|
|
this.handleChangeNestedElementCellTable(event, ddlCommand);
|
|
}
|
|
hookupFieldsCommand(idTable = null) {
|
|
if (idTable == null) idTable = idTableMain;
|
|
Events.hookupEventHandler("click", idTable + ' > tbody > tr > td.' + flagCommand + ' div.' + flagCommand, (event, div) => {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment(div);
|
|
let parentTr = DOM.getRowFromElement(div);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
div,
|
|
parentTr
|
|
});
|
|
let tdCommandCategory = parentTr.querySelector('td.' + flagCommandCategory);
|
|
let idCommandCategoryRow = this.getIdCommandCategoryRow(parentTr); // DOM.getElementAttributeValueCurrent(tdCommandCategory);
|
|
let idCommandCategoryFilter = this.getIdCommandCategoryFilter();
|
|
let filterCommandList = utils_Utils.getListFromDict(filterCommands);
|
|
let commandsInCategory = filterCommandList.filter(command => (command[attrIdCommandCategory] == idCommandCategoryRow || idCommandCategoryRow == 0) && (command[attrIdCommandCategory] == idCommandCategoryFilter || idCommandCategoryFilter == 0));
|
|
let sortedCommands = commandsInCategory.sort((a, b) => a[flagName].localeCompare(b[flagName]));
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
tdCommandCategory,
|
|
idCommandCategoryRow,
|
|
idCommandCategoryFilter,
|
|
filterCommandList,
|
|
commandsInCategory
|
|
});
|
|
utils_Utils.consoleLogIfNotProductionEnvironment(filterCommandList);
|
|
this.handleClickTableCellDdlPreview(event, div, flagCommand // fieldFlag
|
|
, sortedCommands // optionList
|
|
, idTable + ' > tbody > tr > td.' + flagCommand // cellSelector
|
|
, cellSelector => {
|
|
this.hookupTableCellDdls(cellSelector, (event, element) => {
|
|
this.handleChangeCommandDdl(event, element);
|
|
});
|
|
});
|
|
});
|
|
this.hookupTableCellDdls(idTable + ' > tbody > tr > td.' + flagCommand + ' select.' + flagCommand, (event, element) => {
|
|
this.handleChangeCommandDdl(event, element);
|
|
});
|
|
}
|
|
handleChangeCommandDdl(event, ddlCommand) {
|
|
// console.log("handle change command ddl");
|
|
let row = DOM.getRowFromElement(ddlCommand);
|
|
this.handleChangeNestedElementCellTable(event, ddlCommand);
|
|
let idCommandCategoryRowOld = this.getIdCommandCategoryRow(row);
|
|
let idCommandNew = this.getIdCommandRow(row);
|
|
let commandNew = filterCommands[idCommandNew];
|
|
// console.log({ idCommandCategoryRowOld, commandNew });
|
|
if (commandNew == null || idCommandCategoryRowOld == commandNew[attrIdCommandCategory]) return;
|
|
let divCommandCategory = row.querySelector('td.' + flagCommandCategory + ' div');
|
|
if (divCommandCategory) divCommandCategory.dispatchEvent(new Event('click'));
|
|
let ddlCommandCategory = row.querySelector('td.' + flagCommandCategory + ' select.' + flagCommandCategory);
|
|
DOM.setElementValueCurrent(ddlCommandCategory, commandNew[attrIdCommandCategory]);
|
|
// console.log({ ddlCommandCategory, commandNew });
|
|
this.handleChangeNestedElementCellTable(event, ddlCommandCategory);
|
|
}
|
|
getIdCommandCategoryRow(tr) {
|
|
let elementCommandCategory = tr.querySelector('td.' + flagCommandCategory + ' .' + flagCommandCategory);
|
|
return DOM.getElementAttributeValueCurrent(elementCommandCategory);
|
|
}
|
|
getIdCommandCategoryFilter() {
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let idCommandCategory = 0;
|
|
if (formFilters == null) return idCommandCategory;
|
|
let commandCategoryFilter = formFilters.querySelector('#' + attrIdCommandCategory);
|
|
let commandFilter = formFilters.querySelector('#' + attrIdCommand);
|
|
let valueCurrentCommandCategoryFilter = DOM.getElementAttributeValueCurrent(commandCategoryFilter);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
valueCurrentCommandCategoryFilter
|
|
});
|
|
if (valueCurrentCommandCategoryFilter == "") {
|
|
let valueCurrentCommandFilter = DOM.getElementAttributeValueCurrent(commandFilter);
|
|
utils_Utils.consoleLogIfNotProductionEnvironment({
|
|
valueCurrentCommandFilter
|
|
});
|
|
if (valueCurrentCommandFilter != "") {
|
|
let command = filterCommands[valueCurrentCommandFilter];
|
|
idCommandCategory = command[attrIdCommandCategory];
|
|
}
|
|
} else {
|
|
idCommandCategory = Number(valueCurrentCommandCategoryFilter);
|
|
}
|
|
return idCommandCategory;
|
|
}
|
|
getHasCommandCategoryFilter() {
|
|
let idCommandCategoryFilter = this.getIdCommandCategoryFilter();
|
|
return !(Validation.isEmpty(idCommandCategoryFilter) || idCommandCategoryFilter == 0);
|
|
}
|
|
getIdCommandRow(tr) {
|
|
let elementCommand = tr.querySelector('td.' + flagCommand + ' .' + flagCommand);
|
|
return DOM.getElementAttributeValueCurrent(elementCommand);
|
|
}
|
|
getIdCommandFilter() {
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let commandFilter = formFilters.querySelector('#' + attrIdCommand);
|
|
let valueCurrentCommandFilter = DOM.getElementAttributeValueCurrent(commandFilter);
|
|
let idCommand = Number(valueCurrentCommandFilter);
|
|
return idCommand;
|
|
}
|
|
getHasCommandFilter() {
|
|
let idCommandFilter = this.getIdCommandFilter();
|
|
return !(Validation.isEmpty(idCommandFilter) || idCommandFilter == 0);
|
|
}
|
|
/*
|
|
createTdActive(isActive) {
|
|
let tdActive = document.createElement("td");
|
|
tdActive.classList.add(flagActive);
|
|
let buttonActive = document.createElement("button");
|
|
buttonActive.classList.add(flagActive);
|
|
buttonActive.classList.add(isActive ? flagDelete : flagAdd);
|
|
buttonActive.textContent = isActive ? 'x' : '+';
|
|
DOM.setElementAttributesValuesCurrentAndPrevious(buttonActive, isActive);
|
|
tdActive.appendChild(buttonActive);
|
|
return tdActive;
|
|
}
|
|
*/
|
|
leave() {
|
|
if (this.constructor === TableBasePage) {
|
|
throw new Error("Must implement leave() method.");
|
|
}
|
|
super.leave();
|
|
let formFilters = TableBasePage.getFormFilters();
|
|
let dataPage = {};
|
|
dataPage[flagFormFilters] = DOM.convertForm2JSON(formFilters);
|
|
this.setLocalStoragePage(dataPage);
|
|
}
|
|
toggleColumnHasClassnameFlag(columnFlag, isRequiredFlag, classnameFlag) {
|
|
let table = TableBasePage.getTableMain();
|
|
let columnTh = table.querySelector('th.' + columnFlag);
|
|
let columnThHasFlag = columnTh.classList.contains(classnameFlag);
|
|
if (isRequiredFlag == columnThHasFlag) return;
|
|
DOM.toggleElementHasClassnameFlag(columnTh, isRequiredFlag, classnameFlag);
|
|
}
|
|
toggleColumnHeaderHasClassnameFlag(columnFlag, isRequiredFlag, classnameFlag) {
|
|
let table = TableBasePage.getTableMain();
|
|
let columnTh = table.querySelector('th.' + columnFlag);
|
|
DOM.toggleElementHasClassnameFlag(columnTh, isRequiredFlag, classnameFlag);
|
|
}
|
|
updateAndToggleShowButtonsSaveCancel() {
|
|
// let pageBody = document.querySelector(idPageBody);
|
|
let isDirty = DOM.hasDirtyChildrenContainer(pageBody);
|
|
let buttonContainerSelector = '.' + flagContainer + '.' + flagSave + '.' + flagCancel;
|
|
let buttonSave = document.querySelector(buttonContainerSelector + ' ' + idButtonSave);
|
|
let areVisibleSaveCancelButtons = !buttonSave.classList.contains(flagIsCollapsed);
|
|
console.log({
|
|
pageBody,
|
|
isDirty,
|
|
areVisibleSaveCancelButtons
|
|
});
|
|
this.toggleShowButtonsSaveCancel(isDirty || areVisibleSaveCancelButtons);
|
|
}
|
|
}
|
|
;// ./static/js/pages/tcg/mtg_decks.js
|
|
|
|
|
|
|
|
|
|
class PageMtgDecks extends TableBasePage {
|
|
static hash = hashPageMtgDecks;
|
|
static attrIdRowObject = attrDeckId;
|
|
callSaveTableContent = API.saveDeck;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
hookupFilters() {
|
|
/*
|
|
this.sharedHookupFilters();
|
|
this.hookupFilterActive();
|
|
*/
|
|
}
|
|
loadRowTable(rowJson) {
|
|
if (rowJson == null) return;
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("applying data row: ", rowJson);
|
|
}
|
|
}
|
|
getJsonRow(row) {
|
|
return;
|
|
}
|
|
initialiseRowNew(tbody, row) {}
|
|
postInitialiseRowNewCallback(tbody) {
|
|
let newRows = tbody.querySelectorAll('tr.' + flagRowNew);
|
|
let newestRow = newRows[0];
|
|
let clickableElementsSelector = ['td.' + attrCommanderBracketId + ' div.' + attrCommanderBracketId].join('');
|
|
newestRow.querySelectorAll(clickableElementsSelector).forEach(clickableElement => {
|
|
clickableElement.click();
|
|
});
|
|
}
|
|
hookupTableMain() {
|
|
super.hookupTableMain();
|
|
this.hookupTableMainRows();
|
|
this.hookupFieldsNameTable();
|
|
this.hookupTableMainIsCommanderCheckboxes();
|
|
this.hookupTableMainCommanderBracketPreviews();
|
|
this.hookupFieldsActive();
|
|
}
|
|
hookupTableMainRows() {
|
|
return;
|
|
// removed by dead control flow
|
|
|
|
// removed by dead control flow
|
|
|
|
}
|
|
static toggleShowDeckStatisticsSection(showSection) {
|
|
let statisticsSectionTableBody = document.querySelector('table.' + flagStatistics + ' tbody');
|
|
if (showSection) {
|
|
statisticsSectionTableBody.classList.remove(flagIsCollapsed);
|
|
} else {
|
|
statisticsSectionTableBody.classList.add(flagIsCollapsed);
|
|
}
|
|
}
|
|
hookupTableMainIsCommanderCheckboxes() {
|
|
this.hookupChangeHandlerTableCells(idTableMain + ' td.' + flagIsCommander + ' .' + flagIsCommander);
|
|
}
|
|
hookupTableMainCommanderBracketPreviews() {
|
|
this.hookupTableCellDdlPreviews(attrCommanderBracketId, utils_Utils.getListFromDict(commanderBrackets));
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/tcg/mtg_game.js
|
|
|
|
|
|
|
|
|
|
class PageMtgGame extends TableBasePage {
|
|
static hash = hashPageMtgGame;
|
|
static attrIdRowObject = attrGameId;
|
|
callSaveTableContent = API.saveGame;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
this.hookupTcgGame();
|
|
}
|
|
hookupFilters() {
|
|
// this.sharedHookupFilters();
|
|
}
|
|
loadRowTable(rowJson) {
|
|
return;
|
|
}
|
|
getJsonRow(row) {
|
|
return;
|
|
}
|
|
initialiseRowNew(tbody, row) {}
|
|
postInitialiseRowNewCallback(tbody) {}
|
|
hookupTableMain() {
|
|
super.hookupTableMain();
|
|
}
|
|
hookupTcgGame() {
|
|
this.initGamePage();
|
|
let pageHeading = document.querySelector('.container.company-name .tcg-title.company-name');
|
|
pageHeading.innerText = `MTG Game #${gameId}`;
|
|
}
|
|
initGamePage() {
|
|
// Load existing game state from API or show setup
|
|
PageMtgGame.updatePlayerSetup();
|
|
if (typeof gameId !== 'undefined' && gameId) {
|
|
this.loadGameFromServer();
|
|
}
|
|
/*
|
|
else {
|
|
PageMtgGame.updatePlayerSetup();
|
|
}
|
|
*/
|
|
|
|
PageMtgGame.hookupResetButton();
|
|
PageMtgGame.hookupPlayerCountInput();
|
|
this.hookupStartGameButton();
|
|
/*
|
|
this.hookupCommanderDeathIncrementButtons();
|
|
this.hookupEliminateCommanderButtons();
|
|
this.hookupPlayerLifeIncrementButtons();
|
|
this.hookupCommanderDamageIncrementButtons();
|
|
*/
|
|
}
|
|
static hookupResetButton() {
|
|
const resetGameButton = document.querySelector('header.game-header .header-right .btn-tcg.btn-tcg-secondary');
|
|
if (resetGameButton) {
|
|
resetGameButton.addEventListener('click', PageMtgGame.resetGame);
|
|
}
|
|
}
|
|
static hookupPlayerCountInput() {
|
|
const playerCountInput = document.getElementById('playerCount');
|
|
if (playerCountInput) {
|
|
playerCountInput.addEventListener('change', PageMtgGame.updatePlayerSetup);
|
|
}
|
|
}
|
|
hookupStartGameButton() {
|
|
const startGameButton = document.querySelector('.setup-section .setup-actions .btn-tcg');
|
|
if (startGameButton) {
|
|
startGameButton.addEventListener('click', () => {
|
|
this.startGame();
|
|
});
|
|
}
|
|
}
|
|
/*
|
|
hookupCommanderDeathIncrementButtons() {
|
|
const commanderDeathIncremementButtons = document.querySelectorAll('#players-grid .player-card .commander-deaths .death-btn');
|
|
if (commanderDeathIncremementButtons) {
|
|
commanderDeathIncremementButtons.forEach((button) => {
|
|
button.addEventListener('click', PageMtgGame.changeCommanderDeaths);
|
|
});
|
|
}
|
|
}
|
|
hookupEliminateCommanderButtons() {
|
|
const eliminateCommanderButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
|
|
if (eliminateCommanderButtons) {
|
|
eliminateCommanderButtons.forEach((button) => {
|
|
button.addEventListener('click', PageMtgGame.toggleEliminate);
|
|
});
|
|
}
|
|
}
|
|
hookupPlayerLifeIncrementButtons() {
|
|
const playerLifeIncrementButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
|
|
if (playerLifeIncrementButtons) {
|
|
playerLifeIncrementButtons.forEach((button) => {
|
|
button.addEventListener('click', PageMtgGame.changeLife);
|
|
});
|
|
}
|
|
}
|
|
hookupCommanderDamageIncrementButtons() {
|
|
const commanderDamageIncrementButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
|
|
if (commanderDamageIncrementButtons) {
|
|
commanderDamageIncrementButtons.forEach((button) => {
|
|
button.addEventListener('click', PageMtgGame.changeCommanderDamage);
|
|
});
|
|
}
|
|
}
|
|
*/
|
|
|
|
async loadGameFromServer() {
|
|
console.log("loading game from server");
|
|
try {
|
|
// Fetch players, rounds, and damage records from API
|
|
const [playersResponse, roundsResponse, damageResponse] = await Promise.all([API.getGamePlayers(gameId), API.getGameRounds(gameId), API.getGameDamageRecords(gameId)]);
|
|
console.log({
|
|
playersResponse,
|
|
damageResponse
|
|
});
|
|
let setupSection = document.getElementById('setupSection');
|
|
let gameSection = document.getElementById('gameSection');
|
|
setupSection.classList.remove('hidden');
|
|
gameSection.classList.add('hidden');
|
|
if (playersResponse.status !== 'success') {
|
|
console.error('Failed to load players:', playersResponse.message);
|
|
return;
|
|
}
|
|
const savedPlayers = playersResponse.data || [];
|
|
const savedRounds = roundsResponse.status === 'success' ? roundsResponse.data || [] : [];
|
|
const savedDamageRecords = damageResponse.status === 'success' ? damageResponse.data || [] : [];
|
|
players = savedPlayers;
|
|
rounds = savedRounds;
|
|
damageRecords = savedDamageRecords;
|
|
if (savedPlayers.length === 0) {
|
|
// No players yet, show setup section
|
|
return;
|
|
}
|
|
|
|
// Hide setup, show game
|
|
setupSection.classList.add('hidden');
|
|
gameSection.classList.remove('hidden');
|
|
console.log({
|
|
savedPlayers,
|
|
damageRecords
|
|
});
|
|
|
|
// Render players to DOM
|
|
const latestRoundId = PageMtgGame.getLatestRoundId();
|
|
const latestRound = rounds.filter(round => round[attrRoundId] == latestRoundId)[0];
|
|
const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
|
|
DOM.setElementValuesCurrentAndPrevious(roundDisplayOrderLabel, latestRound[flagDisplayOrder]);
|
|
this.renderPlayers();
|
|
} catch (error) {
|
|
console.error('Error loading game from server:', error);
|
|
}
|
|
}
|
|
static getRoundDisplayOrderLabel() {
|
|
return document.querySelector(['#gameSection', ' > .', flagRow, '.', flagRound, ' > .', flagRow, '.', flagRound, ' > .', flagRound, '.', flagDisplayOrder, ' > span.', flagRound, '.', flagDisplayOrder].join(''));
|
|
}
|
|
renderPlayers() {
|
|
const grid = document.getElementById('playersGrid');
|
|
grid.innerHTML = '';
|
|
|
|
// let activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
|
|
const currentRoundDisplayOrder = Number(DOM.getElementValueCurrent(roundDisplayOrderLabel));
|
|
let activeRound = rounds.filter(round => round[flagDisplayOrder] == currentRoundDisplayOrder)[0];
|
|
if (activeRound == null) {
|
|
activeRound = PageMtgGame.makeDefaultGameRound(currentRoundDisplayOrder);
|
|
rounds.push(activeRound);
|
|
}
|
|
DOM.setElementValueCurrent(roundDisplayOrderLabel, activeRound[flagDisplayOrder]);
|
|
const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= currentRoundDisplayOrder).map(round => round[attrRoundId]);
|
|
players.forEach((player, index) => {
|
|
// Build display name: prefer user_name + deck_name, fallback to player name
|
|
const playerId = player[attrPlayerId];
|
|
let displayName = PageMtgGame.makePlayerDisplayName(playerId, index);
|
|
let damagePlayerPairs = [...players, {
|
|
[attrPlayerId]: null
|
|
}];
|
|
let maxCommanderDamageReceived = 0;
|
|
damagePlayerPairs.forEach(damagePlayerPair => {
|
|
const sourceId = damagePlayerPair[attrPlayerId];
|
|
const filteredPlayerDamages = damageRecords.filter(damage => damage[attrRoundId] == activeRound[attrRoundId]
|
|
// previousRoundIds.includes(damage[attrRoundId])
|
|
&& damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == sourceId); //[playerId] || {};
|
|
if (filteredPlayerDamages.length == 0) {
|
|
damageRecords.push(PageMtgGame.makeDefaultGameRoundPlayerDamage(playerId, sourceId));
|
|
}
|
|
maxCommanderDamageReceived = Math.max(maxCommanderDamageReceived, damageRecords.filter(damage => damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == sourceId && damage[attrReceivedFromCommanderPlayerId] != null && previousRoundIds.includes(damage[attrRoundId])).map(damage => damage[flagLifeLoss]).reduce((a, b) => a + b, 0));
|
|
});
|
|
const totalDamage = damageRecords.filter(damage => damage[attrPlayerId] == playerId && previousRoundIds.includes(damage[attrRoundId])).map(damage => damage[flagLifeLoss] - damage[flagLifeGain]).reduce((a, b) => a + b, 0);
|
|
let life = startingLife - totalDamage;
|
|
let isEliminatedByForce = damageRecords.filter(damage => damage[attrPlayerId] == playerId && previousRoundIds.includes(damage[attrRoundId])).map(damage => damage[flagIsEliminated]).some(Boolean);
|
|
console.log("renderPlayers");
|
|
console.log({
|
|
isEliminatedByForce,
|
|
player,
|
|
life,
|
|
maxCommanderDamageReceived
|
|
});
|
|
const isEliminated = isEliminatedByForce || !player[flagActive] || life < 1 || maxCommanderDamageReceived >= 21;
|
|
const totalCommanderDeaths = damageRecords.filter(damage => damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == null && damage[attrRoundId] == activeRound[attrRoundId]).map(damage => damage[flagCommanderDeaths]).reduce((a, b) => a + b, 0);
|
|
const card = document.createElement('div');
|
|
card.className = `player-card ${isEliminated ? 'eliminated' : ''}`;
|
|
card.style.animationDelay = `${index * 0.1}s`;
|
|
card.dataset.playerId = playerId;
|
|
card.dataset.userName = player.user_name || '';
|
|
card.dataset.deckName = player.deck_name || '';
|
|
card.innerHTML = `
|
|
<div class="player-header">
|
|
<div class="player-info">
|
|
<div class="player-name">${displayName}</div>
|
|
<div class="commander-deaths">
|
|
<span>Commander Deaths:</span>
|
|
<div class="death-counter">
|
|
<button class="death-btn death-minus" data-player-id="${playerId}">−</button>
|
|
<span class="death-display" data-player-id="${playerId}" ${attrValuePrevious}="${totalCommanderDeaths}">${totalCommanderDeaths}</span>
|
|
<button class="death-btn death-plus" data-player-id="${playerId}">+</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="eliminate-btn" data-player-id="${playerId}" ${attrValuePrevious}="${isEliminated}">
|
|
${isEliminated ? 'Revive' : 'Eliminate'}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="life-total">
|
|
<input type="hidden" class="life-value" data-player-id="${playerId}" value="${life}">
|
|
<div class="life-display" data-player-id="${playerId}" ${attrValuePrevious}="${life}">${life}</div>
|
|
<label>Gain</label>
|
|
<div class="life-gain-controls">
|
|
<button class="life-gain-btn" data-player-id="${playerId}" data-amount="-5">-5</button>
|
|
<button class="life-gain-btn" data-player-id="${playerId}" data-amount="-1">-1</button>
|
|
<button class="life-gain-btn" data-player-id="${playerId}" data-amount="1">+1</button>
|
|
<button class="life-gain-btn" data-player-id="${playerId}" data-amount="5">+5</button>
|
|
</div>
|
|
<label>Loss</label>
|
|
<div class="life-loss-controls">
|
|
<button class="life-loss-btn" data-player-id="${playerId}" data-amount="-5">-5</button>
|
|
<button class="life-loss-btn" data-player-id="${playerId}" data-amount="-1">-1</button>
|
|
<button class="life-loss-btn" data-player-id="${playerId}" data-amount="1">+1</button>
|
|
<button class="life-loss-btn" data-player-id="${playerId}" data-amount="5">+5</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="commander-damage-section">
|
|
<div class="section-title">Commander Damage Taken</div>
|
|
<div class="damage-grid" data-player-id="${playerId}">
|
|
${PageMtgGame.renderCommanderDamageRows(playerId // playerId
|
|
, player[attrDeckId] // deckId
|
|
)}
|
|
</div>
|
|
</div>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
this.reorderPlayerCards();
|
|
PageMtgGame.renderCommanderDamageLog();
|
|
|
|
// Hookup all event handlers
|
|
this.hookupGameRoundEvents();
|
|
this.hookupPlayerCardEvents();
|
|
}
|
|
static renderCommanderDamageLog() {
|
|
const currentRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
|
|
const damageTableBody = document.querySelector('.' + flagDamageLog + '.' + flagContainer + ' table tbody');
|
|
damageTableBody.innerHTML = '';
|
|
const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= currentRoundDisplayOrder).map(round => round[attrRoundId]);
|
|
let newTableBodyHtml = '';
|
|
damageRecords.forEach(damage => {
|
|
if (damage[flagActive] && (damage[flagCommanderDeaths] > 0 || damage[flagLifeGain] != 0 || damage[flagLifeLoss] != 0 || damage[flagIsEliminated])
|
|
// && rounds.filter(r => r[attrRoundId] == damage[attrRoundId])[0][flagDisplayOrder] <= currentRoundDisplayOrder
|
|
&& previousRoundIds.includes(damage[attrRoundId])) {
|
|
let round = rounds.filter(r => r[attrRoundId] == damage[attrRoundId])[0];
|
|
let player = players.filter(p => p[attrPlayerId] == damage[attrPlayerId])[0];
|
|
let receivedFromPlayer = damage[attrReceivedFromCommanderPlayerId] == null ? {
|
|
[flagName]: ''
|
|
} : players.filter(p => p[attrPlayerId] == damage[attrReceivedFromCommanderPlayerId])[0];
|
|
newTableBodyHtml += `
|
|
<tr ${attrDamageId}="${damage[attrDamageId]}">
|
|
<td class="${attrRoundId}">${round[flagDisplayOrder]}</td>
|
|
<td class="${attrPlayerId}">${player[flagName]}</td>
|
|
<td class="${attrReceivedFromCommanderPlayerId}">${receivedFromPlayer[flagName]}</td>
|
|
<td class="${flagLifeGain}">${damage[flagLifeGain]}</td>
|
|
<td class="${flagLifeLoss}">${damage[flagLifeLoss]}</td>
|
|
<td class="${flagCommanderDeaths}">${damage[flagCommanderDeaths]}</td>
|
|
<td class="${flagIsEliminated}">${damage[flagIsEliminated]}</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
});
|
|
damageTableBody.innerHTML = newTableBodyHtml;
|
|
}
|
|
static makeDefaultGameRoundPlayerDamage(playerId, receivedFromCommanderPlayerId) {
|
|
let roundId = PageMtgGame.getActiveRoundId();
|
|
return {
|
|
[attrDamageId]: -1 - damageRecords.length,
|
|
[attrRoundId]: roundId,
|
|
[attrPlayerId]: playerId,
|
|
[attrReceivedFromCommanderPlayerId]: receivedFromCommanderPlayerId,
|
|
[flagLifeGain]: 0,
|
|
[flagLifeLoss]: 0,
|
|
[flagCommanderDeaths]: 0,
|
|
[flagIsEliminated]: false,
|
|
[flagActive]: true
|
|
};
|
|
}
|
|
static getLatestRoundId() {
|
|
let roundId = -1;
|
|
if (rounds.length > 0) {
|
|
const highestRoundDisplayOrder = rounds.map(round => {
|
|
return round[flagDisplayOrder];
|
|
}).reduce((acc, cur) => Math.max(acc, cur), 0);
|
|
const filteredRounds = rounds.filter(round => round[flagDisplayOrder] == highestRoundDisplayOrder);
|
|
if (filteredRounds.length > 0) {
|
|
roundId = filteredRounds[0][attrRoundId];
|
|
}
|
|
console.log({
|
|
"method": "getLatestRoundId",
|
|
highestRoundDisplayOrder,
|
|
filteredRounds,
|
|
roundId
|
|
});
|
|
}
|
|
return roundId;
|
|
}
|
|
static getActiveRoundDisplayOrder() {
|
|
const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
|
|
return Number(DOM.getElementValueCurrent(roundDisplayOrderLabel));
|
|
}
|
|
static getActiveRoundId() {
|
|
const currentRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
|
|
let roundId = 0;
|
|
if (rounds.length > 0) {
|
|
let filteredRounds = rounds.filter(round => round[flagDisplayOrder] == currentRoundDisplayOrder);
|
|
if (filteredRounds.length > 0) roundId = filteredRounds[0][attrRoundId];
|
|
console.log({
|
|
"method": "getActiveRoundId",
|
|
filteredRounds,
|
|
roundId
|
|
});
|
|
}
|
|
return roundId;
|
|
}
|
|
static makePlayerDisplayName(playerId, index) {
|
|
if (playerId == null) {
|
|
return `Player ${index + 1}`;
|
|
}
|
|
const player = players.filter(player => player[attrPlayerId] == playerId)[0];
|
|
const deckId = player[attrDeckId];
|
|
const deck = deckId == null ? null : decks.filter(deck => deck[attrDeckId] == deckId)[0];
|
|
const user = playerId == null ? null : users[player[attrUserId]];
|
|
return player[flagName] || `${user == null ? 'Error' : user[flagName]} - ${deck == null ? 'Error' : deck[flagName]}`;
|
|
}
|
|
static renderCommanderDamageRows(playerId) {
|
|
const activeRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
|
|
const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= activeRoundDisplayOrder).map(round => round[attrRoundId]);
|
|
return players.filter(otherPlayer => otherPlayer[attrPlayerId] !== playerId).map(otherPlayer => {
|
|
const sourceId = otherPlayer[attrPlayerId];
|
|
let otherPlayerDisplayName = PageMtgGame.makePlayerDisplayName(sourceId);
|
|
const totalDamage = damageRecords.filter(damage => damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == sourceId
|
|
// && damage[attrRoundId] == roundId
|
|
&& previousRoundIds.includes(damage[attrRoundId])).map(damage => damage[flagLifeLoss]).reduce((acc, curr) => acc + curr, 0);
|
|
const isLethal = totalDamage >= 21;
|
|
return `
|
|
<div class="damage-row" data-player-id="${playerId}" data-source-id="${sourceId}">
|
|
<span class="damage-source">from ${otherPlayerDisplayName}</span>
|
|
<div class="damage-controls">
|
|
<button class="damage-btn damage-minus" data-player-id="${playerId}" data-source-id="${sourceId}">−</button>
|
|
<input type="hidden" class="damage-value" data-player-id="${playerId}" data-source-id="${sourceId}" value="${totalDamage}">
|
|
<span class="damage-display ${isLethal ? 'lethal' : ''}" data-player-id="${playerId}" data-source-id="${sourceId}" ${attrValuePrevious}="${totalDamage}">${totalDamage}</span>
|
|
<button class="damage-btn damage-plus" data-player-id="${playerId}" data-source-id="${sourceId}">+</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
hookupGameRoundEvents() {
|
|
let incrementRoundButtonSelector = '#gameSection .' + flagRow + '.' + flagRound + ' button.' + flagRoundDisplayOrderButton;
|
|
Events.hookupEventHandler("click", incrementRoundButtonSelector, (event, button) => {
|
|
const amount = button.classList.contains(flagRoundDisplayOrderPlus) ? 1 : -1;
|
|
const roundDisplayOrderButtonContainer = button.parentElement;
|
|
const roundDisplayOrderLabel = roundDisplayOrderButtonContainer.querySelector('span.' + flagRound + '.' + flagDisplayOrder);
|
|
const currentRoundDisplayOrder = Number(DOM.getElementValueCurrent(roundDisplayOrderLabel));
|
|
const newDisplayOrder = currentRoundDisplayOrder + amount;
|
|
DOM.setElementValueCurrent(roundDisplayOrderLabel, newDisplayOrder);
|
|
DOM.isElementDirty(roundDisplayOrderLabel);
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
this.renderPlayers();
|
|
});
|
|
}
|
|
static makeDefaultGameRound(displayOrder) {
|
|
const newDisplayOrder = displayOrder != null ? displayOrder : 1 + Math.max(rounds.map(round => round[flagDisplayOrder]));
|
|
return {
|
|
[attrRoundId]: -newDisplayOrder,
|
|
[attrGameId]: gameId,
|
|
[flagNotes]: null,
|
|
[flagDisplayOrder]: newDisplayOrder,
|
|
[flagActive]: true
|
|
};
|
|
}
|
|
hookupPlayerCardEvents() {
|
|
// Life gain buttons
|
|
let lifeGainButtonSelector = '.life-gain-btn';
|
|
Events.hookupEventHandler("click", lifeGainButtonSelector, (event, button) => {
|
|
const playerId = button.dataset.playerId;
|
|
const amount = parseInt(button.dataset.amount);
|
|
const activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const damageIndex = damageRecords.findIndex(damage => damage[attrRoundId] == activeRoundId && damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == null);
|
|
this.changeLife(playerId // playerId
|
|
, amount // amount
|
|
, true // isLifeGainNotLoss
|
|
, true // updateDamage
|
|
, damageIndex // damageIndex
|
|
);
|
|
});
|
|
|
|
// Life loss buttons
|
|
let lifeLossButtonSelector = '.life-loss-btn';
|
|
Events.hookupEventHandler("click", lifeLossButtonSelector, (event, button) => {
|
|
const playerId = button.dataset.playerId;
|
|
const amount = parseInt(button.dataset.amount);
|
|
const activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const damageIndex = damageRecords.findIndex(damage => damage[attrRoundId] == activeRoundId && damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == null);
|
|
this.changeLife(playerId // playerId
|
|
, amount // amount
|
|
, false // isLifeGainNotLoss
|
|
, true // updateDamage
|
|
, damageIndex // damageIndex
|
|
);
|
|
});
|
|
|
|
// Commander death buttons
|
|
let commanderDeathButtonSelector = '.death-btn';
|
|
Events.hookupEventHandler("click", commanderDeathButtonSelector, (event, button) => {
|
|
const playerId = button.dataset.playerId;
|
|
const isMinusButton = button.classList.contains('death-minus');
|
|
const amount = isMinusButton ? -1 : 1;
|
|
this.changeCommanderDeaths(playerId, amount);
|
|
});
|
|
|
|
// Commander damage buttons
|
|
let commmanderDamageButtonSelector = '.damage-btn';
|
|
Events.hookupEventHandler("click", commmanderDamageButtonSelector, (event, button) => {
|
|
const playerId = button.dataset.playerId;
|
|
const sourceId = button.dataset.sourceId;
|
|
const isMinusButton = button.classList.contains('damage-minus');
|
|
const amount = isMinusButton ? -1 : 1;
|
|
this.changeCommanderDamage(playerId, sourceId, amount);
|
|
});
|
|
|
|
// Eliminate buttons
|
|
let eliminatePlayerButtonSelector = '.eliminate-btn';
|
|
Events.hookupEventHandler("click", eliminatePlayerButtonSelector, (event, button) => {
|
|
const playerId = button.dataset.playerId;
|
|
this.toggleEliminate(playerId);
|
|
});
|
|
}
|
|
changeLife(playerId, amount, isLifeGainNotLoss = false, updateDamage = false, damageIndex = null) {
|
|
const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
|
|
// if (!card || card.classList.contains('eliminated')) return;
|
|
|
|
const lifeInput = card.querySelector(`.life-value[data-player-id="${playerId}"]`);
|
|
const lifeDisplay = card.querySelector(`.life-display[data-player-id="${playerId}"]`);
|
|
const currentLife = parseInt(lifeInput.value) || 0;
|
|
const newLife = currentLife + amount * (isLifeGainNotLoss ? 1 : -1);
|
|
DOM.setElementAttributeValueCurrent(lifeDisplay, newLife);
|
|
DOM.isElementDirty(lifeDisplay);
|
|
lifeInput.value = newLife;
|
|
lifeDisplay.textContent = newLife;
|
|
if (updateDamage) {
|
|
let fieldFlag = isLifeGainNotLoss ? flagLifeGain : flagLifeLoss;
|
|
damageRecords[damageIndex][fieldFlag] += amount;
|
|
}
|
|
PageMtgGame.renderCommanderDamageLog();
|
|
|
|
// PageMtgGame.debouncedSave();
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
changeCommanderDamage(playerId, sourceId, amount) {
|
|
const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
|
|
// if (!card || card.classList.contains('eliminated')) return;
|
|
|
|
const damageInput = card.querySelector(`.damage-value[data-player-id="${playerId}"][data-source-id="${sourceId}"]`);
|
|
const damageDisplay = card.querySelector(`.damage-display[data-player-id="${playerId}"][data-source-id="${sourceId}"]`);
|
|
const currentDamage = parseInt(damageInput.value) || 0;
|
|
const newDamage = Math.max(0, currentDamage + amount);
|
|
amount = newDamage - currentDamage;
|
|
DOM.setElementAttributeValueCurrent(damageDisplay, newDamage);
|
|
DOM.isElementDirty(damageDisplay);
|
|
damageInput.value = newDamage;
|
|
damageDisplay.textContent = newDamage;
|
|
|
|
// Update lethal class
|
|
if (newDamage >= 21) {
|
|
damageDisplay.classList.add('lethal');
|
|
} else {
|
|
damageDisplay.classList.remove('lethal');
|
|
}
|
|
const activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const damageIndex = damageRecords.findIndex(damageRecord => damageRecord[attrRoundId] == activeRoundId && damageRecord[attrPlayerId] == playerId && damageRecord[attrReceivedFromCommanderPlayerId] == sourceId);
|
|
damageRecords[damageIndex][flagLifeLoss] += amount;
|
|
let isLifeGainNotLoss = false;
|
|
this.changeLife(playerId // playerId
|
|
, -amount // amount
|
|
, isLifeGainNotLoss // isLifeGainNotLoss
|
|
, false // updateDamage
|
|
, damageIndex // damageIndex
|
|
);
|
|
// PageMtgGame.debouncedSave();
|
|
}
|
|
changeCommanderDeaths(playerId, amount) {
|
|
const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
|
|
// if (!card || card.classList.contains('eliminated')) return;
|
|
|
|
const deathDisplay = card.querySelector(`.death-display[data-player-id="${playerId}"]`);
|
|
const currentDeaths = parseInt(deathDisplay.textContent) || 0;
|
|
const newDeaths = Math.max(0, currentDeaths + amount);
|
|
deathDisplay.textContent = newDeaths;
|
|
DOM.setElementAttributeValueCurrent(deathDisplay, newDeaths);
|
|
DOM.isElementDirty(deathDisplay);
|
|
const activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const damageIndex = damageRecords.findIndex(damage => damage[attrRoundId] == activeRoundId && damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == null);
|
|
damageRecords[damageIndex][flagCommanderDeaths] = newDeaths;
|
|
PageMtgGame.renderCommanderDamageLog();
|
|
|
|
// PageMtgGame.debouncedSave();
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
toggleEliminate(playerId) {
|
|
const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
|
|
if (!card) return;
|
|
const eliminateBtn = card.querySelector(`.eliminate-btn[data-player-id="${playerId}"]`);
|
|
const wasEliminated = card.classList.contains('eliminated');
|
|
if (wasEliminated) {
|
|
card.classList.remove('eliminated');
|
|
eliminateBtn.textContent = 'Eliminate';
|
|
} else {
|
|
card.classList.add('eliminated');
|
|
eliminateBtn.textContent = 'Revive';
|
|
}
|
|
const isEliminated = card.classList.contains('eliminated');
|
|
const activeRoundId = PageMtgGame.getActiveRoundId();
|
|
const damageIndex = damageRecords.findIndex(damage => damage[attrRoundId] == activeRoundId && damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] == null);
|
|
damageRecords[damageIndex][flagIsEliminated] = isEliminated;
|
|
DOM.setElementAttributeValueCurrent(eliminateBtn, isEliminated);
|
|
DOM.isElementDirty(eliminateBtn);
|
|
this.reorderPlayerCards();
|
|
PageMtgGame.renderCommanderDamageLog();
|
|
|
|
// PageMtgGame.debouncedSave();
|
|
this.updateAndToggleShowButtonsSaveCancel();
|
|
}
|
|
reorderPlayerCards() {
|
|
let playerGrid = document.getElementById('playersGrid');
|
|
let currentPlayerCards = playerGrid.querySelectorAll('.player-card');
|
|
let newPlayerCards = [];
|
|
let playerCardMetas = [];
|
|
currentPlayerCards.forEach((playerCard, index) => {
|
|
newPlayerCards.push(playerCard.cloneNode(true));
|
|
playerCardMetas.push({
|
|
[flagIsEliminated]: playerCard.classList.contains(flagIsEliminated),
|
|
[attrPlayerId]: playerCard.dataset["playerId"],
|
|
[flagDisplayOrder]: index
|
|
});
|
|
});
|
|
let activeRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
|
|
let newPlayerGridInnerHTML = '';
|
|
let playerIdA, playerIdB, isEliminatedAsIntA, isEliminatedAsIntB, playerA, playerB, indexPlayerCard;
|
|
playerCardMetas.sort((a, b) => {
|
|
playerIdA = a[attrPlayerId];
|
|
playerIdB = b[attrPlayerId];
|
|
isEliminatedAsIntA = PageMtgGame.isPlayerEliminated(playerIdA, activeRoundDisplayOrder) ? 1 : 0;
|
|
isEliminatedAsIntB = PageMtgGame.isPlayerEliminated(playerIdB, activeRoundDisplayOrder) ? 1 : 0;
|
|
playerA = players.filter(p => p[attrPlayerId] == playerIdA)[0];
|
|
playerB = players.filter(p => p[attrPlayerId] == playerIdB)[0];
|
|
return players.length * isEliminatedAsIntA + playerA[flagDisplayOrder] - (players.length * isEliminatedAsIntB + playerB[flagDisplayOrder]);
|
|
}).forEach(playerCardMeta => {
|
|
indexPlayerCard = playerCardMeta[flagDisplayOrder];
|
|
newPlayerGridInnerHTML += newPlayerCards[indexPlayerCard].outerHTML;
|
|
});
|
|
playerGrid.innerHTML = newPlayerGridInnerHTML;
|
|
playerGrid.querySelectorAll('.' + flagInitialised).forEach(initialisedElement => {
|
|
initialisedElement.classList.remove(flagInitialised);
|
|
});
|
|
this.hookupPlayerCardEvents();
|
|
}
|
|
static isPlayerEliminated(playerId, roundDisplayOrder = null) {
|
|
if (roundDisplayOrder == null) roundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
|
|
const filteredRoundIds = rounds.filter(round => round[flagDisplayOrder] <= roundDisplayOrder).map(round => round[attrRoundId]);
|
|
let hasDamageWithIsEliminated = damageRecords.filter(damage =>
|
|
// damage[attrRoundId] <= roundDisplayOrder
|
|
filteredRoundIds.includes(damage[attrRoundId]) && damage[attrPlayerId] == playerId && damage[flagIsEliminated]).length > 0;
|
|
let damageFromOtherPlayers = {};
|
|
let otherPlayerId;
|
|
damageRecords.filter(damage =>
|
|
// damage[attrRoundId] <= roundId
|
|
filteredRoundIds.includes(damage[attrRoundId]) && damage[attrPlayerId] == playerId && damage[attrReceivedFromCommanderPlayerId] != null).forEach(damage => {
|
|
otherPlayerId = damage[attrReceivedFromCommanderPlayerId];
|
|
damageFromOtherPlayers[otherPlayerId] = damage[flagLifeLoss] + (damageFromOtherPlayers[otherPlayerId] == null ? 0 : damageFromOtherPlayers[otherPlayerId]);
|
|
});
|
|
let maxDamageFromOtherCommander = Object.keys(damageFromOtherPlayers).map(playerId => damageFromOtherPlayers[playerId]).reduce((acc, cur) => Math.max(acc, cur), 0);
|
|
let totalDamageTaken = damageRecords.filter(damage =>
|
|
// damage[attrRoundId] <= roundId
|
|
filteredRoundIds.includes(damage[attrRoundId]) && damage[attrPlayerId] == playerId).map(damage => damage[flagLifeLoss] - damage[flagLifeGain]).reduce((a, b) => a + b, 0);
|
|
console.log({
|
|
roundDisplayOrder,
|
|
filteredRoundIds,
|
|
hasDamageWithIsEliminated,
|
|
maxDamageFromOtherCommander,
|
|
totalDamageTaken
|
|
});
|
|
return hasDamageWithIsEliminated || maxDamageFromOtherCommander >= 21 || totalDamageTaken >= startingLife;
|
|
}
|
|
static updatePlayerSetup() {
|
|
const playerCountInput = document.getElementById('playerCount');
|
|
if (!playerCountInput) return;
|
|
const playerCount = parseInt(playerCountInput.value);
|
|
const grid = document.getElementById('playerSetupGrid');
|
|
if (!grid) return;
|
|
grid.innerHTML = '';
|
|
const wrapperTemplate = document.getElementById(playerSetupWrapperTemplateId);
|
|
let player, wrapper, wrapperHeading, userDdl, deckDdl, nameInput;
|
|
for (let i = 0; i < playerCount; i++) {
|
|
if (i < players.length) {
|
|
player = players[i];
|
|
} else {
|
|
player = PageMtgGame.makeDefaultGamePlayer();
|
|
players.push(player);
|
|
}
|
|
wrapper = wrapperTemplate.cloneNode(true);
|
|
wrapper.removeAttribute("id");
|
|
wrapper.setAttribute(flagDisplayOrder, i + 1);
|
|
wrapper.classList.remove(flagIsCollapsed);
|
|
wrapperHeading = wrapper.querySelector('label');
|
|
wrapperHeading.innerText = 'Player ' + (i + 1);
|
|
userDdl = wrapper.querySelector('.playerUser select');
|
|
DOM.setElementValuesCurrentAndPrevious(userDdl, player[attrUserId]);
|
|
deckDdl = wrapper.querySelector('.playerDeck select');
|
|
DOM.setElementValuesCurrentAndPrevious(deckDdl, player[attrDeckId]);
|
|
nameInput = wrapper.querySelector('.playerName input');
|
|
DOM.setElementValuesCurrentAndPrevious(nameInput, player[flagName]);
|
|
console.log('player: ', player);
|
|
grid.appendChild(wrapper);
|
|
}
|
|
}
|
|
static makeDefaultGamePlayer() {
|
|
return {
|
|
[attrPlayerId]: -players.length,
|
|
[attrGameId]: gameId,
|
|
[attrUserId]: user[attrUserId],
|
|
[attrDeckId]: 0,
|
|
[flagName]: "",
|
|
[flagNotes]: null,
|
|
[flagDisplayOrder]: players.length,
|
|
[flagActive]: true
|
|
};
|
|
}
|
|
async startGame() {
|
|
const playerCountInput = document.getElementById('playerCount');
|
|
if (!playerCountInput) return;
|
|
const playerCount = parseInt(playerCountInput.value);
|
|
const playersToSave = [];
|
|
let playerSetupWrapper, playerId, player, userDdl, userId, deckDdl, deckId, nameInput, name;
|
|
for (let i = 0; i < playerCount; i++) {
|
|
playerSetupWrapper = document.querySelector('.player-name-input-wrapper[' + flagDisplayOrder + '="' + (i + 1) + '"]');
|
|
userDdl = playerSetupWrapper.querySelector('.playerUser select');
|
|
deckDdl = playerSetupWrapper.querySelector('.playerDeck select');
|
|
nameInput = playerSetupWrapper.querySelector('.playerName input');
|
|
userId = DOM.getElementValueCurrent(userDdl);
|
|
deckId = DOM.getElementValueCurrent(deckDdl);
|
|
name = nameInput ? nameInput.value.trim() || null : null; // `Player ${i + 1}` : `Player ${i + 1}`;
|
|
|
|
playerId = playerSetupWrapper.getAttribute(attrPlayerId);
|
|
player = players.filter(p => p[attrPlayerId] == playerId)[0];
|
|
playersToSave.push({
|
|
...player,
|
|
[attrGameId]: gameId,
|
|
[attrUserId]: userId,
|
|
[attrDeckId]: deckId,
|
|
[flagName]: name,
|
|
[flagDisplayOrder]: i + 1,
|
|
[flagActive]: true
|
|
});
|
|
}
|
|
|
|
// Save players to server
|
|
const comment = 'Save players';
|
|
const self = this;
|
|
API.saveGamePlayers(playersToSave, null, comment).then(data => {
|
|
if (data[flagStatus] == flagSuccess) {
|
|
self.leave();
|
|
window.location.reload();
|
|
} else {
|
|
console.error('Failed to save players:', data[flagMessage]);
|
|
PageMtgGame.showError('An error occurred while creating the game');
|
|
}
|
|
}).catch(error => {
|
|
console.error('Error creating game:', error);
|
|
PageMtgGame.showError('An error occurred while creating the game');
|
|
}).finally(() => {});
|
|
}
|
|
static resetGame() {
|
|
if (confirm('Are you sure you want to start a new game? Current game will be lost.')) {
|
|
localStorage.removeItem(`mtgGame_${gameId}`);
|
|
window.location.href = hashPageGames;
|
|
}
|
|
}
|
|
async saveGame() {
|
|
/*
|
|
const gameState = {
|
|
[flagPlayer]: players
|
|
, [flagRound]: rounds
|
|
, [flagDamage]: damageRecords
|
|
};
|
|
if (gameState[flagPlayer].length > 0) {
|
|
localStorage.setItem(`mtgGame_${gameId}`, JSON.stringify(gameState));
|
|
PageMtgGame.showSaveIndicator();
|
|
}
|
|
*/
|
|
const comment = 'Save player damage';
|
|
const self = this;
|
|
API.saveGameRoundPlayerDamages(rounds, damageRecords, null, comment).then(data => {
|
|
if (data[flagStatus] == flagSuccess) {
|
|
self.leave();
|
|
window.location.reload();
|
|
} else {
|
|
console.error('Failed to save player damages:', data[flagMessage]);
|
|
PageMtgGame.showError('An error occurred while saving player damages');
|
|
}
|
|
}).catch(error => {
|
|
console.error('Error saving player damages:', error);
|
|
PageMtgGame.showError('An error occurred while saving player damages');
|
|
}).finally(() => {});
|
|
}
|
|
/*
|
|
static debouncedSave() {
|
|
clearTimeout(PageMtgGame._saveTimeout);
|
|
PageMtgGame._saveTimeout = setTimeout(() => PageMtgGame.saveGame(), 500);
|
|
}
|
|
static showSaveIndicator() {
|
|
const indicator = document.getElementById('saveIndicator');
|
|
if (indicator) {
|
|
indicator.classList.add('show');
|
|
setTimeout(() => {
|
|
indicator.classList.remove('show');
|
|
}, 2000);
|
|
}
|
|
}
|
|
*/
|
|
saveRecordsTableDirty() {
|
|
this.saveGame();
|
|
}
|
|
static showError(message) {
|
|
// Check if there's an overlay error element
|
|
const errorOverlay = document.getElementById('overlayError');
|
|
if (errorOverlay) {
|
|
const errorLabel = errorOverlay.querySelector('.error-message, #labelError');
|
|
if (errorLabel) {
|
|
errorLabel.textContent = message;
|
|
}
|
|
errorOverlay.classList.remove('hidden');
|
|
errorOverlay.style.display = 'flex';
|
|
} else {
|
|
// Fallback to alert
|
|
alert(message);
|
|
}
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
|
|
// Static timeout reference for debouncing
|
|
PageMtgGame._saveTimeout = null;
|
|
;// ./static/js/pages/tcg/mtg_games.js
|
|
|
|
|
|
|
|
class PageMtgGames extends TableBasePage {
|
|
static hash = hashPageMtgGames;
|
|
static attrIdRowObject = attrGameId;
|
|
callSaveTableContent = API.saveGame;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
hookupFilters() {
|
|
/*
|
|
this.sharedHookupFilters();
|
|
this.hookupFilterActive();
|
|
*/
|
|
}
|
|
loadRowTable(rowJson) {
|
|
if (rowJson == null) return;
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("applying data row: ", rowJson);
|
|
}
|
|
}
|
|
getJsonRow(row) {
|
|
return;
|
|
}
|
|
initialiseRowNew(tbody, row) {}
|
|
postInitialiseRowNewCallback(tbody) {}
|
|
hookupTableMain() {
|
|
super.hookupTableMain();
|
|
// this.hookupTableMainRows();
|
|
this.hookupTcgGames();
|
|
// PageMtgGames.hideNewGameForm();
|
|
}
|
|
hookupTcgGames() {
|
|
console.log("hookupTableMain PageMtgGames");
|
|
// Initialize form submission
|
|
const newGameForm = document.getElementById('newGameForm');
|
|
if (newGameForm) {
|
|
newGameForm.addEventListener('submit', this.handleNewGameSubmit.bind(this)); // () => { this.handleNewGameSubmit.bind(this); });
|
|
}
|
|
|
|
// Initialize filter form
|
|
const filterForm = document.getElementById('formFilters');
|
|
if (filterForm) {
|
|
filterForm.addEventListener('submit', PageMtgGames.handleFilterSubmit);
|
|
}
|
|
|
|
// Close modal on escape key
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape') {
|
|
PageMtgGames.hideNewGameForm();
|
|
}
|
|
});
|
|
|
|
// Close modal on backdrop click
|
|
const modal = document.getElementById('newGameModal');
|
|
if (modal) {
|
|
modal.addEventListener('click', function (e) {
|
|
if (e.target === modal) {
|
|
PageMtgGames.hideNewGameForm();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Button onclicks
|
|
const newGameButton = document.getElementById('btnNewGame');
|
|
if (newGameButton) {
|
|
newGameButton.addEventListener('click', PageMtgGames.showNewGameForm);
|
|
}
|
|
const cancelNewGameButtons = document.querySelectorAll('#newGameForm .form-actions .btn-tcg.btn-tcg-secondary' + ',' + '#newGameModal .modal-content .modal-header .modal-close');
|
|
if (cancelNewGameButtons.length > 0) {
|
|
cancelNewGameButtons.forEach(button => {
|
|
button.addEventListener('click', PageMtgGames.hideNewGameForm);
|
|
});
|
|
}
|
|
}
|
|
static showNewGameForm() {
|
|
const modal = document.getElementById('newGameModal');
|
|
if (modal) {
|
|
modal.classList.remove(flagIsCollapsed);
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Focus on first input
|
|
const firstInput = modal.querySelector('input, select');
|
|
if (firstInput) {
|
|
firstInput.focus();
|
|
}
|
|
}
|
|
}
|
|
static hideNewGameForm() {
|
|
const modal = document.getElementById('newGameModal');
|
|
if (modal) {
|
|
modal.classList.add(flagIsCollapsed);
|
|
document.body.style.overflow = '';
|
|
|
|
// Reset form
|
|
const form = document.getElementById('newGameForm');
|
|
if (form) {
|
|
form.reset();
|
|
}
|
|
}
|
|
}
|
|
async handleNewGameSubmit(e) {
|
|
e.preventDefault();
|
|
const form = e.target;
|
|
const formData = new FormData(form);
|
|
const gameType = formData.get('game_type');
|
|
const gameData = {
|
|
[attrGameId]: -1,
|
|
[flagIsCommander]: gameType === 'commander',
|
|
[flagIsDraft]: gameType === 'draft',
|
|
[flagIsSealed]: gameType === 'sealed',
|
|
[flagLocationName]: formData.get(flagLocationName) || null,
|
|
[flagNotes]: formData.get(flagNotes) || null,
|
|
[flagStartOn]: new Date().toISOString(),
|
|
[flagStartingLife]: formData.get(flagStartingLife) || 40,
|
|
[flagActive]: true
|
|
};
|
|
const submitBtn = form.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.textContent;
|
|
submitBtn.textContent = 'Creating...';
|
|
submitBtn.disabled = true;
|
|
const games = [gameData];
|
|
const comment = 'Create new game';
|
|
API.saveGame(games, form, comment).then(data => {
|
|
if (data[flagStatus] == flagSuccess) {
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Records saved!');
|
|
utils_Utils.consoleLogIfNotProductionEnvironment('Data received:', data);
|
|
}
|
|
const gamePageHash = `${hashPageGame}/${data[attrGameId]}`;
|
|
let filtersJson = {};
|
|
this.leave();
|
|
API.goToHash(gamePageHash, filtersJson);
|
|
} else {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("error: " + data[flagMessage]);
|
|
// OverlayError.show(data[flagMessage]);
|
|
window.location.reload();
|
|
}
|
|
}).catch(error => {
|
|
console.error('Error creating game:', error);
|
|
PageMtgGames.showError('An error occurred while creating the game');
|
|
}).finally(() => {
|
|
submitBtn.textContent = originalText;
|
|
submitBtn.disabled = false;
|
|
});
|
|
}
|
|
static handleFilterSubmit(e) {
|
|
// Let the form submit normally - it will reload with query params
|
|
// You can add client-side filtering here if needed
|
|
}
|
|
static getCSRFToken() {
|
|
// Try meta tag first
|
|
const metaTag = document.querySelector('meta[name="csrf-token"]');
|
|
if (metaTag) {
|
|
return metaTag.getAttribute('content');
|
|
}
|
|
|
|
// Try hidden input
|
|
const hiddenInput = document.querySelector('input[name="csrf_token"]');
|
|
if (hiddenInput) {
|
|
return hiddenInput.value;
|
|
}
|
|
|
|
// Try cookie
|
|
const cookies = document.cookie.split(';');
|
|
for (let cookie of cookies) {
|
|
const [name, value] = cookie.trim().split('=');
|
|
if (name === 'csrf_token') {
|
|
return value;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
static showError(message) {
|
|
// Check if there's an overlay error element
|
|
const errorOverlay = document.getElementById('overlayError');
|
|
if (errorOverlay) {
|
|
const errorLabel = errorOverlay.querySelector('.error-message, #labelError');
|
|
if (errorLabel) {
|
|
errorLabel.textContent = message;
|
|
}
|
|
errorOverlay.classList.remove(flagIsCollapsed);
|
|
errorOverlay.style.display = 'flex';
|
|
} else {
|
|
// Fallback to alert
|
|
alert(message);
|
|
}
|
|
}
|
|
static showSuccess(message) {
|
|
// Could implement a toast notification here
|
|
console.log('Success:', message);
|
|
}
|
|
static joinGame(gameId) {
|
|
window.location.href = `${hashPageGame}/${gameId}`;
|
|
}
|
|
static async deleteGame(gameId) {
|
|
if (!confirm('Are you sure you want to delete this game? This action cannot be undone.')) {
|
|
return;
|
|
}
|
|
try {
|
|
const gameData = {
|
|
'game_id': gameId,
|
|
'active': false
|
|
};
|
|
const response = await fetch(hashSaveGame, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': PageMtgGames.getCSRFToken()
|
|
},
|
|
body: JSON.stringify({
|
|
[flagGame]: [gameData],
|
|
'form-filters': {},
|
|
'comment': 'Game deleted'
|
|
})
|
|
});
|
|
const result = await response.json();
|
|
if (result.status === 'success') {
|
|
// Remove the row from the table
|
|
const row = document.querySelector(`tr[data-game-id="${gameId}"]`);
|
|
if (row) {
|
|
row.style.animation = 'tcg-fadeOut 0.3s ease-out forwards';
|
|
setTimeout(() => row.remove(), 300);
|
|
}
|
|
} else {
|
|
PageMtgGames.showError(result.message || 'Failed to delete game');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting game:', error);
|
|
PageMtgGames.showError('An error occurred while deleting the game');
|
|
}
|
|
}
|
|
toggleShowButtonsSaveCancel() {}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/tcg/mtg_home.js
|
|
|
|
|
|
|
|
|
|
class PageMtgHome extends BasePage {
|
|
static hash = hashPageMtgHome;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
this.hookupTcgHome();
|
|
}
|
|
hookupTcgHome() {}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/legal/accessibility_report.js
|
|
|
|
class PageAccessibilityReport extends BasePage {
|
|
static hash = hashPageAccessibilityReport;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/legal/accessibility_statement.js
|
|
|
|
class PageAccessibilityStatement extends BasePage {
|
|
static hash = hashPageAccessibilityStatement;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/legal/license.js
|
|
|
|
class PageLicense extends BasePage {
|
|
static hash = hashPageLicense;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/legal/privacy_policy.js
|
|
|
|
class PagePrivacyPolicy extends BasePage {
|
|
static hash = hashPagePrivacyPolicy;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/legal/retention_schedule.js
|
|
|
|
class PageRetentionSchedule extends BasePage {
|
|
static hash = hashPageDataRetentionSchedule;
|
|
constructor(router) {
|
|
super(router);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/mixin.js
|
|
class MixinPage {
|
|
constructor(pageCurrent) {
|
|
this.page = pageCurrent;
|
|
}
|
|
initialize() {
|
|
Utils.consoleLogIfNotProductionEnvironment('hookup start for ', this.page.hash);
|
|
this.hookupFilters();
|
|
this.hookupLocalStorage();
|
|
}
|
|
hookupFilters() {}
|
|
hookupLocalStorage() {}
|
|
leave() {}
|
|
}
|
|
;// ./static/js/pages/mixin_table.js
|
|
|
|
class TableMixinPage extends MixinPage {
|
|
constructor(pageCurrent) {
|
|
super(pageCurrent);
|
|
}
|
|
initialize() {
|
|
super.initialize();
|
|
this.hookupFilters();
|
|
this.hookupTable();
|
|
}
|
|
hookupFilters() {
|
|
// Implement filter-specific functionality here
|
|
}
|
|
hookupTable() {
|
|
// Implement table-specific functionality here
|
|
}
|
|
}
|
|
;// ./static/js/pages/user/user.js
|
|
|
|
|
|
|
|
|
|
class PageUser extends TableBasePage {
|
|
static hash = hashPageUserAccount;
|
|
static attrIdRowObject = attrUserId;
|
|
callSaveTableContent = API.saveUsers;
|
|
constructor(router) {
|
|
super(router);
|
|
this.mixin = new TableMixinPage(this);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
this.hookupTableMain();
|
|
}
|
|
hookupFilters() {}
|
|
loadRowTable(rowJson) {
|
|
if (rowJson == null) return;
|
|
if (_verbose) {
|
|
Utils.consoleLogIfNotProductionEnvironment("applying data row: ", rowJson);
|
|
}
|
|
}
|
|
getTableRecords(dirtyOnly = false) {
|
|
dirtyOnly = true;
|
|
let container = document.querySelector('.' + flagCard + '.' + flagUser);
|
|
return [this.getJsonRow(container)];
|
|
}
|
|
getJsonRow(container) {
|
|
console.log("getJsonRow: ", container);
|
|
if (container == null) return;
|
|
let inputFirstname = container.querySelector(' #' + flagFirstname);
|
|
let inputSurname = container.querySelector(' #' + flagSurname);
|
|
let inputEmail = container.querySelector(' #' + flagEmail);
|
|
let idUser = container.getAttribute(attrUserId);
|
|
let jsonRow = {
|
|
[attrUserAuth0Id]: null,
|
|
[flagEmail]: null,
|
|
[flagIsEmailVerified]: null,
|
|
[flagIsSuperUser]: null,
|
|
[flagCanAdminUser]: null
|
|
};
|
|
jsonRow[attrUserId] = idUser;
|
|
jsonRow[flagFirstname] = DOM.getElementAttributeValueCurrent(inputFirstname);
|
|
jsonRow[flagSurname] = DOM.getElementAttributeValueCurrent(inputSurname);
|
|
jsonRow[flagEmail] = DOM.getElementAttributeValueCurrent(inputEmail);
|
|
return jsonRow;
|
|
}
|
|
initialiseRowNew(tbody, row) {}
|
|
postInitialiseRowNewCallback(tbody) {}
|
|
hookupTableMain() {
|
|
super.hookupTableMain();
|
|
this.hookupFieldsFirstname();
|
|
this.hookupFieldsSurname();
|
|
this.hookupFieldsEmail();
|
|
}
|
|
hookupFieldsFirstname() {
|
|
this.hookupChangeHandlerTableCells('.' + flagCard + '.' + flagUser + ' #' + flagFirstname);
|
|
}
|
|
hookupFieldsSurname() {
|
|
this.hookupChangeHandlerTableCells('.' + flagCard + '.' + flagUser + ' #' + flagSurname);
|
|
}
|
|
hookupFieldsEmail() {
|
|
this.hookupChangeHandlerTableCells('.' + flagCard + '.' + flagUser + ' #' + flagEmail);
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/pages/user/users.js
|
|
|
|
|
|
|
|
|
|
|
|
class PageUsers extends TableBasePage {
|
|
static hash = hashPageUserAccounts;
|
|
static attrIdRowObject = attrUserId;
|
|
callSaveTableContent = API.saveUsers;
|
|
constructor(router) {
|
|
super(router);
|
|
this.mixin = new TableMixinPage(this);
|
|
}
|
|
initialize() {
|
|
this.sharedInitialize();
|
|
}
|
|
hookupFilters() {
|
|
this.sharedHookupFilters();
|
|
this.hookupFilterActive();
|
|
}
|
|
loadRowTable(rowJson) {
|
|
if (rowJson == null) return;
|
|
if (_verbose) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("applying data row: ", rowJson);
|
|
}
|
|
}
|
|
getJsonRow(row) {
|
|
if (row == null) return;
|
|
let inputFirstname = row.querySelector('td.' + flagFirstname + ' .' + flagFirstname);
|
|
let inputSurname = row.querySelector('td.' + flagSurname + ' .' + flagSurname);
|
|
let inputNotes = row.querySelector('td.' + flagNotes + ' .' + flagNotes);
|
|
let buttonActive = row.querySelector('td.' + flagActive + ' .' + flagActive);
|
|
let jsonRow = {
|
|
[attrUserAuth0Id]: null,
|
|
[flagEmail]: null,
|
|
[flagIsEmailVerified]: null,
|
|
[flagIsSuperUser]: null,
|
|
[flagCanAdminUser]: null
|
|
};
|
|
jsonRow[attrUserId] = row.getAttribute(attrUserId);
|
|
jsonRow[flagFirstname] = DOM.getElementAttributeValueCurrent(inputFirstname);
|
|
jsonRow[flagSurname] = DOM.getElementAttributeValueCurrent(inputSurname);
|
|
jsonRow[flagNotes] = DOM.getElementAttributeValueCurrent(inputNotes);
|
|
jsonRow[flagActive] = buttonActive.classList.contains(flagDelete);
|
|
console.log("jsonRow");
|
|
console.log(jsonRow);
|
|
return jsonRow;
|
|
}
|
|
initialiseRowNew(tbody, row) {}
|
|
postInitialiseRowNewCallback(tbody) {
|
|
let newRows = tbody.querySelectorAll('tr.' + flagRowNew);
|
|
let newestRow = newRows[0];
|
|
let clickableElementsSelector = [].join('');
|
|
newestRow.querySelectorAll(clickableElementsSelector).forEach(clickableElement => {
|
|
clickableElement.click();
|
|
});
|
|
}
|
|
hookupTableMain() {
|
|
super.hookupTableMain();
|
|
this.hookupFieldsFirstname();
|
|
this.hookupFieldsSurname();
|
|
this.hookupFieldsNotesTable();
|
|
this.hookupFieldsActive();
|
|
}
|
|
hookupFieldsFirstname() {
|
|
this.hookupChangeHandlerTableCells(flagFirstname);
|
|
}
|
|
hookupFieldsSurname() {
|
|
this.hookupChangeHandlerTableCells(flagSurname);
|
|
}
|
|
leave() {
|
|
super.leave();
|
|
}
|
|
}
|
|
;// ./static/js/router.js
|
|
// Pages
|
|
// Core
|
|
// TCG
|
|
|
|
|
|
|
|
|
|
// Legal
|
|
|
|
|
|
|
|
|
|
|
|
// User
|
|
// import PageUserLogin from './pages/user/login.js';
|
|
// import PageUserLogout from './pages/user/logout.js';
|
|
|
|
|
|
|
|
|
|
|
|
class Router {
|
|
constructor() {
|
|
// Pages
|
|
this.pages = {};
|
|
// Core
|
|
// TCG
|
|
this.pages[hashPageMtgDecks] = {
|
|
name: 'PageMtgDecks',
|
|
module: PageMtgDecks
|
|
};
|
|
this.pages[hashPageMtgGame] = {
|
|
name: 'PageMtgGame',
|
|
module: PageMtgGame
|
|
};
|
|
this.pages[hashPageMtgGames] = {
|
|
name: 'PageMtgGames',
|
|
module: PageMtgGames
|
|
};
|
|
this.pages[hashPageMtgHome] = {
|
|
name: 'PageMtgGame',
|
|
module: PageMtgHome
|
|
};
|
|
// Legal
|
|
this.pages[hashPageAccessibilityStatement] = {
|
|
name: 'PageAccessibilityStatement',
|
|
module: PageAccessibilityStatement
|
|
};
|
|
this.pages[hashPageDataRetentionSchedule] = {
|
|
name: 'PageDataRetentionSchedule',
|
|
module: PageRetentionSchedule
|
|
};
|
|
this.pages[hashPageLicense] = {
|
|
name: 'PageLicense',
|
|
module: PageLicense
|
|
};
|
|
this.pages[hashPagePrivacyPolicy] = {
|
|
name: 'PagePrivacyPolicy',
|
|
module: PagePrivacyPolicy
|
|
};
|
|
// User
|
|
// this.pages[hashPageUserLogin] = { name: 'PageUserLogin', module: PageUserLogin }; // pathModule: './pages/user/login.js' };
|
|
// this.pages[hashPageUserLogout] = { name: 'PageUserLogout', module: PageUserLogout }; // pathModule: './pages/user/logout.js' };
|
|
this.pages[hashPageUserAccount] = {
|
|
name: 'PageUser',
|
|
module: PageUser
|
|
};
|
|
this.pages[hashPageUserAccounts] = {
|
|
name: 'PageUsers',
|
|
module: PageUsers
|
|
};
|
|
// Routes
|
|
this.routes = {};
|
|
// Core
|
|
// TCG
|
|
this.routes[hashPageMtgDecks] = (isPopState = false) => this.navigateToHash(hashPageMtgDecks, isPopState);
|
|
this.routes[hashPageMtgGame] = (isPopState = false) => this.navigateToHash(hashPageMtgGame, isPopState);
|
|
this.routes[hashPageMtgGames] = (isPopState = false) => this.navigateToHash(hashPageMtgGames, isPopState);
|
|
this.routes[hashPageMtgHome] = (isPopState = false) => this.navigateToHash(hashPageMtgHome, isPopState);
|
|
// Legal
|
|
this.routes[hashPageAccessibilityStatement] = (isPopState = false) => this.navigateToHash(hashPageAccessibilityStatement, isPopState);
|
|
this.routes[hashPageDataRetentionSchedule] = (isPopState = false) => this.navigateToHash(hashPageDataRetentionSchedule, isPopState);
|
|
this.routes[hashPageLicense] = (isPopState = false) => this.navigateToHash(hashPageLicense, isPopState);
|
|
this.routes[hashPagePrivacyPolicy] = (isPopState = false) => this.navigateToHash(hashPagePrivacyPolicy, isPopState);
|
|
// User
|
|
// this.routes[hashPageUserLogin] = (isPopState = false) => this.navigateToHash(hashPageUserLogin, isPopState);
|
|
// this.routes[hashPageUserLogout] = (isPopState = false) => this.navigateToHash(hashPageUserLogout, isPopState);
|
|
this.routes[hashPageUserAccount] = (isPopState = false) => this.navigateToHash(hashPageUserAccount, isPopState);
|
|
this.routes[hashPageUserAccounts] = (isPopState = false) => this.navigateToHash(hashPageUserAccounts, isPopState);
|
|
this.initialize();
|
|
}
|
|
loadPage(hashPage, isPopState = false) {
|
|
const PageClass = this.getClassPageFromHash(hashPage);
|
|
this.currentPage = new PageClass(this);
|
|
this.currentPage.initialize(isPopState);
|
|
window.addEventListener('beforeunload', () => this.currentPage.leave());
|
|
}
|
|
getClassPageFromHash(hashPage) {
|
|
let pageJson = this.pages[hashPage];
|
|
try {
|
|
const module = pageJson.module;
|
|
return module;
|
|
} catch (error) {
|
|
utils_Utils.consoleLogIfNotProductionEnvironment("this.pages: ", this.pages);
|
|
console.error('Page not found:', hashPage);
|
|
throw error;
|
|
}
|
|
}
|
|
initialize() {
|
|
window.addEventListener('popstate', this.handlePopState.bind(this));
|
|
}
|
|
handlePopState(event) {
|
|
this.loadPageCurrent();
|
|
}
|
|
loadPageCurrent() {
|
|
const hashPageCurrent = DOM.getHashPageCurrent();
|
|
this.loadPage(hashPageCurrent);
|
|
}
|
|
navigateToHash(hash, data = null, params = null, isPopState = false) {
|
|
let url = API.getUrlFromHash(hash, params);
|
|
history.pushState({
|
|
data: data,
|
|
params: params
|
|
}, '', hash);
|
|
API.goToUrl(url, data);
|
|
}
|
|
navigateToUrl(url, data = null, appendHistory = true) {
|
|
// this.beforeLeave();
|
|
if (appendHistory) history.pushState(data, '', url);
|
|
url = API.parameteriseUrl(url, data);
|
|
API.goToUrl(url);
|
|
}
|
|
static loadPageBodyFromResponse(response) {
|
|
DOM.loadPageBody(response.data);
|
|
}
|
|
}
|
|
const router = new Router();
|
|
;// ./static/js/app.js
|
|
|
|
|
|
|
|
|
|
class App {
|
|
constructor() {
|
|
this.dom = new DOM();
|
|
this.router = new Router();
|
|
}
|
|
initialize() {
|
|
this.setupEventListeners();
|
|
this.start();
|
|
}
|
|
setupEventListeners() {
|
|
// document.addEventListener('click', this.handleGlobalClick.bind(this));
|
|
}
|
|
handleGlobalClick(event) {}
|
|
start() {
|
|
this.initPageCurrent();
|
|
}
|
|
initPageCurrent() {
|
|
this.router.loadPageCurrent();
|
|
}
|
|
}
|
|
const app = new App();
|
|
function domReady(fn) {
|
|
if (document.readyState !== 'loading') {
|
|
fn();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', fn);
|
|
}
|
|
}
|
|
domReady(() => {
|
|
app.initialize();
|
|
});
|
|
window.app = app;
|
|
/* harmony default export */ const js_app = ((/* unused pure expression or super */ null && (app)));
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules.
|
|
(() => {
|
|
// extracted by mini-css-extract-plugin
|
|
|
|
})();
|
|
|
|
/******/ })()
|
|
;
|
|
//# sourceMappingURL=main.bundle.js.map
|