JSword on the Web
JSword can be web-enabled fairly simply, using
DWR 2.0 to call Java as if it were JavaScript
and Sarissa 0.9.9.3 to transform OSIS into HTML locally.
This tutorial assumes that you are able to set up and properly manage a JSP container such as Tomcat
and install a servlet into it.
Upon completion this tutorial servlet should have the following structure:
iBD (You can call it whatever you like)
|-- index.html
|-- sarissa.js
|-- ibd.js
|-- ibd.css
|-- ibd.xsl
|-- WEB-INF
|-- web.xml
|-- dwr.xml
|-- lib (populate this with all the jsword jars, its dependent jars and dwr)
|-- jsword-1.6.jar
|-- javatar-2.5.jar
|-- jdom-1.1.1.jar
|-- commons-codec-1.4.jar
|-- httpcore-4.1.jar
|-- httpclient-4.1-beta1.jar
|-- commons-logging-1.1.1.jar
|-- commons-net-2.2.jar
|-- lucene-core-3.0.3.jar
|-- lucene-snowball-3.0.3.jar
|-- lucene-analyzers-3.0.3.jar
|-- lucene-smartcn-3.0.3.jar
|-- icu4j-4_6.jar
|-- dwr.jar
dwr.xml defines how DWR binds to Java, and for the sake of this tutorial, it binds to JSword's DwrBridge
with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
"http://directwebremoting.org/schema/dwr20.dtd">
<dwr>
<allow>
<create creator="new" javascript="JSword">
<param name="class" value="org.crosswire.jsword.bridge.DWRBridge"/>
</create>
</allow>
</dwr>
web.xml defines the servlet as a binding to DWR and, for the purposes of this tutorial,
has the following content:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<display-name>JSword</display-name>
<description>Servlet interface to the Bible</description>
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
</web-app>
index.html needs to bring in DWR and Sarissa, along with our tutorial JavaScript and CSS StyleSheet.
For the purposes of this tutorial, it has the following content:
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>iBD</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<-- JSword.js is auto-generated by DWR via dwr.xml -->
<script type='text/javascript' src='dwr/interface/JSword.js'></script>
<-- engine.js and util.js are dug out of the dwr.jar -->
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
<-- Bring in the ability to transform OSIS XML into HTML in the user's Web Browser. -->
<script type='text/javascript' src='sarissa.js'></script>
<-- Bring in our JavaScript and CSS StyleSheet -->
<script type='text/javascript' src='tutorial.js'></script>
<link rel="stylesheet" type="text/css" href="tutorial.css"/>
</head>
<body onload="init()">
<!-- The dropdown for books, in this case Bibles, starts out empty -->
<div id="bibleBox">
<select id="pick" onchange="pick();"></select>
</div>
<!-- A text entry and a button both call locate -->
<div id="locateBox" align="left">
<input type="text" id="passageRequest" onkeypress="dwr.util.onReturn(event, locate)"/>
<button type="button" id="passageButton" onclick="locate();">Locate</button>
</div>
<!-- A text entry and a button both call search -->
<div id="searchbox">
<input type="text" id="searchRequest" onclick="dwr.util.onReturn(event, search)"/>
<button type="button" id="searchButton" onclick="search();">Search</button>
</div>
<div id="display"></div>
</body>
</html>
iBD.js is the glue that connects the HTML to DWR's generated JSword.js,
and for the sake of this tutorial it is the following. Note, this should not be a final production representation.
It is not error friendly. It doesn't do any caching. It reparses the iBD.xsl repeatedly, but does not need to.
// Define how the OSIS document that is returned from JSword is styled.
var stylesheet = "iBD.xsl";
// Prevent the server from being hammered.
var verseLimit = 100;
/**
* Prepare the page for use
*/
function init()
{
// Use a Google styled "Loading" message in the upper right corner
dwr.util.useLoadingMessage();
// Display the current SWORD path as a diagnostic.
//JSword.getSwordPath(loadDiagnostic);
// Populate the books dropdown.
// The last argument is an asynchronous callback
JSword.getInstalledBooks("bookCategory=Bible", loadBooks);
// Constrain the display area to be within the boundary of the window.
window.onresize = ibdResize;
}
/*
* Resize the height of the display area.
* I tried pixels but it does not work for IE.
*/
function ibdResize()
{
var top = $("searchBox");
var bottom = $("display");
var offset = top.offsetTop + top.offsetHeight + 1;
var windowHeight = document.body.clientHeight;
var newHeight = windowHeight - offset;
bottom.style.height = (newHeight / windowHeight) * 100 + "%";
}
/**
* Load the list of known Books
*/
function loadBooks(data)
{
// Empty the list.
dwr.util.removeAllOptions("books");
// Then populate it with data, using column "0" as the key and "1" as the display value
// Use "0", "0" to only show the books "initials"
dwr.util.addOptions("books", data, "0", "1");
}
/**
* Called when book data has been fetched
*/
function loadDisplay(data)
{
// Get an XSLT processor that can use xslDoc to do the transform
var processor = new XSLTProcessor();
// Load the stylesheet so that we can transform the document
var xslDoc = Sarissa.getDomDocument();
// Synchronously load the stylesheet do that it is immediately available.
// Otherwise, this will fail.
xslDoc.async = false;
xslDoc.load(stylesheet);
processor.importStylesheet(xslDoc);
// Now take the answer from the locate and parse it into DOM
var parser = new DOMParser();
var dom = parser.parseFromString(data, "text/xml");
// Finally, transform and display the results in one fell swoop.
Sarissa.updateContentFromNode(dom, $("display"), processor);
}
/**
* A Bible has been picked. If there is anything to locate or search, then do it.
*/
function pick()
{
// When the book changes, take what ever is in locate and get it.
// If that doesn't work then try what ever is in search.
locate() || search();
}
/**
* Locate a passage.
*/
function locate()
{
var book = getBook();
var ref = getPassage();
if (book && ref)
{
// Get the OSIS representation from the book for the reference
// But limit the number of verses
// Arrange for asynchronous loading of the display
JSword.getOSISString(book, ref, verseLimit, loadDisplay);
return true;
}
return false;
}
/**
* Perform a search
*/
function search()
{
var book = getBook();
var search = getSearch();
if (book && search)
{
// Get the reference for the search
// and asynchrounously load it in to the locate box
JSword.search(book, search, setPassage);
return true;
}
return false;
}
/**
* Get the search request.
*/
function getSearch()
{
return dwr.util.getValue("searchRequest");
}
/**
* Perform a search.
*/
function setSearch(query)
{
// Whenever we stuff a value into search request
dwr.util.setValue("searchRequest", query);
// do the search
search();
// Allow this to be used in an anchor that ignores its href
return false;
}
/**
* Get the passage to locate.
*/
function getPassage()
{
return dwr.util.getValue("passageRequest");
}
/**
* Locate a passage
*/
function setPassage(ref)
{
// whenever we stuff a value in locate
// Note: search merely stuffs a value here.
dwr.util.setValue("passageRequest", ref);
// go get the content.
locate();
// Allow this to be used in an anchor that ignores its href
return false;
}
/**
* Get the selected book.
*/
function getBook()
{
return dwr.util.getValue("books");
}
/**
* Set the book to search or locate against
*/
function setBook(book)
{
// When ever a book is set
dwr.util.setValue("books", book);
// See if there is something we can locate or search.
pick();
// Allow this to be used in an anchor that ignores its href
return false;
}
JSword.js is generated on demand by DWR, reflecting a JavaScript representation of DwrBridge
and it has the following content:
// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;
if (JSword == null) var JSword = {};
JSword._path = '/jsword/dwr';
JSword.search = function(p0, p1, callback) {
dwr.engine._execute(JSword._path, 'JSword', 'search', p0, p1, callback);
}
JSword.match = function(p0, p1, p2, callback) {
dwr.engine._execute(JSword._path, 'JSword', 'match', p0, p1, p2, callback);
}
JSword.isIndexed = function(p0, callback) {
dwr.engine._execute(JSword._path, 'JSword', 'isIndexed', p0, callback);
}
JSword.getInstalledBooks = function(p0, callback) {
dwr.engine._execute(JSword._path, 'JSword', 'getInstalledBooks', p0, callback);
}
JSword.getOSISString = function(p0, p1, p2, callback) {
dwr.engine._execute(JSword._path, 'JSword', 'getOSISString', p0, p1, p2, callback);
}
JSword.getSwordPath = function(callback) {
dwr.engine._execute(JSword._path, 'JSword', 'getSwordPath', callback);
}
|