ZDDC/zddc/internal/handler/tables.html

2382 lines
117 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZDDC Table</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+CiAgPHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiByeD0iMTIiIGZpbGw9IiMxZTNhNWYiLz4KICA8ZyBmaWxsPSIjZmZmIj4KICAgIDxyZWN0IHg9IjE0IiB5PSIxOCIgd2lkdGg9IjM2IiBoZWlnaHQ9IjciLz4KICAgIDxwb2x5Z29uIHBvaW50cz0iNDMsMjUgNTAsMjUgMjEsNDMgMTQsNDMiLz4KICAgIDxyZWN0IHg9IjE0IiB5PSI0MyIgd2lkdGg9IjM2IiBoZWlnaHQ9IjciLz4KICA8L2c+Cjwvc3ZnPgo=">
<style>
/* ==========================================================================
ZDDC Shared Base — single source of truth for tokens and primitives
Included first by every tool's build.sh via ../shared/base.css
========================================================================== */
/* ── CSS custom properties ────────────────────────────────────────────────── */
:root {
/* Brand / accent (matches zddc.varasys.io website --accent) */
--primary: #2a5a8a;
--primary-hover: #1d4060;
--primary-active: #163352;
--primary-light: #e8f0f7;
/* Semantic colours */
--success: #28a745;
--warning: #d97706;
--danger: #dc3545;
--info: #17a2b8;
/* Backgrounds */
--bg: #ffffff;
--bg-secondary: #f8f9fa;
--bg-hover: #f0f4f8;
--bg-selected: var(--primary-light);
/* Text */
--text: #212529;
--text-muted: #6c757d;
--text-light: #ffffff;
/* Borders */
--border: #dee2e6;
--border-dark: #adb5bd;
/* Shape */
--radius: 4px;
/* Typography */
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', 'Courier New', monospace;
}
/* ── Dark mode tokens ─────────────────────────────────────────────────────── */
/* Applied via: OS preference (auto) or [data-theme="dark"] on <html> */
/* The [data-theme="light"] selector locks light mode regardless of OS pref. */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--primary: #4a90c4;
--primary-hover: #5ba3d9;
--primary-active: #6ab5e8;
--primary-light: #1a3550;
--bg: #1e1e1e;
--bg-secondary: #252526;
--bg-hover: #2d2d30;
--bg-selected: #1a3550;
--text: #d4d4d4;
--text-muted: #9d9d9d;
--text-light: #ffffff;
--border: #3e3e42;
--border-dark: #6e6e72;
}
}
/* Manual dark override — wins over media query */
[data-theme="dark"] {
--primary: #4a90c4;
--primary-hover: #5ba3d9;
--primary-active: #6ab5e8;
--primary-light: #1a3550;
--bg: #1e1e1e;
--bg-secondary: #252526;
--bg-hover: #2d2d30;
--bg-selected: #1a3550;
--text: #d4d4d4;
--text-muted: #9d9d9d;
--text-light: #ffffff;
--border: #3e3e42;
--border-dark: #6e6e72;
}
/* ── Reset ────────────────────────────────────────────────────────────────── */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ── Base document ────────────────────────────────────────────────────────── */
html, body {
height: 100%;
font-family: var(--font);
font-size: 16px;
line-height: 1.5;
color: var(--text);
background-color: var(--bg-secondary);
}
/* ── Typography ───────────────────────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
line-height: 1.2;
}
a {
color: var(--primary);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* ── Utility ──────────────────────────────────────────────────────────────── */
.hidden {
display: none !important;
}
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ── Scrollbars (webkit) ──────────────────────────────────────────────────── */
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a0a0a0;
}
/* ── Button primitive ─────────────────────────────────────────────────────── */
.btn {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.4rem 0.85rem;
font-family: var(--font);
font-size: 0.875rem;
font-weight: 500;
line-height: 1.4;
text-align: center;
text-decoration: none;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: var(--radius);
transition: background 0.15s, box-shadow 0.15s, border-color 0.15s, color 0.15s;
background: var(--bg-secondary);
color: var(--text);
}
.btn:disabled,
.btn[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.btn:not(:disabled):hover {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
}
.btn:not(:disabled):active {
box-shadow: none;
}
/* Variants */
.btn-primary {
background: var(--primary);
color: var(--text-light);
border-color: var(--primary);
}
.btn-primary:not(:disabled):hover {
background: var(--primary-hover);
border-color: var(--primary-hover);
color: var(--text-light);
}
.btn-primary:not(:disabled):active {
background: var(--primary-active);
border-color: var(--primary-active);
}
.btn-secondary {
background: var(--bg);
color: var(--text);
border-color: var(--border);
}
.btn-secondary:not(:disabled):hover {
background: var(--bg-secondary);
}
/* Subdued / de-emphasized variant.
Used on the "Add Local Directory" button when a tool is operating
in server (online) mode — the local-dir affordance is still
available but visually quieter, since the typical user already
has the directory loaded from the server. */
.btn.btn--subtle {
background: transparent;
color: var(--text-muted);
border-color: var(--border);
box-shadow: none;
font-weight: normal;
}
.btn.btn--subtle:not(:disabled):hover {
color: var(--text);
background: var(--bg-secondary);
}
.btn-success {
background: var(--success);
color: var(--text-light);
border-color: var(--success);
}
.btn-danger {
background: var(--danger);
color: var(--text-light);
border-color: var(--danger);
}
/* Sizes */
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.btn-lg {
padding: 0.6rem 1.4rem;
font-size: 1rem;
}
.btn-link {
background: transparent;
border-color: transparent;
color: var(--primary);
padding-left: 0;
padding-right: 0;
}
.btn-link:not(:disabled):hover {
text-decoration: underline;
box-shadow: none;
}
/* ── App header chrome ────────────────────────────────────────────────────── */
.app-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.35rem 1rem;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
/* Tool name inside the header */
.app-header__title {
font-size: 17px;
font-weight: 600;
color: var(--text);
letter-spacing: 0.01em;
white-space: nowrap;
}
/* Brand logo — sits left of the title in every tool's app-header.
Self-contained: the SVG provides its own dark blue rounded background,
so no extra wrapper styling is needed. */
.app-header__logo {
width: 26px;
height: 26px;
flex-shrink: 0;
display: block;
}
/* ── Build timestamp ──────────────────────────────────────────────────────── */
.build-timestamp {
font-size: 0.55rem;
color: var(--text-muted);
opacity: 0.7;
font-weight: 300;
white-space: nowrap;
padding-top: 0.15rem;
}
/* Title + timestamp stacked vertically on the left side of the header */
.header-title-group {
display: flex;
flex-direction: column;
gap: 0;
line-height: 1;
}
/* ── Icon buttons (help, theme, refresh) ─────────────────────────────────── */
/* Square, centered — overrides the asymmetric text-button padding/line-height */
#help-btn,
#theme-btn,
#refreshHeaderBtn {
width: 2rem;
height: 2rem;
padding: 0;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
}
/* Toast CSS lives in classifier/css/base.css — only that tool uses toasts. */
/* ── Theme and help icon buttons ─────────────────────────────────────────── */
#theme-btn,
#help-btn {
font-size: 1rem;
}
/* ── Help panel (shared slide-out drawer) ─────────────────────────────────── */
/* Used by all four tools. Toggle open/close via shared/help.js. */
.help-panel {
position: fixed;
top: 0;
right: 0;
width: min(420px, 85vw);
height: 100vh;
z-index: 1000;
background: var(--bg);
border-left: 1px solid var(--border);
box-shadow: -2px 0 12px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.25s ease;
}
.help-panel:not([hidden]) {
transform: translateX(0);
}
.help-panel[hidden] {
display: flex;
transform: translateX(100%);
pointer-events: none;
}
.help-panel__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
background: var(--bg);
}
.help-panel__title {
font-size: 1rem;
font-weight: 700;
color: var(--text);
margin: 0;
}
.help-panel__close {
background: none;
border: none;
color: var(--text-muted);
font-size: 1.35rem;
cursor: pointer;
padding: 0.25rem 0.5rem;
border-radius: var(--radius);
line-height: 1;
transition: background 0.15s, color 0.15s;
}
.help-panel__close:hover {
color: var(--text);
background: var(--bg-secondary);
}
.help-panel__body {
flex: 1;
overflow-y: auto;
padding: 1rem 1rem 2rem;
font-size: 0.85rem;
line-height: 1.6;
color: var(--text);
}
.help-panel__body h3 {
font-size: 0.95rem;
font-weight: 700;
margin: 1.25rem 0 0.35rem;
color: var(--text);
border-bottom: 1px solid var(--border);
padding-bottom: 0.15rem;
}
.help-panel__body h3:first-child {
margin-top: 0;
}
.help-panel__body h4 {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
margin: 1.25rem 0 0.3rem;
padding-left: 0.5rem;
border-left: 3px solid var(--border-dark);
color: var(--text-muted);
}
.help-panel__body p {
margin: 0 0 0.5rem;
}
.help-panel__body ol,
.help-panel__body ul {
padding-left: 1.5rem;
margin: 0.3rem 0 0.5rem;
}
.help-panel__body li {
margin-bottom: 0.3rem;
}
.help-panel__body dl {
margin: 0.3rem 0;
}
.help-panel__body dt {
font-weight: 600;
color: var(--text);
}
.help-panel__body dd {
margin: 0 0 0.5rem 1rem;
color: var(--text-muted);
}
.help-panel__body code {
font-family: var(--font-mono);
font-size: 0.8em;
background: var(--bg-secondary);
padding: 0.1em 0.3em;
border-radius: 3px;
}
.help-badge {
font-size: 0.7rem;
font-weight: 600;
padding: 0.1rem 0.35rem;
border-radius: var(--radius);
vertical-align: middle;
letter-spacing: 0.02em;
}
.help-badge--draft {
color: #2563eb;
background: #eff6ff;
}
.help-badge--published {
color: #7c3aed;
background: #f5f3ff;
}
/* Shrink main content when help panel is open */
body.help-open .app-header {
margin-right: min(420px, 85vw);
}
/* ── Column filter inputs (shared across archive, classifier, transmittal) ─── */
.column-filter {
display: block;
width: 100%;
box-sizing: border-box;
margin-top: 0.25rem;
padding: 0.2rem 0.4rem;
font-size: 0.8rem;
font-family: var(--font);
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--bg);
color: var(--text);
transition: border-color 0.15s;
}
.column-filter:focus {
border-color: var(--primary);
outline: none;
box-shadow: 0 0 0 1px rgba(42, 90, 138, 0.35);
}
.column-filter::placeholder {
color: var(--text-muted);
}
/* tables/ — directory-of-YAML table view. Reuses tokens from shared/base.css. */
.table-main {
padding: var(--spacing-md);
max-width: 100%;
}
.table-description {
margin: 0 0 var(--spacing-md);
color: var(--color-text-muted);
font-size: 0.95rem;
}
.table-status {
margin: 0 0 var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-bg-warning, #fff8e6);
border: 1px solid var(--color-border, #d6cfa3);
border-radius: var(--radius-sm, 4px);
}
.table-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--spacing-md);
margin: 0 0 var(--spacing-sm);
}
.table-toolbar__left {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.table-rowcount {
color: var(--color-text-muted);
font-size: 0.9rem;
}
.table-scroll {
overflow: auto;
max-height: calc(100vh - 200px);
border: 1px solid var(--color-border, #d8d8d8);
border-radius: var(--radius-sm, 4px);
}
.zddc-table {
border-collapse: collapse;
width: 100%;
font-size: 0.95rem;
}
.zddc-table thead {
position: sticky;
top: 0;
z-index: 2;
background: var(--color-bg-elevated, #f5f5f5);
}
.zddc-table__title-row .zddc-table__th {
padding: var(--spacing-sm) var(--spacing-md);
text-align: left;
font-weight: 600;
border-bottom: 1px solid var(--color-border, #d8d8d8);
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.zddc-table__title-row .zddc-table__th:hover {
background: var(--color-bg-hover, rgba(0, 0, 0, 0.04));
}
.zddc-table__filter-row .zddc-table__filter-cell {
padding: 4px var(--spacing-sm);
border-bottom: 1px solid var(--color-border, #d8d8d8);
background: var(--color-bg-elevated, #f5f5f5);
}
.zddc-table__filter-text,
.zddc-table__filter-enum {
width: 100%;
box-sizing: border-box;
padding: 2px 4px;
font-size: 0.85rem;
border: 1px solid var(--color-border, #d0d0d0);
border-radius: 3px;
background: var(--color-bg, #fff);
color: var(--color-text, #111);
}
.zddc-table__filter-enum {
min-height: 1.8em;
}
.zddc-table__row:nth-child(even) {
background: var(--color-bg-zebra, rgba(0, 0, 0, 0.02));
}
.zddc-table__row--editable {
cursor: pointer;
}
.zddc-table__row--editable:hover {
background: var(--color-bg-hover, rgba(50, 100, 200, 0.08));
}
.zddc-table__row--readonly {
color: var(--color-text-muted);
}
.zddc-table__cell {
padding: var(--spacing-sm) var(--spacing-md);
border-bottom: 1px solid var(--color-border-soft, rgba(0, 0, 0, 0.06));
vertical-align: top;
}
.table-empty {
padding: var(--spacing-lg) var(--spacing-md);
text-align: center;
color: var(--color-text-muted);
font-style: italic;
}
</style>
</head>
<body>
<header class="app-header">
<div class="header-left">
<svg class="app-header__logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" aria-hidden="true">
<rect width="64" height="64" rx="12" fill="#1e3a5f"/>
<g fill="#fff">
<rect x="14" y="18" width="36" height="7"/>
<polygon points="43,25 50,25 21,43 14,43"/>
<rect x="14" y="43" width="36" height="7"/>
</g>
</svg>
<div class="header-title-group">
<span class="app-header__title" id="table-title">ZDDC Table</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.17-beta · 2026-05-08 · falcon-alder-ginger</span></span>
</div>
</div>
<div class="header-right">
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)"></button>
<button id="help-btn" class="btn btn-secondary" title="Help" aria-label="Help">?</button>
</div>
</header>
<main class="table-main">
<div id="table-description" class="table-description" hidden></div>
<div id="table-status" class="table-status" hidden></div>
<div class="table-toolbar" id="table-toolbar">
<div class="table-toolbar__left">
<span id="table-rowcount" class="table-rowcount" aria-live="polite"></span>
<button type="button" id="table-clear-filters" class="btn btn-secondary btn-sm" hidden>Clear filters</button>
</div>
</div>
<div class="table-scroll">
<table id="table-root" class="zddc-table" aria-describedby="table-description">
<thead></thead>
<tbody></tbody>
</table>
</div>
<div id="table-empty" class="table-empty" hidden>No rows match the current filters.</div>
</main>
<!-- Help Panel -->
<aside id="help-panel" class="help-panel" hidden aria-labelledby="help-panel-title">
<div class="help-panel__header">
<h2 id="help-panel-title" class="help-panel__title">Help — ZDDC Table</h2>
<button type="button" class="help-panel__close" id="help-panel-close" aria-label="Close">&times;</button>
</div>
<div class="help-panel__body">
<h3>What is this table?</h3>
<p>Each row in this table is one YAML file in the source directory.
Tables are declared in <code>.zddc</code> via a
<code>tables:</code> map. The columns and row schema come from
a <code>*.table.yaml</code> spec file.</p>
<h3>Sorting</h3>
<p>Click a column header to sort by that column. Click again to
toggle direction. Shift-click another header to add a secondary
sort key.</p>
<h3>Filtering</h3>
<p>Type in the box under a column header to filter rows whose
value contains your text (case-insensitive). For columns with a
fixed enum, the box becomes a multi-select — leave it empty to
show every value.</p>
<h3>Editing a row</h3>
<p>Click a row to open its YAML in the form editor. Whether the
row is editable depends on the cascading <code>.zddc</code>
permissions for the row's path. Rows in <code>Issued</code> or
<code>Received</code> archives are read-only by design (WORM).</p>
<h3>Header buttons</h3>
<dl>
<dt>◐ Theme</dt>
<dd>Cycle auto / light / dark.</dd>
<dt>? Help</dt>
<dd>This panel. Press <kbd>Esc</kbd> to close.</dd>
</dl>
</div>
</aside>
<!--
Server injects the table context here on render. Shape:
{
"title": "Optional page title override",
"description": "Optional description shown above the table",
"columns": [{field, title, width?, format?, filter?, sort?, enum?}],
"rows": [{url, data, editable}],
"defaults": {sort?: [{field, dir}], filter?: {field: value}}
}
-->
<script id="table-context" type="application/json">{}</script>
<script>
/*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).jsyaml={})}(this,(function(e){"use strict";function t(e){return null==e}var n={isNothing:t,isObject:function(e){return"object"==typeof e&&null!==e},toArray:function(e){return Array.isArray(e)?e:t(e)?[]:[e]},repeat:function(e,t){var n,i="";for(n=0;n<t;n+=1)i+=e;return i},isNegativeZero:function(e){return 0===e&&Number.NEGATIVE_INFINITY===1/e},extend:function(e,t){var n,i,r,o;if(t)for(n=0,i=(o=Object.keys(t)).length;n<i;n+=1)e[r=o[n]]=t[r];return e}};function i(e,t){var n="",i=e.reason||"(unknown reason)";return e.mark?(e.mark.name&&(n+='in "'+e.mark.name+'" '),n+="("+(e.mark.line+1)+":"+(e.mark.column+1)+")",!t&&e.mark.snippet&&(n+="\n\n"+e.mark.snippet),i+" "+n):i}function r(e,t){Error.call(this),this.name="YAMLException",this.reason=e,this.mark=t,this.message=i(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||""}r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r.prototype.toString=function(e){return this.name+": "+i(this,e)};var o=r;function a(e,t,n,i,r){var o="",a="",l=Math.floor(r/2)-1;return i-t>l&&(t=i-l+(o=" ... ").length),n-i>l&&(n=i+l-(a=" ...").length),{str:o+e.slice(t,n).replace(/\t/g,"→")+a,pos:i-t+o.length}}function l(e,t){return n.repeat(" ",t-e.length)+e}var c=function(e,t){if(t=Object.create(t||null),!e.buffer)return null;t.maxLength||(t.maxLength=79),"number"!=typeof t.indent&&(t.indent=1),"number"!=typeof t.linesBefore&&(t.linesBefore=3),"number"!=typeof t.linesAfter&&(t.linesAfter=2);for(var i,r=/\r?\n|\r|\0/g,o=[0],c=[],s=-1;i=r.exec(e.buffer);)c.push(i.index),o.push(i.index+i[0].length),e.position<=i.index&&s<0&&(s=o.length-2);s<0&&(s=o.length-1);var u,p,f="",d=Math.min(e.line+t.linesAfter,c.length).toString().length,h=t.maxLength-(t.indent+d+3);for(u=1;u<=t.linesBefore&&!(s-u<0);u++)p=a(e.buffer,o[s-u],c[s-u],e.position-(o[s]-o[s-u]),h),f=n.repeat(" ",t.indent)+l((e.line-u+1).toString(),d)+" | "+p.str+"\n"+f;for(p=a(e.buffer,o[s],c[s],e.position,h),f+=n.repeat(" ",t.indent)+l((e.line+1).toString(),d)+" | "+p.str+"\n",f+=n.repeat("-",t.indent+d+3+p.pos)+"^\n",u=1;u<=t.linesAfter&&!(s+u>=c.length);u++)p=a(e.buffer,o[s+u],c[s+u],e.position-(o[s]-o[s+u]),h),f+=n.repeat(" ",t.indent)+l((e.line+u+1).toString(),d)+" | "+p.str+"\n";return f.replace(/\n$/,"")},s=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],u=["scalar","sequence","mapping"];var p=function(e,t){if(t=t||{},Object.keys(t).forEach((function(t){if(-1===s.indexOf(t))throw new o('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.options=t,this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.representName=t.representName||null,this.defaultStyle=t.defaultStyle||null,this.multi=t.multi||!1,this.styleAliases=function(e){var t={};return null!==e&&Object.keys(e).forEach((function(n){e[n].forEach((function(e){t[String(e)]=n}))})),t}(t.styleAliases||null),-1===u.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')};function f(e,t){var n=[];return e[t].forEach((function(e){var t=n.length;n.forEach((function(n,i){n.tag===e.tag&&n.kind===e.kind&&n.multi===e.multi&&(t=i)})),n[t]=e})),n}function d(e){return this.extend(e)}d.prototype.extend=function(e){var t=[],n=[];if(e instanceof p)n.push(e);else if(Array.isArray(e))n=n.concat(e);else{if(!e||!Array.isArray(e.implicit)&&!Array.isArray(e.explicit))throw new o("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");e.implicit&&(t=t.concat(e.implicit)),e.explicit&&(n=n.concat(e.explicit))}t.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(e.loadKind&&"scalar"!==e.loadKind)throw new o("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(e.multi)throw new o("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")})),n.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.")}));var i=Object.create(d.prototype);return i.implicit=(this.implicit||[]).concat(t),i.explicit=(this.explicit||[]).concat(n),i.compiledImplicit=f(i,"implicit"),i.compiledExplicit=f(i,"explicit"),i.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function i(e){e.multi?(n.multi[e.kind].push(e),n.multi.fallback.push(e)):n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e<t;e+=1)arguments[e].forEach(i);return n}(i.compiledImplicit,i.compiledExplicit),i};var h=d,g=new p("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}}),m=new p("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}}),y=new p("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}}),b=new h({explicit:[g,m,y]});var A=new p("tag:yaml.org,2002:null",{kind:"scalar",resolve:function(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)},construct:function(){return null},predicate:function(e){return null===e},represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"});var v=new p("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"});function w(e){return 48<=e&&e<=55}function k(e){return 48<=e&&e<=57}var C=new p("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i=e.length,r=0,o=!1;if(!i)return!1;if("-"!==(t=e[r])&&"+"!==t||(t=e[++r]),"0"===t){if(r+1===i)return!0;if("b"===(t=e[++r])){for(r++;r<i;r++)if("_"!==(t=e[r])){if("0"!==t&&"1"!==t)return!1;o=!0}return o&&"_"!==t}if("x"===t){for(r++;r<i;r++)if("_"!==(t=e[r])){if(!(48<=(n=e.charCodeAt(r))&&n<=57||65<=n&&n<=70||97<=n&&n<=102))return!1;o=!0}return o&&"_"!==t}if("o"===t){for(r++;r<i;r++)if("_"!==(t=e[r])){if(!w(e.charCodeAt(r)))return!1;o=!0}return o&&"_"!==t}}if("_"===t)return!1;for(;r<i;r++)if("_"!==(t=e[r])){if(!k(e.charCodeAt(r)))return!1;o=!0}return!(!o||"_"===t)},construct:function(e){var t,n=e,i=1;if(-1!==n.indexOf("_")&&(n=n.replace(/_/g,"")),"-"!==(t=n[0])&&"+"!==t||("-"===t&&(i=-1),t=(n=n.slice(1))[0]),"0"===n)return 0;if("0"===t){if("b"===n[1])return i*parseInt(n.slice(2),2);if("x"===n[1])return i*parseInt(n.slice(2),16);if("o"===n[1])return i*parseInt(n.slice(2),8)}return i*parseInt(n,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&e%1==0&&!n.isNegativeZero(e)},represent:{binary:function(e){return e>=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),x=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var I=/^[-+]?[0-9]+e/;var S=new p("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!x.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||n.isNegativeZero(e))},represent:function(e,t){var i;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(n.isNegativeZero(e))return"-0.0";return i=e.toString(10),I.test(i)?i.replace("e",".e"):i},defaultStyle:"lowercase"}),O=b.extend({implicit:[A,v,C,S]}),j=O,T=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),N=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");var F=new p("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(e){return null!==e&&(null!==T.exec(e)||null!==N.exec(e))},construct:function(e){var t,n,i,r,o,a,l,c,s=0,u=null;if(null===(t=T.exec(e))&&(t=N.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(o=+t[4],a=+t[5],l=+t[6],t[7]){for(s=t[7].slice(0,3);s.length<3;)s+="0";s=+s}return t[9]&&(u=6e4*(60*+t[10]+ +(t[11]||0)),"-"===t[9]&&(u=-u)),c=new Date(Date.UTC(n,i,r,o,a,l,s)),u&&c.setTime(c.getTime()-u),c},instanceOf:Date,represent:function(e){return e.toISOString()}});var E=new p("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(e){return"<<"===e||null===e}}),M="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";var L=new p("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i=0,r=e.length,o=M;for(n=0;n<r;n++)if(!((t=o.indexOf(e.charAt(n)))>64)){if(t<0)return!1;i+=6}return i%8==0},construct:function(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,o=M,a=0,l=[];for(t=0;t<r;t++)t%4==0&&t&&(l.push(a>>16&255),l.push(a>>8&255),l.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0===(n=r%4*6)?(l.push(a>>16&255),l.push(a>>8&255),l.push(255&a)):18===n?(l.push(a>>10&255),l.push(a>>2&255)):12===n&&l.push(a>>4&255),new Uint8Array(l)},predicate:function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=M;for(t=0;t<o;t++)t%3==0&&t&&(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0===(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}}),_=Object.prototype.hasOwnProperty,D=Object.prototype.toString;var U=new p("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,n,i,r,o,a=[],l=e;for(t=0,n=l.length;t<n;t+=1){if(i=l[t],o=!1,"[object Object]"!==D.call(i))return!1;for(r in i)if(_.call(i,r)){if(o)return!1;o=!0}if(!o)return!1;if(-1!==a.indexOf(r))return!1;a.push(r)}return!0},construct:function(e){return null!==e?e:[]}}),q=Object.prototype.toString;var Y=new p("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,n,i,r,o,a=e;for(o=new Array(a.length),t=0,n=a.length;t<n;t+=1){if(i=a[t],"[object Object]"!==q.call(i))return!1;if(1!==(r=Object.keys(i)).length)return!1;o[t]=[r[0],i[r[0]]]}return!0},construct:function(e){if(null===e)return[];var t,n,i,r,o,a=e;for(o=new Array(a.length),t=0,n=a.length;t<n;t+=1)i=a[t],r=Object.keys(i),o[t]=[r[0],i[r[0]]];return o}}),R=Object.prototype.hasOwnProperty;var B=new p("tag:yaml.org,2002:set",{kind:"mapping",resolve:function(e){if(null===e)return!0;var t,n=e;for(t in n)if(R.call(n,t)&&null!==n[t])return!1;return!0},construct:function(e){return null!==e?e:{}}}),K=j.extend({implicit:[F,E],explicit:[L,U,Y,B]}),P=Object.prototype.hasOwnProperty,W=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,H=/[\x85\u2028\u2029]/,$=/[,\[\]\{\}]/,G=/^(?:!|!!|![a-z\-]+!)$/i,V=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function Z(e){return Object.prototype.toString.call(e)}function J(e){return 10===e||13===e}function Q(e){return 9===e||32===e}function z(e){return 9===e||32===e||10===e||13===e}function X(e){return 44===e||91===e||93===e||123===e||125===e}function ee(e){var t;return 48<=e&&e<=57?e-48:97<=(t=32|e)&&t<=102?t-97+10:-1}function te(e){return 48===e?"\0":97===e?"":98===e?"\b":116===e||9===e?"\t":110===e?"\n":118===e?"\v":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function ne(e){return e<=65535?String.fromCharCode(e):String.fromCharCode(55296+(e-65536>>10),56320+(e-65536&1023))}for(var ie=new Array(256),re=new Array(256),oe=0;oe<256;oe++)ie[oe]=te(oe)?1:0,re[oe]=te(oe);function ae(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||K,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function le(e,t){var n={name:e.filename,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return n.snippet=c(n),new o(t,n)}function ce(e,t){throw le(e,t)}function se(e,t){e.onWarning&&e.onWarning.call(null,le(e,t))}var ue={YAML:function(e,t,n){var i,r,o;null!==e.version&&ce(e,"duplication of %YAML directive"),1!==n.length&&ce(e,"YAML directive accepts exactly one argument"),null===(i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]))&&ce(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&ce(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=o<2,1!==o&&2!==o&&se(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&ce(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],G.test(i)||ce(e,"ill-formed tag handle (first argument) of the TAG directive"),P.call(e.tagMap,i)&&ce(e,'there is a previously declared suffix for "'+i+'" tag handle'),V.test(r)||ce(e,"ill-formed tag prefix (second argument) of the TAG directive");try{r=decodeURIComponent(r)}catch(t){ce(e,"tag prefix is malformed: "+r)}e.tagMap[i]=r}};function pe(e,t,n,i){var r,o,a,l;if(t<n){if(l=e.input.slice(t,n),i)for(r=0,o=l.length;r<o;r+=1)9===(a=l.charCodeAt(r))||32<=a&&a<=1114111||ce(e,"expected valid JSON character");else W.test(l)&&ce(e,"the stream contains non-printable characters");e.result+=l}}function fe(e,t,i,r){var o,a,l,c;for(n.isObject(i)||ce(e,"cannot merge mappings; the provided source object is unacceptable"),l=0,c=(o=Object.keys(i)).length;l<c;l+=1)a=o[l],P.call(t,a)||(t[a]=i[a],r[a]=!0)}function de(e,t,n,i,r,o,a,l,c){var s,u;if(Array.isArray(r))for(s=0,u=(r=Array.prototype.slice.call(r)).length;s<u;s+=1)Array.isArray(r[s])&&ce(e,"nested arrays are not supported inside keys"),"object"==typeof r&&"[object Object]"===Z(r[s])&&(r[s]="[object Object]");if("object"==typeof r&&"[object Object]"===Z(r)&&(r="[object Object]"),r=String(r),null===t&&(t={}),"tag:yaml.org,2002:merge"===i)if(Array.isArray(o))for(s=0,u=o.length;s<u;s+=1)fe(e,t,o[s],n);else fe(e,t,o,n);else e.json||P.call(n,r)||!P.call(t,r)||(e.line=a||e.line,e.lineStart=l||e.lineStart,e.position=c||e.position,ce(e,"duplicated mapping key")),"__proto__"===r?Object.defineProperty(t,r,{configurable:!0,enumerable:!0,writable:!0,value:o}):t[r]=o,delete n[r];return t}function he(e){var t;10===(t=e.input.charCodeAt(e.position))?e.position++:13===t?(e.position++,10===e.input.charCodeAt(e.position)&&e.position++):ce(e,"a line break is expected"),e.line+=1,e.lineStart=e.position,e.firstTabInLine=-1}function ge(e,t,n){for(var i=0,r=e.input.charCodeAt(e.position);0!==r;){for(;Q(r);)9===r&&-1===e.firstTabInLine&&(e.firstTabInLine=e.position),r=e.input.charCodeAt(++e.position);if(t&&35===r)do{r=e.input.charCodeAt(++e.position)}while(10!==r&&13!==r&&0!==r);if(!J(r))break;for(he(e),r=e.input.charCodeAt(e.position),i++,e.lineIndent=0;32===r;)e.lineIndent++,r=e.input.charCodeAt(++e.position)}return-1!==n&&0!==i&&e.lineIndent<n&&se(e,"deficient indentation"),i}function me(e){var t,n=e.position;return!(45!==(t=e.input.charCodeAt(n))&&46!==t||t!==e.input.charCodeAt(n+1)||t!==e.input.charCodeAt(n+2)||(n+=3,0!==(t=e.input.charCodeAt(n))&&!z(t)))}function ye(e,t){1===t?e.result+=" ":t>1&&(e.result+=n.repeat("\n",t-1))}function be(e,t){var n,i,r=e.tag,o=e.anchor,a=[],l=!1;if(-1!==e.firstTabInLine)return!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=a),i=e.input.charCodeAt(e.position);0!==i&&(-1!==e.firstTabInLine&&(e.position=e.firstTabInLine,ce(e,"tab characters must not be used in indentation")),45===i)&&z(e.input.charCodeAt(e.position+1));)if(l=!0,e.position++,ge(e,!0,-1)&&e.lineIndent<=t)a.push(null),i=e.input.charCodeAt(e.position);else if(n=e.line,we(e,t,3,!1,!0),a.push(e.result),ge(e,!0,-1),i=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==i)ce(e,"bad indentation of a sequence entry");else if(e.lineIndent<t)break;return!!l&&(e.tag=r,e.anchor=o,e.kind="sequence",e.result=a,!0)}function Ae(e){var t,n,i,r,o=!1,a=!1;if(33!==(r=e.input.charCodeAt(e.position)))return!1;if(null!==e.tag&&ce(e,"duplication of a tag property"),60===(r=e.input.charCodeAt(++e.position))?(o=!0,r=e.input.charCodeAt(++e.position)):33===r?(a=!0,n="!!",r=e.input.charCodeAt(++e.position)):n="!",t=e.position,o){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&62!==r);e.position<e.length?(i=e.input.slice(t,e.position),r=e.input.charCodeAt(++e.position)):ce(e,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!z(r);)33===r&&(a?ce(e,"tag suffix cannot contain exclamation marks"):(n=e.input.slice(t-1,e.position+1),G.test(n)||ce(e,"named tag handle cannot contain such characters"),a=!0,t=e.position+1)),r=e.input.charCodeAt(++e.position);i=e.input.slice(t,e.position),$.test(i)&&ce(e,"tag suffix cannot contain flow indicator characters")}i&&!V.test(i)&&ce(e,"tag name cannot contain such characters: "+i);try{i=decodeURIComponent(i)}catch(t){ce(e,"tag name is malformed: "+i)}return o?e.tag=i:P.call(e.tagMap,n)?e.tag=e.tagMap[n]+i:"!"===n?e.tag="!"+i:"!!"===n?e.tag="tag:yaml.org,2002:"+i:ce(e,'undeclared tag handle "'+n+'"'),!0}function ve(e){var t,n;if(38!==(n=e.input.charCodeAt(e.position)))return!1;for(null!==e.anchor&&ce(e,"duplication of an anchor property"),n=e.input.charCodeAt(++e.position),t=e.position;0!==n&&!z(n)&&!X(n);)n=e.input.charCodeAt(++e.position);return e.position===t&&ce(e,"name of an anchor node must contain at least one character"),e.anchor=e.input.slice(t,e.position),!0}function we(e,t,i,r,o){var a,l,c,s,u,p,f,d,h,g=1,m=!1,y=!1;if(null!==e.listener&&e.listener("open",e),e.tag=null,e.anchor=null,e.kind=null,e.result=null,a=l=c=4===i||3===i,r&&ge(e,!0,-1)&&(m=!0,e.lineIndent>t?g=1:e.lineIndent===t?g=0:e.lineIndent<t&&(g=-1)),1===g)for(;Ae(e)||ve(e);)ge(e,!0,-1)?(m=!0,c=a,e.lineIndent>t?g=1:e.lineIndent===t?g=0:e.lineIndent<t&&(g=-1)):c=!1;if(c&&(c=m||o),1!==g&&4!==i||(d=1===i||2===i?t:t+1,h=e.position-e.lineStart,1===g?c&&(be(e,h)||function(e,t,n){var i,r,o,a,l,c,s,u=e.tag,p=e.anchor,f={},d=Object.create(null),h=null,g=null,m=null,y=!1,b=!1;if(-1!==e.firstTabInLine)return!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=f),s=e.input.charCodeAt(e.position);0!==s;){if(y||-1===e.firstTabInLine||(e.position=e.firstTabInLine,ce(e,"tab characters must not be used in indentation")),i=e.input.charCodeAt(e.position+1),o=e.line,63!==s&&58!==s||!z(i)){if(a=e.line,l=e.lineStart,c=e.position,!we(e,n,2,!1,!0))break;if(e.line===o){for(s=e.input.charCodeAt(e.position);Q(s);)s=e.input.charCodeAt(++e.position);if(58===s)z(s=e.input.charCodeAt(++e.position))||ce(e,"a whitespace character is expected after the key-value separator within a block mapping"),y&&(de(e,f,d,h,g,null,a,l,c),h=g=m=null),b=!0,y=!1,r=!1,h=e.tag,g=e.result;else{if(!b)return e.tag=u,e.anchor=p,!0;ce(e,"can not read an implicit mapping pair; a colon is missed")}}else{if(!b)return e.tag=u,e.anchor=p,!0;ce(e,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===s?(y&&(de(e,f,d,h,g,null,a,l,c),h=g=m=null),b=!0,y=!0,r=!0):y?(y=!1,r=!0):ce(e,"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line"),e.position+=1,s=i;if((e.line===o||e.lineIndent>t)&&(y&&(a=e.line,l=e.lineStart,c=e.position),we(e,t,4,!0,r)&&(y?g=e.result:m=e.result),y||(de(e,f,d,h,g,m,a,l,c),h=g=m=null),ge(e,!0,-1),s=e.input.charCodeAt(e.position)),(e.line===o||e.lineIndent>t)&&0!==s)ce(e,"bad indentation of a mapping entry");else if(e.lineIndent<t)break}return y&&de(e,f,d,h,g,null,a,l,c),b&&(e.tag=u,e.anchor=p,e.kind="mapping",e.result=f),b}(e,h,d))||function(e,t){var n,i,r,o,a,l,c,s,u,p,f,d,h=!0,g=e.tag,m=e.anchor,y=Object.create(null);if(91===(d=e.input.charCodeAt(e.position)))a=93,s=!1,o=[];else{if(123!==d)return!1;a=125,s=!0,o={}}for(null!==e.anchor&&(e.anchorMap[e.anchor]=o),d=e.input.charCodeAt(++e.position);0!==d;){if(ge(e,!0,t),(d=e.input.charCodeAt(e.position))===a)return e.position++,e.tag=g,e.anchor=m,e.kind=s?"mapping":"sequence",e.result=o,!0;h?44===d&&ce(e,"expected the node content, but found ','"):ce(e,"missed comma between flow collection entries"),f=null,l=c=!1,63===d&&z(e.input.charCodeAt(e.position+1))&&(l=c=!0,e.position++,ge(e,!0,t)),n=e.line,i=e.lineStart,r=e.position,we(e,t,1,!1,!0),p=e.tag,u=e.result,ge(e,!0,t),d=e.input.charCodeAt(e.position),!c&&e.line!==n||58!==d||(l=!0,d=e.input.charCodeAt(++e.position),ge(e,!0,t),we(e,t,1,!1,!0),f=e.result),s?de(e,o,y,p,u,f,n,i,r):l?o.push(de(e,null,y,p,u,f,n,i,r)):o.push(u),ge(e,!0,t),44===(d=e.input.charCodeAt(e.position))?(h=!0,d=e.input.charCodeAt(++e.position)):h=!1}ce(e,"unexpected end of the stream within a flow collection")}(e,d)?y=!0:(l&&function(e,t){var i,r,o,a,l,c=1,s=!1,u=!1,p=t,f=0,d=!1;if(124===(a=e.input.charCodeAt(e.position)))r=!1;else{if(62!==a)return!1;r=!0}for(e.kind="scalar",e.result="";0!==a;)if(43===(a=e.input.charCodeAt(++e.position))||45===a)1===c?c=43===a?3:2:ce(e,"repeat of a chomping mode identifier");else{if(!((o=48<=(l=a)&&l<=57?l-48:-1)>=0))break;0===o?ce(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):u?ce(e,"repeat of an indentation width identifier"):(p=t+o-1,u=!0)}if(Q(a)){do{a=e.input.charCodeAt(++e.position)}while(Q(a));if(35===a)do{a=e.input.charCodeAt(++e.position)}while(!J(a)&&0!==a)}for(;0!==a;){for(he(e),e.lineIndent=0,a=e.input.charCodeAt(e.position);(!u||e.lineIndent<p)&&32===a;)e.lineIndent++,a=e.input.charCodeAt(++e.position);if(!u&&e.lineIndent>p&&(p=e.lineIndent),J(a))f++;else{if(e.lineIndent<p){3===c?e.result+=n.repeat("\n",s?1+f:f):1===c&&s&&(e.result+="\n");break}for(r?Q(a)?(d=!0,e.result+=n.repeat("\n",s?1+f:f)):d?(d=!1,e.result+=n.repeat("\n",f+1)):0===f?s&&(e.result+=" "):e.result+=n.repeat("\n",f):e.result+=n.repeat("\n",s?1+f:f),s=!0,u=!0,f=0,i=e.position;!J(a)&&0!==a;)a=e.input.charCodeAt(++e.position);pe(e,i,e.position,!1)}}return!0}(e,d)||function(e,t){var n,i,r;if(39!==(n=e.input.charCodeAt(e.position)))return!1;for(e.kind="scalar",e.result="",e.position++,i=r=e.position;0!==(n=e.input.charCodeAt(e.position));)if(39===n){if(pe(e,i,e.position,!0),39!==(n=e.input.charCodeAt(++e.position)))return!0;i=e.position,e.position++,r=e.position}else J(n)?(pe(e,i,r,!0),ye(e,ge(e,!1,t)),i=r=e.position):e.position===e.lineStart&&me(e)?ce(e,"unexpected end of the document within a single quoted scalar"):(e.position++,r=e.position);ce(e,"unexpected end of the stream within a single quoted scalar")}(e,d)||function(e,t){var n,i,r,o,a,l,c;if(34!==(l=e.input.charCodeAt(e.position)))return!1;for(e.kind="scalar",e.result="",e.position++,n=i=e.position;0!==(l=e.input.charCodeAt(e.position));){if(34===l)return pe(e,n,e.position,!0),e.position++,!0;if(92===l){if(pe(e,n,e.position,!0),J(l=e.input.charCodeAt(++e.position)))ge(e,!1,t);else if(l<256&&ie[l])e.result+=re[l],e.position++;else if((a=120===(c=l)?2:117===c?4:85===c?8:0)>0){for(r=a,o=0;r>0;r--)(a=ee(l=e.input.charCodeAt(++e.position)))>=0?o=(o<<4)+a:ce(e,"expected hexadecimal character");e.result+=ne(o),e.position++}else ce(e,"unknown escape sequence");n=i=e.position}else J(l)?(pe(e,n,i,!0),ye(e,ge(e,!1,t)),n=i=e.position):e.position===e.lineStart&&me(e)?ce(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}ce(e,"unexpected end of the stream within a double quoted scalar")}(e,d)?y=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!z(i)&&!X(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&ce(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),P.call(e.anchorMap,n)||ce(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],ge(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,l,c,s,u,p=e.kind,f=e.result;if(z(u=e.input.charCodeAt(e.position))||X(u)||35===u||38===u||42===u||33===u||124===u||62===u||39===u||34===u||37===u||64===u||96===u)return!1;if((63===u||45===u)&&(z(i=e.input.charCodeAt(e.position+1))||n&&X(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==u;){if(58===u){if(z(i=e.input.charCodeAt(e.position+1))||n&&X(i))break}else if(35===u){if(z(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&me(e)||n&&X(u))break;if(J(u)){if(l=e.line,c=e.lineStart,s=e.lineIndent,ge(e,!1,-1),e.lineIndent>=t){a=!0,u=e.input.charCodeAt(e.position);continue}e.position=o,e.line=l,e.lineStart=c,e.lineIndent=s;break}}a&&(pe(e,r,o,!1),ye(e,e.line-l),r=o=e.position,a=!1),Q(u)||(o=e.position+1),u=e.input.charCodeAt(++e.position)}return pe(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,d,1===i)&&(y=!0,null===e.tag&&(e.tag="?")):(y=!0,null===e.tag&&null===e.anchor||ce(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===g&&(y=c&&be(e,h))),null===e.tag)null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);else if("?"===e.tag){for(null!==e.result&&"scalar"!==e.kind&&ce(e,'unacceptable node kind for !<?> tag; it should be "scalar", not "'+e.kind+'"'),s=0,u=e.implicitTypes.length;s<u;s+=1)if((f=e.implicitTypes[s]).resolve(e.result)){e.result=f.construct(e.result),e.tag=f.tag,null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);break}}else if("!"!==e.tag){if(P.call(e.typeMap[e.kind||"fallback"],e.tag))f=e.typeMap[e.kind||"fallback"][e.tag];else for(f=null,s=0,u=(p=e.typeMap.multi[e.kind||"fallback"]).length;s<u;s+=1)if(e.tag.slice(0,p[s].tag.length)===p[s].tag){f=p[s];break}f||ce(e,"unknown tag !<"+e.tag+">"),null!==e.result&&f.kind!==e.kind&&ce(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+f.kind+'", not "'+e.kind+'"'),f.resolve(e.result,e.tag)?(e.result=f.construct(e.result,e.tag),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):ce(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")}return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||y}function ke(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap=Object.create(null),e.anchorMap=Object.create(null);0!==(r=e.input.charCodeAt(e.position))&&(ge(e,!0,-1),r=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==r));){for(a=!0,r=e.input.charCodeAt(++e.position),t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);for(i=[],(n=e.input.slice(t,e.position)).length<1&&ce(e,"directive name must not be less than one character in length");0!==r;){for(;Q(r);)r=e.input.charCodeAt(++e.position);if(35===r){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&!J(r));break}if(J(r))break;for(t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);i.push(e.input.slice(t,e.position))}0!==r&&he(e),P.call(ue,n)?ue[n](e,n,i):se(e,'unknown document directive "'+n+'"')}ge(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,ge(e,!0,-1)):a&&ce(e,"directives end mark is expected"),we(e,e.lineIndent-1,4,!1,!0),ge(e,!0,-1),e.checkLineBreaks&&H.test(e.input.slice(o,e.position))&&se(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&me(e)?46===e.input.charCodeAt(e.position)&&(e.position+=3,ge(e,!0,-1)):e.position<e.length-1&&ce(e,"end of the stream or a document separator is expected")}function Ce(e,t){t=t||{},0!==(e=String(e)).length&&(10!==e.charCodeAt(e.length-1)&&13!==e.charCodeAt(e.length-1)&&(e+="\n"),65279===e.charCodeAt(0)&&(e=e.slice(1)));var n=new ae(e,t),i=e.indexOf("\0");for(-1!==i&&(n.position=i,ce(n,"null byte is not allowed in input")),n.input+="\0";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)ke(n);return n.documents}var xe={loadAll:function(e,t,n){null!==t&&"object"==typeof t&&void 0===n&&(n=t,t=null);var i=Ce(e,n);if("function"!=typeof t)return i;for(var r=0,o=i.length;r<o;r+=1)t(i[r])},load:function(e,t){var n=Ce(e,t);if(0!==n.length){if(1===n.length)return n[0];throw new o("expected a single document in the stream, but found more")}}},Ie=Object.prototype.toString,Se=Object.prototype.hasOwnProperty,Oe=65279,je={0:"\\0",7:"\\a",8:"\\b",9:"\\t",10:"\\n",11:"\\v",12:"\\f",13:"\\r",27:"\\e",34:'\\"',92:"\\\\",133:"\\N",160:"\\_",8232:"\\L",8233:"\\P"},Te=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],Ne=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;function Fe(e){var t,i,r;if(t=e.toString(16).toUpperCase(),e<=255)i="x",r=2;else if(e<=65535)i="u",r=4;else{if(!(e<=4294967295))throw new o("code point within a string may not be greater than 0xFFFFFFFF");i="U",r=8}return"\\"+i+n.repeat("0",r-t.length)+t}function Ee(e){this.schema=e.schema||K,this.indent=Math.max(1,e.indent||2),this.noArrayIndent=e.noArrayIndent||!1,this.skipInvalid=e.skipInvalid||!1,this.flowLevel=n.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=function(e,t){var n,i,r,o,a,l,c;if(null===t)return{};for(n={},r=0,o=(i=Object.keys(t)).length;r<o;r+=1)a=i[r],l=String(t[a]),"!!"===a.slice(0,2)&&(a="tag:yaml.org,2002:"+a.slice(2)),(c=e.compiledTypeMap.fallback[a])&&Se.call(c.styleAliases,l)&&(l=c.styleAliases[l]),n[a]=l;return n}(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.condenseFlow=e.condenseFlow||!1,this.quotingType='"'===e.quotingType?2:1,this.forceQuotes=e.forceQuotes||!1,this.replacer="function"==typeof e.replacer?e.replacer:null,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function Me(e,t){for(var i,r=n.repeat(" ",t),o=0,a=-1,l="",c=e.length;o<c;)-1===(a=e.indexOf("\n",o))?(i=e.slice(o),o=c):(i=e.slice(o,a+1),o=a+1),i.length&&"\n"!==i&&(l+=r),l+=i;return l}function Le(e,t){return"\n"+n.repeat(" ",e.indent*t)}function _e(e){return 32===e||9===e}function De(e){return 32<=e&&e<=126||161<=e&&e<=55295&&8232!==e&&8233!==e||57344<=e&&e<=65533&&e!==Oe||65536<=e&&e<=1114111}function Ue(e){return De(e)&&e!==Oe&&13!==e&&10!==e}function qe(e,t,n){var i=Ue(e),r=i&&!_e(e);return(n?i:i&&44!==e&&91!==e&&93!==e&&123!==e&&125!==e)&&35!==e&&!(58===t&&!r)||Ue(t)&&!_e(t)&&35===e||58===t&&r}function Ye(e,t){var n,i=e.charCodeAt(t);return i>=55296&&i<=56319&&t+1<e.length&&(n=e.charCodeAt(t+1))>=56320&&n<=57343?1024*(i-55296)+n-56320+65536:i}function Re(e){return/^\n* /.test(e)}function Be(e,t,n,i,r,o,a,l){var c,s,u=0,p=null,f=!1,d=!1,h=-1!==i,g=-1,m=De(s=Ye(e,0))&&s!==Oe&&!_e(s)&&45!==s&&63!==s&&58!==s&&44!==s&&91!==s&&93!==s&&123!==s&&125!==s&&35!==s&&38!==s&&42!==s&&33!==s&&124!==s&&61!==s&&62!==s&&39!==s&&34!==s&&37!==s&&64!==s&&96!==s&&function(e){return!_e(e)&&58!==e}(Ye(e,e.length-1));if(t||a)for(c=0;c<e.length;u>=65536?c+=2:c++){if(!De(u=Ye(e,c)))return 5;m=m&&qe(u,p,l),p=u}else{for(c=0;c<e.length;u>=65536?c+=2:c++){if(10===(u=Ye(e,c)))f=!0,h&&(d=d||c-g-1>i&&" "!==e[g+1],g=c);else if(!De(u))return 5;m=m&&qe(u,p,l),p=u}d=d||h&&c-g-1>i&&" "!==e[g+1]}return f||d?n>9&&Re(e)?5:a?2===o?5:2:d?4:3:!m||a||r(e)?2===o?5:2:1}function Ke(e,t,n,i,r){e.dump=function(){if(0===t.length)return 2===e.quotingType?'""':"''";if(!e.noCompatMode&&(-1!==Te.indexOf(t)||Ne.test(t)))return 2===e.quotingType?'"'+t+'"':"'"+t+"'";var a=e.indent*Math.max(1,n),l=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-a),c=i||e.flowLevel>-1&&n>=e.flowLevel;switch(Be(t,c,e.indent,l,(function(t){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n<i;n+=1)if(e.implicitTypes[n].resolve(t))return!0;return!1}(e,t)}),e.quotingType,e.forceQuotes&&!i,r)){case 1:return t;case 2:return"'"+t.replace(/'/g,"''")+"'";case 3:return"|"+Pe(t,e.indent)+We(Me(t,a));case 4:return">"+Pe(t,e.indent)+We(Me(function(e,t){var n,i,r=/(\n+)([^\n]*)/g,o=(l=e.indexOf("\n"),l=-1!==l?l:e.length,r.lastIndex=l,He(e.slice(0,l),t)),a="\n"===e[0]||" "===e[0];var l;for(;i=r.exec(e);){var c=i[1],s=i[2];n=" "===s[0],o+=c+(a||n||""===s?"":"\n")+He(s,t),a=n}return o}(t,l),a));case 5:return'"'+function(e){for(var t,n="",i=0,r=0;r<e.length;i>=65536?r+=2:r++)i=Ye(e,r),!(t=je[i])&&De(i)?(n+=e[r],i>=65536&&(n+=e[r+1])):n+=t||Fe(i);return n}(t)+'"';default:throw new o("impossible error: invalid scalar style")}}()}function Pe(e,t){var n=Re(e)?String(t):"",i="\n"===e[e.length-1];return n+(i&&("\n"===e[e.length-2]||"\n"===e)?"+":i?"":"-")+"\n"}function We(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function He(e,t){if(""===e||" "===e[0])return e;for(var n,i,r=/ [^ ]/g,o=0,a=0,l=0,c="";n=r.exec(e);)(l=n.index)-o>t&&(i=a>o?a:l,c+="\n"+e.slice(o,i),o=i+1),a=l;return c+="\n",e.length-o>t&&a>o?c+=e.slice(o,a)+"\n"+e.slice(a+1):c+=e.slice(o),c.slice(1)}function $e(e,t,n,i){var r,o,a,l="",c=e.tag;for(r=0,o=n.length;r<o;r+=1)a=n[r],e.replacer&&(a=e.replacer.call(n,String(r),a)),(Ve(e,t+1,a,!0,!0,!1,!0)||void 0===a&&Ve(e,t+1,null,!0,!0,!1,!0))&&(i&&""===l||(l+=Le(e,t)),e.dump&&10===e.dump.charCodeAt(0)?l+="-":l+="- ",l+=e.dump);e.tag=c,e.dump=l||"[]"}function Ge(e,t,n){var i,r,a,l,c,s;for(a=0,l=(r=n?e.explicitTypes:e.implicitTypes).length;a<l;a+=1)if(((c=r[a]).instanceOf||c.predicate)&&(!c.instanceOf||"object"==typeof t&&t instanceof c.instanceOf)&&(!c.predicate||c.predicate(t))){if(n?c.multi&&c.representName?e.tag=c.representName(t):e.tag=c.tag:e.tag="?",c.represent){if(s=e.styleMap[c.tag]||c.defaultStyle,"[object Function]"===Ie.call(c.represent))i=c.represent(t,s);else{if(!Se.call(c.represent,s))throw new o("!<"+c.tag+'> tag resolver accepts not "'+s+'" style');i=c.represent[s](t,s)}e.dump=i}return!0}return!1}function Ve(e,t,n,i,r,a,l){e.tag=null,e.dump=n,Ge(e,n,!1)||Ge(e,n,!0);var c,s=Ie.call(e.dump),u=i;i&&(i=e.flowLevel<0||e.flowLevel>t);var p,f,d="[object Object]"===s||"[object Array]"===s;if(d&&(f=-1!==(p=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||f||2!==e.indent&&t>0)&&(r=!1),f&&e.usedDuplicates[p])e.dump="*ref_"+p;else{if(d&&f&&!e.usedDuplicates[p]&&(e.usedDuplicates[p]=!0),"[object Object]"===s)i&&0!==Object.keys(e.dump).length?(!function(e,t,n,i){var r,a,l,c,s,u,p="",f=e.tag,d=Object.keys(n);if(!0===e.sortKeys)d.sort();else if("function"==typeof e.sortKeys)d.sort(e.sortKeys);else if(e.sortKeys)throw new o("sortKeys must be a boolean or a function");for(r=0,a=d.length;r<a;r+=1)u="",i&&""===p||(u+=Le(e,t)),c=n[l=d[r]],e.replacer&&(c=e.replacer.call(n,l,c)),Ve(e,t+1,l,!0,!0,!0)&&((s=null!==e.tag&&"?"!==e.tag||e.dump&&e.dump.length>1024)&&(e.dump&&10===e.dump.charCodeAt(0)?u+="?":u+="? "),u+=e.dump,s&&(u+=Le(e,t)),Ve(e,t+1,c,!0,s)&&(e.dump&&10===e.dump.charCodeAt(0)?u+=":":u+=": ",p+=u+=e.dump));e.tag=f,e.dump=p||"{}"}(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a,l,c="",s=e.tag,u=Object.keys(n);for(i=0,r=u.length;i<r;i+=1)l="",""!==c&&(l+=", "),e.condenseFlow&&(l+='"'),a=n[o=u[i]],e.replacer&&(a=e.replacer.call(n,o,a)),Ve(e,t,o,!1,!1)&&(e.dump.length>1024&&(l+="? "),l+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),Ve(e,t,a,!1,!1)&&(c+=l+=e.dump));e.tag=s,e.dump="{"+c+"}"}(e,t,e.dump),f&&(e.dump="&ref_"+p+" "+e.dump));else if("[object Array]"===s)i&&0!==e.dump.length?(e.noArrayIndent&&!l&&t>0?$e(e,t-1,e.dump,r):$e(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a="",l=e.tag;for(i=0,r=n.length;i<r;i+=1)o=n[i],e.replacer&&(o=e.replacer.call(n,String(i),o)),(Ve(e,t,o,!1,!1)||void 0===o&&Ve(e,t,null,!1,!1))&&(""!==a&&(a+=","+(e.condenseFlow?"":" ")),a+=e.dump);e.tag=l,e.dump="["+a+"]"}(e,t,e.dump),f&&(e.dump="&ref_"+p+" "+e.dump));else{if("[object String]"!==s){if("[object Undefined]"===s)return!1;if(e.skipInvalid)return!1;throw new o("unacceptable kind of an object to dump "+s)}"?"!==e.tag&&Ke(e,e.dump,t,a,u)}null!==e.tag&&"?"!==e.tag&&(c=encodeURI("!"===e.tag[0]?e.tag.slice(1):e.tag).replace(/!/g,"%21"),c="!"===e.tag[0]?"!"+c:"tag:yaml.org,2002:"===c.slice(0,18)?"!!"+c.slice(18):"!<"+c+">",e.dump=c+" "+e.dump)}return!0}function Ze(e,t){var n,i,r=[],o=[];for(Je(e,r,o),n=0,i=o.length;n<i;n+=1)t.duplicates.push(r[o[n]]);t.usedDuplicates=new Array(i)}function Je(e,t,n){var i,r,o;if(null!==e&&"object"==typeof e)if(-1!==(r=t.indexOf(e)))-1===n.indexOf(r)&&n.push(r);else if(t.push(e),Array.isArray(e))for(r=0,o=e.length;r<o;r+=1)Je(e[r],t,n);else for(r=0,o=(i=Object.keys(e)).length;r<o;r+=1)Je(e[i[r]],t,n)}function Qe(e,t){return function(){throw new Error("Function yaml."+e+" is removed in js-yaml 4. Use yaml."+t+" instead, which is now safe by default.")}}var ze=p,Xe=h,et=b,tt=O,nt=j,it=K,rt=xe.load,ot=xe.loadAll,at={dump:function(e,t){var n=new Ee(t=t||{});n.noRefs||Ze(e,n);var i=e;return n.replacer&&(i=n.replacer.call({"":i},"",i)),Ve(n,0,i,!0,!0)?n.dump+"\n":""}}.dump,lt=o,ct={binary:L,float:S,map:y,null:A,pairs:Y,set:B,timestamp:F,bool:v,int:C,merge:E,omap:U,seq:m,str:g},st=Qe("safeLoad","load"),ut=Qe("safeLoadAll","loadAll"),pt=Qe("safeDump","dump"),ft={Type:ze,Schema:Xe,FAILSAFE_SCHEMA:et,JSON_SCHEMA:tt,CORE_SCHEMA:nt,DEFAULT_SCHEMA:it,load:rt,loadAll:ot,dump:at,YAMLException:lt,types:ct,safeLoad:st,safeLoadAll:ut,safeDump:pt};e.CORE_SCHEMA=nt,e.DEFAULT_SCHEMA=it,e.FAILSAFE_SCHEMA=et,e.JSON_SCHEMA=tt,e.Schema=Xe,e.Type=ze,e.YAMLException=lt,e.default=ft,e.dump=at,e.load=rt,e.loadAll=ot,e.safeDump=pt,e.safeLoad=st,e.safeLoadAll=ut,e.types=ct,Object.defineProperty(e,"__esModule",{value:!0})}));
/**
* ZDDC — shared naming convention library
*
* Canonical implementation of all ZDDC filename, folder name, tracking number,
* revision, and status logic. Included in every tool's build via shared/zddc.js.
*
* Exposed as window.zddc (plain global) so it works with every tool's module
* pattern (archive globals, classifier IIFE, transmittal IIFE, mdedit globals).
*
* Public API
* ----------
* zddc.parseFilename(str) → ParsedFile | null
* zddc.parseFolder(str) → ParsedFolder | null
* zddc.parseRevision(str) → ParsedRevision
* zddc.formatFilename(parts) → string
* zddc.formatFolder(parts) → string
* zddc.compareRevisions(a, b) → number (-1 | 0 | 1)
* zddc.isValidStatus(str) → boolean
* zddc.STATUSES → string[]
*
* ParsedFile { trackingNumber, revision, status, title, extension }
* ParsedFolder { date, trackingNumber, status, title }
* ParsedRevision { base, modifier, modifierType, modifierNumber, isDraft, full }
*/
(function (root) {
'use strict';
// ── Valid status codes ───────────────────────────────────────────────────
/**
* Complete list of valid ZDDC document status codes.
* '---' denotes an unknown or not-yet-assigned status.
*/
var STATUSES = [
'---',
'IFA', 'IFB', 'IFC', 'IFD', 'IFI', 'IFP', 'IFR', 'IFU',
'REC',
'RSA', 'RSB', 'RSC', 'RSD', 'RSI',
];
var STATUS_SET = {};
for (var _i = 0; _i < STATUSES.length; _i++) {
STATUS_SET[STATUSES[_i]] = true;
}
function isValidStatus(str) {
return !!STATUS_SET[str];
}
// ── Filename parsing ─────────────────────────────────────────────────────
/**
* Canonical file regex.
* Matches: TRACKING_REVISION (STATUS) - TITLE.EXT
*
* Tracking number: no underscores, no whitespace.
* Revision: no whitespace, no parentheses.
* Status: anything inside parentheses (validated separately).
* Title: everything up to the last dot.
* Extension: after the last dot (lowercased by parseFilename).
*/
var FILE_RE = /^([^_\s]+)_([^\s()_]+)\s*\(([^)]+)\)\s*-\s*(\S.*\S|\S)\.\s*([^\s.]+)$/;
/**
* Parse a ZDDC filename.
*
* @param {string} filename
* @returns {{ trackingNumber: string, revision: string, status: string,
* title: string, extension: string, valid: boolean } | null}
* null only if filename is falsy.
* `valid` is true when all fields matched the ZDDC pattern.
*/
function parseFilename(filename) {
if (!filename) { return null; }
var match = filename.match(FILE_RE);
if (!match) {
var lastDot = filename.lastIndexOf('.');
return {
trackingNumber: '',
revision: '',
status: '',
title: lastDot > 0 ? filename.substring(0, lastDot) : filename,
extension: lastDot > 0 ? filename.substring(lastDot + 1).toLowerCase() : '',
valid: false,
};
}
return {
trackingNumber: match[1].trim(),
revision: match[2].trim(),
status: match[3].trim(),
title: match[4].trim(),
extension: match[5].toLowerCase(),
valid: true,
};
}
// ── Folder name parsing ──────────────────────────────────────────────────
/**
* Transmittal folder regex.
* Matches: YYYY-MM-DD_TRACKING (STATUS) - TITLE
*/
var FOLDER_RE = /^(\d{4}-\d{2}-\d{2})_([^_\s(]+)\s*\(([^)]+)\)\s*-\s*(.+)$/;
/**
* Parse a ZDDC transmittal folder name.
*
* @param {string} foldername
* @returns {{ date: string, trackingNumber: string, status: string,
* title: string, valid: boolean } | null}
* null only if foldername is falsy.
*/
function parseFolder(foldername) {
if (!foldername) { return null; }
var match = foldername.match(FOLDER_RE);
if (!match) {
return {
date: '',
trackingNumber: '',
status: '',
title: foldername,
valid: false,
};
}
return {
date: match[1],
trackingNumber: match[2].trim(),
status: match[3].trim(),
title: match[4].trim(),
valid: true,
};
}
// ── Revision parsing ─────────────────────────────────────────────────────
/**
* Modifier sub-regex: +LETTER DIGITS e.g. +C1, +B2, +N1, +Q1
* The draft prefix (~) may appear inside the modifier: A+~C1
*/
var MODIFIER_RE = /^\+(~?)([A-Za-z])(\d+)$/;
/**
* Parse a ZDDC revision string.
*
* Revision grammar:
* revision = ['~'] base ['+' ['~'] modifier_letter modifier_number]
* base = letter(s) | digit(s) | date(YYYY-MM-DD)
* modifier = letter + digits e.g. C1, B2, N1, Q1
*
* @param {string} revision
* @returns {{
* base: string,
* modifier: string, full modifier string e.g. '+C1', '' if none
* modifierType: string, modifier letter e.g. 'C', '' if none
* modifierNumber: number, modifier number e.g. 1, 0 if none
* modifierIsDraft: boolean,
* isDraft: boolean, true if base revision starts with ~
* full: string, original input
* }}
*/
function parseRevision(revision) {
var raw = (revision || '').toString();
// Split on '+' to separate base from optional modifier
var plusIdx = raw.indexOf('+');
var basePart = plusIdx === -1 ? raw : raw.substring(0, plusIdx);
var modifierPart = plusIdx === -1 ? '' : raw.substring(plusIdx);
// Draft flag on the base part
var isDraft = basePart.startsWith('~');
var base = isDraft ? basePart.substring(1) : basePart;
// Parse modifier
var modifier = '';
var modifierType = '';
var modifierNumber = 0;
var modifierIsDraft = false;
if (modifierPart) {
var mMatch = modifierPart.match(MODIFIER_RE);
if (mMatch) {
modifierIsDraft = mMatch[1] === '~';
modifierType = mMatch[2].toUpperCase();
modifierNumber = parseInt(mMatch[3], 10);
modifier = modifierPart;
} else {
// Unrecognised modifier — preserve as-is
modifier = modifierPart;
}
}
return {
base: base,
modifier: modifier,
modifierType: modifierType,
modifierNumber: modifierNumber,
modifierIsDraft: modifierIsDraft,
isDraft: isDraft,
full: raw,
};
}
// ── Revision comparison ──────────────────────────────────────────────────
/**
* Classify a base revision string into a sort tier:
* 0 = date (YYYY-MM-DD)
* 1 = letter(s) A, B, AA …
* 2 = number(s) 0, 1, 2, 1.5 …
* 3 = other
*/
function _baseTier(base) {
if (/^\d{4}-\d{2}-\d{2}$/.test(base)) { return 0; }
if (/^[A-Za-z]+$/.test(base)) { return 1; }
if (/^\d+(\.\d+)?$/.test(base)) { return 2; }
return 3;
}
/**
* Compare two base revision strings.
* Sort order: dates < letters < numbers < other.
*/
function _compareBase(a, b) {
var ta = _baseTier(a);
var tb = _baseTier(b);
if (ta !== tb) { return ta - tb; }
if (ta === 0) { return a < b ? -1 : a > b ? 1 : 0; } // date lexicographic = chronological
if (ta === 1) { return a.toUpperCase() < b.toUpperCase() ? -1 : a.toUpperCase() > b.toUpperCase() ? 1 : 0; }
if (ta === 2) { return parseFloat(a) - parseFloat(b); }
return a.localeCompare(b);
}
/**
* Compare two ZDDC revision strings for sort ordering.
*
* Canonical order (ascending = older → newer):
* ~A < A < A+B1 < A+C1 < A+~C2 < A+C2 < A+N1 < A+Q1
* < ~B < B < … < 0 < 1 < 2
*
* Rules:
* 1. Compare base revisions first (dates < letters < numbers).
* 2. For equal bases, draft (isDraft=true) comes before final.
* 3. For equal base+draft, no-modifier < has-modifier.
* 4. For equal base+draft+modifier presence:
* a. modifier draft comes before modifier final (modifierIsDraft).
* b. Sort modifier by type letter then by number.
*
* @param {string} a
* @param {string} b
* @returns {number} negative if a < b, 0 if equal, positive if a > b
*/
function compareRevisions(a, b) {
var pa = parseRevision(a);
var pb = parseRevision(b);
// 1. Base revision
var baseCmp = _compareBase(pa.base, pb.base);
if (baseCmp !== 0) { return baseCmp; }
// 2. Draft before final (for same base)
if (pa.isDraft !== pb.isDraft) { return pa.isDraft ? -1 : 1; }
// 3. No modifier before any modifier
var aHasMod = pa.modifier !== '';
var bHasMod = pb.modifier !== '';
if (aHasMod !== bHasMod) { return aHasMod ? 1 : -1; }
if (!aHasMod) { return 0; } // both have no modifier
// 4. Compare modifiers: type → number → draft (draft is a tie-breaker only)
// 4a. Modifier type letter (B < C < N < Q …)
if (pa.modifierType !== pb.modifierType) {
return pa.modifierType < pb.modifierType ? -1 : 1;
}
// 4b. Modifier number (1 < 2 …)
if (pa.modifierNumber !== pb.modifierNumber) {
return pa.modifierNumber - pb.modifierNumber;
}
// 4c. Draft of a modifier comes before the final modifier (same type+number)
if (pa.modifierIsDraft !== pb.modifierIsDraft) {
return pa.modifierIsDraft ? -1 : 1;
}
return 0;
}
// ── Filename / folder formatting ─────────────────────────────────────────
/**
* Build a ZDDC filename from its components.
*
* @param {{ trackingNumber: string, revision: string, status: string,
* title: string, extension: string }} parts
* @returns {string} e.g. "123456-EL-SPC-2623_A (IFR) - Specification.pdf"
*/
function formatFilename(parts) {
var tn = (parts.trackingNumber || '').trim();
var rev = (parts.revision || '').trim();
var st = (parts.status || '').trim();
var ttl = (parts.title || '').trim();
var ext = (parts.extension || '').replace(/^\./, '');
if (!tn || !rev || !st || !ttl) { return ''; }
var name = tn + '_' + rev + ' (' + st + ') - ' + ttl;
return ext ? name + '.' + ext : name;
}
/**
* Build a ZDDC transmittal folder name from its components.
*
* @param {{ date: string, trackingNumber: string, status: string,
* title: string }} parts
* @returns {string} e.g. "2025-10-31_123456-EM-SUB-0001 (IFR) - Title"
*/
function formatFolder(parts) {
var dt = (parts.date || '').trim();
var tn = (parts.trackingNumber || '').trim();
var st = (parts.status || '').trim();
var ttl = (parts.title || '').trim();
if (!dt || !tn || !st || !ttl) { return ''; }
return dt + '_' + tn + ' (' + st + ') - ' + ttl;
}
// ── Filename / extension splitting ───────────────────────────────────────
/**
* Split a filename into its base name and extension (no leading dot).
* Treats leading dot ('.gitignore') as no extension.
*
* @param {string} filename
* @returns {{ name: string, extension: string }}
*/
function splitExtension(filename) {
if (!filename) { return { name: '', extension: '' }; }
var lastDot = filename.lastIndexOf('.');
if (lastDot <= 0) { return { name: filename, extension: '' }; }
return {
name: filename.substring(0, lastDot),
extension: filename.substring(lastDot + 1).toLowerCase(),
};
}
/**
* Join a base name and extension. Tolerant of either form ('pdf' or '.pdf').
* Returns just the name when extension is empty.
*/
function joinExtension(name, extension) {
var ext = (extension || '').replace(/^\./, '');
return ext ? name + '.' + ext : name;
}
// ── Public API ───────────────────────────────────────────────────────────
root.zddc = {
STATUSES: STATUSES,
isValidStatus: isValidStatus,
parseFilename: parseFilename,
parseFolder: parseFolder,
parseRevision: parseRevision,
formatFilename: formatFilename,
formatFolder: formatFolder,
compareRevisions: compareRevisions,
splitExtension: splitExtension,
joinExtension: joinExtension,
};
}(typeof window !== 'undefined' ? window : this));
// shared/zddc-source.js — source abstraction for tools that handle
// directory trees (classifier, mdedit, transmittal, browse, archive).
//
// Two backends:
//
// 1. Local — wraps a real FileSystemDirectoryHandle from the
// File System Access API. Reads + writes go through the
// FS Access API directly.
//
// 2. HTTP — talks to zddc-server's directory listing JSON
// (Accept: application/json) for reads and the file API
// (PUT/DELETE/POST X-ZDDC-Op) for writes. Implements a
// polyfill of the FS Access API surface area the tools
// use (kind, name, values(), getFileHandle, getDirectoryHandle,
// removeEntry, getFile, createWritable, queryPermission /
// requestPermission) so existing code works unchanged.
//
// The polyfill makes auto-load possible: when zddc-server serves
// a tool at /<dir>/<tool>.html, the tool detects HTTP mode at
// startup, builds an HttpDirectoryHandle for the tool's containing
// directory, and hands it to the existing openDirectory(handle)
// flow without ever showing the file picker.
//
// Renames inside a tool today are typically done as
// "write new + remove old". With HTTP-backed handles this becomes
// PUT + DELETE — non-atomic. Tools that prefer the atomic server
// MOVE should call window.zddc.source.moveFile(srcUrl, dstUrl)
// directly instead of going through the polyfill.
(function () {
'use strict';
if (!window.zddc) window.zddc = {};
var FA = window.FileSystemDirectoryHandle || null;
// -----------------------------------------------------------------
// HTTP file API helpers
// -----------------------------------------------------------------
function joinUrl(base, name, isDir) {
if (!base.endsWith('/')) base = base + '/';
return base + encodeURIComponent(name) + (isDir ? '/' : '');
}
// Server returns directory entries with a trailing "/" on names.
// Strip it for the FS Access API name surface.
function stripSlash(name) {
return name.endsWith('/') ? name.slice(0, -1) : name;
}
async function httpListing(url) {
var resp = await fetch(url, {
headers: { 'Accept': 'application/json' },
credentials: 'same-origin'
});
if (!resp.ok) {
var err = new Error('listing ' + url + ': HTTP ' + resp.status);
err.status = resp.status;
throw err;
}
var data = await resp.json();
if (!Array.isArray(data)) {
throw new Error('listing ' + url + ': non-array body');
}
return data;
}
async function httpExists(url) {
try {
var r = await fetch(url, { method: 'HEAD', credentials: 'same-origin' });
return r.ok;
} catch (_) {
return false;
}
}
// -----------------------------------------------------------------
// HttpFileHandle — FileSystemFileHandle polyfill
// -----------------------------------------------------------------
function makeFile(blob, name, modTime) {
return new File([blob], name, {
type: blob.type,
lastModified: modTime ? modTime.getTime() : Date.now()
});
}
function HttpFileHandle(url, name, size, modTime) {
this.kind = 'file';
this.name = name;
this._url = url;
this._size = size || 0;
this._modTime = modTime || null;
this._etag = null;
}
HttpFileHandle.prototype.getFile = async function () {
var resp = await fetch(this._url, { credentials: 'same-origin' });
if (!resp.ok) {
throw new Error('GET ' + this._url + ': ' + resp.status);
}
var etag = resp.headers.get('ETag');
if (etag) this._etag = etag.replace(/"/g, '');
var lm = resp.headers.get('Last-Modified');
var modTime = lm ? new Date(lm) : this._modTime;
var blob = await resp.blob();
return makeFile(blob, this.name, modTime);
};
HttpFileHandle.prototype.createWritable = async function () {
var chunks = [];
var handle = this;
return {
async write(data) {
if (data == null) return;
if (typeof data === 'object' && data && 'type' in data && data.type === 'write') {
chunks.push(data.data);
return;
}
if (typeof data === 'object' && data && 'type' in data) {
// seek/truncate not supported by HTTP backend
throw new Error('HttpFileHandle write op not supported: ' + data.type);
}
chunks.push(data);
},
async close() {
var blob = new Blob(chunks);
var resp = await fetch(handle._url, {
method: 'PUT',
body: blob,
credentials: 'same-origin'
});
if (!resp.ok) {
var body = '';
try { body = await resp.text(); } catch (_) { /* ignore */ }
throw new Error('PUT ' + handle._url + ': ' + resp.status + ' ' + body);
}
var et = resp.headers.get('ETag');
if (et) handle._etag = et.replace(/"/g, '');
handle._size = blob.size;
},
async abort() { chunks = []; }
};
};
HttpFileHandle.prototype.queryPermission = async function () { return 'granted'; };
HttpFileHandle.prototype.requestPermission = async function () { return 'granted'; };
HttpFileHandle.prototype.isHttp = true;
HttpFileHandle.prototype.url = function () { return this._url; };
// -----------------------------------------------------------------
// HttpDirectoryHandle — FileSystemDirectoryHandle polyfill
// -----------------------------------------------------------------
function HttpDirectoryHandle(url, name) {
this.kind = 'directory';
if (!url.endsWith('/')) url = url + '/';
this._url = url;
this.name = name || guessNameFromUrl(url);
}
function guessNameFromUrl(url) {
var u = url.replace(/\/+$/, '');
var slash = u.lastIndexOf('/');
return slash >= 0 ? decodeURIComponent(u.substring(slash + 1)) : u;
}
HttpDirectoryHandle.prototype.values = function () {
var url = this._url;
return (async function* () {
var entries;
try {
entries = await httpListing(url);
} catch (e) {
return;
}
for (var i = 0; i < entries.length; i++) {
var e = entries[i];
var rawName = stripSlash(e.name);
var childUrl = joinUrl(url, rawName, e.is_dir);
if (e.is_dir) {
yield new HttpDirectoryHandle(childUrl, rawName);
} else {
var modTime = e.mod_time ? new Date(e.mod_time) : null;
yield new HttpFileHandle(childUrl, rawName, e.size || 0, modTime);
}
}
})();
};
HttpDirectoryHandle.prototype.entries = function () {
var iter = this.values();
return (async function* () {
for (;;) {
var step = await iter.next();
if (step.done) return;
yield [step.value.name, step.value];
}
})();
};
HttpDirectoryHandle.prototype.keys = function () {
var iter = this.values();
return (async function* () {
for (;;) {
var step = await iter.next();
if (step.done) return;
yield step.value.name;
}
})();
};
HttpDirectoryHandle.prototype.getFileHandle = async function (name, opts) {
opts = opts || {};
var url = joinUrl(this._url, name, false);
var exists = await httpExists(url);
if (!exists && !opts.create) {
var err = new Error('NotFoundError: ' + name);
err.name = 'NotFoundError';
throw err;
}
return new HttpFileHandle(url, name, 0, null);
};
HttpDirectoryHandle.prototype.getDirectoryHandle = async function (name, opts) {
opts = opts || {};
var url = joinUrl(this._url, name, true);
if (opts.create) {
var resp = await fetch(url, {
method: 'POST',
headers: { 'X-ZDDC-Op': 'mkdir' },
credentials: 'same-origin'
});
if (!resp.ok && resp.status !== 200 && resp.status !== 201) {
throw new Error('mkdir ' + url + ': ' + resp.status);
}
}
return new HttpDirectoryHandle(url, name);
};
HttpDirectoryHandle.prototype.removeEntry = async function (name, opts) {
opts = opts || {};
// Probe listing to discover whether name is a file or directory.
var entries;
try {
entries = await httpListing(this._url);
} catch (e) {
throw new Error('removeEntry probe failed: ' + e.message);
}
var match = null;
for (var i = 0; i < entries.length; i++) {
if (stripSlash(entries[i].name) === name) {
match = entries[i];
break;
}
}
if (!match) {
var err = new Error('NotFoundError: ' + name);
err.name = 'NotFoundError';
throw err;
}
if (match.is_dir && !opts.recursive) {
// Server doesn't expose a recursive-delete endpoint yet,
// and FS Access API requires recursive=true to remove a
// non-empty directory anyway. Reject explicitly so the
// caller doesn't silently leave a stale tree behind.
var derr = new Error('Removing directories over HTTP is not supported');
derr.name = 'InvalidStateError';
throw derr;
}
var url = joinUrl(this._url, name, match.is_dir);
var resp = await fetch(url, { method: 'DELETE', credentials: 'same-origin' });
if (!resp.ok && resp.status !== 204) {
throw new Error('DELETE ' + url + ': ' + resp.status);
}
};
HttpDirectoryHandle.prototype.queryPermission = async function () { return 'granted'; };
HttpDirectoryHandle.prototype.requestPermission = async function () { return 'granted'; };
HttpDirectoryHandle.prototype.isHttp = true;
HttpDirectoryHandle.prototype.url = function () { return this._url; };
// -----------------------------------------------------------------
// Top-level helpers
// -----------------------------------------------------------------
// Strip a trailing tool .html (e.g. classifier.html) from a path
// to land on the "directory the tool was opened in".
function pathToDir(pathname) {
if (!pathname) return '/';
if (pathname.endsWith('/')) return pathname;
var slash = pathname.lastIndexOf('/');
return slash >= 0 ? pathname.substring(0, slash + 1) : '/';
}
// Probe the server-mode root for the current page. Returns:
//
// { handle: HttpDirectoryHandle, status: 200 } — server reachable, listing returned
// { handle: null, status: 403 } — server reachable but listing forbidden
// { handle: null, status: 0 } — not http(s), or server unreachable / non-JSON
//
// Tools that auto-load on startup distinguish 403 (show "no
// permission to list this directory" message) from 0 (fall back
// to local-mode welcome screen).
//
// Tool init pattern:
// if (location.protocol !== 'file:') {
// const r = await zddc.source.detectServerRoot();
// if (r.handle) await openDirectory(r.handle);
// else if (r.status === 403) showNoPermissionMessage();
// else showWelcome();
// } else { showWelcome(); }
async function detectServerRoot() {
if (typeof location === 'undefined') {
return { handle: null, status: 0 };
}
if (location.protocol !== 'http:' && location.protocol !== 'https:') {
return { handle: null, status: 0 };
}
var dirPath = pathToDir(location.pathname);
var url = location.origin + dirPath;
try {
await httpListing(url);
} catch (e) {
if (e && e.status === 403) {
return { handle: null, status: 403 };
}
return { handle: null, status: 0 };
}
return {
handle: new HttpDirectoryHandle(url, guessNameFromUrl(url)),
status: 200,
};
}
// Atomic file move. Path arguments are absolute URL paths
// (starting with /). Honors the file API's POST /op=move
// contract. Returns the new ETag.
async function moveFile(srcUrlPath, dstUrlPath, opts) {
opts = opts || {};
var headers = {
'X-ZDDC-Op': 'move',
'X-ZDDC-Destination': dstUrlPath
};
if (opts.ifMatch) headers['If-Match'] = opts.ifMatch;
var resp = await fetch(srcUrlPath, {
method: 'POST',
headers: headers,
credentials: 'same-origin'
});
if (!resp.ok) {
var body = '';
try { body = await resp.text(); } catch (_) { /* ignore */ }
throw new Error('move ' + srcUrlPath + ' → ' + dstUrlPath + ': ' + resp.status + ' ' + body);
}
var et = resp.headers.get('ETag');
return et ? et.replace(/"/g, '') : null;
}
// Detect at construction time whether a directory handle is the
// HTTP polyfill or a real FS Access API handle. Useful for tools
// that want to take the optimized path (e.g. atomic moveFile)
// when in HTTP mode rather than the FS-API copy+remove fallback.
function isHttpHandle(handle) {
return !!(handle && handle.isHttp === true);
}
window.zddc.source = {
HttpDirectoryHandle: HttpDirectoryHandle,
HttpFileHandle: HttpFileHandle,
detectServerRoot: detectServerRoot,
moveFile: moveFile,
isHttpHandle: isHttpHandle,
// Lower-level helpers exposed for tools that want to call the
// server directly without going through the polyfill.
httpListing: httpListing,
joinUrl: joinUrl,
stripSlash: stripSlash
};
})();
/**
* ZDDC shared theme toggle — light / dark / auto.
* Persists choice to localStorage under 'zddc-theme'.
* Works with all four tools regardless of their module pattern.
* Expects: #theme-btn in the DOM (optional — skips gracefully if absent).
*
* Theme cycle: auto → light → dark → auto …
* 'auto' honours the OS prefers-color-scheme media query (CSS handles it).
* 'light' sets data-theme="light" on <html> (overrides dark media query).
* 'dark' sets data-theme="dark" on <html>.
*/
(function () {
'use strict';
var STORAGE_KEY = 'zddc-theme';
var THEMES = ['auto', 'light', 'dark'];
var LABELS = {
auto: '◐',
light: '☀',
dark: '☾'
};
var TITLES = {
auto: 'Theme: auto (follows OS)',
light: 'Theme: light',
dark: 'Theme: dark'
};
function load() {
var stored = localStorage.getItem(STORAGE_KEY);
return THEMES.indexOf(stored) !== -1 ? stored : 'auto';
}
function apply(theme) {
if (theme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
} else if (theme === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
} else {
document.documentElement.removeAttribute('data-theme');
}
}
function save(theme) {
try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) {}
}
function updateButton(btn, theme) {
btn.textContent = LABELS[theme];
btn.title = TITLES[theme];
btn.setAttribute('aria-label', TITLES[theme]);
}
function next(theme) {
return THEMES[(THEMES.indexOf(theme) + 1) % THEMES.length];
}
function init() {
var current = load();
apply(current);
var btn = document.getElementById('theme-btn');
if (!btn) { return; }
updateButton(btn, current);
btn.addEventListener('click', function () {
current = next(current);
apply(current);
save(current);
updateButton(btn, current);
});
}
/* Apply theme immediately (before DOM ready) to avoid flash */
apply(load());
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
}());
/**
* ZDDC shared help panel — open/close logic.
* Works with all four tools regardless of their module pattern.
* Expects: #help-btn, #help-panel, #help-panel-close in the DOM.
*/
(function () {
'use strict';
function init() {
var helpBtn = document.getElementById('help-btn');
var panel = document.getElementById('help-panel');
var closeBtn = document.getElementById('help-panel-close');
if (!helpBtn || !panel) { return; }
function isOpen() { return !panel.hidden; }
function openPanel() {
panel.hidden = false;
document.body.classList.add('help-open');
}
function closePanel() {
panel.hidden = true;
document.body.classList.remove('help-open');
}
helpBtn.addEventListener('click', function () {
if (isOpen()) { closePanel(); } else { openPanel(); }
});
if (closeBtn) {
closeBtn.addEventListener('click', closePanel);
}
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && isOpen()) { closePanel(); }
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
}());
(function (global) {
'use strict';
if (global.tablesApp) {
return;
}
global.tablesApp = {
context: null,
state: {
rows: [],
sort: [],
filter: {}
},
modules: {}
};
})(window);
(function (app) {
'use strict';
// load() resolves to the table context the rest of the app renders:
// { title?, description?, columns, rows, defaults? }
//
// Two paths:
//
// 1. Inline JSON (test seam, and also any host that wants to
// pre-render a context server-side): if #table-context parses
// to a non-empty object, return it as-is.
//
// 2. File-backed walk (the real-world path served by zddc-server):
// fetch <dir>/.zddc, find tables[<name>], fetch the *.table.yaml
// spec, list <dir>/<name>/*.yaml row files, parse each, and
// assemble the same shape.
//
// file:// mode without a directory handle is unsupported in v1 — the
// walk only runs against http(s). file:// users must either inject an
// inline context (tests) or open the page through zddc-server.
async function load() {
const inline = readInlineContext();
if (inline && Object.keys(inline).length > 0) {
return inline;
}
if (typeof location !== 'undefined' &&
(location.protocol === 'http:' || location.protocol === 'https:')) {
try {
const walked = await walkServer();
if (walked) {
return walked;
}
} catch (err) {
console.error('[tables] failed to load table from server', err);
showStatus('Could not load table: ' + (err && err.message ? err.message : err));
}
}
return {};
}
function readInlineContext() {
const el = document.getElementById('table-context');
if (!el) {
return null;
}
try {
return JSON.parse(el.textContent || '{}');
} catch (err) {
console.error('[tables] failed to parse #table-context', err);
return null;
}
}
function showStatus(msg) {
const el = document.getElementById('table-status');
if (!el) return;
el.textContent = msg;
el.hidden = false;
}
async function walkServer() {
const source = window.zddc && window.zddc.source;
if (!source) {
throw new Error('zddc.source not available');
}
const tableName = tableNameFromUrl(location.pathname);
if (!tableName) {
throw new Error('Unrecognized table URL: ' + location.pathname);
}
const probe = await source.detectServerRoot();
if (!probe.handle) {
throw new Error(probe.status === 403
? 'No permission to list this directory'
: 'Server unreachable');
}
const dir = probe.handle;
const zddcDoc = await readYaml(dir, '.zddc');
const tablesMap = (zddcDoc && zddcDoc.tables) || {};
const specRel = tablesMap[tableName];
if (!specRel) {
throw new Error('No tables.' + tableName + ' declared in .zddc');
}
const spec = await readYaml(dir, stripDotSlash(specRel));
if (!spec || !Array.isArray(spec.columns)) {
throw new Error('Spec ' + specRel + ' missing columns[]');
}
const rowsRel = stripDotSlash(spec.rows || ('./' + tableName));
const rowsDir = await resolveDirectory(dir, rowsRel);
const rows = await readRows(rowsDir, rowsRel, tableName);
return {
title: spec.title,
description: spec.description,
columns: spec.columns,
defaults: spec.defaults,
rows: rows
};
}
function tableNameFromUrl(pathname) {
const m = String(pathname || '').match(/\/([^\/]+)\.table\.html$/);
return m ? m[1] : null;
}
function stripDotSlash(p) {
let out = String(p || '');
if (out.startsWith('./')) out = out.slice(2);
if (out.startsWith('/')) out = out.slice(1);
if (out.endsWith('/')) out = out.slice(0, -1);
return out;
}
async function readYaml(dir, relPath) {
const fileHandle = await resolveFile(dir, relPath);
const file = await fileHandle.getFile();
const text = await file.text();
if (!window.jsyaml) {
throw new Error('js-yaml not loaded');
}
return window.jsyaml.load(text);
}
// Walk a "/"-separated relative path under dir, returning the
// FileSystemFileHandle (or HttpFileHandle) at the leaf.
async function resolveFile(dir, relPath) {
const parts = relPath.split('/').filter(Boolean);
if (parts.length === 0) {
throw new Error('Empty file path');
}
const fileName = parts.pop();
let cur = dir;
for (let i = 0; i < parts.length; i++) {
cur = await cur.getDirectoryHandle(parts[i]);
}
return cur.getFileHandle(fileName);
}
async function resolveDirectory(dir, relPath) {
const parts = relPath.split('/').filter(Boolean);
let cur = dir;
for (let i = 0; i < parts.length; i++) {
cur = await cur.getDirectoryHandle(parts[i]);
}
return cur;
}
async function readRows(rowsDir, rowsRel, tableName) {
const rows = [];
for await (const entry of rowsDir.values()) {
if (entry.kind !== 'file') continue;
if (!entry.name.endsWith('.yaml')) continue;
try {
const file = await (await rowsDir.getFileHandle(entry.name)).getFile();
const data = window.jsyaml.load(await file.text());
rows.push({
url: rowEditUrl(rowsRel, tableName, entry.name),
data: data || {},
editable: true
});
} catch (err) {
console.warn('[tables] skipping unparseable row', entry.name, err);
}
}
return rows;
}
// Build the form-handler URL for editing one row. The page is at
// <dir>/<tableName>.table.html; the row file lives at
// <dir>/<rowsRel>/<basename>.yaml; the form re-edit URL is
// <dir>/<rowsRel>/<basename>.yaml.html.
function rowEditUrl(rowsRel, tableName, rowFileName) {
const pageDir = location.pathname.replace(/\/[^\/]+\.table\.html$/, '/');
const rowsPath = pageDir + (rowsRel || tableName) + '/';
return rowsPath + rowFileName + '.html';
}
app.modules.context = { load: load };
})(window.tablesApp);
(function (app) {
'use strict';
const util = {};
util.h = function (tag, attrs) {
const el = document.createElement(tag);
if (attrs) {
for (const k of Object.keys(attrs)) {
const v = attrs[k];
if (v == null || v === false) {
continue;
}
if (k === 'className') {
el.className = v;
} else if (k.length > 2 && k.slice(0, 2) === 'on' && typeof v === 'function') {
el.addEventListener(k.slice(2).toLowerCase(), v);
} else if (v === true) {
el.setAttribute(k, '');
} else {
el.setAttribute(k, v);
}
}
}
for (let i = 2; i < arguments.length; i++) {
const c = arguments[i];
if (c == null || c === false) {
continue;
}
if (typeof c === 'string' || typeof c === 'number') {
el.appendChild(document.createTextNode(String(c)));
} else {
el.appendChild(c);
}
}
return el;
};
// Resolve a column's `field` against a row data object.
// - "" or "/" → the whole object
// - "/foo/bar" → JSON Pointer (RFC 6901) lookup
// - "foo" → top-level key
util.resolveField = function (data, field) {
if (data == null) {
return undefined;
}
if (!field || field === '/') {
return data;
}
if (field.charAt(0) !== '/') {
return data[field];
}
const segments = field.split('/').slice(1).map(function (s) {
return s.replace(/~1/g, '/').replace(/~0/g, '~');
});
let cur = data;
for (let i = 0; i < segments.length; i++) {
if (cur == null) {
return undefined;
}
if (Array.isArray(cur)) {
const idx = parseInt(segments[i], 10);
if (Number.isNaN(idx)) {
return undefined;
}
cur = cur[idx];
} else if (typeof cur === 'object') {
cur = cur[segments[i]];
} else {
return undefined;
}
}
return cur;
};
// Format a raw cell value per column's `format` hint.
util.formatCell = function (value, format) {
if (value == null || value === '') {
return '';
}
if (format === 'date') {
const d = new Date(value);
if (!isNaN(d.getTime())) {
return d.toISOString().slice(0, 10);
}
return String(value);
}
if (format === 'datetime') {
const d = new Date(value);
if (!isNaN(d.getTime())) {
return d.toLocaleString();
}
return String(value);
}
if (format === 'number') {
const n = Number(value);
if (Number.isFinite(n)) {
return n.toLocaleString();
}
return String(value);
}
if (format === 'bool' || typeof value === 'boolean') {
return value ? '✓' : '';
}
if (typeof value === 'object') {
try {
return JSON.stringify(value);
} catch (e) {
return String(value);
}
}
return String(value);
};
// Compare two cell values for sorting. null/undefined sort last.
// Numbers compared numerically, dates compared as Date, otherwise string compare.
util.compareCells = function (a, b, format) {
const aMissing = a == null || a === '';
const bMissing = b == null || b === '';
if (aMissing && bMissing) {
return 0;
}
if (aMissing) {
return 1;
}
if (bMissing) {
return -1;
}
if (format === 'date' || format === 'datetime') {
const da = new Date(a).getTime();
const db = new Date(b).getTime();
if (!isNaN(da) && !isNaN(db)) {
return da - db;
}
}
if (format === 'number' || (typeof a === 'number' && typeof b === 'number')) {
const na = Number(a);
const nb = Number(b);
if (Number.isFinite(na) && Number.isFinite(nb)) {
return na - nb;
}
}
const sa = String(a).toLowerCase();
const sb = String(b).toLowerCase();
if (sa < sb) return -1;
if (sa > sb) return 1;
return 0;
};
app.modules.util = util;
})(window.tablesApp);
(function (app) {
'use strict';
// A filter is per-column and has one of two shapes:
// - free-text: { kind: 'contains', value: '<string>' }
// - enum: { kind: 'enum', value: ['<choice>', ...] }
// An empty value (empty string or empty array) matches everything.
function isEnumColumn(col) {
return Array.isArray(col.enum) && col.enum.length > 0;
}
function defaultFilterFor(col) {
return isEnumColumn(col) ? { kind: 'enum', value: [] } : { kind: 'contains', value: '' };
}
function rowMatches(filter, cellValue) {
if (filter.kind === 'enum') {
if (!Array.isArray(filter.value) || filter.value.length === 0) {
return true;
}
const s = cellValue == null ? '' : String(cellValue);
return filter.value.indexOf(s) !== -1;
}
// contains
if (!filter.value) {
return true;
}
const needle = String(filter.value).toLowerCase();
const hay = cellValue == null ? '' : String(cellValue).toLowerCase();
return hay.indexOf(needle) !== -1;
}
function isEmpty(filter) {
if (filter.kind === 'enum') {
return !Array.isArray(filter.value) || filter.value.length === 0;
}
return !filter.value;
}
function apply(rows, columns, filterMap, resolveField) {
return rows.filter(function (row) {
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
const filter = filterMap[col.field];
if (!filter || isEmpty(filter)) {
continue;
}
const cellValue = resolveField(row.data, col.field);
if (!rowMatches(filter, cellValue)) {
return false;
}
}
return true;
});
}
app.modules.filters = {
defaultFilterFor: defaultFilterFor,
isEnumColumn: isEnumColumn,
isEmpty: isEmpty,
apply: apply
};
})(window.tablesApp);
(function (app) {
'use strict';
// Sort state is an ordered list of {field, dir} keys; the first is
// primary, additional keys break ties.
function defaultsFromContext(ctx) {
const defaults = ctx.defaults || {};
if (Array.isArray(defaults.sort) && defaults.sort.length > 0) {
return defaults.sort.slice();
}
// Fall back to any column with `sort:` set.
const fromCols = (ctx.columns || []).filter(function (c) { return c.sort; });
if (fromCols.length > 0) {
return fromCols.map(function (c) {
const dir = c.sort === 'desc' ? 'desc' : 'asc';
return { field: c.field, dir: dir };
});
}
return [];
}
function findColumn(columns, field) {
for (let i = 0; i < columns.length; i++) {
if (columns[i].field === field) {
return columns[i];
}
}
return null;
}
// Click handler for a header: cycle the sort state for `field`.
// - Not currently a sort key → add as primary, asc
// - Currently primary asc → flip to desc
// - Currently primary desc → remove
// - Currently secondary → promote to primary, asc
// Shift-click is meant for additional accumulation but we keep the
// single-click semantics simple; advanced multi-sort can be a
// follow-up.
function cycle(state, field, multi) {
const idx = state.findIndex(function (s) { return s.field === field; });
if (multi) {
if (idx === -1) {
return state.concat([{ field: field, dir: 'asc' }]);
}
const cur = state[idx];
if (cur.dir === 'asc') {
const next = state.slice();
next[idx] = { field: field, dir: 'desc' };
return next;
}
return state.slice(0, idx).concat(state.slice(idx + 1));
}
if (idx === -1) {
return [{ field: field, dir: 'asc' }];
}
if (idx === 0) {
const cur = state[0];
if (cur.dir === 'asc') {
return [{ field: field, dir: 'desc' }];
}
return [];
}
return [{ field: field, dir: 'asc' }];
}
function apply(rows, sortState, columns, util) {
if (!sortState || sortState.length === 0) {
return rows;
}
const out = rows.slice();
out.sort(function (a, b) {
for (let i = 0; i < sortState.length; i++) {
const key = sortState[i];
const col = findColumn(columns, key.field);
const fmt = col ? col.format : '';
const av = util.resolveField(a.data, key.field);
const bv = util.resolveField(b.data, key.field);
const cmp = util.compareCells(av, bv, fmt);
if (cmp !== 0) {
return key.dir === 'desc' ? -cmp : cmp;
}
}
return 0;
});
return out;
}
function indicator(sortState, field) {
for (let i = 0; i < sortState.length; i++) {
if (sortState[i].field === field) {
const arrow = sortState[i].dir === 'desc' ? ' ▼' : ' ▲';
if (sortState.length > 1) {
return arrow + (i + 1);
}
return arrow;
}
}
return '';
}
app.modules.sort = {
defaultsFromContext: defaultsFromContext,
cycle: cycle,
apply: apply,
indicator: indicator
};
})(window.tablesApp);
(function (app) {
'use strict';
function renderHeader(theadEl, columns, sortState, filterMap, onHeaderClick, onFilterChange) {
const util = app.modules.util;
const filters = app.modules.filters;
const sort = app.modules.sort;
theadEl.innerHTML = '';
const titleRow = util.h('tr', { className: 'zddc-table__title-row' });
const filterRow = util.h('tr', { className: 'zddc-table__filter-row' });
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
const indicator = sort.indicator(sortState, col.field);
const th = util.h('th', {
className: 'zddc-table__th',
'data-field': col.field,
style: col.width ? 'width:' + col.width : null,
onClick: function (ev) { onHeaderClick(col.field, ev.shiftKey); }
}, col.title || col.field, indicator);
titleRow.appendChild(th);
const td = util.h('td', { className: 'zddc-table__filter-cell' });
const f = filterMap[col.field] || filters.defaultFilterFor(col);
if (filters.isEnumColumn(col)) {
const select = util.h('select', {
multiple: true,
'aria-label': 'Filter ' + (col.title || col.field),
className: 'zddc-table__filter-enum',
onChange: function (ev) {
const opts = ev.target.options;
const picked = [];
for (let j = 0; j < opts.length; j++) {
if (opts[j].selected) {
picked.push(opts[j].value);
}
}
onFilterChange(col.field, { kind: 'enum', value: picked });
}
});
for (let j = 0; j < col.enum.length; j++) {
const v = col.enum[j];
const opt = util.h('option', { value: v }, v);
if (Array.isArray(f.value) && f.value.indexOf(v) !== -1) {
opt.selected = true;
}
select.appendChild(opt);
}
td.appendChild(select);
} else {
const input = util.h('input', {
type: 'text',
className: 'zddc-table__filter-text',
placeholder: 'filter…',
'aria-label': 'Filter ' + (col.title || col.field),
value: typeof f.value === 'string' ? f.value : '',
onInput: function (ev) {
onFilterChange(col.field, { kind: 'contains', value: ev.target.value });
}
});
td.appendChild(input);
}
filterRow.appendChild(td);
}
theadEl.appendChild(titleRow);
theadEl.appendChild(filterRow);
}
function renderBody(tbodyEl, rows, columns) {
const util = app.modules.util;
tbodyEl.innerHTML = '';
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const tr = util.h('tr', {
className: 'zddc-table__row' + (row.editable ? ' zddc-table__row--editable' : ' zddc-table__row--readonly'),
'data-url': row.url,
'data-editable': row.editable ? '1' : '0',
onClick: function (ev) {
const target = ev.currentTarget;
const editable = target.getAttribute('data-editable') === '1';
const url = target.getAttribute('data-url');
if (editable && url) {
// Indirection so tests can intercept without
// fighting Chromium's location.assign property
// descriptor. Production calls window.location.assign.
const nav = (window.tablesApp && window.tablesApp.navigateTo) ||
function (u) { window.location.assign(u); };
nav(url);
}
}
});
for (let c = 0; c < columns.length; c++) {
const col = columns[c];
const raw = util.resolveField(row.data, col.field);
const text = util.formatCell(raw, col.format);
tr.appendChild(util.h('td', { className: 'zddc-table__cell' }, text));
}
tbodyEl.appendChild(tr);
}
}
function renderRowCount(el, displayed, total) {
if (!el) return;
if (displayed === total) {
el.textContent = total + (total === 1 ? ' row' : ' rows');
} else {
el.textContent = displayed + ' of ' + total + ' rows';
}
}
app.modules.render = {
header: renderHeader,
body: renderBody,
rowCount: renderRowCount
};
})(window.tablesApp);
(function (app) {
'use strict';
async function init() {
const ctx = await app.modules.context.load();
app.context = ctx;
const titleEl = document.getElementById('table-title');
if (ctx.title && titleEl) {
titleEl.textContent = ctx.title;
document.title = 'ZDDC — ' + ctx.title;
}
const descEl = document.getElementById('table-description');
if (descEl && ctx.description) {
descEl.textContent = ctx.description;
descEl.hidden = false;
}
const tableEl = document.getElementById('table-root');
const theadEl = tableEl.querySelector('thead');
const tbodyEl = tableEl.querySelector('tbody');
const emptyEl = document.getElementById('table-empty');
const countEl = document.getElementById('table-rowcount');
const clearBtn = document.getElementById('table-clear-filters');
const columns = Array.isArray(ctx.columns) ? ctx.columns : [];
const allRows = Array.isArray(ctx.rows) ? ctx.rows : [];
const state = app.state;
state.rows = allRows;
state.sort = app.modules.sort.defaultsFromContext(ctx);
state.filter = {};
// Seed default filters from context.defaults.filter (per-column).
if (ctx.defaults && ctx.defaults.filter && typeof ctx.defaults.filter === 'object') {
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
const seeded = ctx.defaults.filter[col.field];
if (seeded == null) {
continue;
}
if (app.modules.filters.isEnumColumn(col)) {
state.filter[col.field] = {
kind: 'enum',
value: Array.isArray(seeded) ? seeded.slice() : [String(seeded)]
};
} else {
state.filter[col.field] = { kind: 'contains', value: String(seeded) };
}
}
}
function anyFilterActive() {
const filters = app.modules.filters;
const keys = Object.keys(state.filter);
for (let i = 0; i < keys.length; i++) {
if (!filters.isEmpty(state.filter[keys[i]])) {
return true;
}
}
return false;
}
function paint() {
const filtered = app.modules.filters.apply(state.rows, columns, state.filter, app.modules.util.resolveField);
const sorted = app.modules.sort.apply(filtered, state.sort, columns, app.modules.util);
app.modules.render.header(theadEl, columns, state.sort, state.filter, onHeaderClick, onFilterChange);
app.modules.render.body(tbodyEl, sorted, columns);
app.modules.render.rowCount(countEl, sorted.length, state.rows.length);
if (emptyEl) {
emptyEl.hidden = sorted.length > 0 || state.rows.length === 0;
}
if (clearBtn) {
clearBtn.hidden = !anyFilterActive();
}
}
function onHeaderClick(field, shiftKey) {
state.sort = app.modules.sort.cycle(state.sort, field, shiftKey);
paint();
}
function onFilterChange(field, value) {
state.filter[field] = value;
paint();
}
if (clearBtn) {
clearBtn.addEventListener('click', function () {
state.filter = {};
paint();
});
}
paint();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(window.tablesApp);
</script>
</body>
</html>