Revision of Twine: Improved back and forward buttons in Sugarcane from Thu, 06/20/2013 - 00:21

This script allows Sugarcane to use HTML5 history management instead of URL hash strings to alter the browser history. This means that various non-deterministic game state changes (random numbers, player data input, state changes inside <<replace>> macros, etc.) will be properly remembered when you use the browser's Back button. This code also updates <<back>> and <<return>> and the Rewind menu.

Note: due to a conflict in the Twine engine, I've had to include Transition CSS with this script. So, you must also include one of the CSS transitions on that page. The default transition CSS code is as follows:

.transition-in{opacity:0;position:absolute}.passage:not(.transition-out){transition:1s;-webkit-transition:1s}
.transition-out{opacity:0;position:absolute}

Note 2: IE9 users will be given the old hashchange functionality anyway.

Javascript code follows:
(function(){var b=(typeof window.history.pushState=="function");History.prototype.display=function(m,f,g){var n=tale.get(m);if(g!="back"){this.history.unshift({passage:n,variables:clone(this.history[0].variables)});this.history[0].hash=this.save();if(b&&this.history){if(this.history.length==2&&window.history.state===null){window.history.replaceState(this.history,document.title)}else{window.history.pushState(this.history,document.title)}}}this.history[0].hash=this.save();var l=n.render();l.style.visibility="visible";if(g!="offscreen"){var k=$("passages");for(var h=0;h<k.childNodes.length;h+=1){var i=k.childNodes[h];i.classList.add("transition-out");setTimeout(function(){if(i.parentNode){i.parentNode.removeChild(i)}},1000)}l.classList.add("transition-in");setTimeout(function(){l.classList.remove("transition-in")},1);k.appendChild(l)}if((g=="quietly")||(g=="offscreen")){l.style.visibility="visible"}if(g!="offscreen"){document.title=tale.title+": "+n.title;this.hash=this.save();if(!b){window.location.hash=this.hash}window.scroll(0,0)}return l};History.prototype.restart=function(){if(b){window.location.reload()}else{window.location.hash=""}};macros["return"]=macros.back={handler:function(g,f,j){var i,k="";var h=1;if(j[0]){if(j[1]=="steps"){if(isNaN(j[0])){throwError(g,"parameter before 'steps' must be a number.");return}else{if(j[0]<state.history.length){k=state.history[j[0]].passage.title;h=j[0]}}}else{if(tale.get(j[0]).id==undefined){throwError(g,"The "+j[0]+" passage does not exist");return}for(var l=0;l<state.history.length;l++){if(state.history[l].passage.title==j[0]){k=j[0];h=l;break}}}}else{k=state.history[1].passage.title}if(!k){return}else{i=document.createElement("a");i.className="return";i.onclick=function(){if(f=="back"){if(b){window.history.back();return}while(h>=0){if(state.history.length>1){state.history.shift()}h--}}state.display(k)};i.href="javascript:void(0)";i.innerHTML="<b>«</b> "+f[0].toUpperCase()+f.slice(1);g.appendChild(i)}}};Interface.buildSnapback=function(){var f=false;removeChildren(document.getElementById("snapbackMenu"));for(var e=state.history.length-1;e>=0;e--){if(state.history[e].passage&&state.history[e].passage.tags.indexOf("bookmark")!=-1){var d=document.createElement("div");d.pos=e;d.onclick=function(){var c=this.pos;var g=state.history[c].passage.title;window.history.go(-(c+1));while(c>=0){if(state.history.length>1){state.history.shift()}c--}state.display(g)};d.innerHTML=Passage.prototype.excerpt.call(state.history[e].passage);document.getElementById("snapbackMenu").appendChild(d);f=true}}if(!f){var d=document.createElement("div");d.innerHTML="<i>No passages available</i>";document.getElementById("snapbackMenu").appendChild(d)}};History.prototype.init=function(){if(!this.restore()){this.display("Start",null)}if(!b){this.hash=window.location.hash;this.interval=window.setInterval(function(){a.watchHash.apply(a)},250)}};window.onpopstate=function(c){console.log(" Popped");console.log(c.state);if(c.state===null){return}if(c.state&&c.state.length>0){state.history=c.state}else{state=new History();state.init()}state.display(state.history[0].passage.title,null,"back")};if(b){clearInterval(state.interval)}}());
Feel free to report any bugs to @webbedspace.

pensive-mosquitoes