mirror of
https://github.com/TiddlyWiki/TiddlyWiki5.git
synced 2026-04-26 19:24:50 +00:00
* refactor: extract a new $tw.wiki.getParser
* feat: allow $tw.utils.getParseTreeText to render other rules' text
* feat: two example getText handler
* Revert "feat: allow $tw.utils.getParseTreeText to render other rules' text"
This reverts commit 8a12498fa9.
* refactor: keep original getParseTreeText not touched
* refactor: use serialize in rules
* refactor: $tw.utils.extend({},options) -> options || {}
* Update codeinline.js
* Create test-wikitext-serialize.js
* DEBUG: only run my tests for development, remove before PR merge
* lint: if
* feat: add rule: 'parseBlock' metadata
* feat: handle tailing \n that may be missing
* feat: allow recursive
* feat: generate more rule and tests
* feat: generate more rule and tests
* fix: remove pragma:true, otherwise following text will become children of it
* fix: condition manually
Deekseek is silly
* fix: some test
* fix: some test
* feat: $tw.utils.serializeAttribute
* fix: use "" for string param
* feat: list
* refactor: ' -> "
* fix: parsemode don't have node
* fix: render invisible comment and parsemode as data element
* feat: add void: true, in ast node to prevent render
* feat: use void widget, so methods always return a widget
* feat: ast to use new widget type void
* test: add rule: 'parseBlock' and isRuleEnd: true
* lint: quote
* Update widget.js
* fix: void node need to handle its children
* Update test-wikitext-parser.js
* lint: quote
* Update void.js
* Update test-wikitext-parser.js
* fix: macrodef with comment (void node) not working
* lint: ' -> "
* feat: add to styleblock
* feat: styleblock
* feat: styleinline
* Update table.js
* lint: useless comments
* feat: transcludeblock
* refactor: reuse block on inline when possible
* feat: use void node to carry important info for typedblock
* feat: run all tests
* lint: useless ai generated comments
* Update conditional.js to not include space
* Update test-wikitext-serialize.js
* Update conditional.js
* refactor: move tiddlers to /data
* refactor: no need for new $tw.Wiki()
* lint: double quote
* refactor: lowercase the parseblock rule name
* fix: Wiki parser initialize blockRuleClasses only when first new an instance
* feat: restore inline macro def
* fix: macro in widget param
* fix: positional attribute in macro call
* fix: table space and horizrule block new line
* feat: make sure block rule all have \n\n for visiblity
* lint: function param
* fix: empty list item
* feat: add \n\n based on isBlock, if could also be inline
* fix: conditional without elseif
* refactor: use isBlock in macrodef to know inline or block
* fix: link may not have attribute and children
* DEBUG: render result and diff below body only on browser
DEBUG: render result below body only on browser
DEBUG: render result below body
DEBUG: fix build
DEBUG: show render result as ViewTemplate
* fix: remove pad space in />
* test: remove pad space in />
* Revert DEBUG: render result and diff below body only on browser
* refactor: fold commentText variable
* refactor: fold long comment
* fix: double quotes for parameter values
* Update void.js
* refactor: move all exports.serialize = function(tree,serialize) { to plugin
* fix: expost listTypes from core, and require it in plugin
* refactor: move serializeWikitextParseTree to plugin and init it
* refactor: move serializeAttribute util also to the plugin
* fix: Delete unused file
* Update macrodef.js
* Update test-wikitext-parser.js
* lint: fix
* Update plugins/tiddlywiki/wikitext-serialize/rules/filteredtranscludeblock.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update core/modules/widgets/void.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update plugins/tiddlywiki/wikitext-serialize/rules/list.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update plugins/tiddlywiki/wikitext-serialize/rules/list.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update plugins/tiddlywiki/wikitext-serialize/rules/styleblock.js
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Remove unused methods from VoidNodeWidget
Deleted render, execute, and refresh methods from VoidNodeWidget as they are no longer needed. The widget now only inherits from the base Widget class and exports the constructor.
* docs: about regex in styleinline.js
* Update parsetree.js
* Update core/modules/widgets/void.js
Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>
* feat: Ensure at least one space after the style/class
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jeremy Ruston <jeremy@jermolene.com>
194 lines
5.2 KiB
JavaScript
194 lines
5.2 KiB
JavaScript
/*\
|
|
title: $:/core/modules/parsers/wikiparser/rules/html.js
|
|
type: application/javascript
|
|
module-type: wikirule
|
|
|
|
Wiki rule for HTML elements and widgets. For example:
|
|
|
|
{{{
|
|
<aside>
|
|
This is an HTML5 aside element
|
|
</aside>
|
|
|
|
<$slider target="MyTiddler">
|
|
This is a widget invocation
|
|
</$slider>
|
|
|
|
}}}
|
|
|
|
\*/
|
|
|
|
"use strict";
|
|
|
|
exports.name = "html";
|
|
exports.types = {inline: true, block: true};
|
|
|
|
exports.init = function(parser) {
|
|
this.parser = parser;
|
|
};
|
|
|
|
exports.findNextMatch = function(startPos) {
|
|
// Find the next tag
|
|
this.nextTag = this.findNextTag(this.parser.source,startPos,{
|
|
requireLineBreak: this.is.block
|
|
});
|
|
return this.nextTag ? this.nextTag.start : undefined;
|
|
};
|
|
|
|
/*
|
|
Parse the most recent match
|
|
*/
|
|
exports.parse = function() {
|
|
// Retrieve the most recent match so that recursive calls don't overwrite it
|
|
var tag = this.nextTag;
|
|
if(!tag.isSelfClosing) {
|
|
tag.openTagStart = tag.start;
|
|
tag.openTagEnd = tag.end;
|
|
}
|
|
this.nextTag = null;
|
|
// Advance the parser position to past the tag
|
|
this.parser.pos = tag.end;
|
|
// Check for an immediately following double linebreak
|
|
var hasLineBreak = !tag.isSelfClosing && !!$tw.utils.parseTokenRegExp(this.parser.source,this.parser.pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
|
|
// Set whether we're in block mode
|
|
tag.isBlock = this.is.block || hasLineBreak;
|
|
// Parse the body if we need to
|
|
if(!tag.isSelfClosing && $tw.config.htmlVoidElements.indexOf(tag.tag) === -1) {
|
|
var reEndString = "</" + $tw.utils.escapeRegExp(tag.tag) + ">";
|
|
if(hasLineBreak) {
|
|
tag.children = this.parser.parseBlocks(reEndString);
|
|
} else {
|
|
var reEnd = new RegExp("(" + reEndString + ")","mg");
|
|
tag.children = this.parser.parseInlineRun(reEnd,{eatTerminator: true});
|
|
}
|
|
tag.end = this.parser.pos;
|
|
tag.closeTagEnd = tag.end;
|
|
if(tag.closeTagEnd === tag.openTagEnd || this.parser.source[tag.closeTagEnd - 1] !== ">") {
|
|
tag.closeTagStart = tag.end;
|
|
} else {
|
|
tag.closeTagStart = tag.closeTagEnd - 2;
|
|
var closeTagMinPos = tag.children.length > 0 ? tag.children[tag.children.length-1].end : tag.openTagEnd;
|
|
if(!Number.isSafeInteger(closeTagMinPos)) closeTagMinPos = tag.openTagEnd;
|
|
while(tag.closeTagStart >= closeTagMinPos) {
|
|
var char = this.parser.source[tag.closeTagStart];
|
|
if(char === ">") {
|
|
tag.closeTagStart = -1;
|
|
break;
|
|
}
|
|
if(char === "<") break;
|
|
tag.closeTagStart -= 1;
|
|
}
|
|
if(tag.closeTagStart < closeTagMinPos) {
|
|
tag.closeTagStart = tag.end;
|
|
}
|
|
}
|
|
}
|
|
// Return the tag
|
|
return [tag];
|
|
};
|
|
|
|
/*
|
|
Look for an HTML tag. Returns null if not found, otherwise returns {type: "element", name:, attributes: {}, orderedAttributes: [], isSelfClosing:, start:, end:,}
|
|
*/
|
|
exports.parseTag = function(source,pos,options) {
|
|
options = options || {};
|
|
var token,
|
|
node = {
|
|
type: "element",
|
|
start: pos,
|
|
attributes: {},
|
|
orderedAttributes: []
|
|
};
|
|
// Define our regexps
|
|
const reTagName = /([a-zA-Z0-9\-\$\.]+)/y;
|
|
// Skip whitespace
|
|
pos = $tw.utils.skipWhiteSpace(source,pos);
|
|
// Look for a less than sign
|
|
token = $tw.utils.parseTokenString(source,pos,"<");
|
|
if(!token) {
|
|
return null;
|
|
}
|
|
pos = token.end;
|
|
// Get the tag name
|
|
token = $tw.utils.parseTokenRegExp(source,pos,reTagName);
|
|
if(!token) {
|
|
return null;
|
|
}
|
|
node.tag = token.match[1];
|
|
if(node.tag.charAt(0) === "$") {
|
|
node.type = node.tag.substr(1);
|
|
}
|
|
pos = token.end;
|
|
// Check that the tag is terminated by a space, / or >
|
|
if(!$tw.utils.parseWhiteSpace(source,pos) && !(source.charAt(pos) === "/") && !(source.charAt(pos) === ">") ) {
|
|
return null;
|
|
}
|
|
// Process attributes
|
|
var attribute = $tw.utils.parseAttribute(source,pos);
|
|
while(attribute) {
|
|
node.orderedAttributes.push(attribute);
|
|
node.attributes[attribute.name] = attribute;
|
|
pos = attribute.end;
|
|
// Get the next attribute
|
|
attribute = $tw.utils.parseAttribute(source,pos);
|
|
}
|
|
// Skip whitespace
|
|
pos = $tw.utils.skipWhiteSpace(source,pos);
|
|
// Look for a closing slash
|
|
token = $tw.utils.parseTokenString(source,pos,"/");
|
|
if(token) {
|
|
pos = token.end;
|
|
node.isSelfClosing = true;
|
|
}
|
|
// Look for a greater than sign
|
|
token = $tw.utils.parseTokenString(source,pos,">");
|
|
if(!token) {
|
|
return null;
|
|
}
|
|
pos = token.end;
|
|
// Check for a required line break
|
|
if(options.requireLineBreak) {
|
|
token = $tw.utils.parseTokenRegExp(source,pos,/([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/y);
|
|
if(!token) {
|
|
return null;
|
|
}
|
|
}
|
|
// Update the end position
|
|
node.end = pos;
|
|
return node;
|
|
};
|
|
|
|
exports.findNextTag = function(source,pos,options) {
|
|
// A regexp for finding candidate HTML tags
|
|
var reLookahead = /<([a-zA-Z\-\$\.]+)/g;
|
|
// Find the next candidate
|
|
reLookahead.lastIndex = pos;
|
|
var match = reLookahead.exec(source);
|
|
while(match) {
|
|
// Try to parse the candidate as a tag
|
|
var tag = this.parseTag(source,match.index,options);
|
|
// Return success
|
|
if(tag && this.isLegalTag(tag)) {
|
|
return tag;
|
|
}
|
|
// Look for the next match
|
|
reLookahead.lastIndex = match.index + 1;
|
|
match = reLookahead.exec(source);
|
|
}
|
|
// Failed
|
|
return null;
|
|
};
|
|
|
|
exports.isLegalTag = function(tag) {
|
|
// Widgets are always OK
|
|
if(tag.type !== "element") {
|
|
return true;
|
|
// If it's an HTML tag that starts with a dash then it's not legal
|
|
} else if(tag.tag.charAt(0) === "-") {
|
|
return false;
|
|
} else {
|
|
// Otherwise it's OK
|
|
return true;
|
|
}
|
|
};
|