Thought I'd have a stab at making a dynamic web page capable of reading the content.xml/.opf/.ncx files, and then creating a toc and displaying the chapters. I've sort of succeeded; this page works in firefox, just save it at the same level as META-INF in a blown-up epub.
The akward issue is loading files dynamically into an iframe; security-conscious browsers are typically not fond of doing this for local files. Chrome should be able to do it if you use the --allow-file-access-from-files, but I can't get it to work. Firefox also behaves rather strangely; it reports an error for line 233, because the basis variable is undeclared, but if I declare it, it no longer reads loaded files properly.
I ran a bit amuck with css3, I'm afraid. Couldn't resist playing with all the nice new shiny bells and whistles.
Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF8" />
<style type="text/css">
div#innh p:hover {
opacity:1;
background:yellow;
background: -moz-linear-gradient( left ,#ffff55, #995522);
}
div#spine {
position:relative;
left:2px;
top:-4px;
width:60px;
}
div#innh {
opacity:0.95;
border-style:solid double solid none;
border-color:#300808;
border-width:4px 8px 4px;
position:absolute;
z-index:1;
width:400px;
height:823px;
background:lightgreen;
background: -moz-linear-gradient( left ,#9f6a46, #490d0d);
color: -moz-linear-gradient( right ,#11aa55, #dfaa66);
-moz-transform: scaleX(0.05);
-moz-transition:-moz-transform 0.5s;
-moz-transform-origin:left center;
top:-384px;
left:80px;
overflow:hidden;
border-radius:0 20px 20px 0;
}
div#innh p {
margin:0 0 0 20px;
font-family:sans-serif;
white-space:nowrap;
overflow:hidden;
}
div#innh:hover{
-moz-transform: scaleX(1);
min-height:823px;
height:auto;
}
div#chaps p{
margin:0 0 0 0px;
font-family:sans-serif;
white-space:nowrap;
overflow:hidden;
}
div#chaps {
opacity:0;
-moz-transition:opacity 1s;
}
body {
background:#dfaa66;
min-height:600px;
background: -moz-radial-gradient(300px 400px 65deg, ellipse cover, green 50%,blue 100%);
}
div#help{
background:cyan;
background: -moz-linear-gradient( top,#088 , cyan, cyan, cyan,white);
border-color:darkblue ;
border-style:none solid ridge solid;
border-width: 8px 1px 8px 1px;
position:absolute;
height:0px;
width:595px;
top:18px;
left:118px;
overflow-y:auto;
z-index:3;
-moz-transition:height 0.6s;
-moz-transform-origin:center top;
border-radius:0 0 29px 29px ;
font-family:sans-serif;
visibility:hidden;
}
div#help hr{ margin:0 15% ;}
div#help h2 {
text-align:center;
margin:1em 0 0.5em 0 ;
text-shadow:2px 2px 2px #044;
}
div#help p{margin:1em;}
.knapp{
display:block;
text-align:center;
text-shadow:2px 2px 4px blue;
line-height:24px;
font-size:24px;
color:darkblue;
width:24px;
height:24px;
background:red;
border-style:outset;
border-width:3px;
border-color:darkblue;
background: -moz-radial-gradient(10px 12px 65deg, circle cover, #99f 5%,blue 80%);
border-radius:6px;
}
div.knapp:hover {
color:green;
}
div.knapp:active {
border-style:inset;
background: -moz-radial-gradient(10px 12px 65deg, circle cover, #448 5%,#00a 60%);
}
div.hjelp {
width:70px;
border-color:;
color:#442;
font-size:19px;
margin:20px;
text-shadow:1px 1px 3px maroon;
background: -moz-radial-gradient(10px 12px 65deg, circle cover, #ff8 5%,#aa0 80%);
}
div.hjelp:active {
background: -moz-radial-gradient(10px 12px 65deg, circle cover, #884 5%,#550 80%);
}
iframe#ramme {
background:white;
position:absolute;
top:10px;
left:108px;
height:800px;
width:615px;
z-index:0;
border-style:ridge double outset none;
border-width:11px;
border-color:brown;
border-radius:0 9px 9px 0;
}
p#tittel {
font-size:36px;
font-style:italic;
font-weight:bold;
text-shadow:2px 2px 5px maroon;
text-align:center;
padding-bottom:14px;
color:#a68120;
border-color:#300808;
margin-left:-358px;
margin-top:393px;
background:cyan;
border-style:double solid none solid;
border-width:5px 4px 2px 3px;
-moz-transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
background: -moz-linear-gradient( bottom,#9f6a46, #490d0d);
width:824px;
border-radius:20px 20px 0 0;
}
p.nav {
letter-spacing:0.36em;
text-shadow:1px 1px 3px #842;
margin-top:2px;
float:left;
width:308px;
text-align:center;
font-family:sans-serif;
font-weight:bold;
font-style:italic;
border-style:solid;
color:maroon;
background:yellow;
position:absolute;
top:828px;
}
p#next:active { background: -moz-linear-gradient( left ,#ff9f55, #300502); }
p#next:hover { color:#311; }
p#next {
background: -moz-linear-gradient( left ,#ff9f55, #793522);
border-bottom-right-radius:20px;
border-top-right-radius:20px;
left:420px;
}
p#prev:active { background: -moz-linear-gradient( right ,#ff9f55, #300502); }
p#prev:hover { color:#311; }
p#prev {
background: -moz-linear-gradient( right ,#ff9f55, #793522);
border-top-left-radius:20px;
border-bottom-left-radius:20px;
left:110px;
}
div#controls {
position:absolute;
top:30px;
left:750px;
width:110px;
height:180px;
border:ridge;
border-color:green;
border-radius:25px;
margin-left:auto;
margin-right:auto;
background: -moz-radial-gradient(55px 90px 65deg, ellipse cover, #084 80%,lightgreen 100%);
}
</style>
<script type="text/javascript">
var fs=25
var opfFile=""
var ncxFile=""
var author=""
var title=""
var titlepage=""
var indx=0
var toggle=new Boolean()
var urlArr=new Array()
var titleArr=new Array()
var spineArr=new Array()
var tocTxtArr=new Array()
var tocUrlArr=new Array()
var toggleHelp=new Boolean()
function readFile(func, file) {
document.getElementById("ramme").src=file
var t=setTimeout(func+'();', 100)
}
function readContentXml() {
opfFile=document.getElementById("ramme").contentDocument.getElementsByTagName("container")[0].getElementsByTagName("rootfiles")[0].getElementsByTagName("rootfile")[0].getAttribute("full-path")
basis=opfFile.replace(/\/[^/]*$/,"/")
readFile("readOpf", opfFile)
}
function readOpf() {
var d=document.getElementById("ramme").contentDocument.getElementsByTagName("package")[0]
var spineNodes=d.getElementsByTagName("spine")[0].getElementsByTagName("itemref")
var manifestNodes=d.getElementsByTagName("manifest")[0].getElementsByTagName("item")
var i=0
var j=0
for (j=0;j<spineNodes.length;j++) {
for (i=0;i<manifestNodes.length;i++) {
if (manifestNodes[i].getAttribute("id")==spineNodes[j].getAttribute("idref")) {
spineArr[j]=manifestNodes[i].getAttribute("href")
}
}
}
for (i=0;i<manifestNodes.length;i++) {
if (manifestNodes[i].getAttribute("id")==d.getElementsByTagName("spine")[0].getAttribute("toc")) {
ncxFile=manifestNodes[i].getAttribute("href")
}
}
titlepage=d.getElementsByTagName("guide")[0].getElementsByTagName("reference")[0].getAttribute("href")
author=d.getElementsByTagName("metadata")[0].getElementsByTagName("dc:creator")[0].childNodes[0].nodeValue
title=d.getElementsByTagName("metadata")[0].getElementsByTagName("dc:title")[0].childNodes[0].nodeValue
readFile("readNcx", basis+ncxFile)
}
function readNcx() {
var d=document.getElementById("ramme").contentDocument.getElementsByTagName("ncx")[0]
var navPoints=d.getElementsByTagName("navMap")[0].getElementsByTagName("navPoint")
for (i=0;i<navPoints.length;i++) {
tocUrlArr[i]=navPoints[i].getElementsByTagName("content")[0].getAttribute("src")
tocTxtArr[i]= navPoints[i].getElementsByTagName("text")[0].childNodes[0].nodeValue;
}
toggleToc()
document.getElementById("ramme").src=basis+titlepage
document.getElementById("ramme").style.visibility="visible"
document.getElementById("tittel").innerHTML=author+": "+title
}
function fillToc() {
var i=0
var d=document.getElementById("chaps");
d.innerHTML=""
for (i=0; i<titleArr.length;i++) {
d.innerHTML+="<p onClick=\"showChap("+i+")\" id=\""+i+"\">"+titleArr[i]+"</p>"
}
}
function toggleToc() {
var tmp=""
toggle=document.getElementById("toggler").checked
if (urlArr.length>0) tmp=urlArr[indx]
if (toggle) {
titleArr=spineArr
urlArr=spineArr
}
else {
titleArr=tocTxtArr
urlArr=tocUrlArr
}
fillToc()
if (tmp.length>0) {
var i=0
for (i=0;i<urlArr.length;i++) {
if (tmp==urlArr[i]) indx=i
}
}
document.getElementById(indx).style.fontWeight="bold"
}
function hideToc() {
document.getElementById('innh').style.MozTransform='scaleX(0.05)'
document.getElementById('chaps').style.opacity=0
document.getElementById('innh').style.height='823px'
}
function showToc() {
if (toggleHelp) {
document.getElementById('innh').style.MozTransform='scaleX(1)'
document.getElementById('innh').style.height='auto'
document.getElementById('innh').style.minHeight='823px'
document.getElementById('chaps').style.opacity=1
}
}
function nextPrev(t) {
indx+=t
if (indx<0) {indx=0}
if (indx>=urlArr.length) {indx=(urlArr.length-1)}
showChap(indx)
}
function showHelp() {
if (toggleHelp) {
document.getElementById('help').style.visibility="visible"
document.getElementById('help').style.height='600px'
var t=setTimeout("document.getElementById('help').style.overflowY='auto'",1000)
}
else {
document.getElementById('help').style.overflowY='hidden'
document.getElementById('help').style.height='0px'
var t=setTimeout("document.getElementById('help').style.visibility='hidden'",1000)
}
toggleHelp=!toggleHelp
}
function scaleFont(t) {
fs+=t
document.getElementById("ramme").contentDocument.getElementsByTagName("body")[0].style.fontSize=fs+"px"
}
function showChap(n) {
document.getElementById("ramme").src=basis+urlArr[n]
var t=setTimeout('document.getElementById("ramme").contentDocument.getElementsByTagName("body")[0].style.fontSize=fs+"px"',50)
var i=0
for (i=0;i<urlArr.length;i++) {
document.getElementById(i).style.fontWeight="normal"
}
document.getElementById(n).style.fontWeight="bold"
indx=n
}
function init() {
var msg="Only works with Firefox, I'm afraid..."
document.getElementById("ramme").src="META-INF/container.xml"
if (navigator.appVersion.indexOf("Chrome")>-1) {alert(msg+"\nYou could try starting chrome with --allow-file-access-from-local-files")}
if (navigator.appName.indexOf("Opera")>-1) {alert(msg)}
document.getElementById("ramme").style.visibility="hidden"
readFile("readContentXml", "META-INF/container.xml")
document.getElementById("ramme").src=basis+titlepage
}
</script>
</head>
<body onLoad="init()">
<p onClick="nextPrev(-1)" class="nav" id="prev">Previous Chapter</p>
<p onClick="nextPrev(1)" class="nav" id="next">Next Chapter</p>
<iframe name="ramme" id="ramme" src="META-INF/container.xml" ></iframe>
<div id="help" onClick="showHelp()">
<h2>SBT's Attempt at a Viewer for Epub</h2>
<hr>
<ul>
<li> Only works with Firefox when reading from files. If it is accessed through a web server, it should in theory work in all major browsers. Chrome shoul also work if started wit <tt>--allow-file-access-from-files</tt>, but I've not been successful.
<li> Unzip your epub-file, and save this web page on the top level of the unzipped directory structure, i.e. on the same level as META-INF (not <em>in</em> META-INF)
<li>Move the pointer over the book spine to see and select from list of contents.
<li> Use buttons to increase/decrease font size.
<li> If 'Spine' box is checked, the table of contents lists the xhtml files listed in the <tt><spine></tt> section of the OPF-manifest file.
<li>Viewing window is 600x800, so this viewer can be handy when crafting an epub book to see how it looks on a standard e-reader screen.
<li> Click inside the help window or on the help button to make this info go away.
</ul>
<p> No copyright claimed, but an acknowledgement is always appreciated if you use this web page to create a derived work.</p>
<div style="margin:2em 2em 1em 60%">
<p>
<em> SBT
9. March 2012</em></p>
</div>
</div>
<div id="spine" onMouseOut="hideToc()" onMouseOver="showToc()"><div> <p id="tittel">(Author:Title)</p> </div>
<div id="innh">
<div id="chaps">
</div>
</div>
</div>
<div id="controls">
<div class="knapp hjelp" onClick="showHelp()" id="helpbt">Help</div>
<div style="height:30px;margin-bottom:20px;">
<div class="knapp" onClick="scaleFont(-1)" style="margin-left:22px;;float:left"><span style="font-size:10px;">A</span></div>
<div class="knapp" onClick="scaleFont(1)"style="margin-right:22px;float:right"><span style="font-size:20px;">A</span> </div>
</div>
<form>
<input type="checkbox" id="toggler" onClick="toggleToc()" value="true" ><span style="font-size:21px;color:lightgreen;text-shadow:2px 2px 2px #f44;}">Spine</span></input>
</form>
</body>
</html>