diff --git a/src/ttfrog/bootstrap.py b/src/ttfrog/bootstrap.py
index 335930e..60b2e0d 100644
--- a/src/ttfrog/bootstrap.py
+++ b/src/ttfrog/bootstrap.py
@@ -250,6 +250,7 @@ def bootstrap():
groups = root.add_member(schema.Page(name="Group", body="# Groups\ngroups go here."))
npcs = root.add_member(schema.Page(name="NPC", body="# NPCS!"))
widgets = root.add_member(schema.Page(name="Widget", body="Widgets go here."))
+ widgets.add_member(schema.Widget(name="hello", body=schema.Widget.default))
# create the NPCs
npcs.add_member(schema.NPC(name="Sabetha", body=""))
diff --git a/src/ttfrog/schema.py b/src/ttfrog/schema.py
index bef8732..5987ad6 100644
--- a/src/ttfrog/schema.py
+++ b/src/ttfrog/schema.py
@@ -312,42 +312,41 @@ class Widget(Page):
default = dedent(
"""
-# {name}
+# hello
-Insert the current user's name.
+Insert the word "HELLO."
+
+## Usage
+{{{
+
\\{\\{widget hello [name="NAME"] \\}\\}
+}}}
## Example
-***
+This example uses the current page's widget definition: {{widget hello world}} Nice, huh?
-Hello,
-
-***
## Template
-```html
-
+```
+HELLO${name ? ", " + name : ""}.
```
## CSS
-```css
-.widget-user {{
- display: inline- block;
- border: 1px solid green;
- border-radius: 5px;
- padding: 2px;
-}}
+
+```
+display: inline;
+background: green;
+padding: 3px;
+color: white;
+border-radius: 5px;
```
## Processor
-```javascript
-function(tag, template, css) {{
- // Return the HTML that should be inserted into the template div.
- return document.querySelector("nav li.user a:first-child").outerHTML;
-}};
+
```
- """
+```
+"""
)
@classmethod
diff --git a/src/ttfrog/themes/default/page.html b/src/ttfrog/themes/default/page.html
index 08e0f73..236e8b2 100644
--- a/src/ttfrog/themes/default/page.html
+++ b/src/ttfrog/themes/default/page.html
@@ -20,7 +20,7 @@
{% endif %}
{% endblock %}
diff --git a/src/ttfrog/themes/default/static/froghat-editor.js b/src/ttfrog/themes/default/static/froghat-editor.js
index 840d078..20d7c40 100644
--- a/src/ttfrog/themes/default/static/froghat-editor.js
+++ b/src/ttfrog/themes/default/static/froghat-editor.js
@@ -1,10 +1,6 @@
class FroghatEditor extends Froghat {
- constructor(settings) {
- /*
- * Create a new Editor instance.
- */
- super(settings);
+ run() {
this.states = {
VIEW: 'view',
EDIT: 'edit',
@@ -17,9 +13,11 @@ class FroghatEditor extends Froghat {
});
this.turndown.use([turndownPluginGfm.gfm, turndownPluginGfm.tables]);
this.turndown.keep(['pre']);
- this.#bindEvents();
+ //this.#bindEvents();
this.plugins().forEach(plugin => { plugin.setEditable() });
+ this.element.classList.add("loaded");
+ this.view();
}
#bindEvents() {
@@ -30,8 +28,8 @@ class FroghatEditor extends Froghat {
/*
if (event.key === 'Enter') {
- console.log(this.#state, this.#states.EDIT);
- if (this.#state === this.#states.EDIT) {
+ console.log(this.state, this.states.EDIT);
+ if (this.state === this.states.WYSIWYG) {
evt.preventDefault();
this.insertAtCursor(document.createTextNode("\n"));
}
@@ -45,8 +43,8 @@ class FroghatEditor extends Froghat {
});
};
- HtmlToMarkdown(html) {
- return this.turndown.turndown(html);
+ htmlToMarkdown(html) {
+ return this.turndown.turndown(html || this.element.innerHTML);
}
getMarkdown() {
@@ -54,11 +52,14 @@ class FroghatEditor extends Froghat {
* Return the current markdown.
*/
if (this.getState() === this.states.EDIT) {
- this.cachedMarkdown = this.element.innerHTML.replaceAll(/<\/?div>/g, "\n").replaceAll('
', "");
+ var html = this.element.innerHTML;
+ html = html.replaceAll(/<(?:div|br)>/ig, '');
+ html = html.replaceAll(/<\/div>/ig, "\n");
+ this.cachedMarkdown = decodeHtmlEntities(html);
} else if (this.getState() === this.states.WYSIWYG) {
- this.cachedMarkdown = this.HtmlToMarkdown(this.element.innerHTML);
- } else if (!this.cachedMarkdown) {
- this.cachedMarkdown = this.source;
+ this.cachedMarkdown = this.htmlToMarkdown(this.element.innerHTML);
+ } if (!this.cachedMarkdown) {
+ this.cachedMarkdown = this.element.textContent;
}
return this.cachedMarkdown;
}
diff --git a/src/ttfrog/themes/default/static/froghat.js b/src/ttfrog/themes/default/static/froghat.js
index 9487020..37d9c98 100644
--- a/src/ttfrog/themes/default/static/froghat.js
+++ b/src/ttfrog/themes/default/static/froghat.js
@@ -65,7 +65,6 @@ class Froghat {
this.api = settings.api || FroghatAPIv1;
this.element = document.getElementById(settings.editorId || 'froghat');
- this.source = this.element.textContent;
this.marked = marked;
this.marked.use({
@@ -82,10 +81,14 @@ class Froghat {
this.enabledPlugins = {};
settings.plugins.forEach(plugin => {
- this.enabledPlugins[plugin.name] = new plugin({name: plugin.name, editor: this});
+ this.enabledPlugins[plugin.name] = new plugin({name: plugin.name, wiki: this});
});
- this.getHTML();
+
+ }
+
+ run() {
this.element.classList.add("loaded");
+ this.view();
}
plugins() {
@@ -116,35 +119,29 @@ class Froghat {
/*
* Convert the markdown source to HTML.
*/
+ /*
if (this.changed || !this.cachedHTML) {
this.cachedHTML = this.markdownToHTML(this.getMarkdown());
}
+ */
+ var md = this.getMarkdown();
+ this.cachedHTML = this.markdownToHTML(md);
return this.cachedHTML;
}
getMarkdown() {
- if (!this.cachedMarkdown) {
- this.cachedMarkdown = this.source;
- }
return this.cachedMarkdown;
}
- reset() {
- /*
- * Discard any unsaved edits and reset the editor to its initial state.
- */
- this.cachedHTML = null;
- this.cachedMarkdown = null;
- this.view();
- }
-
view() {
/*
- * Convert the editor read-only mode and display the current HTML.
+ * Convert the wiki read-only mode and display the current HTML.
*/
+ /*
if (this.getState() === this.states.VIEW) {
return;
}
+ */
this.element.innerHTML = this.getHTML();
this.setState(this.states.VIEW);
this.contentEditable = false;
@@ -156,7 +153,7 @@ class FroghatPlugin {
constructor(settings) {
this.name = settings.name;
- this.editor = settings.editor;
+ this.wiki = settings.wiki;
this.precedence = 50;
};
@@ -173,76 +170,76 @@ class FroghatPlugin {
};
-class WidgetPlugin extends FroghatPlugin {
+WIDGETS = {};
- setEditable() {
- };
-
- toMarkdown(html) {
- return html;
- };
-
- toHTML(md) {
- return md;
- };
-
- parseWidgetSource(html) {
-
- function block(prefix) {
- return RegExp('##\\s*' + prefix + '.*?```\\w*(.+?)```', 'gims');
- };
-
- const template = block("Template").exec(html)[1];
- const css = block("CSS").exec(html)[1];
- const processor = block("Processor").exec(html)[1];
-
- var func;
- eval("func = " + processor);
-
- return {
- template: template,
- css: css,
- processor: func
- };
- }
-
- async processWidgets(html, callback) {
-
- var widgetPattern = /({{(.+)}})/gm;
-
- if (!html.match(widgetPattern)) {
- callback();
- return;
- }
-
- html.matchAll(widgetPattern).forEach(match => {
- var widgetTag = match[1];
- var widgetName = match[2];
- if (Object.values(WIDGETS).indexOf(widgetName) == -1) {
- APIv1.search("Widget", widgetName, (res) => {
- if (res.code == 200) {
- var parts = parseWidgetSource(res.response[0].body);
- WIDGETS[widgetName] = parts.processor;
- contents = WIDGETS[widgetName](widgetTag, parts.template, parts.css);
+function loadWidget(name, callback) {
+ var widget = null;
+ if (Object.values(WIDGETS).indexOf(name) == -1) {
+ (async () => {
+ await FroghatAPIv1.search("Widget", name, (res) => {
+ if (res.code == 200) {
+ function block(prefix) {
+ return RegExp('##\\s*' + prefix + '.*?```\\w*(.+?)```', 'gims');
+ };
+ var html = res.response[0].body;
+ var proc = block("Processor").exec(html)[1].trim();
+ if (!proc) {
+ proc = function(token, widget) {
+ var name = token.keywords.split(" ").slice(1).join(" ");
+ var ret = '';
+ eval("ret = `" + widget.template + "`");
+ return ret;
+ }
} else {
- contents = `Invalid widget: ${widgetName}`;
+ eval(`proc = ${proc}`);
}
- var rep = `${contents}`;
- html = html.replaceAll(widgetTag, rep);
- if (parts) {
- html = `${html}`;
- }
- callback(html);
- });
- }
- });
- };
+ WIDGETS[name] = {
+ template: block("Template").exec(html)[1],
+ css: block("CSS").exec(html)[1],
+ processor: proc
+ };
+ } else {
+ WIDGETS[name] = {
+ template: "",
+ css: "",
+ processor: function() { return `Invalid Widget: "${name}"` },
+ };
+ }
+ if (callback) {
+ callback(WIDGETS[name]);
+ }
+ });
+ })();
+ } else {
+ if (callback) {
+ callback(WIDGETS[name]);
+ }
+ }
}
+
class MacroPlugin extends FroghatPlugin {
macros = {
// image: {}
+
+ widget: {
+ inline: true,
+ toHTML: (token, node) => {
+ var widgetName = token.keywords.split(" ")[0];
+ var contents = '';
+ loadWidget(widgetName, (widget) => {
+ contents = widget.processor(token, widget);
+ var targets = wiki.element.querySelectorAll(`[data-macro-name="widget"][data-keywords="${token.keywords}"]`);
+ targets.forEach(widgetElement => {
+ widgetElement.style = widget.css;
+ widgetElement.innerHTML = contents;
+ });
+ });
+ return node + "";
+ }
+ },
+
style: {
inline: false,
toHTML: (token, node) => {
@@ -437,14 +434,24 @@ class MacroPlugin extends FroghatPlugin {
constructor(settings) {
super(settings);
- this.pattern = /(?<[^>]+?>){{(?\w+)(?(?:\s*[\w-]+)*?)?(?(?:\s+[\w-]+=\S+?)*)?\s*(?}})?(?<\/[^>]+?>)/mg;
+
+ this.pattern = new RegExp(
+ '(?<[^>]+?>)?' + // capture the enclosing HTML tag, if any
+ '{{' + // start of the macro
+ '(?\\w+)' + // the macro name
+ '(?(?:\\s(?:\\s*(?:[\\w-](?![\\w-]+=))+))+)?' + // zero or more keywords separated by spaces
+ '(?[^}<]+)?' + // anything else before the closing
+ '\\s*(?}})?' + // is the tag closed?
+ '(?(?:>!\\<)*?<\\/[^>]+?>)?', // capture the enclosing HTML tag, if any
+ 'mg'
+ );
this.endPattern = /}}\s*<\/p>/mg;
this.paramPattern = /\s*(?[^=]+)="(?[^"]*)"/g;
this.multilinePattern = /(?(.(?!\}{3})*)+?)[\s\n]*\}{3}/smg;
const plugin = this;
- this.editor.marked.use({
+ this.wiki.marked.use({
extensions: [
{
name: 'heading',
@@ -493,7 +500,8 @@ class MacroPlugin extends FroghatPlugin {
}
setEditable() {
- this.editor.turndown.addRule('macros', {
+ const plugin = this;
+ this.wiki.turndown.addRule('macros', {
filter: function (node, options) {
return ((node.nodeName === 'DIV' || node.nodeName === 'SPAN') && node.dataset.pluginName == 'macro')
},
@@ -518,7 +526,7 @@ class MacroPlugin extends FroghatPlugin {
if (node.dataset.inline == "false") {
md = `\n\n${md}\n\n`;
- md += plugin.editor.HtmlToMarkdown(node.innerHTML);
+ md += plugin.wiki.htmlToMarkdown(node.innerHTML);
md += "\n\n}}\n\n";
} else {
md += "}}";