Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wikify operator #8730

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions core/modules/filters/wikify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*\
title: $:/core/modules/filters/wikify.js
type: application/javascript
module-type: filteroperator

Filter operator wikifying each string in the input list and returning the result as a list of strings

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/*
Export our filter function
*/
exports.wikify = function(source,operator,options) {
var output = operator.operands[0],
mode = operator.operands[1],
type = operator.operands[2],
results = [];
source(function(tiddler,title) {
var wikifier = new $tw.utils.Wikifier({
wiki: options.wiki,
widget: options.widget,
text: title,
type: type,
mode: mode,
output: output
});
results.push(wikifier.getResult());
});
return results;
};

})();
108 changes: 108 additions & 0 deletions core/modules/utils/wikifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*\
title: $:/core/modules/utils/wikifier.js
type: application/javascript
module-type: utils

A high level helper class for parsing and wikification

\*/
(function(){

/*
Options include:
wiki: wiki to be used for wikification
widget: optional widget to be used as parent of wikified text
text: text to be parsed/wikified
type: type of the text
mode: inline or block
output: text, formattedtext, html, parsetree or widgettree
*/
function Wikifier(options) {
this.wiki = options.wiki || $tw.wiki;
this.widget = options.widget || $tw.rootWidget;
this.text = options.text || "";
this.type = options.type || "";
this.mode = options.mode || "block";
this.output = options.output || "text";
// Create the parse tree
this.parser = this.wiki.parseText(this.type,this.text,{
parseAsInline: this.mode === "inline"
});
// Create the widget tree
this.widgetNode = this.wiki.makeWidget(this.parser,{
document: $tw.fakeDocument,
parentWidget: this.widget
});
// Render the widget tree to the container
this.container = $tw.fakeDocument.createElement("div");
this.widgetNode.render(this.container,null);
};

Wikifier.prototype.refresh = function(changedTiddlers) {
// Refresh the widget tree
return this.widgetNode.refresh(changedTiddlers);
};

/*
Return the result string
*/
Wikifier.prototype.getResult = function() {
var result;
switch(this.output) {
case "text":
result = this.container.textContent;
break;
case "formattedtext":
result = this.container.formattedTextContent;
break;
case "html":
result = this.container.innerHTML;
break;
case "parsetree":
result = JSON.stringify(this.parser.tree,0,$tw.config.preferences.jsonSpaces);
break;
case "widgettree":
result = JSON.stringify(this.getWidgetTree(),0,$tw.config.preferences.jsonSpaces);
break;
}
return result;
};

/*
Return a string of the widget tree
*/
Wikifier.prototype.getWidgetTree = function() {
var copyNode = function(widgetNode,resultNode) {
var type = widgetNode.parseTreeNode.type;
resultNode.type = type;
switch(type) {
case "element":
resultNode.tag = widgetNode.parseTreeNode.tag;
break;
case "text":
resultNode.text = widgetNode.parseTreeNode.text;
break;
}
if(Object.keys(widgetNode.attributes || {}).length > 0) {
resultNode.attributes = {};
$tw.utils.each(widgetNode.attributes,function(attr,attrName) {
resultNode.attributes[attrName] = widgetNode.getAttribute(attrName);
});
}
if(Object.keys(widgetNode.children || {}).length > 0) {
resultNode.children = [];
$tw.utils.each(widgetNode.children,function(widgetChildNode) {
var node = {};
resultNode.children.push(node);
copyNode(widgetChildNode,node);
});
}
},
results = {};
copyNode(this.widgetNode,results);
return results;
};

exports.Wikifier = Wikifier;

})();
91 changes: 12 additions & 79 deletions core/modules/widgets/wikify.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,89 +39,22 @@ Compute the internal state of the widget
WikifyWidget.prototype.execute = function() {
// Get our parameters
this.wikifyName = this.getAttribute("name");
this.wikifyText = this.getAttribute("text");
this.wikifyType = this.getAttribute("type");
this.wikifyMode = this.getAttribute("mode","block");
this.wikifyOutput = this.getAttribute("output","text");
// Create the parse tree
this.wikifyParser = this.wiki.parseText(this.wikifyType,this.wikifyText,{
parseAsInline: this.wikifyMode === "inline"
});
// Create the widget tree
this.wikifyWidgetNode = this.wiki.makeWidget(this.wikifyParser,{
document: $tw.fakeDocument,
parentWidget: this
});
// Render the widget tree to the container
this.wikifyContainer = $tw.fakeDocument.createElement("div");
this.wikifyWidgetNode.render(this.wikifyContainer,null);
this.wikifyResult = this.getResult();
// Create the wikifier
this.wikifier = new $tw.utils.Wikifier({
wiki: this.wiki,
widget: this,
text: this.getAttribute("text"),
type: this.getAttribute("type"),
mode: this.getAttribute("mode","block"),
output: this.getAttribute("output","text")
});
this.wikifyResult = this.wikifier.getResult();
// Set context variable
this.setVariable(this.wikifyName,this.wikifyResult);
// Construct the child widgets
this.makeChildWidgets();
};

/*
Return the result string
*/
WikifyWidget.prototype.getResult = function() {
var result;
switch(this.wikifyOutput) {
case "text":
result = this.wikifyContainer.textContent;
break;
case "formattedtext":
result = this.wikifyContainer.formattedTextContent;
break;
case "html":
result = this.wikifyContainer.innerHTML;
break;
case "parsetree":
result = JSON.stringify(this.wikifyParser.tree,0,$tw.config.preferences.jsonSpaces);
break;
case "widgettree":
result = JSON.stringify(this.getWidgetTree(),0,$tw.config.preferences.jsonSpaces);
break;
}
return result;
};

/*
Return a string of the widget tree
*/
WikifyWidget.prototype.getWidgetTree = function() {
var copyNode = function(widgetNode,resultNode) {
var type = widgetNode.parseTreeNode.type;
resultNode.type = type;
switch(type) {
case "element":
resultNode.tag = widgetNode.parseTreeNode.tag;
break;
case "text":
resultNode.text = widgetNode.parseTreeNode.text;
break;
}
if(Object.keys(widgetNode.attributes || {}).length > 0) {
resultNode.attributes = {};
$tw.utils.each(widgetNode.attributes,function(attr,attrName) {
resultNode.attributes[attrName] = widgetNode.getAttribute(attrName);
});
}
if(Object.keys(widgetNode.children || {}).length > 0) {
resultNode.children = [];
$tw.utils.each(widgetNode.children,function(widgetChildNode) {
var node = {};
resultNode.children.push(node);
copyNode(widgetChildNode,node);
});
}
},
results = {};
copyNode(this.wikifyWidgetNode,results);
return results;
};

/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
Expand All @@ -133,9 +66,9 @@ WikifyWidget.prototype.refresh = function(changedTiddlers) {
return true;
} else {
// Refresh the widget tree
if(this.wikifyWidgetNode.refresh(changedTiddlers)) {
if(this.wikifier.refresh(changedTiddlers)) {
// Check if there was any change
var result = this.getResult();
var result = this.wikifier.getResult();
if(result !== this.wikifyResult) {
// If so, save the change
this.wikifyResult = result;
Expand Down
21 changes: 21 additions & 0 deletions editions/test/tiddlers/tests/data/operators/wikify/HtmlMode.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
title: Operators/Wikify/TextMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Output

\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter

[{Text}wikify[html],[inline],[text/vnd.tiddlywiki]]
+
title: Text

This is ''the text'' that is __wikified__
+
title: ExpectedResult

This is &lt;strong&gt;the text&lt;/strong&gt; that is &lt;u&gt;wikified&lt;/u&gt;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
title: Operators/Wikify/ParseTreeMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Output

\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter

[{Text}wikify[parsetree],[inline],[text/vnd.tiddlywiki]]
+
title: Text

This is ''the text'' that is __wikified__
+
title: ExpectedResult

[
{
"type": "text",
"text": "This is ",
"start": 0,
"end": 8
},
{
"type": "element",
"tag": "strong",
"children": [
{
"type": "text",
"text": "the text",
"start": 10,
"end": 18
}
],
"start": 8,
"end": 20,
"rule": "bold"
},
{
"type": "text",
"text": " that is ",
"start": 20,
"end": 29
},
{
"type": "element",
"tag": "u",
"children": [
{
"type": "text",
"text": "wikified",
"start": 31,
"end": 39
}
],
"start": 29,
"end": 41,
"rule": "underscore"
}
]
21 changes: 21 additions & 0 deletions editions/test/tiddlers/tests/data/operators/wikify/TextMode.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
title: Operators/Wikify/TextMode
description: Simple wikify operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Output

\parsermode inline
<$text text={{{ [subfilter{Filter}] }}}/>
+
title: Filter

[{Text}wikify[text],[inline],[text/vnd.tiddlywiki]]
+
title: Text

This is ''the text'' that is __wikified__
+
title: ExpectedResult

This is the text that is wikified
Loading