[Tynstep-svn] r186 - in trunk/step: step-core/src/main/java/com/tyndalehouse/step/core step-core/src/main/java/com/tyndalehouse/step/core/models step-core/src/main/java/com/tyndalehouse/step/core/service step-core/src/main/java/com/tyndalehouse/step/core/service/impl step-core/src/main/java/com/tyndalehouse/step/core/xsl step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl step-core/src/main/resources/com/tyndalehouse/step/core/service/impl step-core/src/test/java/com/tyndalehouse/step/core step-core/src/test/java/com/tyndalehouse/step/core/service step-core/src/test/java/com/tyndalehouse/step/core/xsl step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl step-parent step-web step-web/src/main/java/com/tyndalehouse/step/rest step-web/src/main/java/com/tyndalehouse/step/rest/controllers step-web/src/main/java/com/tyndalehouse/step/rest/wrappers step-web/src/main/webapp step-web/src/main/webapp/WEB-INF step-web/src/main/webapp/css step-web/src/main/webapp/css/ui-layout step-web/src/main/webapp/images step-web/src/main/webapp/js

ChrisBurrell at crosswire.org ChrisBurrell at crosswire.org
Mon Nov 15 13:23:39 MST 2010


Author: ChrisBurrell
Date: 2010-11-15 13:23:39 -0700 (Mon, 15 Nov 2010)
New Revision: 186

Added:
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/IPSample.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/InterlinearProvider.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XslConversionType.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XsltProviders.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/DualKey.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImpl.java
   trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/interlinear.xsl
   trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/
   trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java
   trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/
   trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl/
   trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImplTest.java
   trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/JsonExceptionResolver.java
   trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/wrappers/
   trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/wrappers/HtmlWrapper.java
   trunk/step/step-web/src/main/webapp/images/wait16.gif
   trunk/step/step-web/src/main/webapp/js/bookmark.js
Modified:
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/BibleVersion.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/BibleInformationService.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/JSwordService.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/BibleInformationServiceImpl.java
   trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java
   trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/default.xsl
   trunk/step/step-parent/pom.xml
   trunk/step/step-web/pom.xml
   trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java
   trunk/step/step-web/src/main/webapp/WEB-INF/mvc-config.xml
   trunk/step/step-web/src/main/webapp/WEB-INF/web.xml
   trunk/step/step-web/src/main/webapp/css/initial-layout.css
   trunk/step/step-web/src/main/webapp/css/passage.css
   trunk/step/step-web/src/main/webapp/css/ui-layout/layout-default.css
   trunk/step/step-web/src/main/webapp/index.jsp
   trunk/step/step-web/src/main/webapp/js/init.js
   trunk/step/step-web/src/main/webapp/js/passage.js
   trunk/step/step-web/src/main/webapp/js/passage_toolbar.js
Log:
updated with interlinear

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/BibleVersion.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/BibleVersion.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/BibleVersion.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -3,8 +3,7 @@
 import java.io.Serializable;
 
 /**
- * Contains information about a bible version to be displayed on the screen in
- * the UI
+ * Contains information about a bible version to be displayed on the screen in the UI
  * 
  * @author CJBurrell
  * 
@@ -14,17 +13,25 @@
     private String initials;
     private String name;
     private String language;
+    private boolean hasStrongs;
 
+    public boolean isHasStrongs() {
+        return this.hasStrongs;
+    }
+
+    public void setHasStrongs(final boolean hasStrongs) {
+        this.hasStrongs = hasStrongs;
+    }
+
     /**
      * @return the initials
      */
     public String getInitials() {
-        return initials;
+        return this.initials;
     }
 
     /**
-     * @param initials
-     *            the initials to set
+     * @param initials the initials to set
      */
     public void setInitials(final String initials) {
         this.initials = initials;
@@ -34,12 +41,11 @@
      * @return the name
      */
     public String getName() {
-        return name;
+        return this.name;
     }
 
     /**
-     * @param name
-     *            the name to set
+     * @param name the name to set
      */
     public void setName(final String name) {
         this.name = name;
@@ -49,12 +55,11 @@
      * @return the language
      */
     public String getLanguage() {
-        return language;
+        return this.language;
     }
 
     /**
-     * @param language
-     *            the language to set
+     * @param language the language to set
      */
     public void setLanguage(final String language) {
         this.language = language;

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -1,5 +1,9 @@
 package com.tyndalehouse.step.core.models;
 
+import static com.tyndalehouse.step.core.xsl.XslConversionType.DEFAULT;
+
+import com.tyndalehouse.step.core.xsl.XslConversionType;
+
 /**
  * Outlines a list of options available in lookup
  * 
@@ -8,25 +12,31 @@
  */
 public enum LookupOption {
     /** Strong numbers */
-    STRONG_NUMBERS("StrongsNumbers", "Strongs"),
+    STRONG_NUMBERS("StrongsNumbers", "Strongs", XslConversionType.INTERLINEAR),
 
     /** Morphology */
-    MORPHOLOGY("Morph", "Morphology"),
+    MORPHOLOGY("Morph", "Morphology", XslConversionType.INTERLINEAR),
     /**
      * Interlinears are available when Strongs are available.
      */
-    INTERLINEAR("Interlinear");
+    INTERLINEAR("Interlinear", XslConversionType.INTERLINEAR);
 
     private final String xsltParameterName;
     private final String displayName;
+    private XslConversionType stylesheet;
 
     private LookupOption(final String xsltParameterName) {
-        this(xsltParameterName, xsltParameterName);
+        this(xsltParameterName, xsltParameterName, DEFAULT);
     }
 
-    private LookupOption(final String xsltParameterName, final String displayName) {
+    private LookupOption(final String xsltParameterName, final XslConversionType stylesheet) {
+        this(xsltParameterName, xsltParameterName, stylesheet);
+    }
+
+    private LookupOption(final String xsltParameterName, final String displayName, final XslConversionType stylesheet) {
         this.xsltParameterName = xsltParameterName;
         this.displayName = displayName;
+        this.stylesheet = stylesheet;
     }
 
     /**
@@ -39,4 +49,8 @@
     public String getDisplayName() {
         return this.displayName;
     }
+
+    public XslConversionType getStylesheet() {
+        return this.stylesheet;
+    }
 }

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/BibleInformationService.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/BibleInformationService.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/BibleInformationService.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -29,9 +29,10 @@
      * @param version the initials that identify the bible version
      * @param reference the reference
      * @param lookupOptions options to set for retrieval
+     * @param interlinearVersion version to use as the interlinear
      * @return the HTML string passed back for consumption
      */
-    String getPassageText(String version, String reference, List<LookupOption> lookupOptions);
+    String getPassageText(String version, String reference, List<LookupOption> lookupOptions, String interlinearVersion);
 
     /**
      * 

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/JSwordService.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/JSwordService.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/JSwordService.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -14,55 +14,12 @@
      * @param version version to lookup
      * @param reference the reference to lookup
      * @param options the list of options for the lookup operation
+     * @param interlinearVersion the version to add if there is an interlinear request, or blank if not
      * @return the OSIS text in an HTML form
      */
-    String getOsisText(String version, String reference, List<LookupOption> options);
+    String getOsisText(String version, String reference, List<LookupOption> options, String interlinearVersion);
 
     /**
-     * returns the language of the version specified
-     * 
-     * @param version the version to lookup
-     * @return the language code
-     */
-    String getLanguage(String version);
-
-    /**
-     * retrieves the short form of the key that is a human readable form
-     * 
-     * @param version the version to be retrieved from
-     * @param reference the reference to be converted
-     * @return a human readable version of the key
-     */
-    String getReadableKey(String version, String reference);
-
-    /**
-     * returns the actual reference, removing the strong pattern + first initial. For e.g. removes strong:H from
-     * "strong:H00002"
-     * 
-     * @param reference reference to parse
-     * @return the key in the dictionary to be used for lookup purposes
-     * @throws ActionException the action exception
-     */
-    String getLookupKeyFromReference(String reference);
-
-    /**
-     * dependant on the reference, we return a different module to lookup the definition. For e.g. we use a Greek
-     * dictionary to lookup a reference starting strong:Gxxxxx
-     * 
-     * @param reference reference to be looked up
-     * @return the module initials to use for the dictionary lookup
-     */
-    String getInitialsFromReference(String reference);
-
-    /**
-     * Looks up a definition given a reference in the default JSword module
-     * 
-     * @param reference reference, for e.g. a Strong number
-     * @return the definition
-     */
-    String lookupStrongDefinition(String reference);
-
-    /**
      * looks up any installed module
      * 
      * @param bibleCategory the category of the bible to lookup

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/BibleInformationServiceImpl.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/BibleInformationServiceImpl.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/BibleInformationServiceImpl.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -8,6 +8,7 @@
 
 import org.crosswire.jsword.book.Book;
 import org.crosswire.jsword.book.BookCategory;
+import org.crosswire.jsword.book.FeatureType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -41,6 +42,7 @@
             v.setName(b.getName());
             v.setInitials(b.getInitials());
             v.setLanguage(b.getLanguage().getName());
+            v.setHasStrongs(b.hasFeature(FeatureType.STRONGS_NUMBERS));
             versions.add(v);
         }
 
@@ -48,8 +50,9 @@
         return versions;
     }
 
-    public String getPassageText(final String version, final String reference, final List<LookupOption> options) {
-        return this.jsword.getOsisText(version, reference, options);
+    public String getPassageText(final String version, final String reference, final List<LookupOption> options,
+            final String interlinearVersion) {
+        return this.jsword.getOsisText(version, reference, options, interlinearVersion);
     }
 
     public List<EnrichedLookupOption> getAllFeatures() {

Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -1,15 +1,16 @@
 package com.tyndalehouse.step.core.service.impl;
 
-import static java.lang.String.format;
+import static com.tyndalehouse.step.core.xsl.XslConversionType.DEFAULT;
+import static org.apache.commons.lang.StringUtils.isNotBlank;
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.xml.transform.TransformerException;
 
-import org.apache.commons.lang.StringUtils;
-import org.crosswire.common.util.Language;
 import org.crosswire.common.xml.Converter;
 import org.crosswire.common.xml.SAXEventProvider;
 import org.crosswire.common.xml.TransformingSAXEventProvider;
@@ -22,8 +23,6 @@
 import org.crosswire.jsword.book.Books;
 import org.crosswire.jsword.book.FeatureType;
 import org.crosswire.jsword.passage.NoSuchKeyException;
-import org.crosswire.jsword.versification.BibleInfo;
-import org.jdom.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -31,6 +30,7 @@
 import com.tyndalehouse.step.core.exceptions.StepInternalException;
 import com.tyndalehouse.step.core.models.LookupOption;
 import com.tyndalehouse.step.core.service.JSwordService;
+import com.tyndalehouse.step.core.xsl.XslConversionType;
 
 /**
  * a service providing a wrapper around JSword
@@ -39,36 +39,8 @@
  * 
  */
 public class JSwordServiceImpl implements JSwordService {
-    /**
-     * the pattern with which strong references in OSIS start
-     */
-    public static final String STRONG_PATTERN_START = "strong:";
-
-    /**
-     * a greek marker for strong numbers, e.g. strong:Gxxxx
-     */
-    public static final char STRONG_GREEK_MARKER = 'G';
-
-    /**
-     * Strong hebrew marker, for e.g. strong:Hxxxx
-     */
-    public static final char STRONG_HEBREW_MARKER = 'H';
-
-    /**
-     * Initials of default Hebrew JSword module to use for lookup of dictionary definitions
-     */
-    public static final String STRONG_HEBREW_DICTIONARY_INITIALS = "StrongsHebrew";
-
-    /**
-     * Initials of default Strong JSword greek dictionary module for lookup of dictionary definitions
-     */
-    public static final String STRONG_GREEK_DICTIONARY_INITIALS = "StrongsGreek";
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    // TODO: autowire, or drive from properties file
-    // @Autowired
-    private final String xslConversionDefinition = "default.xsl";
-
     @SuppressWarnings("unchecked")
     public List<Book> getModules(final BookCategory bibleCategory) {
         if (bibleCategory == null) {
@@ -84,25 +56,28 @@
         return Books.installed().getBooks(bf);
     }
 
-    public String getOsisText(final String version, final String reference, final List<LookupOption> options) {
+    public String getOsisText(final String version, final String reference, final List<LookupOption> options,
+            final String interlinearVersion) {
         this.logger.debug("Retrieving text for ({}, {})", version, reference);
 
         try {
             final Book currentBook = Books.installed().getBook(version);
             final BookData bookData = new BookData(currentBook, currentBook.getKey(reference));
+            final Set<XslConversionType> requiredTransformation = identifyStyleSheet(options);
 
             final SAXEventProvider osissep = bookData.getSAXEventProvider();
             TransformingSAXEventProvider htmlsep = null;
             htmlsep = (TransformingSAXEventProvider) new Converter() {
 
-                // TODO cache XSL in memory or Transforming SAX Event Provider
                 public SAXEventProvider convert(final SAXEventProvider provider) throws TransformerException {
                     try {
+                        // for now, we just assume that we'll only have one option, but this may change later
                         final TransformingSAXEventProvider tsep = new TransformingSAXEventProvider(getClass()
-                                .getResource(JSwordServiceImpl.this.xslConversionDefinition).toURI(), osissep);
+                                .getResource(requiredTransformation.iterator().next().getFile()).toURI(), osissep);
 
                         // set parameters here
-                        setOptions(tsep, options);
+                        setOptions(tsep, options, version, reference);
+                        setupInterlinearOptions(tsep, interlinearVersion, reference);
 
                         // then return
                         return tsep;
@@ -110,6 +85,7 @@
                         throw new StepInternalException("Failed to load resource correctly", e);
                     }
                 }
+
             }.convert(osissep);
             return XMLUtil.writeToString(htmlsep);
         } catch (final NoSuchKeyException e) {
@@ -124,6 +100,19 @@
         }
     }
 
+    private Set<XslConversionType> identifyStyleSheet(final List<LookupOption> options) {
+        final Set<XslConversionType> chosenOptions = new HashSet<XslConversionType>();
+
+        for (final LookupOption lo : options) {
+            chosenOptions.add(lo.getStylesheet());
+        }
+
+        if (chosenOptions.isEmpty()) {
+            chosenOptions.add(DEFAULT);
+        }
+        return chosenOptions;
+    }
+
     public List<LookupOption> getFeatures(final String version) {
         final Book book = Books.installed().getBook(version);
         final List<LookupOption> options = new ArrayList<LookupOption>(3);
@@ -138,110 +127,33 @@
         return options;
     }
 
+    private void setupInterlinearOptions(final TransformingSAXEventProvider tsep, final String interlinearVersion,
+            final String reference) {
+        if (tsep.getParameter(LookupOption.INTERLINEAR.getXsltParameterName()) != null) {
+            tsep.setParameter("interlinearReference", reference);
+
+            if (isNotBlank(interlinearVersion)) {
+                tsep.setParameter("interlinearVersion", interlinearVersion);
+            } else {
+                // depending on OT or NT, we select a default interlinear version
+
+            }
+        }
+    }
+
     /**
      * This method sets up the options for the XSLT transformation
      * 
      * @param tsep the xslt transformer
      * @param options the options available
+     * @param version the version to initialise a potential interlinear with
+     * @param textScope the scope of the text to lookup
+     * @return the XSLT that will give me the correct transformation
      */
-    protected void setOptions(final TransformingSAXEventProvider tsep, final List<LookupOption> options) {
+    protected void setOptions(final TransformingSAXEventProvider tsep, final List<LookupOption> options,
+            final String version, final String textScope) {
         for (final LookupOption lookupOption : options) {
             tsep.setParameter(lookupOption.getXsltParameterName(), true);
         }
     }
-
-    public String getLanguage(final String version) {
-        final Book currentBook = Books.installed().getBook(version);
-        if (currentBook == null) {
-            return null;
-        }
-
-        final Language language = currentBook.getLanguage();
-        if (language != null) {
-            return language.getCode();
-        }
-        return null;
-    }
-
-    /**
-     * IMPROVEMENT NOTE that this is not thread safe, since JSword relies on statics in the background
-     */
-    public String getReadableKey(final String version, final String reference) {
-        try {
-            BibleInfo.setFullBookName(false);
-            return Books.installed().getBook(version).getKey(reference).getName();
-        } catch (final NoSuchKeyException e) {
-            throw new StepInternalException("Unable to get readable key from OSIS reference", e);
-        }
-    }
-
-    public String lookupStrongDefinition(final String reference) {
-        if (StringUtils.isEmpty(reference)) {
-            throw new StepInternalException("Reference was not provided");
-        }
-        this.logger.error("definition lookup command");
-        final String initials = getInitialsFromReference(reference);
-        final String lookupKey = getLookupKeyFromReference(reference);
-
-        try {
-            // TODO: ensure a lookup key exists!
-            final Book currentBook = Books.installed().getBook(initials);
-            final BookData data = new BookData(currentBook, currentBook.getKey(lookupKey));
-            return doXslt(data, data.getOsisFragment());
-        } catch (final NoSuchKeyException e) {
-            throw new StepInternalException("Unable to find specified reference", e);
-        } catch (final BookException e) {
-            throw new StepInternalException("An error occurred looking up the passage", e);
-        }
-    }
-
-    /**
-     * does a simple xslt transformation to show the definition on the screen
-     * 
-     * @param data data to be shown
-     * @param osisFragment osisFragment to transform
-     * @return the xslted definition ready to be displayed on the user's screen
-     */
-    private String doXslt(final BookData data, final Element osisFragment) {
-        // if (data == null) {
-        // return "";
-        // }
-        //
-        // try {
-        // final SAXEventProvider osissep = data.getSAXEventProvider();
-        // // TODO: do some work on the XSLT definition
-        // final TransformingSAXEventProvider htmlsep = (TransformingSAXEventProvider) new ConfigurableHTMLConverter()
-        // .convert(osissep);
-        // final String text = XMLUtil.writeToString(htmlsep);
-        // return text;
-        // } catch (final SAXException e) {
-        // Reporter.informUser(this, e);
-        // } catch (final BookException e) {
-        // Reporter.informUser(this, e);
-        // } catch (final TransformerException e) {
-        // Reporter.informUser(this, e);
-        // }
-        return "";
-    }
-
-    public String getInitialsFromReference(final String reference) {
-        if (reference.toLowerCase().startsWith(STRONG_PATTERN_START)) {
-            final int charPosition = STRONG_PATTERN_START.length();
-            if (reference.charAt(charPosition) == STRONG_HEBREW_MARKER) {
-                return STRONG_HEBREW_DICTIONARY_INITIALS;
-            } else if (reference.charAt(charPosition) == STRONG_GREEK_MARKER) {
-                return STRONG_GREEK_DICTIONARY_INITIALS;
-            }
-            // continuing will throw exception
-        }
-        throw new StepInternalException(format("Dictionary reference not recognised: %s", reference));
-    }
-
-    public String getLookupKeyFromReference(final String reference) {
-        if (reference.toLowerCase().startsWith(STRONG_PATTERN_START)) {
-            // remove strong: or strong:
-            return reference.substring(STRONG_PATTERN_START.length());
-        }
-        throw new StepInternalException(format("Lookup key not recognised: %s", reference));
-    }
 }

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/IPSample.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/IPSample.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/IPSample.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,7 @@
+package com.tyndalehouse.step.core.xsl;
+
+public class IPSample {
+    public String getWord(final Object o) {
+        return "hello";
+    }
+}

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/InterlinearProvider.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/InterlinearProvider.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/InterlinearProvider.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,13 @@
+package com.tyndalehouse.step.core.xsl;
+
+public interface InterlinearProvider {
+    /**
+     * This is the more specific method
+     * 
+     * @param verseNumber helps locate the word by adding location awareness
+     * @param strong identifies the word by its root in the original language
+     * @param morph identifies the morphology of the word (i.e. the grammar)
+     * @return the word in the original language (Greek, Hebrew, etc.)
+     */
+    String getWord(String verseNumber, String strong, String morph);
+}

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XslConversionType.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XslConversionType.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XslConversionType.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,25 @@
+package com.tyndalehouse.step.core.xsl;
+
+public enum XslConversionType {
+    /**
+     * a standard text, where only one line of text will be displayed, (i.e. normal style)
+     */
+    DEFAULT("default.xsl"),
+    /**
+     * identifies a text that requires outputs on multiple lines
+     */
+    INTERLINEAR("interlinear.xsl");
+
+    /**
+     * indicates the xsl conversion file to use for this work
+     */
+    private final String file;
+
+    private XslConversionType(final String file) {
+        this.file = file;
+    }
+
+    public String getFile() {
+        return this.file;
+    }
+}

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XsltProviders.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XsltProviders.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/XsltProviders.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,15 @@
+package com.tyndalehouse.step.core.xsl;
+
+public enum XsltProviders {
+    INTERLINEAR_PROVIDER("InterlinearProvider");
+
+    private final String paramName;
+
+    XsltProviders(final String paramName) {
+        this.paramName = paramName;
+    }
+
+    public String getParamName() {
+        return this.paramName;
+    }
+}

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/DualKey.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/DualKey.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/DualKey.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,45 @@
+package com.tyndalehouse.step.core.xsl.impl;
+
+import static java.lang.String.format;
+
+/**
+ * A Strong Morph Map takes two keys, and gives one word back. The following DualKey relies on hashCode. The hash
+ * function relies on toString so T and S need to have fast toString().
+ * 
+ * @author Chris
+ * 
+ */
+public class DualKey<T, S> {
+    private final T t;
+    private final S s;
+
+    public DualKey(final T t, final S s) {
+        this.t = t;
+        this.s = s;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof DualKey)) {
+            return false;
+        }
+
+        final DualKey<?, ?> k = (DualKey<?, ?>) obj;
+        return this.t.equals(k.t) && this.s.equals(k.s);
+    }
+
+    @Override
+    public int hashCode() {
+        // we need to return the same hashcode based on s and t
+        return (this.t.toString().concat(this.s.toString())).hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return format("%s-%s", this.t, this.s);
+    }
+}

Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImpl.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImpl.java	                        (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImpl.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,311 @@
+package com.tyndalehouse.step.core.xsl.impl;
+
+import static org.apache.commons.lang.StringUtils.isBlank;
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.apache.commons.lang.StringUtils.split;
+import static org.crosswire.jsword.book.OSISUtil.ATTRIBUTE_W_LEMMA;
+import static org.crosswire.jsword.book.OSISUtil.ATTRIBUTE_W_MORPH;
+import static org.crosswire.jsword.book.OSISUtil.OSIS_ATTR_OSISID;
+import static org.crosswire.jsword.book.OSISUtil.OSIS_ELEMENT_VERSE;
+import static org.crosswire.jsword.book.OSISUtil.OSIS_ELEMENT_W;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.crosswire.jsword.book.Book;
+import org.crosswire.jsword.book.BookData;
+import org.crosswire.jsword.book.BookException;
+import org.crosswire.jsword.book.Books;
+import org.crosswire.jsword.passage.NoSuchKeyException;
+import org.jdom.Content;
+import org.jdom.Element;
+
+import com.tyndalehouse.step.core.exceptions.StepInternalException;
+import com.tyndalehouse.step.core.xsl.InterlinearProvider;
+
+/**
+ * This object is not purposed to be used as a singleton. It builds up textual information on initialisation, and is
+ * specific to requests. On initialisation, the OSIS XML is retrieved and iterated through to find all strong/morph
+ * candidates
+ * 
+ * @author Chris
+ * 
+ */
+public class InterlinearProviderImpl implements InterlinearProvider {
+    /**
+     * set of characters used to distinguish one strong from another
+     */
+    private static final String STRONG_SEPARATORS = " []";
+
+    /**
+     * bestAccuracy gives a word by its dual key (strong,morph)
+     */
+    private final Map<DualKey<String, String>, String> bestAccuracy = new HashMap<DualKey<String, String>, String>();
+
+    /**
+     * limited accuracy tries to do a location look up by using the verse number as part of the key
+     */
+    private final Map<DualKey<String, String>, List<String>> limitedAccuracy = new HashMap<DualKey<String, String>, List<String>>();
+
+    /**
+     * finally, this is just a list of all the strongs and their mappings. Still would be fairly good as long as the
+     * same word isn't used multiple times.
+     */
+    private final Map<String, String> worstAccuracy = new HashMap<String, String>();
+
+    /**
+     * sets up the interlinear provider with the correct version and text scope.
+     * 
+     * @param version the version to use to set up the interlinear
+     * @param textScope the text scope reference, defining the bounds of the lookup
+     */
+    public InterlinearProviderImpl(final String version, final String textScope) {
+        // first check whether the values passed in are correct
+        if (isBlank(version) || isBlank(textScope)) {
+            return;
+        }
+
+        final Book currentBook = Books.installed().getBook(version);
+        BookData bookData;
+
+        try {
+            bookData = new BookData(currentBook, currentBook.getKey(textScope));
+            scanForTextualInformation(bookData.getOsisFragment());
+        } catch (final NoSuchKeyException e) {
+            throw new StepInternalException(e.getMessage(), e);
+        } catch (final BookException e) {
+            throw new StepInternalException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * package private version for testing purposes
+     */
+    InterlinearProviderImpl() {
+        // exposing package private constructor
+    }
+
+    public String getWord(final String verseNumber, final String strong, final String morph) {
+        // we use a linked hashset, because we want the behaviour of a set while we add to it,
+        // but at the end, we will want to return the elements in order
+        final Set<String> results = new LinkedHashSet<String>();
+        if (isBlank(strong)) {
+            // we might as well return, as we have no information to go on
+        }
+
+        // the keys passed in may have multiple references and morphologies, therefore, we need to lookup
+        // multiple items.
+
+        // ///////////////////////////
+        // TODO we need to remove the strong:[G/H] from the string, since some versions don't have it
+        // and key things differently
+        // ////////////////////////////
+        final String[] strongs = split(strong, STRONG_SEPARATORS);
+        final String[] morphs = split(morph, STRONG_SEPARATORS);
+
+        // if we have no morphs, then might as well call getWord(verseNumber, strong) straight away
+        if (morphs == null || morphs.length == 0) {
+            results.add(getWord(verseNumber, strong));
+            return convertToString(results);
+        }
+
+        // There are at most strongs.length words, and we might have morphological data to help
+        for (final String s : strongs) {
+            boolean foundMatchForStrong = false;
+
+            // each could be using the morphs we have, so try them all
+            for (final String m : morphs) {
+                // lookup (strong,morph) -> word first
+                final DualKey<String, String> key = new DualKey<String, String>(s, m);
+                final String word = this.bestAccuracy.get(key);
+
+                if (word != null) {
+                    results.add(word);
+                    foundMatchForStrong = true;
+                }
+            }
+
+            // have we found a match? if not, we better try and find one using the verse
+            if (!foundMatchForStrong) {
+                results.add(getWord(verseNumber, strong));
+            }
+        }
+
+        return convertToString(results);
+    }
+
+    /**
+     * Takes a set, and outputs the strings concatenated together (and separated by a space.
+     * 
+     * @param results the results that should be converted to a string
+     * @return
+     */
+    private String convertToString(final Set<String> results) {
+        final Iterator<String> iterator = results.iterator();
+        final StringBuffer sb = new StringBuffer(results.size() * 14);
+
+        // add the first word without a space
+        if (iterator.hasNext()) {
+            sb.append(iterator.next());
+        }
+
+        // add spaces between each element now
+        while (iterator.hasNext()) {
+            sb.append(' ');
+            sb.append(iterator.next());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * returns words based on strong and verse number only
+     * 
+     * @param verseNumber the verse number
+     * @param strong the strong reference
+     * @return a word that matches or the empty string
+     */
+    String getWord(final String verseNumber, final String strong) {
+        if (strong != null && verseNumber != null) {
+            final DualKey<String, String> key = new DualKey<String, String>(strong, verseNumber);
+
+            final List<String> list = this.limitedAccuracy.get(key);
+            if (list != null && list.size() != 0) {
+                return list.get(0);
+            }
+        }
+        // so we didn't find anything even with the verse number, now we need to look for a strong
+        // on its own
+        final String lastChance = this.worstAccuracy.get(strong);
+        if (lastChance == null) {
+            return "";
+        }
+
+        // it is important to return an empty string here
+        return lastChance;
+    }
+
+    /**
+     * retrieves context textual information from a passage
+     * 
+     * @param osisFragment the fragment of XML that should be examined
+     */
+    private void scanForTextualInformation(final Element osisFragment) {
+        // redirect with null verse
+        scanForTextualInformation(osisFragment, null);
+    }
+
+    /**
+     * setups all the initial textual information for fast retrieval during XSL transformation.
+     * 
+     * @param element element to start with.
+     * @param currentVerse the current verse to use as part of the key
+     */
+    @SuppressWarnings("unchecked")
+    private void scanForTextualInformation(final Element element, final String currentVerse) {
+        // check to see if we've hit a new verse, if so, we update the verse
+        final String verseToBeUsed = element.getName().equals(OSIS_ELEMENT_VERSE) ? element
+                .getAttributeValue(OSIS_ATTR_OSISID) : currentVerse;
+
+        // check to see if we've hit a node of interest
+        if (element.getName().equals(OSIS_ELEMENT_W)) {
+            extractTextualInfoFromNode(element, verseToBeUsed);
+            return;
+        }
+
+        // iterate through all children and call recursively
+        Object data = null;
+        Element ele = null;
+        final Iterator<Content> contentIter = element.getContent().iterator();
+        while (contentIter.hasNext()) {
+            data = contentIter.next();
+            if (data instanceof Element) {
+                ele = (Element) data;
+                scanForTextualInformation(ele, verseToBeUsed);
+            }
+        }
+    }
+
+    /**
+     * retrieves textual information and adds it to the provider
+     * 
+     * @param element the element to extract information from
+     * @param verseReference verse reference to use for locality of keying
+     */
+    private void extractTextualInfoFromNode(final Element element, final String verseReference) {
+        final String strong = element.getAttributeValue(ATTRIBUTE_W_LEMMA);
+        final String morph = element.getAttributeValue(ATTRIBUTE_W_MORPH);
+        final String word = element.getText();
+
+        // do we need to do any manipulation? probably not because we are going to be
+        // comparing against other OSIS XML texts which should be formatted in the same way!
+        // however, some attributes may contain multiple strongs and morphs tagged to one word.
+        // therefore we do need to split the text.
+        final String[] strongs = split(strong, STRONG_SEPARATORS);
+        final String[] morphs = split(morph, STRONG_SEPARATORS);
+
+        // ///////////////////////////
+        // TODO we need to remove the strong:[G/H] from the string, since some versions don't have it
+        // and key things differently
+        // ////////////////////////////
+
+        if (strongs == null) {
+            return;
+        }
+
+        // there is no way of know which strong goes with which morph, and we only
+        // have one phrase anyway
+        for (int ii = 0; ii < strongs.length; ii++) {
+            if (morphs != null && morphs.length != 0) {
+                for (int jj = 0; jj < morphs.length; jj++) {
+                    addTextualInfo(verseReference, strongs[ii], morphs[jj], word);
+                }
+            } else {
+                addTextualInfo(verseReference, strongs[ii], null, word);
+            }
+        }
+    }
+
+    /**
+     * Finally, we have some information to add to this provider. We try and add it in an efficient fashion.
+     * 
+     * So, how do we store this? The most meaningful piece of data is a STRONG number, since it identifies the word that
+     * we want to retrieve. Without the strong number, we don't have any information at all. Therefore, the first level
+     * of lookup should be by Strong number.
+     * 
+     * Morphology-wise, each word might have a small number of options, so a linked list will do for this
+     * 
+     * One would think that strong -> morph -> word will be unique. In the case of having just strong, we should use
+     * verse locality to maximise our chance of getting the right word (strong -> verse -> word)
+     * 
+     * So in summary, we use: strong -> morph -> word strong -> verse -> list(word) (not unique)
+     * 
+     * @param verseReference the verse reference that specifies locality (least important factor)
+     * @param strong the strong number (identifies the root/meaning of the word)
+     * @param morph the morphology (identifies how the used is word in the sentence - i.e. grammar)
+     * @param word the word to be stored
+     */
+    void addTextualInfo(final String verseReference, final String strong, final String morph, final String word) {
+
+        if (isNotBlank(strong) && isNotBlank(morph)) {
+            final DualKey<String, String> strongMorphKey = new DualKey<String, String>(strong, morph);
+            this.bestAccuracy.put(strongMorphKey, word);
+        }
+
+        final DualKey<String, String> strongVerseKey = new DualKey<String, String>(strong, verseReference);
+
+        List<String> verseKeyedStrongs = this.limitedAccuracy.get(strongVerseKey);
+        if (verseKeyedStrongs == null) {
+            verseKeyedStrongs = new ArrayList<String>();
+            this.limitedAccuracy.put(strongVerseKey, verseKeyedStrongs);
+        }
+        verseKeyedStrongs.add(word);
+
+        // finally add it to the worst accuracy - i.e. just based on strongs (could probably refactor)
+        this.worstAccuracy.put(strong, word);
+    }
+}

Modified: trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/default.xsl
===================================================================
--- trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/default.xsl	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/default.xsl	2010-11-15 20:23:39 UTC (rev 186)
@@ -49,6 +49,7 @@
        but font does not. Therefore we use font as a span.
     -->
   <!-- gdef and hdef refer to hebrew and greek definitions keyed by strongs -->
+  <!-- The absolute base for relative references. -->
   <xsl:param name="greek.def.protocol" select="'gdef:'"/>
   <xsl:param name="hebrew.def.protocol" select="'hdef:'"/>
   <xsl:param name="lex.def.protocol" select="'lex:'"/>
@@ -348,10 +349,10 @@
         -->
       <xsl:choose>
         <xsl:when test="$TinyVNum = 'true' and $Notes = 'true'">
-          <span class="w"><a name="{@osisID}"><span class="verseNumber"><xsl:value-of select="$versenum"/></span></a></span>
+          <a name="{@osisID}"><span class="verseNumber"><xsl:value-of select="$versenum"/></span></a>
         </xsl:when>
         <xsl:when test="$TinyVNum = 'true' and $Notes = 'false'">
-          <span class="w"><span class="verseNumber"><xsl:value-of select="$versenum"/></span></span>
+          <span class="verseNumber"><xsl:value-of select="$versenum"/></span>
         </xsl:when>
         <xsl:when test="$TinyVNum = 'false' and $Notes = 'true'">
           <a name="{@osisID}">(<xsl:value-of select="$versenum"/>)</a>
@@ -459,33 +460,8 @@
   <!--=======================================================================-->
   <xsl:template match="w">
     <!-- Output the content followed by all the lemmas and then all the morphs. -->
-    <xsl:variable name="innerWordText"><xsl:apply-templates/></xsl:variable>
-    <xsl:variable name="isNextSiblingText" select="following-sibling::*[1]/text() = ''" />    
-  	<xsl:variable name="nextNodeCharClass" select="''" />
-  	<xsl:if test="following-sibling::*[1]/text() != ''">
-  		<xsl:variable name="nextNodeCharClass" select="'punctuNext'" />
-  	</xsl:if>
-  	<span class="w">
-  	<span class="text">
-    <xsl:choose>
-    	<xsl:when test="not(normalize-space($innerWordText))">&#160;</xsl:when>
-    	<xsl:otherwise><xsl:value-of select="$innerWordText" /></xsl:otherwise>	
-    </xsl:choose></span>
-    <xsl:if test="$StrongsNumbers = 'true' and (starts-with(@lemma, 'x-Strongs:') or starts-with(@lemma, 'strong:'))">
-      <xsl:call-template name="lemma">
-        <xsl:with-param name="lemma" select="@lemma"/>
-      </xsl:call-template>
-    </xsl:if>
-    <xsl:if test="$Morph = 'true' and (starts-with(@morph, 'x-Robinson:') or starts-with(@morph, 'robinson:'))">
-      <xsl:call-template name="morph">
-        <xsl:with-param name="morph" select="@morph"/>
-      </xsl:call-template>
-    </xsl:if>
-    <xsl:if test="$StrongsNumbers = 'true' and starts-with(@lemma, 'lemma.Strong:')">
-      <xsl:call-template name="lemma">
-        <xsl:with-param name="lemma" select="@lemma"/>
-      </xsl:call-template>
-    </xsl:if>
+    <xsl:apply-templates/>
+    
     <!--
         except when followed by a text node or non-printing node.
         This is true whether the href is output or not.
@@ -495,22 +471,11 @@
     <xsl:if test="$siblings[$next-position] and name($siblings[$next-position]) != ''">
       <xsl:text> </xsl:text>
     </xsl:if>
-    </span>
   </xsl:template>
   
   <xsl:template match="w" mode="jesus">
     <!-- Output the content followed by all the lemmas and then all the morphs. -->
     <xsl:apply-templates mode="jesus"/>
-    <xsl:if test="$StrongsNumbers = 'true' and (starts-with(@lemma, 'x-Strongs:') or starts-with(@lemma, 'strong:'))">
-      <xsl:call-template name="lemma">
-        <xsl:with-param name="lemma" select="@lemma"/>
-      </xsl:call-template>
-    </xsl:if>
-    <xsl:if test="$Morph = 'true' and (starts-with(@morph, 'x-Robinson:') or starts-with(@morph, 'robinson:'))">
-      <xsl:call-template name="morph">
-        <xsl:with-param name="morph" select="@morph"/>
-      </xsl:call-template>
-    </xsl:if>
     <!--
         except when followed by a text node or non-printing node.
         This is true whether the href is output or not.
@@ -522,135 +487,6 @@
     </xsl:if>
   </xsl:template>
   
-  <xsl:template name="lemma">
-    <xsl:param name="lemma"/>
-    <xsl:param name="part" select="0"/>
-    <xsl:param name="className" />
-    <xsl:param name="finalText" />
-    
-    <xsl:variable name="orig-lemma" select="substring-after($lemma, ':')"/>
-    <xsl:variable name="protocol">
-      <xsl:choose>
-        <xsl:when test="substring($orig-lemma, 1, 1) = 'H'">
-          <xsl:value-of select="$hebrew.def.protocol"/>
-        </xsl:when>
-        <xsl:when test="substring($orig-lemma, 1, 1) = 'G'">
-          <xsl:value-of select="$greek.def.protocol"/>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:value-of select="$lex.def.protocol"/>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:variable name="separator">
-      <xsl:choose>
-        <xsl:when test="contains($orig-lemma, '|') ">
-          <xsl:value-of select="'|'"/>
-        </xsl:when>
-        <xsl:when test="contains($orig-lemma, ' ')">
-          <xsl:value-of select="' '"/>
-        </xsl:when>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:variable name="sub">
-      <xsl:choose>
-        <xsl:when test="$separator != '' and $part = '0'">
-          <xsl:value-of select="$part + 1"/>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:value-of select="$part"/>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:choose>
-      <xsl:when test="$protocol = $lex.def.protocol">
-        <font class="lex">[<xsl:value-of select="$orig-lemma"/>]</font>
-      </xsl:when>
-      <xsl:when test="$separator = ''">
-        <span class="strongs {$className} {$orig-lemma}"><xsl:value-of select="concat($finalText, ' ', format-number(substring($orig-lemma, 2), '#'))" />
-        </span>
-      </xsl:when>
-      <xsl:otherwise>
-        <xsl:variable name="processedLemma" select="substring-before($orig-lemma, $separator)" />
-        <xsl:call-template name="lemma">
-          <xsl:with-param name="lemma" select="substring-after($lemma, $separator)"/>
-          <xsl:with-param name="className" select="concat($className, $processedLemma)" />
-          <xsl:with-param name="finalText" select="concat($finalText, ' ', format-number(substring($processedLemma,2),'#'))" />
-          <xsl:with-param name="part">
-            <xsl:choose>
-              <xsl:when test="$sub">
-                <xsl:value-of select="$sub + 1"/>
-              </xsl:when>
-              <xsl:otherwise>
-                <xsl:value-of select="1"/>
-              </xsl:otherwise>
-            </xsl:choose>
-          </xsl:with-param>
-        </xsl:call-template>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-  <xsl:template name="morph">
-    <xsl:param name="morph"/>
-    <xsl:param name="part" select="0"/>
-    <xsl:variable name="orig-work" select="substring-before($morph, ':')"/>
-    <xsl:variable name="orig-morph" select="substring-after($morph, ':')"/>
-    <xsl:variable name="protocol">
-      <xsl:choose>
-        <xsl:when test="starts-with($orig-work, 'x-Robinson') or starts-with($orig-work, 'robinson')">
-          <xsl:value-of select="$greek.morph.protocol"/>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:value-of select="$hebrew.morph.protocol"/>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:variable name="separator">
-      <xsl:choose>
-        <xsl:when test="contains($orig-morph, '|')">
-          <xsl:value-of select="'|'"/>
-        </xsl:when>
-        <xsl:when test="contains($orig-morph, ' ')">
-          <xsl:value-of select="' '"/>
-        </xsl:when>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:variable name="sub">
-      <xsl:choose>
-        <xsl:when test="$separator != '' and $part = '0'">
-          <xsl:value-of select="$part + 1"/>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:value-of select="$part"/>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:variable>
-    <xsl:choose>
-      <xsl:when test="$separator = ''">
-        <!-- <sub class="morph"><a href="{$protocol}{$orig-morph}">M<xsl:number level="any" from="/osis//verse" format="1"/><xsl:number value="$sub" format="a"/></a></sub> -->
-        <sub class="morph"><a href="{$protocol}{$orig-morph}"><xsl:value-of select="$orig-morph"/></a></sub>
-      </xsl:when>
-      <xsl:otherwise>
-        <!-- <sub class="morph"><a href="{$protocol}{substring-before($orig-morph, $separator)}">M<xsl:number level="single" from="/osis//verse" format="1"/><xsl:number value="$sub" format="a"/></a>, </sub> -->
-        <sub class="morph"><a href="{$protocol}{substring-before($orig-morph, $separator)}"><xsl:value-of select="substring-before($orig-morph, $separator)"/></a>, </sub>
-        <xsl:call-template name="morph">
-          <xsl:with-param name="morph" select="substring-after($morph, $separator)"/>
-          <xsl:with-param name="part">
-            <xsl:choose>
-              <xsl:when test="$sub">
-                <xsl:value-of select="$sub + 1"/>
-              </xsl:when>
-              <xsl:otherwise>
-                <xsl:value-of select="1"/>
-              </xsl:otherwise>
-            </xsl:choose>
-          </xsl:with-param>
-        </xsl:call-template>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
   <!--=======================================================================-->
   <xsl:template match="seg">
     <xsl:choose>
@@ -726,11 +562,11 @@
 
   <!--=======================================================================-->
   <xsl:template match="speaker[@who = 'Jesus']">
-    <font class="jesus"><xsl:apply-templates mode="jesus"/></font>
+    <span class="jesus"><xsl:apply-templates mode="jesus"/></span>
   </xsl:template>
 
   <xsl:template match="speaker">
-    <font class="speech"><xsl:apply-templates/></font>
+    <span class="speech"><xsl:apply-templates/></span>
   </xsl:template>
 
   <!--=======================================================================-->
@@ -985,7 +821,7 @@
   <!-- While a BR is a break, if it is immediately followed by punctuation,
        indenting this rule can introduce whitespace.
     -->
-  <xsl:template match="lb"><br/></xsl:template>
+  <xsl:template match="lb"><br /></xsl:template>
   <xsl:template match="lb" mode="jesus"><br/></xsl:template>
 
   <xsl:template match="list">
@@ -1150,23 +986,23 @@
   </xsl:template>
   
   <xsl:template match="q[@who = 'Jesus']">
-    <font class="jesus"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></font>
+    <span class="jesus"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></span>
   </xsl:template>
 
   <xsl:template match="q[@type = 'blockquote']">
-    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></blockquote>
+    <span class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></span>
   </xsl:template>
 
   <xsl:template match="q[@type = 'blockquote']" mode="jesus">
-    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></blockquote>
+    <span class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></span>
   </xsl:template>
 
   <xsl:template match="q[@type = 'citation']">
-    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></blockquote>
+    <span class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></span>
   </xsl:template>
 
   <xsl:template match="q[@type = 'citation']" mode="jesus">
-    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></blockquote>
+    <span class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></span>
   </xsl:template>
 
   <xsl:template match="q[@type = 'embedded']">
@@ -1335,13 +1171,13 @@
         <em><xsl:apply-templates/></em>
       </xsl:when>
       <xsl:when test="$style = 'line-through'">
-        <font class="strike"><xsl:apply-templates/></font>
+        <span class="strike"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'normal'">
-        <font class="normal"><xsl:apply-templates/></font>
+        <span class="normal"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'small-caps'">
-        <font class="small-caps"><xsl:apply-templates/></font>
+        <span class="small-caps"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'sub'">
         <sub><xsl:apply-templates/></sub>
@@ -1350,10 +1186,10 @@
         <sup><xsl:apply-templates/></sup>
       </xsl:when>
       <xsl:when test="$style = 'underline'">
-        <u><xsl:apply-templates/></u>
+        <span class="underline"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'x-caps'">
-        <font class="caps"><xsl:apply-templates/></font>
+        <span class="caps"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:otherwise>
         <xsl:apply-templates/>
@@ -1389,13 +1225,13 @@
         <em><xsl:apply-templates/></em>
       </xsl:when>
       <xsl:when test="$style = 'line-through'">
-        <font class="strike"><xsl:apply-templates/></font>
+        <span class="strike"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'normal'">
-        <font class="normal"><xsl:apply-templates/></font>
+        <span class="normal"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'small-caps'">
-        <font class="small-caps"><xsl:apply-templates/></font>
+        <span class="small-caps"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'sub'">
         <sub><xsl:apply-templates/></sub>
@@ -1404,10 +1240,10 @@
         <sup><xsl:apply-templates/></sup>
       </xsl:when>
       <xsl:when test="$style = 'underline'">
-        <u><xsl:apply-templates/></u>
+        <span class="underline"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:when test="$style = 'x-caps'">
-        <font class="caps"><xsl:apply-templates/></font>
+        <span class="caps"><xsl:apply-templates/></span>
       </xsl:when>
       <xsl:otherwise>
         <xsl:apply-templates/>
@@ -1473,16 +1309,6 @@
     </xsl:if>
   </xsl:template>
 
-  <!-- If the parent of the text is a verse then, we need to wrap in span. This applies
-  to any punctuation really, since all other words should be contained in a W  -->
-  <xsl:template match="text()">
-  		<xsl:choose>
-	  		<xsl:when test="name(..) = 'verse' and normalize-space(.) != ''"><span class="w"><span class="text"><xsl:value-of select="."/></span></span></xsl:when>
-	  		<xsl:when test="normalize-space(.) != ''"><xsl:value-of select="."/></xsl:when>
-  		</xsl:choose>
-  </xsl:template>
-
-
   <xsl:template match="text()" mode="small-caps">
   <xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
   </xsl:template>

Added: trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/interlinear.xsl
===================================================================
--- trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/interlinear.xsl	                        (rev 0)
+++ trunk/step/step-core/src/main/resources/com/tyndalehouse/step/core/service/impl/interlinear.xsl	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,1554 @@
+<?xml version="1.0"?>
+<!--
+ * Distribution License:
+ * JSword is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License, version 2.1 as published by
+ * the Free Software Foundation. This program is distributed in the hope
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The License is available on the internet at:
+ *       http://www.gnu.org/copyleft/lgpl.html
+ * or by writing to:
+ *      Free Software Foundation, Inc.
+ *      59 Temple Place - Suite 330
+ *      Boston, MA 02111-1307, USA
+ *
+ * Copyright: 2005
+ *     The copyright to this program is held by it's authors.
+ *
+ * ID: $Id: default.xsl 1943 2009-03-25 11:43:28Z dmsmith $
+ -->
+ <!--
+ * Transforms OSIS to HTML for viewing within JSword browsers.
+ * Note: There are custom protocols which the browser must handle.
+ * 
+ * @see gnu.lgpl.License for license details.
+ *      The copyright to this program is held by it's authors.
+ * @author Joe Walker [joe at eireneh dot com]
+ * @author DM Smith [dmsmith555 at yahoo dot com]
+ * @author Chris Burrell [chris at burrell dot me dot uk] 
+ -->
+ <xsl:stylesheet
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0"
+  xmlns:jsword="http://xml.apache.org/xalan/java"
+  extension-element-prefixes="jsword">
+
+  <!--  Version 3.0 is necessary to get br to work correctly. -->
+  <xsl:output method="html" version="3.0" omit-xml-declaration="yes" indent="no"/>
+
+  <!-- Be very careful about introducing whitespace into the document.
+       strip-space merely remove space between one tag and another tag.
+       This may cause significant whitespace to be removed.
+       
+       It is easy to have apply-templates on a line to itself which if
+       it encounters text before anything else will introduce whitespace.
+       With the browser we are using, span will introduce whitespace
+       but font does not. Therefore we use font as a span.
+    -->
+  <!-- gdef and hdef refer to hebrew and greek definitions keyed by strongs -->
+  <xsl:param name="greek.def.protocol" select="'gdef:'"/>
+  <xsl:param name="hebrew.def.protocol" select="'hdef:'"/>
+  <xsl:param name="lex.def.protocol" select="'lex:'"/>
+  <!-- currently these are not used, but they are for morphologic forms -->
+  <xsl:param name="greek.morph.protocol" select="'gmorph:'"/>
+  <xsl:param name="hebrew.morph.protocol" select="'hmorph:'"/>
+
+  <!-- The absolute base for relative references. -->
+  <xsl:param name="baseURL" select="''"/>
+
+  <!-- Whether to show Strongs or not -->
+  <xsl:param name="StrongsNumbers" select="'false'"/>
+
+  <!-- Whether to show morphologic forms or not -->
+  <xsl:param name="Morph" select="'false'"/>
+
+  <!-- Whether to start each verse on an new line or not -->
+  <xsl:param name="VLine" select="'false'"/>
+
+  <!-- Whether to show non-canonical "headings" or not -->
+  <xsl:param name="Headings" select="'true'"/>
+
+  <!-- Whether to show notes or not -->
+  <xsl:param name="Notes" select="'false'"/>
+
+  <!-- Whether to have linking cross references or not -->
+  <xsl:param name="XRef" select="'false'"/>
+
+  <!-- Whether to output no Verse numbers -->
+  <xsl:param name="NoVNum" select="'false'"/>
+
+  <!-- Whether to output Verse numbers or not -->
+  <xsl:param name="VNum" select="'true'"/>
+
+  <!-- Whether to output Chapter and Verse numbers or not -->
+  <xsl:param name="CVNum" select="'false'"/>
+
+  <!-- Whether to output Book, Chapter and Verse numbers or not -->
+  <xsl:param name="BCVNum" select="'false'"/>
+
+  <!-- Whether to output superscript verse numbers or normal size ones -->
+  <xsl:param name="TinyVNum" select="'true'"/>
+
+  <!-- The order of display. Hebrew is rtl (right to left) -->
+  <xsl:param name="direction" select="'ltr'"/>
+
+  <!-- Whether to show an interlinear and the provider helping with the lookup -->
+  <xsl:param name="Interlinear" select="'false'" />
+  <xsl:param name="interlinearVersion" select="''" />
+  <xsl:param name="interlinearReference" select="''" />
+
+  <!-- Create a global key factory from which OSIS ids will be generated -->
+  <xsl:variable name="keyf" select="jsword:org.crosswire.jsword.passage.PassageKeyFactory.instance()"/>
+  <!-- Create a global number shaper that can transform 0-9 into other number systems. -->
+  <xsl:variable name="shaper" select="jsword:org.crosswire.common.icu.NumberShaper.new()"/>
+  
+  <!--  set up interlinear provider, if we have requested it -->
+  <xsl:variable name="interlinearProvider" select="jsword:com.tyndalehouse.step.core.xsl.impl.InterlinearProviderImpl.new(string($interlinearVersion), string($interlinearReference))" />
+
+  <!--=======================================================================-->
+  <xsl:template match="/">
+      <div>
+        <!-- If there are notes, output a table with notes in the 2nd column. -->
+        <!-- There is a rendering bug which prevents the notes from adhering to the right edge. -->
+        <xsl:choose>
+          <xsl:when test="$Notes = 'true' and //note[not(@type = 'x-strongsMarkup')]">
+            <xsl:choose>
+              <xsl:when test="$direction != 'rtl'">
+                <table cols="2" cellpadding="5" cellspacing="5">
+                  <tr>
+                    <!-- The two rows are swapped until the bug is fixed. -->
+                    <td valign="top" class="notes">
+                      <p>&#160;</p>
+                      <xsl:apply-templates select="//verse" mode="print-notes"/>
+                    </td>
+                    <td valign="top" class="text">
+                      <xsl:apply-templates/>
+                    </td>
+                  </tr>
+                </table>
+              </xsl:when>
+              <xsl:otherwise>
+                <!-- reverse the table for Right to Left languages -->
+                <table cols="2" cellpadding="5" cellspacing="5">
+                  <!-- In a right to left, the alignment should be reversed too -->
+                  <tr align="right">
+                    <td valign="top" class="notes">
+                      <p>&#160;</p>
+                      <xsl:apply-templates select="//note" mode="print-notes"/>
+                    </td>
+                    <td valign="top" class="text">
+                      <xsl:apply-templates/>
+                    </td>
+                  </tr>
+                </table>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </div>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <!--
+    == A proper OSIS document has osis as its root.
+    == We dig deeper for its content.
+    -->
+  <xsl:template match="osis">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <!--
+    == An OSIS document may contain more that one work.
+    == Each work is held in an osisCorpus element.
+    == If there is only one work, then this element will (should) be absent.
+    == Process each document in turn.
+    == It might be reasonable to dig into the header element of each work
+    == and get its title.
+    == Otherwise, we ignore the header and work elements and just process
+    == the osisText elements.
+    -->
+  <xsl:template match="osisCorpus">
+    <xsl:apply-templates select="osisText"/>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <!--
+    == Each work has an osisText element.
+    == We ignore the header and work elements and process its div elements.
+    == While divs can be milestoned, the osisText element requires container
+    == divs.
+    -->
+  <xsl:template match="osisText">
+    <xsl:apply-templates select="div"/>
+  </xsl:template>
+  
+  <!-- Ignore headers and its elements -->
+  <xsl:template match="header"/>
+  <xsl:template match="revisionDesc"/>
+  <xsl:template match="work"/>
+   <!-- <xsl:template match="title"/> who's parent is work -->
+  <xsl:template match="contributor"/>
+  <xsl:template match="creator"/>
+  <xsl:template match="subject"/>
+  <!-- <xsl:template match="date"/> who's parent is work -->
+  <xsl:template match="description"/>
+  <xsl:template match="publisher"/>
+  <xsl:template match="type"/>
+  <xsl:template match="format"/>
+  <xsl:template match="identifier"/>
+  <xsl:template match="source"/>
+  <xsl:template match="language"/>
+  <xsl:template match="relation"/>
+  <xsl:template match="coverage"/>
+  <xsl:template match="rights"/>
+  <xsl:template match="scope"/>
+  <xsl:template match="workPrefix"/>
+  <xsl:template match="castList"/>
+  <xsl:template match="castGroup"/>
+  <xsl:template match="castItem"/>
+  <xsl:template match="actor"/>
+  <xsl:template match="role"/>
+  <xsl:template match="roleDesc"/>
+  <xsl:template match="teiHeader"/>
+  <xsl:template match="refSystem"/>
+
+
+  <!-- Ignore titlePage -->
+  <xsl:template match="titlePage"/>
+
+  <!--=======================================================================-->
+  <!-- 
+    == Div provides the major containers for a work.
+    == Divs are milestoneable.
+    -->
+  <xsl:template match="div[@type='x-center']">
+    <div align="center">
+      <xsl:apply-templates/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="div">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="div" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <!-- Handle verses as containers and as a start verse.                     -->
+  <xsl:template match="verse[not(@eID)]">
+    <!-- output each preverse element in turn -->
+    <xsl:for-each select=".//*[@subType = 'x-preverse' or @subtype = 'x-preverse']">
+      <xsl:choose>
+        <xsl:when test="local-name() = 'title'">
+          <!-- Always show canonical titles or if headings is turned on -->
+          <xsl:if test="@canonical = 'true' or $Headings = 'true'">
+            <h3 class="heading"><xsl:apply-templates /></h3>
+          </xsl:if>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+    <!-- Handle the KJV paragraph marker. -->
+    <xsl:if test="milestone[@type = 'x-p']"><br/><br/></xsl:if>
+    <!-- If the verse doesn't start on its own line and -->
+    <!-- the verse is not the first verse of a set of siblings, -->
+    <!-- output an extra space. -->
+    <xsl:if test="$VLine = 'false' and preceding-sibling::*[local-name() = 'verse']">
+      <xsl:text>&#160;</xsl:text>
+    </xsl:if>
+    <!-- Always output the verse -->
+    <xsl:choose>
+      <xsl:when test="$VLine = 'true'">
+        <div class="l"><a name="{@osisID}"><xsl:call-template name="versenum"/></a><xsl:apply-templates/></div>
+      </xsl:when>
+      <xsl:otherwise>
+        <span class="interlinear"><xsl:call-template name="versenum"/><xsl:apply-templates/></span>
+        <!-- Follow the verse with an extra space -->
+        <!-- when they don't start on lines to themselves -->
+        <xsl:text> </xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="verse[not(@eID)]" mode="jesus">
+    <!-- If the verse doesn't start on its own line and -->
+    <!-- the verse is not the first verse of a set of siblings, -->
+    <!-- output an extra space. -->
+    <xsl:if test="$VLine = 'false' and preceding-sibling::*[local-name() = 'verse']">
+      <xsl:text>&#160;</xsl:text>
+    </xsl:if>
+    <xsl:variable name="title" select=".//title"/>
+    <xsl:if test="string-length($title) > 0">
+      <h3 class="heading"><xsl:value-of select="$title"/></h3>
+    </xsl:if>
+    <!-- Handle the KJV paragraph marker. -->
+    <xsl:if test="milestone[@type = 'x-p']"><br/><br/></xsl:if>
+    <!-- Always output the verse -->
+    <xsl:choose>
+      <xsl:when test="$VLine = 'true'">
+        <div class="l"><a name="{@osisID}"><xsl:call-template name="versenum"/></a><xsl:apply-templates mode="jesus"/></div>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="versenum"/><xsl:apply-templates mode="jesus"/>
+        <!-- Follow the verse with an extra space -->
+        <!-- when they don't start on lines to themselves -->
+        <xsl:text> </xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="verse" mode="print-notes">
+    <xsl:if test=".//note[not(@type) or not(@type = 'x-strongsMarkup')]">
+      <xsl:variable name="passage" select="jsword:getValidKey($keyf, @osisID)"/>
+      <a href="#{substring-before(concat(@osisID, ' '), ' ')}">
+        <xsl:value-of select="jsword:getName($passage)"/>
+      </a>
+      <xsl:apply-templates select=".//note" mode="print-notes" />
+      <div><xsl:text>&#160;</xsl:text></div>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template name="versenum">
+    <!-- Are verse numbers wanted? -->
+    <xsl:if test="$NoVNum = 'false'">
+      <!-- An osisID can be a space separated list of them -->
+      <xsl:variable name="firstOsisID" select="substring-before(concat(@osisID, ' '), ' ')"/>
+      <xsl:variable name="book" select="substring-before($firstOsisID, '.')"/>
+      <xsl:variable name="chapter" select="jsword:shape($shaper, substring-before(substring-after($firstOsisID, '.'), '.'))"/>
+      <!-- If n is present use it for the number -->
+      <xsl:variable name="verse">
+        <xsl:choose>
+          <xsl:when test="@n">
+            <xsl:value-of select="jsword:shape($shaper, string(@n))"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="jsword:shape($shaper, substring-after(substring-after($firstOsisID, '.'), '.'))"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <xsl:variable name="versenum">
+        <xsl:choose>
+          <xsl:when test="$BCVNum = 'true'">
+            <xsl:variable name="passage" select="jsword:getValidKey($keyf, @osisID)"/>
+            <xsl:value-of select="jsword:getName($passage)"/>
+          </xsl:when>
+          <xsl:when test="$CVNum = 'true'">
+            <xsl:value-of select="concat($chapter, ' : ', $verse)"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$verse"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:variable>
+      <!--
+        == Surround versenum with dup
+        -->
+      <xsl:choose>
+        <xsl:when test="$TinyVNum = 'true' and $Notes = 'true'">
+          <span class="w"><a name="{@osisID}"><span class="verseNumber"><xsl:value-of select="$versenum"/></span></a></span>
+        </xsl:when>
+        <xsl:when test="$TinyVNum = 'true' and $Notes = 'false'">
+          <span class="w"><span class="verseNumber"><xsl:value-of select="$versenum"/></span></span>
+        </xsl:when>
+        <xsl:when test="$TinyVNum = 'false' and $Notes = 'true'">
+          <a name="{@osisID}">(<xsl:value-of select="$versenum"/>)</a>
+          <xsl:text> </xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          (<xsl:value-of select="$versenum"/>)
+          <xsl:text> </xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+    <xsl:if test="$VNum = 'false' and $Notes = 'true'">
+      <a name="{@osisID}"></a>
+    </xsl:if>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="a">
+    <a href="{@href}"><xsl:apply-templates/></a>
+  </xsl:template>
+
+  <xsl:template match="a" mode="jesus">
+    <a href="{@href}"><xsl:apply-templates mode="jesus"/></a>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <!-- When we encounter a note, we merely output a link to the note. -->
+  <xsl:template match="note[@type = 'x-strongsMarkup']"/>
+  <xsl:template match="note[@type = 'x-strongsMarkup']" mode="jesus"/>
+  <xsl:template match="note[@type = 'x-strongsMarkup']" mode="print-notes"/>
+
+  <xsl:template match="note">
+    <xsl:if test="$Notes = 'true'">
+      <!-- If there is a following sibling that is a note, emit a separator -->
+      <xsl:variable name="siblings" select="../child::node()"/>
+      <xsl:variable name="next-position" select="position() + 1"/>
+      <xsl:choose>
+        <xsl:when test="name($siblings[$next-position]) = 'note'">
+          <sup class="note"><a href="#note-{generate-id(.)}"><xsl:call-template name="generateNoteXref"/></a>, </sup>
+        </xsl:when>
+        <xsl:otherwise>
+          <sup class="note"><a href="#note-{generate-id(.)}"><xsl:call-template name="generateNoteXref"/></a></sup>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="note" mode="jesus">
+    <xsl:if test="$Notes = 'true'">
+     <!-- If there is a following sibling that is a note, emit a separator -->
+      <xsl:variable name="siblings" select="../child::node()"/>
+      <xsl:variable name="next-position" select="position() + 1"/>
+      <xsl:choose>
+        <xsl:when test="$siblings[$next-position] and name($siblings[$next-position]) = 'note'">
+          <sup class="note"><a href="#note-{generate-id(.)}"><xsl:call-template name="generateNoteXref"/></a>, </sup>
+        </xsl:when>
+        <xsl:otherwise>
+          <sup class="note"><a href="#note-{generate-id(.)}"><xsl:call-template name="generateNoteXref"/></a></sup>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="note" mode="print-notes">
+    <div class="margin">
+      <strong><xsl:call-template name="generateNoteXref"/></strong>
+      <a name="note-{generate-id(.)}">
+        <xsl:text> </xsl:text>
+      </a>
+      <xsl:apply-templates/>
+    </div>
+  </xsl:template>
+
+  <!--
+    == If the n attribute is present then use that for the cross ref otherwise create a letter.
+    == Note: numbering restarts with each verse.
+    -->
+  <xsl:template name="generateNoteXref">
+    <xsl:choose>
+      <xsl:when test="@n">
+        <xsl:value-of select="@n"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:number level="any" from="/osis//verse" format="a"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="p">
+    <p><xsl:apply-templates/></p>
+  </xsl:template>
+  
+  <xsl:template match="p" mode="jesus">
+    <p><xsl:apply-templates mode="jesus"/></p>
+  </xsl:template>
+  
+  <!--=======================================================================-->
+  <xsl:template match="p" mode="print-notes">
+    <!-- FIXME: This ignores text in the note. -->
+    <!-- don't put para's in notes -->
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="w">
+    <!-- Output the content followed by all the lemmas and then all the morphs. -->
+    <xsl:variable name="innerWordText"><xsl:apply-templates/></xsl:variable>
+    <xsl:variable name="isNextSiblingText" select="following-sibling::*[1]/text() = ''" />    
+  	<xsl:variable name="nextNodeCharClass" select="''" />
+  	<xsl:if test="following-sibling::*[1]/text() != ''">
+  		<xsl:variable name="nextNodeCharClass" select="'punctuNext'" />
+  	</xsl:if>
+  	<span class="w">
+  	<span class="text">
+    <xsl:choose>
+    	<xsl:when test="not(normalize-space($innerWordText))">&#160;</xsl:when>
+    	<xsl:otherwise><xsl:value-of select="$innerWordText" /></xsl:otherwise>	
+    </xsl:choose></span>
+    <xsl:if test="$StrongsNumbers = 'true' and (starts-with(@lemma, 'x-Strongs:') or starts-with(@lemma, 'strong:'))">
+      <xsl:call-template name="lemma">
+        <xsl:with-param name="lemma" select="@lemma"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$Morph = 'true' and (starts-with(@morph, 'x-Robinson:') or starts-with(@morph, 'robinson:'))">
+      <xsl:call-template name="morph">
+        <xsl:with-param name="morph" select="@morph"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$StrongsNumbers = 'true' and starts-with(@lemma, 'lemma.Strong:')">
+      <xsl:call-template name="lemma">
+        <xsl:with-param name="lemma" select="@lemma"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$Interlinear = 'true'">
+      <xsl:call-template name="interlinear">
+        <xsl:with-param name="lemma" select="@lemma" />
+        <xsl:with-param name="morph" select="@morph" />
+        <xsl:with-param name="osisID" select="../@osisID" />
+      </xsl:call-template>
+    </xsl:if>
+    
+    <!--
+        except when followed by a text node or non-printing node.
+        This is true whether the href is output or not.
+    -->
+    <xsl:variable name="siblings" select="../child::node()"/>
+    <xsl:variable name="next-position" select="position() + 1"/>
+    <xsl:if test="$siblings[$next-position] and name($siblings[$next-position]) != ''">
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    </span>
+  </xsl:template>
+  
+  <xsl:template match="w" mode="jesus">
+    <!-- Output the content followed by all the lemmas and then all the morphs. -->
+    <xsl:apply-templates mode="jesus"/>
+    <xsl:if test="$StrongsNumbers = 'true' and (starts-with(@lemma, 'x-Strongs:') or starts-with(@lemma, 'strong:'))">
+      <xsl:call-template name="lemma">
+        <xsl:with-param name="lemma" select="@lemma"/>
+      </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="$Morph = 'true' and (starts-with(@morph, 'x-Robinson:') or starts-with(@morph, 'robinson:'))">
+      <xsl:call-template name="morph">
+        <xsl:with-param name="morph" select="@morph"/>
+      </xsl:call-template>
+    </xsl:if>
+    <!--
+        except when followed by a text node or non-printing node.
+        This is true whether the href is output or not.
+    -->
+    <xsl:variable name="siblings" select="../child::node()"/>
+    <xsl:variable name="next-position" select="position() + 1"/>
+    <xsl:if test="$siblings[$next-position] and name($siblings[$next-position]) != ''">
+      <xsl:text> </xsl:text>
+    </xsl:if>
+  </xsl:template>
+  
+  <xsl:template name="lemma">
+    <xsl:param name="lemma"/>
+    <xsl:param name="part" select="0"/>
+    <xsl:param name="className" />
+    <xsl:param name="finalText" />
+    
+    <xsl:variable name="orig-lemma" select="substring-after($lemma, ':')"/>
+    <xsl:variable name="protocol">
+      <xsl:choose>
+        <xsl:when test="substring($orig-lemma, 1, 1) = 'H'">
+          <xsl:value-of select="$hebrew.def.protocol"/>
+        </xsl:when>
+        <xsl:when test="substring($orig-lemma, 1, 1) = 'G'">
+          <xsl:value-of select="$greek.def.protocol"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$lex.def.protocol"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="separator">
+      <xsl:choose>
+        <xsl:when test="contains($orig-lemma, '|') ">
+          <xsl:value-of select="'|'"/>
+        </xsl:when>
+        <xsl:when test="contains($orig-lemma, ' ')">
+          <xsl:value-of select="' '"/>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="sub">
+      <xsl:choose>
+        <xsl:when test="$separator != '' and $part = '0'">
+          <xsl:value-of select="$part + 1"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$part"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$protocol = $lex.def.protocol">
+        <font class="lex">[<xsl:value-of select="$orig-lemma"/>]</font>
+      </xsl:when>
+      <xsl:when test="$separator = ''">
+        <span class="strongs {$className} {$orig-lemma}"><xsl:value-of select="concat($finalText, ' ', format-number(substring($orig-lemma, 2), '#'))" />
+        </span>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="processedLemma" select="substring-before($orig-lemma, $separator)" />
+        <xsl:call-template name="lemma">
+          <xsl:with-param name="lemma" select="substring-after($lemma, $separator)"/>
+          <xsl:with-param name="className" select="concat($className, $processedLemma)" />
+          <xsl:with-param name="finalText" select="concat($finalText, ' ', format-number(substring($processedLemma,2),'#'))" />
+          <xsl:with-param name="part">
+            <xsl:choose>
+              <xsl:when test="$sub">
+                <xsl:value-of select="$sub + 1"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="1"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:with-param>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="morph">
+    <xsl:param name="morph"/>
+    <xsl:param name="part" select="0"/>
+    <xsl:variable name="orig-work" select="substring-before($morph, ':')"/>
+    <xsl:variable name="orig-morph" select="substring-after($morph, ':')"/>
+    <xsl:variable name="protocol">
+      <xsl:choose>
+        <xsl:when test="starts-with($orig-work, 'x-Robinson') or starts-with($orig-work, 'robinson')">
+          <xsl:value-of select="$greek.morph.protocol"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$hebrew.morph.protocol"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="separator">
+      <xsl:choose>
+        <xsl:when test="contains($orig-morph, '|')">
+          <xsl:value-of select="'|'"/>
+        </xsl:when>
+        <xsl:when test="contains($orig-morph, ' ')">
+          <xsl:value-of select="' '"/>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="sub">
+      <xsl:choose>
+        <xsl:when test="$separator != '' and $part = '0'">
+          <xsl:value-of select="$part + 1"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$part"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$separator = ''">
+        <!-- <sub class="morph"><a href="{$protocol}{$orig-morph}">M<xsl:number level="any" from="/osis//verse" format="1"/><xsl:number value="$sub" format="a"/></a></sub> -->
+        <sub class="morph"><a href="{$protocol}{$orig-morph}"><xsl:value-of select="$orig-morph"/></a></sub>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- <sub class="morph"><a href="{$protocol}{substring-before($orig-morph, $separator)}">M<xsl:number level="single" from="/osis//verse" format="1"/><xsl:number value="$sub" format="a"/></a>, </sub> -->
+        <sub class="morph"><a href="{$protocol}{substring-before($orig-morph, $separator)}"><xsl:value-of select="substring-before($orig-morph, $separator)"/></a>, </sub>
+        <xsl:call-template name="morph">
+          <xsl:with-param name="morph" select="substring-after($morph, $separator)"/>
+          <xsl:with-param name="part">
+            <xsl:choose>
+              <xsl:when test="$sub">
+                <xsl:value-of select="$sub + 1"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="1"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:with-param>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="interlinear">
+    <xsl:param name="lemma" select="''"/>
+    <xsl:param name="morph" select="''"/>
+    <xsl:param name="osisID" />
+    <xsl:variable name="interlinearWord" select="jsword:getWord($interlinearProvider, $osisID, $lemma, $morph)"/>
+    <xsl:choose>
+    	<xsl:when test="normalize-space($interlinearWord) = ''" >&#160;</xsl:when>
+    	<xsl:otherwise><span class="interlinearText"><xsl:value-of select="$interlinearWord"/></span></xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+
+  <!--=======================================================================-->
+  <xsl:template match="seg">
+    <xsl:choose>
+      <xsl:when test="starts-with(@type, 'color:')">
+        <font color="{substring-before(substring-after(@type, 'color: '), ';')}"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="starts-with(@type, 'font-size:')">
+        <font size="{substring-before(substring-after(@type, 'font-size: '), ';')}"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="@type = 'x-variant'">
+        <xsl:if test="@subType = 'x-class-1'">
+          <xsl:apply-templates/>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template match="seg" mode="jesus">
+    <xsl:choose>
+      <xsl:when test="starts-with(@type, 'color:')">
+        <font color="{substring-before(substring-after(@type, 'color: '), ';')}"><xsl:apply-templates mode="jesus"/></font>
+      </xsl:when>
+      <xsl:when test="starts-with(@type, 'font-size:')">
+        <font size="{substring-before(substring-after(@type, 'font-size: '), ';')}"><xsl:apply-templates mode="jesus"/></font>
+      </xsl:when>
+      <xsl:when test="@type = 'x-variant'">
+        <xsl:if test="@subType = 'x-class:1'">
+          <xsl:apply-templates mode="jesus"/>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates mode="jesus"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <!--=======================================================================-->
+  <!-- expansion is OSIS, expan is TEI -->
+  <xsl:template match="abbr">
+    <font class="abbr">
+      <xsl:if test="@expansion">
+        <xsl:attribute name="title">
+          <xsl:value-of select="@expansion"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@expan">
+        <xsl:attribute name="title">
+          <xsl:value-of select="@expan"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:apply-templates/>
+    </font>
+  </xsl:template>
+
+  <xsl:template match="abbr" mode="jesus">
+    <font class="abbr">
+      <xsl:if test="@expansion">
+        <xsl:attribute name="title">
+          <xsl:value-of select="@expansion"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@expan">
+        <xsl:attribute name="title">
+          <xsl:value-of select="@expan"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:apply-templates mode="jesus"/>
+    </font>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="speaker[@who = 'Jesus']">
+    <font class="jesus"><xsl:apply-templates mode="jesus"/></font>
+  </xsl:template>
+
+  <xsl:template match="speaker">
+    <font class="speech"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="title[@subType ='x-preverse' or @subtype = 'x-preverse']">
+  <!-- Done by a line in [verse]
+    <h3 class="heading">
+      <xsl:apply-templates/>
+    </h3>
+  -->
+  </xsl:template>
+
+  <xsl:template match="title[@subType ='x-preverse' or @subtype = 'x-preverse']" mode="jesus">
+  <!-- Done by a line in [verse]
+    <h3 class="heading">
+      <xsl:apply-templates/>
+    </h3>
+  -->
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="title[@level]">
+    <!-- Always show canonical titles or if headings is turned on -->
+    <xsl:if test="@canonical = 'true' or $Headings = 'true'">
+      <xsl:choose>
+        <xsl:when test="@level = '1'">
+          <h1 class="level"><xsl:apply-templates/></h1>
+        </xsl:when>
+        <xsl:when test="@level = '2'">
+          <h2 class="level"><xsl:apply-templates/></h2>
+        </xsl:when>
+        <xsl:when test="@level = '3'">
+          <h3 class="level"><xsl:apply-templates/></h3>
+        </xsl:when>
+        <xsl:when test="@level = '4'">
+          <h4 class="level"><xsl:apply-templates/></h4>
+        </xsl:when>
+        <xsl:when test="@level = '5'">
+          <h5 class="level"><xsl:apply-templates/></h5>
+        </xsl:when>
+        <xsl:otherwise>
+          <h6 class="level"><xsl:apply-templates/></h6>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="title[@level]" mode="jesus">
+    <!-- Always show canonical titles or if headings is turned on -->
+    <xsl:if test="@canonical = 'true' or $Headings = 'true'">
+      <xsl:choose>
+        <xsl:when test="@level = '1'">
+          <h1 class="level"><xsl:apply-templates/></h1>
+        </xsl:when>
+        <xsl:when test="@level = '2'">
+          <h2 class="level"><xsl:apply-templates/></h2>
+        </xsl:when>
+        <xsl:when test="@level = '3'">
+          <h3 class="level"><xsl:apply-templates/></h3>
+        </xsl:when>
+        <xsl:when test="@level = '4'">
+          <h4 class="level"><xsl:apply-templates/></h4>
+        </xsl:when>
+        <xsl:when test="@level = '5'">
+          <h5 class="level"><xsl:apply-templates/></h5>
+        </xsl:when>
+        <xsl:otherwise>
+          <h6 class="level"><xsl:apply-templates/></h6>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="title">
+    <!-- Always show canonical titles or if headings is turned on -->
+    <xsl:if test="@canonical = 'true' or $Headings = 'true'">
+      <h2 class="heading"><xsl:apply-templates/></h2>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="title" mode="jesus">
+    <!-- Always show canonical titles or if headings is turned on -->
+    <xsl:if test="@canonical = 'true' or $Headings = 'true'">
+      <h2 class="heading"><xsl:apply-templates/></h2>
+    </xsl:if>
+  </xsl:template>
+
+  <!--=======================================================================-->
+  <xsl:template match="reference">
+    <xsl:choose>
+      <xsl:when test="$XRef = 'true'">
+        <a href="bible://{@osisRef}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template match="reference" mode="jesus">
+    <xsl:choose>
+      <xsl:when test="$XRef = 'true'">
+        <a href="bible://{@osisRef}"><xsl:apply-templates mode="jesus"/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates mode="jesus"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <!--=======================================================================-->
+  <xsl:template match="caption">
+    <div class="caption"><xsl:apply-templates/></div>
+  </xsl:template>
+  
+  <xsl:template match="caption" mode="jesus">
+    <div class="caption"><xsl:apply-templates/></div>
+  </xsl:template>
+  
+  <xsl:template match="catchWord">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+  <xsl:template match="catchWord" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+  
+  <!--
+      <cell> is handled shortly after <table> below and thus does not appear
+      here.
+  -->
+  
+  <xsl:template match="closer">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+  <xsl:template match="closer" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+  
+  <xsl:template match="date">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+  <xsl:template match="date" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+  
+  <xsl:template match="divineName">
+    <xsl:apply-templates mode="small-caps"/>
+  </xsl:template>
+  
+  <xsl:template match="divineName" mode="jesus">
+    <xsl:apply-templates mode="small-caps"/>
+  </xsl:template>
+  
+  <xsl:template match="figure">
+    <div class="figure">
+      <xsl:choose>
+        <xsl:when test="starts-with(@src, '/')">
+          <img src="{concat($baseURL, @src)}"/>   <!-- FIXME: Not necessarily an image... -->
+        </xsl:when>
+        <xsl:otherwise>
+          <img src="{concat($baseURL, '/',  @src)}"/>   <!-- FIXME: Not necessarily an image... -->
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:apply-templates/>
+    </div>
+  </xsl:template>
+  
+  <xsl:template match="figure" mode="jesus">
+    <div class="figure">
+      <xsl:choose>
+        <xsl:when test="starts-with(@src, '/')">
+          <img src="{concat($baseURL, @src)}"/>   <!-- FIXME: Not necessarily an image... -->
+        </xsl:when>
+        <xsl:otherwise>
+          <img src="{concat($baseURL, '/',  @src)}"/>   <!-- FIXME: Not necessarily an image... -->
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:apply-templates mode="jesus"/>
+    </div>
+  </xsl:template>
+  
+  <xsl:template match="foreign">
+    <em class="foreign"><xsl:apply-templates/></em>
+  </xsl:template>
+  
+  <xsl:template match="foreign" mode="jesus">
+    <em class="foreign"><xsl:apply-templates mode="jesus"/></em>
+  </xsl:template>
+  
+  <!-- This is a subheading. -->
+  <xsl:template match="head//head">
+    <h5 class="head"><xsl:apply-templates/></h5>
+  </xsl:template>
+  
+  <!-- This is a top-level heading. -->
+  <xsl:template match="head">
+    <h4 class="head"><xsl:apply-templates/></h4>
+  </xsl:template>
+  
+  <xsl:template match="index">
+    <a name="index{@id}" class="index"/>
+  </xsl:template>
+
+  <xsl:template match="inscription">
+    <xsl:apply-templates mode="small-caps"/>
+  </xsl:template>
+
+  <xsl:template match="inscription" mode="jesus">
+    <xsl:apply-templates mode="small-caps"/>
+  </xsl:template>
+
+  <xsl:template match="item">
+    <li class="item"><xsl:apply-templates/></li>
+  </xsl:template>
+
+  <xsl:template match="item" mode="jesus">
+    <li class="item"><xsl:apply-templates mode="jesus"/></li>
+  </xsl:template>
+  
+  <!--
+      <item> and <label> are covered by <list> below and so do not appear here.
+  -->
+
+  <xsl:template match="lg">
+    <div class="lg"><xsl:apply-templates/></div>
+  </xsl:template>
+  
+  <xsl:template match="lg" mode="jesus">
+    <div class="lg"><xsl:apply-templates mode="jesus"/></div>
+  </xsl:template>
+  
+  <xsl:template match="lg[@sID or @eID]"/>
+  <xsl:template match="lg[@sID or @eID]" mode="jesus"/>
+
+  <xsl:template match="l[@sID]"/>
+  <xsl:template match="l[@sID]" mode="jesus"/>
+
+  <xsl:template match="l[@eID]"><br/></xsl:template>
+  <xsl:template match="l[@eID]" mode="jesus"><br/></xsl:template>
+
+  <xsl:template match="l">
+    <xsl:apply-templates/><br/>
+  </xsl:template>
+  
+  <xsl:template match="l" mode="jesus">
+    <xsl:apply-templates mode="jesus"/><br/>
+  </xsl:template>
+
+  <!-- While a BR is a break, if it is immediately followed by punctuation,
+       indenting this rule can introduce whitespace.
+    -->
+  <xsl:template match="lb"><br/></xsl:template>
+  <xsl:template match="lb" mode="jesus"><br/></xsl:template>
+
+  <xsl:template match="list">
+    <xsl:choose>
+      <xsl:when test="label">
+        <!-- If there are <label>s in the list, it's a <dl>. -->
+        <dl class="list">
+          <xsl:for-each select="node()">
+            <xsl:choose>
+              <xsl:when test="self::label">
+                <dt class="label"><xsl:apply-templates/></dt>
+              </xsl:when>
+              <xsl:when test="self::item">
+                <dd class="item"><xsl:apply-templates/></dd>
+              </xsl:when>
+              <xsl:when test="self::list">
+                <dd class="list-wrapper"><xsl:apply-templates select="."/></dd>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </dl>
+      </xsl:when>
+
+      <xsl:otherwise>
+        <!-- If there are no <label>s in the list, it's a plain old <ul>. -->
+        <ul class="list">
+          <xsl:for-each select="node()">
+            <xsl:choose>
+              <xsl:when test="self::item">
+                <li class="item"><xsl:apply-templates/></li>
+              </xsl:when>
+              <xsl:when test="self::list">
+                <li class="list-wrapper"><xsl:apply-templates select="."/></li>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </ul>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+
+  <xsl:template match="list" mode="jesus">
+    <xsl:choose>
+      <xsl:when test="label">
+        <!-- If there are <label>s in the list, it's a <dl>. -->
+        <dl class="list">
+          <xsl:for-each select="node()">
+            <xsl:choose>
+              <xsl:when test="self::label">
+                <dt class="label"><xsl:apply-templates mode="jesus"/></dt>
+              </xsl:when>
+              <xsl:when test="self::item">
+                <dd class="item"><xsl:apply-templates mode="jesus"/></dd>
+              </xsl:when>
+              <xsl:when test="self::list">
+                <dd class="list-wrapper"><xsl:apply-templates select="." mode="jesus"/></dd>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates mode="jesus"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </dl>
+      </xsl:when>
+
+      <xsl:otherwise>
+        <!-- If there are no <label>s in the list, it's a plain old <ul>. -->
+        <ul class="list">
+          <xsl:for-each select="node()">
+            <xsl:choose>
+              <xsl:when test="self::item">
+                <li class="item"><xsl:apply-templates mode="jesus"/></li>
+              </xsl:when>
+              <xsl:when test="self::list">
+                <li class="list-wrapper"><xsl:apply-templates select="." mode="jesus"/></li>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:apply-templates mode="jesus"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:for-each>
+        </ul>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="mentioned">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+  <xsl:template match="mentioned" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+  
+  <!-- Milestones represent characteristics of the original manuscript.
+    == that are being preserved. For this reason, most are ignored.
+    ==
+    == The defined types are:
+    == column   Marks the end of a column where there is a multi-column display.
+    == footer   Marks the footer region of a page.
+    == halfLine Used to mark half-line units if not otherwise encoded.
+    == header   Marks the header region of a page.
+    == line     Marks line breaks, particularly important in recording appearance of an original text, such as a manuscript.
+    == pb       Marks a page break in a text.
+    == screen   Marks a preferred place for breaks in an on-screen rendering of the text.
+    == cQuote   Marks the location of a continuation quote mark, with marker containing the publishers mark.
+    -->
+  <!--  This is used by the KJV for paragraph markers. -->
+  <xsl:template match="milestone[@type = 'x-p']"><xsl:text> </xsl:text><xsl:value-of select="@marker"/><xsl:text> </xsl:text></xsl:template>
+  <xsl:template match="milestone[@type = 'x-p']" mode="jesus"><xsl:text> </xsl:text><xsl:value-of select="@marker"/><xsl:text> </xsl:text></xsl:template>
+
+  <xsl:template match="milestone[@type = 'cQuote']">
+    <xsl:value-of select="@marker"/>
+  </xsl:template>
+
+  <xsl:template match="milestone[@type = 'cQuote']" mode="jesus">
+    <xsl:value-of select="@marker"/>
+  </xsl:template>
+
+  <xsl:template match="milestone[@type = 'line']"><br/></xsl:template>
+
+  <xsl:template match="milestone[@type = 'line']" mode="jesus"><br/></xsl:template>
+
+  <!--
+    == Milestone start and end are deprecated.
+    == At this point we expect them to not be in the document.
+    == These have been replace with milestoneable elements.
+    -->
+  <xsl:template match="milestoneStart"/>
+  <xsl:template match="milestoneEnd"/>
+  
+  <xsl:template match="name">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="name" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+
+  <!-- If there is a milestoned q then just output a quotation mark -->
+  <xsl:template match="q[@sID or @eID]">
+    <xsl:choose>
+      <xsl:when test="@marker"><xsl:value-of select="@marker"/></xsl:when>
+      <!-- The chosen mark should be based on the work's author's locale. -->
+      <xsl:otherwise>"</xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template match="q[@sID or @eID]" mode="jesus">
+    <xsl:choose>
+      <xsl:when test="@marker"><xsl:value-of select="@marker"/></xsl:when>
+      <!-- The chosen mark should be based on the work's author's locale. -->
+      <xsl:otherwise>"</xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template match="q[@who = 'Jesus']">
+    <font class="jesus"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></font>
+  </xsl:template>
+
+  <xsl:template match="q[@type = 'blockquote']">
+    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></blockquote>
+  </xsl:template>
+
+  <xsl:template match="q[@type = 'blockquote']" mode="jesus">
+    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></blockquote>
+  </xsl:template>
+
+  <xsl:template match="q[@type = 'citation']">
+    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/></blockquote>
+  </xsl:template>
+
+  <xsl:template match="q[@type = 'citation']" mode="jesus">
+    <blockquote class="q"><xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/></blockquote>
+  </xsl:template>
+
+  <xsl:template match="q[@type = 'embedded']">
+    <xsl:choose>
+      <xsl:when test="@marker">
+        <xsl:value-of select="@marker"/><xsl:apply-templates/><xsl:value-of select="@marker"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <quote class="q"><xsl:apply-templates/></quote>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <xsl:template match="q[@type = 'embedded']" mode="jesus">
+    <xsl:choose>
+      <xsl:when test="@marker">
+      <xsl:value-of select="@marker"/><xsl:apply-templates mode="jesus"/><xsl:value-of select="@marker"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <quote class="q"><xsl:apply-templates/></quote>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  
+  <!-- An alternate reading. -->
+  <xsl:template match="rdg">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+   <xsl:template match="rdg" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+
+  <!--
+      <row> is handled near <table> below and so does not appear here.
+  -->
+  
+  <xsl:template match="salute">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+ <!-- Avoid adding whitespace -->
+  <xsl:template match="salute" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+
+  <xsl:template match="signed">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="signed" mode="jesus">
+    <xsl:apply-templates mode="jesus"/>
+  </xsl:template>
+
+  <xsl:template match="speech">
+    <div class="speech"><xsl:apply-templates/></div>
+  </xsl:template>
+  
+  <xsl:template match="speech" mode="jesus">
+    <div class="speech"><xsl:apply-templates mode="jesus"/></div>
+  </xsl:template>
+
+  <xsl:template match="table">
+    <table class="table">
+      <xsl:copy-of select="@rows|@cols"/>
+      <xsl:if test="head">
+        <thead class="head"><xsl:apply-templates select="head"/></thead>
+      </xsl:if>
+      <tbody><xsl:apply-templates select="row"/></tbody>
+    </table>
+  </xsl:template>
+
+  <xsl:template match="row">
+    <tr class="row"><xsl:apply-templates/></tr>
+  </xsl:template>
+  
+  <xsl:template match="cell">
+    <xsl:variable name="element-name">
+      <xsl:choose>
+        <xsl:when test="@role = 'label'">
+          <xsl:text>th</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:text>td</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="cell-direction">
+      <xsl:if test="@xml:lang">
+        <xsl:call-template name="getDirection">
+         <xsl:with-param name="lang"><xsl:value-of select="@xml:lang"/></xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:variable>
+    <xsl:element name="{$element-name}">
+      <xsl:attribute name="class">cell</xsl:attribute>
+      <xsl:attribute name="valign">top</xsl:attribute>
+      <xsl:if test="@xml:lang">
+        <xsl:attribute name="dir">
+          <xsl:value-of select="$cell-direction"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:if test="$cell-direction = 'rtl'">
+        <xsl:attribute name="align">
+          <xsl:value-of select="'right'"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@rows">
+        <xsl:attribute name="rowspan">
+          <xsl:value-of select="@rows"/>
+        </xsl:attribute>
+      </xsl:if>
+      <xsl:if test="@cols">
+        <xsl:attribute name="colspan">
+          <xsl:value-of select="@cols"/>
+        </xsl:attribute>
+      </xsl:if>
+      <!-- hack alert -->
+      <xsl:choose>
+        <xsl:when test="$cell-direction = 'rtl'">
+          <xsl:text>&#8235;</xsl:text><xsl:apply-templates/><xsl:text>&#8236;</xsl:text>
+        </xsl:when>
+        <xsl:when test="$cell-direction = 'ltr'">
+          <xsl:text>&#8234;</xsl:text><xsl:apply-templates/><xsl:text>&#8236;</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:element>
+  </xsl:template>
+
+  <xsl:template match="transChange">
+    <span><em><xsl:apply-templates/></em></span>
+  </xsl:template>
+  <xsl:template match="transChange" mode="jesus">
+    <span class="w"><em><xsl:apply-templates/></em></span>
+  </xsl:template>
+  
+  <!-- @type is OSIS, @rend is TEI -->
+  <xsl:template match="hi">
+    <xsl:variable name="style">
+      <xsl:choose>
+        <xsl:when test="@type">
+          <xsl:value-of select="@type"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@rend"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$style = 'acrostic'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:when test="$style = 'bold'">
+        <strong><xsl:apply-templates/></strong>
+      </xsl:when>
+      <xsl:when test="$style = 'emphasis'">
+        <em><xsl:apply-templates/></em>
+      </xsl:when>
+      <xsl:when test="$style = 'illuminated'">
+        <strong><em><xsl:apply-templates/></em></strong>
+      </xsl:when>
+      <xsl:when test="$style = 'italic'">
+        <em><xsl:apply-templates/></em>
+      </xsl:when>
+      <xsl:when test="$style = 'line-through'">
+        <font class="strike"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'normal'">
+        <font class="normal"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'small-caps'">
+        <font class="small-caps"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'sub'">
+        <sub><xsl:apply-templates/></sub>
+      </xsl:when>
+      <xsl:when test="$style = 'super'">
+        <sup><xsl:apply-templates/></sup>
+      </xsl:when>
+      <xsl:when test="$style = 'underline'">
+        <u><xsl:apply-templates/></u>
+      </xsl:when>
+      <xsl:when test="$style = 'x-caps'">
+        <font class="caps"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="hi" mode="jesus">
+    <xsl:variable name="style">
+      <xsl:choose>
+        <xsl:when test="@type">
+          <xsl:value-of select="@type"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="@rend"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$style = 'acrostic'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:when test="$style = 'bold'">
+        <strong><xsl:apply-templates/></strong>
+      </xsl:when>
+      <xsl:when test="$style = 'emphasis'">
+        <em><xsl:apply-templates/></em>
+      </xsl:when>
+      <xsl:when test="$style = 'illuminated'">
+        <strong><em><xsl:apply-templates/></em></strong>
+      </xsl:when>
+      <xsl:when test="$style = 'italic'">
+        <em><xsl:apply-templates/></em>
+      </xsl:when>
+      <xsl:when test="$style = 'line-through'">
+        <font class="strike"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'normal'">
+        <font class="normal"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'small-caps'">
+        <font class="small-caps"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:when test="$style = 'sub'">
+        <sub><xsl:apply-templates/></sub>
+      </xsl:when>
+      <xsl:when test="$style = 'super'">
+        <sup><xsl:apply-templates/></sup>
+      </xsl:when>
+      <xsl:when test="$style = 'underline'">
+        <u><xsl:apply-templates/></u>
+      </xsl:when>
+      <xsl:when test="$style = 'x-caps'">
+        <font class="caps"><xsl:apply-templates/></font>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!--
+    The following elements are actually TEI and there is some expectation
+    that these will make it into OSIS.
+  -->
+  <xsl:template match="superentry">
+    <!-- output each preverse element in turn -->
+    <xsl:for-each select="entry|entryFree">
+      <xsl:apply-templates/><br/><br/>
+    </xsl:for-each>
+  </xsl:template>
+
+  <xsl:template match="entry">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="entryFree">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="form">
+    <xsl:apply-templates/><br/>
+  </xsl:template>
+
+  <xsl:template match="orth">
+    <font class="orth"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <xsl:template match="pron">
+    <font class="pron"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <xsl:template match="etym">
+    <font class="etym"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <xsl:template match="def">
+    <font class="def"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <xsl:template match="usg">
+    <font class="usg"><xsl:apply-templates/></font>
+  </xsl:template>
+
+  <xsl:template match="@xml:lang">
+    <xsl:variable name="dir">
+      <xsl:if test="@xml:lang">
+        <xsl:call-template name="getDirection">
+         <xsl:with-param name="lang"><xsl:value-of select="@xml:lang"/></xsl:with-param>
+        </xsl:call-template>
+      </xsl:if>
+    </xsl:variable>
+    <xsl:if test="$dir">
+      <xsl:attribute name="dir">
+        <xsl:value-of select="$dir"/>
+      </xsl:attribute>
+    </xsl:if>
+  </xsl:template>
+
+  <!-- If the parent of the text is a verse then, we need to wrap in span. This applies
+  to any punctuation really, since all other words should be contained in a W  -->
+  <xsl:template match="text()">
+  		<xsl:choose>
+	  		<xsl:when test="name(..) = 'verse' and normalize-space(.) != ''"><span class="w"><span class="text"><xsl:value-of select="."/></span></span></xsl:when>
+	  		<xsl:when test="normalize-space(.) != ''"><xsl:value-of select="."/></xsl:when>
+  		</xsl:choose>
+  </xsl:template>
+
+
+  <xsl:template match="text()" mode="small-caps">
+  <xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
+  </xsl:template>
+
+  <!--
+    The direction is deduced from the xml:lang attribute and is assumed to be meaningful for those elements.
+    Note: there is a bug that prevents dir=rtl from working.
+    see: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4296022 and 4866977
+  -->
+  <xsl:template name="getDirection">
+    <xsl:param name="lang"/>
+    <xsl:choose>
+      <xsl:when test="$lang = 'he' or $lang = 'ar' or $lang = 'fa' or $lang = 'ur' or $lang = 'syr'">
+        <xsl:value-of select="'rtl'"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="'ltr'"/>
+      </xsl:otherwise>
+    </xsl:choose>
+   </xsl:template>
+   
+	<xsl:template name="string-replace-all">
+	<xsl:param name="text" />
+	<xsl:param name="replace" />
+	<xsl:param name="by" />
+	<xsl:choose>
+		<xsl:when test="contains($text, $replace)">
+			<xsl:value-of select="substring-before($text,$replace)" />
+			<xsl:value-of select="$by" />
+			<xsl:call-template name="string-replace-all">
+				<xsl:with-param name="text" select="substring-after($text,$replace)" />
+				<xsl:with-param name="replace" select="$replace" />
+				<xsl:with-param name="by" select="$by" />
+			</xsl:call-template>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:value-of select="$text" />
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>  
+</xsl:stylesheet>

Added: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java	                        (rev 0)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,87 @@
+package com.tyndalehouse.step.core.service;
+
+import static com.tyndalehouse.step.core.models.LookupOption.INTERLINEAR;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+import org.crosswire.jsword.book.Book;
+import org.crosswire.jsword.book.BookData;
+import org.crosswire.jsword.book.Books;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.tyndalehouse.step.core.models.LookupOption;
+import com.tyndalehouse.step.core.service.impl.JSwordServiceImpl;
+
+/**
+ * a service providing a wrapper around JSword
+ * 
+ * @author CJBurrell
+ * 
+ */
+public class JSwordServiceImplTest {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    /**
+     * tests what happens when we select interlinear
+     * 
+     * @throws Exception an uncaught exception
+     */
+    @Test
+    public void testInterlinearTransformation() throws Exception {
+        final Book currentBook = Books.installed().getBook("esv");
+        final BookData bookData = new BookData(currentBook, currentBook.getKey("Romans 1"));
+        final Element osisFragment = bookData.getOsisFragment();
+
+        final XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
+        this.logger.debug(xmlOutputter.outputString(osisFragment));
+
+        // do the test
+        final JSwordServiceImpl jsi = new JSwordServiceImpl();
+        final ArrayList<LookupOption> options = new ArrayList<LookupOption>();
+        options.add(INTERLINEAR);
+
+        final String osisText = jsi.getOsisText("KJV", "Romans 1:4", options, "");
+        final SAXBuilder sb = new SAXBuilder();
+        final Document d = sb.build(new StringReader(osisText));
+
+        this.logger.debug("\n {}", xmlOutputter.outputString(d));
+        Assert.assertTrue(osisText.contains("<span>"));
+
+    }
+
+    /**
+     * tests that the XSLT transformation is handled correctly
+     * 
+     * @throws Exception uncaught exception
+     */
+    @Test
+    public void testXslTransformation() throws Exception {
+        final Book currentBook = Books.installed().getBook("KJV");
+        final BookData bookData = new BookData(currentBook, currentBook.getKey("Romans 1:4"));
+        final Element osisFragment = bookData.getOsisFragment();
+
+        final XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
+        this.logger.debug(xmlOutputter.outputString(osisFragment));
+
+        // do the test
+        final JSwordServiceImpl jsi = new JSwordServiceImpl();
+        final ArrayList<LookupOption> options = new ArrayList<LookupOption>();
+        options.add(LookupOption.STRONG_NUMBERS);
+
+        final String osisText = jsi.getOsisText("KJV", "Romans 1:4", options, "");
+        final SAXBuilder sb = new SAXBuilder();
+        final Document d = sb.build(new StringReader(osisText));
+
+        this.logger.debug("\n {}", xmlOutputter.outputString(d));
+        Assert.assertTrue(osisText.contains("<span>"));
+    }
+}

Added: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImplTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImplTest.java	                        (rev 0)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/xsl/impl/InterlinearProviderImplTest.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,26 @@
+package com.tyndalehouse.step.core.xsl.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class InterlinearProviderImplTest {
+    /**
+     * this checks that when keyed with strong, morph and verse number, we can retrieve the word. We should be able to
+     * retrieve by (strong,morph), regardless of verse number. We should also be able to retrieve by (strong,verse
+     * number)
+     */
+    @Test
+    public void testInterlinearStrongMorphBased() {
+        final InterlinearProviderImpl interlinear = new InterlinearProviderImpl();
+
+        // add a word based on a strong,morph
+        interlinear.addTextualInfo("v1", "strong", "morph", "word");
+
+        assertEquals(interlinear.getWord("v1", "strong", "morph"), "word");
+        assertEquals(interlinear.getWord("x", "strong", "morph"), "word");
+        assertEquals(interlinear.getWord("x", "strong", ""), "word");
+        assertEquals(interlinear.getWord("x", "strong", null), "word");
+        assertEquals(interlinear.getWord("x", "strong"), "word");
+    }
+}

Modified: trunk/step/step-parent/pom.xml
===================================================================
--- trunk/step/step-parent/pom.xml	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-parent/pom.xml	2010-11-15 20:23:39 UTC (rev 186)
@@ -26,6 +26,7 @@
 		<servlet-api.version>2.5</servlet-api.version>
 		<jsp-api.version>2.1</jsp-api.version>
 		<jstl.version>1.2</jstl.version>
+		<pjl-comp-filter.version>1.6.4</pjl-comp-filter.version>
 
 		<!-- Commons -->
 		<commons-beanutils.version>1.8.3</commons-beanutils.version>
@@ -131,6 +132,11 @@
 				<artifactId>jstl</artifactId>
 				<version>${jstl.version}</version>
 			</dependency>
+			<dependency>
+			    <groupId>net.sourceforge.pjl-comp-filter</groupId>
+			    <artifactId>pjl-comp-filter</artifactId>
+			    <version>${pjl-comp-filter.version}</version>
+			</dependency>
 
 			<dependency>
 				<groupId>commons-beanutils</groupId>

Modified: trunk/step/step-web/pom.xml
===================================================================
--- trunk/step/step-web/pom.xml	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/pom.xml	2010-11-15 20:23:39 UTC (rev 186)
@@ -64,6 +64,10 @@
 			<groupId>javax.servlet</groupId>
 			<artifactId>jstl</artifactId>
 		</dependency>
+		<dependency>
+		    <groupId>net.sourceforge.pjl-comp-filter</groupId>
+		    <artifactId>pjl-comp-filter</artifactId>
+		</dependency>
 	
 		<dependency>
 			<groupId>junit</groupId>

Added: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/JsonExceptionResolver.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/JsonExceptionResolver.java	                        (rev 0)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/JsonExceptionResolver.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,24 @@
+package com.tyndalehouse.step.rest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.ModelAndView;
+
+public class JsonExceptionResolver implements HandlerExceptionResolver {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    public ModelAndView resolveException(final HttpServletRequest request, final HttpServletResponse response,
+            final Object handler, final Exception exception) {
+
+        this.logger.error(exception.getMessage(), exception);
+
+        final ModelAndView mav = new ModelAndView();
+        mav.setViewName("MappingJacksonJsonView");
+        mav.addObject("error", exception.getMessage());
+        return mav;
+    }
+}

Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -1,10 +1,13 @@
 package com.tyndalehouse.step.rest.controllers;
 
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -16,12 +19,14 @@
 import com.tyndalehouse.step.core.models.EnrichedLookupOption;
 import com.tyndalehouse.step.core.models.LookupOption;
 import com.tyndalehouse.step.core.service.BibleInformationService;
+import com.tyndalehouse.step.rest.wrappers.HtmlWrapper;
 
 @RequestMapping(value = "/bible", method = RequestMethod.GET)
 @Controller
 public class BibleController {
     @Autowired
     private BibleInformationService bibleInformation;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     /**
      * a REST method that returns version of the Bible that are available
@@ -35,7 +40,7 @@
     }
 
     /**
-     * a REST method that returns
+     * a REST method that returns text from the Bible
      * 
      * @param version the initials identifying the version
      * @param reference the reference to lookup
@@ -43,11 +48,25 @@
      */
     @RequestMapping(value = "/text/{version}/{reference}")
     public @ResponseBody
-    String getBibleText(@PathVariable final String version, @PathVariable final String reference) {
-        return getBibleText(version, reference, null);
+    HtmlWrapper getBibleText(@PathVariable final String version, @PathVariable final String reference) {
+        return getBibleText(version, reference, null, null);
     }
 
     /**
+     * a REST method that returns text from the Bible
+     * 
+     * @param version the initials identifying the version
+     * @param reference the reference to lookup
+     * @return the text to be displayed, formatted as HTML
+     */
+    @RequestMapping(value = "/text/{version}/{reference}/{options}")
+    public @ResponseBody
+    HtmlWrapper getBibleText(@PathVariable final String version, @PathVariable final String reference,
+            @PathVariable final String options) {
+        return getBibleText(version, reference, options, null);
+    }
+
+    /**
      * a REST method that returns
      * 
      * @param version the initials identifying the version
@@ -55,15 +74,15 @@
      * @param options a list of options to be passed in
      * @return the text to be displayed, formatted as HTML
      */
-    @RequestMapping(value = "/text/{version}/{reference}/{options}")
+    @RequestMapping(value = "/text/{version}/{reference}/{options}/{interlinearVersion}")
     public @ResponseBody
-    String getBibleText(@PathVariable final String version, @PathVariable final String reference,
-            @PathVariable final String options) {
+    HtmlWrapper getBibleText(@PathVariable final String version, @PathVariable final String reference,
+            @PathVariable final String options, @PathVariable final String interlinearVersion) {
         Validate.notEmpty(version, "You need to provide a version");
         Validate.notEmpty(reference, "You need to provide a reference");
 
         String[] userOptions = null;
-        if (StringUtils.isNotBlank(options)) {
+        if (isNotBlank(options)) {
             userOptions = options.split(",");
         }
 
@@ -74,7 +93,8 @@
             }
         }
 
-        return this.bibleInformation.getPassageText(version, reference, lookupOptions);
+        return new HtmlWrapper(this.bibleInformation.getPassageText(version, reference, lookupOptions,
+                interlinearVersion));
     }
 
     /**

Added: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/wrappers/HtmlWrapper.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/wrappers/HtmlWrapper.java	                        (rev 0)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/wrappers/HtmlWrapper.java	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,13 @@
+package com.tyndalehouse.step.rest.wrappers;
+
+public class HtmlWrapper {
+    final String value;
+
+    public HtmlWrapper(final String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+}

Modified: trunk/step/step-web/src/main/webapp/WEB-INF/mvc-config.xml
===================================================================
--- trunk/step/step-web/src/main/webapp/WEB-INF/mvc-config.xml	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/WEB-INF/mvc-config.xml	2010-11-15 20:23:39 UTC (rev 186)
@@ -21,10 +21,21 @@
 	<!-- Saves a locale change using a cookie -->
 	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />
 
-	<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
-	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-		<property name="prefix" value="/WEB-INF/views/"/>
-		<property name="suffix" value=".jsp"/>
+	<!-- Handle exceptions neatly -->
+	<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
+	  <property name="mediaTypes">
+	    <map>
+	      <entry key="json" value="application/json" />
+	    </map>
+	  </property>
+	  <property name="defaultContentType" value="application/json" />
+	  <property name="defaultViews">
+	    <list>
+	      <bean name="MappingJacksonJsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
+	    </list>
+	  </property>
 	</bean>
+	   
+	<bean id="exceptionResolver" class="com.tyndalehouse.step.rest.JsonExceptionResolver" />
 
 </beans>

Modified: trunk/step/step-web/src/main/webapp/WEB-INF/web.xml
===================================================================
--- trunk/step/step-web/src/main/webapp/WEB-INF/web.xml	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/WEB-INF/web.xml	2010-11-15 20:23:39 UTC (rev 186)
@@ -5,18 +5,44 @@
 <web-app>
 	<display-name>STEP :: Scripture Tools for Every Pastor</display-name>
 
-    <context-param>
-        <param-name>contextConfigLocation</param-name>
-        <param-value>classpath*:com/tyndalehouse/step/**/config/*-applicationContext.xml</param-value>
-    </context-param>
-    
-    <listener>
-        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-    </listener>
+	<context-param>
+		<param-name>contextConfigLocation</param-name>
+		<param-value>classpath*:com/tyndalehouse/step/**/config/*-applicationContext.xml</param-value>
+	</context-param>
 
+	<filter>
+		<filter-name>requestEncodingFilter</filter-name>
+		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+		<init-param>
+			<param-name>encoding</param-name>
+			<param-value>UTF-8</param-value>
+		</init-param>
+		<init-param>
+			<param-name>forceEncoding</param-name>
+			<param-value>true</param-value>
+		</init-param>
+	</filter>
+	<filter>
+		<filter-name>CompressingFilter</filter-name>
+		<filter-class>com.planetj.servlet.filter.compression.CompressingFilter</filter-class>
+	</filter>
 
+	<!-- don't need to do all the time TODO -->
+	<filter-mapping>
+		<filter-name>requestEncodingFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	<filter-mapping>
+		<filter-name>CompressingFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	<listener>
+		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+	</listener>
+
+
 	<!-- Spring context Configuration Begins -->
- 
+
 	<servlet>
 		<servlet-name>step-rest</servlet-name>
 		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
@@ -24,10 +50,11 @@
 	</servlet>
 
 	<servlet-mapping>
-	   <servlet-name>step-rest</servlet-name>
-	   <url-pattern>/rest/*</url-pattern>
+		<servlet-name>step-rest</servlet-name>
+		<url-pattern>/rest/*</url-pattern>
 	</servlet-mapping>
 
+
 	<welcome-file-list>
 		<welcome-file>index.jsp</welcome-file>
 	</welcome-file-list>

Modified: trunk/step/step-web/src/main/webapp/css/initial-layout.css
===================================================================
--- trunk/step/step-web/src/main/webapp/css/initial-layout.css	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/css/initial-layout.css	2010-11-15 20:23:39 UTC (rev 186)
@@ -35,6 +35,7 @@
 
 .passageContainer {
 	text-align: center;
+	padding-botom: 5px;
 }
 
 .passageVersion {
@@ -59,4 +60,15 @@
 	color: grey;
 }
 
+#loading { 
+	margin-top: 3px;
+	display: none;
+	z-index: 9999; /** always on top */
+}
 
+#error {
+	padding-top: 2px;
+	padding-left: 1px;
+	margin: 0px;
+}
+

Modified: trunk/step/step-web/src/main/webapp/css/passage.css
===================================================================
--- trunk/step/step-web/src/main/webapp/css/passage.css	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/css/passage.css	2010-11-15 20:23:39 UTC (rev 186)
@@ -1,13 +1,14 @@
 .passageText {
+	margin-top: 1px;
 	overflow: auto;
-	height: 100%;
+	height: 96%;
 	position: relative;
 	text-align: left;
 }
 
 /** Some interlinear rules */
 /* Define the nature of a "word" */
-span.verse span { /* Make each word follow the other */
+span.interlinear span { /* Make each word follow the other */
 	float: left;
 	/* Make sure that there is spacing between words */
 	/* And extra spacing between lines */
@@ -16,7 +17,7 @@
 }
 
 /* Make the different views of a word stack. */
-span.verse span span { /* Make this stack on top of each other. */
+span.interlinear span span { /* Make this stack on top of each other. */
 	display: block;
 	/* Turn off floating and padding that was added by the previous rule. */
 	float: none;
@@ -24,16 +25,21 @@
 }
 
 /* Allow for spans to be nested more deeply */
-span.verse span span span {
+span.interlinear span span span {
 	/* Turn off block display that was added by the previous rule. */
 	display: inline;
 }
 
-span.verseNumber {
-	text-align: super;
-	font-size: x-small;	
+.verseNumber {
+	vertical-align: top;
+	font-size: 6pt;
+	padding-right: 2px;
 }
 
+br.paragraph {
+	padding-top: 2px;	
+}
+
 A {
 	text-decoration: none;
 }
@@ -72,36 +78,31 @@
 	color: red;
 }
 
-FONT.jesus {
+span.jesus {
 	color: red;
 }
 
-FONT.speech {
+span.speech {
 	color: blue;
 }
 
-FONT.strike {
+span.strike {
 	text-decoration: line-through;
 }
 
-FONT.small-caps {
+span.small-caps {
 	font-variant: small-caps;
 }
 
-FONT.inscription {
-	font-weight: bold;
-	font-variant: small-caps;
+span.underline {
+	text-decoration: underline;
 }
 
-FONT.divineName {
-	font-variant: small-caps;
-}
-
-FONT.normal {
+span.normal {
 	font-variant: normal;
 }
 
-FONT.caps {
+span.caps {
 	text-transform: uppercase;
 }
 
@@ -163,9 +164,9 @@
 	width: 80%;
 }
 
--->
-<!--
-the following are for dictionary entries -->FONT.orth {
+
+/* the following are for dictionary entries */
+FONT.orth {
 	font-weight: bold;
 }
 
@@ -179,4 +180,4 @@
 
 FONT.usg {
 	font-style: plain;
-}
\ No newline at end of file
+}

Modified: trunk/step/step-web/src/main/webapp/css/ui-layout/layout-default.css
===================================================================
--- trunk/step/step-web/src/main/webapp/css/ui-layout/layout-default.css	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/css/ui-layout/layout-default.css	2010-11-15 20:23:39 UTC (rev 186)
@@ -14,20 +14,6 @@
  * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
  */
 /*
-*	BASIC PAGE COSMETICS
-*/
-/*
-body {
-	font-family:	Lucida Grande, Lucida Sans, Geneva, Arial, Helvetica, sans-serif;
-	font-size:		100%;
-	*font-size:		80%;
-	background-color: #EEE;
-}
-p {
-	margin: 1em 0;
-}
-*/
-/*
 *	PANES
 */
 .ui-layout-pane { /* all 'panes' */
@@ -129,13 +115,8 @@
 	display: none;
 }
 
-/*
-	*	style the text we put INSIDE the east/west togglers
-	*/
 .ui-layout-toggler .content {
-	/* font-size: 12px; */
 	font-weight: bold;
 	color: #666;
 	padding-bottom: 0.35ex;
-	/* to 'vertically center' text inside text-span */
 }

Added: trunk/step/step-web/src/main/webapp/images/wait16.gif
===================================================================
(Binary files differ)


Property changes on: trunk/step/step-web/src/main/webapp/images/wait16.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: trunk/step/step-web/src/main/webapp/index.jsp
===================================================================
--- trunk/step/step-web/src/main/webapp/index.jsp	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/index.jsp	2010-11-15 20:23:39 UTC (rev 186)
@@ -54,6 +54,10 @@
 
     <div class="passageText ui-widget"></div>
 </div>
+
+<div id="loading"><img alt="Loading..." src="images/wait16.gif" />Loading...</div>
+<div id="error" class="ui-state-highlight">A placeholder for error messages</div>
+
 </body>
 
 </HTML>

Added: trunk/step/step-web/src/main/webapp/js/bookmark.js
===================================================================
--- trunk/step/step-web/src/main/webapp/js/bookmark.js	                        (rev 0)
+++ trunk/step/step-web/src/main/webapp/js/bookmark.js	2010-11-15 20:23:39 UTC (rev 186)
@@ -0,0 +1,8 @@
+
+/**
+ * The bookmarks components record events that are happening across the application,
+ * for e.g. passage changes, but will also show related information to the passage.
+ */
+function Bookmark() {
+	//TODO
+}
\ No newline at end of file

Modified: trunk/step/step-web/src/main/webapp/js/init.js
===================================================================
--- trunk/step/step-web/src/main/webapp/js/init.js	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/js/init.js	2010-11-15 20:23:39 UTC (rev 186)
@@ -4,6 +4,7 @@
 function init() {
 	$(document).ready(function() {
 		initLayout();
+		initGlobalHandlers();
 		initDefaultValues();
 		initData();
 	});
@@ -21,11 +22,17 @@
 		autoResize : true, // try to maintain pane-percentages
 		autoReopen : true, // auto-open panes that were previously
 		autoBindCustomButtons : true,
+		north__paneSelector: '#error',
 		west__paneSelector : '.leftPassage',
 		center__paneSelector : '.bookmarks',
 		east__paneSelector : '.rightPassage',
 		west__size : .45, // percentage size expresses as a decimal
 		east__size : .45,
+		north__minSize : 0,
+		north__size: 20,
+		north__spacing_open : 0,
+		north__spacing_closed : 0,
+		north__initClosed: true,
 		minSize : 130,
 		noRoomToOpenAction : "hide"
 	});
@@ -50,20 +57,24 @@
 	});
 }
 
-var nonIDedInputs = 0;
 function initDefaultValues() {
-	$("input.defaultValue").addClass("inactive");
+	addDefaultValue($("input.defaultValue"));
+}
 
+var nonIDedInputs = 0;
+function addDefaultValue(inputSelector) {
 	var default_values = new Array();
-	$("input.defaultValue").focus(function() {
+	inputSelector.each(function(index) {
+		$(this).addClass("inactive");
 		if(this.id == "") {
 			this.id = nonIDedInputs++;
 		}
-		
 		if (!default_values[this.id]) {
 			default_values[this.id] = this.value;
 		}
+	});
 
+	inputSelector.focus(function() {
 		if (this.value == default_values[this.id]) {
 			$(this).removeClass("inactive");
 			this.value = '';
@@ -76,8 +87,10 @@
 			}
 		});
 	});
+	
 }
 
+
 function initData() {
 	//get all supported versions
 	var options;
@@ -89,21 +102,33 @@
 	
 	//get data for passages
 	// make call to server first and once, to cache all passages:
+	var strongedVersions = [];
+	var ii = 0;
+	
 	$.getJSON("rest/bible/versions", function(data) {
 		var parsedResponse = $.map(data, function(item) {
+			var showingText = "[" + item.initials + "] " + item.name;
+			
+			//add to strongs if applicable
+			if(item.hasStrongs) {
+				strongedVersions[ii++] = { label: showingText, value: item.initials };
+			}
+			
+			//return response for dropdowns
 			return {
-				label : "[" + item.initials + "] " + item.name,
+				label : showingText,
 				value : item.initials
 			}
 		});
 		
+		
 		//set up initial passages with reference data:
-		var versions = ["KJV" ];
-		var passages = ["Romans 1:1-10"];
+		var versions = ["ESV" ];
+		var passages = ["Romans 1"];
 		$(".passageContainer").each(
 				function(index) {
 					var passage = new Passage(this, parsedResponse);
-					var toolbar = new Toolbar(passage, options);
+					var toolbar = new Toolbar(passage, options, strongedVersions);
 					passage.setToolbar(toolbar);
 					
 					if(index < versions.length) {
@@ -113,3 +138,36 @@
 		});
 }
 
+function initGlobalHandlers() {
+	$("#loading").ajaxStart(function() {
+		$(this).show();
+	});
+
+	$("#loading").ajaxComplete(function() {
+		$(this).hide();
+	});
+	
+	//set always visible - should probably be its own class
+	$( "#loading" ).position({
+		of: $( "body" ),
+		my: "top",
+		at: "top",
+		collision: "fit"
+	});
+	
+	$("#error").click(function() {
+		$('body').layout().close("north");
+	});
+	
+	$("#error").ajaxComplete(function(ev, req, ajaxOptions) {
+		var currentResponse = $.parseJSON(req.responseText);
+		if(currentResponse.error) {
+			raiseError(currentResponse.error)
+		}
+	});
+}
+
+function raiseError(error) {
+	$("#error").text(error);
+	$('body').layout().open("north");
+}

Modified: trunk/step/step-web/src/main/webapp/js/passage.js
===================================================================
--- trunk/step/step-web/src/main/webapp/js/passage.js	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/js/passage.js	2010-11-15 20:23:39 UTC (rev 186)
@@ -31,15 +31,26 @@
 	this.version.autocomplete({
 		source : versions,
 		minLength : 0,
-		select : function(event, ui) {
+		delay: 0,
+		select: function(event, ui) {
+			//force change?
+			$(this).val(ui.item.value);
+			self.reference.focus();
 			self.toolbar.refreshButtons(ui.item.value);
-			self.changePassage();
-		}
+
+			//we do not change the passage here, as we need to have refreshed the buttons 
+			//so the refresh buttons will fire instead
+			return false;
+		},
+	}).focus(function() {
+		this.select();
 	});
 	
 	//set up blur for textbox
-	this.reference.blur(function(){
+	this.reference.change(function(){
 		self.changePassage();
+	}).focus(function() {
+		this.select();
 	});
 }
 
@@ -48,19 +59,45 @@
  * @param version the version passed in (optional - otherwise takes this.version)
  * @param reference the reference to lookup (optional - otherwise takes this.reference)
  */
-Passage.prototype.changePassage = function(version, reference) {
-	var newVersion = version ? version : (!this.version.hasClass("inactive") ? this.version.val() : null);
-	var newReference = reference ? reference : (!this.reference.hasClass("inactive") ? this.reference.val() : null);
-
+Passage.prototype.changePassage = function(/* optional */ version, /* optional */reference) {
+	//if this was called from somewhere else, rather than as a reaction to an event,
+	//we change the values of the textboxes
+	if(version && this.version.val() != version) {
+		this.version.val(version);
+		this.version.removeClass("inactive");
+		this.toolbar.refreshButtons(version);
+	}
+	
+	if(reference && this.reference.val() != reference) {
+		this.reference.val(reference);
+		this.reference.removeClass("inactive");
+	}
+	
+	if(this.reference.hasClass("inactive") || this.version.hasClass("inactive")) {
+		raiseError("You need to provide both a version and a reference to lookup a passage");
+		return;
+	}
+	
 	//now get the options from toolbar
 	var options = this.toolbar.getSelectedOptions();
+	var interlinearVersion = this.toolbar.getSelectedInterlinearVersion();
 	
 	var self = this;
-	if(newVersion && newReference && newVersion != "" && newReference != "") {
+	if(this.version.val() && this.reference.val() && this.version.val() != "" && this.reference.val() != "") {
+		var url = "rest/bible/text/" + this.version.val() + "/" + this.reference.val();
+		
+		if(options && options.length != 0) {
+			url += "/" + options ;
+
+			if(interlinearVersion && interlinearVersion.length != 0) {
+				url += "/" + interlinearVersion;
+			}
+		}
+		
 		//send to server
-		$.get("rest/bible/text/" + newVersion + "/" + newReference + "/" + options, function (text) {
+		$.get(url, function (text) {
 			//we get html back, so we insert into passage:
-			self.passage.html(text);
+			self.passage.html(text.value);
 		});
 	}
 }

Modified: trunk/step/step-web/src/main/webapp/js/passage_toolbar.js
===================================================================
--- trunk/step/step-web/src/main/webapp/js/passage_toolbar.js	2010-11-07 17:30:58 UTC (rev 185)
+++ trunk/step/step-web/src/main/webapp/js/passage_toolbar.js	2010-11-15 20:23:39 UTC (rev 186)
@@ -5,7 +5,7 @@
  */
 	//static id
 var toolbarId = 0;
-function Toolbar(passage, buttonOptions) {
+function Toolbar(passage, buttonOptions, strongedVersions) {
 
 	var self = this;
 	this.passage = passage;
@@ -15,6 +15,7 @@
 	$(passage.getPassageContainer()).prepend('<div class="toolbarContainer ui-widget-content ui-corner-all" />');
 	this.toolbarContainer = $(".toolbarContainer", passageContainer);
 
+	
 	//create a button for each option
 	$.each(buttonOptions, function(index) {
 		self.toolbarContainer.append(
@@ -22,12 +23,47 @@
 				+ toolbarId + '">' + this.displayName + '</label></input>');
 
 		//find the newly created button
-		$('#sb' + toolbarId, self.toolbarContainer).button().click(function() { self.passage.changePassage(); });
+		var newButton = $('#sb' + toolbarId, self.toolbarContainer);
+		newButton.button().click(function() { self.passage.changePassage(); });
+	
 		
+		//finally, if we're looking at the interlinear, then create a dropdown with potential versions
+		if (this.key == "INTERLINEAR") {
+			self.createInterlinearDropdown(toolbarId, strongedVersions, newButton);
+		}
 		toolbarId++;
 	});
 }
 
+Toolbar.prototype.createInterlinearDropdown = function(toolbarId, strongedVersions, interlinearButton) {
+	this.toolbarContainer.append("<input id='interlinear" + toolbarId
+			+ "' type='text' class='interlinearVersion' value='Interlinear version' disabled='disabled' />");
+
+	var self = this;
+	var interlinearSelector = $('#interlinear' + toolbarId);
+	interlinearSelector.autocomplete({
+		source : strongedVersions,
+		minLength : 0,
+		delay: 0,
+		select : function(event, ui) {
+			$(this).val(ui.item.value);
+			self.passage.changePassage();
+			return false;
+		}
+	});
+	
+	addDefaultValue(interlinearSelector);
+	
+	interlinearButton.click(function() {
+		if($(this).attr('checked')) {
+			interlinearSelector.removeAttr("disabled");
+			interlinearSelector.focus();
+		} else {
+			interlinearSelector.attr("disabled", "disabled");
+		}
+	});
+}
+
 /**
  * resets the buttons
  */
@@ -37,14 +73,25 @@
 	//query the server for features
 	$.getJSON("rest/bible/features/" + version, function (features) {
 		//for each button, if in array, then enable, otherwise disable
-		$("input", self.toolbarContainer).each(function() {
-			$(this).button("disable");
-			for(var i = 0; i < features.length; i++) {
-				if(features[i] == this.value) {
-					$(this).button("enable");
+
+		//TODO: for some reason there are sometime some initialisation issues which throw an exception
+		try {
+			$("input", self.toolbarContainer).each(function() {
+				$(this).button("disable");
+				for(var i = 0; i < features.length; i++) {
+					if(features[i] == this.value) {
+						$(this).button("enable");
+					}
 				}
-			}
-		});
+				
+				if($(this).button( "option", "disabled" )) {
+					$(this).removeAttr("checked");
+				}
+				$(this).button("refresh");
+			});
+		} finally {
+			self.passage.changePassage();
+		}
 	});
 }
 
@@ -58,7 +105,17 @@
 	return options;
 }
 
+Toolbar.prototype.getSelectedInterlinearVersion = function() {
+	var version = $(".interlinearVersion", this.toolbarContainer).val();
+	
+	if(version && !$(".interlinearVersion", this.toolbarContainer).hasClass("inactive")) {
+		return version;
+	}
+	
+	return "";
+}
 
+
 /**
  * Opens the toolbar
  */




More information about the Tynstep-svn mailing list