mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-10-31 15:16:18 +00:00
Introduce "innerwiki" plugin
From the readme: This plugin enables TiddlyWiki to embed a modified copy of itself (an "innerwiki"). The primary motivation is to be able to produce screenshot illustrations that are automatically up-to-date with the appearance of TiddlyWiki as it changes over time, or to produce the same screenshot in different languages
This commit is contained in:
parent
353821f442
commit
ca7b62a5f6
@ -224,6 +224,16 @@ node $TW5_BUILD_TIDDLYWIKI \
|
|||||||
#
|
#
|
||||||
######################################################
|
######################################################
|
||||||
|
|
||||||
|
# /plugins/tiddlywiki/innerwiki/index.html Demo wiki with Innerwiki plugin
|
||||||
|
|
||||||
|
node $TW5_BUILD_TIDDLYWIKI \
|
||||||
|
./editions/innerwikidemo \
|
||||||
|
--verbose \
|
||||||
|
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||||
|
--output $TW5_BUILD_OUTPUT \
|
||||||
|
--rendertiddler $:/core/save/all plugins/tiddlywiki/innerwikidemo/index.html text/plain \
|
||||||
|
|| exit 1
|
||||||
|
|
||||||
# /plugins/tiddlywiki/dynaview/index.html Demo wiki with DynaView plugin
|
# /plugins/tiddlywiki/dynaview/index.html Demo wiki with DynaView plugin
|
||||||
# /plugins/tiddlywiki/dynaview/empty.html Empty wiki with DynaView plugin
|
# /plugins/tiddlywiki/dynaview/empty.html Empty wiki with DynaView plugin
|
||||||
|
|
||||||
|
12
editions/innerwikidemo/tiddlers/HelloThere.tid
Normal file
12
editions/innerwikidemo/tiddlers/HelloThere.tid
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
title: HelloThere
|
||||||
|
|
||||||
|
This is a demo of TiddlyWiki 5's ''innerwiki'' plugin.
|
||||||
|
|
||||||
|
To try these examples under Node.js:
|
||||||
|
|
||||||
|
# Install Puppeteer, as described in the [[readme|$:/plugins/tiddlywiki/innerwiki/readme]]
|
||||||
|
# Execute the following command in the root of the TiddlyWiki 5 repo:
|
||||||
|
|
||||||
|
```
|
||||||
|
./tiddlywiki.js editions/innerwikidemo --screenshot '[[$:/plugins/tiddlywiki/innerwiki/examples]]' 4
|
||||||
|
```
|
@ -0,0 +1,5 @@
|
|||||||
|
title: $:/DefaultTiddlers
|
||||||
|
|
||||||
|
[[$:/plugins/tiddlywiki/innerwiki/readme]]
|
||||||
|
[[$:/plugins/tiddlywiki/innerwiki/examples]]
|
||||||
|
[[$:/plugins/tiddlywiki/innerwiki/docs]]
|
3
editions/innerwikidemo/tiddlers/system/SiteSubtitle.tid
Normal file
3
editions/innerwikidemo/tiddlers/system/SiteSubtitle.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/SiteSubtitle
|
||||||
|
|
||||||
|
Embedding wikis within wikis
|
3
editions/innerwikidemo/tiddlers/system/SiteTitle.tid
Normal file
3
editions/innerwikidemo/tiddlers/system/SiteTitle.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/SiteTitle
|
||||||
|
|
||||||
|
Innerwiki Demo
|
23
editions/innerwikidemo/tiddlywiki.info
Normal file
23
editions/innerwikidemo/tiddlywiki.info
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"description": "Innerwiki Plugin Demo",
|
||||||
|
"plugins": [
|
||||||
|
"tiddlywiki/innerwiki"
|
||||||
|
],
|
||||||
|
"themes": [
|
||||||
|
"tiddlywiki/vanilla",
|
||||||
|
"tiddlywiki/snowwhite"
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"index": [
|
||||||
|
"--rendertiddler",
|
||||||
|
"$:/core/save/all",
|
||||||
|
"index.html",
|
||||||
|
"text/plain"
|
||||||
|
],
|
||||||
|
"save-screenshots": [
|
||||||
|
"--screenshot",
|
||||||
|
"[[$:/plugins/tiddlywiki/innerwiki/examples]]",
|
||||||
|
"4"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
11
editions/tw5.com/tiddlers/plugins/Innerwiki Plugin.tid
Normal file
11
editions/tw5.com/tiddlers/plugins/Innerwiki Plugin.tid
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
created: 20190127104143725
|
||||||
|
modified: 20190127104143725
|
||||||
|
tags: OfficialPlugins
|
||||||
|
title: Innerwiki Plugin
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
The Innerwiki Plugin enables TiddlyWiki to embed a modified copy of itself (an "innerwiki"). The primary motivation is to be able to produce screenshot illustrations that are automatically up-to-date with the appearance of TiddlyWiki as it changes over time, or to produce the same screenshot in different languages.
|
||||||
|
|
||||||
|
In the browser, innerwikis are displayed as an embedded iframe. Under Node.js [[Google's Puppeteer|https://pptr.dev/]] is used to load the innerwikis as off-screen web pages and then snapshot them as a PNG image.
|
||||||
|
|
||||||
|
See the demo at [ext[https://tiddlywiki.com/plugins/tiddlywiki/innerwiki|plugins/tiddlywiki/innerwiki]]
|
56
plugins/tiddlywiki/innerwiki/data.js
Normal file
56
plugins/tiddlywiki/innerwiki/data.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/innerwiki/data.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: widget
|
||||||
|
|
||||||
|
Widget to represent a single item of data
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||||
|
|
||||||
|
var DataWidget = function(parseTreeNode,options) {
|
||||||
|
this.initialise(parseTreeNode,options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inherit from the base widget class
|
||||||
|
*/
|
||||||
|
DataWidget.prototype = new Widget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Render this widget into the DOM
|
||||||
|
*/
|
||||||
|
DataWidget.prototype.render = function(parent,nextSibling) {
|
||||||
|
this.parentDomNode = parent;
|
||||||
|
this.computeAttributes();
|
||||||
|
this.execute();
|
||||||
|
this.renderChildren(parent,nextSibling);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute the internal state of the widget
|
||||||
|
*/
|
||||||
|
DataWidget.prototype.execute = function() {
|
||||||
|
// Construct the child widgets
|
||||||
|
this.makeChildWidgets();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||||
|
*/
|
||||||
|
DataWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
|
// Refresh our attributes
|
||||||
|
var changedAttributes = this.computeAttributes();
|
||||||
|
// Refresh our children
|
||||||
|
return this.refreshChildren(changedTiddlers);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.data = DataWidget;
|
||||||
|
|
||||||
|
})();
|
61
plugins/tiddlywiki/innerwiki/doc/docs.tid
Normal file
61
plugins/tiddlywiki/innerwiki/doc/docs.tid
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/docs
|
||||||
|
|
||||||
|
! `<$innerwiki>` widget
|
||||||
|
|
||||||
|
The `<$innerwiki>` widget encapsulates an embedded wiki. It starts as a blank copy of the current wiki and can have additional payload tiddlers added via embedded `<$data>` widgets (see below).
|
||||||
|
|
||||||
|
It supports the following attributes:
|
||||||
|
|
||||||
|
|!Attribute |!Description |
|
||||||
|
|template |Specifies the template to be used to generate the base wiki (defaults to $:/plugins/tiddlywiki/innerwiki/template) |
|
||||||
|
|width |Width in pixels of the virtual screen for rendering the embedded wiki |
|
||||||
|
|height |Height in pixels of the virtual screen for rendering the embedded wiki |
|
||||||
|
|style |CSS style definitions to be added to the DIV wrapper around the IFRAME containing the embedded wiki |
|
||||||
|
|class |CSS classes to be added to the DIV wrapper around the IFRAME containing the embedded wiki |
|
||||||
|
|filename |Base filename for saving a screenshot of the embedded wiki under Node.js (excludes file extension) |
|
||||||
|
|clipLeft |Position in pixels of the left edge of the clip rectangle (optional) |
|
||||||
|
|clipTop |Position in pixels of the top edge of the clip rectangle (optional) |
|
||||||
|
|clipWidth |Width in pixels of the clip rectangle (optional) |
|
||||||
|
|clipHeight |Height in pixels of the clip rectangle (optional) |
|
||||||
|
|
||||||
|
! `<$data>` widget
|
||||||
|
|
||||||
|
The `<$data>` widget is used within the `<$innerwiki>` widget to specify payload tiddlers to be added to the innerwiki.
|
||||||
|
|
||||||
|
It supports the following attributes:
|
||||||
|
|
||||||
|
|!Attribute |!Description |
|
||||||
|
|$tiddler |The title of a tiddler to be used as a payload tiddler (optional) |
|
||||||
|
|$filter |A filter string identifying tiddlers to be used as payload tiddlers (optional) |
|
||||||
|
|//any attribute<br>not starting<br>with $// |Field values to be assigned to the payload tiddler(s) |
|
||||||
|
|
||||||
|
It can be used in three different ways:
|
||||||
|
|
||||||
|
* Without the `$tiddler` or `$filter` attributes, the remaining attributes provide the fields for a single payload tiddler
|
||||||
|
* With the `$tiddler` attribute present, the payload tiddler takes its fields from that tiddler with the remaining attributes overriding those fields
|
||||||
|
* With the `$filter` attribute present, the payload is a copy of all of the tiddlers identified by the filter, with the remaining attributes overriding those fields of each one
|
||||||
|
|
||||||
|
This example injects a copy of the "HelloThere" tiddler with the addition of the field "custom" set to "Alpha":
|
||||||
|
|
||||||
|
```
|
||||||
|
<$data $tiddler="HelloThere" custom="Alpha"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
This example injects all image tiddlers with the addition of the field "custom" set to "Beta":
|
||||||
|
|
||||||
|
```
|
||||||
|
<$data $filter="[is[image]]" custom="Beta"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
! `screenshot` command
|
||||||
|
|
||||||
|
Saves PNG screenshots of the `<$innerwiki>` widgets rendered by a set of tiddlers identified by a filter.
|
||||||
|
|
||||||
|
```
|
||||||
|
--screenshot <filter> <deviceScaleFactor>
|
||||||
|
```
|
||||||
|
|
||||||
|
* ''filter'': a filter identifying the tiddlers to be rendered, from which the individual `<$innerwiki>` widgets are screenshotted
|
||||||
|
* ''deviceScaleFactor'': a scale factor for the screenshot (optional; defaults to 1)
|
||||||
|
|
||||||
|
A deviceScaleFactor of 4 or 5 gives high quality screenshots suitable for print use.
|
5
plugins/tiddlywiki/innerwiki/doc/example-data.tid
Normal file
5
plugins/tiddlywiki/innerwiki/doc/example-data.tid
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/example-data
|
||||||
|
|
||||||
|
<$data title="$:/SiteTitle" text="Innerwiki Demo"/>
|
||||||
|
<$data title="$:/SiteSubtitle" text="Wikis spawning wikis"/>
|
||||||
|
|
74
plugins/tiddlywiki/innerwiki/doc/examples.tid
Normal file
74
plugins/tiddlywiki/innerwiki/doc/examples.tid
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/examples
|
||||||
|
|
||||||
|
\define example(text)
|
||||||
|
<$codeblock code=<<__text__>>/>
|
||||||
|
|
||||||
|
Renders as:
|
||||||
|
|
||||||
|
$text$
|
||||||
|
\end
|
||||||
|
|
||||||
|
!! Browser
|
||||||
|
|
||||||
|
The innerwiki widget specifies the dimensions of the virtual screen used to render the wiki (in pixels) and CSS styles to apply to it. Nested `<$data>` widgets are used to specify individual payload tiddlers to be loaded into the wiki. In this example, we initialise the innerwiki with two tiddlers "HelloThere" and "$:/DefaultTiddlers":
|
||||||
|
|
||||||
|
<<example """<$innerwiki width="1200" height="400" style="width:100%;" filename="screenshot-1">
|
||||||
|
<$data title="HelloThere" text="This tiddler is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere"/>
|
||||||
|
</$innerwiki>""">>
|
||||||
|
|
||||||
|
Note that the "screenshot" is a shrunken but fully interactive TiddlyWiki.
|
||||||
|
|
||||||
|
!! Node.js
|
||||||
|
|
||||||
|
To render these examples as a PNG bitmap under Node.js, execute the following at the command prompt:
|
||||||
|
|
||||||
|
```
|
||||||
|
tiddlywiki mywiki --screenshot $:/plugins/tiddlywiki/innerwiki/examples
|
||||||
|
```
|
||||||
|
|
||||||
|
The screenshots will be saved as `screenshot-1.png` etc in the `./output` folder of the wiki.
|
||||||
|
|
||||||
|
!! Clipping
|
||||||
|
|
||||||
|
A clipping rectangle can be applied to limit the area of the wiki that is displayed. For example:
|
||||||
|
|
||||||
|
<<example """<$innerwiki width="1200" height="400" style="width:100%;" clipLeft="500" clipTop="100" clipWidth="600" clipHeight="300" filename="screenshot-2">
|
||||||
|
<$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere"/>
|
||||||
|
</$innerwiki>""">>
|
||||||
|
|
||||||
|
!! Transcluding payload tiddlers
|
||||||
|
|
||||||
|
This example shows how the `<$data>` widget can be transcluded from other tiddlers (see $:/plugins/tiddlywiki/innerwiki/example-data):
|
||||||
|
|
||||||
|
<<example """<$innerwiki width="600" height="400" style="width:100%;" filename="screenshot-3">
|
||||||
|
{{$:/plugins/tiddlywiki/innerwiki/example-data}}
|
||||||
|
<$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere"/>
|
||||||
|
</$innerwiki>""">>
|
||||||
|
|
||||||
|
!! Customising the wiki state
|
||||||
|
|
||||||
|
By injecting the right payload tiddlers, the innerwiki can be initialised to any desired state. In this example we inject a configuration tiddler to make the "more" page control button visible, and a state tiddler to cause the dropdown to appear:
|
||||||
|
|
||||||
|
<<example """<$innerwiki template="$:/plugins/tiddlywiki/innerwiki/template" filename="screenshot-4" width="1200" height="400" clipLeft="500" clipTop="100" clipWidth="600" clipHeight="300" style="width:100%;">
|
||||||
|
<$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere"/>
|
||||||
|
<$data title="$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/more-page-actions" text="show"/>
|
||||||
|
<$data title="$:/state/popup/more--1600698846" text="(151,144,21,25)"/>
|
||||||
|
</$innerwiki>""">>
|
||||||
|
|
||||||
|
!! Inception
|
||||||
|
|
||||||
|
An innerwiki can itself contain an inner-innerwiki:
|
||||||
|
|
||||||
|
<<example """<$innerwiki width="1200" height="600" style="width:100%;" filename="screenshot-5">
|
||||||
|
<$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere $:/plugins/tiddlywiki/innerwiki/inner-example"/>
|
||||||
|
<$data $tiddler="$:/plugins/tiddlywiki/innerwiki"/>
|
||||||
|
</$innerwiki>""">>
|
||||||
|
|
||||||
|
(You can see the innerwiki here: $:/plugins/tiddlywiki/innerwiki/inner-example)
|
||||||
|
|
||||||
|
Note the way that the innerwiki plugin has to be explicitly added to the innerwiki.
|
7
plugins/tiddlywiki/innerwiki/doc/inner-example.tid
Normal file
7
plugins/tiddlywiki/innerwiki/doc/inner-example.tid
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/inner-example
|
||||||
|
|
||||||
|
<$innerwiki width="1200" height="400" style="width:100%;">
|
||||||
|
<$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki that is inside a wiki"/>
|
||||||
|
<$data title="$:/DefaultTiddlers" text="HelloThere"/>
|
||||||
|
<$data title="$:/palette" text="$:/palettes/SolarFlare"/>
|
||||||
|
</$innerwiki>
|
25
plugins/tiddlywiki/innerwiki/doc/readme.tid
Normal file
25
plugins/tiddlywiki/innerwiki/doc/readme.tid
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/readme
|
||||||
|
|
||||||
|
!! Introduction
|
||||||
|
|
||||||
|
This plugin enables TiddlyWiki to embed a modified copy of itself (an "innerwiki"). The primary motivation is to be able to produce screenshot illustrations that are automatically up-to-date with the appearance of TiddlyWiki as it changes over time, or to produce the same screenshot in different languages.
|
||||||
|
|
||||||
|
In the browser, innerwikis are displayed as an embedded iframe. Under Node.js [[Google's Puppeteer|https://pptr.dev/]] is used to load the innerwikis as off-screen web pages and then snapshot them as a PNG image.
|
||||||
|
|
||||||
|
!! Warnings
|
||||||
|
|
||||||
|
The `<$innerwiki>` widget is relatively slow and resource intensive: each time it is refreshed it will entirely rebuild the iframe containing the innerwiki.
|
||||||
|
|
||||||
|
The `<$innerwiki>` widget does not automatically resize with its container (for example, try hiding the sidebar in this wiki).
|
||||||
|
|
||||||
|
!! Prequisites
|
||||||
|
|
||||||
|
In order to take screenshots under Node.js, Google Puppeteer must first be installed from [[npm|https://npmjs.org//]]. For most situations, it should be installed at the root of the TiddlyWiki 5 repo:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd Jermolene/TiddlyWiki5
|
||||||
|
npm install puppeteer
|
||||||
|
```
|
||||||
|
|
||||||
|
However, if you're working in a different repo that uses npm to install TiddlyWiki 5 then you should install Puppeteer into the same repo. The general rule is that the `node_modules` folder containing Puppeteer should be contained within an ancestor of the folder containing TiddlyWiki's `tiddlywiki.js` file.
|
||||||
|
|
256
plugins/tiddlywiki/innerwiki/innerwiki.js
Normal file
256
plugins/tiddlywiki/innerwiki/innerwiki.js
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/innerwiki/innerwiki.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: widget
|
||||||
|
|
||||||
|
Widget to display an innerwiki in an iframe
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var DEFAULT_INNERWIKI_TEMPLATE = "$:/plugins/tiddlywiki/innerwiki/template";
|
||||||
|
|
||||||
|
var Widget = require("$:/core/modules/widgets/widget.js").widget,
|
||||||
|
DataWidget = require("$:/plugins/tiddlywiki/innerwiki/data.js").data;
|
||||||
|
|
||||||
|
var InnerWikiWidget = function(parseTreeNode,options) {
|
||||||
|
this.initialise(parseTreeNode,options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inherit from the base widget class
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype = new Widget();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Render this widget into the DOM
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.render = function(parent,nextSibling) {
|
||||||
|
var self = this;
|
||||||
|
this.parentDomNode = parent;
|
||||||
|
this.computeAttributes();
|
||||||
|
this.execute();
|
||||||
|
// Create wrapper
|
||||||
|
var domWrapper = this.document.createElement("div");
|
||||||
|
var classes = (this.innerWikiClass || "").split(" ");
|
||||||
|
classes.push("tc-innerwiki-wrapper");
|
||||||
|
domWrapper.className = classes.join(" ");
|
||||||
|
domWrapper.style = this.innerWikiStyle;
|
||||||
|
// If we're on the real DOM, adjust the wrapper and iframe
|
||||||
|
if(!this.document.isTiddlyWikiFakeDom) {
|
||||||
|
domWrapper.style.overflow = "hidden";
|
||||||
|
domWrapper.style.position = "relative";
|
||||||
|
domWrapper.style.boxSizing = "content-box";
|
||||||
|
// Create iframe
|
||||||
|
var domIFrame = this.document.createElement("iframe");
|
||||||
|
domIFrame.className = "tc-innerwiki-iframe";
|
||||||
|
domIFrame.style.position = "absolute";
|
||||||
|
domIFrame.style.maxWidth = "none";
|
||||||
|
domIFrame.style.border = "none";
|
||||||
|
domIFrame.width = this.innerWikiWidth;
|
||||||
|
domIFrame.height = this.innerWikiHeight;
|
||||||
|
domWrapper.appendChild(domIFrame);
|
||||||
|
}
|
||||||
|
// Insert wrapper into the DOM
|
||||||
|
parent.insertBefore(domWrapper,nextSibling);
|
||||||
|
this.renderChildren(domWrapper,null);
|
||||||
|
this.domNodes.push(domWrapper);
|
||||||
|
// If we're on the real DOM, finish the initialisation that needs us to be in the DOM
|
||||||
|
if(!this.document.isTiddlyWikiFakeDom) {
|
||||||
|
// Write the HTML
|
||||||
|
domIFrame.contentWindow.document.open();
|
||||||
|
domIFrame.contentWindow.document.write(this.createInnerHTML());
|
||||||
|
domIFrame.contentWindow.document.close();
|
||||||
|
// Scale the iframe and adjust the height of the wrapper
|
||||||
|
var clipLeft = self.innerWikiClipLeft,
|
||||||
|
clipTop = self.innerWikiClipTop,
|
||||||
|
clipWidth = self.innerWikiClipWidth,
|
||||||
|
clipHeight = self.innerWikiClipHeight,
|
||||||
|
translateX = -clipLeft,
|
||||||
|
translateY = -clipTop,
|
||||||
|
scale = domWrapper.clientWidth / clipWidth;
|
||||||
|
domIFrame.style.transformOrigin = (-translateX) + "px " + (-translateY) + "px";
|
||||||
|
domIFrame.style.transform = "translate(" + translateX + "px," + translateY + "px) scale(" + scale + ")";
|
||||||
|
domWrapper.style.height = (clipHeight * scale) + "px";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create the HTML of the innerwiki
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.createInnerHTML = function() {
|
||||||
|
// Get the HTML of the iframe
|
||||||
|
var html = this.wiki.renderTiddler("text/plain",this.innerWikiTemplate);
|
||||||
|
// Insert the overlay tiddlers
|
||||||
|
var SPLIT_MARKER = "<!--~~ Boot" + " kernel ~~-->\n",
|
||||||
|
IMPLANT_PREFIX = "<" + "script>\n$tw.preloadTiddlerArray(",
|
||||||
|
IMPLANT_SUFFIX = ");\n</" + "script>\n",
|
||||||
|
parts = html.split(SPLIT_MARKER),
|
||||||
|
tiddlers = this.findDataWidgets(this.children);
|
||||||
|
if(parts.length === 2) {
|
||||||
|
html = parts[0] + IMPLANT_PREFIX + JSON.stringify(tiddlers) + IMPLANT_SUFFIX + SPLIT_MARKER + parts[1];
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find the child data widgets
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.findDataWidgets = function(children) {
|
||||||
|
var self = this,
|
||||||
|
results = [];
|
||||||
|
$tw.utils.each(children,function(child) {
|
||||||
|
if(child instanceof DataWidget) {
|
||||||
|
var item = Object.create(null);
|
||||||
|
$tw.utils.each(child.attributes,function(value,name) {
|
||||||
|
item[name] = value;
|
||||||
|
});
|
||||||
|
Array.prototype.push.apply(results,self.readDataWidget(child));
|
||||||
|
}
|
||||||
|
if(child.children) {
|
||||||
|
results = results.concat(self.findDataWidgets(child.children));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read the value(s) from a data widget
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.readDataWidget = function(dataWidget) {
|
||||||
|
// Start with a blank object
|
||||||
|
var item = Object.create(null);
|
||||||
|
// Read any attributes not prefixed with $
|
||||||
|
$tw.utils.each(dataWidget.attributes,function(value,name) {
|
||||||
|
if(name.charAt(0) !== "$") {
|
||||||
|
item[name] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Deal with $tiddler or $filter attributes
|
||||||
|
var titles;
|
||||||
|
if(dataWidget.hasAttribute("$tiddler")) {
|
||||||
|
titles = [dataWidget.getAttribute("$tiddler")];
|
||||||
|
} else if(dataWidget.hasAttribute("$filter")) {
|
||||||
|
titles = this.wiki.filterTiddlers(dataWidget.getAttribute("$filter"));
|
||||||
|
}
|
||||||
|
if(titles) {
|
||||||
|
var results = [];
|
||||||
|
$tw.utils.each(titles,function(title,index) {
|
||||||
|
var tiddler = $tw.wiki.getTiddler(title),
|
||||||
|
fields;
|
||||||
|
if(tiddler) {
|
||||||
|
fields = tiddler.getFieldStrings();
|
||||||
|
}
|
||||||
|
results.push($tw.utils.extend({},item,fields));
|
||||||
|
})
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
return [item];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute the internal state of the widget
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.execute = function() {
|
||||||
|
var parseStringAsNumber = function(num,defaultValue) {
|
||||||
|
num = parseInt(num + "",10);
|
||||||
|
if(!isNaN(num)) {
|
||||||
|
return num;
|
||||||
|
} else {
|
||||||
|
return parseInt(defaultValue + "",10);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Get our parameters
|
||||||
|
this.innerWikiTemplate = this.getAttribute("template",DEFAULT_INNERWIKI_TEMPLATE);
|
||||||
|
this.innerWikiWidth = parseStringAsNumber(this.getAttribute("width"),800);
|
||||||
|
this.innerWikiHeight = parseStringAsNumber(this.getAttribute("height"),600);
|
||||||
|
this.innerWikiStyle = this.getAttribute("style");
|
||||||
|
this.innerWikiClass = this.getAttribute("class");
|
||||||
|
this.innerWikiFilename = this.getAttribute("filename");
|
||||||
|
this.innerWikiClipLeft = parseStringAsNumber(this.getAttribute("clipLeft"),0);
|
||||||
|
this.innerWikiClipTop = parseStringAsNumber(this.getAttribute("clipTop"),0);
|
||||||
|
this.innerWikiClipWidth = parseStringAsNumber(this.getAttribute("clipWidth"),this.innerWikiWidth);
|
||||||
|
this.innerWikiClipHeight = parseStringAsNumber(this.getAttribute("clipHeight"),this.innerWikiHeight);
|
||||||
|
// Construct the child widgets
|
||||||
|
this.makeChildWidgets();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.refresh = function(changedTiddlers) {
|
||||||
|
var changedAttributes = this.computeAttributes();
|
||||||
|
if(changedAttributes.template || changedAttributes.width || changedAttributes.height || changedAttributes.style || changedAttributes.class) {
|
||||||
|
this.refreshSelf();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Use Puppeteer to save a screenshot to a file
|
||||||
|
*/
|
||||||
|
InnerWikiWidget.prototype.saveScreenshot = function(options,callback) {
|
||||||
|
var self = this,
|
||||||
|
basepath = options.basepath || ".",
|
||||||
|
deviceScaleFactor = options.deviceScaleFactor || 1;
|
||||||
|
// Don't do anything if we don't have a filename
|
||||||
|
if(!this.innerWikiFilename) {
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
var path = require("path"),
|
||||||
|
filepath = path.resolve(basepath,this.innerWikiFilename) + ".png";
|
||||||
|
$tw.utils.createFileDirectories(filepath);
|
||||||
|
console.log("Taking screenshot",filepath);
|
||||||
|
// Fire up Puppeteer
|
||||||
|
var puppeteer;
|
||||||
|
try {
|
||||||
|
puppeteer = require("puppeteer");
|
||||||
|
} catch(e) {
|
||||||
|
throw "Google Puppeteer not found";
|
||||||
|
}
|
||||||
|
// Take screenshots
|
||||||
|
puppeteer.launch().then(async browser => {
|
||||||
|
// NOTE: Copying Google's sample code by using new fangled promises "await"
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.setContent(self.createInnerHTML(),{
|
||||||
|
waitUntil: "domcontentloaded"
|
||||||
|
});
|
||||||
|
await page.setViewport({
|
||||||
|
width: Math.trunc(self.innerWikiWidth),
|
||||||
|
height: Math.trunc(self.innerWikiHeight),
|
||||||
|
deviceScaleFactor: deviceScaleFactor
|
||||||
|
});
|
||||||
|
// PDF generation isn't great: there's no clipping, and pagination is hard to control
|
||||||
|
// await page.emulateMedia("screen");
|
||||||
|
// await page.pdf({
|
||||||
|
// scale: 0.5,
|
||||||
|
// width: self.innerWikiWidth + "px",
|
||||||
|
// height: self.innerWikiHeight + "px",
|
||||||
|
// path: filepath + ".pdf",
|
||||||
|
// printBackground: true
|
||||||
|
// });
|
||||||
|
await page.screenshot({
|
||||||
|
path: filepath,
|
||||||
|
clip: {
|
||||||
|
x: self.innerWikiClipLeft,
|
||||||
|
y: self.innerWikiClipTop,
|
||||||
|
width: self.innerWikiClipWidth,
|
||||||
|
height: self.innerWikiClipHeight
|
||||||
|
},
|
||||||
|
type: "png"
|
||||||
|
});
|
||||||
|
await browser.close();
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.innerwiki = InnerWikiWidget;
|
||||||
|
|
||||||
|
})();
|
7
plugins/tiddlywiki/innerwiki/plugin.info
Normal file
7
plugins/tiddlywiki/innerwiki/plugin.info
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"title": "$:/plugins/tiddlywiki/innerwiki",
|
||||||
|
"description": "Innerwiki: programmable screenshots",
|
||||||
|
"author": "Jeremy Ruston",
|
||||||
|
"plugin-type": "plugin",
|
||||||
|
"list": "readme docs examples"
|
||||||
|
}
|
83
plugins/tiddlywiki/innerwiki/screenshot.js
Normal file
83
plugins/tiddlywiki/innerwiki/screenshot.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/innerwiki/screenshot.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: command
|
||||||
|
|
||||||
|
Commands to render tiddlers identified by a filter and save any screenshots identified by <$innerwiki> widgets
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var InnerWikiWidget = require("$:/plugins/tiddlywiki/innerwiki/innerwiki.js").innerwiki;
|
||||||
|
|
||||||
|
exports.info = {
|
||||||
|
name: "screenshot",
|
||||||
|
synchronous: false
|
||||||
|
};
|
||||||
|
|
||||||
|
var Command = function(params,commander,callback) {
|
||||||
|
this.params = params;
|
||||||
|
this.commander = commander;
|
||||||
|
this.callback = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command.prototype.execute = function() {
|
||||||
|
var self = this;
|
||||||
|
if(this.params.length < 1) {
|
||||||
|
return "Missing filter";
|
||||||
|
}
|
||||||
|
var filter = this.params[0],
|
||||||
|
deviceScaleFactor = parseInt(this.params[1],10) || 1,
|
||||||
|
tiddlers = this.commander.wiki.filterTiddlers(filter);
|
||||||
|
// Render each tiddler into a widget tree
|
||||||
|
var innerWikiWidgets = [];
|
||||||
|
$tw.utils.each(tiddlers,function(title) {
|
||||||
|
var parser = self.commander.wiki.parseTiddler(title),
|
||||||
|
variables = {currentTiddler: title},
|
||||||
|
widgetNode = self.commander.wiki.makeWidget(parser,{variables: variables}),
|
||||||
|
container = $tw.fakeDocument.createElement("div");
|
||||||
|
widgetNode.render(container,null);
|
||||||
|
// Find any innerwiki widgets
|
||||||
|
Array.prototype.push.apply(innerWikiWidgets,self.findInnerWikiWidgets(widgetNode));
|
||||||
|
});
|
||||||
|
// Asynchronously tell each innerwiki widget to save a screenshot
|
||||||
|
var processNextInnerWikiWidget = function() {
|
||||||
|
if(innerWikiWidgets.length > 0) {
|
||||||
|
var widget = innerWikiWidgets[0];
|
||||||
|
innerWikiWidgets.shift();
|
||||||
|
widget.saveScreenshot({
|
||||||
|
basepath: self.commander.outputPath,
|
||||||
|
deviceScaleFactor: deviceScaleFactor
|
||||||
|
},function(err) {
|
||||||
|
if(err) {
|
||||||
|
self.callback(err);
|
||||||
|
}
|
||||||
|
processNextInnerWikiWidget();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.callback(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
processNextInnerWikiWidget();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command.prototype.findInnerWikiWidgets = function(widgetNode) {
|
||||||
|
var self = this,
|
||||||
|
results = [];
|
||||||
|
if(widgetNode.saveScreenshot) {
|
||||||
|
results.push(widgetNode)
|
||||||
|
}
|
||||||
|
$tw.utils.each(widgetNode.children,function(childWidget) {
|
||||||
|
Array.prototype.push.apply(results,self.findInnerWikiWidgets(childWidget));
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Command = Command;
|
||||||
|
|
||||||
|
})();
|
9
plugins/tiddlywiki/innerwiki/styles.tid
Normal file
9
plugins/tiddlywiki/innerwiki/styles.tid
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/styles
|
||||||
|
tags: [[$:/tags/Stylesheet]]
|
||||||
|
|
||||||
|
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline
|
||||||
|
|
||||||
|
.tc-innerwiki-wrapper {
|
||||||
|
border: 1px solid #666;
|
||||||
|
<<box-shadow "2px 2px 5px rgba(0, 0, 0, 0.5)">>
|
||||||
|
}
|
14
plugins/tiddlywiki/innerwiki/template.tid
Normal file
14
plugins/tiddlywiki/innerwiki/template.tid
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/innerwiki/template
|
||||||
|
|
||||||
|
\define saveTiddlerFilter()
|
||||||
|
$:/boot/boot.css
|
||||||
|
$:/boot/boot.js
|
||||||
|
$:/boot/bootprefix.js
|
||||||
|
$:/core
|
||||||
|
$:/library/sjcl.js
|
||||||
|
$:/plugins/tiddlywiki/innerwiki
|
||||||
|
$:/plugins/tiddlywiki/railroad
|
||||||
|
$:/themes/tiddlywiki/snowwhite
|
||||||
|
$:/themes/tiddlywiki/vanilla
|
||||||
|
\end
|
||||||
|
{{$:/core/templates/tiddlywiki5.html}}
|
Loading…
Reference in New Issue
Block a user