이전 util 이전

This commit is contained in:
2023-08-05 15:34:26 +00:00
parent 5ba073c125
commit 8130c731e0
37 changed files with 72 additions and 694 deletions
+1
View File
@@ -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",
+7
View File
@@ -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:
+6 -5
View File
@@ -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: [],
+4 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
export class NotNullExpected extends TypeError {
public name = 'NotNullExpected';
public override name = 'NotNullExpected';
}
-173
View File
@@ -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
View File
@@ -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>;
}
+3 -3
View File
@@ -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;
}
-115
View File
@@ -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()))
);
}
}
-41
View File
@@ -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;
}
}
-92
View File
@@ -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);
});
}
-7
View File
@@ -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 -1
View File
@@ -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>[] {
+8
View File
@@ -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;
}
-35
View File
@@ -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 -1
View File
@@ -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>[] = [];
-9
View File
@@ -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);
}
}
+5
View File
@@ -0,0 +1,5 @@
export declare type ValuesOf<T> = T[keyof T];
export type IDItem<T> = {
id: T;
};
-4
View File
@@ -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;
}
-17
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import { formatTime } from '@util/formatTime';
import { formatTime } from './/formatTime';
export function getDateTimeNow(withFraction = false): string {
return formatTime(new Date(), withFraction);
}
-8
View File
@@ -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}`;
}
}
-7
View File
@@ -1,7 +0,0 @@
export function htmlReady(fn: () => unknown): void {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
-17
View File
@@ -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;
}
+2 -2
View File
@@ -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));
-118
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import { mb_strwidth } from "@util/mb_strwidth";
import { mb_strwidth } from ".//mb_strwidth";
/**
* mb_strimwidth
+1 -1
View File
@@ -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[]{
-10
View File
@@ -1,10 +0,0 @@
/** @deprecated */
export function scrollHardTo(elementId: string): void {
const element = document.getElementById(elementId);
if(!element){
return;
}
element.scrollIntoView({
behavior: 'auto',
});
}
-9
View File
@@ -1,9 +0,0 @@
export function scrollToSelector(selector: string): void {
const element = document.querySelector(selector);
if(!element){
return;
}
element.scrollIntoView({
behavior: 'auto',
});
}
+11
View File
@@ -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);
}
+2 -2
View File
@@ -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) {
+2 -2
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import type { Nullable } from "@util/Nullable";
import type { Nullable } from ".//Nullable";
type ErrType<T> = { new(msg?: string): T }
+2 -2
View File
@@ -24,8 +24,8 @@
"strict": true,
"importHelpers": true,
"paths": {
"@util/*": ["server/util/*"],
"@server/*": ["server/*"],
"@util/*": ["./server/util/*"],
"@server/*": ["./server/*"],
}
}
}
+2 -2
View File
@@ -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": [