mirror of
https://github.com/TiddlyWiki/TiddlyWiki5.git
synced 2026-05-03 02:27:03 +00:00
Innerwiki: Add support for draggable anchors
This commit is contained in:
@@ -15,7 +15,8 @@ Widget to display an innerwiki in an iframe
|
||||
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;
|
||||
DataWidget = require("$:/plugins/tiddlywiki/innerwiki/data.js").data,
|
||||
dm = $tw.utils.domMaker;
|
||||
|
||||
var InnerWikiWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
@@ -35,64 +36,189 @@ InnerWikiWidget.prototype.render = function(parent,nextSibling) {
|
||||
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;
|
||||
domWrapper.style.overflow = "hidden";
|
||||
domWrapper.style.position = "relative";
|
||||
domWrapper.style.boxSizing = "content-box";
|
||||
this.domWrapper = dm("div",{
|
||||
document: this.document,
|
||||
"class": (this.innerWikiClass || "").split(" ").concat(["tc-innerwiki-wrapper"]).join(" "),
|
||||
style: {
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
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.style.pointerEvents = "none";
|
||||
domSVG.setAttribute("viewBox","0 0 " + this.innerWikiClipWidth + " " + this.innerWikiClipHeight);
|
||||
domWrapper.appendChild(domSVG);
|
||||
this.domSVG = dm("svg",{
|
||||
namespace: "http://www.w3.org/2000/svg",
|
||||
document: this.document,
|
||||
style: {
|
||||
width: "100%",
|
||||
position: "absolute",
|
||||
zIndex: "1",
|
||||
pointerEvents: "none"
|
||||
},
|
||||
attributes: {
|
||||
"viewBox": "0 0 " + this.innerWikiClipWidth + " " + this.innerWikiClipHeight
|
||||
}
|
||||
});
|
||||
this.domWrapper.appendChild(this.domSVG);
|
||||
this.setVariable("namespace","http://www.w3.org/2000/svg");
|
||||
// If we're on the real DOM, adjust the wrapper and iframe
|
||||
// Create the iframe for the browser or image for Node.js
|
||||
if(!this.document.isTiddlyWikiFakeDom) {
|
||||
// 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);
|
||||
this.domIFrame = dm("iframe",{
|
||||
document: this.document,
|
||||
"class": "tc-innerwiki-iframe",
|
||||
style: {
|
||||
position: "absolute",
|
||||
maxWidth: "none",
|
||||
border: "none"
|
||||
},
|
||||
attributes: {
|
||||
width: this.innerWikiWidth,
|
||||
height: this.innerWikiHeight
|
||||
}
|
||||
});
|
||||
this.domWrapper.appendChild(this.domIFrame);
|
||||
} else {
|
||||
// Create image placeholder
|
||||
var domImage = this.document.createElement("img");
|
||||
domImage.style = this.innerWikiStyle;
|
||||
domImage.setAttribute("src",this.innerWikiFilename);
|
||||
domWrapper.appendChild(domImage);
|
||||
this.domImage = dm("img",{
|
||||
document: this.document,
|
||||
style: {
|
||||
width: "100%"
|
||||
},
|
||||
attributes: {
|
||||
src: this.innerWikiFilename
|
||||
}
|
||||
});
|
||||
this.domWrapper.appendChild(this.domImage);
|
||||
}
|
||||
// Insert wrapper into the DOM
|
||||
parent.insertBefore(domWrapper,nextSibling);
|
||||
this.renderChildren(domSVG,null);
|
||||
this.domNodes.push(domWrapper);
|
||||
parent.insertBefore(this.domWrapper,nextSibling);
|
||||
this.renderChildren(this.domSVG,null);
|
||||
this.domNodes.push(this.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();
|
||||
this.domIFrame.contentDocument.open();
|
||||
this.domIFrame.contentDocument.write(this.createInnerHTML());
|
||||
this.domIFrame.contentDocument.close();
|
||||
}
|
||||
// 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;
|
||||
this.clipLeft = this.innerWikiClipLeft;
|
||||
this.clipTop = this.innerWikiClipTop;
|
||||
this.clipWidth = this.innerWikiClipWidth;
|
||||
this.clipHeight = this.innerWikiClipHeight;
|
||||
this.scale = this.domWrapper.clientWidth / this.clipWidth;
|
||||
// Display the anchors
|
||||
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";
|
||||
this.domAnchorContainer = dm("div",{
|
||||
document: this.document,
|
||||
style: {
|
||||
position: "relative",
|
||||
zIndex: "2",
|
||||
transformOrigin: "0 0",
|
||||
transform: "scale(" + this.scale + ")"
|
||||
}
|
||||
});
|
||||
this.domAnchorBackdrop = dm("div",{
|
||||
document: this.document,
|
||||
style: {
|
||||
position: "absolute",
|
||||
display: "none"
|
||||
}
|
||||
});
|
||||
this.domAnchorContainer.appendChild(this.domAnchorBackdrop);
|
||||
this.domWrapper.insertBefore(this.domAnchorContainer,this.domWrapper.firstChild);
|
||||
self.createAnchors();
|
||||
}
|
||||
// Scale the iframe and adjust the height of the wrapper
|
||||
if(!this.document.isTiddlyWikiFakeDom) {
|
||||
this.domIFrame.style.transformOrigin = this.clipLeft + "px " + this.clipTop + "px";
|
||||
this.domIFrame.style.transform = "translate(" + (-this.clipLeft) + "px," + (-this.clipTop) + "px) scale(" + this.scale + ")";
|
||||
this.domWrapper.style.height = (this.clipHeight * this.scale) + "px";
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Create the anchors
|
||||
*/
|
||||
InnerWikiWidget.prototype.createAnchors = function() {
|
||||
var self = this;
|
||||
this.findDataWidgets(this.children,"anchor",function(widget) {
|
||||
var anchorWidth = 40,
|
||||
anchorHeight = 40,
|
||||
getAnchorCoordinate = function(name) {
|
||||
return parseInt(self.wiki.getTiddlerText(widget.getAttribute(name)),10) || 0;
|
||||
},
|
||||
setAnchorCoordinate = function(name,value) {
|
||||
self.wiki.addTiddler({
|
||||
title: widget.getAttribute(name),
|
||||
text: value + ""
|
||||
});
|
||||
},
|
||||
domAnchor = dm("img",{
|
||||
document: self.document,
|
||||
style: {
|
||||
position: "absolute",
|
||||
width: anchorWidth + "px",
|
||||
height: anchorHeight + "px",
|
||||
transformOrigin: "50% 50%",
|
||||
transform: "scale(" + (1 / self.scale) + ")",
|
||||
left: (getAnchorCoordinate("x") - anchorWidth / 2) + "px",
|
||||
top: (getAnchorCoordinate("y") - anchorHeight / 2) + "px"
|
||||
},
|
||||
attributes: {
|
||||
draggable: false,
|
||||
src: "data:image/svg+xml," + encodeURIComponent(self.wiki.getTiddlerText("$:/plugins/tiddlywiki/innerwiki/crosshairs.svg"))
|
||||
}
|
||||
});
|
||||
self.domAnchorContainer.appendChild(domAnchor);
|
||||
var posX,posY,dragStartX,dragStartY,deltaX,deltaY,
|
||||
fnMouseDown = function(event) {
|
||||
self.domAnchorBackdrop.style.width = self.clipWidth + "px";
|
||||
self.domAnchorBackdrop.style.height = self.clipHeight + "px";
|
||||
self.domAnchorBackdrop.style.display = "block";
|
||||
posX = domAnchor.offsetLeft;
|
||||
posY = domAnchor.offsetTop;
|
||||
dragStartX = event.clientX;
|
||||
dragStartY = event.clientY;
|
||||
deltaX = 0;
|
||||
deltaY = 0;
|
||||
self.document.addEventListener("mousemove",fnMouseMove,false);
|
||||
self.document.addEventListener("mouseup",fnMouseUp,false);
|
||||
},
|
||||
fnMouseMove = function(event) {
|
||||
deltaX = (event.clientX - dragStartX) / self.scale;
|
||||
deltaY = (event.clientY - dragStartY) / self.scale;
|
||||
domAnchor.style.left = (posX + deltaX) + "px";
|
||||
domAnchor.style.top = (posY + deltaY) + "px";
|
||||
},
|
||||
fnMouseUp = function(event) {
|
||||
var x = getAnchorCoordinate("x") + deltaX,
|
||||
y = getAnchorCoordinate("y") + deltaY;
|
||||
if(x >= 0 && x < self.clipWidth && y >= 0 && y < self.clipHeight) {
|
||||
setAnchorCoordinate("x",x);
|
||||
setAnchorCoordinate("y",y);
|
||||
} else {
|
||||
domAnchor.style.left = posX + "px";
|
||||
domAnchor.style.top = posY + "px";
|
||||
}
|
||||
self.domAnchorBackdrop.style.display = "none";
|
||||
self.document.removeEventListener("mousemove",fnMouseMove,false);
|
||||
self.document.removeEventListener("mouseup",fnMouseUp,false);
|
||||
};
|
||||
domAnchor.addEventListener("mousedown",fnMouseDown,false);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Delete the anchors
|
||||
*/
|
||||
InnerWikiWidget.prototype.deleteAnchors = function() {
|
||||
for(var index=this.domAnchorContainer.childNodes.length-1; index>=0; index--) {
|
||||
var node = this.domAnchorContainer.childNodes[index];
|
||||
if(node.tagName === "IMG") {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,7 +233,7 @@ InnerWikiWidget.prototype.createInnerHTML = function() {
|
||||
IMPLANT_PREFIX = "<" + "script>\n$tw.preloadTiddlerArray(",
|
||||
IMPLANT_SUFFIX = ");\n</" + "script>\n",
|
||||
parts = html.split(SPLIT_MARKER),
|
||||
tiddlers = this.findDataWidgets(this.children);
|
||||
tiddlers = this.readTiddlerDataWidgets(this.children);
|
||||
if(parts.length === 2) {
|
||||
html = parts[0] + IMPLANT_PREFIX + JSON.stringify(tiddlers) + IMPLANT_SUFFIX + SPLIT_MARKER + parts[1];
|
||||
}
|
||||
@@ -115,30 +241,36 @@ InnerWikiWidget.prototype.createInnerHTML = function() {
|
||||
};
|
||||
|
||||
/*
|
||||
Find the child data widgets
|
||||
Find child data widgets
|
||||
*/
|
||||
InnerWikiWidget.prototype.findDataWidgets = function(children) {
|
||||
var self = this,
|
||||
results = [];
|
||||
InnerWikiWidget.prototype.findDataWidgets = function(children,tag,callback) {
|
||||
var self = this;
|
||||
$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.dataWidgetTag === tag) {
|
||||
callback(child);
|
||||
}
|
||||
if(child.children) {
|
||||
results = results.concat(self.findDataWidgets(child.children));
|
||||
self.findDataWidgets(child.children,tag,callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Find the child data widgets
|
||||
*/
|
||||
InnerWikiWidget.prototype.readTiddlerDataWidgets = function(children) {
|
||||
var self = this,
|
||||
results = [];
|
||||
this.findDataWidgets(children,"data",function(widget) {
|
||||
Array.prototype.push.apply(results,self.readTiddlerDataWidget(widget));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Read the value(s) from a data widget
|
||||
*/
|
||||
InnerWikiWidget.prototype.readDataWidget = function(dataWidget) {
|
||||
InnerWikiWidget.prototype.readTiddlerDataWidget = function(dataWidget) {
|
||||
// Start with a blank object
|
||||
var item = Object.create(null);
|
||||
// Read any attributes not prefixed with $
|
||||
@@ -206,7 +338,12 @@ InnerWikiWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
var childrenRefreshed = this.refreshChildren(changedTiddlers);
|
||||
if(childrenRefreshed) {
|
||||
this.deleteAnchors();
|
||||
this.createAnchors();
|
||||
}
|
||||
return childrenRefreshed
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user