diff --git a/core/modules/utils/fakedom.js b/core/modules/utils/fakedom.js index 4a756f43d..3100e731d 100755 --- a/core/modules/utils/fakedom.js +++ b/core/modules/utils/fakedom.js @@ -45,10 +45,28 @@ var TW_Element = function(tag,namespace) { this.attributes = {}; this.isRaw = false; this.children = []; - this.style = {}; + this._style = {}; this.namespaceURI = namespace || "http://www.w3.org/1999/xhtml"; }; +Object.defineProperty(TW_Element.prototype, "style", { + get: function() { + return this._style; + }, + set: function(str) { + var self = this; + str = str || ""; + $tw.utils.each(str.split(";"),function(declaration) { + var parts = declaration.split(":"), + name = $tw.utils.trim(parts[0]), + value = $tw.utils.trim(parts[1]); + if(name && value) { + self._style[$tw.utils.convertStyleNameToPropertyName(name)] = value; + } + }); + } +}); + Object.defineProperty(TW_Element.prototype, "nodeType", { get: function() { return 1; @@ -169,13 +187,13 @@ Object.defineProperty(TW_Element.prototype, "outerHTML", { } } } - if(this.style) { + if(this._style) { var style = []; - for(var s in this.style) { - style.push(s + ":" + this.style[s] + ";"); + for(var s in this._style) { + style.push($tw.utils.convertPropertyNameToStyleName(s) + ":" + this._style[s] + ";"); } if(style.length > 0) { - output.push(" style=\"",style.join(""),"\"") + output.push(" style=\"",style.join(""),"\""); } } output.push(">"); diff --git a/editions/innerwikidemo/tiddlers/HelloThere.tid b/editions/innerwikidemo/tiddlers/HelloThere.tid index 84a0ad99f..9b85ae530 100644 --- a/editions/innerwikidemo/tiddlers/HelloThere.tid +++ b/editions/innerwikidemo/tiddlers/HelloThere.tid @@ -8,5 +8,7 @@ To try these examples under Node.js: # Execute the following command in the root of the TiddlyWiki 5 repo: ``` -./tiddlywiki.js editions/innerwikidemo --screenshot '[[$:/plugins/tiddlywiki/innerwiki/examples]]' 4 +./tiddlywiki.js editions/innerwikidemo --screenshot '[[$:/plugins/tiddlywiki/innerwiki/examples]]' 4 --render '[[$:/plugins/tiddlywiki/innerwiki/examples]]' "examples.html" ``` + +Open `examples.html` to see the generated static HTML rendering. diff --git a/plugins/tiddlywiki/innerwiki/doc/examples.tid b/plugins/tiddlywiki/innerwiki/doc/examples.tid index 64fbc8888..f1dab0213 100644 --- a/plugins/tiddlywiki/innerwiki/doc/examples.tid +++ b/plugins/tiddlywiki/innerwiki/doc/examples.tid @@ -1,5 +1,11 @@ title: $:/plugins/tiddlywiki/innerwiki/examples +\define big-arrow(x,y,colour:"#ff0000",border:"#000000") + + + +\end + \define example(text) <$codeblock code=<<__text__>>/> @@ -12,10 +18,10 @@ $text$ 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": -< +<$macrocall $name="example" text="""<$innerwiki width="1200" height="400" style="width:100%;" filename="screenshot-1.png"> <$data title="HelloThere" text="This tiddler is inside a wiki"/> <$data title="$:/DefaultTiddlers" text="HelloThere"/> -""">> +"""/> Note that the "screenshot" is a shrunken but fully interactive TiddlyWiki. @@ -24,50 +30,70 @@ Note that the "screenshot" is a shrunken but fully interactive TiddlyWiki. 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 +tiddlywiki editions/innerwikidemo --screenshot $:/plugins/tiddlywiki/innerwiki/examples ``` The screenshots will be saved as `screenshot-1.png` etc in the `./output` folder of the wiki. +To render this example tiddler as a static HTML file that embeds the screenshot images and includes the SVG overlays: + +``` +tiddlywiki editions/innerwikidemo --render '[[$:/plugins/tiddlywiki/innerwiki/examples]]' "examples.html" --build index +``` + +!! SVG overlays + +Any displayable content within innerwiki widget is displayed within an automatically created SVG element. This allows overlays to be added: + +<$macrocall $name="example" text="""<$innerwiki width="1200" height="400" style="width:100%;" filename="screenshot-2.png"> + <$data title="HelloThere" text="This tiddler is inside a wiki"/> + <$data title="$:/DefaultTiddlers" text="HelloThere"/> + + <> +"""/> + +Notice how macros can be used to encapsulate SVG fragments ([[see the source of this tiddler|$:/plugins/tiddlywiki/innerwiki/examples]]). + !! Clipping A clipping rectangle can be applied to limit the area of the wiki that is displayed. For example: -< +<$macrocall $name="example" text="""<$innerwiki width="1200" height="400" style="width:100%;" clipLeft="500" clipTop="100" clipWidth="600" clipHeight="300" filename="screenshot-3.png"> <$data title="HelloThere" text="! This tiddler is inside a wiki that is inside a wiki"/> <$data title="$:/DefaultTiddlers" text="HelloThere"/> -""">> +"""/> !! Transcluding payload tiddlers This example shows how the `<$data>` widget can be transcluded from other tiddlers (see $:/plugins/tiddlywiki/innerwiki/example-data): -< +<$macrocall $name="example" text="""<$innerwiki width="600" height="400" style="width:100%;" filename="screenshot-4.png"> {{$:/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"/> -""">> +"""/> !! 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: -< +<$macrocall $name="example" text="""<$innerwiki template="$:/plugins/tiddlywiki/innerwiki/template" filename="screenshot-5.png" 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)"/> -""">> +"""/> !! Inception An innerwiki can itself contain an inner-innerwiki: -< +<$macrocall $name="example" text="""<$innerwiki width="1200" height="600" style="width:100%;" filename="screenshot-6.png"> <$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"/> -""">> + <> +"""/> (You can see the innerwiki here: $:/plugins/tiddlywiki/innerwiki/inner-example) diff --git a/plugins/tiddlywiki/innerwiki/innerwiki.js b/plugins/tiddlywiki/innerwiki/innerwiki.js index 7c1cdc629..05c4a148a 100644 --- a/plugins/tiddlywiki/innerwiki/innerwiki.js +++ b/plugins/tiddlywiki/innerwiki/innerwiki.js @@ -40,11 +40,19 @@ InnerWikiWidget.prototype.render = function(parent,nextSibling) { classes.push("tc-innerwiki-wrapper"); domWrapper.className = classes.join(" "); domWrapper.style = this.innerWikiStyle; + domWrapper.style.overflow = "hidden"; + domWrapper.style.position = "relative"; + domWrapper.style.boxSizing = "content-box"; + // Set up the SVG container + var domSVG = this.document.createElementNS("http://www.w3.org/2000/svg","svg"); + domSVG.style = this.innerWikiStyle; + domSVG.style.position = "absolute"; + domSVG.style.zIndex = "1"; + domSVG.setAttribute("viewBox","0 0 " + this.innerWikiClipWidth + " " + this.innerWikiClipHeight); + domWrapper.appendChild(domSVG); + this.setVariable("namespace","http://www.w3.org/2000/svg"); // 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"; @@ -54,10 +62,16 @@ InnerWikiWidget.prototype.render = function(parent,nextSibling) { domIFrame.width = this.innerWikiWidth; domIFrame.height = this.innerWikiHeight; domWrapper.appendChild(domIFrame); + } else { + // Create image placeholder + var domImage = this.document.createElement("img"); + domImage.style = this.innerWikiStyle; + domImage.setAttribute("src",this.innerWikiFilename); + domWrapper.appendChild(domImage); } // Insert wrapper into the DOM parent.insertBefore(domWrapper,nextSibling); - this.renderChildren(domWrapper,null); + this.renderChildren(domSVG,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) { @@ -65,14 +79,16 @@ InnerWikiWidget.prototype.render = function(parent,nextSibling) { 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; + } + // Scale the iframe and adjust the height of the wrapper + var clipLeft = this.innerWikiClipLeft, + clipTop = this.innerWikiClipTop, + clipWidth = this.innerWikiClipWidth, + clipHeight = this.innerWikiClipHeight, + translateX = -clipLeft, + translateY = -clipTop, + scale = domWrapper.clientWidth / clipWidth; + if(!this.document.isTiddlyWikiFakeDom) { domIFrame.style.transformOrigin = (-translateX) + "px " + (-translateY) + "px"; domIFrame.style.transform = "translate(" + translateX + "px," + translateY + "px) scale(" + scale + ")"; domWrapper.style.height = (clipHeight * scale) + "px"; @@ -205,7 +221,7 @@ InnerWikiWidget.prototype.saveScreenshot = function(options,callback) { return callback(null); } var path = require("path"), - filepath = path.resolve(basepath,this.innerWikiFilename) + ".png"; + filepath = path.resolve(basepath,this.innerWikiFilename); $tw.utils.createFileDirectories(filepath); console.log("Taking screenshot",filepath); // Fire up Puppeteer