Parcourir la source

Markdown playground lets readers be changed on the fly

main
Rocketsoup il y a 1 an
Parent
révision
57fde9acc6
6 fichiers modifiés avec 273 ajouts et 49 suppressions
  1. 49
    27
      js/markdown.js
  2. 1
    1
      js/markdown.min.js
  3. 2
    2
      js/spreadsheet.js
  4. 1
    1
      js/spreadsheet.min.js
  5. 196
    18
      markdownjs.html
  6. 24
    0
      testjs.html

+ 49
- 27
js/markdown.js Voir le fichier

585
 	 * Array of `MDReader`s sorted by block reading priority.
585
 	 * Array of `MDReader`s sorted by block reading priority.
586
 	 * @type {MDReader[]}
586
 	 * @type {MDReader[]}
587
 	 */
587
 	 */
588
-	#readersByBlockPriority = [];
588
+	readersByBlockPriority = [];
589
 
589
 
590
 	/**
590
 	/**
591
 	 * Array of `MDReader`s sorted by tokenization priority.
591
 	 * Array of `MDReader`s sorted by tokenization priority.
592
 	 * @type {MDReader[]}
592
 	 * @type {MDReader[]}
593
 	 */
593
 	 */
594
-	#readersByTokenPriority = [];
594
+	readersByTokenPriority = [];
595
 
595
 
596
 	/**
596
 	/**
597
 	 * Tuples of `pass:number` and `MDReader` sorted substitution priority.
597
 	 * Tuples of `pass:number` and `MDReader` sorted substitution priority.
598
 	 * @type {Array}
598
 	 * @type {Array}
599
 	 */
599
 	 */
600
-	#readersBySubstitutePriority = [];
600
+	readersBySubstitutePriority = [];
601
 
601
 
602
 	/**
602
 	/**
603
 	 * Mapping of reference symbols to URLs.
603
 	 * Mapping of reference symbols to URLs.
631
 		tagFilter=null) {
631
 		tagFilter=null) {
632
 		this.#lines = lines;
632
 		this.#lines = lines;
633
 		this.config = config;
633
 		this.config = config;
634
-		this.#readersByBlockPriority = readersByBlockPriority
635
-		this.#readersByTokenPriority = readersByTokenPriority
636
-		this.#readersBySubstitutePriority = readersBySubstitutePriority
634
+		this.readersByBlockPriority = readersByBlockPriority
635
+		this.readersByTokenPriority = readersByTokenPriority
636
+		this.readersBySubstitutePriority = readersBySubstitutePriority
637
 		this.tagFilter = tagFilter;
637
 		this.tagFilter = tagFilter;
638
 	}
638
 	}
639
 
639
 
707
 			this.p++;
707
 			this.p++;
708
 		}
708
 		}
709
 		if (!this.hasLines(1)) return null;
709
 		if (!this.hasLines(1)) return null;
710
-		for (const reader of this.root.#readersByBlockPriority) {
710
+		for (const reader of this.root.readersByBlockPriority) {
711
 			const startP = this.p;
711
 			const startP = this.p;
712
 			const block = reader.readBlock(this);
712
 			const block = reader.readBlock(this);
713
 			if (block) {
713
 			if (block) {
767
 				continue;
767
 				continue;
768
 			}
768
 			}
769
 			var found = false;
769
 			var found = false;
770
-			for (const reader of this.root.#readersByTokenPriority) {
770
+			for (const reader of this.root.readersByTokenPriority) {
771
 				const token = reader.readToken(this, remainder);
771
 				const token = reader.readToken(this, remainder);
772
 				if (token === null) continue;
772
 				if (token === null) continue;
773
 				if (token === undefined) {
773
 				if (token === undefined) {
827
 		var anyChanges = false;
827
 		var anyChanges = false;
828
 		do {
828
 		do {
829
 			anyChanges = false;
829
 			anyChanges = false;
830
-			for (const readerTuple of this.root.#readersBySubstitutePriority) {
830
+			for (const readerTuple of this.root.readersBySubstitutePriority) {
831
 				/** @type {number} */
831
 				/** @type {number} */
832
 				const pass = readerTuple[0];
832
 				const pass = readerTuple[0];
833
 				/** @type {MDReader} */
833
 				/** @type {MDReader} */
1154
 		if (!state.hasLines(2)) return null;
1154
 		if (!state.hasLines(2)) return null;
1155
 		var modifier;
1155
 		var modifier;
1156
 		let contentLine = state.lines[p++].trim();
1156
 		let contentLine = state.lines[p++].trim();
1157
-		[contentLine, modifier] = MDTagModifier.fromLine(contentLine);
1157
+		[contentLine, modifier] = MDTagModifier.fromLine(contentLine, state);
1158
 		let underLine = state.lines[p++].trim();
1158
 		let underLine = state.lines[p++].trim();
1159
 		if (contentLine == '') return null;
1159
 		if (contentLine == '') return null;
1160
 		if (/^=+$/.exec(underLine)) {
1160
 		if (/^=+$/.exec(underLine)) {
1196
 		var p = state.p;
1196
 		var p = state.p;
1197
 		let line = state.lines[p++];
1197
 		let line = state.lines[p++];
1198
 		var modifier;
1198
 		var modifier;
1199
-		[line, modifier] = MDTagModifier.fromLine(line);
1199
+		[line, modifier] = MDTagModifier.fromLine(line, state);
1200
 		var groups = MDHashHeadingReader.#hashHeadingRegex.exec(line);
1200
 		var groups = MDHashHeadingReader.#hashHeadingRegex.exec(line);
1201
 		if (groups === null) return null;
1201
 		if (groups === null) return null;
1202
 		state.p = p;
1202
 		state.p = p;
1215
 		var p = state.p;
1215
 		var p = state.p;
1216
 		let line = state.lines[p++];
1216
 		let line = state.lines[p++];
1217
 		var modifier;
1217
 		var modifier;
1218
-		[line, modifier] = MDTagModifier.fromLine(line);
1218
+		[line, modifier] = MDTagModifier.fromLine(line, state);
1219
 		var groups = MDSubtextReader.#subtextRegex.exec(line);
1219
 		var groups = MDSubtextReader.#subtextRegex.exec(line);
1220
 		if (groups === null) return null;
1220
 		if (groups === null) return null;
1221
 		state.p = p;
1221
 		state.p = p;
1446
 		var p = state.p;
1446
 		var p = state.p;
1447
 		let openFenceLine = state.lines[p++];
1447
 		let openFenceLine = state.lines[p++];
1448
 		var modifier;
1448
 		var modifier;
1449
-		[openFenceLine, modifier] = MDTagModifier.fromLine(openFenceLine);
1449
+		[openFenceLine, modifier] = MDTagModifier.fromLine(openFenceLine, state);
1450
 		const match = /^```\s*([a-z0-9]*)\s*$/.exec(openFenceLine);
1450
 		const match = /^```\s*([a-z0-9]*)\s*$/.exec(openFenceLine);
1451
 		if (match === null) return null;
1451
 		if (match === null) return null;
1452
 		const language = match[1].length > 0 ? match[1] : null;
1452
 		const language = match[1].length > 0 ? match[1] : null;
1516
 		var p = state.p;
1516
 		var p = state.p;
1517
 		let line = state.lines[p++];
1517
 		let line = state.lines[p++];
1518
 		var modifier;
1518
 		var modifier;
1519
-		[line, modifier] = MDTagModifier.fromLine(line);
1519
+		[line, modifier] = MDTagModifier.fromLine(line, state);
1520
 		if (MDHorizontalRuleReader.#horizontalRuleRegex.exec(line)) {
1520
 		if (MDHorizontalRuleReader.#horizontalRuleRegex.exec(line)) {
1521
 			state.p = p;
1521
 			state.p = p;
1522
 			let block = new MDHorizontalRuleNode();
1522
 			let block = new MDHorizontalRuleNode();
1603
 		if (!state.hasLines(2)) return null;
1603
 		if (!state.hasLines(2)) return null;
1604
 		let startP = state.p;
1604
 		let startP = state.p;
1605
 		let firstLine = state.lines[startP];
1605
 		let firstLine = state.lines[startP];
1606
-		var modifier = MDTagModifier.fromLine(firstLine)[1];
1606
+		var modifier = MDTagModifier.fromLine(firstLine, state)[1];
1607
 		let headerRow = this.#readTableRow(state, true);
1607
 		let headerRow = this.#readTableRow(state, true);
1608
 		if (headerRow === null) {
1608
 		if (headerRow === null) {
1609
 			state.p = startP;
1609
 			state.p = startP;
2210
 			tokens.splice(match.index, match.tokens.length, node);
2210
 			tokens.splice(match.index, match.tokens.length, node);
2211
 			return true;
2211
 			return true;
2212
 		}
2212
 		}
2213
-		if (match = MDToken.findFirstTokens(tokens, [ MDTokenType.Bang, MDTokenType.Label, MDTokenType.META_OptionalWhitespace, MDTokenType.Label ])) {
2214
-			let alt = match.tokens[1].content;
2215
-			let ref = match.tokens[match.tokens.length - 1].content;
2216
-			tokens.splice(match.index, match.tokens.length, new MDReferencedImageNode(ref, alt));
2217
-			return true;
2218
-		}
2219
 		return false;
2213
 		return false;
2220
 	}
2214
 	}
2221
 
2215
 
2222
 	compareSubstituteOrdering(other, pass) {
2216
 	compareSubstituteOrdering(other, pass) {
2223
-		if (other instanceof MDLinkReader) {
2217
+		if (other.constructor === MDLinkReader || other.constructor === MDReferencedLinkReader) {
2224
 			return -1;
2218
 			return -1;
2225
 		}
2219
 		}
2226
 		return 0;
2220
 		return 0;
2228
 }
2222
 }
2229
 
2223
 
2230
 class MDReferencedImageReader extends MDReferencedLinkReader {
2224
 class MDReferencedImageReader extends MDReferencedLinkReader {
2231
-	readBlock(state) { return null; }
2225
+	readToken(state, line) {
2226
+		const s = super.readToken(state, line);
2227
+		if (s) return s;
2228
+		if (line.startsWith('!')) return new MDToken('!', MDTokenType.Bang);
2229
+		return null;
2230
+	}
2231
+
2232
+	substituteTokens(state, pass, tokens) {
2233
+		var match;
2234
+		if (match = MDToken.findFirstTokens(tokens, [ MDTokenType.Bang, MDTokenType.Label, MDTokenType.META_OptionalWhitespace, MDTokenType.Label ])) {
2235
+			let alt = match.tokens[1].content;
2236
+			let ref = match.tokens[match.tokens.length - 1].content;
2237
+			tokens.splice(match.index, match.tokens.length, new MDReferencedImageNode(ref, alt));
2238
+			return true;
2239
+		}
2240
+		return false;
2241
+	}
2232
 
2242
 
2233
 	compareSubstituteOrdering(other, pass) {
2243
 	compareSubstituteOrdering(other, pass) {
2234
-		if (other instanceof MDLinkReader) {
2244
+		if (other.constructor === MDLinkReader || other.constructor === MDReferencedLinkReader) {
2235
 			return -1;
2245
 			return -1;
2236
 		}
2246
 		}
2237
 		return 0;
2247
 		return 0;
2286
 
2296
 
2287
 class MDHTMLTagReader extends MDReader {
2297
 class MDHTMLTagReader extends MDReader {
2288
 	readToken(state, line) {
2298
 	readToken(state, line) {
2289
-		const tag = MDHTMLTag.fromLineStart(line)
2299
+		const tag = MDHTMLTag.fromLineStart(line, state);
2290
 		if (tag === null) return null;
2300
 		if (tag === null) return null;
2291
 		if (!state.root.tagFilter.isValidTagName(tag.tagName)) return null;
2301
 		if (!state.root.tagFilter.isValidTagName(tag.tagName)) return null;
2292
 		state.root.tagFilter.scrubTag(tag);
2302
 		state.root.tagFilter.scrubTag(tag);
2943
 	 */
2953
 	 */
2944
 	toHTML(state) {
2954
 	toHTML(state) {
2945
 		if (this.href === '') {
2955
 		if (this.href === '') {
2946
-			this.href = state.urlForReference(this.reference);
2956
+			const url = state.urlForReference(this.reference);
2957
+			if (url) this.href = url;
2947
 			const title = state.urlTitleForReference(this.reference);
2958
 			const title = state.urlTitleForReference(this.reference);
2948
 			if (title) this.attributes['title'] = title;
2959
 			if (title) this.attributes['title'] = title;
2949
 		}
2960
 		}
3693
 	/**
3704
 	/**
3694
 	 * Extracts modifier from line.
3705
 	 * Extracts modifier from line.
3695
 	 * @param {string} line
3706
 	 * @param {string} line
3707
+	 * @param {MDState} state
3696
 	 * @returns {Array} Tuple with remaining line and MDTagModifier.
3708
 	 * @returns {Array} Tuple with remaining line and MDTagModifier.
3697
 	 */
3709
 	 */
3698
-	static fromLine(line) {
3710
+	static fromLine(line, state) {
3711
+		if (state) {
3712
+			var found = false;
3713
+			for (const reader of state.root.readersByBlockPriority) {
3714
+				if (reader instanceof MDModifierReader) {
3715
+					found = true;
3716
+					break;
3717
+				}
3718
+			}
3719
+			if (!found) return [ line, null ];
3720
+		}
3699
 		let groups = this.#trailingClassRegex.exec(line);
3721
 		let groups = this.#trailingClassRegex.exec(line);
3700
 		if (groups === null) return [ line, null ];
3722
 		if (groups === null) return [ line, null ];
3701
 		let bareLine = groups[1];
3723
 		let bareLine = groups[1];

+ 1
- 1
js/markdown.min.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 2
- 2
js/spreadsheet.js Voir le fichier

1642
 		if (tokens[start + 1].type != CellExpressionTokenType.Colon) return null;
1642
 		if (tokens[start + 1].type != CellExpressionTokenType.Colon) return null;
1643
 		if (!tokens[end].type.isPotentialAddress()) return null;
1643
 		if (!tokens[end].type.isPotentialAddress()) return null;
1644
 		const last = tokens[end].content.toUpperCase();
1644
 		const last = tokens[end].content.toUpperCase();
1645
-		const firstAddress = new CellAddress(first);
1646
-		const lastAddress = new CellAddress(last);
1645
+		const firstAddress = CellAddress.fromString(first);
1646
+		const lastAddress = CellAddress.fromString(last);
1647
 		const range = new CellAddressRange(firstAddress, lastAddress);
1647
 		const range = new CellAddressRange(firstAddress, lastAddress);
1648
 		return new CellExpression(CellExpressionOperation.Range, [ range ]);
1648
 		return new CellExpression(CellExpressionOperation.Range, [ range ]);
1649
 	}
1649
 	}

+ 1
- 1
js/spreadsheet.min.js
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 196
- 18
markdownjs.html Voir le fichier

22
 				background-color: var(--color-background);
22
 				background-color: var(--color-background);
23
 				color: var(--color-text);
23
 				color: var(--color-text);
24
 			}
24
 			}
25
-/* body {
26
-    width: 87.5%;
27
-    margin-left: auto;
28
-    margin-right: auto;
29
-    padding-left: 12.5%;
30
-    background-color: #fffff8;
31
-    color: #111;
32
-    max-width: 1400px;
33
-    counter-reset: sidenote-counter;
34
-} */
35
 			.inputhalf {
25
 			.inputhalf {
36
 				position: absolute;
26
 				position: absolute;
37
 				width: 50%;
27
 				width: 50%;
54
 				margin: 0 auto;
44
 				margin: 0 auto;
55
 				max-width: 600px;
45
 				max-width: 600px;
56
 			}
46
 			}
47
+			.toolbar {
48
+				position: absolute;
49
+				z-index: 1;
50
+				background-color: #eee;
51
+				min-height: 32px;
52
+				/* max-height: 300px; */
53
+				width: 100%;
54
+				color: black;
55
+				border-bottom: 1px solid black;
56
+				box-sizing: border-box;
57
+				padding: 4px 20px;
58
+			}
59
+			#readercontainer {
60
+				overflow-y: scroll;
61
+			}
62
+			.reader-header {
63
+				margin-top: 0.25em;
64
+				font-weight: bold;
65
+			}
66
+			.reader-check {
67
+				display: inline-block;
68
+				padding: 4px 8px;
69
+				border: 1px dotted gray;
70
+				margin: 4px;
71
+			}
57
 			#markdowninput {
72
 			#markdowninput {
73
+				position: absolute;
74
+				top: 32px;
75
+				margin-top: 32px;
58
 				width: 100%;
76
 				width: 100%;
59
-				height: 100%;
77
+				height: calc(100% - 32px);
60
 				box-sizing: border-box;
78
 				box-sizing: border-box;
61
 				margin: 0;
79
 				margin: 0;
62
 				padding: 0.5em;
80
 				padding: 0.5em;
63
 				border: 0;
81
 				border: 0;
64
 				background-color: var(--color-editor-background);
82
 				background-color: var(--color-editor-background);
65
 				color: var(--color-editor-text);
83
 				color: var(--color-editor-text);
84
+				box-sizing: border-box;
66
 			}
85
 			}
67
 			.calculated {
86
 			.calculated {
68
 				background-color: var(--color-calculated-background);
87
 				background-color: var(--color-calculated-background);
203
 			var parser = new Markdown([ ...Markdown.allReaders, new MDSpreadsheetReader() ]);
222
 			var parser = new Markdown([ ...Markdown.allReaders, new MDSpreadsheetReader() ]);
204
 			function onDocumentLoad() {
223
 			function onDocumentLoad() {
205
 				document.getElementById('markdowninput').addEventListener('input', onMarkdownChange);
224
 				document.getElementById('markdowninput').addEventListener('input', onMarkdownChange);
225
+				populateReaderCheckboxes();
206
 				setTimeout(onMarkdownChange, 0);
226
 				setTimeout(onMarkdownChange, 0);
207
 			}
227
 			}
208
 			function onMarkdownChange() {
228
 			function onMarkdownChange() {
210
 				let html = parser.toHTML(markdown);
230
 				let html = parser.toHTML(markdown);
211
 				document.getElementById('preview').innerHTML = html;
231
 				document.getElementById('preview').innerHTML = html;
212
 			}
232
 			}
233
+			var blockReaderClasses = {
234
+				'Heading (underline)': MDUnderlinedHeadingReader,
235
+				'Heading (hash)': MDHashHeadingReader,
236
+				'Subtext': MDSubtextReader,
237
+				'Block quote': MDBlockQuoteReader,
238
+				'Unordered list': MDUnorderedListReader,
239
+				'Ordered list': MDOrderedListReader,
240
+				'Code block (fenced)': MDFencedCodeBlockReader,
241
+				'Code block (indented)': MDIndentedCodeBlockReader,
242
+				'Horizontal rule': MDHorizontalRuleReader,
243
+				'Table': MDTableReader,
244
+				'Definition list': MDDefinitionListReader,
245
+				'Paragraph': MDParagraphReader,
246
+				'Spreadsheets': MDSpreadsheetReader,
247
+			};
248
+			var inlineReaderClasses = {
249
+				'Emphasis': MDEmphasisReader,
250
+				'Strong': MDStrongReader,
251
+				'Strikethrough': MDStrikethroughReader,
252
+				'Underline': MDUnderlineReader,
253
+				'Highlight': MDHighlightReader,
254
+				'Links': MDLinkReader,
255
+				'Referenced links': MDReferencedLinkReader,
256
+				'Images': MDImageReader,
257
+				'Referenced images': MDReferencedImageReader,
258
+				'Code span': MDCodeSpanReader,
259
+				'Subscript': MDSubscriptReader,
260
+				'Superscript': MDSuperscriptReader,
261
+				'Footnotes': MDFootnoteReader,
262
+				'Abbrevations': MDAbbreviationReader,
263
+				'HTML tags': MDHTMLTagReader,
264
+				'Modifiers': MDModifierReader,
265
+			};
266
+			var activeReaders = [];
267
+			function populateReaderCheckboxes() {
268
+				const container = document.getElementById('readercontainer');
269
+				var header = document.createElement('div');
270
+				header.classList.add('reader-header');
271
+				header.append(document.createTextNode('Block Readers'));
272
+				container.append(header);
273
+				const blockContainer = document.createElement('div');
274
+				container.append(blockContainer);
275
+				for (const name of Object.keys(blockReaderClasses).sort()) {
276
+					const readerClass = blockReaderClasses[name];
277
+					activeReaders.push(new readerClass());
278
+					const checkbox = makeReaderCheckbox(name, readerClass);
279
+					blockContainer.append(checkbox);
280
+				}
281
+				header = document.createElement('div');
282
+				header.classList.add('reader-header');
283
+				header.append(document.createTextNode('Inline Readers'));
284
+				container.append(header);
285
+				const inlineContainer = document.createElement('div');
286
+				container.append(inlineContainer);
287
+				for (const name of Object.keys(inlineReaderClasses).sort()) {
288
+					const readerClass = inlineReaderClasses[name];
289
+					activeReaders.push(new readerClass());
290
+					const checkbox = makeReaderCheckbox(name, readerClass);
291
+					inlineContainer.append(checkbox);
292
+				}
293
+			}
294
+			function makeReaderCheckbox(name, readerClass) {
295
+				const check = document.createElement('input');
296
+				check.type = 'checkbox';
297
+				check.checked = true;
298
+				check.addEventListener('change', () => {
299
+					handleCheckChanged(readerClass, check);
300
+				});
301
+				check.id = `reader-${readerClass.name}`;
302
+				const label = document.createElement('label');
303
+				label.htmlFor = check.id;
304
+				label.append(document.createTextNode(name));
305
+				const div = document.createElement('div');
306
+				div.classList.add('reader-check');
307
+				div.append(check);
308
+				div.append(label);
309
+				return div;
310
+			}
311
+			function handleCheckChanged(readerClass, check) {
312
+				console.info(`${readerClass.name}: ${check.checked}`);
313
+				if (check.checked) {
314
+					activeReaders.push(new readerClass());
315
+				} else {
316
+					activeReaders = activeReaders.filter((reader) => reader.constructor.name !== readerClass.name);
317
+				}
318
+				parser = new Markdown(activeReaders);
319
+				onMarkdownChange();
320
+			}
213
 
321
 
214
 			document.addEventListener('DOMContentLoaded', onDocumentLoad);
322
 			document.addEventListener('DOMContentLoaded', onDocumentLoad);
215
 		</script>
323
 		</script>
217
 
325
 
218
 	<body>
326
 	<body>
219
 		<div class="inputhalf half">
327
 		<div class="inputhalf half">
220
-			<textarea id="markdowninput">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut mollis vel metus ut tempor. Morbi dictum, velit quis venenatis consequat, ante leo dignissim magna, quis efficitur ex magna nec ex. In egestas aliquet tortor, vitae posuere dui lobortis ac. Pellentesque quis hendrerit lorem. In maximus ut sapien vel tristique. Pellentesque ut tincidunt orci. Phasellus dignissim massa sit amet tellus placerat finibus. Ut elementum tortor ex, non aliquam libero semper eu.
221
-
222
-Duis gravida convallis nisi ullamcorper tempor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc bibendum varius arcu non molestie. Ut cursus, lorem nec pellentesque interdum, augue risus pellentesque ligula, at vehicula turpis mi vitae nisi. Vivamus pulvinar eget lorem non lacinia. Pellentesque pretium ex at metus elementum semper. Donec tincidunt metus ac ex aliquam, ac iaculis purus facilisis. Phasellus iaculis scelerisque nisl, eu molestie dui sollicitudin sed. Suspendisse ut felis porttitor, sollicitudin urna in, venenatis neque. Donec nec mi ultrices, molestie quam quis, iaculis libero.
223
-
224
-Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut congue consequat vestibulum. Etiam luctus, leo placerat ullamcorper venenatis, nunc metus fringilla sapien, id elementum tellus nulla eu nunc. Integer pulvinar egestas scelerisque. Sed accumsan libero eget pretium sodales. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla pretium egestas elit id cursus. Morbi euismod elementum eros ut eleifend. Praesent velit sem, faucibus sed sapien sit amet, pellentesque fermentum nibh. Nulla consequat turpis nec lacus consequat, sed congue magna consectetur. Nam id urna rhoncus, consectetur eros ac, tempor nisl.
328
+			<div class="toolbar">
329
+				<details>
330
+					<summary>Readers</summary>
225
 
331
 
226
-Curabitur fermentum nunc sed dapibus feugiat. Etiam volutpat viverra eros sit amet tristique. Suspendisse cursus venenatis accumsan. Suspendisse venenatis vehicula lectus, sed venenatis augue pretium eu. Cras dui eros, iaculis in dictum hendrerit, tincidunt ut sapien. Ut nec nulla ut mauris cursus tincidunt. Suspendisse interdum pulvinar elit sit amet auctor. Donec id lacus tincidunt, varius elit non, tempor felis.
332
+					<div id="readercontainer"></div>
333
+				</details>
334
+			</div>
335
+			<textarea id="markdowninput">## Block Formats {#top}
227
 
336
 
228
-Quisque posuere, libero ac elementum maximus, lacus nulla lobortis nibh, id aliquam tortor lacus vel quam. Nunc eget dolor non nunc sagittis varius. Proin viverra vestibulum placerat. Fusce placerat interdum diam quis sollicitudin. Praesent sed tincidunt orci. Suspendisse lectus lorem, porta vel quam sit amet, pharetra interdum purus. Suspendisse ac lacus consequat, dapibus nunc in, porttitor purus. Nam dictum elit eget massa tincidunt tempus. Vestibulum blandit, lorem et accumsan tristique, orci dolor vulputate magna, et posuere lorem sapien non sapien. Vivamus nulla justo, eleifend et metus sit amet, gravida iaculis erat. Fusce lacinia cursus accumsan. 
337
+				* Unordered
338
+				* Lists
339
+				  * Sub list
340
+				
341
+				1. Ordered
342
+				2. Lists
343
+				  6. Sub list
344
+				
345
+				&gt; A block quote lorem ipsum dolor sit amet
346
+				
347
+				A regular paragraph
348
+				
349
+				word
350
+				: a unit of meaning in a sentence
351
+				sentence
352
+				: a collection of words
353
+				
354
+				```javascript
355
+				function foo() {
356
+					return 'Fenced code block';
357
+				}
358
+				```
359
+				
360
+					function bar() {
361
+						return 'Indented code';
362
+					}
363
+				
364
+				### Heading, hash style
365
+				
366
+				Heading, underline style
367
+				---
368
+				
369
+				### Modified Heading {style=color:red;}
370
+				
371
+				-# Sub text
372
+				
373
+				| Unit Price | Qty | Subtotal |
374
+				| ---: | ---: | ---: |
375
+				| $1.23 | 2 | =A*B FILL |
376
+				| $4.99 | 6 | |
377
+				| Total | | =SUM(C:C) |
378
+				
379
+				---
380
+				
381
+				## Inline Formats
382
+				
383
+				Normal, **double asterisk,** __double underscore,__ *asterisks,* _underscores,_ ~~double tildes,~~ ~single tildes,~ ^carets,^ ==double equals==, ``double backticks``, `single backticks`.
384
+				
385
+				Lorem ipsum dolor sit amet,[^1] consectetur adipiscing elit.[^abc] Sed sodales in odio eget porta. Proin et erat sit amet erat semper mollis vitae ut turpis.[^foo] Ut ipsum dui, maximus sit amet suscipit at, dictum id nulla.[^bar] Aenean efficitur rhoncus nulla non fermentum.
386
+				
387
+				[^1]: Donec ut felis volutpat, gravida ipsum scelerisque, accumsan est.
388
+				[^abc]: Cras dictum rutrum quam.
389
+				[^foo]: Donec maximus bibendum lorem.
390
+				[^bar]: Praesent consectetur tristique leo. Morbi nec nisi sit amet quam imperdiet vehicula eu feugiat tortor. 
391
+				
392
+				The HTML on the WWW is often full of JS and CSS.
393
+				
394
+				*[HTML]: Hypertext Markup Language
395
+				*[WWW]: World Wide Web
396
+				*[JS]: JavaScript
397
+				*[CSS]: Cascading Style Sheets
398
+				
399
+				Click [here](#top) to return to the top. Referenced link to [Google][google title="I prefer Duck Duck Go"].
400
+				
401
+				![alt text](https://picsum.photos/100/75) ![alt text](https://picsum.photos/101/75 "With a title") ![][testimage]
402
+				
403
+				[testimage]: https://picsum.photos/102/75
404
+				[google]: https://google.com
405
+				
406
+				Some verbatim &lt;span style="color:green;"&gt;HTML&lt;/span&gt;. A script tag will be ignored. &lt;script&gt;&lt;/script&gt;
229
 			</textarea>
407
 			</textarea>
230
 		</div>
408
 		</div>
231
 		<div class="previewhalf half">
409
 		<div class="previewhalf half">

+ 24
- 0
testjs.html Voir le fichier

1763
 					const result = this.md(markdown);
1763
 					const result = this.md(markdown);
1764
 					this.assertEqual(result, expected);
1764
 					this.assertEqual(result, expected);
1765
 				}
1765
 				}
1766
+
1767
+				test_observedError1() {
1768
+					// Saw "Uncaught Error: columnIndex must be number, got string" from this
1769
+					const markdown = '| Unit Price | Qty | Subtotal |\n| ---: | ---: | ---: |\n| $1.23 | 2 | =A*B FILL |\n| $4.99 | 6 | |\n| Total | | =SUM(C:C) |';
1770
+					const expected = '<table> <thead> <tr> ' +
1771
+						'<th style="text-align: right;">Unit Price</th> ' +
1772
+						'<th style="text-align: right;">Qty</th> ' +
1773
+						'<th>Subtotal</th> ' +
1774
+						'</tr> </thead> <tbody> <tr> ' +
1775
+						'<td class="spreadsheet-type-currency" style="text-align: right;" data-numeric-value="1.23" data-string-value="1.23">$1.23</td> ' +
1776
+						'<td class="spreadsheet-type-number" style="text-align: right;" data-numeric-value="2" data-string-value="2">2</td> ' +
1777
+						'<td class="calculated spreadsheet-type-currency" data-numeric-value="2.46" data-string-value="2.46">$2.46</td> ' +
1778
+						'</tr> <tr> ' +
1779
+						'<td class="spreadsheet-type-currency" style="text-align: right;" data-numeric-value="4.99" data-string-value="4.99">$4.99</td> ' +
1780
+						'<td class="spreadsheet-type-number" style="text-align: right;" data-numeric-value="6" data-string-value="6">6</td> ' +
1781
+						'<td class="calculated spreadsheet-type-currency" data-numeric-value="29.94" data-string-value="29.94">$29.94</td> ' +
1782
+						'</tr> <tr> ' +
1783
+						'<td class="spreadsheet-type-string" style="text-align: right;" data-string-value="Total">Total</td> ' +
1784
+						'<td class="spreadsheet-type-blank" style="text-align: right;" data-numeric-value="0" data-string-value=""></td> ' +
1785
+						'<td class="calculated spreadsheet-type-currency" data-numeric-value="32.4" data-string-value="32.4">$32.40</td> ' +
1786
+						'</tr> </tbody> </table>';
1787
+					const result = this.md(markdown);
1788
+					this.assertEqual(result, expected);
1789
+				}
1766
 			}
1790
 			}
1767
 		</script>
1791
 		</script>
1768
 	</head>
1792
 	</head>

Chargement…
Annuler
Enregistrer