<!DOCTYPE html>

<meta charset="utf8">
<title>...</title>

<div id="slides">
 <iframe allowfullscreen mozallowfullscreen webkitallowfullscreen></iframe>
</div>
<div id="controls">
 <button title="prev" id="back" onclick="Dz.back()">&#9664;</button>
 <button title="next" id="forward" onclick="Dz.forward()">&#9654;</button>
 <div id="rightcontrols">
   <input onchange="Dz.setCursor(this.value)" size="2" id="slideidx" value="0" />/<span id="slidecount">...</span>
   <button title="Go fullscreen or open in a new window" id="fullscreen" onclick="Dz.goFullscreen()">&#8689;</button>
 </div>
</div>

<style>
 html { height: 100%;}
 body {
   margin: 0;
   background-color: black;
   height: 100%;
   width: 100%;
 }
 #slides, #controls {
   left: 0;
   position: absolute;
   right: 0;
 }
 #controls {
   color: white;
   font-family: monospace;
   height: 30px;
   line-height: 30px;
   padding: 5px;
 }
 #slides {
   bottom: 40px;
   top: 0;
 }
 iframe {
   border: none;
   background-color: white;
   height: 100%;
   width: 100%;
 }
 #controls {
   bottom: 0;
   float: right;
   font-size: 13px;
   text-align: center;
 }
 #controls button[disabled] {color: #333;}
 button {
   background-color: transparent;
   border: none;
   cursor: pointer;
   color: #bbb;
   padding: 0;
   font-size: 20px;
   line-height: 100%;
   -webkit-user-select: none;
   -khtml-user-select: none;
   -moz-user-select: none;
   -o-user-select: none;
   user-select: none;
   position: relative;
 }
 button:hover {
   color: white;
 }
 button:active {
   top: 1px;
   left: 1px;
 }
 #slideidx {
   border: none;
   background-color: rgba(255, 255, 255, 0.2);
   color: white;
   text-align: center;
 }
 #rightcontrols * { vertical-align: middle; }
 #rightcontrols {
   bottom: 4px;
   position: absolute;
   top: 4px;
   right: 10px;
 }
 #fullscreen {-moz-transform: scaleX(-1);-webkit-transform: scaleX(-1);-o-transform: scaleX(-1);-ms-transform: scaleX(-1);transform: scaleX(-1);}
</style>

<script>
 var Dz = {
   view: null,
   url: null,
   idx: 1,
   count: null,
   iframe: null
 };

 Dz.init = function() {
   this.loadIframe();
 }

 Dz.onkeydown = function(aEvent) {
   // Don't intercept keyboard shortcuts
   if (aEvent.altKey
     || aEvent.ctrlKey
     || aEvent.metaKey
     || aEvent.shiftKey) {
     return;
   }
   if ( aEvent.keyCode == 37 // left arrow
     || aEvent.keyCode == 38 // up arrow
     || aEvent.keyCode == 33 // page up
   ) {
     aEvent.preventDefault();
     this.back();
   }
   if ( aEvent.keyCode == 39 // right arrow
     || aEvent.keyCode == 40 // down arrow
     || aEvent.keyCode == 34 // page down
   ) {
     aEvent.preventDefault();
     this.forward();
   }
   if (aEvent.keyCode == 35) { // end
     aEvent.preventDefault();
     this.goEnd();
   }
   if (aEvent.keyCode == 36) { // home
     aEvent.preventDefault();
     this.goStart();
   }
   if (aEvent.keyCode == 32) { // space
     aEvent.preventDefault();
     this.toggleContent();
   }
   if (aEvent.keyCode == 70) { // f
     aEvent.preventDefault();
     this.goFullscreen();
   }
 }

 Dz.onmessage = function(aEvent) {
   if (aEvent.source === this.view) {
     var argv = aEvent.data.split(" "), argc = argv.length;
     argv.forEach(function(e, i, a) { a[i] = decodeURIComponent(e) });
     if (argv[0] === "CURSOR" && argc === 2) {
       var cursor = argv[1].split(".");
       this.idx = ~~cursor[0];
       this.step = ~~cursor[1];
       $("#slideidx").value = this.idx;
       $("#back").disabled = this.idx == 1;
       $("#forward").disabled = this.idx == this.count;
     }
     if (argv[0] === "REGISTERED" && argc === 3) {
       $("#slidecount").innerHTML = this.count = argv[2];
       document.title = argv[1];
     }
   }
 }

 /* Get url from hash or prompt and store it */

 Dz.getUrl = function() {
   var u = window.location.hash.split("#")[1];
   if (!u) {
     u = window.prompt("What is the URL of the slides?");
     if (u) {
       window.location.hash = u.split("#")[0];
       return u;
     }
     u = "<style>body{background-color:white;color:black}</style>";
     u += "<strong>ERROR:</strong> No URL specified.<br>";
     u += "Try<em>: " + document.location + "#yourslides.html</em>";
     u = "data:text/html," + encodeURIComponent(u);
   }
   return u;
 }

 Dz.loadIframe = function() {
   this.iframe = $("iframe");
   this.iframe.src = this.url = this.getUrl();
   this.iframe.onload = function() {
     Dz.view = this.contentWindow;
     Dz.postMsg(Dz.view, "REGISTER");
   }
 }

 Dz.toggleContent = function() {
   this.postMsg(this.view, "TOGGLE_CONTENT");
 }

 Dz.onhashchange = function() {
   this.loadIframe();
 }

 Dz.back = function() {
   this.postMsg(this.view, "BACK");
 }

 Dz.forward = function() {
   this.postMsg(this.view, "FORWARD");
 }

 Dz.goStart = function() {
   this.postMsg(this.view, "START");
 }

 Dz.goEnd = function() {
   this.postMsg(this.view, "END");
 }

 Dz.setCursor = function(aCursor) {
   this.postMsg(this.view, "SET_CURSOR", aCursor);
 }

 Dz.goFullscreen = function() {
   var requestFullscreen = this.iframe.requestFullscreen || this.iframe.requestFullScreen || this.iframe.mozRequestFullScreen || this.iframe.webkitRequestFullScreen;
   if (requestFullscreen) {
     requestFullscreen.apply(this.iframe);
   } else {
     window.open(this.url + "#" + this.idx, '', 'width=800,height=600,personalbar=0,toolbar=0,scrollbars=1,resizable=1');
   }
 }

 Dz.postMsg = function(aWin, aMsg) { // [arg0, [arg1...]]
   aMsg = [aMsg];
   for (var i = 2; i < arguments.length; i++)
     aMsg.push(encodeURIComponent(arguments[i]));
   aWin.postMessage(aMsg.join(" "), "*");
 }

 function init() {
   Dz.init();
   window.onkeydown = Dz.onkeydown.bind(Dz);
   window.onhashchange = Dz.loadIframe.bind(Dz);
   window.onmessage = Dz.onmessage.bind(Dz);
 }

 window.onload = init;
</script>


<script> // Helpers
 if (!Function.prototype.bind) {
   Function.prototype.bind = function (oThis) {

     // closest thing possible to the ECMAScript 5 internal IsCallable
     // function
     if (typeof this !== "function")
     throw new TypeError(
       "Function.prototype.bind - what is trying to be fBound is not callable"
     );

     var aArgs = Array.prototype.slice.call(arguments, 1),
         fToBind = this,
         fNOP = function () {},
         fBound = function () {
           return fToBind.apply( this instanceof fNOP ? this : oThis || window,
                  aArgs.concat(Array.prototype.slice.call(arguments)));
         };

     fNOP.prototype = this.prototype;
     fBound.prototype = new fNOP();

     return fBound;
   };
 }

 var $ = (HTMLElement.prototype.$ = function(aQuery) {
   return this.querySelector(aQuery);
 }).bind(document);

</script>