mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-11 18:00:26 +00:00
Get rid of the old story macro, and tidy up
This commit is contained in:
parent
ba4e140147
commit
5a58639131
@ -1,305 +0,0 @@
|
|||||||
/*\
|
|
||||||
title: $:/core/modules/macros/story/story.js
|
|
||||||
type: application/javascript
|
|
||||||
module-type: macro
|
|
||||||
|
|
||||||
Displays a sequence of tiddlers defined in two JSON structures. The story tiddler is the sequence of tiddlers currently present in the DOM:
|
|
||||||
|
|
||||||
{
|
|
||||||
tiddlers: [
|
|
||||||
{title: <string>, draft: <string>}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
The optional `draft` member indicates that the tiddler is in edit mode, and the value is the title of the tiddler being used as the draft.
|
|
||||||
|
|
||||||
When the story tiddler changes, the story macro adjusts the DOM to match. An optional storyview module can be used to visualise the changes.
|
|
||||||
|
|
||||||
And the history tiddler is the stack of tiddlers that were navigated to in turn:
|
|
||||||
|
|
||||||
{
|
|
||||||
stack: [
|
|
||||||
{
|
|
||||||
title: <string>,
|
|
||||||
fromTitle: <string>,
|
|
||||||
fromPosition: {bottom: <num>, height: <num>, top: <num>, right: <num>, left: <num>, width: <num>}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
The history stack is updated during navigation, and again the storyview module is given an opportunity to animate the navigation.
|
|
||||||
|
|
||||||
\*/
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
|
||||||
/*global $tw: false */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
exports.info = {
|
|
||||||
name: "story",
|
|
||||||
params: {
|
|
||||||
story: {byName: "default", type: "tiddler"},
|
|
||||||
history: {byName: "default", type: "tiddler"},
|
|
||||||
viewTemplate: {byName: true, type: "tiddler"},
|
|
||||||
editTemplate: {byName: true, type: "tiddler"},
|
|
||||||
storyviewTiddler: {byName: true, type: "tiddler"},
|
|
||||||
storyview: {byName: true, type: "text"}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get the data from the JSON story tiddler
|
|
||||||
*/
|
|
||||||
exports.getStory = function() {
|
|
||||||
this.story = this.wiki.getTiddlerData(this.params.story,{tiddlers: []});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getHistory = function() {
|
|
||||||
this.history = this.wiki.getTiddlerData(this.params.history,{stack: []});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getViewTemplate = function(title) {
|
|
||||||
if(this.hasParameter("viewTemplate")) {
|
|
||||||
return this.params.viewTemplate;
|
|
||||||
} else {
|
|
||||||
return "$:/templates/ViewTemplate";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getEditTemplate = function(title) {
|
|
||||||
if(this.hasParameter("editTemplate")) {
|
|
||||||
return this.params.editTemplate;
|
|
||||||
} else {
|
|
||||||
return "$:/templates/EditTemplate";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create a story element representing a given tiddler, optionally being editted
|
|
||||||
*/
|
|
||||||
exports.createStoryElement = function(title,draft) {
|
|
||||||
var node = this.createStoryElementMacro(title,draft),
|
|
||||||
eventHandler = {handleEvent: function(event) {
|
|
||||||
// Add context information to the event
|
|
||||||
event.navigateFromStoryElement = node;
|
|
||||||
event.navigateFromTitle = title;
|
|
||||||
return true;
|
|
||||||
}};
|
|
||||||
node.execute(this.parents,this.tiddlerTitle);
|
|
||||||
var storyElement = $tw.Tree.Element("div",{"class": ["tw-story-element"]},[node],{
|
|
||||||
events: ["tw-NewTiddler","tw-navigate","tw-EditTiddler","tw-SaveTiddler","tw-CloseTiddler"],
|
|
||||||
eventHandler: eventHandler
|
|
||||||
});
|
|
||||||
// Save our data inside the story element node
|
|
||||||
storyElement.storyElementInfo = {title: title};
|
|
||||||
if(draft) {
|
|
||||||
storyElement.storyElementInfo.draft = draft;
|
|
||||||
}
|
|
||||||
return storyElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create the tiddler macro needed to represent a given tiddler and its draft status
|
|
||||||
*/
|
|
||||||
exports.createStoryElementMacro = function(title,draft) {
|
|
||||||
var srcParams;
|
|
||||||
if(draft) {
|
|
||||||
srcParams = {target: draft, template: this.getEditTemplate(title)};
|
|
||||||
} else {
|
|
||||||
srcParams = {target: title, template: this.getViewTemplate(title)};
|
|
||||||
}
|
|
||||||
return $tw.Tree.Macro("tiddler",{
|
|
||||||
srcParams: srcParams,
|
|
||||||
wiki: this.wiki
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Remove a story element from the story, along with the attendant DOM nodes
|
|
||||||
*/
|
|
||||||
exports.removeStoryElement = function(storyElementIndex) {
|
|
||||||
var storyElement = this.storyNode.children[storyElementIndex];
|
|
||||||
// Invoke the storyview to animate the removal
|
|
||||||
if(this.storyview && this.storyview.remove) {
|
|
||||||
if(!this.storyview.remove(storyElement,storyElementIndex)) {
|
|
||||||
// Only delete the DOM element if the storyview.remove() returned false
|
|
||||||
storyElement.domNode.parentNode.removeChild(storyElement.domNode);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Always delete the story element if we didn't invoke the storyview
|
|
||||||
storyElement.domNode.parentNode.removeChild(storyElement.domNode);
|
|
||||||
}
|
|
||||||
// Then delete the actual renderer node
|
|
||||||
this.storyNode.children.splice(storyElementIndex,1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return the index of the story element that corresponds to a particular title
|
|
||||||
startIndex: index to start search (use zero to search from the top)
|
|
||||||
tiddlerTitle: tiddler title to seach for
|
|
||||||
*/
|
|
||||||
exports.findStoryElementByTitle = function(startIndex,tiddlerTitle) {
|
|
||||||
while(startIndex < this.storyNode.children.length) {
|
|
||||||
if(this.storyNode.children[startIndex].storyElementInfo.title === tiddlerTitle) {
|
|
||||||
return startIndex;
|
|
||||||
}
|
|
||||||
startIndex++;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.executeMacro = function() {
|
|
||||||
// Get the story object
|
|
||||||
this.getStory();
|
|
||||||
// Create the story frame
|
|
||||||
var attributes = {"class": "tw-story-frame"};
|
|
||||||
this.storyNode = $tw.Tree.Element("div",attributes,[]);
|
|
||||||
// Create each story element
|
|
||||||
for(var t=0; t<this.story.tiddlers.length; t++) {
|
|
||||||
this.storyNode.children.push(this.createStoryElement(this.story.tiddlers[t].title,this.story.tiddlers[t].draft));
|
|
||||||
}
|
|
||||||
if(this.classes) {
|
|
||||||
$tw.utils.pushTop(attributes["class"],this.classes);
|
|
||||||
}
|
|
||||||
return this.storyNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.postRenderInDom = function() {
|
|
||||||
// Get the history object and use it to set the previous history
|
|
||||||
this.getHistory();
|
|
||||||
this.prevHistory = this.history;
|
|
||||||
this.history = null;
|
|
||||||
// Instantiate the story view
|
|
||||||
var storyviewName;
|
|
||||||
if(this.hasParameter("storyviewTiddler")) {
|
|
||||||
storyviewName = this.wiki.getTextReference(this.params.storyviewTiddler);
|
|
||||||
}
|
|
||||||
if(!storyviewName && this.hasParameter("storyview")) {
|
|
||||||
storyviewName = this.params.storyview;
|
|
||||||
}
|
|
||||||
var StoryView = this.wiki.macros.story.viewers[storyviewName];
|
|
||||||
if(StoryView) {
|
|
||||||
this.storyview = new StoryView(this);
|
|
||||||
}
|
|
||||||
if(!this.storyview) {
|
|
||||||
StoryView = this.wiki.macros.story.viewers.classic;
|
|
||||||
if(StoryView) {
|
|
||||||
this.storyview = new StoryView(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.refreshInDom = function(changes) {
|
|
||||||
// If the storyview has changed we'll have to completely re-execute the macro
|
|
||||||
if(this.hasParameter("storyviewTiddler") && $tw.utils.hop(changes,this.params.storyviewTiddler)) {
|
|
||||||
this.reexecuteInDom();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If the story tiddler has changed we need to sync the story elements
|
|
||||||
if(this.hasParameter("story") && $tw.utils.hop(changes,this.params.story)) {
|
|
||||||
this.processStoryChange(changes);
|
|
||||||
} else {
|
|
||||||
// If the story didn't change then we must refresh our content
|
|
||||||
this.child.refreshInDom(changes);
|
|
||||||
}
|
|
||||||
// If the history tiddler has changed we may need to visualise something
|
|
||||||
if(this.hasParameter("history") && $tw.utils.hop(changes,this.params.history)) {
|
|
||||||
this.processHistoryChange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.processStoryChange = function(changes) {
|
|
||||||
// Get the tiddlers we're supposed to be displaying
|
|
||||||
var self = this,storyElement,
|
|
||||||
t,n,domNode;
|
|
||||||
// Get the story object
|
|
||||||
this.getStory();
|
|
||||||
// Check through each tiddler in the story
|
|
||||||
for(t=0; t<this.story.tiddlers.length; t++) {
|
|
||||||
// See if the node we want is already there
|
|
||||||
var tiddlerNode = this.findStoryElementByTitle(t,this.story.tiddlers[t].title);
|
|
||||||
if(tiddlerNode === undefined) {
|
|
||||||
// If not, render the tiddler
|
|
||||||
this.storyNode.children.splice(t,0,this.createStoryElement(this.story.tiddlers[t].title,this.story.tiddlers[t].draft));
|
|
||||||
this.storyNode.children[t].renderInDom(this.storyNode.domNode,this.storyNode.domNode.childNodes[t]);
|
|
||||||
// Invoke the storyview to animate the navigation
|
|
||||||
if(this.storyview && this.storyview.insert) {
|
|
||||||
this.storyview.insert(this.storyNode.children[t]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Delete any nodes preceding the one we want
|
|
||||||
if(tiddlerNode > t) {
|
|
||||||
for(n=tiddlerNode-1; n>=t; n--) {
|
|
||||||
this.removeStoryElement(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storyElement = this.storyNode.children[t];
|
|
||||||
// Check that the edit status matches
|
|
||||||
if(this.story.tiddlers[t].draft !== storyElement.storyElementInfo.draft) {
|
|
||||||
// If not, we'll have to recreate the story element
|
|
||||||
storyElement.children[0] = this.createStoryElementMacro(this.story.tiddlers[t].title,this.story.tiddlers[t].draft);
|
|
||||||
// Remove the DOM node in the story element
|
|
||||||
storyElement.domNode.removeChild(storyElement.domNode.firstChild);
|
|
||||||
// Reexecute the story element
|
|
||||||
storyElement.children[0].execute(this.parents,this.tiddlerTitle);
|
|
||||||
// Render the story element in the DOM
|
|
||||||
storyElement.children[0].renderInDom(storyElement.domNode);
|
|
||||||
// Reset the information in the story element
|
|
||||||
storyElement.storyElementInfo = {title: this.story.tiddlers[t].title, draft: this.story.tiddlers[t].draft};
|
|
||||||
} else {
|
|
||||||
// If the draft status matches then just refresh the DOM node we're reusing
|
|
||||||
this.storyNode.children[t].refreshInDom(changes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove any left over nodes
|
|
||||||
if(this.storyNode.children.length > this.story.tiddlers.length) {
|
|
||||||
for(t=this.storyNode.children.length-1; t>=this.story.tiddlers.length; t--) {
|
|
||||||
this.removeStoryElement(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Respond to a change in the history tiddler. The basic idea is to issue forward/back navigation commands to the story view that correspond to the tiddlers that need to be popped on or off the stack
|
|
||||||
*/
|
|
||||||
exports.processHistoryChange = function() {
|
|
||||||
// Read the history tiddler
|
|
||||||
this.getHistory();
|
|
||||||
if(this.storyview) {
|
|
||||||
var t,indexTo,indexFrom,
|
|
||||||
topCommon = Math.min(this.history.stack.length,this.prevHistory.stack.length);
|
|
||||||
// Find the common heritage of the new history stack and the previous one
|
|
||||||
for(t=0; t<topCommon; t++) {
|
|
||||||
if(this.history.stack[t].title !== this.prevHistory.stack[t].title) {
|
|
||||||
topCommon = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We now navigate backwards through the previous history to get back to the common ancestor
|
|
||||||
for(t=this.prevHistory.stack.length-1; t>=topCommon; t--) {
|
|
||||||
indexTo = this.findStoryElementByTitle(0,this.prevHistory.stack[t].fromTitle);
|
|
||||||
indexFrom = this.findStoryElementByTitle(0,this.prevHistory.stack[t].title);
|
|
||||||
// Call the story view if it defines a navigateBack() method
|
|
||||||
if(indexTo !== undefined && indexFrom !== undefined && this.storyview.navigateBack) {
|
|
||||||
this.storyview.navigateBack(this.storyNode.children[indexTo],this.storyNode.children[indexFrom],this.prevHistory.stack[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// And now we navigate forwards through the new history to get to the latest tiddler
|
|
||||||
for(t=topCommon; t<this.history.stack.length; t++) {
|
|
||||||
indexTo = this.findStoryElementByTitle(0,this.history.stack[t].title);
|
|
||||||
indexFrom = this.findStoryElementByTitle(0,this.history.stack[t].fromTitle);
|
|
||||||
if(indexTo !== undefined && this.storyview.navigateForward) {
|
|
||||||
this.storyview.navigateForward(this.storyNode.children[indexTo],
|
|
||||||
indexFrom !== undefined ? this.storyNode.children[indexFrom] : undefined,
|
|
||||||
this.history.stack[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Record the history stack for next time
|
|
||||||
this.prevHistory = this.history;
|
|
||||||
this.history = undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
@ -1,60 +0,0 @@
|
|||||||
/*\
|
|
||||||
title: $:/core/modules/macros/story/views/classic.js
|
|
||||||
type: application/javascript
|
|
||||||
module-type: storyview
|
|
||||||
|
|
||||||
A storyview that shows a sequence of tiddlers and navigates by smoothly scrolling
|
|
||||||
|
|
||||||
\*/
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
|
||||||
/*global $tw: false */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function ClassicScroller(story) {
|
|
||||||
this.story = story;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassicScroller.prototype.remove = function(storyElementNode) {
|
|
||||||
var targetElement = storyElementNode.domNode;
|
|
||||||
// Get the current height of the tiddler
|
|
||||||
var currHeight = targetElement.offsetHeight;
|
|
||||||
// Put a wrapper around the dom node we're closing
|
|
||||||
var wrapperElement = document.createElement("div");
|
|
||||||
targetElement.parentNode.insertBefore(wrapperElement,targetElement);
|
|
||||||
wrapperElement.appendChild(targetElement);
|
|
||||||
// Attach an event handler for the end of the transition
|
|
||||||
wrapperElement.addEventListener($tw.browser.transitionEnd,function(event) {
|
|
||||||
if(wrapperElement.parentNode) {
|
|
||||||
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
||||||
}
|
|
||||||
},false);
|
|
||||||
// Animate the closure
|
|
||||||
var d = $tw.config.preferences.animationDuration + "ms";
|
|
||||||
wrapperElement.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, " +
|
|
||||||
"opacity " + d + " ease-out, " +
|
|
||||||
"height " + d + " ease-in-out";
|
|
||||||
wrapperElement.style[$tw.browser.transformorigin] = "0% 0%";
|
|
||||||
wrapperElement.style[$tw.browser.transform] = "translateX(0px)";
|
|
||||||
wrapperElement.style.opacity = "1.0";
|
|
||||||
wrapperElement.style.height = currHeight + "px";
|
|
||||||
$tw.utils.forceLayout(wrapperElement);
|
|
||||||
wrapperElement.style[$tw.browser.transform] = "translateX(" + window.innerWidth + "px)";
|
|
||||||
wrapperElement.style.opacity = "0.0";
|
|
||||||
wrapperElement.style.height = "0px";
|
|
||||||
// Returning true causes the DOM node not to be deleted
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
ClassicScroller.prototype.navigateBack = function(toStoryElement,fromStoryElement,historyInfo) {
|
|
||||||
$tw.scroller.scrollIntoView(toStoryElement.domNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
ClassicScroller.prototype.navigateForward = function(toStoryElement,fromStoryElement,historyInfo) {
|
|
||||||
$tw.scroller.scrollIntoView(toStoryElement.domNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.classic = ClassicScroller;
|
|
||||||
|
|
||||||
})();
|
|
@ -1,87 +0,0 @@
|
|||||||
/*\
|
|
||||||
title: $:/core/modules/macros/story/views/sideways.js
|
|
||||||
type: application/javascript
|
|
||||||
module-type: storyview
|
|
||||||
|
|
||||||
A storyview that shows a sequence of tiddlers as horizontally stacked blocks
|
|
||||||
|
|
||||||
\*/
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
|
||||||
/*global $tw: false */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function setStoryElementStyles(e) {
|
|
||||||
e.style.display = "inline-block";
|
|
||||||
e.style.width = "380px";
|
|
||||||
e.style.verticalAlign = "top";
|
|
||||||
e.style.whiteSpace = "normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
function SidewaysView(story) {
|
|
||||||
this.story = story;
|
|
||||||
var wrapper = this.story.child.domNode;
|
|
||||||
// Scroll horizontally
|
|
||||||
wrapper.style.whiteSpace = "nowrap";
|
|
||||||
// Make all the tiddlers position absolute, and hide all but the first one
|
|
||||||
for(var t=0; t<wrapper.children.length; t++) {
|
|
||||||
setStoryElementStyles(wrapper.children[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Visualise navigation to a new tiddler
|
|
||||||
toStoryElementNode: tree node of the tiddler macro we're navigating to
|
|
||||||
fromStoryElementNode: optionally, tree node of the tiddler we're navigating from
|
|
||||||
historyInfo: record from the history tiddler corresponding to this navigation
|
|
||||||
*/
|
|
||||||
SidewaysView.prototype.navigateForward = function(toStoryElement,fromStoryElement,historyInfo) {
|
|
||||||
$tw.scroller.scrollIntoView(toStoryElement.domNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Visualise insertion of the specified tiddler macro, optionally specifying a source node for the visualisation
|
|
||||||
storyElementNode: tree node of the tiddler macro we're navigating to
|
|
||||||
*/
|
|
||||||
SidewaysView.prototype.insert = function(storyElementNode) {
|
|
||||||
setStoryElementStyles(storyElementNode.domNode);
|
|
||||||
$tw.scroller.scrollIntoView(storyElementNode.domNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
SidewaysView.prototype.remove = function(storyElementNode) {
|
|
||||||
var targetElement = storyElementNode.domNode;
|
|
||||||
// Get the current width of the tiddler
|
|
||||||
var currWidth = targetElement.offsetWidth;
|
|
||||||
// Put a wrapper around the dom node we're closing
|
|
||||||
var wrapperElement = document.createElement("div");
|
|
||||||
targetElement.parentNode.insertBefore(wrapperElement,targetElement);
|
|
||||||
wrapperElement.appendChild(targetElement);
|
|
||||||
// Animate the closure
|
|
||||||
var d = $tw.config.preferences.animationDuration + "ms";
|
|
||||||
wrapperElement.style.display = "inline-block";
|
|
||||||
wrapperElement.style[$tw.browser.transformorigin] = "0% 0%";
|
|
||||||
wrapperElement.style[$tw.browser.transform] = "translateY(0px)";
|
|
||||||
wrapperElement.style.opacity = "1.0";
|
|
||||||
wrapperElement.style.width = currWidth + "px";
|
|
||||||
wrapperElement.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, " +
|
|
||||||
"opacity " + d + " ease-out, " +
|
|
||||||
"width " + d + " ease-in-out";
|
|
||||||
// Attach an event handler for th eend of the transition
|
|
||||||
wrapperElement.addEventListener($tw.browser.transitionEnd,function(event) {
|
|
||||||
if(wrapperElement.parentNode) {
|
|
||||||
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
||||||
}
|
|
||||||
},true);
|
|
||||||
// Animate
|
|
||||||
$tw.utils.forceLayout(wrapperElement);
|
|
||||||
wrapperElement.style[$tw.browser.transform] = "translateY(" + window.innerHeight + "px)";
|
|
||||||
wrapperElement.style.opacity = "0.0";
|
|
||||||
wrapperElement.style.width = "0px";
|
|
||||||
// Returning true causes the DOM node not to be deleted
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.sideways = SidewaysView;
|
|
||||||
|
|
||||||
})();
|
|
@ -1,182 +0,0 @@
|
|||||||
/*\
|
|
||||||
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.
|
|
||||||
|
|
||||||
To do this, the story wrapper is set to `position:relative` and then each of the story elements to `position:absolute`. This results in all of the tiddlers being stacked on top of one another flush with the top left of the story wrapper.
|
|
||||||
|
|
||||||
Navigating between tiddlers is accomplished by switching the story nodes between `display:block` and `display:none`, but the implementation is considerably more complex due to the animation.
|
|
||||||
|
|
||||||
\*/
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/*jslint node: true, browser: true */
|
|
||||||
/*global $tw: false, Node: false */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function Zoomin(story) {
|
|
||||||
// Save the story
|
|
||||||
this.story = story;
|
|
||||||
this.storyNode = this.story.child.domNode;
|
|
||||||
// Set the current tiddler
|
|
||||||
this.currentTiddler = this.story.child.children[0];
|
|
||||||
// Make all the tiddlers position absolute, and hide all but the first one
|
|
||||||
this.storyNode.style.position = "relative";
|
|
||||||
for(var t=0; t<this.storyNode.children.length; t++) {
|
|
||||||
if(t) {
|
|
||||||
this.storyNode.children[t].style.display = "none";
|
|
||||||
}
|
|
||||||
this.storyNode.children[t].style.position = "absolute";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Visualise navigation to a new tiddler
|
|
||||||
toStoryElementNode: tree node of the tiddler macro we're navigating to
|
|
||||||
fromStoryElementNode: optionally, tree node of the tiddler we're navigating from
|
|
||||||
historyInfo: record from the history tiddler corresponding to this navigation
|
|
||||||
*/
|
|
||||||
Zoomin.prototype.navigateForward = function(toStoryElement,fromStoryElement,historyInfo) {
|
|
||||||
// Make the new tiddler be position absolute and visible so that we can measure it
|
|
||||||
toStoryElement.domNode.style.position = "absolute";
|
|
||||||
toStoryElement.domNode.style.display = "block";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transformorigin] = "0 0";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transition] = "none";
|
|
||||||
toStoryElement.domNode.style.opacity = "0.0";
|
|
||||||
// Get the position of the source node, or use the centre of the window as the source position
|
|
||||||
var sourceBounds = historyInfo.fromPosition || {
|
|
||||||
left: window.innerWidth/2 - 2,
|
|
||||||
top: window.innerHeight/2 - 2,
|
|
||||||
width: window.innerWidth/8,
|
|
||||||
height: window.innerHeight/8
|
|
||||||
};
|
|
||||||
// Try to find the title node in the target tiddler
|
|
||||||
var titleNode = findTitleNode(toStoryElement) || toStoryElement,
|
|
||||||
titleBounds = titleNode.getNodeBounds();
|
|
||||||
// Compute the transform for the target tiddler to make the title lie over the source rectange
|
|
||||||
var targetBounds = toStoryElement.getNodeBounds(),
|
|
||||||
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 to its starting position
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transform] = "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")";
|
|
||||||
// Get the animation duration
|
|
||||||
var d = $tw.config.preferences.animationDuration + "ms";
|
|
||||||
// Apply the ending transitions with a timeout to ensure that the previously applied transformations are applied first
|
|
||||||
var self = this,
|
|
||||||
prevCurrentTiddler = this.currentTiddler;
|
|
||||||
this.currentTiddler = toStoryElement;
|
|
||||||
// Force layout
|
|
||||||
$tw.utils.forceLayout(this.storyNode);
|
|
||||||
// Transform the target tiddler to its natural size
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
|
|
||||||
toStoryElement.domNode.style.opacity = "1.0";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
|
|
||||||
toStoryElement.domNode.style.zIndex = "500";
|
|
||||||
// Transform the previous tiddler out of the way and then hide it
|
|
||||||
if(prevCurrentTiddler && prevCurrentTiddler !== toStoryElement) {
|
|
||||||
scale = titleBounds.width / sourceBounds.width;
|
|
||||||
x = titleBounds.left - targetBounds.left - (sourceBounds.left - targetBounds.left) * scale;
|
|
||||||
y = titleBounds.top - targetBounds.top - (sourceBounds.top - targetBounds.top) * scale;
|
|
||||||
prevCurrentTiddler.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
|
|
||||||
prevCurrentTiddler.domNode.style.opacity = "0.0";
|
|
||||||
prevCurrentTiddler.domNode.style[$tw.browser.transformorigin] = "0 0";
|
|
||||||
prevCurrentTiddler.domNode.style[$tw.browser.transform] = "translateX(" + x + "px) translateY(" + y + "px) scale(" + scale + ")";
|
|
||||||
prevCurrentTiddler.domNode.style.zIndex = "0";
|
|
||||||
var eventHandler = function(event) {
|
|
||||||
// Hide the DOM node when the transition is over
|
|
||||||
if(self.currentTiddler !== prevCurrentTiddler) {
|
|
||||||
prevCurrentTiddler.domNode.style.display = "none";
|
|
||||||
}
|
|
||||||
prevCurrentTiddler.domNode.removeEventListener($tw.browser.transitionEnd,eventHandler,true);
|
|
||||||
};
|
|
||||||
prevCurrentTiddler.domNode.addEventListener($tw.browser.transitionEnd,eventHandler,true);
|
|
||||||
}
|
|
||||||
// Scroll the target into view
|
|
||||||
$tw.scroller.scrollIntoView(toStoryElement.domNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Visualise navigating back to the previous tiddler
|
|
||||||
storyElement: story element being closed
|
|
||||||
*/
|
|
||||||
Zoomin.prototype.remove = function(storyElement,storyElementIndex) {
|
|
||||||
// Get the animation duration
|
|
||||||
var d = $tw.config.preferences.animationDuration + "ms";
|
|
||||||
// Set up the tiddler that is being closed
|
|
||||||
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(1)";
|
|
||||||
storyElement.domNode.style[$tw.browser.transition] = "none";
|
|
||||||
storyElement.domNode.style.zIndex = "0";
|
|
||||||
// We'll move back to the previous or next element in the story
|
|
||||||
var toStoryElement = this.story.storyNode.children[storyElementIndex - 1];
|
|
||||||
if(!toStoryElement) {
|
|
||||||
toStoryElement = this.story.storyNode.children[storyElementIndex + 1];
|
|
||||||
}
|
|
||||||
// Set up the tiddler we're moving back in
|
|
||||||
if(toStoryElement) {
|
|
||||||
toStoryElement.domNode.style.position = "absolute";
|
|
||||||
toStoryElement.domNode.style.display = "block";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transformorigin] = "50% 50%";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(10)";
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out";
|
|
||||||
toStoryElement.domNode.style.opacity = "0.0";
|
|
||||||
toStoryElement.domNode.style.zIndex = "500";
|
|
||||||
this.currentTiddler = toStoryElement;
|
|
||||||
}
|
|
||||||
// Animate them both
|
|
||||||
$tw.utils.forceLayout(this.storyNode);
|
|
||||||
// First, the tiddler we're closing
|
|
||||||
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[$tw.browser.transformorigin] = "50% 50%";
|
|
||||||
storyElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(0.1)";
|
|
||||||
storyElement.domNode.style.zIndex = "0";
|
|
||||||
var eventHandler = function(event) {
|
|
||||||
// Delete the DOM node when the transition is over
|
|
||||||
if(storyElement.domNode.parentNode) {
|
|
||||||
storyElement.domNode.parentNode.removeChild(storyElement.domNode);
|
|
||||||
}
|
|
||||||
storyElement.domNode.removeEventListener($tw.browser.transitionEnd,eventHandler,true);
|
|
||||||
};
|
|
||||||
storyElement.domNode.addEventListener($tw.browser.transitionEnd,eventHandler,true);
|
|
||||||
// Now the tiddler we're going back to
|
|
||||||
if(toStoryElement) {
|
|
||||||
toStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)";
|
|
||||||
toStoryElement.domNode.style.opacity = "1.0";
|
|
||||||
}
|
|
||||||
return true; // Indicate that we'll delete the DOM node
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.zoomin = Zoomin;
|
|
||||||
|
|
||||||
})();
|
|
@ -74,16 +74,16 @@ exports.startup = function() {
|
|||||||
defaultTiddlers = $tw.wiki.filterTiddlers(defaultTiddlersTiddler.fields.text);
|
defaultTiddlers = $tw.wiki.filterTiddlers(defaultTiddlersTiddler.fields.text);
|
||||||
}
|
}
|
||||||
// Initialise the story and history
|
// Initialise the story and history
|
||||||
var storyTitle = "$:/StoryTiddlers",
|
var storyTitle = "$:/StoryList",
|
||||||
historyTitle = "$:/History",
|
historyTitle = "$:/HistoryList",
|
||||||
story = {tiddlers: []},
|
story = [],
|
||||||
history = {stack: []};
|
history = [];
|
||||||
for(var t=0; t<defaultTiddlers.length; t++) {
|
for(var t=0; t<defaultTiddlers.length; t++) {
|
||||||
story.tiddlers[t] = {title: defaultTiddlers[t]};
|
story[t] = defaultTiddlers[t];
|
||||||
history.stack[defaultTiddlers.length - t - 1] = {title: defaultTiddlers[t], fromTitle: defaultTiddlers[t+1]};
|
history[defaultTiddlers.length - t - 1] = defaultTiddlers[t];
|
||||||
}
|
}
|
||||||
$tw.wiki.setTiddlerData(storyTitle,story);
|
$tw.wiki.addTiddler({title: storyTitle, text: story.join("\n")});
|
||||||
$tw.wiki.setTiddlerData(historyTitle,history);
|
$tw.wiki.addTiddler({title: historyTitle, text: history.join("\n")});
|
||||||
// If we're being viewed on a data: URI then give instructions for how to save
|
// If we're being viewed on a data: URI then give instructions for how to save
|
||||||
if(document.location.protocol === "data:") {
|
if(document.location.protocol === "data:") {
|
||||||
var event = document.createEvent("Event");
|
var event = document.createEvent("Event");
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
title: $:/History
|
|
||||||
type: application/json
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
title: $:/StoryTiddlers
|
|
||||||
type: application/json
|
|
||||||
|
|
@ -4,4 +4,3 @@ HelloThere
|
|||||||
Introduction
|
Introduction
|
||||||
Improvements
|
Improvements
|
||||||
Docs
|
Docs
|
||||||
Search
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
title: $:/StoryList
|
|
||||||
|
|
||||||
HelloThere
|
|
||||||
Introduction
|
|
||||||
Improvements
|
|
||||||
Docs
|
|
@ -1,7 +1,7 @@
|
|||||||
title: $:/templates/PageTemplate
|
title: $:/templates/PageTemplate
|
||||||
|
|
||||||
<!-- The navigator catches navigation events and updates the story and history tiddlers -->
|
<!-- The navigator catches navigation events and updates the story and history tiddlers -->
|
||||||
<<navigator story:"$:/StoryTiddlers" history:"$:/History" ><
|
<<navigator><
|
||||||
|
|
||||||
<!-- Zooming chooser -->
|
<!-- Zooming chooser -->
|
||||||
{{navigation-panel{
|
{{navigation-panel{
|
||||||
@ -68,7 +68,7 @@ title: $:/templates/PageTemplate
|
|||||||
|
|
||||||
<!-- The main story references the same story and history tiddlers as the outer navigator -->
|
<!-- The main story references the same story and history tiddlers as the outer navigator -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<<story story:"$:/StoryTiddlers" history:"$:/History" storyviewTiddler:[[$:/CurrentView]] storyview:classic >>
|
<<list filter:"[list[$:/StoryList]]" template:"$:/templates/ViewTemplate" editTemplate:"$:/templates/EditTemplate" listview:classic >>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
>>
|
>>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
title: $:/templates/PageTemplate
|
title: $:/templates/PageTemplate
|
||||||
|
|
||||||
<!-- The navigator catches navigation events and updates the story and history tiddlers -->
|
<!-- The navigator catches navigation events and updates the story and history tiddlers -->
|
||||||
<<navigator story:"$:/StoryTiddlers" history:"$:/History" ><
|
<<navigator><
|
||||||
|
|
||||||
<!-- The top navigation bar -->
|
<!-- The top navigation bar -->
|
||||||
<div class="navbar navbar-fixed-top">
|
<div class="navbar navbar-fixed-top">
|
||||||
@ -22,7 +22,7 @@ title: $:/templates/PageTemplate
|
|||||||
|
|
||||||
<!-- The main story references the same story and history tiddlers as the outer navigator -->
|
<!-- The main story references the same story and history tiddlers as the outer navigator -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<<story story:"$:/StoryTiddlers" history:"$:/History" storyviewTiddler:[[$:/CurrentView]] storyview:classic >>
|
<<list filter:"[list[$:/StoryList]]" template:"$:/templates/ViewTemplate" editTemplate:"$:/templates/EditTemplate" listview:classic >>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
>>
|
>>
|
||||||
|
Loading…
Reference in New Issue
Block a user