Explorar el Código

Referenced URLs working. Clickable images working.

main
Rocketsoup hace 1 año
padre
commit
a0d86c5e32
Se han modificado 1 ficheros con 99 adiciones y 22 borrados
  1. 99
    22
      js/markdown.js

+ 99
- 22
js/markdown.js Ver fichero

1
-// TODO: Linked image not parsed correctly.  [![](image.jpg)](link.html)
2
-// TODO: Referenced URL definitions
3
 // TODO: HTML tags probably need better handling. Consider whether interior of matched tags should be interpreted as markdown.
1
 // TODO: HTML tags probably need better handling. Consider whether interior of matched tags should be interpreted as markdown.
4
 // TODO: {.class #cssid lang=fr}
2
 // TODO: {.class #cssid lang=fr}
5
 //     # Header {.class}
3
 //     # Header {.class}
213
 	target = null;
211
 	target = null;
214
 	/** @var {_MDSpan} */
212
 	/** @var {_MDSpan} */
215
 	content;
213
 	content;
214
+	/** @var {String|null} */
215
+	title = null;
216
 
216
 
217
 	/**
217
 	/**
218
 	 * @param {String} link
218
 	 * @param {String} link
219
 	 * @param {_MDSpan} content
219
 	 * @param {_MDSpan} content
220
 	 */
220
 	 */
221
-	constructor(link, content) {
221
+	constructor(link, content, title=null) {
222
 		super();
222
 		super();
223
 		this.link = link;
223
 		this.link = link;
224
 		this.content = content;
224
 		this.content = content;
225
+		this.title = title;
225
 	}
226
 	}
226
 
227
 
227
 	toHTML(state) {
228
 	toHTML(state) {
228
 		let escapedLink = this.link.replace('"', '"');
229
 		let escapedLink = this.link.replace('"', '"');
229
 		var html = `<a href="${escapedLink}"`;
230
 		var html = `<a href="${escapedLink}"`;
230
-		if (target) {
231
+		if (this.target) {
231
 			let escapedTarget = this.target.replace('"', '&quot;');
232
 			let escapedTarget = this.target.replace('"', '&quot;');
232
 			html += ` target="${escapedTarget}"`;
233
 			html += ` target="${escapedTarget}"`;
233
 		}
234
 		}
235
+		if (this.title) {
236
+			html += ` title="${this.title.replace('"', '&quot;')}"`;
237
+		}
234
 		html += this.htmlAttributes();
238
 		html += this.htmlAttributes();
235
 		html += '>' + this.content.toHTML(state) + '</a>';
239
 		html += '>' + this.content.toHTML(state) + '</a>';
236
 		return html;
240
 		return html;
246
 		this.id = id;
250
 		this.id = id;
247
 	}
251
 	}
248
 
252
 
253
+	/**
254
+	 * @param {_MDState} state
255
+	 */
249
 	toHTML(state) {
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
 		if (this.link) {
263
 		if (this.link) {
251
 			return super.toHTML(state);
264
 			return super.toHTML(state);
252
 		} else {
265
 		} else {
376
 	}
389
 	}
377
 
390
 
378
 	toHTML(state) {
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
 		if (this.source) {
398
 		if (this.source) {
380
 			return super.toHTML(state);
399
 			return super.toHTML(state);
381
 		} else {
400
 		} else {
791
 	/** @var {Object} */
810
 	/** @var {Object} */
792
 	#footnotes = {};
811
 	#footnotes = {};
793
 
812
 
813
+	/** @var {Object} */
814
+	#urlDefinitions = {};
815
+
816
+	/** @var {Object} */
817
+	#urlTitles = {};
818
+
794
 	/** @var {number} */
819
 	/** @var {number} */
795
 	p = 0;
820
 	p = 0;
796
 
821
 
812
 		return (this.#parent) ? this.#parent.footnotes : this.#footnotes;
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
 	 * @param {String[]} lines
851
 	 * @param {String[]} lines
817
 	 */
852
 	 */
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
 	hasLines(minCount, p=-1) {
898
 	hasLines(minCount, p=-1) {
853
 		let relativeTo = (p < 0) ? this.p : p;
899
 		let relativeTo = (p < 0) ? this.p : p;
854
 		return relativeTo + minCount <= this.lines.length;
900
 		return relativeTo + minCount <= this.lines.length;
923
 		block = this.#readTable(state); if (block) return block;
969
 		block = this.#readTable(state); if (block) return block;
924
 		block = this.#readFootnoteDef(state); if (block) return block;
970
 		block = this.#readFootnoteDef(state); if (block) return block;
925
 		block = this.#readAbbreviationDef(state); if (block) return block;
971
 		block = this.#readAbbreviationDef(state); if (block) return block;
972
+		block = this.#readURLDef(state); if (block) return block;
926
 		block = this.#readDefinitionList(state); if (block) return block;
973
 		block = this.#readDefinitionList(state); if (block) return block;
927
 		block = this.#readParagraph(state); if (block) return block;
974
 		block = this.#readParagraph(state); if (block) return block;
928
 		return null;
975
 		return null;
1102
 
1149
 
1103
 	static #footnoteWithTitleRegex = /^\[\^(\d+?)\s+"(.*?)"\]/;  // 1=symbol, 2=title
1150
 	static #footnoteWithTitleRegex = /^\[\^(\d+?)\s+"(.*?)"\]/;  // 1=symbol, 2=title
1104
 	static #footnoteRegex = /^\[\^(\d+?)\]/;  // 1=symbol
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
 	static #urlWithTitleRegex = /^\((\S+?)\s+"(.*?)"\)/i;  // 1=URL, 2=title
1154
 	static #urlWithTitleRegex = /^\((\S+?)\s+"(.*?)"\)/i;  // 1=URL, 2=title
1107
 	static #urlRegex = /^\((\S+?)\)/i;  // 1=URL
1155
 	static #urlRegex = /^\((\S+?)\)/i;  // 1=URL
1108
 	static #emailWithTitleRegex = new RegExp("^\\(\\s*(" + this.#baseEmailRegex.source + ")\\s+\"(.*?)\"\\s*\\)", "i");  // 1=email, 2=title
1156
 	static #emailWithTitleRegex = new RegExp("^\\(\\s*(" + this.#baseEmailRegex.source + ")\\s+\"(.*?)\"\\s*\\)", "i");  // 1=email, 2=title
1255
 	 */
1303
 	 */
1256
 	static #readInline(state, line) {
1304
 	static #readInline(state, line) {
1257
 		var tokens = this.#tokenize(line);
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
 	 * @param {Array} tokens
1310
 	 * @param {Array} tokens
1263
 	 * @returns {_MDSpan[]} spans
1311
 	 * @returns {_MDSpan[]} spans
1264
 	 */
1312
 	 */
1265
-	static #tokensToSpans(tokens) {
1313
+	static #tokensToSpans(tokens, state) {
1266
 		var spans = tokens.slice(0, tokens.length);
1314
 		var spans = tokens.slice(0, tokens.length);
1267
 		var anyChanges = false;
1315
 		var anyChanges = false;
1268
 		var index, index0;
1316
 		var index, index0;
1275
 				_MDTokenType.Label,
1323
 				_MDTokenType.Label,
1276
 				_MDTokenType.URL,
1324
 				_MDTokenType.URL,
1277
 			])) !== null) {
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
 				anyChanges = true;
1330
 				anyChanges = true;
1282
 			}
1331
 			}
1283
 			
1332
 			
1287
 				_MDTokenType.Label,
1336
 				_MDTokenType.Label,
1288
 				_MDTokenType.Label,
1337
 				_MDTokenType.Label,
1289
 			])) !== null) {
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
 				anyChanges = true;
1342
 				anyChanges = true;
1294
 			}
1343
 			}
1295
 			
1344
 			
1298
 				_MDTokenType.Label,
1347
 				_MDTokenType.Label,
1299
 				_MDTokenType.URL,
1348
 				_MDTokenType.URL,
1300
 			])) !== null) {
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
 				anyChanges = true;
1353
 				anyChanges = true;
1305
 			}
1354
 			}
1306
 
1355
 
1309
 				_MDTokenType.Label,
1358
 				_MDTokenType.Label,
1310
 				_MDTokenType.Label,
1359
 				_MDTokenType.Label,
1311
 			])) !== null) {
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
 				spans.splice(index, 2, new _MDReferencedLinkSpan(ref, this.#readInline(state, text)));
1363
 				spans.splice(index, 2, new _MDReferencedLinkSpan(ref, this.#readInline(state, text)));
1315
 				anyChanges = true;
1364
 				anyChanges = true;
1316
 			}
1365
 			}
1319
 			else if ((index = this.#firstTokenIndex(spans, [
1368
 			else if ((index = this.#firstTokenIndex(spans, [
1320
 				_MDTokenType.Footnote,
1369
 				_MDTokenType.Footnote,
1321
 			])) !== null) {
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
 				anyChanges = true;
1373
 				anyChanges = true;
1325
 			}
1374
 			}
1326
 		} while (anyChanges);
1375
 		} while (anyChanges);
1349
 					}
1398
 					}
1350
 					if (hasNewStart) continue;
1399
 					if (hasNewStart) continue;
1351
 				}
1400
 				}
1352
-				let contentSpans = Markdown.#tokensToSpans(contentTokens);
1401
+				let contentSpans = Markdown.#tokensToSpans(contentTokens, state);
1353
 				return {
1402
 				return {
1354
 					startIndex: startIndex,
1403
 					startIndex: startIndex,
1355
 					toDelete: endIndex - startIndex + delimiter.length + 1,
1404
 					toDelete: endIndex - startIndex + delimiter.length + 1,
1812
 	 * @param {_MDState} state
1861
 	 * @param {_MDState} state
1813
 	 * @returns {_MDBlock|null}
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
 	static #readParagraph(state) {
1893
 	static #readParagraph(state) {
1816
 		var paragraphLines = [];
1894
 		var paragraphLines = [];
1817
 		var p = state.p;
1895
 		var p = state.p;
1866
 		}
1944
 		}
1867
 		html += '</ol>';
1945
 		html += '</ol>';
1868
 		html += '</div>';
1946
 		html += '</div>';
1869
-		// <!--FNREF:{symbol}-->
1870
 		return html;
1947
 		return html;
1871
 	}
1948
 	}
1872
 
1949
 

Loading…
Cancelar
Guardar