mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-04-14 23:03:16 +00:00
Merge da497c3b3f850001a27dda73030fd235754d7f1e into 961e74f73d230d0028efb586db07699120eac888
This commit is contained in:
commit
14b472d4e4
49
core/modules/parsers/wikiparser/rules/blockmark.js
Normal file
49
core/modules/parsers/wikiparser/rules/blockmark.js
Normal file
@ -0,0 +1,49 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/blockmark.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Use hash as a tag for paragraph, we call it block mark.
|
||||
|
||||
1. Hash won't change, it can be written by hand or be generated, and it is a ` \^\S+$` string after line: `text ^cb9d485` or `text ^1`, so it can be human readable (while without space), here are the parse rule for this.
|
||||
2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an mark to jump to.
|
||||
|
||||
\*/
|
||||
exports.name = "blockmark";
|
||||
exports.types = {inline: true};
|
||||
|
||||
/*
|
||||
Instantiate parse rule
|
||||
*/
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match the block mark.
|
||||
// 1. inlineId: located on the end of the line, with a space before it, means it's the id of the current block.
|
||||
// 2. blockId: located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock.
|
||||
this.matchRegExp = /[ ]\^(\S+)$|^\^(\S+)$/mg;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// will be one of following case, another will be undefined
|
||||
var inlineId = this.match[1];
|
||||
var blockId = this.match[2];
|
||||
var id = inlineId || blockId || "";
|
||||
var blockMarkStart = this.parser.pos;
|
||||
var blockMarkEnd = blockMarkStart + id.length;
|
||||
// Parse tree nodes to return
|
||||
return [{
|
||||
type: "blockmark",
|
||||
attributes: {
|
||||
id: {type: "string", value: id, start: blockMarkStart, end: blockMarkEnd},
|
||||
// `yes` means the block that this block mark pointing to, is before this node, both block mark and the block itself, is in a same parent node's children list.
|
||||
// empty means the block is this node's direct parent node.
|
||||
previousSibling: {type: "string", value: Boolean(blockId) ? "yes" : ""},
|
||||
},
|
||||
children: []
|
||||
}];
|
||||
};
|
@ -20,8 +20,8 @@ exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg;
|
||||
// Regexp to match `[[Alias|Title^blockId]]`, the `Alias|` and `^blockId` are optional.
|
||||
this.matchRegExp = /\[\[(.*?)(?:\|(.*?)?)?(?:\^([^|\s^]+)?)?\]\]/mg;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
@ -31,6 +31,7 @@ exports.parse = function() {
|
||||
// Process the link
|
||||
var text = this.match[1],
|
||||
link = this.match[2] || text,
|
||||
blockMark = this.match[3] || "",
|
||||
textEndPos = this.parser.source.indexOf("|", start);
|
||||
if (textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) {
|
||||
textEndPos = this.matchRegExp.lastIndex - 2;
|
||||
@ -38,6 +39,10 @@ exports.parse = function() {
|
||||
var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start;
|
||||
var linkEnd = linkStart + link.length;
|
||||
if($tw.utils.isLinkExternal(link)) {
|
||||
// add back the part after `^` to the ext link, if it happen to has one. Here is is not an block mark, but a part of the external URL.
|
||||
if(blockMark) {
|
||||
link = link + "^" + blockMark;
|
||||
}
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "a",
|
||||
@ -52,10 +57,13 @@ exports.parse = function() {
|
||||
}]
|
||||
}];
|
||||
} else {
|
||||
var blockMarkStart = blockMark ? (linkEnd + 1) : linkEnd;
|
||||
var blockMarkEnd = blockMarkStart + blockMark.length;
|
||||
return [{
|
||||
type: "link",
|
||||
attributes: {
|
||||
to: {type: "string", value: link, start: linkStart, end: linkEnd}
|
||||
to: {type: "string", value: link, start: linkStart, end: linkEnd},
|
||||
toBlockMark: {type: "string", value: blockMark, start: blockMarkStart, end: blockMarkEnd},
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: text, start: start, end: textEndPos
|
||||
|
@ -87,12 +87,12 @@ Story.prototype.saveStoryList = function(storyList) {
|
||||
));
|
||||
};
|
||||
|
||||
Story.prototype.addToHistory = function(navigateTo,navigateFromClientRect) {
|
||||
Story.prototype.addToHistory = function(navigateTo,fromPageRect,blockMark) {
|
||||
var titles = $tw.utils.isArray(navigateTo) ? navigateTo : [navigateTo];
|
||||
// Add a new record to the top of the history stack
|
||||
var historyList = this.wiki.getTiddlerData(this.historyTitle,[]);
|
||||
$tw.utils.each(titles,function(title) {
|
||||
historyList.push({title: title, fromPageRect: navigateFromClientRect});
|
||||
historyList.push({title: title, fromPageRect: fromPageRect, blockMark: blockMark});
|
||||
});
|
||||
this.wiki.setTiddlerData(this.historyTitle,historyList,{"current-tiddler": titles[titles.length-1]});
|
||||
};
|
||||
|
@ -23,12 +23,23 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) {
|
||||
}
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// If block mark is provided, find the block element that this mark is pointing to
|
||||
var foundBlockMark = false;
|
||||
if(listItemWidget && historyInfo.blockMark) {
|
||||
var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) {
|
||||
return widget.blockMarkId === historyInfo.blockMark;
|
||||
});
|
||||
if(blockMarkWidget) {
|
||||
targetElement = blockMarkWidget.findBlockMarkTargetDomNode()
|
||||
foundBlockMark = true;
|
||||
}
|
||||
}
|
||||
// Abandon if the list entry isn't a DOM element (it might be a text node)
|
||||
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
|
||||
return;
|
||||
}
|
||||
// Scroll the node into view
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement});
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundBlockMark});
|
||||
};
|
||||
|
||||
ClassicStoryView.prototype.insert = function(widget) {
|
||||
|
@ -20,12 +20,23 @@ PopStoryView.prototype.navigateTo = function(historyInfo) {
|
||||
}
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// If block mark is provided, find the block element that this mark is pointing to
|
||||
var foundBlockMark = false;
|
||||
if(listItemWidget && historyInfo.blockMark) {
|
||||
var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) {
|
||||
return widget.blockMarkId === historyInfo.blockMark;
|
||||
});
|
||||
if(blockMarkWidget) {
|
||||
targetElement = blockMarkWidget.findBlockMarkTargetDomNode()
|
||||
foundBlockMark = true;
|
||||
}
|
||||
}
|
||||
// Abandon if the list entry isn't a DOM element (it might be a text node)
|
||||
if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) {
|
||||
return;
|
||||
}
|
||||
// Scroll the node into view
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement});
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundBlockMark});
|
||||
};
|
||||
|
||||
PopStoryView.prototype.insert = function(widget) {
|
||||
|
@ -48,6 +48,17 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
}
|
||||
var listItemWidget = this.listWidget.children[listElementIndex],
|
||||
targetElement = listItemWidget.findFirstDomNode();
|
||||
// If block mark is provided, find the block element that this mark is pointing to
|
||||
var foundBlockMark = false;
|
||||
if(listItemWidget && historyInfo.blockMark) {
|
||||
var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) {
|
||||
return widget.blockMarkId === historyInfo.blockMark;
|
||||
});
|
||||
if(blockMarkWidget) {
|
||||
targetElement = blockMarkWidget.findBlockMarkTargetDomNode()
|
||||
foundBlockMark = true;
|
||||
}
|
||||
}
|
||||
// Abandon if the list entry isn't a DOM element (it might be a text node)
|
||||
if(!targetElement) {
|
||||
return;
|
||||
@ -116,7 +127,7 @@ ZoominListView.prototype.navigateTo = function(historyInfo) {
|
||||
},duration);
|
||||
}
|
||||
// Scroll the target into view
|
||||
// $tw.pageScroller.scrollIntoView(targetElement);
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: true});
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -54,7 +54,8 @@ exports.hasClass = function(el,className) {
|
||||
};
|
||||
|
||||
exports.addClass = function(el,className) {
|
||||
var c = (el.getAttribute("class") || "").split(" ");
|
||||
var classAttr = el.getAttribute("class") || "";
|
||||
var c = classAttr.trim() ? classAttr.split(" ") : [];
|
||||
if(c.indexOf(className) === -1) {
|
||||
c.push(className);
|
||||
el.setAttribute("class",c.join(" "));
|
||||
|
@ -46,7 +46,9 @@ Handle an event
|
||||
*/
|
||||
PageScroller.prototype.handleEvent = function(event) {
|
||||
if(event.type === "tm-scroll") {
|
||||
var options = {};
|
||||
var options = {
|
||||
highlight: event.highlight,
|
||||
};
|
||||
if($tw.utils.hop(event.paramObject,"animationDuration")) {
|
||||
options.animationDuration = event.paramObject.animationDuration;
|
||||
}
|
||||
@ -62,14 +64,21 @@ PageScroller.prototype.handleEvent = function(event) {
|
||||
|
||||
/*
|
||||
Handle a scroll event hitting the page document
|
||||
|
||||
options:
|
||||
- animationDuration: total time of scroll animation
|
||||
- highlight: highlight the element after scrolling, to make it evident. Usually to focus an anchor in the middle of the tiddler.
|
||||
*/
|
||||
PageScroller.prototype.scrollIntoView = function(element,callback,options) {
|
||||
var self = this,
|
||||
duration = $tw.utils.hop(options,"animationDuration") ? parseInt(options.animationDuration) : $tw.utils.getAnimationDuration(),
|
||||
highlight = options.highlight || false,
|
||||
srcWindow = element ? element.ownerDocument.defaultView : window;
|
||||
// Now get ready to scroll the body
|
||||
this.cancelScroll(srcWindow);
|
||||
this.startTime = Date.now();
|
||||
// toggle class to allow trigger the highlight animation
|
||||
$tw.utils.removeClass(element,"tc-focus-highlight");
|
||||
// Get the height of any position:fixed toolbars
|
||||
var toolbar = srcWindow.document.querySelector(".tc-adjust-top-of-scroll"),
|
||||
offset = 0;
|
||||
@ -118,6 +127,12 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) {
|
||||
srcWindow.scrollTo(scrollPosition.x + (endX - scrollPosition.x) * t,scrollPosition.y + (endY - scrollPosition.y) * t);
|
||||
if(t < 1) {
|
||||
self.idRequestFrame = self.requestAnimationFrame.call(srcWindow,drawFrame);
|
||||
} else {
|
||||
// the animation is end.
|
||||
if(highlight) {
|
||||
element.focus({ focusVisible: true });
|
||||
$tw.utils.addClass(element,"tc-focus-highlight");
|
||||
}
|
||||
}
|
||||
};
|
||||
drawFrame();
|
||||
|
@ -100,6 +100,21 @@ exports.findParseTreeNode = function(nodeArray,search) {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
exports.findChildNodeInTree = function(root,searchFn) {
|
||||
if(searchFn(root)) {
|
||||
return root;
|
||||
}
|
||||
if(root.children && root.children.length > 0) {
|
||||
for(var i=0; i<root.children.length; i++) {
|
||||
var result = exports.findChildNodeInTree(root.children[i], searchFn);
|
||||
if(result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Helper to get the text of a parse tree node or array of nodes
|
||||
*/
|
||||
|
81
core/modules/widgets/blockmark.js
Normal file
81
core/modules/widgets/blockmark.js
Normal file
@ -0,0 +1,81 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/block-mark.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
An invisible element with block mark id metadata. Marking a block level element, so that it can be jumped to or transcluded.
|
||||
\*/
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
var BlockMarkWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
BlockMarkWidget.prototype = new Widget();
|
||||
|
||||
BlockMarkWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
this.computeAttributes();
|
||||
// Execute our logic
|
||||
this.execute();
|
||||
// Create an invisible DOM element with data that can be accessed from JS or CSS
|
||||
this.idNode = this.document.createElement("span");
|
||||
this.idNode.setAttribute("data-block-mark-id",this.blockMarkId);
|
||||
if(this.tiddlerTitle) {
|
||||
this.idNode.setAttribute("data-block-mark-title",this.tiddlerTitle);
|
||||
// id for anchor jumping in static site
|
||||
this.idNode.setAttribute("id",this.tiddlerTitle + "-" + this.blockMarkId);
|
||||
} else {
|
||||
this.idNode.setAttribute("id",this.blockMarkId);
|
||||
}
|
||||
// if the actual block is before this node, we need to add a flag to the node
|
||||
if(this.previousSibling) {
|
||||
this.idNode.setAttribute("data-block-mark-previous-sibling","true");
|
||||
}
|
||||
this.idNode.className = "tc-block-mark";
|
||||
parent.insertBefore(this.idNode,nextSibling);
|
||||
this.domNodes.push(this.idNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
BlockMarkWidget.prototype.execute = function() {
|
||||
// Get the id from the parse tree node or manually assigned attributes
|
||||
this.blockMarkId = this.getAttribute("id");
|
||||
this.tiddlerTitle = this.getVariable("currentTiddler", "");
|
||||
this.previousSibling = this.getAttribute("previousSibling") === "yes";
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Find the DOM node pointed by this block mark
|
||||
*/
|
||||
Widget.prototype.findBlockMarkTargetDomNode = function() {
|
||||
if(!this.idNode) {
|
||||
return null;
|
||||
}
|
||||
// the actual block is always at the parent level
|
||||
targetElement = this.idNode.parentNode;
|
||||
// need to check if the block is before this node
|
||||
if(this.previousSibling) {
|
||||
targetElement = targetElement.previousSibling;
|
||||
}
|
||||
return targetElement;
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
BlockMarkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(($tw.utils.count(changedAttributes) > 0)) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports.blockmark = BlockMarkWidget;
|
@ -89,6 +89,7 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
||||
}
|
||||
// Set an href
|
||||
var wikilinkTransformFilter = this.getVariable("tv-filter-export-link"),
|
||||
to = this.toBlockMark ? this.to + "-" + this.toBlockMark : this.to,
|
||||
wikiLinkText;
|
||||
if(wikilinkTransformFilter) {
|
||||
// Use the filter to construct the href
|
||||
@ -99,8 +100,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
||||
// Expand the tv-wikilink-template variable to construct the href
|
||||
var wikiLinkTemplateMacro = this.getVariable("tv-wikilink-template"),
|
||||
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$";
|
||||
wikiLinkText = $tw.utils.replaceString(wikiLinkTemplate,"$uri_encoded$",$tw.utils.encodeURIComponentExtended(this.to));
|
||||
wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$uri_doubleencoded$",$tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(this.to)));
|
||||
wikiLinkText = $tw.utils.replaceString(wikiLinkTemplate,"$uri_encoded$",$tw.utils.encodeURIComponentExtended(to));
|
||||
wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$uri_doubleencoded$",$tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(to)));
|
||||
}
|
||||
// Override with the value of tv-get-export-link if defined
|
||||
wikiLinkText = this.getVariable("tv-get-export-link",{params: [{name: "to",value: this.to}],defaultValue: wikiLinkText});
|
||||
@ -157,6 +158,7 @@ LinkWidget.prototype.handleClickEvent = function(event) {
|
||||
this.dispatchEvent({
|
||||
type: "tm-navigate",
|
||||
navigateTo: this.to,
|
||||
toBlockMark: this.toBlockMark,
|
||||
navigateFromTitle: this.getVariable("storyTiddler"),
|
||||
navigateFromNode: this,
|
||||
navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
|
||||
@ -187,6 +189,7 @@ Compute the internal state of the widget
|
||||
LinkWidget.prototype.execute = function() {
|
||||
// Pick up our attributes
|
||||
this.to = this.getAttribute("to",this.getVariable("currentTiddler"));
|
||||
this.toBlockMark = this.getAttribute("toBlockMark");
|
||||
this.tooltip = this.getAttribute("tooltip");
|
||||
this["aria-label"] = this.getAttribute("aria-label");
|
||||
this.linkClasses = this.getAttribute("class");
|
||||
|
@ -135,9 +135,10 @@ NavigatorWidget.prototype.addToStory = function(title,fromTitle) {
|
||||
Add a new record to the top of the history stack
|
||||
title: a title string or an array of title strings
|
||||
fromPageRect: page coordinates of the origin of the navigation
|
||||
anchor:optional anchor id in this tiddler
|
||||
*/
|
||||
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect) {
|
||||
this.story.addToHistory(title,fromPageRect,this.historyTitle);
|
||||
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect,anchor) {
|
||||
this.story.addToHistory(title,fromPageRect,anchor);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -148,7 +149,7 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) {
|
||||
if(event.navigateTo) {
|
||||
this.addToStory(event.navigateTo,event.navigateFromTitle);
|
||||
if(!event.navigateSuppressNavigation) {
|
||||
this.addToHistory(event.navigateTo,event.navigateFromClientRect);
|
||||
this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toBlockMark);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -4,7 +4,7 @@ tags: $:/tags/EditTemplate
|
||||
\whitespace trim
|
||||
<$edit-text field="draft.title" class="tc-titlebar tc-edit-texteditor" focus={{{ [{$:/config/AutoFocus}match[title]then[true]] ~[[false]] }}} tabindex={{$:/config/EditTabIndex}} cancelPopups="yes"/>
|
||||
|
||||
<$vars pattern="""[\|\[\]{}]""" bad-chars="""`| [ ] { }`""">
|
||||
<$vars pattern="""[\^\|\[\]{}]""" bad-chars="""`| ^ [ ] { }`""">
|
||||
|
||||
<$list filter="[all[current]regexp:draft.title<pattern>]" variable="listItem">
|
||||
|
||||
|
8
editions/dev/tiddlers/mechanism/HistoryMechanism.tid
Normal file
8
editions/dev/tiddlers/mechanism/HistoryMechanism.tid
Normal file
@ -0,0 +1,8 @@
|
||||
created: 20230922141042399
|
||||
modified: 20230922141423022
|
||||
tags:
|
||||
title: HistoryMechanism
|
||||
|
||||
The story view is created by [[$:/core/ui/PageTemplate/story]] core page template, which uses list widget to render tiddlers. In this way, page template will reflect to history's change.
|
||||
|
||||
List widget has a `history="$:/HistoryList"` parameter, that will be used in list widget's `handleHistoryChanges` method, and pass to the `this.storyview.navigateTo`, you can read [[storyview module]] for how storyview use the changed history.
|
@ -9,17 +9,50 @@ test('get started link', async ({ page }) => {
|
||||
// The tests can take a while to run
|
||||
const timeout = 1000 * 30;
|
||||
test.setTimeout(timeout);
|
||||
|
||||
// Load the generated test TW html
|
||||
await page.goto(`file:///${crossPlatformIndexPath}`);
|
||||
|
||||
// Sanity check
|
||||
await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5');
|
||||
|
||||
await expect(page.locator('.tc-site-title'), "Correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5');
|
||||
// Wait for jasmine results bar to appear
|
||||
await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout});
|
||||
|
||||
await expect(page.locator('.jasmine-overall-result'), "Jasmine test results bar presented").toBeVisible({timeout});
|
||||
// Assert the tests have passed
|
||||
await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible();
|
||||
await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible();
|
||||
await Promise.all([
|
||||
expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Jasmine tests not failed").not.toBeVisible(),
|
||||
expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Jasmine tests passed").toBeVisible()
|
||||
]);
|
||||
});
|
||||
|
||||
test('Search tiddler with link and navigate to block mark', async ({ page }) => {
|
||||
const timeout = 1000 * 30;
|
||||
test.setTimeout(timeout);
|
||||
await page.goto(`file:///${crossPlatformIndexPath}`);
|
||||
|
||||
// Engaging the search input
|
||||
// Search in default search box
|
||||
await page.fill('input[type="search"]', 'BlockMark/Links');
|
||||
// click on link in search results inside the dropdown
|
||||
await page.click('div.tc-search-drop-down a:has-text("BlockMark/Links")');
|
||||
// wait for link to appear and check its href
|
||||
const searchResultLink = page.locator('a:has-text("Block Level Links in WikiText")');
|
||||
await expect(searchResultLink, "Search result link presented").toBeVisible({timeout});
|
||||
await expect(searchResultLink, "Search result link have correct href").toHaveAttribute('href', '#BlockMark%2FMarks-BlockLevelLinksID1');
|
||||
// click on this link
|
||||
await page.click('a:has-text("Block Level Links in WikiText")');
|
||||
|
||||
// The tiddler should be opened and operational, allow clicking link to navigate to block mark
|
||||
// wait for tiddler to appear and the block focused, and check its properties
|
||||
const blockMarkedText = page.locator('p:has-text("A block level mark in WikiText.")');
|
||||
await expect(blockMarkedText, "Block marked text presented").toBeVisible({timeout});
|
||||
await expect(blockMarkedText, "A highlight animation").toHaveClass('tc-focus-highlight');
|
||||
// Check the mark span properties
|
||||
const markSpan = page.locator('span[data-block-mark-id="BlockLevelLinksID1"]')
|
||||
await expect(markSpan, "Mark span presented but not visible").not.toBeVisible({timeout}),
|
||||
await Promise.all([
|
||||
expect(markSpan).toHaveAttribute('data-block-mark-title', 'BlockMark/Marks'),
|
||||
expect(markSpan).toHaveClass('tc-block-mark'),
|
||||
expect(markSpan).toHaveText(''),
|
||||
expect(markSpan).toHaveClass('tc-block-mark'),
|
||||
markSpan.evaluate(e => e.id).then(id => expect(id).toBe('BlockMark/Marks-BlockLevelLinksID1'))
|
||||
])
|
||||
});
|
||||
|
3
editions/test/tiddlers/tests/data/block-mark/Links.tid
Normal file
3
editions/test/tiddlers/tests/data/block-mark/Links.tid
Normal file
@ -0,0 +1,3 @@
|
||||
title: BlockMark/Links
|
||||
|
||||
[[Block Level Links in WikiText|BlockMark/Marks^BlockLevelLinksID1]].
|
5
editions/test/tiddlers/tests/data/block-mark/Marks.tid
Normal file
5
editions/test/tiddlers/tests/data/block-mark/Marks.tid
Normal file
@ -0,0 +1,5 @@
|
||||
title: BlockMark/Marks
|
||||
|
||||
First normal paragraph.
|
||||
|
||||
A block level mark in WikiText. ^BlockLevelLinksID1
|
@ -8,7 +8,7 @@ Tests the backlinks mechanism.
|
||||
\*/
|
||||
"use strict";
|
||||
|
||||
describe('Backlinks tests', function() {
|
||||
describe("Backlinks tests", function() {
|
||||
function setupWiki(wikiOptions) {
|
||||
wikiOptions = wikiOptions || {};
|
||||
// Create a wiki
|
||||
@ -16,125 +16,137 @@ describe('Backlinks tests', function() {
|
||||
wiki.addIndexersToWiki();
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestIncoming',
|
||||
text: '',
|
||||
title: "TestIncoming",
|
||||
text: "",
|
||||
});
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestOutgoing',
|
||||
text: 'A link to [[TestIncoming]]',
|
||||
title: "TestOutgoing",
|
||||
text: "A link to [[TestIncoming]]",
|
||||
});
|
||||
return wiki;
|
||||
}
|
||||
|
||||
describe('a tiddler with no links to it', function() {
|
||||
describe("a tiddler with no links to it", function() {
|
||||
var wiki = new $tw.Wiki();
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestIncoming',
|
||||
text: ''});
|
||||
title: "TestIncoming",
|
||||
text: ""});
|
||||
|
||||
it('should have no backlinks', function() {
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||
it("should have no backlinks", function() {
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe('A tiddler added to the wiki with a link to it', function() {
|
||||
describe("A tiddler added to the wiki with a link to it", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
it('should have a backlink', function() {
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
it("should have a backlink", function() {
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
});
|
||||
|
||||
it("should have a backlink to block mark", function() {
|
||||
wiki.addTiddler({
|
||||
title: "TestIncoming",
|
||||
text: "With a mark. ^ToThisBlock",
|
||||
});
|
||||
wiki.addTiddler({
|
||||
title: "TestOutgoing",
|
||||
text: "A link to [[TestIncoming^ToThisBlock]]",
|
||||
});
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
});
|
||||
});
|
||||
|
||||
describe('A tiddler that has a link added to it later', function() {
|
||||
it('should have an additional backlink', function() {
|
||||
describe("A tiddler that has a link added to it later", function() {
|
||||
it("should have an additional backlink", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestOutgoing2',
|
||||
text: 'Nothing yet!'});
|
||||
title: "TestOutgoing2",
|
||||
text: "Nothing yet!"});
|
||||
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestOutgoing2',
|
||||
text: 'Updated with link to [[TestIncoming]]'});
|
||||
title: "TestOutgoing2",
|
||||
text: "Updated with link to [[TestIncoming]]"});
|
||||
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing,TestOutgoing2');
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing,TestOutgoing2");
|
||||
});
|
||||
});
|
||||
|
||||
describe('A tiddler that has a link remove from it later', function() {
|
||||
describe("A tiddler that has a link remove from it later", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
it('should have one fewer backlink', function() {
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
it("should have one fewer backlink", function() {
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestOutgoing',
|
||||
text: 'No link to ~TestIncoming'});
|
||||
title: "TestOutgoing",
|
||||
text: "No link to ~TestIncoming"});
|
||||
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe('A tiddler linking to another that gets renamed', function() {
|
||||
describe("A tiddler linking to another that gets renamed", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
it('should have its name changed in the backlinks', function() {
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
it("should have its name changed in the backlinks", function() {
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
|
||||
wiki.renameTiddler('TestOutgoing', 'TestExtroverted');
|
||||
wiki.renameTiddler("TestOutgoing", "TestExtroverted");
|
||||
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestExtroverted');
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestExtroverted");
|
||||
});
|
||||
});
|
||||
|
||||
describe('A tiddler linking to another that gets deleted', function() {
|
||||
describe("A tiddler linking to another that gets deleted", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
it('should be removed from backlinks', function() {
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
it("should be removed from backlinks", function() {
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
|
||||
wiki.deleteTiddler('TestOutgoing');
|
||||
wiki.deleteTiddler("TestOutgoing");
|
||||
|
||||
expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('');
|
||||
expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Binary tiddlers should not be parsed', function() {
|
||||
describe("Binary tiddlers should not be parsed", function() {
|
||||
var wiki = setupWiki();
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestDoc.doc',
|
||||
text: 'A link to [[TestOutgoing]]',
|
||||
type: 'application/msword'
|
||||
title: "TestDoc.doc",
|
||||
text: "A link to [[TestOutgoing]]",
|
||||
type: "application/msword"
|
||||
});
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestExcel.xls',
|
||||
text: 'A link to [[TestOutgoing]]',
|
||||
type: 'application/excel'
|
||||
title: "TestExcel.xls",
|
||||
text: "A link to [[TestOutgoing]]",
|
||||
type: "application/excel"
|
||||
});
|
||||
|
||||
wiki.addTiddler({
|
||||
title: 'TestOutgoing',
|
||||
text: 'Some links to [[TestDoc.doc]] and [[TestExcel.xls]].'
|
||||
title: "TestOutgoing",
|
||||
text: "Some links to [[TestDoc.doc]] and [[TestExcel.xls]]."
|
||||
});
|
||||
|
||||
it('should ignore office files', function() {
|
||||
expect(wiki.getIndexer("BackIndexer").subIndexers.link._getTarget(wiki.getTiddler('TestExcel.xls'))).toEqual([]);
|
||||
it("should ignore office files", function() {
|
||||
expect(wiki.getIndexer("BackIndexer").subIndexers.link._getTarget(wiki.getTiddler("TestExcel.xls"))).toEqual([]);
|
||||
|
||||
expect(wiki.filterTiddlers('[all[]] +[backlinks[]]').join(',')).toBe('TestOutgoing');
|
||||
expect(wiki.filterTiddlers("[all[]] +[backlinks[]]").join(",")).toBe("TestOutgoing");
|
||||
|
||||
// make it tw5 tiddler
|
||||
wiki.addTiddler({
|
||||
title: 'TestExcel.xls',
|
||||
text: 'A link to [[TestOutgoing]]'
|
||||
title: "TestExcel.xls",
|
||||
text: "A link to [[TestOutgoing]]"
|
||||
});
|
||||
|
||||
expect(wiki.filterTiddlers('[all[]] +[backlinks[]]').join(',')).toBe('TestOutgoing,TestExcel.xls');
|
||||
expect(wiki.filterTiddlers("[all[]] +[backlinks[]]").join(",")).toBe("TestOutgoing,TestExcel.xls");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -23,87 +23,87 @@ describe("WikiText parser tests", function() {
|
||||
it("should parse tags", function() {
|
||||
expect(parse("<br>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 4, children : [ { type : 'element', tag : 'br', start : 0, end : 4, openTagStart: 0, openTagEnd: 4, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ] } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 4, children : [ { type : "element", tag : "br", start : 0, end : 4, openTagStart: 0, openTagEnd: 4, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("</br>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 5, children : [ { type : 'text', text : '</br>', start : 0, end : 5 } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 5, children : [ { type : "text", text : "</br>", start : 0, end : 5 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 5, children : [ { type : 'element', tag : 'div', start : 0, end : 5, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 5, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ] } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 5, children : [ { type : "element", tag : "div", start : 0, end : 5, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 5, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div/>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 6, children : [ { type : 'element', tag : 'div', isSelfClosing : true, isBlock : false, attributes : { }, orderedAttributes: [ ], start : 0, end : 6, rule: 'html' } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 6, children : [ { type : "element", tag : "div", isSelfClosing : true, isBlock : false, attributes : { }, orderedAttributes: [ ], start : 0, end : 6, rule: "html" } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div></div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 11, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ], start : 0, end : 11, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 11, rule: 'html' } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 11, children : [ { type : "element", tag : "div", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ], start : 0, end : 11, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 11, rule: "html" } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div>some text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 20, children : [ { type : 'element', tag : 'div', openTagStart: 0, openTagEnd: 5, closeTagStart: 14, closeTagEnd: 20, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ { type : 'text', text : 'some text', start : 5, end : 14 } ], start : 0, end : 20 } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 20, children : [ { type : "element", tag : "div", openTagStart: 0, openTagEnd: 5, closeTagStart: 14, closeTagEnd: 20, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ { type : "text", text : "some text", start : 5, end : 14 } ], start : 0, end : 20 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute>some text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 30, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { attribute : { type : 'string', value : 'true', start : 4, end : 14, name: 'attribute' } }, orderedAttributes: [ { type : 'string', value : 'true', start : 4, end : 14, name: 'attribute' } ], children : [ { type : 'text', text : 'some text', start : 15, end : 24 } ], start : 0, end : 30, openTagStart: 0, openTagEnd: 15, closeTagStart: 24, closeTagEnd: 30, rule: 'html' } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 30, children : [ { type : "element", tag : "div", isBlock : false, attributes : { attribute : { type : "string", value : "true", start : 4, end : 14, name: "attribute" } }, orderedAttributes: [ { type : "string", value : "true", start : 4, end : 14, name: "attribute" } ], children : [ { type : "text", text : "some text", start : 15, end : 24 } ], start : 0, end : 30, openTagStart: 0, openTagEnd: 15, closeTagStart: 24, closeTagEnd: 30, rule: "html" } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute='value'>some text</div>")).toEqual(
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 38, children : [ { type : 'element', tag : 'div', openTagStart: 0, openTagEnd: 23, closeTagStart: 32, closeTagEnd: 38, rule: 'html', isBlock : false, attributes : { attribute : { type : 'string', name: 'attribute', value : 'value', start: 4, end: 22 } }, orderedAttributes: [ { type: 'string', name: 'attribute', value : 'value', start: 4, end: 22 } ], children : [ { type : 'text', text : 'some text', start : 23, end : 32 } ], start : 0, end : 38 } ] } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 38, children : [ { type : "element", tag : "div", openTagStart: 0, openTagEnd: 23, closeTagStart: 32, closeTagEnd: 38, rule: "html", isBlock : false, attributes : { attribute : { type : "string", name: "attribute", value : "value", start: 4, end: 22 } }, orderedAttributes: [ { type: "string", name: "attribute", value : "value", start: 4, end: 22 } ], children : [ { type : "text", text : "some text", start : 23, end : 32 } ], start : 0, end : 38 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute={{TiddlerTitle}}>some text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 47, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { attribute : { type : 'indirect', name: 'attribute', textReference : 'TiddlerTitle', start : 4, end : 31 } }, orderedAttributes: [ { type : 'indirect', name: 'attribute', textReference : 'TiddlerTitle', start : 4, end : 31 } ], children : [ { type : 'text', text : 'some text', start : 32, end : 41 } ], start : 0, end : 47, openTagStart: 0, openTagEnd: 32, closeTagStart: 41, closeTagEnd: 47, rule: 'html' } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 47, children : [ { type : "element", tag : "div", isBlock : false, attributes : { attribute : { type : "indirect", name: "attribute", textReference : "TiddlerTitle", start : 4, end : 31 } }, orderedAttributes: [ { type : "indirect", name: "attribute", textReference : "TiddlerTitle", start : 4, end : 31 } ], children : [ { type : "text", text : "some text", start : 32, end : 41 } ], start : 0, end : 47, openTagStart: 0, openTagEnd: 32, closeTagStart: 41, closeTagEnd: 47, rule: "html" } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<$reveal state='$:/temp/search' type='nomatch' text=''>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 55, children : [ { type : 'reveal', tag: '$reveal', rule: 'html', attributes : { state : { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, type : { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, text : { start : 46, name : 'text', type : 'string', value : '', end : 54 } }, orderedAttributes: [ { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, { start : 46, name : 'text', type : 'string', value : '', end : 54 } ], start: 0, end : 55, openTagStart: 0, openTagEnd: 55, closeTagStart: 55, closeTagEnd: 55, isBlock : false, children : [ ] } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 55, children : [ { type : "reveal", tag: "$reveal", rule: "html", attributes : { state : { start : 8, name : "state", type : "string", value : "$:/temp/search", end : 31 }, type : { start : 31, name : "type", type : "string", value : "nomatch", end : 46 }, text : { start : 46, name : "text", type : "string", value : "", end : 54 } }, orderedAttributes: [ { start : 8, name : "state", type : "string", value : "$:/temp/search", end : 31 }, { start : 31, name : "type", type : "string", value : "nomatch", end : 46 }, { start : 46, name : "text", type : "string", value : "", end : 54 } ], start: 0, end : 55, openTagStart: 0, openTagEnd: 55, closeTagStart: 55, closeTagEnd: 55, isBlock : false, children : [ ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute={{TiddlerTitle!!field}}>some text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 54, children : [ { type : 'element', tag : 'div', rule: 'html', isBlock : false, attributes : { attribute : { type : 'indirect', name : 'attribute', textReference : 'TiddlerTitle!!field', start : 4, end : 38 } }, orderedAttributes: [ { type : 'indirect', name : 'attribute', textReference : 'TiddlerTitle!!field', start : 4, end : 38 } ], children : [ { type : 'text', text : 'some text', start : 39, end : 48 } ], start : 0, end : 54, openTagStart: 0, openTagEnd: 39, closeTagStart: 48, closeTagEnd: 54 } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 54, children : [ { type : "element", tag : "div", rule: "html", isBlock : false, attributes : { attribute : { type : "indirect", name : "attribute", textReference : "TiddlerTitle!!field", start : 4, end : 38 } }, orderedAttributes: [ { type : "indirect", name : "attribute", textReference : "TiddlerTitle!!field", start : 4, end : 38 } ], children : [ { type : "text", text : "some text", start : 39, end : 48 } ], start : 0, end : 54, openTagStart: 0, openTagEnd: 39, closeTagStart: 48, closeTagEnd: 54 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute={{Tiddler Title!!field}}>some text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 55, children : [ { type : 'element', tag : 'div', rule: 'html', isBlock : false, attributes : { attribute : { type : 'indirect', name : 'attribute', textReference : 'Tiddler Title!!field', start : 4, end : 39 } }, orderedAttributes: [ { type : 'indirect', name : 'attribute', textReference : 'Tiddler Title!!field', start : 4, end : 39 } ], children : [ { type : 'text', text : 'some text', start : 40, end : 49 } ], start : 0, end : 55, openTagStart: 0, openTagEnd: 40, closeTagStart: 49, closeTagEnd: 55 } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 55, children : [ { type : "element", tag : "div", rule: "html", isBlock : false, attributes : { attribute : { type : "indirect", name : "attribute", textReference : "Tiddler Title!!field", start : 4, end : 39 } }, orderedAttributes: [ { type : "indirect", name : "attribute", textReference : "Tiddler Title!!field", start : 4, end : 39 } ], children : [ { type : "text", text : "some text", start : 40, end : 49 } ], start : 0, end : 55, openTagStart: 0, openTagEnd: 40, closeTagStart: 49, closeTagEnd: 55 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div attribute={{TiddlerTitle!!field}}>\n\nsome text</div>")).toEqual(
|
||||
|
||||
[ { type : 'element', start : 0, attributes : { attribute : { start : 4, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 38 } }, orderedAttributes: [ { start : 4, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 38 } ], tag : 'div', rule: 'html', end : 56, openTagStart: 0, openTagEnd: 39, closeTagStart: 50, closeTagEnd: 56, isBlock : true, children : [ { type : 'element', tag : 'p', start : 41, end : 50, children : [ { type : 'text', text : 'some text', start : 41, end : 50 } ] } ] } ]
|
||||
[ { type : "element", start : 0, attributes : { attribute : { start : 4, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 38 } }, orderedAttributes: [ { start : 4, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 38 } ], tag : "div", rule: "html", end : 56, openTagStart: 0, openTagEnd: 39, closeTagStart: 50, closeTagEnd: 56, isBlock : true, children : [ { type : "element", tag : "p", start : 41, end : 50, children : [ { type : "text", text : "some text", start : 41, end : 50 } ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div><div attribute={{TiddlerTitle!!field}}>\n\nsome text</div></div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 67, children : [ { type : 'element', start : 0, end: 67, openTagStart: 0, openTagEnd: 5, closeTagStart: 61, closeTagEnd: 67, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 61, openTagStart: 5, openTagEnd: 44, closeTagStart: 55, closeTagEnd: 61, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'p', start : 46, end : 55, children : [ { type : 'text', text : 'some text', start : 46, end : 55 } ] } ] } ] } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 67, children : [ { type : "element", start : 0, end: 67, openTagStart: 0, openTagEnd: 5, closeTagStart: 61, closeTagEnd: 67, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 61, openTagStart: 5, openTagEnd: 44, closeTagStart: 55, closeTagEnd: 61, rule: "html", isBlock : true, children : [ { type : "element", tag : "p", start : 46, end : 55, children : [ { type : "text", text : "some text", start : 46, end : 55 } ] } ] } ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div><div attribute={{TiddlerTitle!!field}}>\n\n!some heading</div></div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 71, children : [ { type : 'element', start : 0, end: 71, openTagStart: 0, openTagEnd: 5, closeTagStart: 71, closeTagEnd: 71, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 71, openTagStart: 5, openTagEnd: 44, closeTagStart: 71, closeTagEnd: 71, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'h1', start: 46, end: 71, rule: 'heading', attributes : { class : { type : 'string', value : '', start: 47, end: 47 } }, children : [ { type : 'text', text : 'some heading</div></div>', start : 47, end : 71 } ] } ] } ] } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 71, children : [ { type : "element", start : 0, end: 71, openTagStart: 0, openTagEnd: 5, closeTagStart: 71, closeTagEnd: 71, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 71, openTagStart: 5, openTagEnd: 44, closeTagStart: 71, closeTagEnd: 71, rule: "html", isBlock : true, children : [ { type : "element", tag : "h1", start: 46, end: 71, rule: "heading", attributes : { class : { type : "string", value : "", start: 47, end: 47 } }, children : [ { type : "text", text : "some heading</div></div>", start : 47, end : 71 } ] } ] } ] } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<div><div attribute={{TiddlerTitle!!field}}>\n!some heading</div></div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 70, children : [ { type : 'element', start : 0, end: 70, openTagStart: 0, openTagEnd: 5, closeTagStart: 64, closeTagEnd: 70, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 64, openTagStart: 5, openTagEnd: 44, closeTagStart: 58, closeTagEnd: 64, rule: 'html', isBlock : false, children : [ { type : 'text', text : '\n!some heading', start : 44, end : 58 } ] } ] } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 70, children : [ { type : "element", start : 0, end: 70, openTagStart: 0, openTagEnd: 5, closeTagStart: 64, closeTagEnd: 70, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 64, openTagStart: 5, openTagEnd: 44, closeTagStart: 58, closeTagEnd: 64, rule: "html", isBlock : false, children : [ { type : "text", text : "\n!some heading", start : 44, end : 58 } ] } ] } ] } ]
|
||||
|
||||
);
|
||||
// Regression test for issue (#3306)
|
||||
expect(parse("<div><span><span>\n\nSome text</span></span></div>")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start: 0, end: 48, children : [ { type : 'element', start : 0, end: 48, openTagStart: 0, openTagEnd: 5, closeTagStart: 42, closeTagEnd: 48, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { }, orderedAttributes: [ ], tag : 'span', end : 42, openTagStart: 5, openTagEnd: 11, closeTagStart: 35, closeTagEnd: 42, rule: 'html', isBlock : false, children : [ { type : 'element', start : 11, attributes : { }, orderedAttributes: [ ], tag : 'span', end : 35, openTagStart: 11, openTagEnd: 17, closeTagStart: 28, closeTagEnd: 35, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'p', start : 19, end : 28, children : [ { type : 'text', text : 'Some text', start : 19, end : 28 } ] } ] } ] } ] } ] } ]
|
||||
[ { type : "element", tag : "p", start: 0, end: 48, children : [ { type : "element", start : 0, end: 48, openTagStart: 0, openTagEnd: 5, closeTagStart: 42, closeTagEnd: 48, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { }, orderedAttributes: [ ], tag : "span", end : 42, openTagStart: 5, openTagEnd: 11, closeTagStart: 35, closeTagEnd: 42, rule: "html", isBlock : false, children : [ { type : "element", start : 11, attributes : { }, orderedAttributes: [ ], tag : "span", end : 35, openTagStart: 11, openTagEnd: 17, closeTagStart: 28, closeTagEnd: 35, rule: "html", isBlock : true, children : [ { type : "element", tag : "p", start : 19, end : 28, children : [ { type : "text", text : "Some text", start : 19, end : 28 } ] } ] } ] } ] } ] } ]
|
||||
|
||||
);
|
||||
});
|
||||
@ -190,12 +190,12 @@ describe("WikiText parser tests", function() {
|
||||
it("should block mode filtered transclusions", function() {
|
||||
expect(parse("{{{ filter }}}")).toEqual(
|
||||
|
||||
[ { type: 'list', attributes: { filter: { type: 'string', value: ' filter ', start: 3, end: 11 } }, isBlock: true, start: 0, end: 14, rule: "filteredtranscludeblock" } ]
|
||||
[ { type: "list", attributes: { filter: { type: "string", value: " filter ", start: 3, end: 11 } }, isBlock: true, start: 0, end: 14, rule: "filteredtranscludeblock" } ]
|
||||
|
||||
);
|
||||
expect(parse("{{{ fil\nter }}}")).toEqual(
|
||||
|
||||
[ { type: 'list', attributes: { filter: { type: 'string', value: ' fil\nter ', start: 3, end: 12 } }, isBlock: true, start: 0, end: 15, rule: "filteredtranscludeblock" } ]
|
||||
[ { type: "list", attributes: { filter: { type: "string", value: " fil\nter ", start: 3, end: 12 } }, isBlock: true, start: 0, end: 15, rule: "filteredtranscludeblock" } ]
|
||||
|
||||
);
|
||||
});
|
||||
@ -243,7 +243,7 @@ describe("WikiText parser tests", function() {
|
||||
it("should parse block macro calls", function() {
|
||||
expect(parse("<<john>>\n<<paul>>\r\n<<george>>\n<<ringo>>")).toEqual(
|
||||
|
||||
[ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: 'transclude', start: 9, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: 'transclude', start: 19, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: 'transclude', start: 30, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ]
|
||||
[ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: "transclude", start: 9, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: "transclude", start: 19, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: "transclude", start: 30, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ]
|
||||
|
||||
);
|
||||
expect(parse("<<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
|
||||
@ -253,17 +253,17 @@ describe("WikiText parser tests", function() {
|
||||
);
|
||||
expect(parse("<< carrots\n\n<<john>>")).toEqual(
|
||||
|
||||
[ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'transclude', start: 12, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ]
|
||||
[ { type: "element", tag: "p", start : 0, end : 10, children: [ { type: "text", text: "<< carrots", start : 0, end : 10 } ] }, { type: "transclude", start: 12, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ]
|
||||
|
||||
);
|
||||
expect(parse("before\n\n<<john>>")).toEqual(
|
||||
|
||||
[ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'transclude', start: 8, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ]
|
||||
[ { type: "element", tag: "p", start : 0, end : 6, children: [ { type: "text", text: "before", start : 0, end : 6 } ] }, { type: "transclude", start: 8, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ]
|
||||
|
||||
);
|
||||
expect(parse("<<john>>\nafter")).toEqual(
|
||||
|
||||
[ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ]
|
||||
[ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: "element", tag: "p", start: 9, end: 14, children: [ { type: "text", text: "after", start: 9, end: 14 } ] } ]
|
||||
|
||||
);
|
||||
expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual(
|
||||
@ -273,7 +273,7 @@ describe("WikiText parser tests", function() {
|
||||
);
|
||||
expect(parse("<<outie one:'my <<innie>>' >>")).toEqual(
|
||||
|
||||
[ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} ], end: 29, isBlock: true } ]
|
||||
[ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} ], end: 29, isBlock: true } ]
|
||||
|
||||
);
|
||||
});
|
||||
@ -306,7 +306,7 @@ describe("WikiText parser tests", function() {
|
||||
it("should parse horizontal rules", function() {
|
||||
expect(parse("---Not a rule\n\n----\n\nBetween\n\n---")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', start : 0, end : 13, children : [ { type : 'entity', entity : '—', start: 0, end: 3, rule: 'dash' }, { type : 'text', text : 'Not a rule', start : 3, end : 13 } ] }, { type : 'element', tag : 'hr', start: 15, end: 20, rule: 'horizrule' }, { type : 'element', tag : 'p', start : 21, end : 28, children : [ { type : 'text', text : 'Between', start : 21, end : 28 } ] }, { type : 'element', tag : 'hr', start: 30, end: 33, rule: 'horizrule' } ]
|
||||
[ { type : "element", tag : "p", start : 0, end : 13, children : [ { type : "entity", entity : "—", start: 0, end: 3, rule: "dash" }, { type : "text", text : "Not a rule", start : 3, end : 13 } ] }, { type : "element", tag : "hr", start: 15, end: 20, rule: "horizrule" }, { type : "element", tag : "p", start : 21, end : 28, children : [ { type : "text", text : "Between", start : 21, end : 28 } ] }, { type : "element", tag : "hr", start: 30, end: 33, rule: "horizrule" } ]
|
||||
|
||||
);
|
||||
|
||||
@ -315,99 +315,99 @@ describe("WikiText parser tests", function() {
|
||||
it("should parse hard linebreak areas", function() {
|
||||
expect(parse("\"\"\"Something\nin the\nway she moves\n\"\"\"\n\n")).toEqual(
|
||||
|
||||
[ { type : 'element', tag : 'p', children : [ { type : 'text', text : 'Something', start : 3, end : 12, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 12, end: 13 }, { type : 'text', text : 'in the', start : 13, end : 19, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 19, end: 20 }, { type : 'text', text : 'way she moves', start : 20, end : 33, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 33, end: 34 } ], start : 0, end : 37 } ]
|
||||
[ { type : "element", tag : "p", children : [ { type : "text", text : "Something", start : 3, end : 12, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 12, end: 13 }, { type : "text", text : "in the", start : 13, end : 19, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 19, end: 20 }, { type : "text", text : "way she moves", start : 20, end : 33, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 33, end: 34 } ], start : 0, end : 37 } ]
|
||||
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
it("should parse tables", function() {
|
||||
let wikitext = `
|
||||
|!Cell1 |!Cell2 |
|
||||
|Cell3 |Cell4 |`.trim();
|
||||
it("should parse tables", function() {
|
||||
var wikitext =
|
||||
"|!Cell1 |!Cell2 |\n" +
|
||||
"|Cell3 |Cell4 |".trim();
|
||||
|
||||
let expectedParseTree = [{
|
||||
type: 'element',
|
||||
tag: 'table',
|
||||
var expectedParseTree = [{
|
||||
type: "element",
|
||||
tag: "table",
|
||||
start: 0,
|
||||
end: 33,
|
||||
rule: 'table',
|
||||
rule: "table",
|
||||
children: [{
|
||||
type: 'element',
|
||||
tag: 'tbody',
|
||||
type: "element",
|
||||
tag: "tbody",
|
||||
start: 0,
|
||||
end: 33,
|
||||
children: [{
|
||||
type: 'element',
|
||||
tag: 'tr',
|
||||
type: "element",
|
||||
tag: "tr",
|
||||
attributes: {
|
||||
'class': { name: 'class', type: 'string', value: 'evenRow' },
|
||||
"class": { name: "class", type: "string", value: "evenRow" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'class', type: 'string', value: 'evenRow' },
|
||||
{ name: "class", type: "string", value: "evenRow" },
|
||||
],
|
||||
start: 0,
|
||||
end: 18,
|
||||
children: [{
|
||||
type: 'element',
|
||||
tag: 'th',
|
||||
type: "element",
|
||||
tag: "th",
|
||||
attributes: {
|
||||
'align': { name: 'align', type: 'string', value: 'left' },
|
||||
"align": { name: "align", type: "string", value: "left" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'align', type: 'string', value: 'left' },
|
||||
{ name: "align", type: "string", value: "left" },
|
||||
],
|
||||
start: 1,
|
||||
end: 8,
|
||||
children: [{type: 'text', text: 'Cell1', start: 2, end: 7}],
|
||||
children: [{type: "text", text: "Cell1", start: 2, end: 7}],
|
||||
}, {
|
||||
type: 'element',
|
||||
tag: 'th',
|
||||
type: "element",
|
||||
tag: "th",
|
||||
attributes: {
|
||||
'align': { name: 'align', type: 'string', value: 'left' },
|
||||
"align": { name: "align", type: "string", value: "left" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'align', type: 'string', value: 'left' },
|
||||
{ name: "align", type: "string", value: "left" },
|
||||
],
|
||||
start: 9,
|
||||
end: 16,
|
||||
children: [{type: 'text', text: 'Cell2', start: 10, end: 15}],
|
||||
children: [{type: "text", text: "Cell2", start: 10, end: 15}],
|
||||
}],
|
||||
}, {
|
||||
type: 'element',
|
||||
tag: 'tr',
|
||||
type: "element",
|
||||
tag: "tr",
|
||||
attributes: {
|
||||
'class': { name: 'class', type: 'string', value: 'oddRow' },
|
||||
"class": { name: "class", type: "string", value: "oddRow" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'class', type: 'string', value: 'oddRow' },
|
||||
{ name: "class", type: "string", value: "oddRow" },
|
||||
],
|
||||
start: 18,
|
||||
end: 33,
|
||||
children: [{
|
||||
type: 'element',
|
||||
tag: 'td',
|
||||
type: "element",
|
||||
tag: "td",
|
||||
attributes: {
|
||||
'align': { name: 'align', type: 'string', value: 'left' },
|
||||
"align": { name: "align", type: "string", value: "left" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'align', type: 'string', value: 'left' },
|
||||
{ name: "align", type: "string", value: "left" },
|
||||
],
|
||||
start: 19,
|
||||
end: 25,
|
||||
children: [{type: 'text', text: 'Cell3', start: 19, end: 24}],
|
||||
children: [{type: "text", text: "Cell3", start: 19, end: 24}],
|
||||
}, {
|
||||
type: 'element',
|
||||
tag: 'td',
|
||||
type: "element",
|
||||
tag: "td",
|
||||
attributes: {
|
||||
'align': { name: 'align', type: 'string', value: 'left' },
|
||||
"align": { name: "align", type: "string", value: "left" },
|
||||
},
|
||||
orderedAttributes: [
|
||||
{ name: 'align', type: 'string', value: 'left' },
|
||||
{ name: "align", type: "string", value: "left" },
|
||||
],
|
||||
start: 26,
|
||||
end: 32,
|
||||
children: [{type: 'text', text: 'Cell4', start: 26, end: 31}],
|
||||
children: [{type: "text", text: "Cell4", start: 26, end: 31}],
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
@ -415,5 +415,65 @@ describe("WikiText parser tests", function() {
|
||||
|
||||
expect(parse(wikitext)).toEqual(expectedParseTree);
|
||||
});
|
||||
|
||||
|
||||
it("should parse section marks", function () {
|
||||
expect(
|
||||
parse(
|
||||
"There is a block id that is invisible, but you can find it using developer tool's inspect element feature. ^BlockLevelLinksID1"
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
type: "element",
|
||||
tag: "p",
|
||||
children: [
|
||||
{
|
||||
type: "text",
|
||||
text: "There is a block id that is invisible, but you can find it using developer tool's inspect element feature.",
|
||||
start: 0,
|
||||
end: 106,
|
||||
},
|
||||
{
|
||||
type: "blockmark",
|
||||
attributes: {
|
||||
id: { type: "string", value: "BlockLevelLinksID1", start: 126, end: 144 },
|
||||
previousSibling: { type: "string", value: "" },
|
||||
},
|
||||
children: [],
|
||||
start: 106,
|
||||
end: 126,
|
||||
rule: "blockmark",
|
||||
},
|
||||
],
|
||||
start: 0,
|
||||
end: 126,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it("should parse link to section marks", function () {
|
||||
expect(parse("[[Link to BlockLevelLinksID1|Block Level Links in WikiText^BlockLevelLinksID1]]")).toEqual([
|
||||
{
|
||||
type: "element",
|
||||
tag: "p",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
attributes: {
|
||||
to: { type: "string", value: "Block Level Links in WikiText", start: 29, end: 58 },
|
||||
toBlockMark: { type: "string", value: "BlockLevelLinksID1", start: 59, end: 77 },
|
||||
},
|
||||
children: [{ type: "text", text: "Link to BlockLevelLinksID1", start: 2, end: 28 }],
|
||||
start: 0,
|
||||
end: 79,
|
||||
rule: "prettylink",
|
||||
},
|
||||
],
|
||||
start: 0,
|
||||
end: 79,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -18,6 +18,7 @@ describe("WikiText tests", function() {
|
||||
wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"});
|
||||
wiki.addTiddler({title: "TiddlerThree", text: "The speed of sound\n\nThe light of speed"});
|
||||
wiki.addTiddler({title: "TiddlerFour", text: "\\define my-macro(adjective:'groovy')\nThis is my ''amazingly'' $adjective$ macro!\n\\end\n\n<$link to=<<my-macro>>>This is a link</$link>"});
|
||||
wiki.addTiddler({title: "TiddlerFive", text: "Paragraph. ^markID"});
|
||||
|
||||
it("should render tiddlers with no special markup as-is", function() {
|
||||
expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox");
|
||||
@ -66,5 +67,18 @@ describe("WikiText tests", function() {
|
||||
expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
|
||||
"some @@background:red;color:white;.myClass 2 style and 1 class@@ text")).toBe('<p>some <span class=" myClass " style="background:red;color:white;">2 style and 1 class</span> text</p>');
|
||||
});
|
||||
it("handles link wikitext notation", function() {
|
||||
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('<p>A link to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="#TiddlerFive">TiddlerFive</a></p>' );
|
||||
var tiddler = wiki.getTiddler("TiddlerFive");
|
||||
wiki.deleteTiddler("TiddlerFive");
|
||||
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('<p>A link to <a class="tc-tiddlylink tc-tiddlylink-missing" href="#TiddlerFive">TiddlerFive</a></p>');
|
||||
wiki.addTiddler(tiddler);
|
||||
});
|
||||
it("handles block mark wikitext notation", function() {
|
||||
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","Link to section [[TiddlerFive^markID]]")).toBe('<p>Link to section <a class="tc-tiddlylink tc-tiddlylink-resolves" href="#TiddlerFive-markID">TiddlerFive</a></p>' );
|
||||
// It has title as namespace in id when `currentTiddler` is set
|
||||
expect(wiki.renderTiddler("text/html","TiddlerFive", { variables: { currentTiddler: "TiddlerFive" }})).toBe('<p>Paragraph.<span class="tc-block-mark" data-block-mark-id="markID" data-block-mark-title="TiddlerFive" id="TiddlerFive-markID"></span></p>');
|
||||
expect(wiki.renderTiddler("text/html","TiddlerFive")).toBe('<p>Paragraph.<span class="tc-block-mark" data-block-mark-id="markID" id="markID"></span></p>');
|
||||
});
|
||||
});
|
||||
|
||||
|
34
editions/tw5.com/tiddlers/widgets/BlockMarkWidget.tid
Normal file
34
editions/tw5.com/tiddlers/widgets/BlockMarkWidget.tid
Normal file
@ -0,0 +1,34 @@
|
||||
caption: block id
|
||||
created: 20230916061829840
|
||||
modified: 20230922150245402
|
||||
tags: Widgets
|
||||
title: BlockMarkWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
! Introduction
|
||||
|
||||
The block id widget make an block mark that can be focused and jump to.
|
||||
|
||||
! Content and Attributes
|
||||
|
||||
The content of the `<$blockmark>` widget is ignored.
|
||||
|
||||
|!Attribute |!Description |
|
||||
|id |The unique id for the block |
|
||||
|previousSibling |`yes` means the block is before this node, in parent node's children list, else it means the block is this node's direct parent node. |
|
||||
|
||||
See [[Block Level Links in WikiText^🤗→AddingIDforblock]] for WikiText syntax of block ID.
|
||||
|
||||
! Example
|
||||
|
||||
<<wikitext-example-without-html """The block id widget is invisible, and is usually located at the end of the line. ID is here:<$blockmark id="BlockLevelLinksID1"/>
|
||||
|
||||
[[Link to BlockLevelLinksID1|BlockMarkWidget^BlockLevelLinksID1]]
|
||||
""">>
|
||||
|
||||
<<wikitext-example """You can refer to the block that is a line before the block id widget. Make sure block id widget itself is in a block (paragraph).
|
||||
|
||||
ID is here:<$blockmark id="BlockLevelLinksID2" previousSibling="yes"/>
|
||||
|
||||
[[Link to BlockLevelLinksID2|BlockMarkWidget^BlockLevelLinksID2]]
|
||||
""">>
|
@ -16,6 +16,7 @@ The content of the link widget is rendered within the `<a>` tag representing the
|
||||
|
||||
|!Attribute |!Description |
|
||||
|to |The title of the target tiddler for the link (defaults to the [[current tiddler|Current Tiddler]]) |
|
||||
|toBlockMark |<<.from-version "5.3.6">> Optional id of the anchor for [[Block Level Links in WikiText]] |
|
||||
|aria-label |Optional accessibility label |
|
||||
|tooltip |Optional tooltip WikiText |
|
||||
|tabindex |Optional numeric [[tabindex|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex]] |
|
||||
|
@ -1,14 +1,14 @@
|
||||
created: 20190311094603013
|
||||
modified: 20201130064330388
|
||||
modified: 20241002211330388
|
||||
tags: [[HTML in WikiText]]
|
||||
title: Anchor Links using HTML
|
||||
|
||||
<a id=<<qualify "#Top">>></a>
|
||||
|
||||
Here we describe a HTML way to link to a section. To use WikiText style anchor links, see [[Block Level Links in WikiText|Block Level Links in WikiText^BlockLevelLinksID1]].
|
||||
|
||||
<h2 id="#Introduction:Anchor-Links-using-HTML">''HTML Anchor Links in Tiddlers''</h2>
|
||||
|
||||
|
||||
|
||||
* <a href=<<qualify "##Introduction">>>What do they do?</a>
|
||||
* <a href=<<qualify "##How-do-I-make-them">>>How do I make them?</a>
|
||||
* <a href=<<qualify "##Anchor-link-limitations">>>Limitations and things to look out for</a>
|
||||
|
@ -0,0 +1,48 @@
|
||||
caption: Block Level Links
|
||||
created: 20230916061138153
|
||||
modified: 20230922150740619
|
||||
tags: WikiText
|
||||
title: Block Level Links in WikiText
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<<wikitext-example-without-html src:"! Adding ID for block ^🤗→AddingIDforblock">>
|
||||
|
||||
The basic syntax for block id is:
|
||||
|
||||
<<wikitext-example src:"There is a block id that is invisible, but you can find it using developer tool's inspect element feature. ^BlockLevelLinksID1">>
|
||||
|
||||
# You should add a space between the end of your text and the `^`.
|
||||
# And there is no space between `^` and the id.
|
||||
# ID can contain any char other than `^` and space ` `.
|
||||
|
||||
And this block id widget will be rendered as an invisible element:
|
||||
|
||||
```html
|
||||
<span class="tc-block-id" data-block-id="BlockLevelLinksID1" data-block-title="Block Level Links in WikiText"></span>
|
||||
```
|
||||
|
||||
!! Adding id to previous block
|
||||
|
||||
Some block, for example, code block, can't be suffixed by `^id`, but we can add the id in the next line, with no space prefix to it.
|
||||
|
||||
<<wikitext-example src:"```css
|
||||
.main {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
^BlockLevelLinksID2
|
||||
|
||||
">>
|
||||
|
||||
! Link to the block ID ^091607
|
||||
|
||||
Adding `^blockID` after the title in the link, will make this link highlight the block with that ID.
|
||||
|
||||
<<wikitext-example-without-html src:"[[Link to BlockLevelLinksID1|Block Level Links in WikiText^BlockLevelLinksID1]]">>
|
||||
|
||||
<<wikitext-example-without-html src:"[[Link to BlockLevelLinksID2|Block Level Links in WikiText^BlockLevelLinksID2]]">>
|
||||
|
||||
<<wikitext-example-without-html src:"[[Link to Title|Block Level Links in WikiText^🤗→AddingIDforblock]]">>
|
||||
|
||||
<<wikitext-example-without-html src:"[[Link to Non-existing anchor|Block Level Links in WikiText^😂❎]]">>
|
@ -1,6 +1,6 @@
|
||||
caption: Linking
|
||||
created: 20131205155230596
|
||||
modified: 20211230145939554
|
||||
modified: 20230917121659927
|
||||
tags: WikiText
|
||||
title: Linking in WikiText
|
||||
type: text/vnd.tiddlywiki
|
||||
@ -123,3 +123,9 @@ See also another example of [[constructing dynamic links|Concatenating text and
|
||||
In TiddlyWiki anchor links can help us link to target points and distinct sections within rendered tiddlers. They can help the reader navigate longer tiddler content.
|
||||
|
||||
See [[Anchor Links using HTML]] for more information.
|
||||
|
||||
! Linking within tiddlers - Link to block mark
|
||||
|
||||
You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique.
|
||||
|
||||
See [[Block Level Links in WikiText^091607]] for more information.
|
||||
|
@ -2481,6 +2481,21 @@ html body.tc-body.tc-single-tiddler-window {
|
||||
color: <<colour alert-highlight>>;
|
||||
}
|
||||
|
||||
@keyframes fade-highlight {
|
||||
0% {
|
||||
background-color: <<colour highlight-background>>;
|
||||
border-color: <<colour highlight-background>>;
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.tc-focus-highlight {
|
||||
animation: fade-highlight 2s forwards;
|
||||
}
|
||||
|
||||
@media (min-width: <<sidebarbreakpoint>>) {
|
||||
|
||||
.tc-static-alert {
|
||||
|
Loading…
x
Reference in New Issue
Block a user