/*\ title: $:/plugins/tiddlywiki/cecily/cecily.js type: application/javascript module-type: storyview Positions tiddlers on a 2D map \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; var CecilyStoryView = function(listWidget) { var self = this; this.listWidget = listWidget; // Load the map this.loadMap(); // Position the existing tiddlers $tw.utils.each(this.listWidget.children,function(itemWidget,index) { var domNode = itemWidget.findFirstDomNode(); domNode.style.position = "absolute"; var title = itemWidget.parseTreeNode.itemTitle; self.positionTiddler(title,domNode); }); }; CecilyStoryView.prototype.navigateTo = function(historyInfo) { var listElementIndex = this.listWidget.findListItem(0,historyInfo.title); if(listElementIndex === undefined) { return; } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); // Scroll the node into view this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement}); }; CecilyStoryView.prototype.insert = function(widget) { var domNode = widget.findFirstDomNode(), duration = $tw.utils.getAnimationDuration(); // Make the newly inserted node position absolute $tw.utils.setStyle(domNode,[ {position: "absolute"}, {transition: ""}, {opacity: "0.0"} ]); // Position it var title = widget.parseTreeNode.itemTitle; this.positionTiddler(title,domNode); $tw.utils.forceLayout(domNode); // Animate it in $tw.utils.setStyle(domNode,[ {transition: "opacity " + duration + "ms ease-out"}, {opacity: "1.0"} ]); }; CecilyStoryView.prototype.remove = function(widget) { var targetElement = widget.findFirstDomNode(), duration = $tw.utils.getAnimationDuration(); // Remove the widget at the end of the transition setTimeout(function() { widget.destroy(); },duration); // Animate the closure $tw.utils.setStyle(targetElement,[ {transition: "none"}, {opacity: "1.0"} ]); $tw.utils.forceLayout(targetElement); $tw.utils.setStyle(targetElement,[ {transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out, " + "opacity " + duration + "ms ease-in-out"}, {transform: "scale(0.01)"}, {opacity: "0.0"} ]); }; /* Load the current map */ CecilyStoryView.prototype.loadMap = function() { this.map = this.listWidget.wiki.getTiddlerData(this.getMapTiddlerTitle(),{ positions: {}, newTiddlerPosition: {x: 0, y: 0}, width: parseInt(this.listWidget.getAttribute("cecily-width","600"),10) }); }; CecilyStoryView.prototype.getMapTiddlerTitle = function() { return this.listWidget.getAttribute("cecily-map","$:/TiddlerMap"); }; /* Position a tiddler according to the map */ CecilyStoryView.prototype.positionTiddler = function(title,domNode) { var pos = this.lookupTiddlerInMap(title,domNode), scale = pos.w/domNode.offsetWidth; $tw.utils.setStyle(domNode,[ {width: this.map.width + "px"}, {transformOrigin: "0% 0%"}, {transform: "translateX(" + pos.x + "px) translateY(" + pos.y + "px) scale(" + scale + ") translateX(-50%) rotate(" + (pos.r || 0) + "deg) translateX(50%)"} ]); }; // Get the position of a particular tiddler CecilyStoryView.prototype.lookupTiddlerInMap = function(title,domNode) { // If this is a draft tiddler then look for the position of the original tiddler var tiddler = this.listWidget.wiki.getTiddler(title); if(tiddler) { var draftOf = tiddler.fields["draft.of"]; if(draftOf && this.map.positions[draftOf]) { return this.map.positions[draftOf]; } } // Try looking the target tiddler up in the map if(this.map.positions[title]) { return this.map.positions[title]; } // If the tiddler wasn't in the map we'll have to compute it var newPosition; switch(this.map.positionNew) { default: // "right" newPosition = { x: this.map.newTiddlerPosition.x, y: this.map.newTiddlerPosition.y, w: 200, h: 200 }; this.map.newTiddlerPosition.x += newPosition.w * 1.1; break; } // A default position newPosition = newPosition || {x: 0,y: 0,w: 100,h: 100}; // Save the position back to the map this.map.positions[title] = newPosition; return newPosition; }; exports.cecily = CecilyStoryView; })();