Просмотр исходного кода

CSS modifiers work for inline tags now

main
Rocketsoup 1 год назад
Родитель
Сommit
859a8172df
1 измененных файлов: 69 добавлений и 11 удалений
  1. 69
    11
      js/markdown.js

+ 69
- 11
js/markdown.js Просмотреть файл

50
 	static SimpleLink = new _MDTokenType('SimpleLink'); // content=URL
50
 	static SimpleLink = new _MDTokenType('SimpleLink'); // content=URL
51
 	static SimpleEmail = new _MDTokenType('SimpleEmail'); // content=email address
51
 	static SimpleEmail = new _MDTokenType('SimpleEmail'); // content=email address
52
 	static Footnote = new _MDTokenType('Footnote'); // content=symbol
52
 	static Footnote = new _MDTokenType('Footnote'); // content=symbol
53
-	static Class = new _MDTokenType('Class'); // content
53
+	static Modifier = new _MDTokenType('Modifier'); // content
54
 
54
 
55
 	static HTMLTag = new _MDTokenType('HTMLTag'); // content=tag string, tag=_MDHTMLTag
55
 	static HTMLTag = new _MDTokenType('HTMLTag'); // content=tag string, tag=_MDHTMLTag
56
 
56
 
82
 	extra;
82
 	extra;
83
 	/** @var {_MDHTMLTag|null} */
83
 	/** @var {_MDHTMLTag|null} */
84
 	tag;
84
 	tag;
85
+	/** @var {_MDTagModifier|null} */
86
+	modifier;
85
 
87
 
86
 	/**
88
 	/**
87
 	 * @param {String} original
89
 	 * @param {String} original
88
 	 * @param {_MDTokenType} type
90
 	 * @param {_MDTokenType} type
89
-	 * @param {String|null} content
91
+	 * @param {String|_MDTagModifier|null} content
90
 	 * @param {String|null} extra
92
 	 * @param {String|null} extra
91
 	 * @param {_MDHTMLTag|null} tag
93
 	 * @param {_MDHTMLTag|null} tag
92
 	 */
94
 	 */
93
 	constructor(original, type, content=null, extra=null, tag=null) {
95
 	constructor(original, type, content=null, extra=null, tag=null) {
94
 		this.original = original;
96
 		this.original = original;
95
 		this.type = type;
97
 		this.type = type;
96
-		this.content = content;
98
+		if (content instanceof _MDTagModifier) {
99
+			this.content = null;
100
+			this.modifier = content;
101
+		} else {
102
+			this.content = content;
103
+			this.modifier = null;
104
+		}
97
 		this.extra = extra;
105
 		this.extra = extra;
98
 		this.tag = tag;
106
 		this.tag = tag;
99
 	}
107
 	}
925
 	/** @var {Object} */
933
 	/** @var {Object} */
926
 	attributes = {};
934
 	attributes = {};
927
 
935
 
928
-	static #baseClassRegex = /\.([a-z_\-][a-z0-9_\-]*)/i;
929
-	static #baseIdRegex = /#([a-z_\-][a-z0-9_\-]*)/i;
930
-	static #baseAttributeRegex = /([a-z0-9]+)=([^\s\}]+)/i;
931
-	static #modifierRegex = new RegExp('(?:' + this.#baseClassRegex.source + '|' + this.#baseIdRegex.source + '|' + this.#baseAttributeRegex.source + ')', 'i');
932
-	// static #baseRegex = new RegExp('\\{(' + this.#modifierRegex.source + '(?:\\s+' + this.#modifierRegex.source + '))\\}', 'i'); //  /\{((?:||)(?:\s+(?:\.[a-z_\-][a-z0-9_\-]*|#[a-z_\-][a-z0-9_\-]*|[a-z0-9]+=\S+)))\}/i; // 1=content
933
-	static #baseRegex = /\{([^}]+)}/i;
936
+	static #baseClassRegex = /\.([a-z_\-][a-z0-9_\-]*?)/i;
937
+	static #baseIdRegex = /#([a-z_\-][a-z0-9_\-]*?)/i;
938
+	static #baseAttributeRegex = /([a-z0-9]+?)=([^\s\}]+?)/i;
939
+	static #baseRegex = /\{([^}]+?)}/i;
934
 	static #leadingClassRegex = new RegExp('^' + this.#baseRegex.source, 'i');
940
 	static #leadingClassRegex = new RegExp('^' + this.#baseRegex.source, 'i');
935
 	static #trailingClassRegex = new RegExp('^(.*?)\\s*' + this.#baseRegex.source + '\\s*$', 'i');
941
 	static #trailingClassRegex = new RegExp('^(.*?)\\s*' + this.#baseRegex.source + '\\s*$', 'i');
936
 	static #classRegex = new RegExp('^' + this.#baseClassRegex.source + '$', 'i');  // 1=classname
942
 	static #classRegex = new RegExp('^' + this.#baseClassRegex.source + '$', 'i');  // 1=classname
984
 	}
990
 	}
985
 
991
 
986
 	/**
992
 	/**
993
+	 * Extracts modifier from head of string.
994
+	 * @param {String} line
995
+	 * @returns {_MDTagModifier}
996
+	 */
997
+	static fromStart(line) {
998
+		let groups = this.#leadingClassRegex.exec(line);
999
+		if (groups === null) return null;
1000
+		return this.#fromContents(groups[1]);
1001
+	}
1002
+
1003
+	/**
987
 	 * @param {String} line
1004
 	 * @param {String} line
988
 	 * @returns {String}
1005
 	 * @returns {String}
989
 	 */
1006
 	 */
1243
 	static #footnoteWithTitleRegex = /^\[\^(\d+?)\s+"(.*?)"\]/;  // 1=symbol, 2=title
1260
 	static #footnoteWithTitleRegex = /^\[\^(\d+?)\s+"(.*?)"\]/;  // 1=symbol, 2=title
1244
 	static #footnoteRegex = /^\[\^(\d+?)\]/;  // 1=symbol
1261
 	static #footnoteRegex = /^\[\^(\d+?)\]/;  // 1=symbol
1245
 	// Note: label contents have to have matching pairs of [] and (). Handles images inside links.
1262
 	// Note: label contents have to have matching pairs of [] and (). Handles images inside links.
1246
-	static #labelRegex = /^\[((?:[^\[\]]*\[[^\[\]]*\][^\[\]]*|[^\(\)]*\([^\(\)]*?\)[^\(\)]*|[^\[\]\(\)]*?)*)\]/;  // 1=content
1263
+	static #labelRegex = /^\[((?:[^\[\]]*?\[[^\[\]]*?\][^\[\]]*?|[^\(\)]*?\([^\(\)]*?\)[^\(\)]*?|[^\[\]\(\)]*?)*?)\]/;  // 1=content
1247
 	static #urlWithTitleRegex = /^\((\S+?)\s+"(.*?)"\)/i;  // 1=URL, 2=title
1264
 	static #urlWithTitleRegex = /^\((\S+?)\s+"(.*?)"\)/i;  // 1=URL, 2=title
1248
 	static #urlRegex = /^\((\S+?)\)/i;  // 1=URL
1265
 	static #urlRegex = /^\((\S+?)\)/i;  // 1=URL
1249
 	static #emailWithTitleRegex = new RegExp("^\\(\\s*(" + this.#baseEmailRegex.source + ")\\s+\"(.*?)\"\\s*\\)", "i");  // 1=email, 2=title
1266
 	static #emailWithTitleRegex = new RegExp("^\\(\\s*(" + this.#baseEmailRegex.source + ")\\s+\"(.*?)\"\\s*\\)", "i");  // 1=email, 2=title
1253
 
1270
 
1254
 	/**
1271
 	/**
1255
 	 * @param {String} line
1272
 	 * @param {String} line
1273
+	 * @returns {String[]}
1274
+	 */
1275
+	static #matchLabel(line) {
1276
+		if (!line.startsWith('[')) return null;
1277
+		var parenCount = 0;
1278
+		var bracketCount = 0;
1279
+		for (var p = 1; p < line.length; p++) {
1280
+			let ch = line.substring(p, p + 1);
1281
+			if (ch == '(') {
1282
+				parenCount++;
1283
+			} else if (ch == ')') {
1284
+				parenCount--;
1285
+				if (parenCount < 0) return null;
1286
+			} else if (ch == '[') {
1287
+				bracketCount++;
1288
+			} else if (ch == ']') {
1289
+				if (bracketCount > 0) {
1290
+					bracketCount--;
1291
+				} else {
1292
+					return [ line.substring(0, p + 1), line.substring(1, p) ];
1293
+				}
1294
+			}
1295
+		}
1296
+		return null;
1297
+	}
1298
+
1299
+	/**
1300
+	 * @param {String} line
1256
 	 * @returns {_MDToken[]} tokens
1301
 	 * @returns {_MDToken[]} tokens
1257
 	 */
1302
 	 */
1258
 	static #tokenize(line) {
1303
 	static #tokenize(line) {
1261
 		var expectLiteral = false;
1306
 		var expectLiteral = false;
1262
 		var groups = null;
1307
 		var groups = null;
1263
 		var tag = null;
1308
 		var tag = null;
1309
+		var modifier = null;
1264
 		const endText = function() {
1310
 		const endText = function() {
1265
 			if (text.length == 0) return;
1311
 			if (text.length == 0) return;
1266
 			let textGroups = Markdown.#textWhitespaceRegex.exec(text);
1312
 			let textGroups = Markdown.#textWhitespaceRegex.exec(text);
1315
 				endText();
1361
 				endText();
1316
 				tokens.push(new _MDToken(groups[0], _MDTokenType.Footnote, groups[1]));
1362
 				tokens.push(new _MDToken(groups[0], _MDTokenType.Footnote, groups[1]));
1317
 				p += groups[0].length - 1;
1363
 				p += groups[0].length - 1;
1318
-			} else if (groups = this.#labelRegex.exec(remainder)) {
1364
+			} else if (groups = this.#matchLabel(remainder)) {
1319
 				// Label/ref for link/image   [Foo]
1365
 				// Label/ref for link/image   [Foo]
1320
 				endText();
1366
 				endText();
1321
 				tokens.push(new _MDToken(groups[0], _MDTokenType.Label, groups[1]));
1367
 				tokens.push(new _MDToken(groups[0], _MDTokenType.Label, groups[1]));
1354
 				endText();
1400
 				endText();
1355
 				tokens.push(new _MDToken(tag.fullTag, _MDTokenType.HTMLTag, tag.fullTag, null, tag));
1401
 				tokens.push(new _MDToken(tag.fullTag, _MDTokenType.HTMLTag, tag.fullTag, null, tag));
1356
 				p += tag.fullTag.length - 1;
1402
 				p += tag.fullTag.length - 1;
1403
+			} else if (modifier = _MDTagModifier.fromStart(remainder)) {
1404
+				endText();
1405
+				tokens.push(new _MDToken(modifier.original, _MDTokenType.Modifier, modifier));
1406
+				p += modifier.original.length - 1;
1357
 			} else {
1407
 			} else {
1358
 				text += ch;
1408
 				text += ch;
1359
 			}
1409
 			}
1551
 				}
1601
 				}
1552
 			} while (anyChanges);
1602
 			} while (anyChanges);
1553
 		}
1603
 		}
1604
+		var lastSpan = null;
1554
 		spans = spans.map(function(span) {
1605
 		spans = spans.map(function(span) {
1555
 			if (span instanceof _MDToken) {
1606
 			if (span instanceof _MDToken) {
1607
+				if (span.type == _MDTokenType.Modifier && lastSpan) {
1608
+					span.modifier.applyTo(lastSpan);
1609
+					lastSpan = null;
1610
+					return new _MDTextSpan('');
1611
+				}
1612
+				lastSpan = null;
1556
 				return new _MDTextSpan(span.original);
1613
 				return new _MDTextSpan(span.original);
1557
 			} else if (span instanceof _MDSpan) {
1614
 			} else if (span instanceof _MDSpan) {
1615
+				lastSpan = (span instanceof _MDTextSpan) ? null : span;
1558
 				return span;
1616
 				return span;
1559
 			} else {
1617
 			} else {
1560
 				throw new Error(`Unexpected span type ${span.constructor.name}`);
1618
 				throw new Error(`Unexpected span type ${span.constructor.name}`);

Загрузка…
Отмена
Сохранить