이전 util 이전
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.2",
|
||||
"@tsconfig/node18": "^18.2.0",
|
||||
"@types/async-lock": "^1.4.0",
|
||||
"@types/bootstrap": "^5.2.6",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/express-session": "^1.17.7",
|
||||
|
||||
Generated
+7
@@ -73,6 +73,9 @@ devDependencies:
|
||||
'@tsconfig/node18':
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
'@types/async-lock':
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
'@types/bootstrap':
|
||||
specifier: ^5.2.6
|
||||
version: 5.2.6
|
||||
@@ -528,6 +531,10 @@ packages:
|
||||
resolution: {integrity: sha512-yhxwIlFVSVcMym3O31HoMnRXpoenmpIxcj4Yoes2DUpe+xCJnA7ECQP1Vw889V0jTt/2nzvpLQ/UuMYCd3JPIg==}
|
||||
dev: true
|
||||
|
||||
/@types/async-lock@1.4.0:
|
||||
resolution: {integrity: sha512-2+rYSaWrpdbQG3SA0LmMT6YxWLrI81AqpMlSkw3QtFc2HGDufkweQSn30Eiev7x9LL0oyFrBqk1PXOnB9IEgKg==}
|
||||
dev: true
|
||||
|
||||
/@types/body-parser@1.19.2:
|
||||
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
|
||||
dependencies:
|
||||
|
||||
@@ -2,11 +2,12 @@ import "dotenv/config";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
export const AppDataSource = new DataSource({
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
database: "db/database.sqlite",
|
||||
synchronize: true,
|
||||
logging: false,
|
||||
type: "mariadb",
|
||||
host: process.env.GAME_DB_HOST,
|
||||
port: Number(process.env.GAME_DB_PORT),
|
||||
username: process.env.GAME_DB_USER,
|
||||
password: process.env.GAME_DB_PASSWORD,
|
||||
charset: "utf8mb4",
|
||||
entities: [],
|
||||
migrations: [],
|
||||
subscribers: [],
|
||||
|
||||
Vendored
+4
-1
@@ -1,9 +1,12 @@
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NODE_ENV: 'development' | 'production';
|
||||
GAME_DB_SERVER: string;
|
||||
GAME_DB_HOST: string;
|
||||
GAME_DB_DATABASE: string;
|
||||
GAME_DB_PORT: string;
|
||||
GAME_DB_USER: string;
|
||||
GAME_DB_PASSWORD: string;
|
||||
SERVER_PORT: string; //숫자
|
||||
SESSION_SECRET: string; //길게
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -2,7 +2,7 @@ import 'dotenv/config';
|
||||
import 'reflect-metadata';
|
||||
import { BasicQueryDTO } from './api/index.js';
|
||||
import { validate } from 'class-validator';
|
||||
import express, { Request, Response } from "express"
|
||||
import express, { type Request, type Response } from "express"
|
||||
import { AppDataSource } from "./data_source"
|
||||
import dotenv from "dotenv";
|
||||
import session from "express-session";
|
||||
@@ -23,6 +23,10 @@ export async function test() {
|
||||
|
||||
await test();
|
||||
|
||||
const ownConfig = {
|
||||
sessionSecret: process.env.SESSION_SECRET,
|
||||
port: Number(process.env.SERVER_PORT),
|
||||
}
|
||||
|
||||
AppDataSource.initialize().then(async () => {
|
||||
|
||||
@@ -35,7 +39,7 @@ AppDataSource.initialize().then(async () => {
|
||||
}))
|
||||
app.set('etag', false);
|
||||
|
||||
app.use('/', rootRouter);
|
||||
//app.use('/', rootRouter);
|
||||
|
||||
// start express server
|
||||
app.listen(ownConfig.port)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
export class NotNullExpected extends TypeError {
|
||||
public name = 'NotNullExpected';
|
||||
public override name = 'NotNullExpected';
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
import type { TurnObj } from "@/defs";
|
||||
import { clone, isString, range } from "lodash-es";
|
||||
import { ref, type Ref } from "vue";
|
||||
import { unwrap } from "./unwrap";
|
||||
|
||||
export type TurnObjWithTime = TurnObj & {
|
||||
time: string;
|
||||
year?: number;
|
||||
month?: number;
|
||||
tooltip?: string;
|
||||
style?: Record<string, string>;
|
||||
};
|
||||
|
||||
export const getEmptyTurn = (maxTurn: number): TurnObjWithTime[] => Array.from<TurnObjWithTime>({
|
||||
length: maxTurn,
|
||||
}).fill({
|
||||
arg: {},
|
||||
brief: "",
|
||||
action: "",
|
||||
year: undefined,
|
||||
month: undefined,
|
||||
time: "",
|
||||
});
|
||||
|
||||
export class QueryActionHelper {
|
||||
public readonly reservedCommandList: Ref<TurnObjWithTime[]>;
|
||||
public readonly selectedTurnList: Ref<Set<number>>;
|
||||
public readonly prevSelectedTurnList: Ref<Set<number>>;
|
||||
|
||||
constructor(
|
||||
protected maxTurn: number,
|
||||
) {
|
||||
this.reservedCommandList = ref(getEmptyTurn(maxTurn));
|
||||
this.selectedTurnList = ref(new Set());
|
||||
this.prevSelectedTurnList = ref(new Set([0]));
|
||||
}
|
||||
|
||||
toggleTurn(...reqTurnList: number[] | string[]) {
|
||||
for (let turnIdx of reqTurnList) {
|
||||
if (isString(turnIdx)) {
|
||||
turnIdx = parseInt(turnIdx);
|
||||
}
|
||||
if (this.selectedTurnList.value.has(turnIdx)) {
|
||||
this.selectedTurnList.value.delete(turnIdx);
|
||||
} else {
|
||||
this.selectedTurnList.value.add(turnIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectTurn(...reqTurnList: number[] | string[]) {
|
||||
this.selectedTurnList.value.clear();
|
||||
for (const turnIdx of reqTurnList) {
|
||||
if (isString(turnIdx)) {
|
||||
this.selectedTurnList.value.add(parseInt(turnIdx));
|
||||
} else {
|
||||
this.selectedTurnList.value.add(turnIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectStep(begin: number, step: number) {
|
||||
this.selectedTurnList.value.clear();
|
||||
for (const idx of range(0, this.maxTurn)) {
|
||||
if ((idx - begin) % step == 0) {
|
||||
this.selectedTurnList.value.add(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
for (let i = 0; i < this.maxTurn; i++) {
|
||||
this.selectedTurnList.value.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedTurnList(useSort = true): number[] {
|
||||
let result: number[];
|
||||
if (this.selectedTurnList.value.size) {
|
||||
result = Array.from(this.selectedTurnList.value);
|
||||
}
|
||||
else if (this.prevSelectedTurnList.value.size) {
|
||||
result = Array.from(this.prevSelectedTurnList.value);
|
||||
}
|
||||
else {
|
||||
return [0];
|
||||
}
|
||||
|
||||
if (useSort) {
|
||||
return result.sort((a, b) => a - b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
releaseSelectedTurnList() {
|
||||
if (this.selectedTurnList.value.size > 0) {
|
||||
this.prevSelectedTurnList.value.clear();
|
||||
for (const v of this.selectedTurnList.value) {
|
||||
this.prevSelectedTurnList.value.add(v);
|
||||
}
|
||||
this.selectedTurnList.value.clear();
|
||||
}
|
||||
}
|
||||
|
||||
extractQueryActions(): [number[], TurnObj][] {
|
||||
const reqTurnList = this.getSelectedTurnList();
|
||||
const selectedMinTurnIdx = unwrap(Math.min(...reqTurnList));
|
||||
|
||||
const buffer = new Map<string, [number[], TurnObj]>();
|
||||
for (const rawTurnIdx of reqTurnList) {
|
||||
const turnIdx = rawTurnIdx - selectedMinTurnIdx;
|
||||
const rawAction = this.reservedCommandList.value[rawTurnIdx]
|
||||
const actionStr = JSON.stringify([rawAction.action, rawAction.arg]);
|
||||
if (buffer.has(actionStr)) {
|
||||
const items = unwrap(buffer.get(actionStr));
|
||||
items[0].push(turnIdx);
|
||||
}
|
||||
else {
|
||||
buffer.set(actionStr, [[turnIdx], {
|
||||
action: rawAction.action,
|
||||
arg: clone(rawAction.arg),
|
||||
brief: rawAction.brief
|
||||
}])
|
||||
}
|
||||
}
|
||||
return Array.from(buffer.values());
|
||||
}
|
||||
|
||||
amplifyQueryActions(rawActions: [number[], TurnObj][], reqTurnList: number[]): [number[], TurnObj][] {
|
||||
if (reqTurnList.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let minQueryIdx = this.maxTurn;
|
||||
let maxQueryIdx = 0;
|
||||
for (const [turnList] of rawActions) {
|
||||
for (const turnIdx of turnList) {
|
||||
minQueryIdx = Math.min(minQueryIdx, turnIdx);
|
||||
maxQueryIdx = Math.max(maxQueryIdx, turnIdx);
|
||||
}
|
||||
}
|
||||
const queryLength = maxQueryIdx - minQueryIdx + 1;
|
||||
|
||||
const queryTurnList: number[] = [reqTurnList[0]];
|
||||
for (const reqTurnIdx of reqTurnList) {
|
||||
const last = queryTurnList[queryTurnList.length - 1];
|
||||
if (reqTurnIdx < last + queryLength) {
|
||||
continue;
|
||||
}
|
||||
queryTurnList.push(reqTurnIdx);
|
||||
}
|
||||
|
||||
const actions: [number[], TurnObj][] = [];
|
||||
for (const [baseTurnList, action] of rawActions) {
|
||||
const subTurnList: number[] = [];
|
||||
for (const baseTurnIdx of baseTurnList) {
|
||||
for (const queryTurnIdx of queryTurnList) {
|
||||
const targetTurn = baseTurnIdx + queryTurnIdx;
|
||||
if (targetTurn >= this.maxTurn) {
|
||||
continue;
|
||||
}
|
||||
subTurnList.push(baseTurnIdx + queryTurnIdx);
|
||||
}
|
||||
}
|
||||
if (subTurnList.length == 0) {
|
||||
continue;
|
||||
}
|
||||
actions.push([subTurnList, action]);
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -5,9 +5,9 @@ export interface RNG {
|
||||
*/
|
||||
getMaxInt(): number;
|
||||
|
||||
nextBytes(bytes: number): Uint8Array;
|
||||
nextBits(bits: number): Uint8Array;
|
||||
nextBytes(bytes: number): Promise<Uint8Array>;
|
||||
nextBits(bits: number): Promise<Uint8Array>;
|
||||
|
||||
nextInt(max?: number): number;
|
||||
nextFloat1(): number;
|
||||
nextInt(max?: number): Promise<number>;
|
||||
nextFloat1(): Promise<number>;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
export class RuntimeError extends Error {
|
||||
public name = 'RuntimeError';
|
||||
constructor(public message: string = '') {
|
||||
public override name = 'RuntimeError';
|
||||
constructor(public override message: string = '') {
|
||||
super(message);
|
||||
}
|
||||
toString(): string {
|
||||
override toString(): string {
|
||||
if (this.message) {
|
||||
return this.name + ': ' + this.message;
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import type { TurnObj } from '@/defs';
|
||||
import { ref, watch, type Ref } from 'vue';
|
||||
|
||||
|
||||
export class StoredActionsHelper {
|
||||
public readonly recentActions: Ref<Map<string, TurnObj>>;
|
||||
public readonly storedActions = ref(new Map<string, [number[], TurnObj][]>());
|
||||
public readonly clipboard = ref<[number[], TurnObj][] | undefined>(undefined);
|
||||
public readonly activatedCategory = ref<string>("");
|
||||
public readonly isEditMode = ref(false);
|
||||
|
||||
public readonly recentActionsKey: string;
|
||||
public readonly storedActionsKey: string;
|
||||
public readonly clipboardKey: string;
|
||||
public readonly activatedCategoryKey: string;
|
||||
public readonly editModeKey: string;
|
||||
|
||||
constructor(protected serverNick: string, protected type: 'general' | 'nation', protected mapName: string, protected unitSet: string, protected maxRecent = 10) {
|
||||
this.recentActions = ref(new Map());
|
||||
const typeKey = `${serverNick}_${mapName}_${unitSet}_${type}`;
|
||||
this.recentActionsKey = `${typeKey}RecentActions`;
|
||||
this.storedActionsKey = `${typeKey}StoredActions`;
|
||||
this.clipboardKey = `${typeKey}Clipboard`;
|
||||
this.activatedCategoryKey = `${typeKey}ActivatedCategory`;
|
||||
this.editModeKey = `${serverNick}_${type}_isEditMode`;
|
||||
|
||||
this.loadRecentActions();
|
||||
this.loadStoredActions();
|
||||
|
||||
const rawClipboard = localStorage.getItem(this.clipboardKey);
|
||||
if (rawClipboard !== null) {
|
||||
this.clipboard.value = JSON.parse(rawClipboard);
|
||||
}
|
||||
watch(this.clipboard, (newValue) => {
|
||||
localStorage.setItem(this.clipboardKey, JSON.stringify(newValue));
|
||||
});
|
||||
|
||||
const rawActivatedCategory = localStorage.getItem(this.activatedCategoryKey);
|
||||
if (rawActivatedCategory !== null) {
|
||||
this.activatedCategory.value = JSON.parse(rawActivatedCategory);
|
||||
}
|
||||
watch(this.activatedCategory, (newValue) => {
|
||||
localStorage.setItem(this.activatedCategoryKey, JSON.stringify(newValue));
|
||||
});
|
||||
|
||||
this.isEditMode.value = localStorage.getItem(this.editModeKey) === '1';
|
||||
watch(this.isEditMode, (newValue) => {
|
||||
localStorage.setItem(this.editModeKey, newValue ? '1' : '0')
|
||||
})
|
||||
}
|
||||
|
||||
loadRecentActions() {
|
||||
try {
|
||||
const rawRecentActions = JSON.parse(localStorage.getItem(this.recentActionsKey) ?? '[]') as TurnObj[];
|
||||
const recentActions = new Map<string, TurnObj>();
|
||||
for (const action of rawRecentActions) {
|
||||
const actionKey = JSON.stringify([action.action, action.arg]);
|
||||
recentActions.set(actionKey, action);
|
||||
}
|
||||
this.recentActions.value = recentActions;
|
||||
}
|
||||
catch(e){
|
||||
console.log(`loadRecentActions error ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
pushRecentActions(action: TurnObj) {
|
||||
const actionKey = JSON.stringify([action.action, action.arg]);
|
||||
if (this.recentActions.value.has(actionKey)) {
|
||||
this.recentActions.value.delete(actionKey);
|
||||
}
|
||||
else if (this.recentActions.value.size > this.maxRecent) {
|
||||
const firstKey = this.recentActions.value.keys().next().value;
|
||||
this.recentActions.value.delete(firstKey);
|
||||
}
|
||||
this.recentActions.value.set(actionKey, action);
|
||||
this.saveRecentActions();
|
||||
}
|
||||
|
||||
saveRecentActions() {
|
||||
localStorage.setItem(this.recentActionsKey, JSON.stringify(Array.from(this.recentActions.value.values())));
|
||||
}
|
||||
|
||||
loadStoredActions() {
|
||||
try {
|
||||
const rawValue: [string, [number[], TurnObj][]][] = JSON.parse(
|
||||
localStorage.getItem(this.storedActionsKey) ?? '[]'
|
||||
);
|
||||
this.storedActions.value = new Map(rawValue);
|
||||
}
|
||||
catch(e){
|
||||
console.log(`loadStoredActions error ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
setStoredActions(actionKey: string, actions: [number[], TurnObj][]) {
|
||||
this.storedActions.value.set(actionKey, actions);
|
||||
console.log(this.storedActions.value);
|
||||
this.saveStoredActions();
|
||||
}
|
||||
|
||||
deleteStoredActions(actionKey: string) {
|
||||
if (this.storedActions.value.delete(actionKey)) {
|
||||
this.saveStoredActions();
|
||||
}
|
||||
}
|
||||
|
||||
saveStoredActions() {
|
||||
localStorage.setItem(
|
||||
this.storedActionsKey,
|
||||
JSON.stringify(Array.from(this.storedActions.value.entries()))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { escapeHtml } from '@/legacy/escapeHtml';
|
||||
import linkifyStr from 'linkify-string';
|
||||
/**
|
||||
* 단순한 Template 함수. <%변수명%>으로 template 가능
|
||||
* @see https://github.com/krasimir/absurd/blob/master/lib/processors/html/helpers/TemplateEngine.js
|
||||
* @param {string} html
|
||||
* @param {object} options
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
export function TemplateEngine(html: string, options: Record<string | number, unknown> = {}): string {
|
||||
const re = /<%(.+?)%>/g;
|
||||
const reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g;
|
||||
const code = ['with(obj) { var r=[];\n'];
|
||||
let cursor = 0;
|
||||
const add = function (line: string, js?: boolean) {
|
||||
js ? code.push(line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
|
||||
code.push(line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
|
||||
return add;
|
||||
};
|
||||
options.e = escapeHtml;
|
||||
options.linkifyStr = linkifyStr;
|
||||
for (; ;) {
|
||||
const match = re.exec(html);
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
add(html.slice(cursor, match.index))(match[1], true);
|
||||
cursor = match.index + match[0].length;
|
||||
}
|
||||
add(html.substr(cursor, html.length - cursor));
|
||||
|
||||
code.push('return r.join(""); }');
|
||||
const compiledCode = code.join('').replace(/[\r\t\n]/g, ' ');
|
||||
try {
|
||||
return new Function('obj', compiledCode).apply(options, [options]);
|
||||
} catch (err: unknown) {
|
||||
console.error(err, " in \n\nCode:\n", code, "\n");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { htmlReady } from "@util/htmlReady";
|
||||
import { unwrap } from "@util/unwrap";
|
||||
import { keyScreenMode, type ScreenModeType } from "@/defs";
|
||||
|
||||
export function auto500px(targetHeight = 700): void {
|
||||
let deviceWidth = -1;
|
||||
let viewportMeta!: HTMLMetaElement;
|
||||
let oldMode: ScreenModeType = 'auto';
|
||||
|
||||
|
||||
function init() {
|
||||
let _viewPortMeta = document.querySelector<HTMLMetaElement>("meta[name=viewport]");
|
||||
if (_viewPortMeta) {
|
||||
viewportMeta = _viewPortMeta;
|
||||
return;
|
||||
}
|
||||
|
||||
const htmlTag = unwrap(document.querySelector("head"));
|
||||
_viewPortMeta = document.createElement("meta");
|
||||
_viewPortMeta.name = 'viewport';
|
||||
_viewPortMeta.content = 'width=500';
|
||||
htmlTag.appendChild(_viewPortMeta);
|
||||
viewportMeta = _viewPortMeta;
|
||||
}
|
||||
|
||||
function adjustViewportWidth() {
|
||||
const screenMode = (localStorage.getItem(keyScreenMode) as ScreenModeType)??'auto';
|
||||
if(screenMode != oldMode){
|
||||
oldMode = screenMode;
|
||||
deviceWidth = window.screen.availWidth;
|
||||
|
||||
if(screenMode == '500px'){
|
||||
viewportMeta.content = 'width=500';
|
||||
return;
|
||||
}
|
||||
|
||||
if(screenMode == '1000px'){
|
||||
viewportMeta.content = 'width=1000';
|
||||
return;
|
||||
}
|
||||
|
||||
if(screenMode == 'auto'){
|
||||
deviceWidth = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceWidth == window.screen.availWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(oldMode != 'auto'){
|
||||
oldMode = 'auto';
|
||||
adjustViewportWidth();
|
||||
return;
|
||||
}
|
||||
deviceWidth = window.screen.availWidth;
|
||||
const innerHeight = window.innerHeight;
|
||||
const selectorHeight = targetHeight;
|
||||
|
||||
if (deviceWidth < 500) {
|
||||
viewportMeta.content = 'width=500';
|
||||
return;
|
||||
}
|
||||
|
||||
if (innerHeight < selectorHeight) {
|
||||
const maybeNextWidth = deviceWidth / innerHeight * selectorHeight;
|
||||
if (maybeNextWidth >= 700) {
|
||||
viewportMeta.content = 'width=1000';
|
||||
}
|
||||
else {
|
||||
viewportMeta.content = `height=${Math.ceil(selectorHeight)}`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(deviceWidth >= 700){
|
||||
viewportMeta.content = 'width=1000';
|
||||
}
|
||||
else{
|
||||
viewportMeta.content = 'width=device-width, initial-scale=1';
|
||||
}
|
||||
}
|
||||
|
||||
htmlReady(() => {
|
||||
init();
|
||||
adjustViewportWidth();
|
||||
window.addEventListener('scroll', adjustViewportWidth, true);
|
||||
window.addEventListener('orientationchange', adjustViewportWidth, true);
|
||||
document.addEventListener('tryChangeScreenMode', adjustViewportWidth, false);
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { unwrap } from "@util/unwrap";
|
||||
|
||||
export function autoResizeTextarea(e: Event): void {
|
||||
const el = unwrap(e.target) as HTMLInputElement;
|
||||
el.style.height = 'auto';
|
||||
el.style.height = `${el.scrollHeight + 1}px`;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { combineObject } from "../common_legacy";
|
||||
import { combineObject } from "./combineObject";
|
||||
|
||||
|
||||
export function combineArray<K extends string, V>(array: V[][], columnList: K[]): Record<K, V>[] {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export function combineObject<K extends string, V>(item: V[], columnList: K[]): Record<K, V> {
|
||||
const newItem: Record<string, V> = {};
|
||||
for (const columnIdx in columnList) {
|
||||
const columnName = columnList[columnIdx];
|
||||
newItem[columnName] = item[columnIdx];
|
||||
}
|
||||
return newItem;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { isArray, isString, isNumber, isBoolean } from "lodash-es";
|
||||
|
||||
export function convertFormData(values: Record<string, null | number[] | string[] | boolean[] | number | string | boolean>): FormData {
|
||||
const formData = new FormData();
|
||||
|
||||
const simpleConv = (v: unknown, key: string): string => {
|
||||
if (isString(v)) {
|
||||
return v;
|
||||
}
|
||||
if (isNumber(v)) {
|
||||
return v.toString();
|
||||
}
|
||||
if (isBoolean(v)) {
|
||||
return v ? 'true' : 'false';
|
||||
}
|
||||
if (v === null) {
|
||||
return '';
|
||||
}
|
||||
throw new TypeError(`지원하지 않는 formData Type: ${key}`);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(values)) {
|
||||
if (isArray(value)) {
|
||||
const arrKey = `${key}[]`;
|
||||
for (const subValue of value) {
|
||||
formData.append(arrKey, simpleConv(subValue, key));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
formData.append(key, simpleConv(value, key));
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IDItem } from '@/defs';
|
||||
import type { IDItem } from './defs';
|
||||
|
||||
export function convertIDArray<T>(array: Iterable<T>): IDItem<T>[] {
|
||||
const result: IDItem<T>[] = [];
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export function insertCustomCSS(key = 'sam_customCSS'){
|
||||
const customCSS = localStorage.getItem(key);
|
||||
if (customCSS) {
|
||||
const css = document.createElement('style');
|
||||
css.innerHTML = customCSS;
|
||||
console.log(css);
|
||||
document.getElementsByTagName('head')[0].appendChild(css);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export declare type ValuesOf<T> = T[keyof T];
|
||||
|
||||
export type IDItem<T> = {
|
||||
id: T;
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
export function exportWindow(obj:unknown, objName: string, targetWindow?: unknown):void{
|
||||
const target:unknown = targetWindow ?? window;
|
||||
(target as {[v: string]: unknown})[objName] = obj;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { unwrap } from "./unwrap";
|
||||
|
||||
//https://stackoverflow.com/questions/36280818/how-to-convert-file-to-base64-in-javascript
|
||||
export function getBase64FromFileObject(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
let encoded = unwrap(reader.result).toString().replace(/^data:(.*,)?/, '');
|
||||
if ((encoded.length % 4) > 0) {
|
||||
encoded += '='.repeat(4 - (encoded.length % 4));
|
||||
}
|
||||
resolve(encoded);
|
||||
};
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { formatTime } from '@util/formatTime';
|
||||
import { formatTime } from './/formatTime';
|
||||
export function getDateTimeNow(withFraction = false): string {
|
||||
return formatTime(new Date(), withFraction);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export function getIconPath(imgsvr: boolean | 1 | 0, picture: string): string {
|
||||
// ../d_shared/common_path.js 필요
|
||||
if (!imgsvr) {
|
||||
return `${window.pathConfig.sharedIcon}/${picture}`;
|
||||
} else {
|
||||
return `${window.pathConfig.root}/d_pic/${picture}`;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function htmlReady(fn: () => unknown): void {
|
||||
if (document.readyState != 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { BootstrapVueNext, BToastPlugin } from "bootstrap-vue-next";
|
||||
import { Directives } from "bootstrap-vue-next";
|
||||
import type { App } from "vue";
|
||||
import Multiselect from "vue-multiselect";
|
||||
|
||||
export function installVue3Components<T>(app: App<T>): App<T> {
|
||||
|
||||
app.use(BootstrapVueNext).use(BToastPlugin).component('v-multiselect', Multiselect);
|
||||
for (const [name, directive] of Object.entries(Directives)) {
|
||||
//BVN 0.7.3 directive 이름 hack
|
||||
if (!name.startsWith('v')) {
|
||||
continue;
|
||||
}
|
||||
app.directive(name.substring(1), directive);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { unwrap } from "@util/unwrap";
|
||||
import { hexToRgb } from "@util/hexToRgb";
|
||||
import { unwrap } from ".//unwrap";
|
||||
import { hexToRgb } from ".//hexToRgb";
|
||||
|
||||
export function isBrightColor(color: string): boolean {
|
||||
const cv = unwrap(hexToRgb(color));
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import Schema, { type Rule, type Values } from "async-validator";
|
||||
import { isArray } from "lodash-es";
|
||||
import { mergeKVArray } from "@util/mergeKVArray";
|
||||
import $ from 'jquery';
|
||||
|
||||
type Option = {
|
||||
preParse?: ($target?: JQuery<HTMLElement>)=>void,
|
||||
postParse?: (values:Record<string, string | string[]>, $target?: JQuery<HTMLElement>)=>[Record<string, string | string[]>, Map<string, string>]
|
||||
}
|
||||
|
||||
type DefaultFormDataType = Record<string, null|number[]|string[]|boolean[]|number|string|boolean>;
|
||||
|
||||
export type NamedRules<T extends Record<string, unknown>> = {
|
||||
[v in keyof T]: Rule
|
||||
}
|
||||
|
||||
export class JQValidateForm<TypedValue extends Values = DefaultFormDataType> {
|
||||
|
||||
public readonly validator: Schema;
|
||||
public readonly inputs: JQuery<HTMLElement>;
|
||||
constructor(public readonly target: JQuery<HTMLElement>, public readonly rule: NamedRules<TypedValue>, public readonly option?:Option) {
|
||||
this.validator = new Schema(rule);
|
||||
this.inputs = target.find(':input');
|
||||
}
|
||||
|
||||
public clearErrMsg():void {
|
||||
this.inputs.removeClass('is-invalid');
|
||||
this.inputs.removeClass('is-valid');
|
||||
this.target.find('.invalid-feedback').detach();
|
||||
}
|
||||
|
||||
public installChangeHandler():this{
|
||||
this.inputs.on('change', ()=>{
|
||||
void this.validate();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public async validate(): Promise<undefined | TypedValue> {
|
||||
if(this.option?.preParse !== undefined){
|
||||
this.option.preParse(this.target);
|
||||
}
|
||||
let rawValues = mergeKVArray(this.inputs.serializeArray());
|
||||
let optMap: Map<string, string>;
|
||||
if(this.option?.postParse !== undefined){
|
||||
[rawValues, optMap] = this.option.postParse(rawValues, this.target);
|
||||
}
|
||||
else{
|
||||
optMap = new Map();
|
||||
}
|
||||
console.log(rawValues);
|
||||
|
||||
const validateResult = await this.validator.validate(rawValues).catch(({fields }) => {
|
||||
if(fields === undefined){
|
||||
console.error('validator 에러, 조건 검사 구문을 확인하세요.');
|
||||
return;
|
||||
}
|
||||
this.clearErrMsg();
|
||||
for(const rawKey of Object.keys(fields)){
|
||||
let $item: JQuery<HTMLElement>;
|
||||
const key = rawKey.split('.')[0];
|
||||
console.log(`ErrorType: ${key}:${rawValues[key]}`);
|
||||
const errMsg = fields[rawKey][0].message;
|
||||
|
||||
if(optMap.has(key)){
|
||||
$item = this.target.find(optMap.get(key) as string);
|
||||
}
|
||||
else if(isArray(rawValues[key])){
|
||||
$item = this.target.find(`:input[name='${key}[]']`);
|
||||
}
|
||||
else{
|
||||
$item = this.target.find(`:input[name='${key}']`);
|
||||
}
|
||||
|
||||
if($item.length == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
const $error = $(`<span>${errMsg}</span>`);
|
||||
|
||||
$error.addClass( "invalid-feedback" );
|
||||
|
||||
if ("radio" == $item.prop( "type" )) {
|
||||
const $target = $item.parents( ".btn-group" );
|
||||
$error.insertAfter( $target );
|
||||
$target.addClass('is-invalid');
|
||||
}
|
||||
else if ("checkbox" == $item.prop( "type" )) {
|
||||
let $target = $item.parent( "label" );
|
||||
if($target.parent(".btn-group").length){
|
||||
$target = $target.parent('.btn-group');
|
||||
}
|
||||
$error.insertAfter( $target );
|
||||
$target.addClass('is-invalid');
|
||||
} else {
|
||||
$error.insertAfter( $item );
|
||||
$item.addClass('is-invalid');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.inputs.each(function(){
|
||||
const name = (this as HTMLInputElement).name;
|
||||
if(name in fields){
|
||||
return;
|
||||
}
|
||||
this.classList.add('is-valid');
|
||||
});
|
||||
return undefined;
|
||||
});
|
||||
if(validateResult === undefined){
|
||||
return undefined;
|
||||
}
|
||||
this.clearErrMsg();
|
||||
this.inputs.addClass('is-valid');
|
||||
return validateResult as TypedValue;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mb_strwidth } from "@util/mb_strwidth";
|
||||
import { mb_strwidth } from ".//mb_strwidth";
|
||||
|
||||
/**
|
||||
* mb_strimwidth
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import type { ValuesOf } from "@/defs";
|
||||
import type { ValuesOf } from "./defs";
|
||||
import { zip } from "lodash-es";
|
||||
|
||||
export function merge2DArrToObjectArr<T extends Record<string, unknown>>(column: (keyof T)[], list: ValuesOf<T>[][]): T[]{
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/** @deprecated */
|
||||
export function scrollHardTo(elementId: string): void {
|
||||
const element = document.getElementById(elementId);
|
||||
if(!element){
|
||||
return;
|
||||
}
|
||||
element.scrollIntoView({
|
||||
behavior: 'auto',
|
||||
});
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export function scrollToSelector(selector: string): void {
|
||||
const element = document.querySelector(selector);
|
||||
if(!element){
|
||||
return;
|
||||
}
|
||||
element.scrollIntoView({
|
||||
behavior: 'auto',
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { webcrypto } from "node:crypto";
|
||||
|
||||
const subtle = webcrypto.subtle;
|
||||
|
||||
export async function sha256(msg: ArrayBuffer): Promise<ArrayBuffer>{
|
||||
return await subtle.digest('SHA-256', msg);
|
||||
}
|
||||
|
||||
export async function sha512(msg: ArrayBuffer): Promise<ArrayBuffer>{
|
||||
return await subtle.digest('SHA-512', msg);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Nullable } from "@util/Nullable";
|
||||
import { NotNullExpected } from "@util/NotNullExpected";
|
||||
import type { Nullable } from './Nullable';
|
||||
import { NotNullExpected } from "./NotNullExpected";
|
||||
|
||||
export function unwrap<T>(result: Nullable<T>): T {
|
||||
if (result === null || result === undefined) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Nullable } from "@util/Nullable";
|
||||
import { NotNullExpected } from "@util/NotNullExpected";
|
||||
import type { Nullable } from ".//Nullable";
|
||||
import { NotNullExpected } from ".//NotNullExpected";
|
||||
|
||||
|
||||
export function unwrap_any<T>(result: Nullable<unknown>): T {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Nullable } from "@util/Nullable";
|
||||
import type { Nullable } from ".//Nullable";
|
||||
|
||||
type ErrType<T> = { new(msg?: string): T }
|
||||
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"paths": {
|
||||
"@util/*": ["server/util/*"],
|
||||
"@server/*": ["server/*"],
|
||||
"@util/*": ["./server/util/*"],
|
||||
"@server/*": ["./server/*"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"paths": {
|
||||
"@util/*": ["./server/util/*"],
|
||||
"@server/*": ["./server/*"],
|
||||
"@util/*": ["./util/*"],
|
||||
"@server/*": ["./*"],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user