사용자 확정(2026-09-07): 「연암과 경암 / 발파암과 리핑암 선택은 횡단도에서 선택 안함. 사유는 향후 설계내역에서 설계자가 직접 비율로 지정하기로 함. 암반 지정과 범위 애매모호한 경우가 있어. 실무자는 그렇게 하기로 판단함. 대신 경고부분은 삭제해주고 대신 각도를 사용자가 넣을수 있게 반영.」 **왜 폐기인가** — 판정이 서려면 측점마다 암질(연암·경암)을 못 박아야 하는데, 실무에서 암반 지정·범위가 애매해 그 못 박음 자체가 틀린 전제였음. 암 비율은 **설계내역 단계에서 설계자가 비율로** 넣음. 기준이 없으니 경고도 없음. 되돌린 것 — 1e596846(암질 선택) + 973128f7(별표2 경고). 카드 경고 배지 · 판정 모듈 · config 범위표 · context 필드 · 표준 패널 암질 칸 전부 사라짐. ⚠ **남긴 것** — `cut_slope_segments`(구간별 경사)는 지우지 않음. 소단 기하가 그 위에 서 있고 구간 경사를 아는 값이라 뒤에 쓸 자리가 있음. 4e7a4bf7(사면 구간 재료를 그린 경사비로 가름 — 무릎 위 라벨 13구간 오류 수정)도 남김. 검사와 무관하게 옳은 고침임. 시험 4건 — 되살아나지 않게 막는 쪽으로 다시 씀(판정 모듈 없음 · 카드 경고 없음 · 암질 고르는 자리 없음 · **구간별 경사값은 남아 있음**). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
487 lines
18 KiB
TypeScript
487 lines
18 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_UI_Standard_Panel.ts
|
|
* 좌측 사이드 "표준 횡단면 설정" 패널 — **「표준횡단면 상세값」 한 컨테이너**(2026-09-04
|
|
* 사용자 지시). 토사·암·포장 3그룹이 같은 필드를 각각 갖던 화면을 공통 한 벌 +
|
|
* 구간별로 다른 값만 남겼다. 공통 = 노폭·노견·측구·절토/성토 경사·횡단 경사,
|
|
* 암 = 절토 경사·L형 측구, 포장 = 횡단 경사.
|
|
*
|
|
* **저장 구조는 그대로 3그룹**(`standard_cross_section`) — 화면만 합치고 저장할 때
|
|
* 공통값을 세 그룹에 펼쳐 넣는다. 백엔드·기존 프로젝트가 그대로 동작한다.
|
|
*
|
|
* 각 그룹의 노폭·노견·측구 규격·경사값을 편집한다. 기본값은 백엔드 config
|
|
* (STANDARD_CROSS_SECTION, context.standard_cross_section)에서 내려오고, 사용자가
|
|
* 편집한 값은 프론트 세션(sessionStorage)에 프로젝트 단위로 보관한다. 종·횡단
|
|
* 확정 시 이 값을 백엔드/DB에 저장하기 위해 getValues()를 노출한다.
|
|
*
|
|
* 암 그룹은 일반 측구 + L형 측구 두 세트를 보관하며, 둘 중 선택은 각 횡단면도
|
|
* 카드에서 이뤄진다(여기서는 값만 보관).
|
|
* ========================================================================== */
|
|
|
|
import {
|
|
clearState,
|
|
readState,
|
|
readStateRaw,
|
|
writeState,
|
|
writeStateRaw,
|
|
} from "../A00_Common/b_page_state";
|
|
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
|
import { createButton, createInputField, createSelectField } from "@ui/ui_template_elements";
|
|
import { buildStandardDiagram } from "./B06_Section_UI_Standard_Diagram";
|
|
import {
|
|
getCompanyStandard,
|
|
listCompanyStandards,
|
|
type StandardCrossGroup,
|
|
type StandardCrossKey,
|
|
type StandardCrossSection,
|
|
} from "./B06_Section_Api_Fetch";
|
|
|
|
function L(key: keyof typeof ui_locales): string {
|
|
return ui_locales[key][currentLanguageIndex];
|
|
}
|
|
|
|
/**
|
|
* 서버가 내려 준 표준횡단 **config 기본값**을 세션에 둔다(`sections/context` 응답).
|
|
*
|
|
* 횡단 계산이 브라우저에서 돌므로 config 수치가 프론트에도 있어야 하는데, 상수를
|
|
* 복제하면 정본이 둘이 된다. 그래서 복제 대신 **서버가 준 값을 그대로 기억**한다.
|
|
* 패널을 열지 않는 B05도 이 값으로 계산해야 두 화면 결과가 같다(2026-09-03 로컬 전환).
|
|
*/
|
|
export function rememberStandardDefaults(projectId: string, defaults: StandardCrossSection): void {
|
|
writeState("std-cross-default", defaults, projectId);
|
|
}
|
|
|
|
/** 기억해 둔 config 기본값. 아직 컨텍스트를 못 받았으면 null. */
|
|
export function readStandardDefaults(projectId: string): StandardCrossSection | null {
|
|
return readState<StandardCrossSection>("std-cross-default", projectId);
|
|
}
|
|
|
|
/**
|
|
* 암반 경계선 기본 오프셋(config `STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M`)을 기억한다.
|
|
* 저장분에 경계 오프셋이 없는 옛 암 측점을 서버와 **같은 기본값**으로 다시 계산하려면
|
|
* 브라우저에도 이 값이 있어야 한다 — 상수 복제 대신 컨텍스트 응답을 기억하는 방식이다.
|
|
*/
|
|
export function rememberRockBoundaryDefault(projectId: string, offsetM: number): void {
|
|
writeStateRaw("rock-boundary-default", String(offsetM), projectId);
|
|
}
|
|
|
|
/** 기억해 둔 암반 경계 기본 오프셋(m). 없으면 null. */
|
|
export function readRockBoundaryDefault(projectId: string): number | null {
|
|
const raw = readStateRaw("rock-boundary-default", projectId);
|
|
const parsed = raw === null ? Number.NaN : Number(raw);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
/**
|
|
* 횡단 계산에 넣을 표준단면 한 벌 — **세션 편집값이 있으면 그쪽, 없으면 config 기본값**.
|
|
* 서버 `_resolve_group(preset, standard)`의 "요청값 → config" 우선순위와 같은 뜻이다.
|
|
*/
|
|
export function effectiveStandardCross(projectId: string): StandardCrossSection | null {
|
|
return readStandardCrossSession(projectId) ?? readStandardDefaults(projectId);
|
|
}
|
|
|
|
/** 표준횡단 세션 편집값을 버린다 — B05 [초기화]가 부른다(정의처를 여기 하나로 둔다). */
|
|
export function clearStandardCrossSession(projectId: string): void {
|
|
clearState("std-cross", projectId);
|
|
}
|
|
|
|
/** config 기본값을 깊은 복사해 편집용 초기 상태로 만든다. */
|
|
function cloneDefaults(defaults: StandardCrossSection): StandardCrossSection {
|
|
return JSON.parse(JSON.stringify(defaults)) as StandardCrossSection;
|
|
}
|
|
|
|
/**
|
|
* 세션에 저장된 편집값을 읽는다. 없거나 손상 시 null.
|
|
*
|
|
* 패널 밖에서도 필요하다 — 횡단 재계산 단일 창구(`B06_Section_Cross_Refresh`)가 B05처럼
|
|
* 패널이 없는 화면에서도 **같은 표준 단면값**을 서버로 보내야 두 화면 결과가 같다.
|
|
*/
|
|
export function readStandardCrossSession(projectId: string): StandardCrossSection | null {
|
|
return readState<StandardCrossSection>("std-cross", projectId);
|
|
}
|
|
|
|
function writeSession(projectId: string, value: StandardCrossSection): void {
|
|
writeState("std-cross", value, projectId);
|
|
}
|
|
|
|
export interface StandardPanelController {
|
|
root: HTMLElement;
|
|
/** 확정 시 백엔드/DB 저장에 쓰는 현재 편집값. */
|
|
getValues: () => StandardCrossSection;
|
|
/**
|
|
* DB에 확정 저장된 값으로 복원한다. 단, 세션에 미확정 편집값이 있으면
|
|
* 그쪽을 우선하고 무시한다(진행 중 편집 보호).
|
|
*/
|
|
applyStored: (stored: StandardCrossSection) => void;
|
|
}
|
|
|
|
/** 값을 어느 그룹에 쓸 것인가. `common` 은 세 그룹에 함께 펼쳐 넣는다. */
|
|
type FieldScope = "common" | "rock" | "paved";
|
|
|
|
interface NumberFieldSpec {
|
|
label: keyof typeof ui_locales;
|
|
scope: FieldScope;
|
|
/** 화면에 보일 값을 읽는다 — 공통은 **토사 값이 기준**(2026-09-04 사용자 확정). */
|
|
get: (group: StandardCrossGroup) => number;
|
|
set: (group: StandardCrossGroup, value: number) => void;
|
|
/**
|
|
* 공통값이지만 이 그룹은 따로 값을 갖는다 — 공통을 펼칠 때 건너뛴다.
|
|
* (절토 경사는 암이, 횡단 경사는 포장이 자기 값을 쓴다)
|
|
*/
|
|
exclude?: StandardCrossKey;
|
|
}
|
|
|
|
/** 한 컨테이너에 늘어놓을 편집 필드. 순서 = 화면 표기 순서. */
|
|
const FIELD_SPECS: NumberFieldSpec[] = [
|
|
{
|
|
label: "B06_Std_Field_RoadWidth",
|
|
scope: "common",
|
|
get: (g) => g.road_width_m,
|
|
set: (g, v) => (g.road_width_m = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_ShoulderLeft",
|
|
scope: "common",
|
|
get: (g) => g.shoulder_left_m,
|
|
set: (g, v) => (g.shoulder_left_m = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_ShoulderRight",
|
|
scope: "common",
|
|
get: (g) => g.shoulder_right_m,
|
|
set: (g, v) => (g.shoulder_right_m = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_DitchTop",
|
|
scope: "common",
|
|
get: (g) => g.ditch.top_width_m,
|
|
set: (g, v) => (g.ditch.top_width_m = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_DitchBottom",
|
|
scope: "common",
|
|
get: (g) => g.ditch.bottom_width_m,
|
|
set: (g, v) => (g.ditch.bottom_width_m = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_DitchDepth",
|
|
scope: "common",
|
|
get: (g) => g.ditch.depth_m,
|
|
set: (g, v) => (g.ditch.depth_m = v),
|
|
},
|
|
{
|
|
// 암은 아래에서 자기 절토 경사를 따로 가진다 — 공통은 토사·포장 몫이다.
|
|
label: "B06_Std_Field_CutSlope",
|
|
scope: "common",
|
|
exclude: "rock",
|
|
get: (g) => g.cut_slope_ratio,
|
|
set: (g, v) => (g.cut_slope_ratio = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_FillSlope",
|
|
scope: "common",
|
|
get: (g) => g.fill_slope_ratio,
|
|
set: (g, v) => (g.fill_slope_ratio = v),
|
|
},
|
|
{
|
|
// 포장은 아래에서 자기 횡단 경사를 따로 가진다.
|
|
label: "B06_Std_Field_CrossSlopeMin",
|
|
scope: "common",
|
|
exclude: "paved",
|
|
get: (g) => g.cross_slope_pct.min,
|
|
set: (g, v) => (g.cross_slope_pct.min = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_CrossSlopeMax",
|
|
scope: "common",
|
|
exclude: "paved",
|
|
get: (g) => g.cross_slope_pct.max,
|
|
set: (g, v) => (g.cross_slope_pct.max = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_CutSlope",
|
|
scope: "rock",
|
|
get: (g) => g.cut_slope_ratio,
|
|
set: (g, v) => (g.cut_slope_ratio = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_LDitchWidth",
|
|
scope: "rock",
|
|
get: (g) => g.ditch_l_type?.width_m ?? 0,
|
|
set: (g, v) => {
|
|
g.ditch_l_type = { width_m: v, depth_m: g.ditch_l_type?.depth_m ?? 0 };
|
|
},
|
|
},
|
|
{
|
|
label: "B06_Std_Field_LDitchDepth",
|
|
scope: "rock",
|
|
get: (g) => g.ditch_l_type?.depth_m ?? 0,
|
|
set: (g, v) => {
|
|
g.ditch_l_type = { width_m: g.ditch_l_type?.width_m ?? 0, depth_m: v };
|
|
},
|
|
},
|
|
{
|
|
label: "B06_Std_Field_CrossSlopeMin",
|
|
scope: "paved",
|
|
get: (g) => g.cross_slope_pct.min,
|
|
set: (g, v) => (g.cross_slope_pct.min = v),
|
|
},
|
|
{
|
|
label: "B06_Std_Field_CrossSlopeMax",
|
|
scope: "paved",
|
|
get: (g) => g.cross_slope_pct.max,
|
|
set: (g, v) => (g.cross_slope_pct.max = v),
|
|
},
|
|
];
|
|
|
|
/** 공통 필드 하나를 그룹들에 펼쳐 넣는다(자기 값을 갖는 그룹은 건너뛴다). */
|
|
function spread(state: StandardCrossSection, spec: NumberFieldSpec, value: number): void {
|
|
for (const key of ["soil", "rock", "paved"] as StandardCrossKey[]) {
|
|
if (spec.exclude === key) continue;
|
|
const group = state[key];
|
|
if (group) spec.set(group, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 그룹마다 공통값이 다르게 저장돼 있을 수 있다(옛 프로젝트) — **토사 값을 기준**으로
|
|
* 한 벌로 맞춘다(2026-09-04 사용자 확정). 화면은 값 하나를 보이는데 저장분이 셋으로
|
|
* 갈려 있으면 어느 값이 나갔는지 알 수 없기 때문이다.
|
|
*/
|
|
function unifyCommon(state: StandardCrossSection): void {
|
|
const soil = state.soil;
|
|
if (!soil) return;
|
|
for (const spec of FIELD_SPECS) {
|
|
if (spec.scope !== "common") continue;
|
|
spread(state, spec, spec.get(soil));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 표준 횡단면 설정 패널을 만든다.
|
|
* @param projectId 세션 캐시 스코프.
|
|
* @param defaults config에서 내려온 기본값(복원 기준).
|
|
*/
|
|
export function createStandardPanel(
|
|
projectId: string,
|
|
defaults: StandardCrossSection,
|
|
onApplyAll?: () => void | Promise<void>,
|
|
): StandardPanelController {
|
|
// 브라우저 횡단 계산이 패널 없이도 config 기본값을 쓸 수 있게 먼저 기억해 둔다.
|
|
rememberStandardDefaults(projectId, defaults);
|
|
// 세션값 우선, 없으면 config 기본값. defaults는 복원 기준으로 보존한다.
|
|
const sessionValue = readStandardCrossSession(projectId);
|
|
const hadSession = sessionValue !== null;
|
|
const state: StandardCrossSection = sessionValue ?? cloneDefaults(defaults);
|
|
|
|
const root = document.createElement("div");
|
|
root.className = "b06-std";
|
|
|
|
// 변수 위치 안내 모식도(고정 도형): 각 설정값이 횡단면 어느 위치인지 표시(작업 C-5).
|
|
root.append(buildStandardDiagram());
|
|
|
|
// 그룹 재구성(리셋 시) 편의를 위해 본문 컨테이너를 분리한다.
|
|
const body = document.createElement("div");
|
|
body.className = "b06-std__body";
|
|
|
|
const persist = (): void => writeSession(projectId, state);
|
|
|
|
/** 필드 한 칸. 공통은 토사 값을 보이고, 고치면 세 그룹에 함께 펼친다. */
|
|
const buildField = (spec: NumberFieldSpec, grid: HTMLElement): void => {
|
|
const source = spec.scope === "common" ? state.soil : state[spec.scope];
|
|
if (!source) return;
|
|
const field = createInputField({
|
|
label: L(spec.label),
|
|
type: "number",
|
|
value: String(spec.get(source)),
|
|
onInput: (raw) => {
|
|
const parsed = Number(raw);
|
|
if (!Number.isFinite(parsed)) return;
|
|
if (spec.scope === "common") spread(state, spec, parsed);
|
|
else spec.set(source, parsed);
|
|
persist();
|
|
},
|
|
});
|
|
field.input.step = "0.1";
|
|
field.input.min = "0";
|
|
grid.append(field.root);
|
|
};
|
|
|
|
/** 구분선 + 구간 이름 — 아래 값들이 그 구간에서만 쓰인다는 표시. */
|
|
const buildDivider = (labelKey: keyof typeof ui_locales): HTMLElement => {
|
|
const divider = document.createElement("p");
|
|
divider.className = "b06-std__divider";
|
|
divider.textContent = L(labelKey);
|
|
return divider;
|
|
};
|
|
|
|
const buildScope = (scope: FieldScope): HTMLElement => {
|
|
const grid = document.createElement("div");
|
|
grid.className = "b06-std__grid";
|
|
for (const spec of FIELD_SPECS) {
|
|
if (spec.scope === scope) buildField(spec, grid);
|
|
}
|
|
return grid;
|
|
};
|
|
|
|
/** 「표준횡단면 상세값」 한 컨테이너 — 공통 → 암 → 포장 순, 구분선으로 나눈다. */
|
|
const buildDetails = (): HTMLElement => {
|
|
const fieldset = document.createElement("details");
|
|
fieldset.className = "b06-std__group";
|
|
fieldset.open = true;
|
|
const legend = document.createElement("summary");
|
|
legend.className = "b06-std__legend";
|
|
legend.textContent = L("B06_Std_Detail_Title");
|
|
const note = document.createElement("p");
|
|
note.className = "b06-std__note";
|
|
note.textContent = L("B06_Std_LType_Note");
|
|
fieldset.append(
|
|
legend,
|
|
buildScope("common"),
|
|
buildDivider("B06_Std_Section_RockOnly"),
|
|
buildScope("rock"),
|
|
buildDivider("B06_Std_Section_PavedOnly"),
|
|
buildScope("paved"),
|
|
note,
|
|
);
|
|
return fieldset;
|
|
};
|
|
|
|
const renderBody = (): void => {
|
|
body.replaceChildren(buildDetails());
|
|
};
|
|
// 옛 저장분이 그룹마다 다른 공통값을 갖고 있으면 토사 기준으로 한 벌로 맞춘다.
|
|
unifyCommon(state);
|
|
persist();
|
|
renderBody();
|
|
|
|
/** 소스 표준값을 현재 상태에 전부 덮어쓴다(사용자가 "적용"을 눌렀을 때만 호출). */
|
|
const applyStandard = (source: StandardCrossSection): void => {
|
|
(Object.keys(source) as StandardCrossKey[]).forEach((key) => {
|
|
if (source[key]) state[key] = JSON.parse(JSON.stringify(source[key])) as StandardCrossGroup;
|
|
});
|
|
unifyCommon(state);
|
|
persist();
|
|
renderBody();
|
|
};
|
|
|
|
// 다른 프로젝트에서 설계값 불러오기: 선택·미리보기만으로는 현재 값이 바뀌지 않고,
|
|
// "적용" 버튼을 눌러야만 반영된다(작업 C-6).
|
|
const loader = buildProjectLoader(projectId, applyStandard);
|
|
|
|
const resetButton = createButton({
|
|
label: L("B06_Std_Reset"),
|
|
variant: "ghost",
|
|
onClick: () => {
|
|
const fresh = cloneDefaults(defaults);
|
|
(Object.keys(fresh) as StandardCrossKey[]).forEach((key) => {
|
|
state[key] = fresh[key];
|
|
});
|
|
unifyCommon(state);
|
|
persist();
|
|
renderBody();
|
|
},
|
|
});
|
|
const actions = document.createElement("div");
|
|
actions.className = "b06-std__actions";
|
|
// [전체 반영](N-2-1): 패널 수치를 design 보유 전 측점에 서버 재계산으로 일괄 반영한다.
|
|
// 측점별 버튼 선택값(지반/단면 등)은 보존하고 표준단면 수치만 갱신한다.
|
|
if (onApplyAll) {
|
|
actions.append(
|
|
createButton({
|
|
label: L("B06_Std_ApplyAll"),
|
|
variant: "filled",
|
|
onClick: () => void onApplyAll(),
|
|
}),
|
|
);
|
|
}
|
|
actions.append(resetButton);
|
|
|
|
// 횡단 반폭은 사이드의 **별도 컨테이너**로 뺐다(2026-08-23) — 여기는 표준단면 설정만.
|
|
root.append(body, loader, actions);
|
|
|
|
return {
|
|
root,
|
|
getValues: () => state,
|
|
applyStored: (stored) => {
|
|
if (hadSession) return; // 진행 중 세션 편집값이 우선.
|
|
(Object.keys(stored) as StandardCrossKey[]).forEach((key) => {
|
|
if (stored[key]) state[key] = JSON.parse(JSON.stringify(stored[key])) as StandardCrossGroup;
|
|
});
|
|
unifyCommon(state);
|
|
renderBody();
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* "다른 프로젝트에서 불러오기" 컨트롤. 같은 회사의 최근 프로젝트 5개를 보여주고,
|
|
* 사용자가 선택하면 그 프로젝트의 저장 설계값을 즉시 현재 설정(그룹 값)에 적용한다.
|
|
* 각 횡단면도 카드에서 사용자가 고른 버튼 옵션(지반유형·단면유형 등)은 건드리지 않는다
|
|
* — 이 값들은 측점별 design으로 별도 보관되며 패널 값과 독립이다.
|
|
*/
|
|
function buildProjectLoader(
|
|
projectId: string,
|
|
onApply: (source: StandardCrossSection) => void,
|
|
): HTMLElement {
|
|
const wrap = document.createElement("details");
|
|
wrap.className = "b06-std__loader";
|
|
const summary = document.createElement("summary");
|
|
summary.className = "b06-std__loader-summary";
|
|
summary.textContent = L("B06_Std_Load_Title");
|
|
wrap.append(summary);
|
|
|
|
const status = document.createElement("p");
|
|
status.className = "b06-std__loader-status";
|
|
status.textContent = L("B06_Std_Load_Loading");
|
|
|
|
const field = createSelectField({
|
|
label: L("B06_Std_Load_Select"),
|
|
options: [{ value: "", text: L("B06_Std_Load_Placeholder") }],
|
|
onChange: (value) => void onSelect(value),
|
|
});
|
|
field.root.hidden = true;
|
|
|
|
async function onSelect(sourceId: string): Promise<void> {
|
|
if (!sourceId) {
|
|
status.textContent = "";
|
|
return;
|
|
}
|
|
status.textContent = L("B06_Std_Load_Loading");
|
|
try {
|
|
const response = await getCompanyStandard(projectId, sourceId);
|
|
onApply(response.standard_cross_section);
|
|
status.textContent = L("B06_Std_Load_Applied");
|
|
} catch (error) {
|
|
// 404 = 해당 프로젝트에 저장된 설계값 없음(목록은 보유 여부 무관 최근 5개).
|
|
const message = error instanceof Error ? error.message : "";
|
|
status.textContent = message.includes("찾을 수 없")
|
|
? L("B06_Std_Load_None")
|
|
: L("B06_Std_Load_Failed");
|
|
}
|
|
}
|
|
|
|
// 같은 회사 최근 프로젝트 5개 비동기 로드 → 셀렉트 채우기.
|
|
void (async () => {
|
|
try {
|
|
const response = await listCompanyStandards(projectId);
|
|
if (!response.projects.length) {
|
|
status.textContent = L("B06_Std_Load_Empty");
|
|
return;
|
|
}
|
|
for (const project of response.projects) {
|
|
const option = document.createElement("option");
|
|
option.value = project.project_id;
|
|
option.textContent = project.name;
|
|
field.select.append(option);
|
|
}
|
|
field.root.hidden = false;
|
|
status.textContent = "";
|
|
} catch {
|
|
status.textContent = L("B06_Std_Load_Failed");
|
|
}
|
|
})();
|
|
|
|
wrap.append(status, field.root);
|
|
return wrap;
|
|
}
|