Преглед изворни кода

Integrated spreadsheet system into markdown

main
Rocketsoup пре 1 година
родитељ
комит
c85a73c15a
3 измењених фајлова са 77 додато и 53 уклоњено
  1. 1
    0
      js/markdown.js
  2. 62
    52
      js/spreadsheet.js
  3. 14
    1
      markdownjs.html

+ 1
- 0
js/markdown.js Прегледај датотеку

@@ -226,6 +226,7 @@ class MDUtils {
226 226
 	 * @returns {string} escaped HTML
227 227
 	 */
228 228
 	static escapeHTML(str, encodeNewlinesAsBreaks=false) {
229
+		if (typeof str !== 'string') return '';
229 230
 		var html = str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
230 231
 		if (encodeNewlinesAsBreaks) {
231 232
 			html = html.replace(/\n/g, "<br>\n");

+ 62
- 52
js/spreadsheet.js Прегледај датотеку

@@ -2686,47 +2686,10 @@ class SpreadsheetGrid {
2686 2686
 	outputValueAt(address) {
2687 2687
 		return this.cellAt(address)?.outputValue;
2688 2688
 	}
2689
-
2690
-	applyToTable() {
2691
-		for (const column of this.cells) {
2692
-			for (const cell of column) {
2693
-				cell.applyToBlock();
2694
-			}
2695
-		}
2696
-	}
2697
-
2698
-	/**
2699
-	 * @param {MDTableBlock} tableBlock
2700
-	 * @param {MDState} state
2701
-	 * @returns {SpreadsheetGrid}
2702
-	 */
2703
-	static fromTableBlock(tableBlock, state) {
2704
-		var columnCount = tableBlock.headerRow.cells.length;
2705
-		for (const row of tableBlock.bodyRows) {
2706
-			columnCount = Math.max(columnCount, row.cells.length);
2707
-		}
2708
-		const rowCount = tableBlock.bodyRows.length;
2709
-		const grid = new SpreadsheetGrid(columnCount, rowCount);
2710
-		for (var c = 0; c < columnCount; c++) {
2711
-			for (var r = 0; r < rowCount; r++) {
2712
-				const cellBlock = tableBlock.bodyRows[r].cells[c];
2713
-				if (cellBlock === undefined) continue;
2714
-				const cell = grid.cells[c][r];
2715
-				cell.block = cellBlock;
2716
-				cell.originalValue = CellValue.fromCellString(cellBlock.content.toPlaintext(state));
2717
-			}
2718
-		}
2719
-		return grid;
2720
-	}
2721 2689
 }
2722 2690
 
2723 2691
 class SpreadsheetCell {
2724 2692
 	/**
2725
-	 * @type {MDTableCellBlock|null}
2726
-	 */
2727
-	block = null;
2728
-
2729
-	/**
2730 2693
 	 * @type {CellValue}
2731 2694
 	 */
2732 2695
 	originalValue = CellValue.BLANK;
@@ -2746,18 +2709,6 @@ class SpreadsheetCell {
2746 2709
 	 * @type {CellValue|null}
2747 2710
 	 */
2748 2711
 	get resolvedValue() { return this.outputValue ?? this.originalValue; }
2749
-
2750
-	applyToBlock() {
2751
-		if (this.block === null) return;
2752
-		const resolvedValue = this.outputValue ?? this.originalValue;
2753
-		const num = resolvedValue.numericValue;
2754
-		if (num !== null) {
2755
-			this.block.attributes['data-numeric-value'] = `${num}`;
2756
-		}
2757
-		if (this.outputValue === null) return;
2758
-		this.block.content = new MDInlineBlock(new MDTextSpan(this.outputValue.formattedValue));
2759
-		this.block.cssClasses.push('calculated', `type-${this.outputValue.type}`);
2760
-	}
2761 2712
 }
2762 2713
 
2763 2714
 class SpreadsheetBlockReader extends MDBlockReader {
@@ -2768,7 +2719,7 @@ class SpreadsheetBlockReader extends MDBlockReader {
2768 2719
 	postProcess(state, blocks) {
2769 2720
 		for (const block of blocks) {
2770 2721
 			if (block instanceof MDTableBlock) {
2771
-				this.#processTable(tableBlock, state);
2722
+				this.#processTable(block, state);
2772 2723
 			}
2773 2724
 		}
2774 2725
 	}
@@ -2778,7 +2729,66 @@ class SpreadsheetBlockReader extends MDBlockReader {
2778 2729
 	 * @param {MDState} state
2779 2730
 	 */
2780 2731
 	#processTable(tableBlock, state) {
2781
-		const grid = SpreadsheetGrid.fromTableBlock(tableBlock, state);
2782
-		// TODO
2732
+		// Measure table
2733
+		const rowCount = tableBlock.bodyRows.length;
2734
+		var columnCount = 0;
2735
+		for (const row of tableBlock.bodyRows) {
2736
+			columnCount = Math.max(columnCount, row.cells.length);
2737
+		}
2738
+
2739
+		// Create and populate grid
2740
+		const grid = new SpreadsheetGrid(columnCount, rowCount);
2741
+		for (var c = 0; c < columnCount; c++) {
2742
+			for (var r = 0; r < rowCount; r++) {
2743
+				const cellBlock = tableBlock.bodyRows[r].cells[c];
2744
+				if (cellBlock === undefined) continue;
2745
+				const cellText = cellBlock.toPlaintext(state);
2746
+				const gridCell = grid.cells[c][r];
2747
+				gridCell.originalValue = CellValue.fromCellString(cellText);
2748
+			}
2749
+		}
2750
+
2751
+		// Calculate
2752
+		const expressions = new CellExpressionSet(grid);
2753
+		expressions.calculateCells();
2754
+
2755
+		// See if anything was calculated. If not, don't mess with table.
2756
+		var isCalculated = false;
2757
+		for (var c = 0; c < columnCount && !isCalculated; c++) {
2758
+			for (var r = 0; r < rowCount; r++) {
2759
+				if (grid.cells[c][r].isCalculated) {
2760
+					isCalculated = true;
2761
+					break;
2762
+				}
2763
+			}
2764
+		}
2765
+		if (!isCalculated) return;
2766
+
2767
+		// Copy results back to table
2768
+		for (var c = 0; c < columnCount; c++) {
2769
+			for (var r = 0; r < rowCount; r++) {
2770
+				const cellBlock = tableBlock.bodyRows[r].cells[c];
2771
+				if (cellBlock === undefined) continue;
2772
+				const gridCell = grid.cells[c][r];
2773
+				const gridValue = gridCell.outputValue;
2774
+				const cellText = gridValue.formattedValue;
2775
+				cellBlock.content = new MDInlineBlock(new MDTextSpan(cellText));
2776
+				if (gridCell.isCalculated) {
2777
+					cellBlock.cssClasses.push('calculated');
2778
+				}
2779
+				cellBlock.cssClasses.push(`spreadsheet-type-${gridValue.type}`);
2780
+				if (gridValue.type == CellValue.TYPE_ERROR) {
2781
+					cellBlock.attributes['title'] = gridValue.value;
2782
+				}
2783
+				const gridNumber = gridValue.numericValue();
2784
+				if (gridNumber !== null) {
2785
+					cellBlock.attributes['data-numeric-value'] = `${gridNumber}`;
2786
+				}
2787
+				const gridString = gridValue.stringValue(false);
2788
+				if (gridString !== null) {
2789
+					cellBlock.attributes['data-string-value'] = gridString;
2790
+				}
2791
+			}
2792
+		}
2783 2793
 	}
2784 2794
 }

+ 14
- 1
markdownjs.html Прегледај датотеку

@@ -12,6 +12,7 @@
12 12
 				--color-editor-text: white;
13 13
 				--color-table-header: #ddd;
14 14
 				--color-table-border: black;
15
+				--color-calculated-background: #eee;
15 16
 			}
16 17
 			:root, body {
17 18
 				width: 100%;
@@ -53,6 +54,15 @@
53 54
 				background-color: var(--color-editor-background);
54 55
 				color: var(--color-editor-text);
55 56
 			}
57
+			.calculated {
58
+				background-color: var(--color-calculated-background);
59
+				font-style: italic;
60
+			}
61
+			.spreadsheet-type-number,
62
+			.spreadsheet-type-currency,
63
+			.spreadsheet-type-percent {
64
+				text-align: right;
65
+			}
56 66
 			table {
57 67
 				border-collapse: collapse;
58 68
 			}
@@ -69,18 +79,21 @@
69 79
 					--color-text: white;
70 80
 					--color-table-header: #333;
71 81
 					--color-table-border: #ccc;
82
+					--color-calculated-background: #444;
72 83
 				}
73 84
 			}
74 85
 		</style>
75 86
 		<script src="js/markdown.js"></script>
87
+		<script src="js/spreadsheet.js"></script>
76 88
 		<script>
89
+			var parser = new Markdown([ ...Markdown.allBlockReaders, new SpreadsheetBlockReader() ], Markdown.allInlineReaders);
77 90
 			function onDocumentLoad() {
78 91
 				document.getElementById('markdowninput').addEventListener('input', onMarkdownChange);
79 92
 				setTimeout(onMarkdownChange, 0);
80 93
 			}
81 94
 			function onMarkdownChange() {
82 95
 				let markdown = document.getElementById('markdowninput').value;
83
-				let html = Markdown.completeParser.toHTML(markdown);
96
+				let html = parser.toHTML(markdown);
84 97
 				document.getElementById('preview').innerHTML = html;
85 98
 			}
86 99
 

Loading…
Откажи
Сачувај