js2ts: chiefCenter

- common: unwrap_any
- jQuery export 수정
- axios로 변경
- moment -> luxon
This commit is contained in:
2021-08-16 01:06:17 +09:00
parent aac6632f4b
commit bc218b73e1
23 changed files with 376 additions and 214 deletions
-1
View File
@@ -19,7 +19,6 @@ $generalObj = General::createGeneralObjFromDB($session->generalID);
<?=WebUtil::printJS('js/vendors.js')?>
<?=WebUtil::printJS('js/common.js')?>
<?=WebUtil::printJS('../e_lib/jquery.redirect.js')?>
<?=WebUtil::printJS('../e_lib/moment.min.js')?>
<?=WebUtil::printJS('js/chiefCenter.js')?>
<?=WebUtil::printCSS('../e_lib/bootstrap.min.css')?>
<?=WebUtil::printCSS('../d_shared/common.css')?>
+2 -185
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+39
View File
@@ -0,0 +1,39 @@
type JqueryRedirectOpts = {
url: string,
values?: Record<string, string | number | string[] | boolean>,
method?: 'GET' | 'POST',
target?: string,
traditional?: boolean
redirectTop?: boolean,
}
interface JQueryStatic {
/**
* jQuery Redirect
* @param {string} url - Url of the redirection
* @param {Object} values - (optional) An object with the data to send. If not present will look for values as QueryString in the target url.
* @param {string} method - (optional) The HTTP verb can be GET or POST (defaults to POST)
* @param {string} target - (optional) The target of the form. If you set "_blank" will open the url in a new window.
* @param {boolean} traditional - (optional) This provides the same function as jquery's ajax function. The brackets are omitted on the field name if its an array. This allows arrays to work with MVC.net among others.
* @param {boolean} redirectTop - (optional) If its called from a iframe, force to navigate the top window.
*/
redirect(
url: string,
values?: Record<string, string | number | string[] | boolean>,
method?: 'GET' | 'POST',
target?: string, traditional?:
boolean, redirectTop?: boolean
): void;
/**
* jQuery Redirect
* @param {string} opts - Options object
* @param {string} opts.url - Url of the redirection
* @param {Object} opts.values - (optional) An object with the data to send. If not present will look for values as QueryString in the target url.
* @param {string} opts.method - (optional) The HTTP verb can be GET or POST (defaults to POST)
* @param {string} opts.target - (optional) The target of the form. "_blank" will open the url in a new window.
* @param {boolean} opts.traditional - (optional) This provides the same function as jquery's ajax function. The brackets are omitted on the field name if its an array. This allows arrays to work with MVC.net among others.
* @param {boolean} opts.redirectTop - (optional) If its called from a iframe, force to navigate the top window.
*/
redirect(opt: JqueryRedirectOpts): void;
}
+256
View File
@@ -0,0 +1,256 @@
import axios from 'axios';
import $ from 'jquery';
import { range } from 'lodash';
import { DateTime } from 'luxon';
import { errUnknown, getNpcColor } from './common_legacy';
import { InvalidResponse } from './defs';
import { unwrap } from './util';
import { convertFormData } from './util/convertFormData';
import { unwrap_any } from "./util/unwrap_any";
declare const maxChiefTurn: number;
type TurnDOMObj = {
turnTime: JQuery<HTMLElement>,
turnPad: JQuery<HTMLElement>,
turnText: JQuery<HTMLElement>
};
type TableObj = {
btns: JQuery<HTMLElement>;
[key: number]: {
officerLevelText: JQuery<HTMLElement>,
name: JQuery<HTMLElement>,
turn: TurnDOMObj[],
}
}
type ChiefResponse = InvalidResponse | {
result: true,
date: string,
nationTurnBrief: {
name:string|null,
turnTime:string|null,
officerLevelText:string,
npcType:number,
turn:string
}[],
isChief: boolean,
turnTerm: number
}
let filledChiefList: Record<number, boolean> = {};
let chiefTableObj: TableObj = undefined as unknown as TableObj;//TODO: 매우 지저분하다. class 기반으로 고치던가 할 것
/*
function clearTable() {
$('.chiefLevelText').html('-');
$('.chiefTurnTime, .chiefTurnText, .chiefName').html('&nbsp;');
}
*/
function genChiefTableObj(): TableObj {
const objTable: TableObj = {
btns: $('#turnPush,#turnPull,#setCommand')
};
for (const chiefIdx of range(5, 13)) {
const $plate = $('#chief_{0}'.format(chiefIdx));
const $officerLevelText = $plate.find('.chiefLevelText');
const $name = $plate.find('.chiefName');
const turn: TurnDOMObj[] = [];
for (const turnIdx of range(maxChiefTurn)) {
const $turn = $plate.find('.turn{0}'.format(turnIdx));
const $turnTime = $turn.find('.chiefTurnTime');
const $turnPad = $turn.find('.chiefTurnPad');
const $turnText = $turn.find('.chiefTurnText');
turn.push({ turnTime: $turnTime, turnPad: $turnPad, turnText: $turnText });
}
objTable[chiefIdx] = {
officerLevelText: $officerLevelText,
name: $name,
turn: turn
};
}
return objTable;
}
function clearChief(chiefIdx: number): void {
const $plate = $('#chief_{0}'.format(chiefIdx));
$plate.find('.chiefLevelText').html('-');
$plate.find('.chiefTurnTime, .chiefTurnText, .chiefName').html('&nbsp;');
}
async function reloadTable() {
const data: ChiefResponse = await (async () => {
try {
const response = await axios({
url: 'j_getChiefTurn.php',
responseType: 'json'
});
return response.data;
}
catch (e) {
console.error(e);
errUnknown();
}
})();
if (!data.result) {
alert(data.reason);
return;
}
const turnTerm = data.turnTerm;
const tmpFilledChiefList:Record<number, boolean> = {};
if (data.isChief) {
chiefTableObj.btns.css('visibility', 'visible');
}
else {
chiefTableObj.btns.css('visibility', 'hidden');
}
$.each(data.nationTurnBrief, function (chiefIdx, chiefInfo) {
tmpFilledChiefList[chiefIdx] = true;
filledChiefList[chiefIdx] = true;
const plateObj = chiefTableObj[chiefIdx];
if (chiefInfo.name) {
const $name = $('<span>{0}</span>'.format(chiefInfo.name));
const nameColor = getNpcColor(chiefInfo.npcType);
if (nameColor) {
$name.css('color', nameColor);
}
plateObj.name.empty().append($name);
}
else {
plateObj.name.html('');
}
plateObj.officerLevelText.text(chiefInfo.officerLevelText);
let turnTimeObj: DateTime|undefined;
if (chiefInfo.turnTime) {
turnTimeObj = DateTime.fromSQL(chiefInfo.turnTime);
}
const turnList = plateObj.turn;
$.each(chiefInfo.turn, function (turnIdx, turnText) {
if (turnTimeObj) {
turnList[turnIdx].turnTime.text(turnTimeObj.toFormat('HH:mm'));
}
else {
turnList[turnIdx].turnTime.text('');
}
turnList[turnIdx].turnText.html(turnText).css('font-size', '13px');
const oWidth = unwrap(turnList[turnIdx].turnPad.innerWidth());
const iWidth = unwrap(turnList[turnIdx].turnText.outerWidth());
if (iWidth > oWidth * 0.95) {
const newFontSize = 13 * oWidth / iWidth * 0.9;
turnList[turnIdx].turnText.css('font-size', '{0}px'.format(newFontSize));
}
if (turnTimeObj) {
turnTimeObj = turnTimeObj.plus({'minutes': turnTerm});
}
});
});
for (const idx of range(5, 13)) {
if (idx in tmpFilledChiefList) {
continue;
}
if (idx in filledChiefList) {
clearChief(idx);
}
}
filledChiefList = tmpFilledChiefList;
}
async function reserveTurn(turnList: number[], command: string) {
console.log(turnList, command);
try{
const response = await axios({
url: 'j_set_chief_command.php',
responseType:'json',
method: 'post',
data: convertFormData({
turnList,
action: command
})
});
const data: InvalidResponse = response.data;
if (!data.result) {
alert(data.reason);
}
await reloadTable();
}
catch(e){
console.error(e);
errUnknown();
}
}
async function pushTurn(turnCnt: number) {
try {
const response = await axios({
url: 'j_chief_turn.php',
responseType: 'json',
method: 'post',
data: convertFormData({
amount: turnCnt
})
});
const data = response.data;
if (!data.result) {
alert(data.reason);
}
await reloadTable();
}
catch (e) {
console.error(e);
errUnknown();
}
}
jQuery(function ($) {
chiefTableObj = genChiefTableObj();
void reloadTable();
$('#reloadTable').on('click', reloadTable);
$('#setCommand').on('click', function () {
const turnList = unwrap_any<string[]>($('#chiefTurnSelector').val()).map(function (v) { return parseInt(v); });
const $command = $('#chiefCommandList option:selected');
if ($command.data('reqarg')) {
$.redirect(
"b_processing.php", {
command: unwrap($command.val()),
turnList: turnList.join('_'),
is_chief: true
}, "GET");
}
else {
void reserveTurn(turnList, unwrap_any<string>($command.val()));
}
return false;
});
$('#turnPush').on('click', function () {
void pushTurn(1);
return false;
});
$('#turnPull').on('click', function () {
void pushTurn(-1);
return false;
});
})
+2 -5
View File
@@ -1,7 +1,7 @@
import { activeFlip, escapeHtml, isInt, mb_strwidth, mb_strimwidth, convertDictById, convertSet, hexToRgb, isBrightColor, convColorValue, numberWithCommas, linkifyStrWithOpt, TemplateEngine, getIconPath, combineObject, combineArray, activeFlipItem, errUnknown, errUnknownToast, quickReject, nl2br, getNpcColor, initTooltip } from "./common_legacy";
import jQuery from "jquery";
window.jQuery = jQuery;
window.$ = jQuery;
jQuery(function ($) {
initTooltip();
@@ -68,9 +68,6 @@ declare global {
}
}
window.$ = jQuery;
window.jQuery = jQuery;
window.escapeHtml = escapeHtml;
window.isInt = isInt;
window.mb_strwidth = mb_strwidth;
+8 -3
View File
@@ -3,6 +3,11 @@ import { unwrap } from "./util";
import jQuery from "jquery";
import 'bootstrap';
import axios from "axios";
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
//TODO: X-Requested-With 믿지 말자.
/**
* <>& 등을 html에서도 그대로 보이도록 escape주는 함수
* @see https://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
@@ -122,12 +127,12 @@ export function convertSet<K extends string | number>(arr: ArrayLike<K>): Record
declare global {
interface String {
format(...args: string[]): string;
format(...args: (string | number)[]): string;
}
}
String.prototype.format = function (...args: string[]) {
String.prototype.format = function (...args: (string | number)[]) {
return this.replace(/{(\d+)}/g, function (match, number) {
return (typeof args[number] != 'undefined') ? args[number] : match;
return (typeof args[number] != 'undefined') ? args[number].toString() : match;
});
};
+4
View File
@@ -0,0 +1,4 @@
export type InvalidResponse = {
result: false;
reason: string;
}
+5 -5
View File
@@ -6,7 +6,7 @@ import "../scss/troop.scss";
jQuery(function($){
//btnJoinTroop, btnLeaveTroop, btnKickTroop, btnCreateTroop, btnChangeTroopName
$('#btnLeaveTroop').click(function(e){
$('#btnLeaveTroop').click(function(){
if(!confirm("정말 부대를 탈퇴하시겠습니까?")){
return false;
}
@@ -29,7 +29,7 @@ jQuery(function($){
return false;
});
$('#btnCreateTroop').click(function(e){
$('#btnCreateTroop').click(function(){
$.post({
url:'j_troop.php',
dataType:'json',
@@ -50,7 +50,7 @@ jQuery(function($){
return false;
});
$('#btnChangeTroopName').click(function(e){
$('#btnChangeTroopName').click(function(){
$.post({
url:'j_troop.php',
dataType:'json',
@@ -71,7 +71,7 @@ jQuery(function($){
return false;
});
$('#btnKickTroop').click(function(e){
$('#btnKickTroop').click(function(){
$.post({
url:'j_troop.php',
dataType:'json',
@@ -92,7 +92,7 @@ jQuery(function($){
return false;
});
$('#btnJoinTroop').click(function(e){
$('#btnJoinTroop').click(function(){
$.post({
url:'j_troop.php',
dataType:'json',
+1 -1
View File
@@ -1,5 +1,5 @@
type ErrType<T> = { new(msg?: string): T }
type Nullable<T> = T | null | undefined
export type Nullable<T> = T | null | undefined
export class RuntimeError extends Error {
public name = 'RuntimeError';
+32
View File
@@ -0,0 +1,32 @@
import { isArray, isString, isNumber, isBoolean } from "lodash";
export function convertFormData(values: Record<string, number[]|string[]|boolean[]|number|string|boolean>): FormData{
const formData = new FormData();
const simpleConv = (v: unknown):string=>{
if(isString(v)){
return v;
}
if(isNumber(v)){
return v.toString();
}
if(isBoolean(v)){
return v?'true':'false';
}
throw new TypeError('지원하지 않는 formData Type');
}
for(const [key, value] of Object.entries(values)){
if(isArray(value)){
const arrKey = `${key}[]`;
for(const subValue of value){
formData.append(arrKey, simpleConv(subValue));
}
continue;
}
formData.append(key, simpleConv(value));
}
return formData;
}
+9
View File
@@ -0,0 +1,9 @@
import { Nullable, NotNullExpected } from "../util";
export function unwrap_any<T>(result: Nullable<unknown>): T {
if (result === null || result === undefined) {
throw new NotNullExpected();
}
return result as T;
}
+4 -2
View File
@@ -16,13 +16,14 @@
"author": "Hide_D",
"license": "MIT",
"dependencies": {
"@types/luxon": "^2.0.0",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"core-js": "^3.16.1",
"jquery": "^3.6.0",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"vue": "^3.2.2",
"jquery": "^3.6.0"
"vue": "^3.2.2"
},
"devDependencies": {
"@babel/cli": "^7.14.8",
@@ -45,6 +46,7 @@
"sass": "^1.37.5",
"sass-loader": "^12.1.0",
"style-loader": "^3.2.1",
"ts-loader": "^9.2.5",
"typescript": "^4.3.5",
"vue-loader": "^16.5.0",
"webpack": "^5.49.0",
+2 -2
View File
@@ -12,14 +12,14 @@
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "../js", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+3 -2
View File
@@ -9,6 +9,7 @@ module.exports = [
extensions: [".js", ".ts", ".tsx"]
},
entry: {
chiefCenter: './hwe/ts/chiefCenter.ts',
inheritPoint: './hwe/ts/inheritPoint.ts',
common: './hwe/ts/common_deprecated.ts',
troop: './hwe/ts/troop.ts',
@@ -34,7 +35,7 @@ module.exports = [
rules: [{
test: /\.(ts|tsx)$/i,
exclude: /(node_modules)/,
use: {
use: [{
loader: 'babel-loader',
options: {
presets: [
@@ -47,7 +48,7 @@ module.exports = [
'@babel/preset-typescript'
]
}
}
}, 'ts-loader']
}, {
test: /\.vue$/i,
exclude: /(node_modules)/,