1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-19 10:49:43 +00:00
TiddlyWiki5/core/modules/macros/story/views/zoomin.js
2012-06-22 18:02:50 +01:00

209 lines
8.5 KiB
JavaScript

/*\
title: $:/core/modules/macros/story/views/zoomin.js
type: application/javascript
module-type: storyview
A storyview that shows a single tiddler and navigates by zooming into links
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false, Node: false */
"use strict";
function Zoomin(story) {
// Save the story
this.story = story;
var wrapper = this.story.child.children[1].domNode;
// Make all the tiddlers position absolute, and hide all but the first one
wrapper.style.position = "relative";
for(var t=0; t<wrapper.children.length; t++) {
if(t) {
wrapper.children[t].style.display = "none";
}
wrapper.children[t].style.position = "absolute";
}
// Record the current tiddler node
this.currTiddler = this.story.child.children[1].children[0];
// Set up the stack of previously viewed tiddlers
this.prevTiddlers = [this.currTiddler.children[0].params.target];
}
/*
Find the first descendent node that is a <<view title>> macro
*/
function findTitleNode(node) {
var r;
if(node.macroName && node.macroName === "view" && node.params && node.params.field && node.params.field === "title") {
return node;
}
if(node.children) {
for(var t=0; t<node.children.length; t++) {
r = findTitleNode(node.children[t]);
if(r) {
return r;
}
}
} else if(node.child) {
r = findTitleNode(node.child);
if(r) {
return r;
}
}
return null;
}
/*
Given a tree node find the bounding rectange of its first child element
*/
function getNodeBounds(node) {
if(node && node.domNode) {
if(node.domNode.nodeType === Node.TEXT_NODE) {
return node.domNode.parentNode.getBoundingClientRect();
} else {
return node.domNode.getBoundingClientRect();
}
} else {
return getNodeBounds(node.child);
}
}
/*
Visualise navigation to the specified tiddler macro, optionally specifying a source node for the visualisation
targetTiddlerNode: tree node of the tiddler macro we're navigating to
isNew: true if the node we're navigating to has just been added to the DOM
sourceNode: optional tree node that initiated the navigation
*/
Zoomin.prototype.navigate = function(targetTiddlerNode,isNew,sourceEvent) {
// Do nothing if the target tiddler is already the current tiddler
if(targetTiddlerNode === this.currTiddler) {
return;
}
// Make the new tiddler be position absolute and visible
targetTiddlerNode.domNode.style.position = "absolute";
targetTiddlerNode.domNode.style.display = "block";
targetTiddlerNode.domNode.style[$tw.browser.transformorigin] = "0 0";
targetTiddlerNode.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
targetTiddlerNode.domNode.style[$tw.browser.transition] = "none";
// Get the position of the source node, or use the centre of the window as the source position
var sourceBounds;
if(sourceEvent && sourceEvent.navigateFrom) {
sourceBounds = getNodeBounds(sourceEvent.navigateFrom);
} else {
sourceBounds = {
left: window.innerWidth/2 - 2,
top: window.innerHeight/2 - 2,
width: 4,
height: 4
};
}
// Try to find the title node in the target tiddler
var titleNode = findTitleNode(targetTiddlerNode) || targetTiddlerNode;
// Compute the transform for the target tiddler to make the title lie over the source rectange
var targetBounds = getNodeBounds(targetTiddlerNode),
titleBounds = getNodeBounds(titleNode),
scale = sourceBounds.width / titleBounds.width,
x = sourceBounds.left - targetBounds.left - (titleBounds.left - targetBounds.left) * scale,
y = sourceBounds.top - targetBounds.top - (titleBounds.top - targetBounds.top) * scale;
// Transform the target tiddler
targetTiddlerNode.domNode.style[$tw.browser.transform] = "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")";
// Get the animation duration
var d = ($tw.config.preferences.animationDuration/1000).toFixed(8) + "s";
// Apply the ending transitions with a timeout to ensure that the previously applied transformations are applied first
var self = this,
currTiddler = this.currTiddler;
$tw.utils.nextTick(function() {
// Transform the target tiddler
var currTiddlerBounds = getNodeBounds(currTiddler),
x = (currTiddlerBounds.left - targetBounds.left),
y = (currTiddlerBounds.top - targetBounds.top);
targetTiddlerNode.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
targetTiddlerNode.domNode.style.opacity = "1.0";
targetTiddlerNode.domNode.style[$tw.browser.transform] = "translateX(" + x + "px) translateY(" + y + "px) scale(1)";
targetTiddlerNode.domNode.style.zIndex = "500";
// Transform the current tiddler
var scale = titleBounds.width / sourceBounds.width;
x = titleBounds.left - targetBounds.left - (sourceBounds.left - currTiddlerBounds.left) * scale;
y = titleBounds.top - targetBounds.top - (sourceBounds.top - currTiddlerBounds.top) * scale;
currTiddler.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
currTiddler.domNode.style.opacity = "0.0";
currTiddler.domNode.style[$tw.browser.transformorigin] = "0 0";
currTiddler.domNode.style[$tw.browser.transform] = "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")";
currTiddler.domNode.style.zIndex = "0";
});
// Record the new current tiddler
this.currTiddler = targetTiddlerNode;
// Save the tiddler in the stack
this.prevTiddlers.push(targetTiddlerNode.children[0].params.target);
};
/*
Visualise closing a tiddler
*/
Zoomin.prototype.close = function(targetTiddlerNode,sourceEvent) {
// Remove the last entry from the navigation stack, which will be to navigate to the current tiddler
this.prevTiddlers.pop();
// Find the top entry in the navigation stack that still exists
var storyElementIndex,storyElement;
while(this.prevTiddlers.length > 0) {
var title = this.prevTiddlers.pop();
storyElementIndex = this.story.findStoryElementByTitle(0,title);
if(storyElementIndex !== undefined) {
break;
}
}
if(storyElementIndex !== undefined) {
storyElement = this.story.storyNode.children[storyElementIndex];
}
// Get the animation duration
var d = ($tw.config.preferences.animationDuration/1000).toFixed(8) + "s";
// Set up the tiddler that is being closed
targetTiddlerNode.domNode.style.position = "absolute";
targetTiddlerNode.domNode.style.display = "block";
targetTiddlerNode.domNode.style[$tw.browser.transformorigin] = "50% 50%";
targetTiddlerNode.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
targetTiddlerNode.domNode.style[$tw.browser.transition] = "none";
targetTiddlerNode.domNode.style.zIndex = "0";
// Set up the tiddler we're moving back in
if(storyElement !== undefined) {
storyElement.domNode.style.position = "absolute";
storyElement.domNode.style.display = "block";
storyElement.domNode.style[$tw.browser.transformorigin] = "50% 50%";
storyElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(10)";
storyElement.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
storyElement.domNode.style.opacity = "0.0";
storyElement.domNode.style.zIndex = "500";
// Push the tiddler we're moving back to back on the stack
this.prevTiddlers.push(storyElement.children[0].params.target);
this.currTiddler = storyElement;
} else {
this.currTiddler = null;
}
// Animate them both
$tw.utils.nextTick(function() {
// First, the tiddler we're closing
targetTiddlerNode.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
targetTiddlerNode.domNode.style.opacity = "0.0";
targetTiddlerNode.domNode.style[$tw.browser.transformorigin] = "50% 50%";
targetTiddlerNode.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(0.1)";
targetTiddlerNode.domNode.style.zIndex = "0";
targetTiddlerNode.domNode.addEventListener($tw.browser.transitionEnd,function(event) {
// Delete the DOM node when the transition is over
if(targetTiddlerNode.domNode.parentNode) {
targetTiddlerNode.domNode.parentNode.removeChild(targetTiddlerNode.domNode);
}
},true);
// Now the tiddler we're going back to
if(storyElement !== undefined) {
storyElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
storyElement.domNode.style.opacity = "1.0";
}
});
return true; // Indicate that we'll delete the DOM node
};
exports.zoomin = Zoomin;
})();