csv-grid 3.0.7__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ tests/**/*.csv
2
+ # …but commit the small, curated fixtures (the big volume CSVs stay ignored)
3
+ !tests/csv/curated/
4
+ !tests/csv/curated/*.csv
5
+
6
+ node_modules/
7
+ dev/tmp-big.csv
8
+ python/dist/
9
+
10
+ venv/
11
+ .venv/
12
+
13
+ __pycache__/
14
+
15
+ .virtual_documents/
16
+ .ipynb_checkpoints/
17
+
18
+ hacks/
19
+
20
+ CSV-VIEWER.*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csv-grid
3
- Version: 3.0.7
3
+ Version: 3.3.0
4
4
  Summary: Emit csv-viewer's CsvGrid interactive tables from pandas DataFrames (Jupyter / Quarto / static HTML).
5
5
  Project-URL: Homepage, https://github.com/mynl/CSV_Viewer
6
6
  Project-URL: Live viewer, https://mynl.github.io/CSV_Viewer/
@@ -74,9 +74,14 @@ html = to_html(df, name="results.df", assets="inline") # fragment string
74
74
  - Options mirror the JS API in snake_case: `global_search`,
75
75
  `column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
76
76
  (`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
77
- `'year'`, `'eng'`, None = auto), `render_cap`, `eager_cells`,
77
+ `'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
78
+ or `'coverage'` to maximize the count of fully-shown cells), `rows`
79
+ (cap the viewport to ~N rows, vertical scroll for the rest) /
80
+ `max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
78
81
  `worker` (default False — data is inlined), plus `name` (status line)
79
- and `index` (include the DataFrame index as leading columns).
82
+ and `index` (include the DataFrame index as leading columns). Dark mode
83
+ follows the host page (`prefers-color-scheme`; JupyterLab dark themes
84
+ included).
80
85
 
81
86
  Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
82
87
  non-midnight times); integral float columns are emitted as integers so
@@ -48,9 +48,14 @@ html = to_html(df, name="results.df", assets="inline") # fragment string
48
48
  - Options mirror the JS API in snake_case: `global_search`,
49
49
  `column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
50
50
  (`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
51
- `'year'`, `'eng'`, None = auto), `render_cap`, `eager_cells`,
51
+ `'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
52
+ or `'coverage'` to maximize the count of fully-shown cells), `rows`
53
+ (cap the viewport to ~N rows, vertical scroll for the rest) /
54
+ `max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
52
55
  `worker` (default False — data is inlined), plus `name` (status line)
53
- and `index` (include the DataFrame index as leading columns).
56
+ and `index` (include the DataFrame index as leading columns). Dark mode
57
+ follows the host page (`prefers-color-scheme`; JupyterLab dark themes
58
+ included).
54
59
 
55
60
  Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
56
61
  non-midnight times); integral float columns are emitted as integers so
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csv-grid"
3
- version = "3.0.7"
3
+ version = "3.3.0"
4
4
  description = "Emit csv-viewer's CsvGrid interactive tables from pandas DataFrames (Jupyter / Quarto / static HTML)."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -18,7 +18,7 @@ import json
18
18
  import uuid
19
19
  from importlib import resources
20
20
 
21
- __version__ = "3.0.7"
21
+ __version__ = "3.3.0"
22
22
  __all__ = ["show", "to_html", "payload"]
23
23
 
24
24
  # python snake_case -> CsvGrid option names (see src/grid/grid.js)
@@ -30,6 +30,9 @@ _OPTION_MAP = {
30
30
  "expand_buttons": "expandButtons",
31
31
  "align": "align",
32
32
  "formats": "formats",
33
+ "width_mode": "widthMode",
34
+ "rows": "maxRows",
35
+ "max_height": "height",
33
36
  "render_cap": "renderCap",
34
37
  "eager_cells": "eagerCells",
35
38
  "worker": "worker",
@@ -158,10 +161,60 @@ _assets_emitted = False
158
161
 
159
162
  def show(df, *, name: str | None = None, assets=None,
160
163
  index: bool = False, **options) -> None:
161
- """Display `df` as a CsvGrid in Jupyter / Quarto. Assets are emitted
162
- once per kernel session (= once per rendered page); assets='inline'
163
- forces re-emission (e.g. after restarting the browser page without
164
- the kernel), a base-URL string loads them from there instead.
164
+ """Display `df` as a CsvGrid in Jupyter / Quarto.
165
+
166
+ Assets (the grid's JS + CSS) are emitted once per kernel session (=
167
+ once per rendered page); ``assets='inline'`` forces re-emission (e.g.
168
+ after reloading the browser page without restarting the kernel), and a
169
+ base-URL string loads them from there instead of inlining.
170
+
171
+ Parameters
172
+ ----------
173
+ df : pandas.DataFrame
174
+ Data to render. Types and number/date formatting are re-inferred
175
+ grid-side from the emitted values (dates -> ISO, NaN/None -> blank,
176
+ integral float columns -> ints) exactly as the csv-viewer app does.
177
+ name : str, optional
178
+ Label shown in the grid's status line.
179
+ assets : {None, 'inline', str, False}
180
+ None (default) inlines on the first call per session, then dedupes;
181
+ 'inline' forces inlining; a base-URL string links the assets from
182
+ there; False omits them (already on the page).
183
+ index : bool, default False
184
+ Include the DataFrame index as leading column(s).
185
+
186
+ Other options (keyword, snake_case; mirror the JS CsvGrid API)
187
+ --------------------------------------------------------------
188
+ global_search : bool, default True fzf search box.
189
+ column_filters : bool, default True per-column filter row.
190
+ sortable : bool, default True click headers to sort.
191
+ status_bar : bool, default True row-counts line.
192
+ expand_buttons : bool, default True Expand / Contract pair.
193
+ align : str, optional 'llrcr…', one of l/r/c per column,
194
+ overriding the type-based default.
195
+ formats / fmt : list, optional per-column format spec, None entry
196
+ = auto rules; subset of d3/Python
197
+ ``[,][.N](f|d|%|e|s)`` plus the
198
+ named 'year' and 'eng'.
199
+ width_mode : {'equal-risk', 'coverage'}, default 'equal-risk'
200
+ squeeze allocation when the table
201
+ is wider than its container:
202
+ equal-risk = every column truncates
203
+ with equal probability; coverage =
204
+ maximize the count of cells shown
205
+ in full.
206
+ rows : int, optional cap the scroll viewport to ~N rows
207
+ (vertical scroll for the rest).
208
+ max_height : str, optional raw CSS max-height (e.g. '400px');
209
+ overrides ``rows`` when set.
210
+ render_cap : int, default 2000 rows rendered before "show all".
211
+ eager_cells : int, default 200000 below this, format everything up
212
+ front (else lazy).
213
+ worker : bool, default False parse worker (off by default here
214
+ — the data is inlined, not fetched).
215
+
216
+ Dark mode follows the host page automatically (prefers-color-scheme;
217
+ JupyterLab dark themes included).
165
218
  """
166
219
  global _assets_emitted
167
220
  try:
@@ -0,0 +1,233 @@
1
+ /* csv-grid styles — self-contained, no framework. Namespaced .csvgrid-*
2
+ * (structural) with table-internal classes scoped under .csvgrid .csvgrid-table.
3
+ * Replicates the viewer's Bootstrap-era look so embed pages match.
4
+ *
5
+ * All colors come from custom properties on .csvgrid (light defaults
6
+ * below). Dark mode auto-follows the OS via prefers-color-scheme, and a
7
+ * host can force either theme with .csvgrid[data-theme="dark"|"light"].
8
+ * The variable indirection also fixes the JupyterLab "white island"
9
+ * header: the sticky header bg is var(--csvgrid-bg), so it tracks the
10
+ * surrounding theme instead of a hardcoded #fff. */
11
+
12
+ .csvgrid {
13
+ --csvgrid-fg: #212529;
14
+ --csvgrid-bg: #fff;
15
+ --csvgrid-muted: #6c757d;
16
+ --csvgrid-border: #dee2e6;
17
+ --csvgrid-input-border: #ced4da;
18
+ --csvgrid-accent: #0d6efd;
19
+ --csvgrid-focus-ring: rgba(13, 110, 253, 0.25);
20
+ --csvgrid-row-hover: rgba(0, 0, 0, 0.075);
21
+ --csvgrid-grip-hover: rgba(13, 110, 253, 0.3);
22
+ --csvgrid-blank: #adb5bd;
23
+ --csvgrid-filter-active-bg: #e7f1ff;
24
+ --csvgrid-error-fg: #842029;
25
+ --csvgrid-error-bg: #f8d7da;
26
+ --csvgrid-error-border: #f5c2c7;
27
+
28
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
29
+ color: var(--csvgrid-fg);
30
+ }
31
+ .csvgrid-hidden { display: none !important; }
32
+
33
+ /* Dark palette — auto via the OS unless the host forced light, and
34
+ * explicitly via [data-theme="dark"]. (Two selectors, one palette.) */
35
+ @media (prefers-color-scheme: dark) {
36
+ .csvgrid:not([data-theme="light"]) {
37
+ --csvgrid-fg: #dee2e6;
38
+ --csvgrid-bg: #1e1e1e;
39
+ --csvgrid-muted: #adb5bd;
40
+ --csvgrid-border: #495057;
41
+ --csvgrid-input-border: #495057;
42
+ --csvgrid-accent: #6ea8fe;
43
+ --csvgrid-focus-ring: rgba(110, 168, 254, 0.25);
44
+ --csvgrid-row-hover: rgba(255, 255, 255, 0.075);
45
+ --csvgrid-grip-hover: rgba(110, 168, 254, 0.4);
46
+ --csvgrid-blank: #6c757d;
47
+ --csvgrid-filter-active-bg: #2b3a55;
48
+ --csvgrid-error-fg: #ea868f;
49
+ --csvgrid-error-bg: #2c0b0e;
50
+ --csvgrid-error-border: #842029;
51
+ }
52
+ }
53
+ .csvgrid[data-theme="dark"] {
54
+ --csvgrid-fg: #dee2e6;
55
+ --csvgrid-bg: #1e1e1e;
56
+ --csvgrid-muted: #adb5bd;
57
+ --csvgrid-border: #495057;
58
+ --csvgrid-input-border: #495057;
59
+ --csvgrid-accent: #6ea8fe;
60
+ --csvgrid-focus-ring: rgba(110, 168, 254, 0.25);
61
+ --csvgrid-row-hover: rgba(255, 255, 255, 0.075);
62
+ --csvgrid-grip-hover: rgba(110, 168, 254, 0.4);
63
+ --csvgrid-blank: #6c757d;
64
+ --csvgrid-filter-active-bg: #2b3a55;
65
+ --csvgrid-error-fg: #ea868f;
66
+ --csvgrid-error-bg: #2c0b0e;
67
+ --csvgrid-error-border: #842029;
68
+ }
69
+
70
+ /* Toolbar (only generated when globalSearch / expandButtons are on) */
71
+ .csvgrid-toolbar {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 8px;
75
+ margin-bottom: 8px;
76
+ flex-wrap: wrap;
77
+ }
78
+ .csvgrid-search {
79
+ flex: 1;
80
+ max-width: 320px;
81
+ min-width: 8em;
82
+ font-size: 0.875rem;
83
+ padding: 0.25rem 0.5rem;
84
+ border: 1px solid var(--csvgrid-input-border);
85
+ border-radius: 0.25rem;
86
+ background: var(--csvgrid-bg);
87
+ color: var(--csvgrid-fg);
88
+ }
89
+ .csvgrid-search:focus {
90
+ outline: 0;
91
+ border-color: var(--csvgrid-accent);
92
+ box-shadow: 0 0 0 0.25rem var(--csvgrid-focus-ring);
93
+ }
94
+ .csvgrid-btn {
95
+ font-size: 0.875rem;
96
+ padding: 0.25rem 0.5rem;
97
+ border: 1px solid var(--csvgrid-muted);
98
+ border-radius: 0.25rem;
99
+ background: var(--csvgrid-bg);
100
+ color: var(--csvgrid-muted);
101
+ cursor: pointer;
102
+ white-space: nowrap;
103
+ }
104
+ .csvgrid-btn:hover {
105
+ background: var(--csvgrid-muted);
106
+ color: var(--csvgrid-bg);
107
+ }
108
+
109
+ /* Scrollable table region; hosts cap its height / add card chrome */
110
+ .csvgrid-scroll { overflow: auto; }
111
+
112
+ /* Respond to the GRID's own width (embeds), not the viewport: a narrow
113
+ * container lets the toolbar wrap and shrinks the search field rather than
114
+ * overflowing. */
115
+ .csvgrid { container-type: inline-size; }
116
+ @container (max-width: 360px) {
117
+ .csvgrid-search { max-width: none; flex-basis: 100%; }
118
+ }
119
+
120
+ /* Widths are pinned per load via <colgroup> + table-layout: fixed (set from
121
+ * JS); tight when everything fits, equal-risk truncation when it doesn't. */
122
+ .csvgrid .csvgrid-table {
123
+ border-collapse: collapse;
124
+ width: auto;
125
+ font-size: 0.8rem;
126
+ line-height: 1.5;
127
+ font-variant-numeric: tabular-nums;
128
+ background: var(--csvgrid-bg);
129
+ }
130
+ .csvgrid .csvgrid-table th,
131
+ .csvgrid .csvgrid-table td {
132
+ padding: 0.25rem;
133
+ border-bottom: 1px solid var(--csvgrid-border);
134
+ text-align: left;
135
+ }
136
+ .csvgrid .csvgrid-table tbody tr:hover td { background: var(--csvgrid-row-hover); }
137
+
138
+ .csvgrid .csvgrid-table thead th {
139
+ position: sticky;
140
+ top: 0;
141
+ z-index: 2;
142
+ background: var(--csvgrid-bg);
143
+ box-shadow: inset 0 -2px 0 var(--csvgrid-border);
144
+ cursor: pointer;
145
+ user-select: none;
146
+ white-space: nowrap;
147
+ overflow: hidden;
148
+ text-overflow: ellipsis;
149
+ }
150
+ .csvgrid .csvgrid-table thead th.csvgrid-nosort { cursor: default; }
151
+ .csvgrid .csvgrid-table thead th .col-resizer {
152
+ /* th has overflow:hidden, so the grip must sit inside the edge */
153
+ position: absolute;
154
+ top: 0;
155
+ right: 0;
156
+ width: 7px;
157
+ height: 100%;
158
+ cursor: col-resize;
159
+ z-index: 3;
160
+ }
161
+ .csvgrid .csvgrid-table thead th .col-resizer:hover { background: var(--csvgrid-grip-hover); }
162
+ body.csvgrid-resizing { cursor: col-resize; user-select: none; }
163
+
164
+ .csvgrid .csvgrid-table thead th .sort-arrow {
165
+ display: inline-block;
166
+ width: 1em;
167
+ color: var(--csvgrid-accent);
168
+ }
169
+
170
+ .csvgrid .csvgrid-table thead tr.filter-row th {
171
+ cursor: default;
172
+ box-shadow: inset 0 -2px 0 var(--csvgrid-border);
173
+ top: 28px; /* sits below the (0.8rem) header row */
174
+ padding: 2px 4px;
175
+ }
176
+ .csvgrid-filter {
177
+ width: 100%;
178
+ min-width: 4em;
179
+ font-size: 0.78rem;
180
+ padding: 1px 6px;
181
+ border: 1px solid var(--csvgrid-input-border);
182
+ border-radius: 0.25rem;
183
+ background: var(--csvgrid-bg);
184
+ color: var(--csvgrid-fg);
185
+ }
186
+ .csvgrid-filter:focus {
187
+ outline: 0;
188
+ border-color: var(--csvgrid-accent);
189
+ box-shadow: 0 0 0 0.2rem var(--csvgrid-focus-ring);
190
+ }
191
+ .csvgrid-filter.active-filter {
192
+ background-color: var(--csvgrid-filter-active-bg);
193
+ border-color: var(--csvgrid-accent);
194
+ }
195
+
196
+ .csvgrid .csvgrid-table td {
197
+ white-space: nowrap;
198
+ overflow: hidden;
199
+ text-overflow: ellipsis;
200
+ }
201
+
202
+ .csvgrid .csvgrid-table .col-number { text-align: right; }
203
+ .csvgrid .csvgrid-table .col-date { text-align: center; } /* greater_tables convention */
204
+ .csvgrid .csvgrid-table .col-text { text-align: left; }
205
+ /* explicit alignment (align option / markdown spec) overrides type alignment */
206
+ .csvgrid .csvgrid-table .align-left { text-align: left; }
207
+ .csvgrid .csvgrid-table .align-center { text-align: center; }
208
+ .csvgrid .csvgrid-table .align-right { text-align: right; }
209
+ .csvgrid .csvgrid-table td.blank { color: var(--csvgrid-blank); }
210
+
211
+ /* "Showing first N — show all" note below the table */
212
+ .csvgrid-capnote {
213
+ text-align: center;
214
+ margin: 1rem 0;
215
+ }
216
+
217
+ /* Row-counts line (only when statusBar: true generates it) */
218
+ .csvgrid-status {
219
+ font-size: 0.75rem;
220
+ color: var(--csvgrid-muted);
221
+ padding: 3px 0;
222
+ }
223
+
224
+ /* Load errors ({url} fetch failures, unusable input) */
225
+ .csvgrid-error {
226
+ margin: 0.5rem 0;
227
+ padding: 0.5rem 0.75rem;
228
+ font-size: 0.875rem;
229
+ color: var(--csvgrid-error-fg);
230
+ background: var(--csvgrid-error-bg);
231
+ border: 1px solid var(--csvgrid-error-border);
232
+ border-radius: 0.25rem;
233
+ }
@@ -0,0 +1,6 @@
1
+ var CsvGrid=(function(){var e=typeof document<`u`&&document.currentScript&&document.currentScript.src||(typeof document<`u`?document.baseURI:``);function t(e){return(e??``).replace(/^\uFEFF/,``).replace(/^(?:[ \t]*(?:\r\n|\n|\r))+/,``)}function n(e){let t=[`,`,` `,`;`,`|`],n=e.split(/\r\n|\n|\r/,20).filter(e=>e.length),i=`,`,a=0;for(let e of t){let t=n.map(t=>r(t,e).length),o=t[0];if(o<2)continue;let s=o*(t.every(e=>e===o)?10:1);s>a&&(a=s,i=e)}return i}function r(e,t){let n=[],r=``,i=!1;for(let a=0;a<e.length;a++){let o=e[a];i?o===`"`?i=!1:r+=o:o===`"`?i=!0:o===t?(n.push(r),r=``):r+=o}return n.push(r),n}function i(e,t){let n=[],r=[],i=``,a=!1,o=0,s=e.length;for(;o<s;){let s=e[o];if(a){if(s===`"`){if(e[o+1]===`"`){i+=`"`,o+=2;continue}a=!1,o++;continue}i+=s,o++;continue}if(s===`"`){a=!0,o++;continue}if(s===t){r.push(i),i=``,o++;continue}if(s===`\r`||s===`
2
+ `){r.push(i),i=``,n.push(r),r=[],s===`\r`&&e[o+1]===`
3
+ `&&o++,o++;continue}i+=s,o++}for((i.length||r.length)&&(r.push(i),n.push(r));n.length&&n[n.length-1].every(e=>e.trim()===``);)n.pop();return n}function a(e){e=e.trim(),e.startsWith(`|`)&&(e=e.slice(1)),e.endsWith(`|`)&&!e.endsWith(`\\|`)&&(e=e.slice(0,-1));let t=[],n=``;for(let r=0;r<e.length;r++){let i=e[r];i===`\\`&&e[r+1]===`|`?(n+=`|`,r++):i===`|`?(t.push(n),n=``):n+=i}return t.push(n),t.map(e=>e.trim())}var o=/^:?-+:?$/;function s(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``);if(t.length<2||!t[0].includes(`|`))return!1;let n=a(t[1]);return n.length>0&&n.every(e=>o.test(e))}function c(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``),n=a(t[0]).map((e,t)=>e||`col${t+1}`),r=a(t[1]).map(e=>{let t=e.startsWith(`:`),n=e.endsWith(`:`);return t&&n?`center`:n?`right`:t?`left`:null});for(;r.length<n.length;)r.push(null);return{headers:n,rows:t.slice(2).filter(e=>e.includes(`|`)).map(e=>{let t=a(e).slice(0,n.length);for(;t.length<n.length;)t.push(``);return t}),aligns:r}}var l=/^\(?\$?-?(?:[0-9][0-9,]*(?:\.[0-9]+)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?%?\)?$/,u=/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?Z?)?$/,d=/^(\d{1,4})([\/\-.])(\d{1,2})\2(\d{1,4})$/,f=/^(\d{1,2})[ \-]([A-Za-z]{3,9})\.?,?[ \-](\d{2,4})$/,p=/^([A-Za-z]{3,9})\.?,?[ \-](\d{1,2}),?[ \-](\d{2,4})$/,m=[`january`,`february`,`march`,`april`,`may`,`june`,`july`,`august`,`september`,`october`,`november`,`december`],h=new Set([`nan`,`na`,`n/a`,`#n/a`,`null`,`none`,`-`,`--`,`.`]);function g(e){return h.has((e??``).trim().toLowerCase())}function _(e){if(e=e.trim(),!l.test(e))return null;let t=!1;e.startsWith(`(`)&&e.endsWith(`)`)&&(t=!0,e=e.slice(1,-1));let n=!1;e.endsWith(`%`)&&(n=!0,e=e.slice(0,-1)),e=e.replace(/[$,]/g,``);let r=parseFloat(e);if(!isFinite(r))return null;t&&(r=-r),n&&(r/=100);let i=/^([^eE]*)[eE]([+-]?\d+)$/.exec(e),a=i?i[1]:e,o=i?+i[2]:0,s=a.indexOf(`.`),c=Math.max(0,(s<0?0:a.length-s-1)-o);return n&&(c+=2),{v:r,dec:c}}var v=`9007199254740991`;function y(e){return e=e.trim(),e.endsWith(`%`)||(e.startsWith(`(`)&&e.endsWith(`)`)&&(e=e.slice(1,-1)),e=e.replace(/[$,]/g,``).replace(/^[+-]/,``),!/^\d+$/.test(e))?!1:(e=e.replace(/^0+(?=\d)/,``),e.length>16||e.length===16&&e>v)}function b(e){let t=e.toLowerCase(),n=m.findIndex(e=>e.startsWith(t)||t===`sept`&&e===`september`);return n<0||t.length<3?null:n+1}function x(e){return e=+e,e<100?e<50?2e3+e:1900+e:e}function S(e,t,n,r=0,i=0,a=0,o=!1){let s=new Date(e,t-1,n,r,i,a);return s.getFullYear()!==e||s.getMonth()!==t-1||s.getDate()!==+n?null:{t:s.getTime(),hasTime:o}}function C(e,t=!1){e=e.trim();let n=u.exec(e);if(n){let[,e,t,r,i,a,o]=n;return S(+e,+t,+r,+(i||0),+(a||0),+(o||0),i!==void 0)}if(n=d.exec(e),n){let[,e,,r,i]=n;if(e.length===4&&i.length<=2)return S(+e,+r,+i);if(e.length<=2&&(i.length===4||i.length===2)){let n=x(i);return+e>12&&+r<=12?S(n,+r,+e):+r>12&&+e<=12?S(n,+e,+r):t?S(n,+r,+e):S(n,+e,+r)}return null}if(n=f.exec(e),n){let e=b(n[2]);return e?S(x(n[3]),e,+n[1]):null}if(n=p.exec(e),n){let e=b(n[1]);return e?S(x(n[3]),e,+n[2]):null}return null}function ee(e){let t=d.exec(e.trim());if(!t)return null;let n=t[1],r=t[3],i=t[4];return n.length===4||!(i.length===4||i.length===2)?null:+n>12&&+r<=12?`day`:+r>12&&+n<=12?`month`:+n<=12&&+r<=12?`ambiguous`:null}function te(e){return e.some(e=>{let t=(e??``).trim();return t!==``&&(_(t)!==null||C(t)!==null)})}function ne(e){let t=e=>e.type===`date`?`Date`:e.type===`number`?e.format===`year`?`Year`:`Amount`:`Description`,n={},r={};e.forEach(e=>{let r=t(e);n[r]=(n[r]||0)+1}),e.forEach(e=>{let i=t(e);r[i]=(r[i]||0)+1,e.name=n[i]>1?`${i} ${r[i]}`:i})}var re=/\b(year|yr|vintage|cohort)\b/i,w=/\b(amount|amt|balance|bal|price|cost|fee|fees|charge|paid|payment|debit|credit|total|premium|loss|salary|wage|income|expense|revenue|usd|gbp|eur|cad)\b|[$£€]/i,T=/\b(id|no|num|number|account|acct|code|zip|postal|phone|fax|ssn|ein|tin|invoice|inv|ref|reference|sku|upc|isbn|order|customer|cust|member|policy|claim|seq)\b/i,E=/(?<![a-z])(ratio|rate|roe|roa|coc|lr|elr|plr|margin|yield|return|growth|retention|cede|ceded|discount|apr|apy|coupon|util|utilization|share|pct|percent|frequency)(?![a-z])/i;function D(e,t,n){let r=t.filter(e=>e!==null);if(r.every(e=>Number.isInteger(e))&&r.length)return re.test(e)||r.every(e=>e>=1800&&e<=2100)?{format:`year`,dec:0}:T.test(e)&&!w.test(e)?{format:`plain`,dec:0}:w.test(e)?{format:`float`,dec:2}:{format:`int`,dec:0};let i=0,a=0,o=1/0,s=0;for(let e of r){if(e===0)continue;let t=Math.abs(e);i++,t>a&&(a=t),t<o&&(o=t),s+=t}if(!i)return{format:`float`,dec:Math.min(n,6)};if(E.test(e)&&a<=2)return{format:`pct`,dec:Math.max(1,Math.min(4,n-2))};if(w.test(e)||n<=2&&a<1e5)return{format:`float`,dec:2};if(a/o>1e6)return{format:`eng`,dec:0};let c=s/i;return{format:`float`,dec:Math.max(0,Math.min(n,3-Math.floor(Math.log10(c)),6))}}var O={"-9":`n`,"-6":`µ`,"-3":`m`,0:``,3:`k`,6:`M`,9:`G`,12:`T`};function k(e){if(e===0)return`0`;let t=Math.abs(e),n=Math.floor(Math.log10(t)/3)*3;n=Math.max(-9,Math.min(12,n));let r=t/10**n;return(e<0?`-`:``)+Number(r.toPrecision(3))+O[n]}function A(e,t){if(e<=t)return Array.from({length:e},(e,t)=>t);let n=Array(t),r=e/t;for(let e=0;e<t;e++)n[e]=Math.floor(e*r);return n}var j=2048,M=/^-?0\d/;function N(e,t){let n=A(t.length,j);return e.map((e,r)=>{let i=!0,a=!0,o=!1,s=!1,c=0;for(let e of n){let n=(t[e][r]??``).trim();if(!(n===``||g(n))&&(c++,i&&(_(n)===null?i=!1:(!o&&M.test(n)&&(o=!0),!s&&y(n)&&(s=!0))),a&&C(n,!1)===null&&(a=!1),o||s||!i&&!a))break}if(c===0||o||s)return s?{name:e,type:`text`,align:`right`,values:null}:{name:e,type:`text`,values:null};if(i){let n=Array(t.length).fill(null),i=0;for(let e=0;e<t.length;e++){let a=(t[e][r]??``).trim();if(a===``||g(a))continue;let o=_(a);o&&(n[e]=o.v,o.dec>i&&(i=o.dec))}let a=D(e,n,i);return{name:e,type:`number`,format:a.format,dec:a.dec,values:n}}if(a){let n=!1,i=!1,a=!1,o=!1,s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let c=(t[e][r]??``).trim();if(c===``||g(c))continue;let l=C(c,!1);l&&(s[e]=l.t,i||=l.hasTime);let u=ee(c);u===`day`?(n=!0,o=!0):u===`month`?o=!0:u===`ambiguous`&&(a=!0)}if(n){s=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let n=(t[e][r]??``).trim();if(n===``||g(n))continue;let i=C(n,!0);i&&(s[e]=i.t)}}return{name:e,type:`date`,hasTime:i,ambiguousOrder:a&&!o,values:s}}return{name:e,type:`text`,values:null}})}function P(e,t=null){let r,a,o=null,l;if(s(e)){if({headers:r,rows:a,aligns:o}=c(e),l=t===!1,l&&(a=[r,...a],r=r.map((e,t)=>`col${t+1}`)),!a.length)throw Error(`Markdown table has no data rows.`)}else{let o=i(e,n(e));if(o.length<2)throw Error(`Need a header row and at least one data row.`);l=t===null?te(o[0]):!t,r=l?o[0].map((e,t)=>`col${t+1}`):o[0].map((e,t)=>e.trim()||`col${t+1}`),a=(l?o:o.slice(1)).map(e=>{if(e.length===r.length)return e;let t=e.slice(0,r.length);for(;t.length<r.length;)t.push(``);return t})}let u=N(r,a);return l&&ne(u),o&&u.forEach((e,t)=>{o[t]&&(e.align=o[t])}),{headers:u.map(e=>e.name),rows:a,cols:u,headerless:l}}var F=new Map;function I(e){let t=F.get(e);return t||(t=new Intl.NumberFormat(`en-US`,{minimumFractionDigits:e,maximumFractionDigits:e}),F.set(e,t)),t}function L(e){if(e==null||e===``)return null;if(e===`year`||e===`eng`)return{kind:e};let t=/^(,)?(?:\.(\d+))?([fd%es])$/.exec(e);if(!t)throw Error(`CsvGrid: unrecognized format spec '${e}'`);return{kind:t[3],comma:!!t[1],dec:t[2]===void 0?null:+t[2]}}var R=[[0xe8d4a51000,`T`],[1e9,`G`],[1e6,`M`],[1e3,`k`],[1,``],[.001,`m`],[1e-6,`µ`],[1e-9,`n`]];function z(e,t){switch(t.kind){case`year`:return String(e);case`eng`:return k(e);case`d`:{let n=Math.round(e);return t.comma?I(0).format(n):String(n)}case`f`:{let n=t.dec??2;return t.comma?I(n).format(e):e.toFixed(n)}case`%`:{let n=t.dec??0,r=e*100;return(t.comma?I(n).format(r):r.toFixed(n))+`%`}case`e`:return e.toExponential(t.dec??2);case`s`:{if(t.dec===null||t.dec===void 0)return k(e);if(e===0)return 0 .toFixed(t.dec);let n=Math.abs(e);for(let[r,i]of R)if(n>=r)return(e/r).toFixed(t.dec)+i;return(e/1e-9).toFixed(t.dec)+`n`}}}function ie(e){return[...e].map(e=>({l:`left`,r:`right`,c:`center`})[e]??null)}function B(e,t,n,r=`auto`){if(e=(e??``).trim(),e===``)return``;if(r===`raw`)return e;if(t.type===`number`){let r=t.values[n];return r===null?g(e)?``:e:t.fmt?z(r,t.fmt):t.format===`year`||t.format===`plain`?String(r):t.format===`eng`?k(r):t.format===`pct`?I(t.dec).format(r*100)+`%`:I(t.dec).format(r)}if(t.type===`date`){let r=t.values[n];if(r===null)return g(e)?``:e;let i=new Date(r),a=e=>String(e).padStart(2,`0`),o=`${i.getFullYear()}-${a(i.getMonth()+1)}-${a(i.getDate())}`;return t.hasTime&&(o+=` ${a(i.getHours())}:${a(i.getMinutes())}`),o}return e}function V(e,t){let n=e=>(e=(e??``)+``,/[",\r\n]/.test(e)?`"`+e.replace(/"/g,`""`)+`"`:e),r=e=>e.map(n).join(`,`),i=[r(e)];for(let e of t)i.push(r(e));return i.join(`\r
4
+ `)}function H(e,t,n=[]){let r=e=>((e??``)+``).replace(/\|/g,`\\|`).replace(/\s*\r?\n\s*/g,` `),i=e=>e===`right`?`---:`:e===`center`?`:--:`:e===`left`?`:---`:`---`,a=e=>`| `+e.map(r).join(` | `)+` |`,o=`|`+e.map((e,t)=>i(n[t])).join(`|`)+`|`,s=[a(e),o];for(let e of t)s.push(a(e));return s.join(`
5
+ `)}function U(e,t){if(!Array.isArray(e))throw Error(`CsvGrid: records must be an array.`);let n=e=>e==null||typeof e==`number`&&Number.isNaN(e)?``:String(e),r,i;if(e.length&&Array.isArray(e[0])){if(!t)throw Error(`CsvGrid: columns are required with array-of-arrays records.`);r=t.map(String),i=e.map(e=>r.map((t,r)=>n(e[r])))}else r=(t??Object.keys(e[0]??{})).map(String),i=e.map(e=>r.map(t=>n(e[t])));let a=N(r,i);return{headers:r,rows:i,cols:a,headerless:!1}}function W(e){let t=[];for(let n of e.trim().split(/\s+/)){if(!n)continue;let e={kind:`fuzzy`,negate:!1};n.startsWith(`!`)&&(e.negate=!0,e.kind=`exact`,n=n.slice(1)),n.startsWith(`'`)&&(e.kind=`exact`,n=n.slice(1)),n.startsWith(`^`)&&(e.kind=`prefix`,n=n.slice(1)),n.endsWith(`$`)&&(e.kind=e.kind===`prefix`?`exact`:`suffix`,n=n.slice(0,-1)),n&&(e.cs=/[A-Z]/.test(n),e.str=e.cs?n:n.toLowerCase(),t.push(e))}return t}var G=/[\s_\-\/\\.,:;()[\]{}"']/;function K(e,t){let n=t.length,r=e.length;if(r===0)return 0;if(r>n)return-1;let i=0,a=-1;for(let o=0;o<n;o++)if(t[o]===e[i]&&(i++,i===r)){a=o;break}if(a<0)return-1;i=r-1;let o=a;for(let n=a;n>=0&&!(t[n]===e[i]&&(o=n,i--,i<0));n--);let s=100-3*(a-o+1-r)-Math.min(o,20);i=0;let c=!1;for(let n=o;n<=a&&i<r;n++)t[n]===e[i]?((n===0||G.test(t[n-1]))&&(s+=8),c&&(s+=4),c=!0,i++):c=!1;return s}function q(e,t,n){let r=e.cs?n:t,i,a=0;switch(e.kind){case`exact`:i=r.includes(e.str);break;case`prefix`:i=r.startsWith(e.str);break;case`suffix`:i=r.endsWith(e.str);break;default:{let t=K(e.str,r);i=t>=0,a=t}}return e.negate&&(i=!i),i?a:-1}function J(e,t,n,r=`equal-risk`){return r===`coverage`?ae(e,t,n):Y(e,t,n)}function Y(e,t,n){let r=(e,t)=>e.length?e[Math.floor(t*(e.length-1))]:0,i=n=>e.map((e,i)=>Math.max(t[i],r(e,n))),a=e=>e.reduce((e,t)=>e+t,0),o=i(1);if(a(o)<=n)return o;if(a(i(0))>=n)return i(0);let s=0,c=1;for(let e=0;e<32;e++){let e=(s+c)/2;a(i(e))<=n?s=e:c=e}return i(s)}function ae(e,t,n){let r=e.map((e,n)=>Math.max(t[n],e.length?e[e.length-1]:0)),i=e=>e.reduce((e,t)=>e+t,0);if(i(r)<=n)return r;if(i(t)>=n)return t.slice();let a=t.slice(),o=n-i(t),s=[];for(let n=0;n<e.length;n++){let r=oe(e[n],t[n]);for(let e=1;e<r.length;e++){let t=r[e].w-r[e-1].w,i=r[e].cells-r[e-1].cells;t>0&&i>0&&s.push({j:n,dw:t,slope:i/t})}}s.sort((e,t)=>t.slope-e.slope);for(let e of s){if(o<=0)break;let t=Math.min(e.dw,o);a[e.j]+=t,o-=t}return a}function oe(e,t){let n=e.length,r=0;for(;r<n&&e[r]<=t;)r++;let i=[{w:t,cells:r}];for(;r<n;){let t=e[r];for(;r<n&&e[r]===t;)r++;i.push({w:t,cells:r})}let a=[];for(let e of i){for(;a.length>=2;){let t=a[a.length-2],n=a[a.length-1];if((n.w-t.w)*(e.cells-t.cells)-(n.cells-t.cells)*(e.w-t.w)>=0)a.pop();else break}a.push(e)}return a}function se(e,t){let n=e.trim();if(!n)return null;if(t.type===`number`||t.type===`date`){let e=t.type===`number`?e=>{let t=_(e);return t?t.v:NaN}:e=>{let t=C(e);return t?t.t:NaN},r=/^(>=|<=|>|<|=)\s*(.+)$/.exec(n);if(r){let n=e(r[2]);if(!isNaN(n)){let e=r[1];return(r,i)=>{let a=t.values[i];if(a===null)return!1;switch(e){case`>`:return a>n;case`>=`:return a>=n;case`<`:return a<n;case`<=`:return a<=n;default:return a===n}}}}if(r=/^(.+?)\.\.(.+)$/.exec(n),r){let n=e(r[1]),i=e(r[2]);if(!isNaN(n)&&!isNaN(i))return(e,r)=>{let a=t.values[r];return a!==null&&a>=n&&a<=i}}}let r=n.toLowerCase();return(e,t)=>e.toLowerCase().includes(r)}function X(e){return e.align?`col-${e.type} align-${e.align}`:`col-${e.type}`}function Z(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}var Q=1e6,ce=2048,le=1e4;function $(e,t){let n=document.createElement(e);return t&&(n.className=t),n}return class n{constructor(e,t,n={}){let r=typeof e==`string`?document.querySelector(e):e;if(!r)throw Error(`CsvGrid: target element not found.`);this.root=r,this.opts={globalSearch:!0,columnFilters:!0,sortable:!0,statusBar:!0,expandButtons:!0,align:null,formats:null,renderCap:2048,eagerCells:262144,worker:!0,headerMode:`auto`,widthMode:`equal-risk`,maxRows:null,height:null,displayMode:`auto`,...n},this.displayMode=this.opts.displayMode===`raw`?`raw`:`auto`,this.fileName=``,this.headers=[],this.rows=[],this.cols=[],this.formatted=[],this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.loadGen=0,this.scores=[],this.layout=null,this.expandAll=!1,this.manualWidths=new Map,this.guessedHeaders=!1,this.ambiguousDateCols=[],this.view=[],this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=[],this.showAll=!1,this._worker=void 0,this._pending=new Map,this._buildScaffold(),t&&this.setData(t)}_buildScaffold(){let e=this.opts,t=this.root;if(t.classList.add(`csvgrid`),t.replaceChildren(),this.els={},e.globalSearch||e.expandButtons){let n=$(`div`,`csvgrid-toolbar`);if(e.globalSearch){let e=$(`input`,`csvgrid-search`);e.type=`text`,e.placeholder=`fzf search: term 'exact !not ^pre fix$`,e.title=`Space-separated terms AND together. Fuzzy by default; 'exact, !exclude, ^prefix, suffix$. Uppercase = case-sensitive.`,e.addEventListener(`input`,()=>this.setGlobalFilter(e.value)),e.addEventListener(`keydown`,t=>{t.key===`Escape`&&(t.preventDefault(),e.value=``,e.blur(),this.setGlobalFilter(``))}),n.appendChild(e),this.els.search=e}if(e.expandButtons){let e=$(`button`,`csvgrid-btn`);e.type=`button`,e.textContent=`Expand`,e.title=`Expand all columns to their full natural width (table scrolls horizontally)`,e.addEventListener(`click`,()=>this.expand());let t=$(`button`,`csvgrid-btn`);t.type=`button`,t.textContent=`Contract`,t.title=`Back to fitted widths (equal-risk squeeze); also clears any dragged widths`,t.addEventListener(`click`,()=>this.contract()),n.append(e,t)}t.appendChild(n)}let n=$(`div`,`csvgrid-scroll`),r=$(`table`,`csvgrid-table`),i=$(`thead`),a=$(`tbody`);r.append(i,a),n.appendChild(r),t.appendChild(n);let o=$(`div`,`csvgrid-capnote csvgrid-hidden`),s=$(`button`,`csvgrid-btn`);s.type=`button`,s.addEventListener(`click`,()=>{this.showAll=!0,this.renderBody(),this.renderStatus()}),o.appendChild(s),t.appendChild(o);let c=$(`div`,`csvgrid-error csvgrid-hidden`);t.appendChild(c);let l=null;e.statusBar===!0?(l=$(`div`,`csvgrid-status`),t.appendChild(l)):e.statusBar&&(l=e.statusBar),Object.assign(this.els,{table:r,head:i,body:a,scroll:n,capNote:o,showAllBtn:s,error:c,status:l}),r.addEventListener(`mouseover`,e=>{let t=e.target.closest(`td, th`);t&&!t.title&&t.scrollWidth>t.clientWidth&&(t.title=t.textContent)})}setData(e){let t=++this.loadGen,n=new Promise((n,r)=>{this._resolveData(e,t).then(({d:e,name:r})=>{t===this.loadGen&&(this._install(e,r),n())},e=>{t===this.loadGen&&(this._showError(e.message||String(e)),r(e))})});return n.catch(()=>{}),n}async _resolveData(e,t){if(!e||typeof e!=`object`)throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`);if(this._headerMode=e.headerMode??this.opts.headerMode,e.url!==void 0){let n=String(e.url),r=e.name??decodeURIComponent(n.split(`/`).pop()||n),i=await fetch(n);if(!i.ok)throw Error(`HTTP ${i.status}`);return{d:await this._parse(await i.text(),t,r),name:r}}if(e.csv!==void 0){let n=e.name??``;return{d:await this._parse(e.csv,t,n),name:n}}if(e.records!==void 0)return{d:U(e.records,e.columns),name:e.name??``};throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`)}_parse(e,n,r){if(e=t(e),!e.trim())throw Error(`No data found.`);let i=this._headerMode===`first-row`?!0:this._headerMode===`headerless`?!1:null,a=this.opts.worker!==!1&&e.length>=Q?this._getWorker():null;return a?(this._setStatus(`parsing ${r||`data`} (${(e.length/1e6).toFixed(1)} MB)…`),new Promise((t,r)=>{this._pending.set(n,{resolve:t,reject:r}),a.postMessage({gen:n,text:e,headerOverride:i})})):P(e,i)}_getWorker(){if(this._worker===void 0){this._worker=null;try{let t=typeof this.opts.worker==`string`?new Worker(this.opts.worker):new Worker(new URL(``+new URL(`csv-grid.worker.js`,e).href,``+e),{type:`module`});t.onmessage=e=>{let{gen:t,result:n,error:r}=e.data,i=this._pending.get(t);i&&(this._pending.delete(t),r?i.reject(Error(r)):i.resolve(n))},t.onerror=()=>{let e=[...this._pending.values()];this._pending.clear();for(let t of e)t.reject(Error(`Background parse failed.`))},this._worker=t}catch{}}return this._worker}_install(e,t){let{rows:n,cols:r}=e;if(this.opts.align){let e=ie(this.opts.align);r.forEach((t,n)=>{e[n]&&(t.align=e[n])})}if(this.opts.formats&&r.forEach((e,t)=>{e.fmt=L(this.opts.formats[t])}),this.fileName=t||``,this.guessedHeaders=e.headerless,this.ambiguousDateCols=r.filter(e=>e.ambiguousOrder).map(e=>e.name),this.headers=e.headers,this.rows=n,this.cols=r,this.formatted=Array(n.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,n.length*r.length<=this.opts.eagerCells){for(let e=0;e<n.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+n[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=Array(r.length).fill(``),this.manualWidths=new Map,this.showAll=!1,this.els.search&&(this.els.search.value=``),this.els.error.classList.add(`csvgrid-hidden`),this.renderHead(),this.layout=this.measureLayout(),this.applyLayout(),this.refresh(),this._applyHeight()}_applyHeight(){let e=this.opts;if(e.height){this.els.scroll.style.maxHeight=e.height;return}if(e.maxRows&&this.els.body.rows.length){let t=this.els.head.offsetHeight,n=this.els.body.rows[0].offsetHeight;this.els.scroll.style.maxHeight=Math.ceil(t+n*e.maxRows+2)+`px`}}_showError(e){this.els.error.textContent=e,this.els.error.classList.remove(`csvgrid-hidden`)}_setStatus(e){this.els.status&&(this.els.status.textContent=e)}destroy(){this.loadGen++,this._pending.clear(),this._worker&&=(this._worker.terminate(),null),this.root.classList.remove(`csvgrid`),this.root.replaceChildren()}setGlobalFilter(e){this.globalFilter=e,this.refresh()}clearFilters(){this.globalFilter=``,this.colFilters=this.colFilters.map(()=>``),this.els.search&&(this.els.search.value=``),this.renderHead(),this.refresh()}expand(){this.expandAll=!0,this.applyLayout()}contract(){this.expandAll=!1,this.manualWidths.clear(),this.applyLayout()}export({scope:e=`view`,format:t=`csv`,values:n=`raw`}={}){let r=e===`all`?this.rows.map((e,t)=>t):this.view,i=n===`formatted`&&r.length<=this.opts.renderCap,a=r.map(e=>i?this.getFormattedRow(e):this.cols.map((t,n)=>this.rows[e][n]??``));if(t===`md`){let e=this.cols.map(e=>e.align||(e.type===`number`?`right`:e.type===`date`?`center`:`left`));return H(this.headers,a,e)}return V(this.headers,a)}setWidthMode(e){this.opts.widthMode=e===`coverage`?`coverage`:`equal-risk`,this.applyLayout()}setDisplayMode(e){if(e=e===`raw`?`raw`:`auto`,e===this.displayMode||!this.cols.length){this.displayMode=e;return}if(this.displayMode=e,this.formatted=Array(this.rows.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.rows.length*this.cols.length<=this.opts.eagerCells){for(let e=0;e<this.rows.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+this.rows[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.layout=this.measureLayout(),this.applyLayout(),this.refresh()}measureLayout(){let e=(n._canvas||=document.createElement(`canvas`)).getContext(`2d`),t=getComputedStyle(this.els.table),r=`${t.fontSize} ${t.fontFamily}`,i=A(this.rows.length,ce),a=[],o=[];for(let t=0;t<this.cols.length;t++){e.font=`bold ${r}`,o.push(Math.max(50,Math.ceil(e.measureText(this.cols[t].name).width)+14+18)),e.font=r;let n=[];for(let r of i){let i=this.getFormattedRow(r)[t];i!==``&&n.push(Math.ceil(e.measureText(i).width)+18)}n.sort((e,t)=>e-t),a.push(n)}return{arrays:a,floors:o}}startColResize(e,t){e.preventDefault(),e.stopPropagation();let n=this.els.table,r=n.querySelectorAll(`colgroup col`)[t];if(!r)return;let i=e.clientX,a=parseFloat(r.style.width);document.body.classList.add(`csvgrid-resizing`);let o=()=>{let e=0;n.querySelectorAll(`colgroup col`).forEach(t=>{e+=parseFloat(t.style.width)}),n.style.width=e+`px`},s=e=>{let n=Math.max(24,Math.round(a+e.clientX-i));this.manualWidths.set(t,n),r.style.width=n+`px`,o()},c=()=>{document.body.classList.remove(`csvgrid-resizing`),document.removeEventListener(`mousemove`,s),document.removeEventListener(`mouseup`,c)};document.addEventListener(`mousemove`,s),document.addEventListener(`mouseup`,c)}fitColumn(e){let{arrays:t,floors:n}=this.layout,r=Math.max(n[e],t[e].length?t[e][t[e].length-1]:0);this.manualWidths.set(e,r),this.applyLayout()}applyLayout(){if(!this.layout)return;let e=this.els.table,t=this.expandAll?1/0:e.parentElement.clientWidth;if(!t)return;let n=J(this.layout.arrays,this.layout.floors,t,this.opts.widthMode);for(let[e,t]of this.manualWidths)e<n.length&&(n[e]=t);let r=e.querySelector(`colgroup`);r&&r.remove(),r=document.createElement(`colgroup`);for(let e of n){let t=document.createElement(`col`);t.style.width=e+`px`,r.appendChild(t)}e.prepend(r),e.style.tableLayout=`fixed`,e.style.width=n.reduce((e,t)=>e+t,0)+`px`}getFormattedRow(e){let t=this.formatted[e];return t||(t=this.cols.map((t,n)=>B(this.rows[e][n],t,e,this.displayMode)),this.formatted[e]=t),t}buildSearchIndexChunked(){let e=this.loadGen,t=this.rows.length,n=Array(t),r=Array(t),i=0;this.indexing=0;let a=()=>{if(e!==this.loadGen)return;let o=Math.min(t,i+le);for(;i<o;i++){let e=this.getFormattedRow(i).join(` `)+` `+this.rows[i].join(` `);n[i]=e,r[i]=e.toLowerCase()}i<t?(this.indexing=i/t,this.renderStatus(),setTimeout(a,0)):(this.searchRaw=n,this.searchLow=r,this.searchReady=!0,this.indexing=null,this.refresh())};a()}rebuildView(){let{rows:e,cols:t}=this,n=W(this.globalFilter);n.length&&!this.searchReady&&(this.indexing===null&&this.buildSearchIndexChunked(),n=[]);let r=n.some(e=>e.kind===`fuzzy`&&!e.negate),i=this.colFilters.map((e,n)=>se(e||``,t[n])),a=i.some(e=>e)||n.length,o=[];this.scores=[];for(let t=0;t<e.length;t++){let r=0;if(a){let a=!0;for(let e of n){let n=q(e,this.searchLow[t],this.searchRaw[t]);if(n<0){a=!1;break}r+=n}if(a){for(let n=0;n<i.length;n++)if(i[n]&&!i[n](e[t][n]??``,t)){a=!1;break}}if(!a)continue}this.scores[t]=r,o.push(t)}let s=this.sortCol;if(s===null&&r)o.sort((e,t)=>this.scores[t]-this.scores[e]||e-t);else if(s!==null){let e=this.cols[s],t=this.sortDir;if(e.type===`text`){let e=new Intl.Collator(`en`,{sensitivity:`base`,numeric:!0});o.sort((n,r)=>{let i=(this.rows[n][s]??``).trim(),a=(this.rows[r][s]??``).trim();return i===``||a===``?i===a?0:i===``?1:-1:t*e.compare(i,a)})}else o.sort((n,r)=>{let i=e.values[n],a=e.values[r];return i===null||a===null?i===a?0:i===null?1:-1:t*(i-a)})}this.view=o}renderHead(){let{cols:e}=this,t=this.els.head;t.innerHTML=``;let n=document.createElement(`tr`);if(e.forEach((e,t)=>{let r=document.createElement(`th`);r.className=X(e),this.opts.sortable?(r.innerHTML=`<span class="sort-arrow">${this.sortCol===t?this.sortDir===1?`▲`:`▼`:``}</span>${Z(e.name)}`,r.title=`${e.name} (${e.type}) — click to sort`,r.addEventListener(`click`,()=>this.onSort(t))):(r.innerHTML=`<span class="sort-arrow"></span>${Z(e.name)}`,r.title=`${e.name} (${e.type})`,r.classList.add(`csvgrid-nosort`));let i=document.createElement(`span`);i.className=`col-resizer`,i.title=`Drag to resize — double-click to fit content`,i.addEventListener(`mousedown`,e=>this.startColResize(e,t)),i.addEventListener(`dblclick`,e=>{e.stopPropagation(),this.fitColumn(t)}),i.addEventListener(`click`,e=>e.stopPropagation()),r.appendChild(i),n.appendChild(r)}),t.appendChild(n),!this.opts.columnFilters)return;let r=document.createElement(`tr`);r.className=`filter-row`,e.forEach((e,t)=>{let n=document.createElement(`th`),i=document.createElement(`input`);i.type=`text`,i.className=`csvgrid-filter`,i.placeholder=e.type===`text`?`filter`:`filter, >, .. `,i.value=this.colFilters[t]||``,i.addEventListener(`input`,()=>{this.colFilters[t]=i.value,i.classList.toggle(`active-filter`,i.value.trim()!==``),this.refresh()}),i.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),i.value=``,this.colFilters[t]=``,i.classList.remove(`active-filter`),i.blur(),this.refresh())}),n.appendChild(i),r.appendChild(n)}),t.appendChild(r)}renderBody(){let{cols:e,view:t}=this,n=this.showAll?t.length:Math.min(t.length,this.opts.renderCap),r=[];for(let i=0;i<n;i++){let n=t[i],a=this.getFormattedRow(n),o=e.map((e,t)=>{let n=a[t];return n===``?`<td class="${X(e)} blank">·</td>`:`<td class="${X(e)}">${Z(n)}</td>`});r.push(`<tr>${o.join(``)}</tr>`)}this.els.body.innerHTML=r.join(``);let i=this.els.capNote;t.length>n?(i.classList.remove(`csvgrid-hidden`),this.els.showAllBtn.textContent=`Showing first ${n.toLocaleString()} of ${t.length.toLocaleString()} rows — show all`):i.classList.add(`csvgrid-hidden`)}renderStatus(){if(!this.els.status)return;let e=e=>e.toLocaleString(),t=this.rows.length,n=this.view.length,r=this.showAll?n:Math.min(n,this.opts.renderCap),i=this.fileName?this.fileName+` — `:``;i+=n===t?`${e(t)} rows`:`${e(n)} of ${e(t)} rows`,i+=` × ${this.cols.length} cols`,r<n&&(i+=` — showing rows 1–${e(r)}`),this.guessedHeaders&&(i+=` (headers guessed)`),this.indexing!==null&&(i+=` — indexing search ${Math.round(this.indexing*100)}%`),this.els.status.textContent=i}refresh(){this.rebuildView(),this.renderBody(),this.renderStatus()}onSort(e){this.sortCol===e?this.sortDir===1?this.sortDir=-1:(this.sortCol=null,this.sortDir=1):(this.sortCol=e,this.sortDir=1),this.renderHead(),this.refresh()}}})();
6
+ //# sourceMappingURL=csv-grid.iife.js.map
csv_grid-3.0.7/.gitignore DELETED
@@ -1,15 +0,0 @@
1
- tests/**/*.csv
2
-
3
- node_modules/
4
- dev/tmp-big.csv
5
- python/dist/
6
-
7
- venv/
8
- .venv/
9
-
10
- __pycache__/
11
-
12
- .virtual_documents/
13
- .ipynb_checkpoints/
14
-
15
- hacks/
@@ -1,160 +0,0 @@
1
- /* csv-grid styles — self-contained, no framework. Namespaced .csvgrid-*
2
- * (structural) with table-internal classes scoped under .csvgrid .csvgrid-table.
3
- * Replicates the viewer's Bootstrap-era look so embed pages match. */
4
-
5
- .csvgrid {
6
- font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
7
- color: #212529;
8
- }
9
- .csvgrid-hidden { display: none !important; }
10
-
11
- /* Toolbar (only generated when globalSearch / expandButtons are on) */
12
- .csvgrid-toolbar {
13
- display: flex;
14
- align-items: center;
15
- gap: 8px;
16
- margin-bottom: 8px;
17
- }
18
- .csvgrid-search {
19
- flex: 1;
20
- max-width: 320px;
21
- font-size: 0.875rem;
22
- padding: 0.25rem 0.5rem;
23
- border: 1px solid #ced4da;
24
- border-radius: 0.25rem;
25
- }
26
- .csvgrid-search:focus {
27
- outline: 0;
28
- border-color: #86b7fe;
29
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
30
- }
31
- .csvgrid-btn {
32
- font-size: 0.875rem;
33
- padding: 0.25rem 0.5rem;
34
- border: 1px solid #6c757d;
35
- border-radius: 0.25rem;
36
- background: #fff;
37
- color: #6c757d;
38
- cursor: pointer;
39
- }
40
- .csvgrid-btn:hover {
41
- background: #6c757d;
42
- color: #fff;
43
- }
44
-
45
- /* Scrollable table region; hosts cap its height / add card chrome */
46
- .csvgrid-scroll { overflow: auto; }
47
-
48
- /* Widths are pinned per load via <colgroup> + table-layout: fixed (set from
49
- * JS); tight when everything fits, equal-risk truncation when it doesn't. */
50
- .csvgrid .csvgrid-table {
51
- border-collapse: collapse;
52
- width: auto;
53
- font-size: 0.8rem;
54
- line-height: 1.5;
55
- font-variant-numeric: tabular-nums;
56
- background: #fff;
57
- }
58
- .csvgrid .csvgrid-table th,
59
- .csvgrid .csvgrid-table td {
60
- padding: 0.25rem;
61
- border-bottom: 1px solid #dee2e6;
62
- text-align: left;
63
- }
64
- .csvgrid .csvgrid-table tbody tr:hover td { background: rgba(0, 0, 0, 0.075); }
65
-
66
- .csvgrid .csvgrid-table thead th {
67
- position: sticky;
68
- top: 0;
69
- z-index: 2;
70
- background: #fff;
71
- box-shadow: inset 0 -2px 0 #dee2e6;
72
- cursor: pointer;
73
- user-select: none;
74
- white-space: nowrap;
75
- overflow: hidden;
76
- text-overflow: ellipsis;
77
- }
78
- .csvgrid .csvgrid-table thead th.csvgrid-nosort { cursor: default; }
79
- .csvgrid .csvgrid-table thead th .col-resizer {
80
- /* th has overflow:hidden, so the grip must sit inside the edge */
81
- position: absolute;
82
- top: 0;
83
- right: 0;
84
- width: 7px;
85
- height: 100%;
86
- cursor: col-resize;
87
- z-index: 3;
88
- }
89
- .csvgrid .csvgrid-table thead th .col-resizer:hover { background: rgba(13, 110, 253, 0.3); }
90
- body.csvgrid-resizing { cursor: col-resize; user-select: none; }
91
-
92
- .csvgrid .csvgrid-table thead th .sort-arrow {
93
- display: inline-block;
94
- width: 1em;
95
- color: #0d6efd;
96
- }
97
-
98
- .csvgrid .csvgrid-table thead tr.filter-row th {
99
- cursor: default;
100
- box-shadow: inset 0 -2px 0 #dee2e6;
101
- top: 28px; /* sits below the (0.8rem) header row */
102
- padding: 2px 4px;
103
- }
104
- .csvgrid-filter {
105
- width: 100%;
106
- min-width: 4em;
107
- font-size: 0.78rem;
108
- padding: 1px 6px;
109
- border: 1px solid #ced4da;
110
- border-radius: 0.25rem;
111
- color: #212529;
112
- }
113
- .csvgrid-filter:focus {
114
- outline: 0;
115
- border-color: #86b7fe;
116
- box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
117
- }
118
- .csvgrid-filter.active-filter {
119
- background-color: #e7f1ff;
120
- border-color: #0d6efd;
121
- }
122
-
123
- .csvgrid .csvgrid-table td {
124
- white-space: nowrap;
125
- overflow: hidden;
126
- text-overflow: ellipsis;
127
- }
128
-
129
- .csvgrid .csvgrid-table .col-number { text-align: right; }
130
- .csvgrid .csvgrid-table .col-date { text-align: center; } /* greater_tables convention */
131
- .csvgrid .csvgrid-table .col-text { text-align: left; }
132
- /* explicit alignment (align option / markdown spec) overrides type alignment */
133
- .csvgrid .csvgrid-table .align-left { text-align: left; }
134
- .csvgrid .csvgrid-table .align-center { text-align: center; }
135
- .csvgrid .csvgrid-table .align-right { text-align: right; }
136
- .csvgrid .csvgrid-table td.blank { color: #adb5bd; }
137
-
138
- /* "Showing first N — show all" note below the table */
139
- .csvgrid-capnote {
140
- text-align: center;
141
- margin: 1rem 0;
142
- }
143
-
144
- /* Row-counts line (only when statusBar: true generates it) */
145
- .csvgrid-status {
146
- font-size: 0.75rem;
147
- color: #6c757d;
148
- padding: 3px 0;
149
- }
150
-
151
- /* Load errors ({url} fetch failures, unusable input) */
152
- .csvgrid-error {
153
- margin: 0.5rem 0;
154
- padding: 0.5rem 0.75rem;
155
- font-size: 0.875rem;
156
- color: #842029;
157
- background: #f8d7da;
158
- border: 1px solid #f5c2c7;
159
- border-radius: 0.25rem;
160
- }
@@ -1,4 +0,0 @@
1
- var CsvGrid=(function(){var e=typeof document<`u`&&document.currentScript&&document.currentScript.src||(typeof document<`u`?document.baseURI:``);function t(e){return(e??``).replace(/^\uFEFF/,``).replace(/^(?:[ \t]*(?:\r\n|\n|\r))+/,``)}function n(e){let t=[`,`,` `,`;`,`|`],n=e.split(/\r\n|\n|\r/,20).filter(e=>e.length),i=`,`,a=0;for(let e of t){let t=n.map(t=>r(t,e).length),o=t[0];if(o<2)continue;let s=o*(t.every(e=>e===o)?10:1);s>a&&(a=s,i=e)}return i}function r(e,t){let n=[],r=``,i=!1;for(let a=0;a<e.length;a++){let o=e[a];i?o===`"`?i=!1:r+=o:o===`"`?i=!0:o===t?(n.push(r),r=``):r+=o}return n.push(r),n}function i(e,t){let n=[],r=[],i=``,a=!1,o=0,s=e.length;for(;o<s;){let s=e[o];if(a){if(s===`"`){if(e[o+1]===`"`){i+=`"`,o+=2;continue}a=!1,o++;continue}i+=s,o++;continue}if(s===`"`){a=!0,o++;continue}if(s===t){r.push(i),i=``,o++;continue}if(s===`\r`||s===`
2
- `){r.push(i),i=``,n.push(r),r=[],s===`\r`&&e[o+1]===`
3
- `&&o++,o++;continue}i+=s,o++}for((i.length||r.length)&&(r.push(i),n.push(r));n.length&&n[n.length-1].every(e=>e.trim()===``);)n.pop();return n}function a(e){e=e.trim(),e.startsWith(`|`)&&(e=e.slice(1)),e.endsWith(`|`)&&!e.endsWith(`\\|`)&&(e=e.slice(0,-1));let t=[],n=``;for(let r=0;r<e.length;r++){let i=e[r];i===`\\`&&e[r+1]===`|`?(n+=`|`,r++):i===`|`?(t.push(n),n=``):n+=i}return t.push(n),t.map(e=>e.trim())}var o=/^:?-+:?$/;function s(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``);if(t.length<2||!t[0].includes(`|`))return!1;let n=a(t[1]);return n.length>0&&n.every(e=>o.test(e))}function c(e){let t=e.split(/\r\n|\n|\r/).filter(e=>e.trim()!==``),n=a(t[0]).map((e,t)=>e||`col${t+1}`),r=a(t[1]).map(e=>{let t=e.startsWith(`:`),n=e.endsWith(`:`);return t&&n?`center`:n?`right`:t?`left`:null});for(;r.length<n.length;)r.push(null);return{headers:n,rows:t.slice(2).filter(e=>e.includes(`|`)).map(e=>{let t=a(e).slice(0,n.length);for(;t.length<n.length;)t.push(``);return t}),aligns:r}}var l=/^\(?\$?-?(?:[0-9][0-9,]*(?:\.[0-9]+)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?%?\)?$/,u=/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?Z?)?$/,d=/^(\d{1,4})([\/\-.])(\d{1,2})\2(\d{1,4})$/,f=/^(\d{1,2})[ \-]([A-Za-z]{3,9})\.?,?[ \-](\d{2,4})$/,p=/^([A-Za-z]{3,9})\.?,?[ \-](\d{1,2}),?[ \-](\d{2,4})$/,m=[`january`,`february`,`march`,`april`,`may`,`june`,`july`,`august`,`september`,`october`,`november`,`december`];function h(e){if(e=e.trim(),!l.test(e))return null;let t=!1;e.startsWith(`(`)&&e.endsWith(`)`)&&(t=!0,e=e.slice(1,-1));let n=!1;e.endsWith(`%`)&&(n=!0,e=e.slice(0,-1)),e=e.replace(/[$,]/g,``);let r=parseFloat(e);if(!isFinite(r))return null;t&&(r=-r),n&&(r/=100);let i=/^([^eE]*)[eE]([+-]?\d+)$/.exec(e),a=i?i[1]:e,o=i?+i[2]:0,s=a.indexOf(`.`),c=Math.max(0,(s<0?0:a.length-s-1)-o);return n&&(c+=2),{v:r,dec:c}}function g(e){let t=e.toLowerCase(),n=m.findIndex(e=>e.startsWith(t)||t===`sept`&&e===`september`);return n<0||t.length<3?null:n+1}function _(e){return e=+e,e<100?e<50?2e3+e:1900+e:e}function v(e,t,n,r=0,i=0,a=0,o=!1){let s=new Date(e,t-1,n,r,i,a);return s.getFullYear()!==e||s.getMonth()!==t-1||s.getDate()!==+n?null:{t:s.getTime(),hasTime:o}}function y(e,t=!1){e=e.trim();let n=u.exec(e);if(n){let[,e,t,r,i,a,o]=n;return v(+e,+t,+r,+(i||0),+(a||0),+(o||0),i!==void 0)}if(n=d.exec(e),n){let[,e,,r,i]=n;if(e.length===4&&i.length<=2)return v(+e,+r,+i);if(e.length<=2&&(i.length===4||i.length===2)){let n=_(i);return+e>12&&+r<=12?v(n,+r,+e):+r>12&&+e<=12?v(n,+e,+r):t?v(n,+r,+e):v(n,+e,+r)}return null}if(n=f.exec(e),n){let e=g(n[2]);return e?v(_(n[3]),e,+n[1]):null}if(n=p.exec(e),n){let e=g(n[1]);return e?v(_(n[3]),e,+n[2]):null}return null}function b(e){let t=d.exec(e.trim());return!!t&&t[1].length<=2&&+t[1]>12&&+t[3]<=12}function x(e){return e.some(e=>{let t=(e??``).trim();return t!==``&&(h(t)!==null||y(t)!==null)})}function S(e){let t=e=>e.type===`date`?`Date`:e.type===`number`?e.format===`year`?`Year`:`Amount`:`Description`,n={},r={};e.forEach(e=>{let r=t(e);n[r]=(n[r]||0)+1}),e.forEach(e=>{let i=t(e);r[i]=(r[i]||0)+1,e.name=n[i]>1?`${i} ${r[i]}`:i})}var C=/\b(year|yr|vintage|cohort)\b/i,w=/\b(amount|amt|balance|bal|price|cost|fee|fees|charge|paid|payment|debit|credit|total|premium|loss|salary|wage|income|expense|revenue|usd|gbp|eur|cad)\b|[$£€]/i;function T(e,t,n){let r=t.filter(e=>e!==null),i=r.every(e=>Number.isInteger(e));if(i&&r.length)return C.test(e)||r.every(e=>e>=1800&&e<=2100)?{format:`year`,dec:0}:w.test(e)?{format:`float`,dec:2}:{format:`int`,dec:0};if(!i&&w.test(e))return{format:`float`,dec:2};let a=0,o=0,s=1/0,c=0;for(let e of r){if(e===0)continue;let t=Math.abs(e);a++,t>o&&(o=t),t<s&&(s=t),c+=t}if(!a)return{format:`float`,dec:Math.min(n,6)};if(!i&&n<=2&&o<1e5)return{format:`float`,dec:2};if(o/s>1e6)return{format:`eng`,dec:0};let l=c/a;return{format:`float`,dec:Math.max(0,Math.min(n,3-Math.floor(Math.log10(l)),6))}}var E={"-9":`n`,"-6":`µ`,"-3":`m`,0:``,3:`k`,6:`M`,9:`G`,12:`T`};function D(e){if(e===0)return`0`;let t=Math.abs(e),n=Math.floor(Math.log10(t)/3)*3;n=Math.max(-9,Math.min(12,n));let r=t/10**n;return(e<0?`-`:``)+Number(r.toPrecision(3))+E[n]}function O(e,t){return e.map((e,n)=>{let r=!0,i=!0,a=0,o=0,s=!1,c=!1,l=Array(t.length).fill(null),u=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let d=(t[e][n]??``).trim();if(d!==``){if(o++,r){let t=h(d);t?(l[e]=t.v,t.dec>a&&(a=t.dec)):r=!1}if(i){let t=y(d,!1);t?(u[e]=t.t,c||=t.hasTime,!s&&b(d)&&(s=!0)):i=!1}if(!r&&!i)break}}if(o===0)return{name:e,type:`text`,values:null};if(r){let t=T(e,l,a);return{name:e,type:`number`,format:t.format,dec:t.dec,values:l}}if(i){if(s){u=Array(t.length).fill(null);for(let e=0;e<t.length;e++){let r=(t[e][n]??``).trim();if(r===``)continue;let i=y(r,!0);i&&(u[e]=i.t)}}return{name:e,type:`date`,hasTime:c,values:u}}return{name:e,type:`text`,values:null}})}function k(e,t=null){let r,a,o=null,l;if(s(e)){if({headers:r,rows:a,aligns:o}=c(e),l=t===!1,l&&(a=[r,...a],r=r.map((e,t)=>`col${t+1}`)),!a.length)throw Error(`Markdown table has no data rows.`)}else{let o=i(e,n(e));if(o.length<2)throw Error(`Need a header row and at least one data row.`);l=t===null?x(o[0]):!t,r=l?o[0].map((e,t)=>`col${t+1}`):o[0].map((e,t)=>e.trim()||`col${t+1}`),a=(l?o:o.slice(1)).map(e=>{if(e.length===r.length)return e;let t=e.slice(0,r.length);for(;t.length<r.length;)t.push(``);return t})}let u=O(r,a);return l&&S(u),o&&u.forEach((e,t)=>{o[t]&&(e.align=o[t])}),{headers:u.map(e=>e.name),rows:a,cols:u,headerless:l}}var A=new Map;function j(e){let t=A.get(e);return t||(t=new Intl.NumberFormat(`en-US`,{minimumFractionDigits:e,maximumFractionDigits:e}),A.set(e,t)),t}function M(e){if(e==null||e===``)return null;if(e===`year`||e===`eng`)return{kind:e};let t=/^(,)?(?:\.(\d+))?([fd%es])$/.exec(e);if(!t)throw Error(`CsvGrid: unrecognized format spec '${e}'`);return{kind:t[3],comma:!!t[1],dec:t[2]===void 0?null:+t[2]}}var N=[[0xe8d4a51000,`T`],[1e9,`G`],[1e6,`M`],[1e3,`k`],[1,``],[.001,`m`],[1e-6,`µ`],[1e-9,`n`]];function P(e,t){switch(t.kind){case`year`:return String(e);case`eng`:return D(e);case`d`:{let n=Math.round(e);return t.comma?j(0).format(n):String(n)}case`f`:{let n=t.dec??2;return t.comma?j(n).format(e):e.toFixed(n)}case`%`:{let n=t.dec??0,r=e*100;return(t.comma?j(n).format(r):r.toFixed(n))+`%`}case`e`:return e.toExponential(t.dec??2);case`s`:{if(t.dec===null||t.dec===void 0)return D(e);if(e===0)return 0 .toFixed(t.dec);let n=Math.abs(e);for(let[r,i]of N)if(n>=r)return(e/r).toFixed(t.dec)+i;return(e/1e-9).toFixed(t.dec)+`n`}}}function F(e){return[...e].map(e=>({l:`left`,r:`right`,c:`center`})[e]??null)}function I(e,t,n){if(e=(e??``).trim(),e===``)return``;if(t.type===`number`){let r=t.values[n];return r===null?e:t.fmt?P(r,t.fmt):t.format===`year`?String(r):t.format===`eng`?D(r):j(t.dec).format(r)}if(t.type===`date`){let r=t.values[n];if(r===null)return e;let i=new Date(r),a=e=>String(e).padStart(2,`0`),o=`${i.getFullYear()}-${a(i.getMonth()+1)}-${a(i.getDate())}`;return t.hasTime&&(o+=` ${a(i.getHours())}:${a(i.getMinutes())}`),o}return e}function L(e,t){if(!Array.isArray(e))throw Error(`CsvGrid: records must be an array.`);let n=e=>e==null||typeof e==`number`&&Number.isNaN(e)?``:String(e),r,i;if(e.length&&Array.isArray(e[0])){if(!t)throw Error(`CsvGrid: columns are required with array-of-arrays records.`);r=t.map(String),i=e.map(e=>r.map((t,r)=>n(e[r])))}else r=(t??Object.keys(e[0]??{})).map(String),i=e.map(e=>r.map(t=>n(e[t])));let a=O(r,i);return{headers:r,rows:i,cols:a,headerless:!1}}function R(e){let t=[];for(let n of e.trim().split(/\s+/)){if(!n)continue;let e={kind:`fuzzy`,negate:!1};n.startsWith(`!`)&&(e.negate=!0,e.kind=`exact`,n=n.slice(1)),n.startsWith(`'`)&&(e.kind=`exact`,n=n.slice(1)),n.startsWith(`^`)&&(e.kind=`prefix`,n=n.slice(1)),n.endsWith(`$`)&&(e.kind=e.kind===`prefix`?`exact`:`suffix`,n=n.slice(0,-1)),n&&(e.cs=/[A-Z]/.test(n),e.str=e.cs?n:n.toLowerCase(),t.push(e))}return t}var z=/[\s_\-\/\\.,:;()[\]{}"']/;function B(e,t){let n=t.length,r=e.length;if(r===0)return 0;if(r>n)return-1;let i=0,a=-1;for(let o=0;o<n;o++)if(t[o]===e[i]&&(i++,i===r)){a=o;break}if(a<0)return-1;i=r-1;let o=a;for(let n=a;n>=0&&!(t[n]===e[i]&&(o=n,i--,i<0));n--);let s=100-3*(a-o+1-r)-Math.min(o,20);i=0;let c=!1;for(let n=o;n<=a&&i<r;n++)t[n]===e[i]?((n===0||z.test(t[n-1]))&&(s+=8),c&&(s+=4),c=!0,i++):c=!1;return s}function V(e,t,n){let r=e.cs?n:t,i,a=0;switch(e.kind){case`exact`:i=r.includes(e.str);break;case`prefix`:i=r.startsWith(e.str);break;case`suffix`:i=r.endsWith(e.str);break;default:{let t=B(e.str,r);i=t>=0,a=t}}return e.negate&&(i=!i),i?a:-1}function H(e,t,n){let r=(e,t)=>e.length?e[Math.floor(t*(e.length-1))]:0,i=n=>e.map((e,i)=>Math.max(t[i],r(e,n))),a=e=>e.reduce((e,t)=>e+t,0),o=i(1);if(a(o)<=n)return o;if(a(i(0))>=n)return i(0);let s=0,c=1;for(let e=0;e<32;e++){let e=(s+c)/2;a(i(e))<=n?s=e:c=e}return i(s)}function U(e,t){if(e<=t)return Array.from({length:e},(e,t)=>t);let n=Array(t),r=e/t;for(let e=0;e<t;e++)n[e]=Math.floor(e*r);return n}function W(e,t){let n=e.trim();if(!n)return null;if(t.type===`number`||t.type===`date`){let e=t.type===`number`?e=>{let t=h(e);return t?t.v:NaN}:e=>{let t=y(e);return t?t.t:NaN},r=/^(>=|<=|>|<|=)\s*(.+)$/.exec(n);if(r){let n=e(r[2]);if(!isNaN(n)){let e=r[1];return(r,i)=>{let a=t.values[i];if(a===null)return!1;switch(e){case`>`:return a>n;case`>=`:return a>=n;case`<`:return a<n;case`<=`:return a<=n;default:return a===n}}}}if(r=/^(.+?)\.\.(.+)$/.exec(n),r){let n=e(r[1]),i=e(r[2]);if(!isNaN(n)&&!isNaN(i))return(e,r)=>{let a=t.values[r];return a!==null&&a>=n&&a<=i}}}let r=n.toLowerCase();return(e,t)=>e.toLowerCase().includes(r)}function G(e){return e.align?`col-${e.type} align-${e.align}`:`col-${e.type}`}function K(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}var q=1e6,J=2e3,Y=1e4;function X(e,t){let n=document.createElement(e);return t&&(n.className=t),n}return class n{constructor(e,t,n={}){let r=typeof e==`string`?document.querySelector(e):e;if(!r)throw Error(`CsvGrid: target element not found.`);this.root=r,this.opts={globalSearch:!0,columnFilters:!0,sortable:!0,statusBar:!0,expandButtons:!0,align:null,formats:null,renderCap:2e3,eagerCells:2e5,worker:!0,headerMode:`auto`,...n},this.fileName=``,this.headers=[],this.rows=[],this.cols=[],this.formatted=[],this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,this.loadGen=0,this.scores=[],this.layout=null,this.expandAll=!1,this.manualWidths=new Map,this.guessedHeaders=!1,this.view=[],this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=[],this.showAll=!1,this._worker=void 0,this._pending=new Map,this._buildScaffold(),t&&this.setData(t)}_buildScaffold(){let e=this.opts,t=this.root;if(t.classList.add(`csvgrid`),t.replaceChildren(),this.els={},e.globalSearch||e.expandButtons){let n=X(`div`,`csvgrid-toolbar`);if(e.globalSearch){let e=X(`input`,`csvgrid-search`);e.type=`text`,e.placeholder=`fzf search: term 'exact !not ^pre fix$`,e.title=`Space-separated terms AND together. Fuzzy by default; 'exact, !exclude, ^prefix, suffix$. Uppercase = case-sensitive.`,e.addEventListener(`input`,()=>this.setGlobalFilter(e.value)),e.addEventListener(`keydown`,t=>{t.key===`Escape`&&(t.preventDefault(),e.value=``,e.blur(),this.setGlobalFilter(``))}),n.appendChild(e),this.els.search=e}if(e.expandButtons){let e=X(`button`,`csvgrid-btn`);e.type=`button`,e.textContent=`Expand`,e.title=`Expand all columns to their full natural width (table scrolls horizontally)`,e.addEventListener(`click`,()=>this.expand());let t=X(`button`,`csvgrid-btn`);t.type=`button`,t.textContent=`Contract`,t.title=`Back to fitted widths (equal-risk squeeze); also clears any dragged widths`,t.addEventListener(`click`,()=>this.contract()),n.append(e,t)}t.appendChild(n)}let n=X(`div`,`csvgrid-scroll`),r=X(`table`,`csvgrid-table`),i=X(`thead`),a=X(`tbody`);r.append(i,a),n.appendChild(r),t.appendChild(n);let o=X(`div`,`csvgrid-capnote csvgrid-hidden`),s=X(`button`,`csvgrid-btn`);s.type=`button`,s.addEventListener(`click`,()=>{this.showAll=!0,this.renderBody(),this.renderStatus()}),o.appendChild(s),t.appendChild(o);let c=X(`div`,`csvgrid-error csvgrid-hidden`);t.appendChild(c);let l=null;e.statusBar===!0?(l=X(`div`,`csvgrid-status`),t.appendChild(l)):e.statusBar&&(l=e.statusBar),Object.assign(this.els,{table:r,head:i,body:a,scroll:n,capNote:o,showAllBtn:s,error:c,status:l}),r.addEventListener(`mouseover`,e=>{let t=e.target.closest(`td, th`);t&&!t.title&&t.scrollWidth>t.clientWidth&&(t.title=t.textContent)})}setData(e){let t=++this.loadGen,n=new Promise((n,r)=>{this._resolveData(e,t).then(({d:e,name:r})=>{t===this.loadGen&&(this._install(e,r),n())},e=>{t===this.loadGen&&(this._showError(e.message||String(e)),r(e))})});return n.catch(()=>{}),n}async _resolveData(e,t){if(!e||typeof e!=`object`)throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`);if(this._headerMode=e.headerMode??this.opts.headerMode,e.url!==void 0){let n=String(e.url),r=e.name??decodeURIComponent(n.split(`/`).pop()||n),i=await fetch(n);if(!i.ok)throw Error(`HTTP ${i.status}`);return{d:await this._parse(await i.text(),t,r),name:r}}if(e.csv!==void 0){let n=e.name??``;return{d:await this._parse(e.csv,t,n),name:n}}if(e.records!==void 0)return{d:L(e.records,e.columns),name:e.name??``};throw Error(`CsvGrid: data must be {csv}, {records[, columns]}, or {url}.`)}_parse(e,n,r){if(e=t(e),!e.trim())throw Error(`No data found.`);let i=this._headerMode===`first-row`?!0:this._headerMode===`headerless`?!1:null,a=this.opts.worker!==!1&&e.length>=q?this._getWorker():null;return a?(this._setStatus(`parsing ${r||`data`} (${(e.length/1e6).toFixed(1)} MB)…`),new Promise((t,r)=>{this._pending.set(n,{resolve:t,reject:r}),a.postMessage({gen:n,text:e,headerOverride:i})})):k(e,i)}_getWorker(){if(this._worker===void 0){this._worker=null;try{let t=typeof this.opts.worker==`string`?new Worker(this.opts.worker):new Worker(new URL(``+new URL(`csv-grid.worker.js`,e).href,``+e),{type:`module`});t.onmessage=e=>{let{gen:t,result:n,error:r}=e.data,i=this._pending.get(t);i&&(this._pending.delete(t),r?i.reject(Error(r)):i.resolve(n))},t.onerror=()=>{let e=[...this._pending.values()];this._pending.clear();for(let t of e)t.reject(Error(`Background parse failed.`))},this._worker=t}catch{}}return this._worker}_install(e,t){let{rows:n,cols:r}=e;if(this.opts.align){let e=F(this.opts.align);r.forEach((t,n)=>{e[n]&&(t.align=e[n])})}if(this.opts.formats&&r.forEach((e,t)=>{e.fmt=M(this.opts.formats[t])}),this.fileName=t||``,this.guessedHeaders=e.headerless,this.headers=e.headers,this.rows=n,this.cols=r,this.formatted=Array(n.length),this.searchRaw=null,this.searchLow=null,this.searchReady=!1,this.indexing=null,n.length*r.length<=this.opts.eagerCells){for(let e=0;e<n.length;e++)this.getFormattedRow(e);this.searchRaw=this.formatted.map((e,t)=>e.join(` `)+` `+n[t].join(` `)),this.searchLow=this.searchRaw.map(e=>e.toLowerCase()),this.searchReady=!0}this.sortCol=null,this.sortDir=1,this.globalFilter=``,this.colFilters=Array(r.length).fill(``),this.manualWidths=new Map,this.showAll=!1,this.els.search&&(this.els.search.value=``),this.els.error.classList.add(`csvgrid-hidden`),this.renderHead(),this.layout=this.measureLayout(),this.applyLayout(),this.refresh()}_showError(e){this.els.error.textContent=e,this.els.error.classList.remove(`csvgrid-hidden`)}_setStatus(e){this.els.status&&(this.els.status.textContent=e)}destroy(){this.loadGen++,this._pending.clear(),this._worker&&=(this._worker.terminate(),null),this.root.classList.remove(`csvgrid`),this.root.replaceChildren()}setGlobalFilter(e){this.globalFilter=e,this.refresh()}clearFilters(){this.globalFilter=``,this.colFilters=this.colFilters.map(()=>``),this.els.search&&(this.els.search.value=``),this.renderHead(),this.refresh()}expand(){this.expandAll=!0,this.applyLayout()}contract(){this.expandAll=!1,this.manualWidths.clear(),this.applyLayout()}measureLayout(){let e=(n._canvas||=document.createElement(`canvas`)).getContext(`2d`),t=getComputedStyle(this.els.table),r=`${t.fontSize} ${t.fontFamily}`,i=U(this.rows.length,J),a=[],o=[];for(let t=0;t<this.cols.length;t++){e.font=`bold ${r}`,o.push(Math.max(50,Math.ceil(e.measureText(this.cols[t].name).width)+14+18)),e.font=r;let n=[];for(let r of i){let i=this.getFormattedRow(r)[t];i!==``&&n.push(Math.ceil(e.measureText(i).width)+18)}n.sort((e,t)=>e-t),a.push(n)}return{arrays:a,floors:o}}startColResize(e,t){e.preventDefault(),e.stopPropagation();let n=this.els.table,r=n.querySelectorAll(`colgroup col`)[t];if(!r)return;let i=e.clientX,a=parseFloat(r.style.width);document.body.classList.add(`csvgrid-resizing`);let o=()=>{let e=0;n.querySelectorAll(`colgroup col`).forEach(t=>{e+=parseFloat(t.style.width)}),n.style.width=e+`px`},s=e=>{let n=Math.max(24,Math.round(a+e.clientX-i));this.manualWidths.set(t,n),r.style.width=n+`px`,o()},c=()=>{document.body.classList.remove(`csvgrid-resizing`),document.removeEventListener(`mousemove`,s),document.removeEventListener(`mouseup`,c)};document.addEventListener(`mousemove`,s),document.addEventListener(`mouseup`,c)}fitColumn(e){let{arrays:t,floors:n}=this.layout,r=Math.max(n[e],t[e].length?t[e][t[e].length-1]:0);this.manualWidths.set(e,r),this.applyLayout()}applyLayout(){if(!this.layout)return;let e=this.els.table,t=this.expandAll?1/0:e.parentElement.clientWidth;if(!t)return;let n=H(this.layout.arrays,this.layout.floors,t);for(let[e,t]of this.manualWidths)e<n.length&&(n[e]=t);let r=e.querySelector(`colgroup`);r&&r.remove(),r=document.createElement(`colgroup`);for(let e of n){let t=document.createElement(`col`);t.style.width=e+`px`,r.appendChild(t)}e.prepend(r),e.style.tableLayout=`fixed`,e.style.width=n.reduce((e,t)=>e+t,0)+`px`}getFormattedRow(e){let t=this.formatted[e];return t||(t=this.cols.map((t,n)=>I(this.rows[e][n],t,e)),this.formatted[e]=t),t}buildSearchIndexChunked(){let e=this.loadGen,t=this.rows.length,n=Array(t),r=Array(t),i=0;this.indexing=0;let a=()=>{if(e!==this.loadGen)return;let o=Math.min(t,i+Y);for(;i<o;i++){let e=this.getFormattedRow(i).join(` `)+` `+this.rows[i].join(` `);n[i]=e,r[i]=e.toLowerCase()}i<t?(this.indexing=i/t,this.renderStatus(),setTimeout(a,0)):(this.searchRaw=n,this.searchLow=r,this.searchReady=!0,this.indexing=null,this.refresh())};a()}rebuildView(){let{rows:e,cols:t}=this,n=R(this.globalFilter);n.length&&!this.searchReady&&(this.indexing===null&&this.buildSearchIndexChunked(),n=[]);let r=n.some(e=>e.kind===`fuzzy`&&!e.negate),i=this.colFilters.map((e,n)=>W(e||``,t[n])),a=i.some(e=>e)||n.length,o=[];this.scores=[];for(let t=0;t<e.length;t++){let r=0;if(a){let a=!0;for(let e of n){let n=V(e,this.searchLow[t],this.searchRaw[t]);if(n<0){a=!1;break}r+=n}if(a){for(let n=0;n<i.length;n++)if(i[n]&&!i[n](e[t][n]??``,t)){a=!1;break}}if(!a)continue}this.scores[t]=r,o.push(t)}let s=this.sortCol;if(s===null&&r)o.sort((e,t)=>this.scores[t]-this.scores[e]||e-t);else if(s!==null){let e=this.cols[s],t=this.sortDir;if(e.type===`text`){let e=new Intl.Collator(`en`,{sensitivity:`base`,numeric:!0});o.sort((n,r)=>{let i=(this.rows[n][s]??``).trim(),a=(this.rows[r][s]??``).trim();return i===``||a===``?i===a?0:i===``?1:-1:t*e.compare(i,a)})}else o.sort((n,r)=>{let i=e.values[n],a=e.values[r];return i===null||a===null?i===a?0:i===null?1:-1:t*(i-a)})}this.view=o}renderHead(){let{cols:e}=this,t=this.els.head;t.innerHTML=``;let n=document.createElement(`tr`);if(e.forEach((e,t)=>{let r=document.createElement(`th`);r.className=G(e),this.opts.sortable?(r.innerHTML=`<span class="sort-arrow">${this.sortCol===t?this.sortDir===1?`▲`:`▼`:``}</span>${K(e.name)}`,r.title=`${e.name} (${e.type}) — click to sort`,r.addEventListener(`click`,()=>this.onSort(t))):(r.innerHTML=`<span class="sort-arrow"></span>${K(e.name)}`,r.title=`${e.name} (${e.type})`,r.classList.add(`csvgrid-nosort`));let i=document.createElement(`span`);i.className=`col-resizer`,i.title=`Drag to resize — double-click to fit content`,i.addEventListener(`mousedown`,e=>this.startColResize(e,t)),i.addEventListener(`dblclick`,e=>{e.stopPropagation(),this.fitColumn(t)}),i.addEventListener(`click`,e=>e.stopPropagation()),r.appendChild(i),n.appendChild(r)}),t.appendChild(n),!this.opts.columnFilters)return;let r=document.createElement(`tr`);r.className=`filter-row`,e.forEach((e,t)=>{let n=document.createElement(`th`),i=document.createElement(`input`);i.type=`text`,i.className=`csvgrid-filter`,i.placeholder=e.type===`text`?`filter`:`filter, >, .. `,i.value=this.colFilters[t]||``,i.addEventListener(`input`,()=>{this.colFilters[t]=i.value,i.classList.toggle(`active-filter`,i.value.trim()!==``),this.refresh()}),i.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),i.value=``,this.colFilters[t]=``,i.classList.remove(`active-filter`),i.blur(),this.refresh())}),n.appendChild(i),r.appendChild(n)}),t.appendChild(r)}renderBody(){let{cols:e,view:t}=this,n=this.showAll?t.length:Math.min(t.length,this.opts.renderCap),r=[];for(let i=0;i<n;i++){let n=t[i],a=this.getFormattedRow(n),o=e.map((e,t)=>{let n=a[t];return n===``?`<td class="${G(e)} blank">·</td>`:`<td class="${G(e)}">${K(n)}</td>`});r.push(`<tr>${o.join(``)}</tr>`)}this.els.body.innerHTML=r.join(``);let i=this.els.capNote;t.length>n?(i.classList.remove(`csvgrid-hidden`),this.els.showAllBtn.textContent=`Showing first ${n.toLocaleString()} of ${t.length.toLocaleString()} rows — show all`):i.classList.add(`csvgrid-hidden`)}renderStatus(){if(!this.els.status)return;let e=e=>e.toLocaleString(),t=this.rows.length,n=this.view.length,r=this.showAll?n:Math.min(n,this.opts.renderCap),i=this.fileName?this.fileName+` — `:``;i+=n===t?`${e(t)} rows`:`${e(n)} of ${e(t)} rows`,i+=` × ${this.cols.length} cols`,r<n&&(i+=` — showing rows 1–${e(r)}`),this.guessedHeaders&&(i+=` (headers guessed)`),this.indexing!==null&&(i+=` — indexing search ${Math.round(this.indexing*100)}%`),this.els.status.textContent=i}refresh(){this.rebuildView(),this.renderBody(),this.renderStatus()}onSort(e){this.sortCol===e?this.sortDir===1?this.sortDir=-1:(this.sortCol=null,this.sortDir=1):(this.sortCol=e,this.sortDir=1),this.renderHead(),this.refresh()}}})();
4
- //# sourceMappingURL=csv-grid.iife.js.map
File without changes
File without changes