From f412dd19d92c4dcdeebce9b424f5353e919586a1 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sat, 7 Jul 2012 17:14:50 +0100 Subject: [PATCH] Lots of fixes to storyview mechanism And a new scroller mechanism --- core/modules/macros/navigator.js | 42 ++++- core/modules/macros/story/story.js | 34 +++- core/modules/macros/story/views/classic.js | 10 +- core/modules/macros/story/views/sideways.js | 14 +- core/modules/macros/story/views/zoomin.js | 194 ++++++++++---------- core/modules/scroller.js | 59 ++++++ core/modules/utils.dom.js | 38 ++-- 7 files changed, 241 insertions(+), 150 deletions(-) create mode 100644 core/modules/scroller.js diff --git a/core/modules/macros/navigator.js b/core/modules/macros/navigator.js index b48a9a1f2..972b7551d 100644 --- a/core/modules/macros/navigator.js +++ b/core/modules/macros/navigator.js @@ -17,8 +17,6 @@ exports.info = { params: { story: {byName: "default", type: "text"}, // Actually a tiddler, but we don't want it to be a dependency history: {byName: "default", type: "text"}, // Actually a tiddler, but we don't want it to be a dependency - defaultViewTemplate: {byName: true, type: "tiddler"}, - defaultEditTemplate: {byName: true, type: "tiddler"}, set: {byName: true, type: "tiddler"} } }; @@ -64,8 +62,7 @@ exports.eventMap["tw-navigate"] = function(event) { // Update the story tiddler if specified if(this.hasParameter("story")) { this.getStory(); - var template = this.params.defaultViewTemplate || "$:/templates/ViewTemplate", - t,tiddler,slot; + var t,tiddler,slot; // See if the tiddler is already there for(t=0; t} + { + title: , + fromTitle: , + fromPosition: {bottom: , height: , top: , right: , left: , width: } + } ] } @@ -132,6 +136,9 @@ exports.removeStoryElement = function(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); @@ -169,8 +176,10 @@ exports.executeMacro = function() { }; exports.postRenderInDom = function() { - // Reset the record of the previous history stack - this.prevHistory = {stack: []}; + // 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")) { @@ -278,7 +287,7 @@ exports.processHistoryChange = function() { // Read the history tiddler this.getHistory(); if(this.storyview) { - var t,index, + 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--) { - index = this.findStoryElementByTitle(0,this.prevHistory.stack[t].title); - if(index !== undefined && this.storyview.navigateBack) { - this.storyview.navigateBack(this.storyNode.children[index],this.history.stack[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 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]; - } +Zoomin.prototype.navigateBack = function(toStoryElement,fromStoryElement,historyInfo) { // Get the animation duration - var d = ($tw.config.preferences.animationDuration/1000).toFixed(8) + "s"; + var d = $tw.config.preferences.animationDuration + "ms"; // Set up the tiddler that is being closed - storyElementNode.domNode.style.position = "absolute"; - storyElementNode.domNode.style.display = "block"; - storyElementNode.domNode.style[$tw.browser.transformorigin] = "50% 50%"; - storyElementNode.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)"; - storyElementNode.domNode.style[$tw.browser.transition] = "none"; - storyElementNode.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; + if(fromStoryElement) { + fromStoryElement.domNode.style.position = "absolute"; + fromStoryElement.domNode.style.display = "block"; + fromStoryElement.domNode.style[$tw.browser.transformorigin] = "50% 50%"; + fromStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(1)"; + fromStoryElement.domNode.style[$tw.browser.transition] = "none"; + fromStoryElement.domNode.style.zIndex = "0"; } + // Set up the tiddler we're moving back in + 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.nextTick(function() { // First, the tiddler we're closing - storyElementNode.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out"; - storyElementNode.domNode.style.opacity = "0.0"; - storyElementNode.domNode.style[$tw.browser.transformorigin] = "50% 50%"; - storyElementNode.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(0.1)"; - storyElementNode.domNode.style.zIndex = "0"; - storyElementNode.domNode.addEventListener($tw.browser.transitionEnd,function(event) { - // Delete the DOM node when the transition is over - if(storyElementNode.domNode.parentNode) { - storyElementNode.domNode.parentNode.removeChild(storyElementNode.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"; + if(fromStoryElement) { + fromStoryElement.domNode.style[$tw.browser.transition] = "-" + $tw.browser.prefix.toLowerCase() + "-transform " + d + " ease-in-out, opacity " + d + " ease-out"; + fromStoryElement.domNode.style.opacity = "0.0"; + fromStoryElement.domNode.style[$tw.browser.transformorigin] = "50% 50%"; + fromStoryElement.domNode.style[$tw.browser.transform] = "translateX(0px) translateY(0px) scale(0.1)"; + fromStoryElement.domNode.style.zIndex = "0"; + var eventHandler = function(event) { + // Delete the DOM node when the transition is over + if(fromStoryElement.domNode.parentNode) { + fromStoryElement.domNode.parentNode.removeChild(fromStoryElement.domNode); + } + fromStoryElement.domNode.removeEventListener($tw.browser.transitionEnd,eventHandler,true); + }; + fromStoryElement.domNode.addEventListener($tw.browser.transitionEnd,eventHandler,true); } + // Now the tiddler we're going back to + 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 }; diff --git a/core/modules/scroller.js b/core/modules/scroller.js new file mode 100644 index 000000000..f0eaa9df1 --- /dev/null +++ b/core/modules/scroller.js @@ -0,0 +1,59 @@ +/*\ +title: $:/core/modules/scroller.js +type: application/javascript +module-type: utils + +Plugin that creates a $tw.utils.Scroller object prototype that manages scrolling in the browser + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var slowInSlowOut = function(t) { + return (1 - ((Math.cos(t * Math.PI) + 1) / 2)); +}; + +/* +Creates a Scroller object +*/ +var Scroller = function() { +}; + +Scroller.prototype.cancel = function() { + if(this.timerId) { + window.clearInterval(this.timerId); + this.timerId = null; + } +}; + +/* +Smoothly scroll an element back into view if needed +*/ +Scroller.prototype.scrollIntoView = function(element) { + var scrollPosition = $tw.utils.getScrollPosition(); + this.cancel(); + this.startTime = new Date(); + this.startX = scrollPosition.x; + this.startY = scrollPosition.y; + this.endX = element.offsetLeft; + this.endY = element.offsetTop; + if((this.endX < this.startX) || (this.endX > (this.startX + window.innerWidth)) || (this.endY < this.startY) || (this.endY > (this.startY + window.innerHeight))) { + var self = this; + this.timerId = window.setInterval(function() { + var t = ((new Date()) - self.startTime) / $tw.config.preferences.animationDuration; + if(t >= 1) { + self.cancel(); + t = 1; + } + t = slowInSlowOut(t); + window.scrollTo(self.startX + (self.endX - self.startX) * t,self.startY + (self.endY - self.startY) * t); + }, 10); + } +}; + +exports.Scroller = Scroller; + +})(); diff --git a/core/modules/utils.dom.js b/core/modules/utils.dom.js index 7486ab551..259f6b1db 100644 --- a/core/modules/utils.dom.js +++ b/core/modules/utils.dom.js @@ -76,34 +76,18 @@ exports.applyStyleSheet = function(id,css) { }; /* -Smoothly scroll an element back into view if needed +Get the scroll position of the viewport +Returns: + { + x: horizontal scroll position in pixels, + y: vertical scroll position in pixels + } */ -exports.scrollIntoView = function(element) { - var slowInSlowOut = function(t) { - return (1 - ((Math.cos(t * Math.PI) + 1) / 2)); - }, - animateScroll = function(startX,startY,endX,endY,duration) { - var startTime = new Date(), - timerId = window.setInterval(function() { - var t = ((new Date()) - startTime) / duration; - if(t >= 1) { - window.clearInterval(timerId); - t = 1; - } - t = slowInSlowOut(t); - var x = startX + (endX - startX) * t, - y = startY + (endY - startY) * t; - window.scrollTo(x,y); - }, 10); - }, - x = element.offsetLeft, - y = element.offsetTop, - winWidth = window.innerWidth, - winHeight = window.innerHeight, - scrollLeft = window.scrollX || document.documentElement.scrollLeft, - scrollTop = window.scrollY || document.documentElement.scrollTop; - if((x < scrollLeft) || (x > (scrollLeft + winWidth)) || (y < scrollTop) || (y > (scrollTop + winHeight))) { - animateScroll(scrollLeft,scrollTop,x,y,$tw.config.preferences.animationDuration); +exports.getScrollPosition = function() { + if("scrollX" in window) { + return {x: window.scrollX, y: window.scrollY}; + } else { + return {x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop}; } };