// deno:https://jsr.io/@std/csv/1.0.6/_shared.ts function codePointLength(s) { return Array.from(s).length; } // deno:https://jsr.io/@std/csv/1.0.6/_io.ts function createBareQuoteErrorMessage(zeroBasedRecordStartLine, zeroBasedLine, zeroBasedColumn) { return `Syntax error on line ${zeroBasedRecordStartLine + 1}; parse error on line ${zeroBasedLine + 1}, column ${zeroBasedColumn + 1}: bare " in non-quoted-field`; } function createQuoteErrorMessage(zeroBasedRecordStartLine, zeroBasedLine, zeroBasedColumn) { return `Syntax error on line ${zeroBasedRecordStartLine + 1}; parse error on line ${zeroBasedLine + 1}, column ${zeroBasedColumn + 1}: extraneous or missing " in quoted-field`; } function convertRowToObject(row, headers, zeroBasedLine) { if (row.length !== headers.length) { throw new Error(`Syntax error on line ${zeroBasedLine + 1}: The record has ${row.length} fields, but the header has ${headers.length} fields`); } const out = {}; for (const [index, header] of headers.entries()) { out[header] = row[index]; } return out; } // deno:https://jsr.io/@std/csv/1.0.6/parse.ts var BYTE_ORDER_MARK = "\uFEFF"; var Parser = class { #input = ""; #cursor = 0; #options; constructor({ separator = ",", trimLeadingSpace = false, comment, lazyQuotes, fieldsPerRecord } = {}) { this.#options = { separator, trimLeadingSpace, comment, lazyQuotes, fieldsPerRecord }; } #readLine() { if (this.#isEOF()) return null; let buffer = ""; let hadNewline = false; while (this.#cursor < this.#input.length) { if (this.#input.startsWith("\r\n", this.#cursor)) { hadNewline = true; this.#cursor += 2; break; } if (this.#input.startsWith("\n", this.#cursor)) { hadNewline = true; this.#cursor += 1; break; } buffer += this.#input[this.#cursor]; this.#cursor += 1; } if (!hadNewline && buffer.endsWith("\r")) { buffer = buffer.slice(0, -1); } return buffer; } #isEOF() { return this.#cursor >= this.#input.length; } #parseRecord(zeroBasedStartLine) { let fullLine = this.#readLine(); if (fullLine === null) return null; if (fullLine.length === 0) { return []; } let zeroBasedLine = zeroBasedStartLine; if (this.#options.comment && fullLine[0] === this.#options.comment) { return []; } let line = fullLine; const quote = '"'; const quoteLen = quote.length; const separatorLen = this.#options.separator.length; let recordBuffer = ""; const fieldIndexes = []; parseField: while (true) { if (this.#options.trimLeadingSpace) { line = line.trimStart(); } if (line.length === 0 || !line.startsWith(quote)) { const i = line.indexOf(this.#options.separator); let field = line; if (i >= 0) { field = field.substring(0, i); } if (!this.#options.lazyQuotes) { const j = field.indexOf(quote); if (j >= 0) { const col = codePointLength(fullLine.slice(0, fullLine.length - line.slice(j).length)); throw new SyntaxError(createBareQuoteErrorMessage(zeroBasedStartLine, zeroBasedLine, col)); } } recordBuffer += field; fieldIndexes.push(recordBuffer.length); if (i >= 0) { line = line.substring(i + separatorLen); continue parseField; } break parseField; } else { line = line.substring(quoteLen); while (true) { const i = line.indexOf(quote); if (i >= 0) { recordBuffer += line.substring(0, i); line = line.substring(i + quoteLen); if (line.startsWith(quote)) { recordBuffer += quote; line = line.substring(quoteLen); } else if (line.startsWith(this.#options.separator)) { line = line.substring(separatorLen); fieldIndexes.push(recordBuffer.length); continue parseField; } else if (0 === line.length) { fieldIndexes.push(recordBuffer.length); break parseField; } else if (this.#options.lazyQuotes) { recordBuffer += quote; } else { const col = codePointLength(fullLine.slice(0, fullLine.length - line.length - quoteLen)); throw new SyntaxError(createQuoteErrorMessage(zeroBasedStartLine, zeroBasedLine, col)); } } else if (line.length > 0 || !this.#isEOF()) { recordBuffer += line; const r = this.#readLine(); line = r ?? ""; fullLine = line; if (r === null) { if (!this.#options.lazyQuotes) { const col = codePointLength(fullLine); throw new SyntaxError(createQuoteErrorMessage(zeroBasedStartLine, zeroBasedLine, col)); } fieldIndexes.push(recordBuffer.length); break parseField; } zeroBasedLine++; recordBuffer += "\n"; } else { if (!this.#options.lazyQuotes) { const col = codePointLength(fullLine); throw new SyntaxError(createQuoteErrorMessage(zeroBasedStartLine, zeroBasedLine, col)); } fieldIndexes.push(recordBuffer.length); break parseField; } } } } const result = []; let preIdx = 0; for (const i of fieldIndexes) { result.push(recordBuffer.slice(preIdx, i)); preIdx = i; } return result; } parse(input) { this.#input = input.startsWith(BYTE_ORDER_MARK) ? input.slice(1) : input; this.#cursor = 0; const result = []; let lineResult; let first = true; let lineIndex = 0; const INVALID_RUNE = [ "\r", "\n", '"' ]; const options = this.#options; if (INVALID_RUNE.includes(options.separator) || typeof options.comment === "string" && INVALID_RUNE.includes(options.comment) || options.separator === options.comment) { throw new Error("Cannot parse input: invalid delimiter"); } let _nbFields; if (options.fieldsPerRecord === void 0 || options.fieldsPerRecord < 0) { _nbFields = "ANY"; } else if (options.fieldsPerRecord === 0) { _nbFields = "UNINITIALIZED"; } else { _nbFields = options.fieldsPerRecord; } while (true) { const r = this.#parseRecord(lineIndex); if (r === null) break; lineResult = r; lineIndex++; if (first) { first = false; if (_nbFields === "UNINITIALIZED") { _nbFields = lineResult.length; } } if (lineResult.length > 0) { if (typeof _nbFields === "number" && _nbFields !== lineResult.length) { throw new SyntaxError(`Syntax error on line ${lineIndex}: expected ${_nbFields} fields but got ${lineResult.length}`); } result.push(lineResult); } } return result; } }; function parse(input, options = { skipFirstRow: false }) { const parser = new Parser(options); const r = parser.parse(input); if (options.skipFirstRow || options.columns) { let headers = []; if (options.skipFirstRow) { const head = r.shift(); if (head === void 0) { throw new TypeError("Cannot parse input: headers must be defined"); } headers = head; } if (options.columns) { headers = options.columns; } const zeroBasedFirstLineIndex = options.skipFirstRow ? 1 : 0; return r.map((row, i) => { return convertRowToObject(row, headers, zeroBasedFirstLineIndex + i); }); } return r; } // deno:https://jsr.io/@std/streams/1.0.10/_common.ts var DEFAULT_BUFFER_SIZE = 32 * 1024; function createLPS(pat) { const length = pat.length; const lps = new Uint8Array(length); lps[0] = 0; let prefixEnd = 0; let i = 1; while (i < length) { if (pat[i] === pat[prefixEnd]) { prefixEnd++; lps[i] = prefixEnd; i++; } else if (prefixEnd === 0) { lps[i] = 0; i++; } else { prefixEnd = lps[prefixEnd - 1]; } } return lps; } // deno:https://jsr.io/@std/streams/1.0.10/text_delimiter_stream.ts var TextDelimiterStream = class extends TransformStream { #buf = ""; #delimiter; #inspectIndex = 0; #matchIndex = 0; #delimLPS; #disp; /** * Constructs a new instance. * * @param delimiter A delimiter to split the stream by. * @param options Options for the stream. */ constructor(delimiter, options) { super({ transform: (chunk, controller) => { this.#handle(chunk, controller); }, flush: (controller) => { controller.enqueue(this.#buf); } }); this.#delimiter = delimiter; this.#delimLPS = createLPS(new TextEncoder().encode(delimiter)); this.#disp = options?.disposition ?? "discard"; } #handle(chunk, controller) { this.#buf += chunk; let localIndex = 0; while (this.#inspectIndex < this.#buf.length) { if (chunk[localIndex] === this.#delimiter[this.#matchIndex]) { this.#inspectIndex++; localIndex++; this.#matchIndex++; if (this.#matchIndex === this.#delimiter.length) { const start = this.#inspectIndex - this.#delimiter.length; const end = this.#disp === "suffix" ? this.#inspectIndex : start; const copy = this.#buf.slice(0, end); controller.enqueue(copy); const shift = this.#disp === "prefix" ? start : this.#inspectIndex; this.#buf = this.#buf.slice(shift); this.#inspectIndex = this.#disp === "prefix" ? this.#delimiter.length : 0; this.#matchIndex = 0; } } else { if (this.#matchIndex === 0) { this.#inspectIndex++; localIndex++; } else { this.#matchIndex = this.#delimLPS[this.#matchIndex - 1]; } } } } }; // deno:https://jsr.io/@std/csv/1.0.6/stringify.ts var QUOTE = '"'; var LF = "\n"; var CRLF = "\r\n"; var BYTE_ORDER_MARK2 = "\uFEFF"; function getEscapedString(value, sep) { if (value === void 0 || value === null) return ""; let str = ""; if (typeof value === "object") str = JSON.stringify(value); else str = String(value); if (str.includes(sep) || str.includes(LF) || str.includes(QUOTE)) { return `${QUOTE}${str.replaceAll(QUOTE, `${QUOTE}${QUOTE}`)}${QUOTE}`; } return str; } function normalizeColumn(column) { let header; let prop; if (typeof column === "object") { if (Array.isArray(column)) { header = String(column[column.length - 1]); prop = column; } else { prop = Array.isArray(column.prop) ? column.prop : [ column.prop ]; header = typeof column.header === "string" ? column.header : String(prop[prop.length - 1]); } } else { header = String(column); prop = [ column ]; } return { header, prop }; } function getValuesFromItem(item, normalizedColumns) { const values = []; if (normalizedColumns.length) { for (const column of normalizedColumns) { let value = item; for (const prop of column.prop) { if (typeof value !== "object" || value === null) { continue; } if (Array.isArray(value)) { if (typeof prop === "number") value = value[prop]; else { throw new TypeError('Property accessor is not of type "number"'); } } else value = value[prop]; } values.push(value); } } else { if (Array.isArray(item)) { values.push(...item); } else if (typeof item === "object") { throw new TypeError("No property accessor function was provided for object"); } else { values.push(item); } } return values; } function stringify(data, options) { const { headers = true, separator: sep = ",", columns = [], bom = false } = options ?? {}; if (sep.includes(QUOTE) || sep.includes(CRLF)) { const message = [ "Separator cannot include the following strings:", ' - U+0022: Quotation mark (")', " - U+000D U+000A: Carriage Return + Line Feed (\\r\\n)" ].join("\n"); throw new TypeError(message); } const normalizedColumns = columns.map(normalizeColumn); let output = ""; if (bom) { output += BYTE_ORDER_MARK2; } if (headers && normalizedColumns.length > 0) { output += normalizedColumns.map((column) => getEscapedString(column.header, sep)).join(sep); output += CRLF; } for (const item of data) { const values = getValuesFromItem(item, normalizedColumns); output += values.map((value) => getEscapedString(value, sep)).join(sep); output += CRLF; } return output; } // deno:https://jsr.io/@std/csv/1.0.6/stringify_stream.ts var CsvStringifyStream = class extends TransformStream { /** * Construct a new instance. * * @param options Options for the stream. */ constructor(options) { const { separator, columns = [] } = options ?? {}; super({ start(controller) { if (columns && columns.length > 0) { try { controller.enqueue(stringify([ columns ], separator !== void 0 ? { separator, headers: false } : { headers: false })); } catch (error) { controller.error(error); } } }, transform(chunk, controller) { try { controller.enqueue(stringify([ chunk ], separator !== void 0 ? { separator, headers: false, columns } : { headers: false, columns })); } catch (error) { controller.error(error); } } }); } }; // src/textarea-csv.js var TextareaCSV = class extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); this.isComponentInitialized = false; this.custcomCleanupFilter = void 0; } static get observedAttributes() { return ["column-headings", "id", "class", "caption", "text", "placeholder", "css-href", "debug"]; } attributeChangedCallback(name) { if (name === "column-headings" && this.isComponentInitialized) { this.initializeTable(); } } connectedCallback() { this.initializeComponent(); this.isComponentInitialized = true; this.initializeTable(); this.setupEventListeners(); this.setupMutationObserver(); } setupMutationObserver() { const targetNode = this.shadowRoot.querySelector("tbody"); const config = { attributes: true, childList: true, subtree: true, attributeFilter: ["value"] // This ensures we only observe changes to the 'value' attribute }; const callback = (mutationsList, _observer) => { for (const mutation of mutationsList) { if (mutation.type === "attributes" && mutation.attributeName === "value") { this.toTextarea(); } else if (mutation.type === "childList") { this.toTextarea(); } } }; this.observer = new MutationObserver(callback); if (targetNode) { this.observer.observe(targetNode, config); } } disconnectedCallback() { if (this.observer) { this.observer.disconnect(); } } initializeComponent() { const template = document.createElement("template"); template.innerHTML = `