非同期通信(Ajax)でEPUBデータを処理する
最後に、ローカルファイルではなく、サーバー上のEPUBデータを非同期通信で取得して表示してみましょう。非同期通信を使ったサンプルはFirefoxだけでなくSafari (iPhone/iPadもOK)やGoogle Chrome、Operaでも動作します(Operaでは一部でエラーになる場合があります。IE9ベータとAndroid 1.6端末では残念ながら動作しません)。
File APIベースのプログラムを非同期通信に変更する場合、File API自体が非同期なのでオブジェクトとメソッドを少し書き換えるだけで簡単です。非同期通信のオブジェクトを生成し、EPUBのファイル名をopen()メソッドに指定します。ただし、非同期通信の制約上、同一ドメイン上にあるEPUBファイルしかアクセスできません。また、MIME Typeを指定しないとエラーが発生して内容がまったく表示されません。
var xhr = new XMLHttpRequest(); xhr.open("get", filename); xhr.overrideMimeType("text/plain;charset=x-user-defined");
EPUBデータの読み込みに成功するか、エラーが発生するとonloadに設定したイベントハンドラが呼び出されます。読み込みに成功した場合だけ処理するので以下のようにステータスを調べます。readyStateが4の場合は読み込みが完了したことを示しています。statusはWebサーバーが返すステータスコードで、200であれば正常、404であればファイルが存在しません。
if ((xhr.readyState == 4) && (xhr.status == 200)){ 〜 }
File APIの場合はバイナリーデータとして読み込みましたが、非同期通信の場合はテキストデータになるので、以下のように論理積(&記号)を使って文字コードの値を0〜255までの範囲に収める必要があります。
for (var i=0; i<byteData.length; i++)bytes[i] = byteData.charCodeAt(i) & 0xff;
以降の処理はこれまでとまったく同じです。ここまでをまとめたがサンプル5です。
■サンプル5
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>JavaScript EPUB Viewer (Ajax版)</title> <link rel="stylesheet" href="css/main.css" type="text/css" media="all" /> <script type="text/javascript" src="js/sjis.js" charset="shift_jis"></script> <script type="text/javascript" src="js/zip.js" charset="shift_jis"></script> <script type="text/javascript" src="js/utf8.js" charset="utf-8"></script> <script> var zip = null; window.addEventListener("load", function(){ document.getElementById("viewEPUB").addEventListener("click", function(){ document.getElementById("bookIndex").innerHTML = ""; document.getElementById("bookContents").innerHTML = ""; var filename = document.getElementById("myFile").value; var xhr = new XMLHttpRequest(); // ■非同期通信用オブジェクトを生成 xhr.open("get", filename); // ■指定されたファイル名を取得 xhr.onload = function(){ // ■正常に読み込まれた場合のみ処理 if ((xhr.readyState == 4) && (xhr.status == 200)){ var bytes = []; var byteData = xhr.responseText; for (var i=0; i<byteData.length; i++)bytes[i] = byteData.charCodeAt(i) & 0xff; zip = Zip.inflate(bytes); // console.dir(zip.files["OEBPS/content.opf"]); var xhtmlList = analysisXML(zip.files["OEBPS/content.opf"].data); // XMLデータ解析 // 目次を生成 var contentsList = "<ol>"; for(i=0; i<xhtmlList.length; i++){ contentsList += '<li><a href="#" onclick=view("'+xhtmlList[i]+'")>'+xhtmlList[i]+"</a></li>"; } document.getElementById("bookIndex").innerHTML = contentsList+"</ol>"; }else{ // ■エラーの場合の処理 alert("通信エラーです:"+xhr.status); } } xhr.overrideMimeType("text/plain;charset=x-user-defined"); xhr.send(null); }, true); }, true); // XMLデータを解析してXHTMLファイル一覧を返す function analysisXML(xmldata){ document.getElementById("epubxml").innerHTML = xmldata; var d = document.getElementById("epubxml"); var fileList = []; // EPUB表示に必要なファイル項目を取得 var itemTag = d.getElementsByTagName("manifest")[0].getElementsByTagName("item"); // 表示順を示すitemrefタグを取得 var indexItem = d.getElementsByTagName("spine")[0].getElementsByTagName("itemref"); // ページの表示順番を取得(IDからファイル名を取得) for(var i=0; i<indexItem.length; i++){ var ID = indexItem[i].getAttribute("idref"); // IDを読み出す for(var j=0; j<itemTag.length; j++){ var cID = itemTag[j].getAttribute("id"); // IDを読み出す // console.log(ID+" = "+cID); // 確認したい人はコメント削除してください if (ID == cID){ fileList.push(itemTag[j].getAttribute("href")); // href属性を読み出す console.log(itemTag[j].getAttribute("href")); // 確認したい人はコメント削除してください break; // ループから抜ける } } } return fileList; } // 目次がクリックされたら対応するページを表示 function view(url){ var text = zip.files["OEBPS/"+url].data; //指定されたページを表示 document.getElementById("bookContents").innerHTML = Utf8.decode(text); } </script> </head> <body> <h1>JavaScript EPUB Viewer (Ajax版)</h1> <form action="./test.cgi" method="get"> <input type="text" id="myFile" value="sample.epub" /> <input type="button" id="viewEPUB" value="EPUB表示" /> </form> <div id="bookIndex">- 目次 -<br></div> <div id="bookContents">本文</div> <div id="epubxml"></div> </body> </html>
EPUBビューアーといっても最低限の機能しかありませんが、基本的な部分ができてしまえばあとは応用です。今回の記事を参考にして、好みのEPUBビューアーを作成してみてください。