@asciidoctor/core 3.0.3 → 4.0.0-alpha.1
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.
- package/README.md +42 -10
- package/build/browser/index.js +24154 -0
- package/build/node/index.cjs +24735 -0
- package/{dist/css/asciidoctor.css → data/asciidoctor-default.css} +54 -53
- package/package.json +53 -100
- package/src/abstract_block.js +849 -0
- package/src/abstract_node.js +954 -0
- package/src/attribute_entry.js +12 -0
- package/src/attribute_list.js +380 -0
- package/src/block.js +168 -0
- package/src/browser/asset.js +22 -0
- package/src/browser/reader.js +138 -0
- package/src/browser.js +121 -0
- package/src/callouts.js +85 -0
- package/src/compliance.js +54 -0
- package/src/constants.js +665 -0
- package/src/convert.js +370 -0
- package/src/converter/composite.js +83 -0
- package/src/converter/docbook5.js +1031 -0
- package/src/converter/html5.js +1899 -0
- package/src/converter/manpage.js +935 -0
- package/src/converter/template.js +459 -0
- package/src/converter.js +478 -0
- package/src/data/stylesheet-data.js +2 -0
- package/src/document.js +2134 -0
- package/src/extensions.js +1952 -0
- package/src/footnote.js +28 -0
- package/src/helpers.js +355 -0
- package/src/index.js +138 -0
- package/src/inline.js +158 -0
- package/src/list.js +240 -0
- package/src/load.js +276 -0
- package/src/logging.js +526 -0
- package/src/parser.js +3661 -0
- package/src/path_resolver.js +472 -0
- package/src/reader.js +1755 -0
- package/src/rx.js +829 -0
- package/src/section.js +354 -0
- package/src/stylesheets.js +30 -0
- package/src/substitutors.js +2241 -0
- package/src/syntaxHighlighter/highlightjs.js +90 -0
- package/src/syntaxHighlighter/html_pipeline.js +33 -0
- package/src/syntax_highlighter.js +304 -0
- package/src/table.js +952 -0
- package/src/timings.js +78 -0
- package/types/abstract_block.d.ts +343 -0
- package/types/abstract_node.d.ts +471 -0
- package/types/attribute_entry.d.ts +7 -0
- package/types/attribute_list.d.ts +52 -0
- package/types/block.d.ts +55 -0
- package/types/browser/asset.d.ts +7 -0
- package/types/browser/reader.d.ts +29 -0
- package/types/callouts.d.ts +36 -0
- package/types/compliance.d.ts +23 -0
- package/types/constants.d.ts +268 -0
- package/types/convert.d.ts +34 -0
- package/types/converter/composite.d.ts +20 -0
- package/types/converter/docbook5.d.ts +41 -0
- package/types/converter/html5.d.ts +51 -0
- package/types/converter/manpage.d.ts +59 -0
- package/types/converter/template.d.ts +83 -0
- package/types/converter.d.ts +150 -0
- package/types/data/stylesheet-data.d.ts +2 -0
- package/types/document.d.ts +495 -0
- package/types/extensions.d.ts +876 -0
- package/types/footnote.d.ts +18 -0
- package/types/helpers.d.ts +146 -0
- package/types/index.d.ts +73 -3731
- package/types/inline.d.ts +69 -0
- package/types/list.d.ts +114 -0
- package/types/load.d.ts +39 -0
- package/types/logging.d.ts +187 -0
- package/types/parser.d.ts +114 -0
- package/types/path_resolver.d.ts +103 -0
- package/types/reader.d.ts +184 -0
- package/types/rx.d.ts +513 -0
- package/types/section.d.ts +122 -0
- package/types/stylesheets.d.ts +10 -0
- package/types/substitutors.d.ts +208 -0
- package/types/syntaxHighlighter/highlightjs.d.ts +33 -0
- package/types/syntaxHighlighter/html_pipeline.d.ts +16 -0
- package/types/syntax_highlighter.d.ts +167 -0
- package/types/table.d.ts +231 -0
- package/types/timings.d.ts +25 -0
- package/types/tsconfig.json +9 -0
- package/LICENSE +0 -21
- package/dist/browser/asciidoctor.js +0 -47654
- package/dist/browser/asciidoctor.min.js +0 -1452
- package/dist/graalvm/asciidoctor.js +0 -47402
- package/dist/node/asciidoctor.cjs +0 -21567
- package/dist/node/asciidoctor.js +0 -23037
package/src/table.js
ADDED
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
// ESM conversion of table.rb
|
|
2
|
+
//
|
|
3
|
+
// Ruby-to-JavaScript notes:
|
|
4
|
+
// - Table::Rows#[] (alias for send) → explicit bySection/head/foot/body access.
|
|
5
|
+
// - Table::Cell references Document, PreprocessorReader, Parser — these are
|
|
6
|
+
// imported lazily (dynamic import) to avoid circular dependency issues.
|
|
7
|
+
// - String#squeeze(ch) → replaceAll(ch+ch, ch) loop (only used for '"').
|
|
8
|
+
// - Number#truncate(precision) → Math.trunc(n * 10^p) / 10^p.
|
|
9
|
+
// - :asciidoc / :literal / :header symbols → strings 'asciidoc', 'literal', 'header'.
|
|
10
|
+
|
|
11
|
+
import { AbstractBlock } from './abstract_block.js'
|
|
12
|
+
import { AbstractNode } from './abstract_node.js'
|
|
13
|
+
import { Inline } from './inline.js'
|
|
14
|
+
import { applyLogging } from './logging.js'
|
|
15
|
+
import { LF, ATTR_REF_HEAD, BASIC_SUBS, NORMAL_SUBS } from './constants.js'
|
|
16
|
+
import { BlankLineRx, LeadingInlineAnchorRx } from './rx.js'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Truncate a float to `precision` decimal places (like Ruby's Float#truncate).
|
|
20
|
+
* @param {number} value
|
|
21
|
+
* @param {number} precision
|
|
22
|
+
* @returns {number}
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
function truncate(value, precision) {
|
|
26
|
+
const factor = 10 ** precision
|
|
27
|
+
return Math.trunc(value * factor) / factor
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Collapse consecutive identical characters (like Ruby's String#squeeze(q)).
|
|
32
|
+
* @param {string} str
|
|
33
|
+
* @param {string} ch
|
|
34
|
+
* @returns {string}
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function squeezeChar(str, ch) {
|
|
38
|
+
const double = ch + ch
|
|
39
|
+
while (str.includes(double)) str = str.replaceAll(double, ch)
|
|
40
|
+
return str
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const DEFAULT_PRECISION = 4
|
|
44
|
+
|
|
45
|
+
// ── Table ─────────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
export class Table extends AbstractBlock {
|
|
48
|
+
constructor(parent, attributes) {
|
|
49
|
+
super(parent, 'table')
|
|
50
|
+
this.rows = new Table.Rows()
|
|
51
|
+
this.columns = []
|
|
52
|
+
this.hasHeaderOption = false
|
|
53
|
+
|
|
54
|
+
// Resolve tablepcwidth from 'width' attribute
|
|
55
|
+
let pcwidthIntval = 100
|
|
56
|
+
const pcwidth = attributes.width
|
|
57
|
+
if (pcwidth != null) {
|
|
58
|
+
let v = parseInt(pcwidth, 10)
|
|
59
|
+
if (Number.isNaN(v)) v = 0
|
|
60
|
+
if (v > 100 || v < 1) {
|
|
61
|
+
if (!(v === 0 && (pcwidth === '0' || pcwidth === '0%'))) v = 100
|
|
62
|
+
}
|
|
63
|
+
pcwidthIntval = v
|
|
64
|
+
}
|
|
65
|
+
this.attributes.tablepcwidth = pcwidthIntval
|
|
66
|
+
|
|
67
|
+
const pagewidthAttr = this.document.attributes.pagewidth
|
|
68
|
+
if (pagewidthAttr != null) {
|
|
69
|
+
const abswidthVal = truncate(
|
|
70
|
+
(pcwidthIntval / 100.0) * parseFloat(pagewidthAttr),
|
|
71
|
+
DEFAULT_PRECISION
|
|
72
|
+
)
|
|
73
|
+
this.attributes.tableabswidth =
|
|
74
|
+
abswidthVal === Math.trunc(abswidthVal)
|
|
75
|
+
? Math.trunc(abswidthVal)
|
|
76
|
+
: abswidthVal
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if ('rotate-option' in attributes) this.attributes.orientation = 'landscape'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns the header option state if the row being processed is the header row, otherwise false.
|
|
84
|
+
* @returns {boolean|string}
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
87
|
+
headerRow() {
|
|
88
|
+
const val = this.hasHeaderOption
|
|
89
|
+
return val && this.rows.body.length === 0 ? val : false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create Column objects from the column test array.
|
|
94
|
+
* @param {Object[]} colspecs
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
createColumns(colspecs) {
|
|
98
|
+
const cols = []
|
|
99
|
+
let autowidthCols = null
|
|
100
|
+
let widthBase = 0
|
|
101
|
+
for (const colspec of colspecs) {
|
|
102
|
+
const colwidth = colspec.width
|
|
103
|
+
cols.push(new Table.Column(this, cols.length, colspec))
|
|
104
|
+
if (colwidth < 0) {
|
|
105
|
+
;(autowidthCols ??= []).push(cols[cols.length - 1])
|
|
106
|
+
} else {
|
|
107
|
+
widthBase += colwidth
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.columns = cols
|
|
111
|
+
const numCols = cols.length
|
|
112
|
+
if (numCols > 0) {
|
|
113
|
+
this.attributes.colcount = numCols
|
|
114
|
+
const effectiveWidthBase =
|
|
115
|
+
widthBase > 0 || autowidthCols ? widthBase : null
|
|
116
|
+
this.assignColumnWidths(effectiveWidthBase, autowidthCols)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Assign percentage (and absolute) widths to all columns.
|
|
122
|
+
* @param {number|null} [widthBase=null]
|
|
123
|
+
* @param {Table.Column[]|null} [autowidthCols=null]
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
assignColumnWidths(widthBase = null, autowidthCols = null) {
|
|
127
|
+
const precision = DEFAULT_PRECISION
|
|
128
|
+
let totalWidth = 0
|
|
129
|
+
let colPcwidth = 0
|
|
130
|
+
|
|
131
|
+
if (widthBase != null) {
|
|
132
|
+
if (autowidthCols) {
|
|
133
|
+
let autowidth
|
|
134
|
+
if (widthBase > 100) {
|
|
135
|
+
autowidth = 0
|
|
136
|
+
this.logger.warn(
|
|
137
|
+
`total column width must not exceed 100% when using autowidth columns; got ${widthBase}%`
|
|
138
|
+
)
|
|
139
|
+
} else {
|
|
140
|
+
autowidth = truncate(
|
|
141
|
+
(100.0 - widthBase) / autowidthCols.length,
|
|
142
|
+
precision
|
|
143
|
+
)
|
|
144
|
+
if (Math.trunc(autowidth) === autowidth)
|
|
145
|
+
autowidth = Math.trunc(autowidth)
|
|
146
|
+
widthBase = 100
|
|
147
|
+
}
|
|
148
|
+
const autowAttrs = { width: autowidth, 'autowidth-option': '' }
|
|
149
|
+
for (const col of autowidthCols) col.updateAttributes(autowAttrs)
|
|
150
|
+
}
|
|
151
|
+
for (const col of this.columns) {
|
|
152
|
+
totalWidth += colPcwidth = col.assignWidth(null, widthBase, precision)
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
colPcwidth = truncate(100.0 / this.columns.length, precision)
|
|
156
|
+
if (Math.trunc(colPcwidth) === colPcwidth)
|
|
157
|
+
colPcwidth = Math.trunc(colPcwidth)
|
|
158
|
+
for (const col of this.columns) {
|
|
159
|
+
totalWidth += col.assignWidth(colPcwidth, null, precision)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Donate balance to the last column (half-up rounding)
|
|
164
|
+
if (totalWidth !== 100) {
|
|
165
|
+
const balance = +(100 - totalWidth + colPcwidth).toFixed(precision)
|
|
166
|
+
this.columns[this.columns.length - 1].assignWidth(
|
|
167
|
+
balance,
|
|
168
|
+
null,
|
|
169
|
+
precision
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Partition rows into header, footer, and body.
|
|
176
|
+
* @param {Object} attrs
|
|
177
|
+
* @internal
|
|
178
|
+
*/
|
|
179
|
+
async partitionHeaderFooter(attrs) {
|
|
180
|
+
const body = this.rows.body
|
|
181
|
+
let numBodyRows = (this.attributes.rowcount = body.length)
|
|
182
|
+
|
|
183
|
+
if (numBodyRows > 0) {
|
|
184
|
+
if (this.hasHeaderOption === true) {
|
|
185
|
+
this.rows.head = [
|
|
186
|
+
await Promise.all(
|
|
187
|
+
body.shift().map((cell) => cell.reinitialize(true))
|
|
188
|
+
),
|
|
189
|
+
]
|
|
190
|
+
numBodyRows--
|
|
191
|
+
} else if (this.hasHeaderOption === null) {
|
|
192
|
+
this.hasHeaderOption = false
|
|
193
|
+
body.unshift(
|
|
194
|
+
await Promise.all(
|
|
195
|
+
body.shift().map((cell) => cell.reinitialize(false))
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (numBodyRows > 0 && 'footer-option' in attrs) {
|
|
202
|
+
this.rows.foot = [body.pop()]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── Table.Rows ────────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
Table.Rows = class Rows {
|
|
210
|
+
constructor(head = [], foot = [], body = []) {
|
|
211
|
+
this.head = head
|
|
212
|
+
this.foot = foot
|
|
213
|
+
this.body = body
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Retrieve the rows grouped by section as a nested Array.
|
|
218
|
+
* @returns {Array<[string, Array]>}
|
|
219
|
+
*/
|
|
220
|
+
bySection() {
|
|
221
|
+
return [
|
|
222
|
+
['head', this.head],
|
|
223
|
+
['body', this.body],
|
|
224
|
+
['foot', this.foot],
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
toObject() {
|
|
229
|
+
return { head: this.head, body: this.body, foot: this.foot }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ── Table.Column ──────────────────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
Table.Column = class Column extends AbstractNode {
|
|
236
|
+
constructor(table, index, attributes = {}) {
|
|
237
|
+
super(table, 'table_column')
|
|
238
|
+
this.style = attributes.style ?? null
|
|
239
|
+
attributes.colnumber = index + 1
|
|
240
|
+
if (!('width' in attributes)) attributes.width = 1
|
|
241
|
+
if (!('halign' in attributes)) attributes.halign = 'left'
|
|
242
|
+
if (!('valign' in attributes)) attributes.valign = 'top'
|
|
243
|
+
this.updateAttributes(attributes)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Alias for parent (always a Table). */
|
|
247
|
+
get table() {
|
|
248
|
+
return this.getParent()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Calculate and assign the widths for this column.
|
|
253
|
+
* @param {number|null} colPcwidth
|
|
254
|
+
* @param {number|null} widthBase
|
|
255
|
+
* @param {number} precision
|
|
256
|
+
* @returns {number} The resolved colpcwidth value.
|
|
257
|
+
* @internal
|
|
258
|
+
*/
|
|
259
|
+
assignWidth(colPcwidth, widthBase, precision) {
|
|
260
|
+
if (widthBase != null) {
|
|
261
|
+
colPcwidth = truncate(
|
|
262
|
+
(parseFloat(this.attributes.width) * 100.0) / widthBase,
|
|
263
|
+
precision
|
|
264
|
+
)
|
|
265
|
+
if (Math.trunc(colPcwidth) === colPcwidth)
|
|
266
|
+
colPcwidth = Math.trunc(colPcwidth)
|
|
267
|
+
}
|
|
268
|
+
const tableAbswidth = this.getParent().attributes.tableabswidth
|
|
269
|
+
if (tableAbswidth != null) {
|
|
270
|
+
const colAbswidth = truncate(
|
|
271
|
+
(colPcwidth / 100.0) * tableAbswidth,
|
|
272
|
+
precision
|
|
273
|
+
)
|
|
274
|
+
this.attributes.colabswidth =
|
|
275
|
+
colAbswidth === Math.trunc(colAbswidth)
|
|
276
|
+
? Math.trunc(colAbswidth)
|
|
277
|
+
: colAbswidth
|
|
278
|
+
}
|
|
279
|
+
this.attributes.colpcwidth = colPcwidth
|
|
280
|
+
return colPcwidth
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
isBlock() {
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
isInline() {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ── JavaScript-style accessors ────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get the parent table of this column.
|
|
294
|
+
* @returns {Table}
|
|
295
|
+
*/
|
|
296
|
+
getTable() {
|
|
297
|
+
return this.table
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Table.Cell ────────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
/** @extends {AbstractBlock<string | string[]>} */
|
|
304
|
+
class Cell extends AbstractBlock {
|
|
305
|
+
static get DOUBLE_LF() {
|
|
306
|
+
return LF + LF
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** @internal */
|
|
310
|
+
_reinitializeArgs
|
|
311
|
+
|
|
312
|
+
/** @internal */
|
|
313
|
+
_innerDocSetup
|
|
314
|
+
|
|
315
|
+
/** @internal */
|
|
316
|
+
_subs
|
|
317
|
+
|
|
318
|
+
/** @internal */
|
|
319
|
+
_text
|
|
320
|
+
|
|
321
|
+
/** @internal */
|
|
322
|
+
_cellbgcolor
|
|
323
|
+
|
|
324
|
+
/** @internal */
|
|
325
|
+
_convertedText
|
|
326
|
+
|
|
327
|
+
constructor(column, cellText, attributes = {}, opts = {}) {
|
|
328
|
+
super(column, 'table_cell')
|
|
329
|
+
this._cursor = null
|
|
330
|
+
this._reinitializeArgs = null
|
|
331
|
+
if (this.document.sourcemap && opts.cursor) {
|
|
332
|
+
this.sourceLocation = Object.assign({}, opts.cursor)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let cellStyle = null
|
|
336
|
+
let inHeaderRow = false
|
|
337
|
+
let asciidoc = false
|
|
338
|
+
let literal = false
|
|
339
|
+
let normalPsv = false
|
|
340
|
+
let innerDocumentCursor = null
|
|
341
|
+
|
|
342
|
+
if (column) {
|
|
343
|
+
inHeaderRow = column.table.headerRow()
|
|
344
|
+
if (inHeaderRow) {
|
|
345
|
+
if (inHeaderRow === 'implicit') {
|
|
346
|
+
const cs = column.style ?? attributes?.style
|
|
347
|
+
if (cs === 'asciidoc' || cs === 'literal') {
|
|
348
|
+
this._reinitializeArgs = [
|
|
349
|
+
column,
|
|
350
|
+
cellText,
|
|
351
|
+
attributes && { ...attributes },
|
|
352
|
+
opts,
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
cellStyle = null
|
|
356
|
+
}
|
|
357
|
+
// else: don't set cellStyle from column for header row
|
|
358
|
+
} else {
|
|
359
|
+
cellStyle = column.style ?? null
|
|
360
|
+
}
|
|
361
|
+
// Inherit column attributes
|
|
362
|
+
this.updateAttributes(column.attributes)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (attributes != null) {
|
|
366
|
+
if (Object.keys(attributes).length === 0) {
|
|
367
|
+
this.colspan = null
|
|
368
|
+
this.rowspan = null
|
|
369
|
+
} else {
|
|
370
|
+
this.colspan = attributes.colspan
|
|
371
|
+
? parseInt(attributes.colspan, 10)
|
|
372
|
+
: null
|
|
373
|
+
this.rowspan = attributes.rowspan
|
|
374
|
+
? parseInt(attributes.rowspan, 10)
|
|
375
|
+
: null
|
|
376
|
+
delete attributes.colspan
|
|
377
|
+
delete attributes.rowspan
|
|
378
|
+
if (!inHeaderRow) cellStyle = attributes.style ?? cellStyle
|
|
379
|
+
this.updateAttributes(attributes)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
switch (cellStyle) {
|
|
383
|
+
case 'asciidoc': {
|
|
384
|
+
asciidoc = true
|
|
385
|
+
innerDocumentCursor = opts.cursor
|
|
386
|
+
cellText = cellText.trimEnd()
|
|
387
|
+
if (cellText.startsWith(LF)) {
|
|
388
|
+
let linesAdvanced = 0
|
|
389
|
+
while (cellText.startsWith(LF)) {
|
|
390
|
+
cellText = cellText.slice(1)
|
|
391
|
+
linesAdvanced++
|
|
392
|
+
}
|
|
393
|
+
if (
|
|
394
|
+
innerDocumentCursor &&
|
|
395
|
+
typeof innerDocumentCursor.advance === 'function'
|
|
396
|
+
) {
|
|
397
|
+
innerDocumentCursor.advance(linesAdvanced)
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
cellText = cellText.trimStart()
|
|
401
|
+
}
|
|
402
|
+
break
|
|
403
|
+
}
|
|
404
|
+
case 'literal':
|
|
405
|
+
literal = true
|
|
406
|
+
cellText = cellText.trimEnd()
|
|
407
|
+
while (cellText.startsWith(LF)) cellText = cellText.slice(1)
|
|
408
|
+
break
|
|
409
|
+
default:
|
|
410
|
+
normalPsv = true
|
|
411
|
+
cellText = cellText != null ? cellText.trim() : ''
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
this.colspan = null
|
|
415
|
+
this.rowspan = null
|
|
416
|
+
if (cellStyle === 'asciidoc') asciidoc = true
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (asciidoc) {
|
|
420
|
+
const parentDoc = this.document
|
|
421
|
+
// Store the setup data for create() to handle asynchronously.
|
|
422
|
+
this._innerDocSetup = {
|
|
423
|
+
lines: cellText.split(LF, -1),
|
|
424
|
+
parentDoc,
|
|
425
|
+
parentDoctitle: parentDoc.attributes.doctitle,
|
|
426
|
+
options: {
|
|
427
|
+
safe: parentDoc.safe,
|
|
428
|
+
backend: parentDoc.backend,
|
|
429
|
+
header_footer: false,
|
|
430
|
+
parent: parentDoc,
|
|
431
|
+
cursor: innerDocumentCursor,
|
|
432
|
+
},
|
|
433
|
+
}
|
|
434
|
+
delete parentDoc.attributes.doctitle
|
|
435
|
+
this._subs = null
|
|
436
|
+
} else if (literal) {
|
|
437
|
+
this.contentModel = 'verbatim'
|
|
438
|
+
this._subs = [...BASIC_SUBS]
|
|
439
|
+
} else {
|
|
440
|
+
if (normalPsv) {
|
|
441
|
+
if (inHeaderRow) {
|
|
442
|
+
this._cursor = opts.cursor ?? null
|
|
443
|
+
} else {
|
|
444
|
+
this._catalogInlineAnchor(cellText, opts.cursor)
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
this.contentModel = 'simple'
|
|
448
|
+
this._subs = [...NORMAL_SUBS]
|
|
449
|
+
}
|
|
450
|
+
this._text = cellText
|
|
451
|
+
this.style = cellStyle
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Alias for parent (always a Column). */
|
|
455
|
+
get column() {
|
|
456
|
+
return this.getParent()
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Factory — create and fully initialize a Cell asynchronously.
|
|
461
|
+
* For AsciiDoc cells, parses the nested document.
|
|
462
|
+
*
|
|
463
|
+
* NOTE: _innerContent is NOT pre-computed here. Document.convert() will call
|
|
464
|
+
* _convertAsciiDocCells() after parse completes (so callouts are rewound and
|
|
465
|
+
* all cross-references from the parent document are already registered).
|
|
466
|
+
* @param {Table.Column} column
|
|
467
|
+
* @param {string} cellText
|
|
468
|
+
* @param {Object} [attributes={}]
|
|
469
|
+
* @param {Object} [opts={}]
|
|
470
|
+
* @returns {Promise<Table.Cell>}
|
|
471
|
+
*/
|
|
472
|
+
static async create(column, cellText, attributes = {}, opts = {}) {
|
|
473
|
+
const cell = new Table.Cell(column, cellText, attributes, opts)
|
|
474
|
+
if (cell._innerDocSetup) {
|
|
475
|
+
const { lines, parentDoc, parentDoctitle, options } = cell._innerDocSetup
|
|
476
|
+
cell._innerDocSetup = null
|
|
477
|
+
const innerDoc = await parentDoc.constructor.create(lines, options)
|
|
478
|
+
if (parentDoctitle) parentDoc.attributes.doctitle = parentDoctitle
|
|
479
|
+
cell._innerDocument = innerDoc
|
|
480
|
+
}
|
|
481
|
+
return /** @type {Table.Cell} */ cell
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** @returns {Promise<Table.Cell>} */
|
|
485
|
+
async reinitialize(hasHeader) {
|
|
486
|
+
if (hasHeader) {
|
|
487
|
+
this._reinitializeArgs = null
|
|
488
|
+
} else if (this._reinitializeArgs) {
|
|
489
|
+
return Table.Cell.create(...this._reinitializeArgs)
|
|
490
|
+
} else {
|
|
491
|
+
this.style = this.attributes.style ?? null
|
|
492
|
+
}
|
|
493
|
+
if (this._cursor) this._catalogInlineAnchor()
|
|
494
|
+
return /** @type {Table.Cell} */ (this)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
_catalogInlineAnchor(cellText = this._text, cursor = null) {
|
|
498
|
+
if (!cursor) {
|
|
499
|
+
cursor = this._cursor
|
|
500
|
+
this._cursor = null
|
|
501
|
+
}
|
|
502
|
+
if (!cellText.startsWith('[[')) return
|
|
503
|
+
const m = cellText.match(LeadingInlineAnchorRx)
|
|
504
|
+
if (!m) return
|
|
505
|
+
const doc = this.document
|
|
506
|
+
let reftext = m[2] ?? null
|
|
507
|
+
if (reftext?.includes(ATTR_REF_HEAD)) reftext = doc.subAttributes(reftext)
|
|
508
|
+
doc.register('refs', [
|
|
509
|
+
m[1],
|
|
510
|
+
new Inline(this, 'anchor', reftext, { type: 'ref', id: m[1] }),
|
|
511
|
+
])
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Get the text with substitutions applied.
|
|
516
|
+
* The result is pre-computed during Document.parse() via precomputeText().
|
|
517
|
+
* Falls back to the raw text if precomputeText() has not been called yet.
|
|
518
|
+
* @returns {string|null}
|
|
519
|
+
*/
|
|
520
|
+
get text() {
|
|
521
|
+
return this._convertedText ?? this._text ?? null
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Pre-compute the converted text asynchronously.
|
|
526
|
+
* Called during Document.parse() so the synchronous getter works during conversion.
|
|
527
|
+
* @returns {Promise<void>}
|
|
528
|
+
*/
|
|
529
|
+
async precomputeText() {
|
|
530
|
+
if (this._subs && this._convertedText == null) {
|
|
531
|
+
this._convertedText = await this.applySubs(this._text, this._subs)
|
|
532
|
+
// Capture the cellbgcolor attribute value as set by {set:cellbgcolor:...} in cell text.
|
|
533
|
+
// Since {set:...} attribute assignments happen during applySubs, and the document attribute
|
|
534
|
+
// is shared state, we must capture it per-cell immediately after text processing.
|
|
535
|
+
this._cellbgcolor = this.document.attributes.cellbgcolor
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
set text(val) {
|
|
540
|
+
this._text = val
|
|
541
|
+
this._convertedText = null
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Get the content — converted body data.
|
|
546
|
+
* For AsciiDoc cells, returns the pre-computed content (set by Document.convert()).
|
|
547
|
+
* @returns {Promise<string|string[]>}
|
|
548
|
+
*/
|
|
549
|
+
async content() {
|
|
550
|
+
if (this.style === 'asciidoc') {
|
|
551
|
+
return this._innerContent ?? ''
|
|
552
|
+
}
|
|
553
|
+
if (this._text.includes(Table.Cell.DOUBLE_LF)) {
|
|
554
|
+
const parts = []
|
|
555
|
+
for (const rawPara of this.text.split(BlankLineRx)) {
|
|
556
|
+
const para = rawPara.trim()
|
|
557
|
+
if (!para) continue
|
|
558
|
+
const cs = this.style
|
|
559
|
+
parts.push(
|
|
560
|
+
cs && cs !== 'header'
|
|
561
|
+
? await new Inline(this.getParent(), 'quoted', para, {
|
|
562
|
+
type: cs,
|
|
563
|
+
}).convert()
|
|
564
|
+
: para
|
|
565
|
+
)
|
|
566
|
+
}
|
|
567
|
+
return parts
|
|
568
|
+
}
|
|
569
|
+
const subbedText = this.text
|
|
570
|
+
if (!subbedText) return []
|
|
571
|
+
const cs = this.style
|
|
572
|
+
if (cs && cs !== 'header') {
|
|
573
|
+
return [
|
|
574
|
+
await new Inline(this.getParent(), 'quoted', subbedText, {
|
|
575
|
+
type: cs,
|
|
576
|
+
}).convert(),
|
|
577
|
+
]
|
|
578
|
+
}
|
|
579
|
+
return [subbedText]
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
lines() {
|
|
583
|
+
return this._text.split(LF)
|
|
584
|
+
}
|
|
585
|
+
source() {
|
|
586
|
+
return this._text
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
get innerDocument() {
|
|
590
|
+
return this._innerDocument ?? null
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
get file() {
|
|
594
|
+
return this.sourceLocation?.file ?? null
|
|
595
|
+
}
|
|
596
|
+
get lineno() {
|
|
597
|
+
return this.sourceLocation?.lineno ?? null
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
toString() {
|
|
601
|
+
return `${super.toString()} - [text: ${this._text}, colspan: ${this.colspan ?? 1}, rowspan: ${this.rowspan ?? 1}, attributes: ${JSON.stringify(this.attributes)}]`
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ── JavaScript-style accessors ────────────────────────────────────────────────
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Get the text with substitutions applied.
|
|
608
|
+
* @returns {string|null}
|
|
609
|
+
*/
|
|
610
|
+
getText() {
|
|
611
|
+
return this.text
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Set the raw text of this cell.
|
|
616
|
+
* @param {string|null} val
|
|
617
|
+
*/
|
|
618
|
+
setText(val) {
|
|
619
|
+
this.text = val
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Get the inner document for AsciiDoc-style cells.
|
|
624
|
+
* @returns {Document|null}
|
|
625
|
+
*/
|
|
626
|
+
getInnerDocument() {
|
|
627
|
+
return this.innerDocument
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Get the source file where this cell is defined.
|
|
632
|
+
* @returns {string|null}
|
|
633
|
+
*/
|
|
634
|
+
getFile() {
|
|
635
|
+
return this.file
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Get the source line number where this cell is defined.
|
|
640
|
+
* @returns {number|null}
|
|
641
|
+
*/
|
|
642
|
+
getLineNumber() {
|
|
643
|
+
return this.lineno
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
Table.Cell = Cell
|
|
647
|
+
|
|
648
|
+
// ── Table.ParserContext ───────────────────────────────────────────────────────
|
|
649
|
+
|
|
650
|
+
Table.ParserContext = class ParserContext {
|
|
651
|
+
static get FORMATS() {
|
|
652
|
+
return new Set(['psv', 'csv', 'dsv', 'tsv'])
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
static get DELIMITERS() {
|
|
656
|
+
return {
|
|
657
|
+
psv: ['|', /\|/],
|
|
658
|
+
csv: [',', /,/],
|
|
659
|
+
dsv: [':', /:/],
|
|
660
|
+
tsv: ['\t', /\t/],
|
|
661
|
+
'!sv': ['!', /!/],
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
constructor(reader, table, attributes = {}) {
|
|
666
|
+
this._reader = reader
|
|
667
|
+
this._startCursor = reader.cursor
|
|
668
|
+
reader.mark()
|
|
669
|
+
this.table = table
|
|
670
|
+
this.buffer = ''
|
|
671
|
+
|
|
672
|
+
// Determine format
|
|
673
|
+
let xsv
|
|
674
|
+
if ('format' in attributes) {
|
|
675
|
+
xsv = attributes.format
|
|
676
|
+
if (ParserContext.FORMATS.has(xsv)) {
|
|
677
|
+
if (xsv === 'tsv') {
|
|
678
|
+
this.format = 'csv'
|
|
679
|
+
} else {
|
|
680
|
+
this.format = xsv
|
|
681
|
+
if (xsv === 'psv' && table.document.nested()) xsv = '!sv'
|
|
682
|
+
}
|
|
683
|
+
} else {
|
|
684
|
+
this.logger.error(
|
|
685
|
+
this.messageWithContext(`illegal table format: ${xsv}`, {
|
|
686
|
+
source_location: reader.cursorAtPrevLine(),
|
|
687
|
+
})
|
|
688
|
+
)
|
|
689
|
+
this.format = 'psv'
|
|
690
|
+
xsv = table.document.nested() ? '!sv' : 'psv'
|
|
691
|
+
}
|
|
692
|
+
} else {
|
|
693
|
+
this.format = 'psv'
|
|
694
|
+
xsv = table.document.nested() ? '!sv' : 'psv'
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Determine delimiter
|
|
698
|
+
const delimiters = ParserContext.DELIMITERS
|
|
699
|
+
if ('separator' in attributes) {
|
|
700
|
+
const sep = attributes.separator
|
|
701
|
+
if (!sep) {
|
|
702
|
+
;[this.delimiter, this.delimiterRe] = delimiters[xsv]
|
|
703
|
+
} else if (sep === '\\t') {
|
|
704
|
+
;[this.delimiter, this.delimiterRe] = delimiters.tsv
|
|
705
|
+
} else {
|
|
706
|
+
this.delimiter = sep
|
|
707
|
+
this.delimiterRe = new RegExp(
|
|
708
|
+
sep.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
} else {
|
|
712
|
+
;[this.delimiter, this.delimiterRe] = delimiters[xsv]
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
this.colcount = table.columns.length === 0 ? -1 : table.columns.length
|
|
716
|
+
this._cellspecs = []
|
|
717
|
+
this._cellOpen = false
|
|
718
|
+
this._activeRowspans = [0]
|
|
719
|
+
this._columnVisits = 0
|
|
720
|
+
this._currentRow = []
|
|
721
|
+
this._linenum = -1
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
startsWith(line) {
|
|
725
|
+
return line.startsWith(this.delimiter)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
matchDelimiter(line) {
|
|
729
|
+
return line.match(this.delimiterRe)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
skipPastDelimiter(pre) {
|
|
733
|
+
this.buffer = `${this.buffer}${pre}${this.delimiter}`
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
skipPastEscapedDelimiter(pre) {
|
|
737
|
+
this.buffer = `${this.buffer}${pre.slice(0, -1)}${this.delimiter}`
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
bufferHasUnclosedQuotesInText(text, q = '"') {
|
|
741
|
+
let record = text.trim()
|
|
742
|
+
if (record === q) return true
|
|
743
|
+
if (!record.startsWith(q)) return false
|
|
744
|
+
const qq = q + q
|
|
745
|
+
const trailingQuote = record.endsWith(q)
|
|
746
|
+
if ((trailingQuote && record.endsWith(qq)) || record.startsWith(qq)) {
|
|
747
|
+
record = squeezeChar(record, q)
|
|
748
|
+
return record.startsWith(q) && !record.endsWith(q)
|
|
749
|
+
}
|
|
750
|
+
return !trailingQuote
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
bufferHasUnclosedQuotes(append = null, q = '"') {
|
|
754
|
+
const record = (append ? this.buffer + append : this.buffer).trim()
|
|
755
|
+
if (!record.startsWith(q)) return false
|
|
756
|
+
// Walk the quoted field character by character (RFC 4180)
|
|
757
|
+
let i = 1 // skip the opening quote
|
|
758
|
+
while (i < record.length) {
|
|
759
|
+
if (record[i] === q) {
|
|
760
|
+
if (i + 1 < record.length && record[i + 1] === q) {
|
|
761
|
+
i += 2 // escaped quote ""
|
|
762
|
+
} else {
|
|
763
|
+
return false // closing quote found → field is closed
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
i++
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return true // closing quote never found
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
takeCellspec() {
|
|
773
|
+
return this._cellspecs.shift() ?? null
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
pushCellspec(cellspec = {}) {
|
|
777
|
+
this._cellspecs.push(cellspec ?? {})
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
keepCellOpen() {
|
|
781
|
+
this._cellOpen = true
|
|
782
|
+
}
|
|
783
|
+
markCellClosed() {
|
|
784
|
+
this._cellOpen = false
|
|
785
|
+
}
|
|
786
|
+
isCellOpen() {
|
|
787
|
+
return this._cellOpen
|
|
788
|
+
}
|
|
789
|
+
isCellClosed() {
|
|
790
|
+
return !this._cellOpen
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async closeOpenCell(nextCellspec = {}) {
|
|
794
|
+
this.pushCellspec(nextCellspec)
|
|
795
|
+
if (this.isCellOpen()) await this.closeCell(true)
|
|
796
|
+
this._advance()
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
async closeCell(eol = false) {
|
|
800
|
+
let cellText, cellspec, repeat
|
|
801
|
+
|
|
802
|
+
if (this.format === 'psv') {
|
|
803
|
+
cellText = this.buffer
|
|
804
|
+
this.buffer = ''
|
|
805
|
+
cellspec = this.takeCellspec()
|
|
806
|
+
if (cellspec) {
|
|
807
|
+
repeat = cellspec.repeatcol ?? 1
|
|
808
|
+
delete cellspec.repeatcol
|
|
809
|
+
} else {
|
|
810
|
+
this.logger.error(
|
|
811
|
+
this.messageWithContext(
|
|
812
|
+
'table missing leading separator; recovering automatically',
|
|
813
|
+
{
|
|
814
|
+
source_location: this._startCursor,
|
|
815
|
+
}
|
|
816
|
+
)
|
|
817
|
+
)
|
|
818
|
+
cellspec = {}
|
|
819
|
+
repeat = 1
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
cellText = this.buffer.trim()
|
|
823
|
+
this.buffer = ''
|
|
824
|
+
cellspec = null
|
|
825
|
+
repeat = 1
|
|
826
|
+
if (this.format === 'csv' && cellText && cellText.includes('"')) {
|
|
827
|
+
const q = '"'
|
|
828
|
+
if (cellText.startsWith(q)) {
|
|
829
|
+
if (
|
|
830
|
+
cellText.length > 1 &&
|
|
831
|
+
cellText.endsWith(q) &&
|
|
832
|
+
!this.bufferHasUnclosedQuotesInText(cellText, q)
|
|
833
|
+
) {
|
|
834
|
+
const inner = cellText.slice(1, cellText.length - 1)
|
|
835
|
+
cellText = squeezeChar(inner.trim(), q)
|
|
836
|
+
} else {
|
|
837
|
+
this.logger.error(
|
|
838
|
+
this.messageWithContext(
|
|
839
|
+
'unclosed quote in CSV data; setting cell to empty',
|
|
840
|
+
{
|
|
841
|
+
source_location: this._reader.cursorAtPrevLine(),
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
cellText = ''
|
|
846
|
+
}
|
|
847
|
+
} else {
|
|
848
|
+
cellText = squeezeChar(cellText, '"')
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
for (let i = 1; i <= repeat; i++) {
|
|
854
|
+
let column
|
|
855
|
+
if (this.colcount === -1) {
|
|
856
|
+
this.table.columns.push(
|
|
857
|
+
(column = new Table.Column(
|
|
858
|
+
this.table,
|
|
859
|
+
this.table.columns.length + i - 1
|
|
860
|
+
))
|
|
861
|
+
)
|
|
862
|
+
if (cellspec && 'colspan' in cellspec) {
|
|
863
|
+
const extraCols = parseInt(cellspec.colspan, 10) - 1
|
|
864
|
+
if (extraCols > 0) {
|
|
865
|
+
const offset = this.table.columns.length
|
|
866
|
+
for (let j = 0; j < extraCols; j++) {
|
|
867
|
+
this.table.columns.push(new Table.Column(this.table, offset + j))
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
} else {
|
|
872
|
+
column = this.table.columns[this._currentRow.length] ?? null
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const cursorBeforeMark = this._reader.cursorBeforeMark()
|
|
876
|
+
this._reader.mark()
|
|
877
|
+
const cell = await Table.Cell.create(column, cellText, cellspec, {
|
|
878
|
+
cursor: cursorBeforeMark,
|
|
879
|
+
})
|
|
880
|
+
|
|
881
|
+
if (cell.rowspan && cell.rowspan !== 1) {
|
|
882
|
+
this._activateRowspan(cell.rowspan, cell.colspan ?? 1)
|
|
883
|
+
}
|
|
884
|
+
this._columnVisits += cell.colspan ?? 1
|
|
885
|
+
this._currentRow.push(cell)
|
|
886
|
+
|
|
887
|
+
const rowStatus = this._endOfRow()
|
|
888
|
+
if (
|
|
889
|
+
rowStatus > -1 &&
|
|
890
|
+
(this.colcount !== -1 || this._linenum > 0 || (eol && i === repeat))
|
|
891
|
+
) {
|
|
892
|
+
if (rowStatus > 0) {
|
|
893
|
+
this.logger.error(
|
|
894
|
+
this.messageWithContext(
|
|
895
|
+
'dropping cell because it exceeds specified number of columns',
|
|
896
|
+
{ source_location: cursorBeforeMark }
|
|
897
|
+
)
|
|
898
|
+
)
|
|
899
|
+
this._closeRow(true)
|
|
900
|
+
} else {
|
|
901
|
+
this._closeRow()
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
this._cellOpen = false
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
closeTable() {
|
|
909
|
+
if (this._columnVisits === 0) return
|
|
910
|
+
this.logger.error(
|
|
911
|
+
this.messageWithContext(
|
|
912
|
+
'dropping cells from incomplete row detected end of table',
|
|
913
|
+
{
|
|
914
|
+
source_location: this._reader.cursorBeforeMark(),
|
|
915
|
+
}
|
|
916
|
+
)
|
|
917
|
+
)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* @param {boolean} [drop=false]
|
|
922
|
+
* @internal
|
|
923
|
+
*/
|
|
924
|
+
_closeRow(drop = false) {
|
|
925
|
+
if (!drop) this.table.rows.body.push(this._currentRow)
|
|
926
|
+
if (this.colcount === -1) this.colcount = this._columnVisits
|
|
927
|
+
this._columnVisits = 0
|
|
928
|
+
this._currentRow = []
|
|
929
|
+
this._activeRowspans.shift()
|
|
930
|
+
this._activeRowspans[0] ??= 0
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
_activateRowspan(rowspan, colspan) {
|
|
934
|
+
for (let i = 1; i < rowspan; i++) {
|
|
935
|
+
this._activeRowspans[i] = (this._activeRowspans[i] ?? 0) + colspan
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
_endOfRow() {
|
|
940
|
+
if (this.colcount === -1) return 0
|
|
941
|
+
const eff = this._columnVisits + (this._activeRowspans[0] ?? 0)
|
|
942
|
+
if (eff < this.colcount) return -1
|
|
943
|
+
if (eff === this.colcount) return 0
|
|
944
|
+
return 1
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
_advance() {
|
|
948
|
+
this._linenum++
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
applyLogging(Table.ParserContext.prototype)
|