Selaa lähdekoodia

Referenced URLs working. Clickable images working.

main
Rocketsoup 1 vuosi sitten
vanhempi
commit
a0d86c5e32
1 muutettua tiedostoa jossa 99 lisäystä ja 22 poistoa
  1. 99
    22
      js/markdown.js

+ 99
- 22
js/markdown.js Näytä tiedosto

@@ -1,5 +1,3 @@
1
-// TODO: Linked image not parsed correctly.  [![](image.jpg)](link.html)
2
-// TODO: Referenced URL definitions
3 1
 // TODO: HTML tags probably need better handling. Consider whether interior of matched tags should be interpreted as markdown.
4 2
 // TODO: {.class #cssid lang=fr}
5 3
 //     # Header {.class}
@@ -213,24 +211,30 @@ class _MDLinkSpan extends _MDSpan {
213 211
 	target = null;
214 212
 	/** @var {_MDSpan} */
215 213
 	content;
214
+	/** @var {String|null} */
215
+	title = null;
216 216
 
217 217
 	/**
218 218
 	 * @param {String} link
219 219
 	 * @param {_MDSpan} content
220 220
 	 */
221
-	constructor(link, content) {
221
+	constructor(link, content, title=null) {
222 222
 		super();
223 223
 		this.link = link;
224 224
 		this.content = content;
225
+		this.title = title;
225 226
 	}
226 227
 
227 228
 	toHTML(state) {
228 229
 		let escapedLink = this.link.replace('"', '"');
229 230
 		var html = `<a href="${escapedLink}"`;
230
-		if (target) {
231
+		if (this.target) {
231 232
 			let escapedTarget = this.target.replace('"', '&quot;');
232 233
 			html += ` target="${escapedTarget}"`;
233 234
 		}
235
+		if (this.title) {
236
+			html += ` title="${this.title.replace('"', '&quot;')}"`;
237
+		}
234 238
 		html += this.htmlAttributes();
235 239
 		html += '>' + this.content.toHTML(state) + '</a>';
236 240
 		return html;
@@ -246,7 +250,16 @@ class _MDReferencedLinkSpan extends _MDLinkSpan {
246 250
 		this.id = id;
247 251
 	}
248 252
 
253
+	/**
254
+	 * @param {_MDState} state
255
+	 */
249 256
 	toHTML(state) {
257
+		if (!this.link) {
258
+			let url = state.urls[this.id.toLowerCase()];
259
+			let title = state.urlTitles[this.id.toLowerCase()];
260
+			this.link = url;
261
+			this.title = title || this.title;
262
+		}
250 263
 		if (this.link) {
251 264
 			return super.toHTML(state);
252 265
 		} else {
@@ -376,6 +389,12 @@ class _MDReferencedImageSpan extends _MDImageSpan {
376 389
 	}
377 390
 
378 391
 	toHTML(state) {
392
+		if (!this.source) {
393
+			let url = state.urls[this.id.toLowerCase()];
394
+			let title = state.urlTitles[this.id.toLowerCase()];
395
+			this.source = url;
396
+			this.title = title || this.title;
397
+		}
379 398
 		if (this.source) {
380 399
 			return super.toHTML(state);
381 400
 		} else {
@@ -791,6 +810,12 @@ class _MDState {
791 810
 	/** @var {Object} */
792 811
 	#footnotes = {};
793 812
 
813
+	/** @var {Object} */
814
+	#urlDefinitions = {};
815
+
816
+	/** @var {Object} */
817
+	#urlTitles = {};
818
+
794 819
 	/** @var {number} */
795 820
 	p = 0;
796 821
 
@@ -812,6 +837,16 @@ class _MDState {
812 837
 		return (this.#parent) ? this.#parent.footnotes : this.#footnotes;
813 838
 	}
814 839
 
840
+	/** @var {Object} */
841
+	get urls() {
842
+		return (this.#parent) ? this.#parent.urls : this.#urlDefinitions;
843
+	}
844
+
845
+	/** @var {Object} */
846
+	get urlTitles() {
847
+		return (this.#parent) ? this.#parent.urlTitles : this.#urlTitles;
848
+	}
849
+
815 850
 	/**
816 851
 	 * @param {String[]} lines
817 852
 	 */
@@ -849,6 +884,17 @@ class _MDState {
849 884
 		}
850 885
 	}
851 886
 
887
+	defineURL(symbol, url, title=null) {
888
+		if (this.#parent) {
889
+			this.#parent.defineURL(symbol, url, title);
890
+		} else {
891
+			this.#urlDefinitions[symbol.toLowerCase()] = url;
892
+			if (title !== null) {
893
+				this.#urlTitles[symbol.toLowerCase()] = title;
894
+			}
895
+		}
896
+	}
897
+
852 898
 	hasLines(minCount, p=-1) {
853 899
 		let relativeTo = (p < 0) ? this.p : p;
854 900
 		return relativeTo + minCount <= this.lines.length;
@@ -923,6 +969,7 @@ class Markdown {
923 969
 		block = this.#readTable(state); if (block) return block;
924 970
 		block = this.#readFootnoteDef(state); if (block) return block;
925 971
 		block = this.#readAbbreviationDef(state); if (block) return block;
972
+		block = this.#readURLDef(state); if (block) return block;
926 973
 		block = this.#readDefinitionList(state); if (block) return block;
927 974
 		block = this.#readParagraph(state); if (block) return block;
928 975
 		return null;
@@ -1102,7 +1149,8 @@ class Markdown {
1102 1149
 
1103 1150
 	static #footnoteWithTitleRegex = /^\[\^(\d+?)\s+"(.*?)"\]/;  // 1=symbol, 2=title
1104 1151
 	static #footnoteRegex = /^\[\^(\d+?)\]/;  // 1=symbol
1105
-	static #labelRegex = /^\[(.*?)\]/;  // 1=content
1152
+	// Note: label contents have to have matching pairs of [] and (). Handles images inside links.
1153
+	static #labelRegex = /^\[((?:[^\[\]]*\[[^\[\]]*\][^\[\]]*|[^\(\)]*\([^\(\)]*?\)[^\(\)]*|[^\[\]\(\)]*?)*)\]/;  // 1=content
1106 1154
 	static #urlWithTitleRegex = /^\((\S+?)\s+"(.*?)"\)/i;  // 1=URL, 2=title
1107 1155
 	static #urlRegex = /^\((\S+?)\)/i;  // 1=URL
1108 1156
 	static #emailWithTitleRegex = new RegExp("^\\(\\s*(" + this.#baseEmailRegex.source + ")\\s+\"(.*?)\"\\s*\\)", "i");  // 1=email, 2=title
@@ -1255,14 +1303,14 @@ class Markdown {
1255 1303
 	 */
1256 1304
 	static #readInline(state, line) {
1257 1305
 		var tokens = this.#tokenize(line);
1258
-		return new _MDInlineBlock(this.#tokensToSpans(tokens));
1306
+		return new _MDInlineBlock(this.#tokensToSpans(tokens, state));
1259 1307
 	}
1260 1308
 
1261 1309
 	/**
1262 1310
 	 * @param {Array} tokens
1263 1311
 	 * @returns {_MDSpan[]} spans
1264 1312
 	 */
1265
-	static #tokensToSpans(tokens) {
1313
+	static #tokensToSpans(tokens, state) {
1266 1314
 		var spans = tokens.slice(0, tokens.length);
1267 1315
 		var anyChanges = false;
1268 1316
 		var index, index0;
@@ -1275,9 +1323,10 @@ class Markdown {
1275 1323
 				_MDTokenType.Label,
1276 1324
 				_MDTokenType.URL,
1277 1325
 			])) !== null) {
1278
-				let alt = spans[index + 1];
1279
-				let url = spans[index + 2];
1280
-				spans.splice(index, 3, new _MDImageSpan(url.content, alt.content, url.extra));
1326
+				let alt = spans[index + 1].content;
1327
+				let url = spans[index + 2].content;
1328
+				let title = spans[index + 2].extra;
1329
+				spans.splice(index, 3, new _MDImageSpan(url, alt, title));
1281 1330
 				anyChanges = true;
1282 1331
 			}
1283 1332
 			
@@ -1287,9 +1336,9 @@ class Markdown {
1287 1336
 				_MDTokenType.Label,
1288 1337
 				_MDTokenType.Label,
1289 1338
 			])) !== null) {
1290
-				let alt = spans[index + 1];
1291
-				let ref = spans[index + 2];
1292
-				spans.splice(index, 3, new _MDReferencedImageSpan(ref.content, alt.content));
1339
+				let alt = spans[index + 1].content;
1340
+				let ref = spans[index + 2].content;
1341
+				spans.splice(index, 3, new _MDReferencedImageSpan(ref, alt));
1293 1342
 				anyChanges = true;
1294 1343
 			}
1295 1344
 			
@@ -1298,9 +1347,9 @@ class Markdown {
1298 1347
 				_MDTokenType.Label,
1299 1348
 				_MDTokenType.URL,
1300 1349
 			])) !== null) {
1301
-				let text = spans[index + 0];
1302
-				let url = spans[index + 1];
1303
-				spans.splice(index, 2, new _MDLinkSpan(url.content, this.#readInline(state, text.content)));
1350
+				let text = spans[index + 0].content;
1351
+				let url = spans[index + 1].content;
1352
+				spans.splice(index, 2, new _MDLinkSpan(url, this.#readInline(state, text)));
1304 1353
 				anyChanges = true;
1305 1354
 			}
1306 1355
 
@@ -1309,8 +1358,8 @@ class Markdown {
1309 1358
 				_MDTokenType.Label,
1310 1359
 				_MDTokenType.Label,
1311 1360
 			])) !== null) {
1312
-				let text = spans[index + 0];
1313
-				let ref = spans[index + 1];
1361
+				let text = spans[index + 0].content;
1362
+				let ref = spans[index + 1].content;
1314 1363
 				spans.splice(index, 2, new _MDReferencedLinkSpan(ref, this.#readInline(state, text)));
1315 1364
 				anyChanges = true;
1316 1365
 			}
@@ -1319,8 +1368,8 @@ class Markdown {
1319 1368
 			else if ((index = this.#firstTokenIndex(spans, [
1320 1369
 				_MDTokenType.Footnote,
1321 1370
 			])) !== null) {
1322
-				let symbol = spans[index];
1323
-				spans.splice(index, 1, new _MDFootnoteReferenceSpan(symbol.content));
1371
+				let symbol = spans[index].content;
1372
+				spans.splice(index, 1, new _MDFootnoteReferenceSpan(symbol));
1324 1373
 				anyChanges = true;
1325 1374
 			}
1326 1375
 		} while (anyChanges);
@@ -1349,7 +1398,7 @@ class Markdown {
1349 1398
 					}
1350 1399
 					if (hasNewStart) continue;
1351 1400
 				}
1352
-				let contentSpans = Markdown.#tokensToSpans(contentTokens);
1401
+				let contentSpans = Markdown.#tokensToSpans(contentTokens, state);
1353 1402
 				return {
1354 1403
 					startIndex: startIndex,
1355 1404
 					toDelete: endIndex - startIndex + delimiter.length + 1,
@@ -1812,6 +1861,35 @@ class Markdown {
1812 1861
 	 * @param {_MDState} state
1813 1862
 	 * @returns {_MDBlock|null}
1814 1863
 	 */
1864
+	static #readURLDef(state) {
1865
+		var p = state.p;
1866
+		let line = state.lines[p++];
1867
+		var symbol;
1868
+		var url;
1869
+		var title = null;
1870
+		let groups = /^\s*\[(.+?)]:\s*(\S+)\s+"(.*?)"\s*$/.exec(line);
1871
+		if (groups) {
1872
+			symbol = groups[1];
1873
+			url = groups[2];
1874
+			title = groups[3];
1875
+		} else {
1876
+			groups = /^\s*\[(.+?)]:\s*(\S+)\s*$/.exec(line);
1877
+			if (groups) {
1878
+				symbol = groups[1];
1879
+				url = groups[2];
1880
+			} else {
1881
+				return null;
1882
+			}
1883
+		}
1884
+		state.defineURL(symbol, url, title);
1885
+		state.p = p;
1886
+		return new _MDInlineBlock([]);
1887
+	}
1888
+
1889
+	/**
1890
+	 * @param {_MDState} state
1891
+	 * @returns {_MDBlock|null}
1892
+	 */
1815 1893
 	static #readParagraph(state) {
1816 1894
 		var paragraphLines = [];
1817 1895
 		var p = state.p;
@@ -1866,7 +1944,6 @@ class Markdown {
1866 1944
 		}
1867 1945
 		html += '</ol>';
1868 1946
 		html += '</div>';
1869
-		// <!--FNREF:{symbol}-->
1870 1947
 		return html;
1871 1948
 	}
1872 1949
 

Loading…
Peruuta
Tallenna