feat: jqValidateForm에 Values Type 지정

This commit is contained in:
2021-08-26 21:10:02 +09:00
parent 11511316d1
commit d7b1db0081
11 changed files with 122 additions and 199 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
import { activeFlip, mb_strwidth, isBrightColor, getIconPath, errUnknown, errUnknownToast, quickReject, initTooltip } from "./common_legacy";
import { activeFlip, isBrightColor, getIconPath, errUnknown, errUnknownToast, quickReject, initTooltip } from "./common_legacy";
import { mb_strwidth } from "./util/mb_strwidth";
import { TemplateEngine } from "./util/TemplateEngine";
import { escapeHtml } from "./legacy/escapeHtml";
import { nl2br } from "./util/nl2br";
-60
View File
@@ -8,66 +8,6 @@ import 'bootstrap';
import axios from "axios";
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
//TODO: X-Requested-With 믿지 말자.
//https://gist.github.com/demouth/3217440
/**
* mb_strwidth
* @see http://php.net/manual/function.mb-strwidth.php
*/
export function mb_strwidth(str: string): number {
const l = str.length;
let length = 0;
for (let i = 0; i < l; i++) {
const c = str.charCodeAt(i);
if (0x0000 <= c && c <= 0x0019) {
length += 0;
} else if (0x0020 <= c && c <= 0x1FFF) {
length += 1;
} else if (0x2000 <= c && c <= 0xFF60) {
length += 2;
} else if (0xFF61 <= c && c <= 0xFF9F) {
length += 1;
} else if (0xFFA0 <= c) {
length += 2;
}
}
return length;
}
/**
* mb_strimwidth
* @param String
* @param int
* @param int
* @param String
* @return String
* @see http://www.php.net/manual/function.mb-strimwidth.php
*/
export function mb_strimwidth(str: string, start: number, width: number, trimmarker = ''): string {
const trimmakerWidth = mb_strwidth(trimmarker);
const l = str.length;
let trimmedLength = 0;
const trimmedStr: string[] = [];
for (let i = start; i < l; i++) {
const c = str.charAt(i);
const charWidth = mb_strwidth(c);
const next = str.charAt(i + 1);
const nextWidth = mb_strwidth(next);
trimmedLength += charWidth;
trimmedStr.push(c);
if (trimmedLength + trimmakerWidth + nextWidth > width) {
trimmedStr.push(trimmarker);
break;
}
}
return trimmedStr.join('');
}
/**
* object의 array를 id를 key로 삼는 object로 재 변환
*/
+17 -3
View File
@@ -5,8 +5,7 @@ import 'bootstrap';
import { setAxiosXMLHttpRequest } from "./util/setAxiosXMLHttpRequest";
import axios from "axios";
import { InvalidResponse } from "./defs";
import { Rules } from "async-validator";
import { JQValidateForm } from "./util/jqValidateForm";
import { JQValidateForm, NamedRules } from "./util/jqValidateForm";
import { convertFormData } from "./util/convertFormData";
type ResponseScenarioItem = {
@@ -130,7 +129,22 @@ function scenarioPreview() {
});
}
const descriptor: Rules = {
type InstallFormType = {
turnterm: number,
sync: 1|0,
scenario: number,
fiction: number,
extend: number,
npcmode: number,
show_img_level: number,
tournament_trig: number,
join_mode: number,
autorun_user: string[],
autorun_user_minutes: number,
reserve_open?: string,
pre_reserve_open?: string,
}
const descriptor: NamedRules<InstallFormType> = {
turnterm: {
required: true,
type: "enum",
+10 -3
View File
@@ -1,8 +1,7 @@
import axios from 'axios';
import jQuery from 'jquery';
import { Rules } from 'async-validator';
import { JQValidateForm } from './util/jqValidateForm';
import { JQValidateForm, NamedRules } from './util/jqValidateForm';
import { convertFormData } from './util/convertFormData';
import { InvalidResponse } from './defs';
import { setAxiosXMLHttpRequest } from './util/setAxiosXMLHttpRequest';
@@ -10,7 +9,15 @@ import { setAxiosXMLHttpRequest } from './util/setAxiosXMLHttpRequest';
jQuery(async function ($) {
setAxiosXMLHttpRequest();
const descriptor: Rules = {
type FormValue = {
full_reset: string,
db_host: string,
db_port: number,
db_id: string,
db_pw: string,
db_name: string,
}
const descriptor: NamedRules<FormValue> = {
full_reset: {
required: true,
},
+4 -4
View File
@@ -3,7 +3,7 @@ import { isArray, isString, isNumber, isBoolean } from "lodash";
export function convertFormData(values: Record<string, null|number[]|string[]|boolean[]|number|string|boolean>): FormData{
const formData = new FormData();
const simpleConv = (v: unknown):string=>{
const simpleConv = (v: unknown, key: string):string=>{
if(isString(v)){
return v;
}
@@ -16,19 +16,19 @@ export function convertFormData(values: Record<string, null|number[]|string[]|bo
if(v===null){
return '';
}
throw new TypeError('지원하지 않는 formData Type');
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));
formData.append(arrKey, simpleConv(subValue, key));
}
continue;
}
formData.append(key, simpleConv(value));
formData.append(key, simpleConv(value, key));
}
return formData;
+12 -5
View File
@@ -1,4 +1,4 @@
import Schema, { Rules, Values } from "async-validator";
import Schema, { Rule, Values } from "async-validator";
import { isArray } from "lodash";
import { mergeKVArray } from "./mergeKVArray";
import $ from 'jquery';
@@ -8,10 +8,17 @@ type Option = {
postParse?: (values:Record<string, string | string[]>, $target?: JQuery<HTMLElement>)=>[Record<string, string | string[]>, Map<string, string>]
}
export class JQValidateForm {
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: Rules, public readonly option?:Option) {
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');
}
@@ -29,7 +36,7 @@ export class JQValidateForm {
return this;
}
public async validate(): Promise<undefined | Values> {
public async validate(): Promise<undefined | TypedValue> {
if(this.option?.preParse !== undefined){
this.option.preParse(this.target);
}
@@ -106,6 +113,6 @@ export class JQValidateForm {
}
this.clearErrMsg();
this.inputs.addClass('is-valid');
return validateResult;
return validateResult as TypedValue;
}
}
+31
View File
@@ -0,0 +1,31 @@
import { mb_strwidth } from "./mb_strwidth";
/**
* mb_strimwidth
* @param String
* @param int
* @param int
* @param String
* @return String
* @see http://www.php.net/manual/function.mb-strimwidth.php
*/
export function mb_strimwidth(str: string, start: number, width: number, trimmarker = ''): string {
const trimmakerWidth = mb_strwidth(trimmarker);
const l = str.length;
let trimmedLength = 0;
const trimmedStr: string[] = [];
for (let i = start; i < l; i++) {
const c = str.charAt(i);
const charWidth = mb_strwidth(c);
const next = str.charAt(i + 1);
const nextWidth = mb_strwidth(next);
trimmedLength += charWidth;
trimmedStr.push(c);
if (trimmedLength + trimmakerWidth + nextWidth > width) {
trimmedStr.push(trimmarker);
break;
}
}
return trimmedStr.join('');
}
+25
View File
@@ -0,0 +1,25 @@
//TODO: X-Requested-With 믿지 말자.
//https://gist.github.com/demouth/3217440
/**
* mb_strwidth
* @see http://php.net/manual/function.mb-strwidth.php
*/
export function mb_strwidth(str: string): number {
const l = str.length;
let length = 0;
for (let i = 0; i < l; i++) {
const c = str.charCodeAt(i);
if (0x0000 <= c && c <= 0x0019) {
length += 0;
} else if (0x0020 <= c && c <= 0x1FFF) {
length += 1;
} else if (0x2000 <= c && c <= 0xFF60) {
length += 2;
} else if (0xFF61 <= c && c <= 0xFF9F) {
length += 1;
} else if (0xFFA0 <= c) {
length += 2;
}
}
return length;
}
-113
View File
@@ -1,113 +0,0 @@
$(document).ready(function () {
$("#main_form").validate({
rules: {
username: {
required: true
},
password: {
required: true,
}
},
messages: {
username: {
required: "유저명을 입력해주세요"
},
password: {
required: "비밀번호를 입력해주세요"
}
},
errorElement: "div",
errorPlacement: function (error, element) {
// Add the `help-block` class to the error element
error.addClass("invalid-feedback");
if (element.prop("type") === "checkbox") {
error.insertAfter(element.parent("label"));
} else {
error.insertAfter(element);
}
},
highlight: function (element, errorClass, validClass) {
$(element).addClass("is-invalid").removeClass("is-valid");
},
unhighlight: function (element, errorClass, validClass) {
$(element).addClass("is-valid").removeClass("is-invalid");
}
});
$("#main_form").submit(function () {
var raw_password = $('#password').val();
var salt = $('#global_salt').val();
var hash_pw = sha512(salt + raw_password + salt);
$.post({
url: 'j_login.php',
dataType: 'json',
data: {
'username': $('#username').val(),
'password': hash_pw
}
}).then(function (obj) {
if (obj.result) {
window.location.href = "./";
return;
}
if (!obj.reqOTP) {
alert(obj.reason);
return;
}
var $modal = $('#modalOTP').modal();
$modal.on('shown.bs.modal', function () {
$('#otp_code').focus();
});
});
return false;
});
$('#otp_form').submit(function () {
$.post({
url: 'oauth_kakao/j_check_OTP.php',
dataType: 'json',
data: {
'otp': $('#otp_code').val(),
}
}).then(function (obj) {
if (obj.result) {
alert(obj.reason);
window.location.href = "./";
return;
}
alert(obj.reason);
if (obj.reset) {
$('#modalOTP').modal('hide')
return;
}
});
return false;
});
if (document.body.clientWidth < 700) {
var targetWidth = document.body.clientWidth * 0.9;
var scale = targetWidth / 700;
var $map = $('#running_map');
$map.find('.col').css('max-width', targetWidth);
$map.find('.card').css('width', targetWidth);
$map.find('.map-container').css({
'transform-origin': 'top left',
'transform': 'scale({0}, {0})'.format(scale),
'height': 500 * scale,
});
$map.find('.map_body').data('scale', scale);
}
});
window.fitIframe = function () {
var iframe = document.querySelector('#running_map');
iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 'px';
}
+15 -7
View File
@@ -1,13 +1,13 @@
import $ from 'jquery';
import axios from 'axios';
import { Rules } from 'async-validator';
import { JQValidateForm } from '../hwe/ts/util/jqValidateForm';
import { JQValidateForm, NamedRules } from '../hwe/ts/util/jqValidateForm';
import { convertFormData } from '../hwe/ts/util/convertFormData';
import { InvalidResponse } from '../hwe/ts/defs';
import { setAxiosXMLHttpRequest } from '../hwe/ts/util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../hwe/ts/util//unwrap_any';
import { sha512 } from 'js-sha512';
import { isString } from 'lodash';
import { mb_strwidth } from '../hwe/ts/util/mb_strwidth';
$(async function () {
setAxiosXMLHttpRequest();
@@ -15,7 +15,16 @@ $(async function () {
const terms1P = axios('../terms.1.html');
const terms2P = axios('../terms.2.html');
const descriptor: Rules = {
type JoinFormType = {
secret_agree: boolean,
secret_agree2: boolean,
third_use: boolean,
username: string,
password: string,
confirm_password: string,
nickname: string,
}
const descriptor: NamedRules<JoinFormType> = {
username: {
required: true,
min: 4,
@@ -81,8 +90,10 @@ $(async function () {
},
nickname: {
required: true,
max: 9,
asyncValidator: async (rule, value: string) => {
if (!value || !isString(value) || mb_strwidth(value) > 18) {
throw new Error(`글자 너비가 알파벳 ${18}자보다 길지 않아야합니다`);
}
const response = await axios({
url: 'j_check_dup.php',
method: 'post',
@@ -100,9 +111,6 @@ $(async function () {
options: {
messages: {
required: "유저명을 입력해주세요",
string: {
max: (v, l) => `닉네임은 ${l}자를 넘을 수 없습니다`,
}
}
}
},
+6 -3
View File
@@ -3,9 +3,8 @@ import 'popper.js';
import Popper from 'popper.js';
(window as unknown as {Popper:unknown}).Popper = Popper;
import 'bootstrap';
import { JQValidateForm } from '../hwe/ts/util/jqValidateForm';
import { JQValidateForm, NamedRules } from '../hwe/ts/util/jqValidateForm';
import axios from 'axios';
import { Rules } from 'async-validator';
import { convertFormData } from '../hwe/ts/util/convertFormData';
import { setAxiosXMLHttpRequest } from '../hwe/ts/util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../hwe/ts/util/unwrap_any';
@@ -161,7 +160,11 @@ window.postOAuthResult = postOAuthResult;
$(function ($) {
setAxiosXMLHttpRequest();
const descriptor: Rules = {
type LoginFormType = {
username: string,
password: string,
};
const descriptor: NamedRules<LoginFormType> = {
username: {
required: true,
type: 'string',