@trebco/treb 23.6.5 → 25.0.0-rc1
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/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +285 -269
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +43 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1227 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5362 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +320 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// support for entities removed, as it adds a lot of bloat. we
|
|
23
|
+
// could easily support numbers (+hex), but html entities require
|
|
24
|
+
// a table. see
|
|
25
|
+
//
|
|
26
|
+
// https://html.spec.whatwg.org/entities.json
|
|
27
|
+
//
|
|
28
|
+
// I don't think we need this, since we're not html -- you can just
|
|
29
|
+
// enter utf8 characters. we might think about emoji entities, though,
|
|
30
|
+
// along the lines of
|
|
31
|
+
//
|
|
32
|
+
// https://github.com/markdown-it/markdown-it-emoji
|
|
33
|
+
//
|
|
34
|
+
|
|
35
|
+
// import * as he from 'he';
|
|
36
|
+
|
|
37
|
+
export interface StringFormat {
|
|
38
|
+
strong?: boolean;
|
|
39
|
+
emphasis?: boolean;
|
|
40
|
+
|
|
41
|
+
strike?: boolean; // TODO
|
|
42
|
+
pre?: boolean; // TODO
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface NewlineToken extends StringFormat {
|
|
46
|
+
type: 'newline';
|
|
47
|
+
text: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface WhitespaceToken extends StringFormat {
|
|
51
|
+
type: 'whitespace';
|
|
52
|
+
text: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface TextToken extends StringFormat {
|
|
56
|
+
type: 'text';
|
|
57
|
+
text: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface DelimeterToken extends StringFormat {
|
|
61
|
+
type: 'delimeter';
|
|
62
|
+
text: string;
|
|
63
|
+
left_flanking?: boolean;
|
|
64
|
+
right_flanking?: boolean;
|
|
65
|
+
char: string;
|
|
66
|
+
length: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type Token = NewlineToken | WhitespaceToken | TextToken | DelimeterToken;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* this external type only has text information
|
|
73
|
+
*/
|
|
74
|
+
export interface FormattedString extends StringFormat {
|
|
75
|
+
text: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* utility for formatting markdown strings. we split text into tokens
|
|
80
|
+
* by format. implemented as a factory/singleton, stateless.
|
|
81
|
+
*
|
|
82
|
+
* note: in case it's not clear, where I reference MD rules, I mean
|
|
83
|
+
* CommonMark. we may add some GFM as well (strike?).
|
|
84
|
+
*
|
|
85
|
+
* UPDATE: moving into the parser lib, since it's a parser. even though
|
|
86
|
+
* it's totally independent. (has no deps, though, nice).
|
|
87
|
+
*/
|
|
88
|
+
export class MDParser {
|
|
89
|
+
|
|
90
|
+
private static _instance: MDParser = new MDParser();
|
|
91
|
+
|
|
92
|
+
protected constructor() {
|
|
93
|
+
// ...
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public static get instance(): MDParser {
|
|
97
|
+
return this._instance;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* given some formatted text (output of the `Parse` method), return HTML.
|
|
102
|
+
* FIXME: is this used outside of testing? seems like we're wasting bytes.
|
|
103
|
+
*
|
|
104
|
+
* also the way this works adds extra tags if you have nested styles. not
|
|
105
|
+
* an issue if it's just for testing though.
|
|
106
|
+
*/
|
|
107
|
+
public HTML(formatted: FormattedString[][]): string {
|
|
108
|
+
|
|
109
|
+
const lines: string[] = [];
|
|
110
|
+
|
|
111
|
+
for (const line of formatted) {
|
|
112
|
+
const text: string[] = [];
|
|
113
|
+
|
|
114
|
+
for (const element of line) {
|
|
115
|
+
if (element.pre) { text.push('<pre>'); }
|
|
116
|
+
if (element.emphasis) { text.push('<em>'); }
|
|
117
|
+
if (element.strong) { text.push('<strong>'); }
|
|
118
|
+
if (element.strike) { text.push('<strike>'); }
|
|
119
|
+
|
|
120
|
+
text.push(element.text);
|
|
121
|
+
|
|
122
|
+
if (element.strike) { text.push('</strike>'); }
|
|
123
|
+
if (element.strong) { text.push('</strong>'); }
|
|
124
|
+
if (element.emphasis) { text.push('</em>'); }
|
|
125
|
+
if (element.pre) { text.push('</pre>'); }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
lines.push(text.join(''));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return lines.join('<br/>\n');
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* this is a replacement for the Parse() method, if you don't actually
|
|
137
|
+
* want to parse markdown. the aim is to have a unified result format,
|
|
138
|
+
* even if we're not handling md.
|
|
139
|
+
*/
|
|
140
|
+
public Dummy(text = ''): FormattedString[][] {
|
|
141
|
+
return text.split(/\n/).map(text => [{ text }]);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* given some input text, creates a set of text tokens with
|
|
146
|
+
* emphasis/strong emphasis applied. splits into lines (the
|
|
147
|
+
* outer array). whitespace (other than newlines) is preserved.
|
|
148
|
+
*/
|
|
149
|
+
public Parse(text = ''): FormattedString[][] {
|
|
150
|
+
|
|
151
|
+
// first pass: tokenize
|
|
152
|
+
|
|
153
|
+
const tokens = this.Tokenize(text);
|
|
154
|
+
|
|
155
|
+
// for the most part, MD emphapsis/strong can be parsed as if it were
|
|
156
|
+
// using open/close tags. that's not strictly the case, however, and
|
|
157
|
+
// there is at least one situation that has some required ambiguity.
|
|
158
|
+
|
|
159
|
+
// MD does specify "left-flanking" and "right-flanking" delimiter runs,
|
|
160
|
+
// which can open or close formatting, respectively (and a delimeter run
|
|
161
|
+
// may be both left- and right-flanking).
|
|
162
|
+
|
|
163
|
+
// second pass: assign those flanks on delimeters. from CM spec:
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
|
|
167
|
+
A left-flanking delimiter run is a delimiter run that is (1) not followed by
|
|
168
|
+
Unicode whitespace, and either (2a) not followed by a punctuation character,
|
|
169
|
+
or (2b) followed by a punctuation character and preceded by Unicode whitespace
|
|
170
|
+
or a punctuation character. For purposes of this definition, the beginning and
|
|
171
|
+
the end of the line count as Unicode whitespace.
|
|
172
|
+
|
|
173
|
+
A right-flanking delimiter run is a delimiter run that is (1) not preceded by
|
|
174
|
+
Unicode whitespace, and either (2a) not preceded by a punctuation character,
|
|
175
|
+
or (2b) preceded by a punctuation character and followed by Unicode whitespace
|
|
176
|
+
or a punctuation character. For purposes of this definition, the beginning and
|
|
177
|
+
the end of the line count as Unicode whitespace.
|
|
178
|
+
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
// FIXME: could this not be consolidated with "apply formatting", below? or
|
|
182
|
+
// is the concern that if we do that, we might calculate more than once for
|
|
183
|
+
// any given token? it might still be more efficient...
|
|
184
|
+
|
|
185
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
186
|
+
const token = tokens[i];
|
|
187
|
+
if (token.type === 'delimeter') {
|
|
188
|
+
|
|
189
|
+
const preceding = tokens[i-1];
|
|
190
|
+
const following = tokens[i+1];
|
|
191
|
+
|
|
192
|
+
const preceded_by_whitespace = !preceding || preceding.type === 'whitespace' || preceding.type === 'newline';
|
|
193
|
+
const preceded_by_punctuation = preceding && preceding.type === 'text' && /[^\w\d]$/.test(preceding.text);
|
|
194
|
+
|
|
195
|
+
const followed_by_whitespace = !following || following.type === 'whitespace' || following.type === 'newline';
|
|
196
|
+
const followed_by_punctuation = following && following.type === 'text' && /^[^\w\d]/.test(following.text);
|
|
197
|
+
|
|
198
|
+
token.left_flanking = ((!followed_by_whitespace) && ((!followed_by_punctuation) || preceded_by_whitespace));
|
|
199
|
+
token.right_flanking = ((!preceded_by_whitespace) && ((!preceded_by_punctuation) || followed_by_whitespace));
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// next pass does left/right token matching and applies formatting
|
|
205
|
+
|
|
206
|
+
this.ApplyFormatting(tokens);
|
|
207
|
+
|
|
208
|
+
// last pass consolidates text with like formats, scrubs used tokens
|
|
209
|
+
// (actually changes _unused_ tokens -> text), and splits into lines.
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
const formatted: FormattedString[][] = this.Consolidate(tokens);
|
|
213
|
+
|
|
214
|
+
for (const line of formatted) {
|
|
215
|
+
for (const token of line) {
|
|
216
|
+
token.text = he.decode(token.text);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return formatted ; // this.Consolidate(tokens) as FormattedString[][];
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
return this.Consolidate(tokens) as FormattedString[][];
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** is this worth a function call? will it get inlined? */
|
|
228
|
+
protected IsWhitespace(char: string): boolean {
|
|
229
|
+
return char === ' ' || char === '\t';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** is this worth a function call? will it get inlined? */
|
|
233
|
+
protected IsNewline(char: string): boolean {
|
|
234
|
+
return char === '\r' || char === '\n';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** is this worth a function call? will it get inlined? */
|
|
238
|
+
protected IsDelimeter(char: string): boolean {
|
|
239
|
+
return char === '*' || char === '_' || char === '~';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* consolidate text with common formatting. splits into lines (newlines are not rendered).
|
|
244
|
+
*/
|
|
245
|
+
protected Consolidate(tokens: Token[]): TextToken[][] {
|
|
246
|
+
const result: TextToken[][] = [];
|
|
247
|
+
const format: StringFormat = {};
|
|
248
|
+
|
|
249
|
+
let line: TextToken[] = [];
|
|
250
|
+
let current_token: TextToken = {type: 'text', text: ''};
|
|
251
|
+
for (const token of tokens) {
|
|
252
|
+
if (token.type === 'newline') {
|
|
253
|
+
if (current_token.text.length) {
|
|
254
|
+
line.push(current_token);
|
|
255
|
+
}
|
|
256
|
+
current_token = {...format, text: '', type: 'text'};
|
|
257
|
+
result.push(line);
|
|
258
|
+
line = [];
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
|
|
262
|
+
// yuck
|
|
263
|
+
|
|
264
|
+
// can we have a method? or maybe this should be a bitmask,
|
|
265
|
+
// so we can use === and only have to worry about 0
|
|
266
|
+
|
|
267
|
+
if ((!!format.strong !== !!token.strong) || (!!format.emphasis !== !!token.emphasis) || (!!format.strike !== !!token.strike)) {
|
|
268
|
+
format.strong = !!token.strong;
|
|
269
|
+
format.emphasis = !!token.emphasis;
|
|
270
|
+
format.strike = !!token.strike;
|
|
271
|
+
if (current_token.text.length) {
|
|
272
|
+
line.push(current_token);
|
|
273
|
+
}
|
|
274
|
+
current_token = {...format, text: '', type: 'text'};
|
|
275
|
+
}
|
|
276
|
+
switch (token.type) {
|
|
277
|
+
case 'text':
|
|
278
|
+
case 'whitespace':
|
|
279
|
+
current_token.text += token.text;
|
|
280
|
+
break;
|
|
281
|
+
|
|
282
|
+
case 'delimeter':
|
|
283
|
+
for (let i = 0; i < token.length; i++) { current_token.text += token.char; }
|
|
284
|
+
break;
|
|
285
|
+
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (current_token.text.length) {
|
|
291
|
+
line.push(current_token);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (line.length) {
|
|
295
|
+
result.push(line);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
*
|
|
303
|
+
*/
|
|
304
|
+
protected ApplyFormatting(tokens: Token[], open?: DelimeterToken): {index: number, token?: DelimeterToken} {
|
|
305
|
+
|
|
306
|
+
// if we're called with no opening token, that's the start
|
|
307
|
+
// of the text block and formatting is clear (no emphasis).
|
|
308
|
+
|
|
309
|
+
// console.info("AF", "open", open);
|
|
310
|
+
|
|
311
|
+
let index = 0;
|
|
312
|
+
const length = tokens.length;
|
|
313
|
+
|
|
314
|
+
for (index = 0; index < length; index++) {
|
|
315
|
+
const token = tokens[index];
|
|
316
|
+
|
|
317
|
+
if (token.type === 'delimeter') {
|
|
318
|
+
|
|
319
|
+
// check if this token can close (all or in part) our opening tag.
|
|
320
|
+
// if so, return closing token and index. note that we are checking
|
|
321
|
+
// length > 0 here; that is because operations may reduce the
|
|
322
|
+
// "available length" when processing.
|
|
323
|
+
|
|
324
|
+
if (open && token.right_flanking && open.char === token.char && token.length > 0) {
|
|
325
|
+
// console.info(" ", "close", token);
|
|
326
|
+
return {index, token};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// if not, see if we can start a new block
|
|
330
|
+
|
|
331
|
+
if (token.left_flanking) {
|
|
332
|
+
const result = this.ApplyFormatting(tokens.slice(index + 1), token);
|
|
333
|
+
if (result.token) {
|
|
334
|
+
|
|
335
|
+
// what format do we apply? it depends on the MIN of open, close,
|
|
336
|
+
// because it may be a partial close or it may have extra characters.
|
|
337
|
+
|
|
338
|
+
const format = Math.min(result.token.length, token.length);
|
|
339
|
+
|
|
340
|
+
// what format to we apply to the contained block? depends on the
|
|
341
|
+
// CLOSING delimeter, which may be < the opening delimeter.
|
|
342
|
+
|
|
343
|
+
const strike = token.char === '~';
|
|
344
|
+
|
|
345
|
+
const emphasis = !strike && !!(format % 2);
|
|
346
|
+
const strong = !strike && (format >= 2);
|
|
347
|
+
|
|
348
|
+
/*
|
|
349
|
+
const formats: string[] = [];
|
|
350
|
+
if (emphasis) formats.push('emphasis');
|
|
351
|
+
if (strong) formats.push('strong');
|
|
352
|
+
console.info('applying', formats, 'to tokens from', index + 1, 'to', index + result.index, `(len ${length})`);
|
|
353
|
+
*/
|
|
354
|
+
|
|
355
|
+
// apply this format to all the handled tokens (inclusive)
|
|
356
|
+
|
|
357
|
+
for (let i = index + 1; i <= index + result.index; i++) {
|
|
358
|
+
tokens[i].strong = (!!tokens[i].strong) || strong;
|
|
359
|
+
tokens[i].emphasis = (!!tokens[i].emphasis) || emphasis;
|
|
360
|
+
tokens[i].strike = (!!tokens[i].strike) || strike;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// now we have to handle two separate cases.
|
|
364
|
+
|
|
365
|
+
// one, the closing delimeter is shorter than the opening delimeter.
|
|
366
|
+
// this happens if you have composite formatting (generally three, but
|
|
367
|
+
// could be more) and only partially close, like
|
|
368
|
+
//
|
|
369
|
+
// ___something_ strange__
|
|
370
|
+
//
|
|
371
|
+
// in that case, we want to handle the opening delimeter again, but
|
|
372
|
+
// only with the remaining length.
|
|
373
|
+
//
|
|
374
|
+
// for case two, closing length >= opening length. in this case, we
|
|
375
|
+
// reduce the lengths of both tokens but don't handle the opening again.
|
|
376
|
+
// in the case of === length, both tokens should basically disappear.
|
|
377
|
+
// if close > open, then the remaining balance will be treated as text.
|
|
378
|
+
// then we can jump ahead by the handled amount.
|
|
379
|
+
|
|
380
|
+
result.token.length -= format;
|
|
381
|
+
token.length -= format;
|
|
382
|
+
|
|
383
|
+
if (token.length > 0) {
|
|
384
|
+
index--; // repeat
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
index += result.index;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// console.info("finished");
|
|
399
|
+
|
|
400
|
+
return {index};
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
*
|
|
406
|
+
*/
|
|
407
|
+
protected Tokenize(text = ''): Token[] {
|
|
408
|
+
const tokens: Token[] = [];
|
|
409
|
+
const length = text.length;
|
|
410
|
+
|
|
411
|
+
// first pass parse converts text into tokens
|
|
412
|
+
|
|
413
|
+
// FIXME: our escape rule is not quite right -- escape turns out to
|
|
414
|
+
// be pretty complicated, see CM spec @ 6.1. punting for the time being,
|
|
415
|
+
// we just always escape the next character.
|
|
416
|
+
|
|
417
|
+
let index = 0;
|
|
418
|
+
let escape = false;
|
|
419
|
+
let current_token = ''; // implicit text token
|
|
420
|
+
|
|
421
|
+
for (index = 0; index < length; index++) {
|
|
422
|
+
const char = text[index];
|
|
423
|
+
|
|
424
|
+
// we do this three times, but it's kind of hard to fold properly
|
|
425
|
+
|
|
426
|
+
if (this.IsWhitespace(char)) {
|
|
427
|
+
|
|
428
|
+
if (current_token) {
|
|
429
|
+
tokens.push({ type: 'text', text: current_token });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
let tmp = char;
|
|
433
|
+
for (;;) { // while (true) {
|
|
434
|
+
const next_char = text[index+1];
|
|
435
|
+
if (this.IsWhitespace(next_char)) {
|
|
436
|
+
tmp += next_char;
|
|
437
|
+
index++;
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
tokens.push({
|
|
444
|
+
type: 'whitespace',
|
|
445
|
+
text: tmp,
|
|
446
|
+
})
|
|
447
|
+
escape = false;
|
|
448
|
+
current_token = '';
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
else if (this.IsNewline(char)) {
|
|
452
|
+
|
|
453
|
+
if (current_token) {
|
|
454
|
+
tokens.push({ type: 'text', text: current_token });
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let tmp = char;
|
|
458
|
+
for (;;) { // while (true) {
|
|
459
|
+
const next_char = text[index+1];
|
|
460
|
+
if (this.IsNewline(next_char)) {
|
|
461
|
+
tmp += next_char;
|
|
462
|
+
index++;
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
tokens.push({
|
|
469
|
+
type: 'newline',
|
|
470
|
+
text: tmp,
|
|
471
|
+
})
|
|
472
|
+
escape = false;
|
|
473
|
+
current_token = '';
|
|
474
|
+
|
|
475
|
+
}
|
|
476
|
+
else if (escape) {
|
|
477
|
+
current_token += char;
|
|
478
|
+
escape = false;
|
|
479
|
+
}
|
|
480
|
+
else if (this.IsDelimeter(char)) {
|
|
481
|
+
|
|
482
|
+
if (current_token) {
|
|
483
|
+
tokens.push({ type: 'text', text: current_token });
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let tmp = char;
|
|
487
|
+
for (;;) { // while (true) {
|
|
488
|
+
const next_char = text[index+1];
|
|
489
|
+
if (next_char === char) { // delimeters do not mix
|
|
490
|
+
tmp += next_char;
|
|
491
|
+
index++;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
tokens.push({
|
|
498
|
+
type: 'delimeter',
|
|
499
|
+
text: tmp,
|
|
500
|
+
char,
|
|
501
|
+
length: tmp.length,
|
|
502
|
+
})
|
|
503
|
+
escape = false;
|
|
504
|
+
current_token = '';
|
|
505
|
+
|
|
506
|
+
}
|
|
507
|
+
else if (char === '\\') {
|
|
508
|
+
escape = true;
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
current_token += char;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (current_token) {
|
|
517
|
+
tokens.push({type: 'text', text: current_token});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return tokens;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
|