csv-grid 3.1.0__tar.gz → 3.3.1__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.1.0
3
+ Version: 3.3.1
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/
@@ -62,26 +62,28 @@ show(df, align="llrcr", fmt=[None, None, ",d", "year", ",.2f"])
62
62
  html = to_html(df, name="results.df", assets="inline") # fragment string
63
63
  ```
64
64
 
65
- - `show(df, **options)` displays via IPython. The JS + CSS assets are
66
- emitted once per kernel session (so once per rendered page); pass
67
- `assets="inline"` to force re-emission, or `assets="https://…/base"`
68
- to load them from a URL instead of inlining.
69
- - `to_html(df, **options)` returns an HTML fragment. The first fragment
70
- on a page should carry the assets (`assets="inline"` — the default —
71
- or a base URL); pass `assets=False` for subsequent tables.
65
+ - `show(df, **options)` displays via IPython. Each grid carries the JS +
66
+ CSS via an idempotent `<head>` guard (`assets="inline"`, the default),
67
+ so fragments are self-contained and re-running/clearing a notebook cell
68
+ can't strip a shared stylesheet. Use `assets="https://…/base"` to link
69
+ the assets from a URL instead, or `assets=False` if they are already on
70
+ the page.
71
+ - `to_html(df, **options)` returns a self-contained HTML fragment;
72
+ fragments compose freely (no need to mark a "first" one).
72
73
  - `payload(df)` returns the `{records, columns}` dict the grid consumes,
73
74
  if you want to ship data yourself.
74
75
  - Options mirror the JS API in snake_case: `global_search`,
75
76
  `column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
76
77
  (`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
77
78
  `'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
78
- or `'coverage'` to maximize the count of fully-shown cells), `rows`
79
+ or `'coverage'` to maximize the count of fully-shown cells),
80
+ `display_mode` (`'auto'` formatted / `'raw'` verbatim), `rows`
79
81
  (cap the viewport to ~N rows, vertical scroll for the rest) /
80
82
  `max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
81
83
  `worker` (default False — data is inlined), plus `name` (status line)
82
84
  and `index` (include the DataFrame index as leading columns). Dark mode
83
85
  follows the host page (`prefers-color-scheme`; JupyterLab dark themes
84
- included).
86
+ included) unless `theme="light"`/`"dark"` forces it.
85
87
 
86
88
  Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
87
89
  non-midnight times); integral float columns are emitted as integers so
@@ -36,26 +36,28 @@ show(df, align="llrcr", fmt=[None, None, ",d", "year", ",.2f"])
36
36
  html = to_html(df, name="results.df", assets="inline") # fragment string
37
37
  ```
38
38
 
39
- - `show(df, **options)` displays via IPython. The JS + CSS assets are
40
- emitted once per kernel session (so once per rendered page); pass
41
- `assets="inline"` to force re-emission, or `assets="https://…/base"`
42
- to load them from a URL instead of inlining.
43
- - `to_html(df, **options)` returns an HTML fragment. The first fragment
44
- on a page should carry the assets (`assets="inline"` — the default —
45
- or a base URL); pass `assets=False` for subsequent tables.
39
+ - `show(df, **options)` displays via IPython. Each grid carries the JS +
40
+ CSS via an idempotent `<head>` guard (`assets="inline"`, the default),
41
+ so fragments are self-contained and re-running/clearing a notebook cell
42
+ can't strip a shared stylesheet. Use `assets="https://…/base"` to link
43
+ the assets from a URL instead, or `assets=False` if they are already on
44
+ the page.
45
+ - `to_html(df, **options)` returns a self-contained HTML fragment;
46
+ fragments compose freely (no need to mark a "first" one).
46
47
  - `payload(df)` returns the `{records, columns}` dict the grid consumes,
47
48
  if you want to ship data yourself.
48
49
  - Options mirror the JS API in snake_case: `global_search`,
49
50
  `column_filters`, `sortable`, `status_bar`, `expand_buttons`, `align`
50
51
  (`'llrcr…'`), `formats`/`fmt` (per-column `[,][.N](f|d|%|e|s)`,
51
52
  `'year'`, `'eng'`, None = auto), `width_mode` (`'equal-risk'` default,
52
- or `'coverage'` to maximize the count of fully-shown cells), `rows`
53
+ or `'coverage'` to maximize the count of fully-shown cells),
54
+ `display_mode` (`'auto'` formatted / `'raw'` verbatim), `rows`
53
55
  (cap the viewport to ~N rows, vertical scroll for the rest) /
54
56
  `max_height` (raw CSS, e.g. `'400px'`), `render_cap`, `eager_cells`,
55
57
  `worker` (default False — data is inlined), plus `name` (status line)
56
58
  and `index` (include the DataFrame index as leading columns). Dark mode
57
59
  follows the host page (`prefers-color-scheme`; JupyterLab dark themes
58
- included).
60
+ included) unless `theme="light"`/`"dark"` forces it.
59
61
 
60
62
  Dates are emitted ISO (`yyyy-mm-dd`, with `hh:mm` only when a column has
61
63
  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.1.0"
3
+ version = "3.3.1"
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.1.0"
21
+ __version__ = "3.3.1"
22
22
  __all__ = ["show", "to_html", "payload"]
23
23
 
24
24
  # python snake_case -> CsvGrid option names (see src/grid/grid.js)
@@ -31,6 +31,7 @@ _OPTION_MAP = {
31
31
  "align": "align",
32
32
  "formats": "formats",
33
33
  "width_mode": "widthMode",
34
+ "display_mode": "displayMode",
34
35
  "rows": "maxRows",
35
36
  "max_height": "height",
36
37
  "render_cap": "renderCap",
@@ -112,20 +113,49 @@ def _asset_text(fname: str) -> str:
112
113
  return resources.files("csv_grid").joinpath("assets", fname).read_text(encoding="utf-8")
113
114
 
114
115
 
115
- def _assets_fragment(assets) -> str:
116
- """'inline' -> embed css+js; a string -> <link>/<script src> against
117
- that base URL; False/None -> '' (already on the page). The iife build
118
- is used (not umd): pages rendered by Quarto/Jupyter can carry
119
- RequireJS, which hijacks a umd wrapper via define.amd and the global
120
- CsvGrid never appears."""
116
+ def _js_str(text: str) -> str:
117
+ """A JS string literal (double-quoted, JSON-escaped) safe inside a
118
+ <script>: the `</` -> `<\\/` guard stops an embedded '</script>' in the
119
+ payload from closing our tag."""
120
+ return json.dumps(text, ensure_ascii=False).replace("</", "<\\/")
121
+
122
+
123
+ def _assets_html(assets) -> str:
124
+ """The CSS + JS the grid needs, emitted with EVERY grid (no per-kernel
125
+ state — that was the notebook bug: assets parked in one cell's output
126
+ vanish when that cell is cleared/re-run, and every grid shares them).
127
+
128
+ 'inline' (default): a guard that idempotently injects the CSS and the
129
+ iife into the document <head>. <head> lives outside cell output, so
130
+ clearing/re-running a cell can't strip the assets, and any grid
131
+ re-establishes them if missing. The injected <script> sets its code via
132
+ textContent, so it runs synchronously on insertion — window.CsvGrid is
133
+ defined before the grid-construction script that follows.
134
+
135
+ A base-URL string: plain <link>/<script src> tags (parser-ordered, so
136
+ they run before construction; the browser caches them, so re-emitting
137
+ per grid is cheap). False/None: nothing (assets already on the page).
138
+
139
+ The iife build is used (not umd): Quarto/Jupyter pages can carry
140
+ RequireJS, whose define.amd hijacks a umd wrapper so window.CsvGrid
141
+ never appears."""
142
+ if not assets:
143
+ return ""
121
144
  if assets == "inline":
122
- return (f"<style>\n{_asset_text('csv-grid.css')}\n</style>\n"
123
- f"<script>\n{_asset_text('csv-grid.iife.js')}\n</script>")
124
- if assets:
125
- base = str(assets).rstrip("/")
126
- return (f'<link rel="stylesheet" href="{base}/csv-grid.css">\n'
127
- f'<script src="{base}/csv-grid.iife.js"></script>')
128
- return ""
145
+ css = _js_str(_asset_text("csv-grid.css"))
146
+ js = _js_str(_asset_text("csv-grid.iife.js"))
147
+ body = (
148
+ "if(!document.getElementById('csvgrid-css')){"
149
+ "var s=document.createElement('style');s.id='csvgrid-css';"
150
+ f"s.textContent={css};document.head.appendChild(s);}}"
151
+ "if(!window.CsvGrid){"
152
+ "var j=document.createElement('script');j.id='csvgrid-js';"
153
+ f"j.textContent={js};document.head.appendChild(j);}}"
154
+ )
155
+ return f"<script>(function(){{{body}}})();</script>"
156
+ base = str(assets).rstrip("/")
157
+ return (f'<link rel="stylesheet" href="{base}/csv-grid.css">\n'
158
+ f'<script src="{base}/csv-grid.iife.js"></script>')
129
159
 
130
160
 
131
161
  def _dump(obj) -> str:
@@ -134,40 +164,37 @@ def _dump(obj) -> str:
134
164
 
135
165
 
136
166
  def to_html(df, *, name: str | None = None, assets="inline",
137
- index: bool = False, **options) -> str:
138
- """HTML fragment rendering `df` as a CsvGrid. The first fragment on a
139
- page should carry the assets (default 'inline'; or a base URL hosting
140
- csv-grid.umd.js + csv-grid.css); pass assets=False for later tables.
141
- Options are the grid's, in snake_case (see _OPTION_MAP); `fmt` is an
167
+ index: bool = False, theme: str = "auto", **options) -> str:
168
+ """HTML fragment rendering `df` as a CsvGrid, self-contained by default.
169
+
170
+ Every fragment carries the assets via an idempotent guard (see
171
+ ``assets``), so fragments compose freely no need to mark a "first"
172
+ one. Options are the grid's, in snake_case (see ``show``); `fmt` is an
142
173
  alias for `formats`; `worker` defaults to False (data is inlined).
174
+ `assets='inline'` embeds the CSS + JS (injected once into <head>); a
175
+ base-URL string links them; False omits them. `theme` forces the color
176
+ scheme via ``data-theme`` ('auto' follows prefers-color-scheme).
143
177
  """
144
178
  opts = _map_options(options)
145
179
  opts.setdefault("worker", False)
146
180
  div = f"csvgrid-{uuid.uuid4().hex[:12]}"
181
+ theme_attr = f' data-theme="{theme}"' if theme in ("light", "dark") else ""
147
182
  parts = []
148
- head = _assets_fragment(assets)
183
+ head = _assets_html(assets)
149
184
  if head:
150
185
  parts.append(head)
151
186
  parts.append(
152
- f'<div id="{div}"></div>\n'
187
+ f'<div id="{div}"{theme_attr}></div>\n'
153
188
  f'<script>new CsvGrid(document.getElementById("{div}"), '
154
189
  f'{_dump(payload(df, name, index))}, {_dump(opts)});</script>'
155
190
  )
156
191
  return "\n".join(parts)
157
192
 
158
193
 
159
- _assets_emitted = False
160
-
161
-
162
- def show(df, *, name: str | None = None, assets=None,
163
- index: bool = False, **options) -> None:
194
+ def show(df, *, name: str | None = None, assets="inline",
195
+ index: bool = False, theme: str = "auto", **options) -> None:
164
196
  """Display `df` as a CsvGrid in Jupyter / Quarto.
165
197
 
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
198
  Parameters
172
199
  ----------
173
200
  df : pandas.DataFrame
@@ -176,12 +203,17 @@ def show(df, *, name: str | None = None, assets=None,
176
203
  integral float columns -> ints) exactly as the csv-viewer app does.
177
204
  name : str, optional
178
205
  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).
206
+ assets : {'inline', str, False}, default 'inline'
207
+ 'inline' embeds the grid's CSS + JS, injected into the document
208
+ <head> by an idempotent guard emitted with every grid — clearing or
209
+ re-running a cell can't strip them, and any grid re-establishes them
210
+ if missing. A base-URL string links the assets from there instead.
211
+ False emits nothing (use only when the assets are already present).
183
212
  index : bool, default False
184
213
  Include the DataFrame index as leading column(s).
214
+ theme : {'auto', 'light', 'dark'}, default 'auto'
215
+ 'auto' follows the host page / OS via prefers-color-scheme; 'light'
216
+ or 'dark' forces the grid's color scheme (sets ``data-theme``).
185
217
 
186
218
  Other options (keyword, snake_case; mirror the JS CsvGrid API)
187
219
  --------------------------------------------------------------
@@ -203,26 +235,28 @@ def show(df, *, name: str | None = None, assets=None,
203
235
  with equal probability; coverage =
204
236
  maximize the count of cells shown
205
237
  in full.
238
+ display_mode : {'auto', 'raw'}, default 'auto'
239
+ 'auto' = type-aware formatting;
240
+ 'raw' = verbatim source text (a view
241
+ lens; types still drive alignment
242
+ and sort).
206
243
  rows : int, optional cap the scroll viewport to ~N rows
207
244
  (vertical scroll for the rest).
208
245
  max_height : str, optional raw CSS max-height (e.g. '400px');
209
246
  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
247
+ render_cap : int, default 2048 rows rendered before "show all".
248
+ eager_cells : int, default 262144 below this, format everything up
212
249
  front (else lazy).
213
250
  worker : bool, default False parse worker (off by default here
214
251
  — the data is inlined, not fetched).
215
252
 
216
- Dark mode follows the host page automatically (prefers-color-scheme;
217
- JupyterLab dark themes included).
253
+ Dark mode follows the host page automatically unless ``theme`` forces
254
+ it (prefers-color-scheme; JupyterLab dark themes included).
218
255
  """
219
- global _assets_emitted
220
256
  try:
221
257
  from IPython.display import HTML, display
222
258
  except ImportError as e: # pragma: no cover
223
259
  raise ImportError("csv_grid.show() needs IPython; "
224
260
  "use to_html() outside Jupyter/Quarto") from e
225
- if assets is None:
226
- assets = False if _assets_emitted else "inline"
227
- _assets_emitted = True
228
- display(HTML(to_html(df, name=name, assets=assets, index=index, **options)))
261
+ display(HTML(to_html(df, name=name, assets=assets, index=index,
262
+ theme=theme, **options)))
@@ -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
@@ -12,7 +12,7 @@ resolution-markers = [
12
12
 
13
13
  [[package]]
14
14
  name = "csv-grid"
15
- version = "3.0.7"
15
+ version = "3.3.1"
16
16
  source = { editable = "." }
17
17
  dependencies = [
18
18
  { name = "pandas" },
csv_grid-3.1.0/.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,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,r=`equal-risk`){return r===`coverage`?W(e,t,n):U(e,t,n)}function U(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 W(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=G(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 G(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 K(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 q(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 J(e){return e.align?`col-${e.type} align-${e.align}`:`col-${e.type}`}function Y(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}var X=1e6,Z=2e3,Q=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:2e3,eagerCells:2e5,worker:!0,headerMode:`auto`,widthMode:`equal-risk`,maxRows:null,height:null,...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=$(`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: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>=X?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(),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()}setWidthMode(e){this.opts.widthMode=e===`coverage`?`coverage`:`equal-risk`,this.applyLayout()}measureLayout(){let e=(n._canvas||=document.createElement(`canvas`)).getContext(`2d`),t=getComputedStyle(this.els.table),r=`${t.fontSize} ${t.fontFamily}`,i=K(this.rows.length,Z),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,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)=>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+Q);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)=>q(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=J(e),this.opts.sortable?(r.innerHTML=`<span class="sort-arrow">${this.sortCol===t?this.sortDir===1?`▲`:`▼`:``}</span>${Y(e.name)}`,r.title=`${e.name} (${e.type}) — click to sort`,r.addEventListener(`click`,()=>this.onSort(t))):(r.innerHTML=`<span class="sort-arrow"></span>${Y(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="${J(e)} blank">·</td>`:`<td class="${J(e)}">${Y(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