[sword-svn] r2628 - in trunk: examples/cmdline include src/mgr src/modules/filters tests

scribe at crosswire.org scribe at crosswire.org
Tue Jun 28 13:40:34 MST 2011


Author: scribe
Date: 2011-06-28 13:40:34 -0700 (Tue, 28 Jun 2011)
New Revision: 2628

Added:
   trunk/include/gbfxhtml.h
   trunk/include/osisxhtml.h
   trunk/include/thmlxhtml.h
   trunk/src/modules/filters/gbfxhtml.cpp
   trunk/src/modules/filters/osisxhtml.cpp
   trunk/src/modules/filters/thmlxhtml.cpp
Modified:
   trunk/examples/cmdline/lookup.cpp
   trunk/include/defs.h
   trunk/include/gbfwebif.h
   trunk/include/osiswebif.h
   trunk/include/swfilter.h
   trunk/include/thmlwebif.h
   trunk/src/mgr/markupfiltmgr.cpp
   trunk/src/modules/filters/Makefile.am
   trunk/src/modules/filters/gbfwebif.cpp
   trunk/src/modules/filters/osishtmlhref.cpp
   trunk/src/modules/filters/osiswebif.cpp
   trunk/src/modules/filters/thmlwebif.cpp
   trunk/tests/Makefile.am
Log:
Added new filter set XHTML


Modified: trunk/examples/cmdline/lookup.cpp
===================================================================
--- trunk/examples/cmdline/lookup.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/examples/cmdline/lookup.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <swmgr.h>
 #include <swmodule.h>
+#include <swfilter.h>
 #include <markupfiltmgr.h>
 #include <versekey.h>
 
@@ -38,6 +39,7 @@
 using sword::AttributeList;
 using sword::AttributeValue;
 using sword::VerseKey;
+using sword::FilterList;
 
 int main(int argc, char **argv)
 {
@@ -87,6 +89,11 @@
 	std::cout << target->getRawEntry();
 	std::cout << "\n";
 	std::cout << "==Render=Entry============\n";
+	FilterList::const_iterator first = target->getRenderFilters().begin();
+	if (first != target->getRenderFilters().end()) {
+		std::cout << (*first)->getHeader();
+		std::cout << "\n";
+	}
 	std::cout << target->RenderText();
 	std::cout << "\n";
 	std::cout << "==========================\n";

Modified: trunk/include/defs.h
===================================================================
--- trunk/include/defs.h	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/include/defs.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -109,7 +109,7 @@
 #endif
 
 enum {DIRECTION_LTR = 0, DIRECTION_RTL, DIRECTION_BIDI};
-enum {FMT_UNKNOWN = 0, FMT_PLAIN, FMT_THML, FMT_GBF, FMT_HTML, FMT_HTMLHREF, FMT_RTF, FMT_OSIS, FMT_WEBIF, FMT_TEI};
+enum {FMT_UNKNOWN = 0, FMT_PLAIN, FMT_THML, FMT_GBF, FMT_HTML, FMT_HTMLHREF, FMT_RTF, FMT_OSIS, FMT_WEBIF, FMT_TEI, FMT_XHTML};
 enum {ENC_UNKNOWN = 0, ENC_LATIN1, ENC_UTF8, ENC_UTF16, ENC_RTF, ENC_HTML};
 
 SWORD_NAMESPACE_END

Modified: trunk/include/gbfwebif.h
===================================================================
--- trunk/include/gbfwebif.h	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/include/gbfwebif.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -21,13 +21,13 @@
 #ifndef GBFWEBIF_H
 #define GBFWEBIF_H
 
-#include <gbfhtmlhref.h>
+#include <gbfxhtml.h>
 
 SWORD_NAMESPACE_START
 
-/** this filter converts GBF  text to HTML text with hrefs
+/** this filter converts GBF text to classed XHTML for web interfaces
  */
-class SWDLLEXPORT GBFWEBIF : public GBFHTMLHREF {
+class SWDLLEXPORT GBFWEBIF : public GBFXHTML {
 	const SWBuf baseURL;
 	const SWBuf passageStudyURL;
 

Added: trunk/include/gbfxhtml.h
===================================================================
--- trunk/include/gbfxhtml.h	                        (rev 0)
+++ trunk/include/gbfxhtml.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ * gbfxhtmlhref.h
+ *
+ * Copyright 2011 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+#ifndef GBFXHTML_H
+#define GBFXHTML_H
+
+#include <swbasicfilter.h>
+
+SWORD_NAMESPACE_START
+
+/** this filter converts GBF text to classed XHTML text
+ */
+class SWDLLEXPORT GBFXHTML : public SWBasicFilter {
+protected:
+	class MyUserData : public BasicFilterUserData {
+	public:
+		MyUserData(const SWModule *module, const SWKey *key);
+		bool hasFootnotePreTag;
+		SWBuf version;
+	};
+	virtual BasicFilterUserData *createUserData(const SWModule *module, const SWKey *key) {
+		return new MyUserData(module, key);
+	}
+	virtual bool handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData);
+public:
+	GBFXHTML();
+	virtual const char *getHeader() const;
+};
+
+SWORD_NAMESPACE_END
+#endif

Modified: trunk/include/osiswebif.h
===================================================================
--- trunk/include/osiswebif.h	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/include/osiswebif.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -21,13 +21,13 @@
 #ifndef OSISWEBIF_H
 #define OSISWEBIF_H
 
-#include <osishtmlhref.h>
+#include <osisxhtml.h>
 
 SWORD_NAMESPACE_START
 
 /** this filter converts OSIS  text to HTML text with hrefs
  */
-class SWDLLEXPORT OSISWEBIF : public OSISHTMLHREF {
+class SWDLLEXPORT OSISWEBIF : public OSISXHTML {
 	const SWBuf baseURL;
 	const SWBuf passageStudyURL;
 	bool javascript;

Added: trunk/include/osisxhtml.h
===================================================================
--- trunk/include/osisxhtml.h	                        (rev 0)
+++ trunk/include/osisxhtml.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * osisxhtml.h
+ *
+ * Copyright 2011 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+#ifndef OSISXHTML_H
+#define OSISXHTML_H
+
+#include <swbasicfilter.h>
+
+SWORD_NAMESPACE_START
+
+/** this filter converts OSIS text to classed XHTML
+ */
+class SWDLLEXPORT OSISXHTML : public SWBasicFilter {
+private:
+	bool morphFirst;
+protected:
+	// used by derived classes so we have it in the header
+	class TagStacks;
+	class SWDLLEXPORT MyUserData : public BasicFilterUserData {
+	public:
+		bool osisQToTick;
+		bool inBold;	// TODO: obsolete. left for binary compat for 1.6.x
+		bool inXRefNote;
+		bool BiblicalText;
+		int suspendLevel;
+		SWBuf wordsOfChristStart;
+		SWBuf wordsOfChristEnd;
+                TagStacks *tagStacks;	// TODO: modified to wrap all TagStacks necessary for this filter until 1.7.x
+//                TagStack *hiStack;	// TODO: commented out for binary compat for 1.6.x	 wrapped in tagStacks until 1.7.x
+		SWBuf lastTransChange;
+		SWBuf w;
+		SWBuf fn;
+		SWBuf version;
+		MyUserData(const SWModule *module, const SWKey *key);
+		~MyUserData();
+	};
+	virtual BasicFilterUserData *createUserData(const SWModule *module, const SWKey *key) {
+		return new MyUserData(module, key);
+	}
+	virtual bool handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData);
+public:
+	OSISXHTML();
+	void setMorphFirst(bool val = true) { morphFirst = val; }
+	virtual const char *getHeader() const;
+};
+
+SWORD_NAMESPACE_END
+#endif

Modified: trunk/include/swfilter.h
===================================================================
--- trunk/include/swfilter.h	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/include/swfilter.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -42,13 +42,21 @@
 public:
 	virtual ~SWFilter() {}
 
-	/** This is the main filter function
+	/** This method processes and appropriately modifies the text given it
+	 *	for a particular filter task
+	 *
 	 * @param text The text to be filtered/converted
 	 * @param key Current key That was used.
 	 * @param module Current module.
 	 * @return 0
 	 */
 	virtual char processText(SWBuf &text, const SWKey *key = 0, const SWModule *module = 0) = 0;
+
+	/** This method can supply a header associated with the processing done with this filter.
+	 *	A typical example is a suggested CSS style block for classed containers.
+	 * @param text The text to be filtered/converted
+	 */
+	virtual const char *getHeader() const { return ""; }
 };
 
 	SWORD_NAMESPACE_END

Modified: trunk/include/thmlwebif.h
===================================================================
--- trunk/include/thmlwebif.h	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/include/thmlwebif.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -21,13 +21,13 @@
 #ifndef _ThMLWEBIF_H
 #define _ThMLWEBIF_H
 
-#include <thmlhtmlhref.h>
+#include <thmlxhtml.h>
 
 SWORD_NAMESPACE_START
 
 /** this filter converts ThML text to HTML text with hrefs
  */
-class SWDLLEXPORT ThMLWEBIF : public ThMLHTMLHREF {
+class SWDLLEXPORT ThMLWEBIF : public ThMLXHTML {
 	const SWBuf baseURL;
 	const SWBuf passageStudyURL;
 
@@ -36,5 +36,7 @@
 public:
 	ThMLWEBIF();
 };
+
 SWORD_NAMESPACE_END
-#endif /* _ThMLWEBIF_H */
+
+#endif

Added: trunk/include/thmlxhtml.h
===================================================================
--- trunk/include/thmlxhtml.h	                        (rev 0)
+++ trunk/include/thmlxhtml.h	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * $Id: thmlhtmlhref.h 2157 2008-05-13 23:37:56Z scribe $
+ *
+ * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _THMLXHTML_H
+#define _THMLXHTML_H
+
+#include <swbasicfilter.h>
+#include <utilxml.h>
+
+SWORD_NAMESPACE_START
+
+/** this filter converts ThML text to classed XHTML
+ */
+class SWDLLEXPORT ThMLXHTML : public SWBasicFilter {
+	SWBuf imgPrefix;
+protected:
+	class MyUserData : public BasicFilterUserData {
+	public:
+		MyUserData(const SWModule *module, const SWKey *key);//: BasicFilterUserData(module, key) {}
+		bool inscriptRef;
+		bool SecHead;
+		bool BiblicalText;
+		SWBuf version;
+		XMLTag startTag;
+	};
+	virtual BasicFilterUserData *createUserData(const SWModule *module, const SWKey *key) {
+		return new MyUserData(module, key);
+	}
+	virtual bool handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData);
+public:
+	ThMLXHTML();
+	virtual const char *getImagePrefix() { return imgPrefix.c_str(); }
+	virtual void setImagePrefix(const char *newImgPrefix) { imgPrefix = newImgPrefix; }
+	virtual const char *getHeader() const;
+};
+
+SWORD_NAMESPACE_END
+
+#endif

Modified: trunk/src/mgr/markupfiltmgr.cpp
===================================================================
--- trunk/src/mgr/markupfiltmgr.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/mgr/markupfiltmgr.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -43,6 +43,9 @@
 #include <thmlwebif.h>
 #include <osiswebif.h>
 #include <swmodule.h>
+#include <thmlxhtml.h>
+#include <gbfxhtml.h>
+#include <osisxhtml.h>
 
 #include <markupfiltmgr.h>
 
@@ -288,6 +291,13 @@
                         fromosis = NULL;
                         fromtei = NULL;
                         break;
+                case FMT_XHTML:
+                        fromplain = NULL;
+                        fromthml = new ThMLXHTML();
+                        fromgbf = new GBFXHTML();
+                        fromosis = new OSISXHTML();
+                        fromtei = NULL;
+                        break;
                 }
 
 }

Modified: trunk/src/modules/filters/Makefile.am
===================================================================
--- trunk/src/modules/filters/Makefile.am	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/modules/filters/Makefile.am	2011-06-28 20:40:34 UTC (rev 2628)
@@ -5,6 +5,7 @@
 
 GBFFIL = $(filtersdir)/gbfhtml.cpp
 GBFFIL += $(filtersdir)/gbfhtmlhref.cpp
+GBFFIL += $(filtersdir)/gbfxhtml.cpp
 GBFFIL += $(filtersdir)/gbfwebif.cpp
 GBFFIL += $(filtersdir)/gbfplain.cpp
 GBFFIL += $(filtersdir)/gbfrtf.cpp
@@ -26,6 +27,7 @@
 THMLFIL += $(filtersdir)/thmlrtf.cpp
 THMLFIL += $(filtersdir)/thmlhtml.cpp
 THMLFIL += $(filtersdir)/thmlhtmlhref.cpp
+THMLFIL += $(filtersdir)/thmlxhtml.cpp
 THMLFIL += $(filtersdir)/thmlwebif.cpp
 THMLFIL += $(filtersdir)/thmlwordjs.cpp
 
@@ -42,6 +44,7 @@
 OSISFIL = $(filtersdir)/osisheadings.cpp
 OSISFIL += $(filtersdir)/osisfootnotes.cpp 
 OSISFIL += $(filtersdir)/osishtmlhref.cpp
+OSISFIL += $(filtersdir)/osisxhtml.cpp
 OSISFIL += $(filtersdir)/osiswebif.cpp
 OSISFIL += $(filtersdir)/osismorph.cpp
 OSISFIL += $(filtersdir)/osisstrongs.cpp

Modified: trunk/src/modules/filters/gbfwebif.cpp
===================================================================
--- trunk/src/modules/filters/gbfwebif.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/modules/filters/gbfwebif.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -29,9 +29,6 @@
 SWORD_NAMESPACE_START
 
 GBFWEBIF::GBFWEBIF() : baseURL(""), passageStudyURL(baseURL + "passagestudy.jsp") {
-//all is done in GBFHTMLHREF since it inherits form this class
-	addTokenSubstitute("FR", "<span class=\"wordsOfJesus\">"); // words of Jesus begin
-	addTokenSubstitute("Fr", "</span>");
 }
 
 bool GBFWEBIF::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
@@ -189,7 +186,7 @@
 		}
 
 		else {
-			return GBFHTMLHREF::handleToken(buf, token, userData);
+			return GBFXHTML::handleToken(buf, token, userData);
 		}
 	}
 	return true;

Added: trunk/src/modules/filters/gbfxhtml.cpp
===================================================================
--- trunk/src/modules/filters/gbfxhtml.cpp	                        (rev 0)
+++ trunk/src/modules/filters/gbfxhtml.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,232 @@
+/******************************************************************************
+ *
+ * gbfxhtml.cpp -	GBF to classed XHTML
+ *
+ *
+ * Copyright 2011 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+ 
+#include <stdlib.h>
+#include <gbfxhtml.h>
+#include <swmodule.h>
+#include <utilxml.h>
+#include <versekey.h>
+#include <ctype.h>
+#include <url.h>
+
+SWORD_NAMESPACE_START
+
+const char *GBFXHTML::getHeader() const {
+	return "\
+		.wordsOfJesus {\
+			color: red;\
+		}\
+	";
+}
+
+GBFXHTML::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
+	if (module) {
+		version = module->Name(); 
+	}	
+}
+
+GBFXHTML::GBFXHTML() {
+	setTokenStart("<");
+	setTokenEnd(">");
+	
+	setTokenCaseSensitive(true);
+
+	//addTokenSubstitute("Rf", ")</small></font>");
+	addTokenSubstitute("FA", "<font color=\"#800000\">"); // for ASV footnotes to mark text
+	addTokenSubstitute("Rx", "</a>");
+	addTokenSubstitute("FI", "<i>"); // italics begin
+	addTokenSubstitute("Fi", "</i>");
+	addTokenSubstitute("FB", "<b>"); // bold begin
+	addTokenSubstitute("Fb", "</b>");
+	addTokenSubstitute("FR", "<span class=\"wordsOfJesus\">"); // words of Jesus begin
+	addTokenSubstitute("Fr", "</span>");
+	addTokenSubstitute("FU", "<u>"); // underline begin
+	addTokenSubstitute("Fu", "</u>");
+	addTokenSubstitute("FO", "<cite>"); //  Old Testament quote begin
+	addTokenSubstitute("Fo", "</cite>");
+	addTokenSubstitute("FS", "<sup>"); // Superscript begin// Subscript begin
+	addTokenSubstitute("Fs", "</sup>");
+	addTokenSubstitute("FV", "<sub>"); // Subscript begin
+	addTokenSubstitute("Fv", "</sub>");
+	addTokenSubstitute("TT", "<big>"); // Book title begin
+	addTokenSubstitute("Tt", "</big>");
+	addTokenSubstitute("PP", "<cite>"); //  poetry  begin
+	addTokenSubstitute("Pp", "</cite>");
+	addTokenSubstitute("Fn", "</font>"); //  font  end
+	addTokenSubstitute("CL", "<br />"); //  new line
+	addTokenSubstitute("CM", "<!P><br />"); //  paragraph <!P> is a non showing comment that can be changed in the front end to <P> if desired
+	addTokenSubstitute("CG", ""); //  ???
+	addTokenSubstitute("CT", ""); // ???
+	addTokenSubstitute("JR", "<div align=\"right\">"); // right align begin
+	addTokenSubstitute("JC", "<div align=\"center\">"); // center align begin
+	addTokenSubstitute("JL", "</div>"); // align end
+	
+}
+
+
+bool GBFXHTML::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+	const char *tok;
+	MyUserData *u = (MyUserData *)userData;
+
+	if (!substituteToken(buf, token)) {
+		XMLTag tag(token);
+		
+		if (!strncmp(token, "WG", 2)) { // strong's numbers
+			//buf += " <small><em><<a href=\"type=Strongs value=";
+			buf += " <small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=Greek&value=";
+			for (tok = token+2; *tok; tok++)
+				//if(token[i] != '\"')
+					buf += *tok;
+			buf += "\" class=\"strongs\">";
+			for (tok = token + 2; *tok; tok++)
+				//if(token[i] != '\"')
+					buf += *tok;
+			buf += "</a>></em></small>";
+		}
+		else if (!strncmp(token, "WH", 2)) { // strong's numbers
+			//buf += " <small><em><<a href=\"type=Strongs value=";
+			buf += " <small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=Hebrew&value=";
+			for (tok = token+2; *tok; tok++)
+				//if(token[i] != '\"')
+					buf += *tok;
+			buf += "\" class=\"strongs\">";
+			for (tok = token + 2; *tok; tok++)
+				//if(token[i] != '\"')
+					buf += *tok;
+			buf += "</a>></em></small>";
+		}
+		else if (!strncmp(token, "WTG", 3)) { // strong's numbers tense
+			//buf += " <small><em>(<a href=\"type=Strongs value=";
+			buf += " <small><em class=\"strongs\">(<a href=\"passagestudy.jsp?action=showStrongs&type=Greek&value=";
+			for (tok = token + 3; *tok; tok++)
+				if(*tok != '\"')
+					buf += *tok;
+			buf += "\" class=\"strongs\">";
+			for (tok = token + 3; *tok; tok++)
+				if(*tok != '\"')
+					buf += *tok;
+			buf += "</a>)</em></small>";
+		}
+		else if (!strncmp(token, "WTH", 3)) { // strong's numbers tense
+			//buf += " <small><em>(<a href=\"type=Strongs value=";
+			buf += " <small><em class=\"strongs\">(<a href=\"passagestudy.jsp?action=showStrongs&type=Hebrew&value=";
+			for (tok = token + 3; *tok; tok++)
+				if(*tok != '\"')
+					buf += *tok;
+			buf += "\" class=\"strongs\">";
+			for (tok = token + 3; *tok; tok++)
+				if(*tok != '\"')
+					buf += *tok;
+			buf += "</a>)</em></small>";
+		}
+
+		else if (!strncmp(token, "WT", 2) && strncmp(token, "WTH", 3) && strncmp(token, "WTG", 3)) { // morph tags
+			//buf += " <small><em>(<a href=\"type=morph class=none value=";
+			buf += " <small><em class=\"morph\">(<a href=\"passagestudy.jsp?action=showMorph&type=Greek&value=";
+			
+			for (tok = token + 2; *tok; tok++)
+				if(*tok != '\"')
+					buf += *tok;
+			buf += "\" class=\"morph\">";
+			for (tok = token + 2; *tok; tok++)				
+				if(*tok != '\"') 			
+					buf += *tok;		
+			buf += "</a>)</em></small>";
+		}
+
+		else if (!strcmp(tag.getName(), "RX")) {
+			buf += "<a href=\"";
+			for (tok = token + 3; *tok; tok++) {
+			  if(*tok != '<' && *tok+1 != 'R' && *tok+2 != 'x') {
+			    buf += *tok;
+			  }
+			  else {
+			    break;
+			  }
+			}
+			buf += "\">";
+		}
+		else if (!strcmp(tag.getName(), "RF")) {
+			SWBuf type = tag.getAttribute("type");
+			SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
+			VerseKey *vkey = NULL;
+			// see if we have a VerseKey * or descendant
+			SWTRY {
+				vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+			}
+			SWCATCH ( ... ) {	}
+			if (vkey) {
+				// leave this special osis type in for crossReference notes types?  Might thml use this some day? Doesn't hurt.
+				//char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+				buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=n&value=%s&module=%s&passage=%s\"><small><sup class=\"n\">*n</sup></small></a> ", 
+					URL::encode(footnoteNumber.c_str()).c_str(),
+					URL::encode(u->version.c_str()).c_str(), 
+					URL::encode(vkey->getText()).c_str());
+			}
+			u->suspendTextPassThru = true;
+		}
+		else if (!strcmp(tag.getName(), "Rf")) {
+			u->suspendTextPassThru = false;
+		}
+/*
+		else if (!strncmp(token, "RB", 2)) {
+			buf += "<i> ";
+			u->hasFootnotePreTag = true;
+		}
+
+		else if (!strncmp(token, "Rf", 2)) {
+			buf += "&nbsp<a href=\"note=";
+			buf += u->lastTextNode.c_str();
+			buf += "\">";
+			buf += "<small><sup>*n</sup></small></a>&nbsp";
+			// let's let text resume to output again
+			u->suspendTextPassThru = false;
+		}
+		
+		else if (!strncmp(token, "RF", 2)) {
+			if (u->hasFootnotePreTag) {
+				u->hasFootnotePreTag = false;
+				buf += "</i> ";
+			}
+			u->suspendTextPassThru = true;
+		}
+*/
+		else if (!strncmp(token, "FN", 2)) {
+			buf += "<font face=\"";
+			for (tok = token + 2; *tok; tok++)				
+				if(*tok != '\"') 			
+					buf += *tok;
+			buf += "\">";
+		}
+
+		else if (!strncmp(token, "CA", 2)) {	// ASCII value
+			buf += (char)atoi(&token[2]);
+		}
+		
+		else {
+			return false;
+		}
+	}
+	return true;
+}
+
+SWORD_NAMESPACE_END

Modified: trunk/src/modules/filters/osishtmlhref.cpp
===================================================================
--- trunk/src/modules/filters/osishtmlhref.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/modules/filters/osishtmlhref.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -36,60 +36,6 @@
 
 namespace {
 	typedef std::stack<SWBuf> TagStack;
-}
-
-// TODO: this bridge pattern is to preserve binary compat on 1.6.x
-class OSISHTMLHREF::TagStacks {
-public:
-	TagStack quoteStack;
-	TagStack hiStack;
-};
-
-OSISHTMLHREF::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
-	inXRefNote    = false;
-	suspendLevel = 0;
-	tagStacks = new TagStacks();
-	wordsOfChristStart = "<font color=\"red\"> ";
-	wordsOfChristEnd   = "</font> ";
-	if (module) {
-		osisQToTick = ((!module->getConfigEntry("OSISqToTick")) || (strcmp(module->getConfigEntry("OSISqToTick"), "false")));
-		version = module->Name();
-		BiblicalText = (!strcmp(module->Type(), "Biblical Texts"));
-	}
-	else {
-		osisQToTick = true;	// default
-		version = "";
-	}
-}
-
-OSISHTMLHREF::MyUserData::~MyUserData() {
-	delete tagStacks;
-}
-
-OSISHTMLHREF::OSISHTMLHREF() {
-	setTokenStart("<");
-	setTokenEnd(">");
-
-	setEscapeStart("&");
-	setEscapeEnd(";");
-
-	setEscapeStringCaseSensitive(true);
-	setPassThruNumericEscapeString(true);
-
-	addAllowedEscapeString("quot");
-	addAllowedEscapeString("apos");
-	addAllowedEscapeString("amp");
-	addAllowedEscapeString("lt");
-	addAllowedEscapeString("gt");
-
-	setTokenCaseSensitive(true);
-	
-	//	addTokenSubstitute("lg",  "<br />");
-	//	addTokenSubstitute("/lg", "<br />");
-
-	morphFirst = false;
-}
-
 // though this might be slightly slower, possibly causing an extra bool check, this is a renderFilter
 // so speed isn't the absolute highest priority, and this is a very minor possible hit
 static inline void outText(const char *t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
@@ -157,7 +103,61 @@
 		//}
 	}
 }
+}	// end anonymous namespace
 
+// TODO: this bridge pattern is to preserve binary compat on 1.6.x
+class OSISHTMLHREF::TagStacks {
+public:
+	TagStack quoteStack;
+	TagStack hiStack;
+};
+
+OSISHTMLHREF::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
+	inXRefNote    = false;
+	suspendLevel = 0;
+	tagStacks = new TagStacks();
+	wordsOfChristStart = "<font color=\"red\"> ";
+	wordsOfChristEnd   = "</font> ";
+	if (module) {
+		osisQToTick = ((!module->getConfigEntry("OSISqToTick")) || (strcmp(module->getConfigEntry("OSISqToTick"), "false")));
+		version = module->Name();
+		BiblicalText = (!strcmp(module->Type(), "Biblical Texts"));
+	}
+	else {
+		osisQToTick = true;	// default
+		version = "";
+	}
+}
+
+OSISHTMLHREF::MyUserData::~MyUserData() {
+	delete tagStacks;
+}
+
+OSISHTMLHREF::OSISHTMLHREF() {
+	setTokenStart("<");
+	setTokenEnd(">");
+
+	setEscapeStart("&");
+	setEscapeEnd(";");
+
+	setEscapeStringCaseSensitive(true);
+	setPassThruNumericEscapeString(true);
+
+	addAllowedEscapeString("quot");
+	addAllowedEscapeString("apos");
+	addAllowedEscapeString("amp");
+	addAllowedEscapeString("lt");
+	addAllowedEscapeString("gt");
+
+	setTokenCaseSensitive(true);
+	
+	//	addTokenSubstitute("lg",  "<br />");
+	//	addTokenSubstitute("/lg", "<br />");
+
+	morphFirst = false;
+}
+
+
 bool OSISHTMLHREF::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
 	MyUserData *u = (MyUserData *)userData;
 	SWBuf scratch;

Modified: trunk/src/modules/filters/osiswebif.cpp
===================================================================
--- trunk/src/modules/filters/osiswebif.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/modules/filters/osiswebif.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -39,8 +39,6 @@
 
 BasicFilterUserData *OSISWEBIF::createUserData(const SWModule *module, const SWKey *key) {
 	MyUserData *u = new MyUserData(module, key);
-	u->wordsOfChristStart = "<span class=\"wordsOfJesus\"> ";
-	u->wordsOfChristEnd   = "</span> ";
 	return u;
 }
 
@@ -50,7 +48,8 @@
 	SWBuf scratch;
 	bool sub = (u->suspendTextPassThru) ? substituteToken(scratch, token) : substituteToken(buf, token);
 	if (!sub) {
-  // manually process if it wasn't a simple substitution
+
+		// manually process if it wasn't a simple substitution
 		XMLTag tag(token);
 
 		// <w> tag
@@ -165,43 +164,10 @@
 			}
 		}
 
-		// <title>
-		else if (!strcmp(tag.getName(), "title")) {
-			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
-				buf += "<h3>";
-			}
-			else if (tag.isEndTag()) {
-				buf += "</h3>";
-			}
-		}
 
-                // Milestoned paragraphs, created by osis2mod
-                // <div type="paragraph" sID.../>
-                // <div type="paragraph" eID.../>
-                else if (tag.isEmpty() && !strcmp(tag.getName(), "div") && tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "paragraph")) {
-			// This is properly handled by base class.
-			return OSISHTMLHREF::handleToken(buf, token, userData);
-                }
-
-		// ok to leave these in
-		else if (!strcmp(tag.getName(), "div")) {
-			buf += tag;
-		}
-		else if (!strcmp(tag.getName(), "span")) {
-			buf += tag;
-		}
-		else if (!strcmp(tag.getName(), "br")) {
-			buf += tag;
-		}
-
 		// handled appropriately in base class
-		// <catchWord> & <rdg> tags (italicize)
-		// <hi> text highlighting
-		// <q> quote
-		// <milestone type="cQuote" marker="x"/>
-		// <transChange>
 		else {
-			return OSISHTMLHREF::handleToken(buf, token, userData);
+			return OSISXHTML::handleToken(buf, token, userData);
 		}
 	}
 	return true;

Added: trunk/src/modules/filters/osisxhtml.cpp
===================================================================
--- trunk/src/modules/filters/osisxhtml.cpp	                        (rev 0)
+++ trunk/src/modules/filters/osisxhtml.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,644 @@
+/******************************************************************************
+ *
+ * osisxhtml -	Render filter for classed XHTML
+ *			of an OSIS module.
+ *
+ *
+ * Copyright 2011 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <osisxhtml.h>
+#include <utilxml.h>
+#include <utilstr.h>
+#include <versekey.h>
+#include <swmodule.h>
+#include <url.h>
+#include <stringmgr.h>
+#include <stack>
+
+SWORD_NAMESPACE_START
+
+const char *OSISXHTML::getHeader() const {
+	return "\
+		.divineName {\
+			font-variant: small-caps;\
+		}\
+		.wordsOfJesus {\
+			color: red;\
+		}\
+	";
+}
+
+
+namespace {
+	typedef std::stack<SWBuf> TagStack;
+
+// though this might be slightly slower, possibly causing an extra bool check, this is a renderFilter
+// so speed isn't the absolute highest priority, and this is a very minor possible hit
+static inline void outText(const char *t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
+static inline void outText(char t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
+
+void processLemma(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
+	const char *attrib;
+	const char *val;
+	if ((attrib = tag.getAttribute("lemma"))) {
+		int count = tag.getAttributePartCount("lemma", ' ');
+		int i = (count > 1) ? 0 : -1;		// -1 for whole value cuz it's faster, but does the same thing as 0
+		do {
+			attrib = tag.getAttribute("lemma", i, ' ');
+			if (i < 0) i = 0;	// to handle our -1 condition
+			val = strchr(attrib, ':');
+			val = (val) ? (val + 1) : attrib;
+			SWBuf gh;
+			if(*val == 'G')
+				gh = "Greek";
+			if(*val == 'H')
+				gh = "Hebrew";
+			const char *val2 = val;
+			if ((strchr("GH", *val)) && (isdigit(val[1])))
+				val2++;
+			//if ((!strcmp(val2, "3588")) && (lastText.length() < 1))
+			//	show = false;
+			//else {
+				if (!suspendTextPassThru) {
+					buf.appendFormatted("<small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=%s&value=%s\" class=\"strongs\">%s</a>></em></small>",
+							(gh.length()) ? gh.c_str() : "", 
+							URL::encode(val2).c_str(),
+							val2);
+				}
+			//}
+			
+		} while (++i < count);
+	}
+}
+
+void processMorph(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
+	const char * attrib;
+	const char *val;
+	if ((attrib = tag.getAttribute("morph"))) { // && (show)) {
+		SWBuf savelemma = tag.getAttribute("savlm");
+		//if ((strstr(savelemma.c_str(), "3588")) && (lastText.length() < 1))
+		//	show = false;
+		//if (show) {
+			int count = tag.getAttributePartCount("morph", ' ');
+			int i = (count > 1) ? 0 : -1;		// -1 for whole value cuz it's faster, but does the same thing as 0
+			do {
+				attrib = tag.getAttribute("morph", i, ' ');
+				if (i < 0) i = 0;	// to handle our -1 condition
+				val = strchr(attrib, ':');
+				val = (val) ? (val + 1) : attrib;
+				const char *val2 = val;
+				if ((*val == 'T') && (strchr("GH", val[1])) && (isdigit(val[2])))
+					val2+=2;
+				if (!suspendTextPassThru) {
+					buf.appendFormatted("<small><em class=\"morph\">(<a href=\"passagestudy.jsp?action=showMorph&type=%s&value=%s\" class=\"morph\">%s</a>)</em></small>",
+							URL::encode(tag.getAttribute("morph")).c_str(),
+							URL::encode(val).c_str(), 
+							val2);
+				}
+			} while (++i < count);
+		//}
+	}
+}
+}	// end anonymous namespace
+
+
+// TODO: this bridge pattern is to preserve binary compat on 1.6.x
+class OSISXHTML::TagStacks {
+public:
+	TagStack quoteStack;
+	TagStack hiStack;
+};
+
+OSISXHTML::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
+	inXRefNote    = false;
+	suspendLevel = 0;
+	tagStacks = new TagStacks();
+	wordsOfChristStart = "<span class=\"wordsOfJesus\"> ";
+	wordsOfChristEnd   = "</span> ";
+	if (module) {
+		osisQToTick = ((!module->getConfigEntry("OSISqToTick")) || (strcmp(module->getConfigEntry("OSISqToTick"), "false")));
+		version = module->Name();
+		BiblicalText = (!strcmp(module->Type(), "Biblical Texts"));
+	}
+	else {
+		osisQToTick = true;	// default
+		version = "";
+	}
+}
+
+OSISXHTML::MyUserData::~MyUserData() {
+	delete tagStacks;
+}
+
+OSISXHTML::OSISXHTML() {
+	setTokenStart("<");
+	setTokenEnd(">");
+
+	setEscapeStart("&");
+	setEscapeEnd(";");
+
+	setEscapeStringCaseSensitive(true);
+	setPassThruNumericEscapeString(true);
+
+	addAllowedEscapeString("quot");
+	addAllowedEscapeString("apos");
+	addAllowedEscapeString("amp");
+	addAllowedEscapeString("lt");
+	addAllowedEscapeString("gt");
+
+	setTokenCaseSensitive(true);
+	
+	//	addTokenSubstitute("lg",  "<br />");
+	//	addTokenSubstitute("/lg", "<br />");
+
+	morphFirst = false;
+}
+
+bool OSISXHTML::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+	MyUserData *u = (MyUserData *)userData;
+	SWBuf scratch;
+	bool sub = (u->suspendTextPassThru) ? substituteToken(scratch, token) : substituteToken(buf, token);
+	if (!sub) {
+  // manually process if it wasn't a simple substitution
+		XMLTag tag(token);
+		
+		// <w> tag
+		if (!strcmp(tag.getName(), "w")) {
+ 
+			// start <w> tag
+			if ((!tag.isEmpty()) && (!tag.isEndTag())) {
+				u->w = token;
+			}
+
+			// end or empty <w> tag
+			else {
+				bool endTag = tag.isEndTag();
+				SWBuf lastText;
+				//bool show = true;	// to handle unplaced article in kjv2003-- temporary till combined
+
+				if (endTag) {
+					tag = u->w.c_str();
+					lastText = u->lastTextNode.c_str();
+				}
+				else lastText = "stuff";
+
+				const char *attrib;
+				const char *val;
+				if ((attrib = tag.getAttribute("xlit"))) {
+					val = strchr(attrib, ':');
+					val = (val) ? (val + 1) : attrib;
+					outText(" ", buf, u);
+					outText(val, buf, u);
+				}
+				if ((attrib = tag.getAttribute("gloss"))) {
+					// I'm sure this is not the cleanest way to do it, but it gets the job done
+					// for rendering ruby chars properly ^_^
+					buf -= lastText.length();
+					
+					outText("<ruby><rb>", buf, u);
+					outText(lastText, buf, u);
+					val = strchr(attrib, ':');
+					val = (val) ? (val + 1) : attrib;
+					outText("</rb><rp>(</rp><rt>", buf, u);
+					outText(val, buf, u);
+					outText("</rt><rp>)</rp></ruby>", buf, u);
+				}
+				if (!morphFirst) {
+					processLemma(u->suspendTextPassThru, tag, buf);
+					processMorph(u->suspendTextPassThru, tag, buf);
+				}
+				else {
+					processMorph(u->suspendTextPassThru, tag, buf);
+					processLemma(u->suspendTextPassThru, tag, buf);
+				}
+				if ((attrib = tag.getAttribute("POS"))) {
+					val = strchr(attrib, ':');
+					val = (val) ? (val + 1) : attrib;
+					outText(" ", buf, u);
+					outText(val, buf, u);
+				}
+
+				/*if (endTag)
+					buf += "}";*/
+			}
+		}
+
+		// <note> tag
+		else if (!strcmp(tag.getName(), "note")) {
+			if (!tag.isEndTag()) {
+				SWBuf type = tag.getAttribute("type");
+				bool strongsMarkup = (type == "x-strongsMarkup" || type == "strongsMarkup");	// the latter is deprecated
+				if (strongsMarkup) {
+					tag.setEmpty(false);	// handle bug in KJV2003 module where some note open tags were <note ... />
+				}
+
+				if (!tag.isEmpty()) {
+
+					if (!strongsMarkup) {	// leave strong's markup notes out, in the future we'll probably have different option filters to turn different note types on or off
+						SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
+						VerseKey *vkey = NULL;
+						char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+
+						u->inXRefNote = true; // Why this change? Ben Morgan: Any note can have references in, so we need to set this to true for all notes
+//						u->inXRefNote = (ch == 'x');
+
+						// see if we have a VerseKey * or descendant
+						SWTRY {
+							vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+						}
+						SWCATCH ( ... ) {	}
+						if (vkey) {
+							//printf("URL = %s\n",URL::encode(vkey->getText()).c_str());
+							buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c</sup></small></a>",
+								ch, 
+								URL::encode(footnoteNumber.c_str()).c_str(), 
+								URL::encode(u->version.c_str()).c_str(), 
+								URL::encode(vkey->getText()).c_str(), 
+								ch,
+								ch);
+						}
+						else {
+							buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c</sup></small></a>",
+								ch, 
+								URL::encode(footnoteNumber.c_str()).c_str(), 
+								URL::encode(u->version.c_str()).c_str(), 
+								URL::encode(u->key->getText()).c_str(),  
+								ch,
+								ch);
+						}
+					}
+				}
+				u->suspendTextPassThru = (++u->suspendLevel);
+			}
+			if (tag.isEndTag()) {
+				u->suspendTextPassThru = (--u->suspendLevel);
+				u->inXRefNote = false;
+				u->lastSuspendSegment = ""; // fix/work-around for nasb devineName in note bug
+			}
+		}
+
+		// <p> paragraph and <lg> linegroup tags
+		else if (!strcmp(tag.getName(), "p") || !strcmp(tag.getName(), "lg")) {
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {	// non-empty start tag
+				outText("<!P><br />", buf, u);
+			}
+			else if (tag.isEndTag()) {	// end tag
+				outText("<!/P><br />", buf, u);
+				userData->supressAdjacentWhitespace = true;
+			}
+			else {					// empty paragraph break marker
+				outText("<!P><br />", buf, u);
+				userData->supressAdjacentWhitespace = true;
+			}
+		}
+
+		// Milestoned paragraphs, created by osis2mod
+		// <div type="paragraph" sID.../>
+		// <div type="paragraph" eID.../>
+		else if (tag.isEmpty() && !strcmp(tag.getName(), "div") && tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "paragraph")) {
+			// <div type="paragraph"  sID... />
+			if (tag.getAttribute("sID")) {	// non-empty start tag
+				outText("<!P><br />", buf, u);
+			}
+			// <div type="paragraph"  eID... />
+			else if (tag.getAttribute("eID")) {
+				outText("<!/P><br />", buf, u);
+				userData->supressAdjacentWhitespace = true;
+			}
+		}
+
+		// <reference> tag
+		else if (!strcmp(tag.getName(), "reference")) {	
+			if (!u->inXRefNote) {	// only show these if we're not in an xref note				
+				if (!tag.isEndTag()) {
+					SWBuf target;
+					SWBuf work;
+					SWBuf ref;
+					bool is_scripRef = false;
+
+					target = tag.getAttribute("osisRef");
+					const char* the_ref = strchr(target, ':');
+					
+					if(!the_ref) {
+						// No work
+						ref = target;
+						is_scripRef = true;
+					}
+					else {
+						// Compensate for starting :
+						ref = the_ref + 1;
+
+						int size = target.size() - ref.size() - 1;
+						work.setSize(size);
+						strncpy(work.getRawData(), target, size);
+
+						// For Bible:Gen.3.15 or Bible.vulgate:Gen.3.15
+						if(!strncmp(work, "Bible", 5))
+							is_scripRef = true;
+					}
+
+					if(is_scripRef)
+					{
+						buf.appendFormatted("<a href=\"passagestudy.jsp?action=showRef&type=scripRef&value=%s&module=\">",
+							URL::encode(ref.c_str()).c_str()
+//							(work.size()) ? URL::encode(work.c_str()).c_str() : "")
+							);
+					}
+					else
+					{
+						// Dictionary link, or something
+						buf.appendFormatted("<a href=\"sword://%s/%s\">",
+							URL::encode(work.c_str()).c_str(),
+							URL::encode(ref.c_str()).c_str()
+							);
+					}
+				}
+				else {
+					outText("</a>", buf, u);
+				}
+			}
+		}
+
+		// <l> poetry, etc
+		else if (!strcmp(tag.getName(), "l")) {
+			// end line marker
+			if (tag.getAttribute("eID")) {
+				outText("<br />", buf, u);
+			}
+			// <l/> without eID or sID
+			// Note: this is improper osis. This should be <lb/>
+			else if (tag.isEmpty() && !tag.getAttribute("sID")) {
+				outText("<br />", buf, u);
+			}
+			// end of the line
+			else if (tag.isEndTag()) {
+				outText("<br />", buf, u);
+			}
+		}
+
+		// <lb.../>
+		else if (!strcmp(tag.getName(), "lb")) {
+			outText("<br />", buf, u);
+			userData->supressAdjacentWhitespace = true;
+		}
+		// <milestone type="line"/>
+		// <milestone type="x-p"/>
+		// <milestone type="cQuote" marker="x"/>
+		else if ((!strcmp(tag.getName(), "milestone")) && (tag.getAttribute("type"))) {
+			if (!strcmp(tag.getAttribute("type"), "line")) {
+				outText("<br />", buf, u);
+				if (tag.getAttribute("subType") && !strcmp(tag.getAttribute("subType"), "x-PM")) {
+					outText("<br />", buf, u);
+				}
+				userData->supressAdjacentWhitespace = true;
+			}
+			else if (!strcmp(tag.getAttribute("type"),"x-p"))  {
+				if (tag.getAttribute("marker"))
+					outText(tag.getAttribute("marker"), buf, u);
+				else outText("<!p>", buf, u);
+			}
+			else if (!strcmp(tag.getAttribute("type"), "cQuote")) {
+				const char *tmp = tag.getAttribute("marker");
+				bool hasMark    = tmp;
+				SWBuf mark      = tmp;
+				tmp             = tag.getAttribute("level");
+				int level       = (tmp) ? atoi(tmp) : 1;
+
+				// first check to see if we've been given an explicit mark
+				if (hasMark)
+					outText(mark, buf, u);
+				// finally, alternate " and ', if config says we should supply a mark
+				else if (u->osisQToTick)
+					outText((level % 2) ? '\"' : '\'', buf, u);
+			}
+		}
+
+		// <title>
+		else if (!strcmp(tag.getName(), "title")) {
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+				buf += "<h3>";
+			}
+			else if (tag.isEndTag()) {
+				buf += "</h3>";
+			}
+		}
+		
+		// <list>
+		else if (!strcmp(tag.getName(), "list")) {
+			if((!tag.isEndTag()) && (!tag.isEmpty())) {
+				outText("<ul>", buf, u);
+			}
+			else if (tag.isEndTag()) {
+				outText("</ul>", buf, u);
+			}
+		}
+
+		// <item>
+		else if (!strcmp(tag.getName(), "item")) {
+			if((!tag.isEndTag()) && (!tag.isEmpty())) {
+				outText("<li>", buf, u);
+			}
+			else if (tag.isEndTag()) {
+				outText("</li>", buf, u);
+			}
+		}
+		// <catchWord> & <rdg> tags (italicize)
+		else if (!strcmp(tag.getName(), "rdg") || !strcmp(tag.getName(), "catchWord")) {
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+				outText("<i>", buf, u);
+			}
+			else if (tag.isEndTag()) {
+				outText("</i>", buf, u);
+			}
+		}
+
+		// divineName  
+		else if (!strcmp(tag.getName(), "divineName")) {
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+				u->suspendTextPassThru = (++u->suspendLevel);
+			}
+			else if (tag.isEndTag()) {
+				SWBuf lastText = u->lastSuspendSegment.c_str();
+				u->suspendTextPassThru = (--u->suspendLevel);
+				if (lastText.size()) {
+					scratch.setFormatted("<span class=\"divineName\">%s</span>", lastText.c_str());
+					outText(scratch.c_str(), buf, u);
+				}               
+			} 
+		}
+
+		// <hi> text highlighting
+		else if (!strcmp(tag.getName(), "hi")) {
+			SWBuf type = tag.getAttribute("type");
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+				if (type == "bold" || type == "b" || type == "x-b") {
+					outText("<b>", buf, u);
+				}
+				else {	// all other types
+					outText("<i>", buf, u);
+				}
+				u->tagStacks->hiStack.push(tag.toString());
+			}
+			else if (tag.isEndTag()) {
+				SWBuf type = "";
+				if (!u->tagStacks->hiStack.empty()) {
+					XMLTag tag(u->tagStacks->hiStack.top());
+					u->tagStacks->hiStack.pop();
+					type = tag.getAttribute("type");
+				}
+				if (type == "bold" || type == "b" || type == "x-b") {
+					outText("</b>", buf, u);
+				}
+				else outText("</i>", buf, u);
+			}
+		}
+
+		// <q> quote
+		// Rules for a quote element:
+		// If the tag is empty with an sID or an eID then use whatever it specifies for quoting.
+		//    Note: empty elements without sID or eID are ignored.
+		// If the tag is <q> then use it's specifications and push it onto a stack for </q>
+		// If the tag is </q> then use the pushed <q> for specification
+		// If there is a marker attribute, possibly empty, this overrides osisQToTick.
+		// If osisQToTick, then output the marker, using level to determine the type of mark.
+		else if (!strcmp(tag.getName(), "q")) {
+			SWBuf type      = tag.getAttribute("type");
+			SWBuf who       = tag.getAttribute("who");
+			const char *tmp = tag.getAttribute("level");
+			int level       = (tmp) ? atoi(tmp) : 1;
+			tmp             = tag.getAttribute("marker");
+			bool hasMark    = tmp;
+			SWBuf mark      = tmp;
+
+			// open <q> or <q sID... />
+			if ((!tag.isEmpty() && !tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("sID"))) {
+				// if <q> then remember it for the </q>
+				if (!tag.isEmpty()) {
+					u->tagStacks->quoteStack.push(tag.toString());
+				}
+
+				// Do this first so quote marks are included as WoC
+				if (who == "Jesus")
+					outText(u->wordsOfChristStart, buf, u);
+
+				// first check to see if we've been given an explicit mark
+				if (hasMark)
+					outText(mark, buf, u);
+				//alternate " and '
+				else if (u->osisQToTick)
+					outText((level % 2) ? '\"' : '\'', buf, u);
+			}
+			// close </q> or <q eID... />
+			else if ((tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("eID"))) {
+				// if it is </q> then pop the stack for the attributes
+				if (tag.isEndTag() && !u->tagStacks->quoteStack.empty()) {
+					XMLTag qTag(u->tagStacks->quoteStack.top());
+					u->tagStacks->quoteStack.pop();
+
+					type    = qTag.getAttribute("type");
+					who     = qTag.getAttribute("who");
+					tmp     = qTag.getAttribute("level");
+					level   = (tmp) ? atoi(tmp) : 1;
+					tmp     = qTag.getAttribute("marker");
+					hasMark = tmp;
+					mark    = tmp;
+				}
+
+				// first check to see if we've been given an explicit mark
+				if (hasMark)
+					outText(mark, buf, u);
+				// finally, alternate " and ', if config says we should supply a mark
+				else if (u->osisQToTick)
+					outText((level % 2) ? '\"' : '\'', buf, u);
+
+				// Do this last so quote marks are included as WoC
+				if (who == "Jesus")
+					outText(u->wordsOfChristEnd, buf, u);
+			}
+		}
+
+		// <transChange>
+		else if (!strcmp(tag.getName(), "transChange")) {
+			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+				SWBuf type = tag.getAttribute("type");
+				u->lastTransChange = type;
+
+				// just do all transChange tags this way for now
+				if ((type == "added") || (type == "supplied"))
+					outText("<i>", buf, u);
+				else if (type == "tenseChange")
+					buf += "*";
+			}
+			else if (tag.isEndTag()) {
+				SWBuf type = u->lastTransChange;
+				if ((type == "added") || (type == "supplied"))
+					outText("</i>", buf, u);
+			}
+			else {	// empty transChange marker?
+			}
+		}
+
+		// image
+		else if (!strcmp(tag.getName(), "figure")) {
+			const char *src = tag.getAttribute("src");
+			if (!src)		// assert we have a src attribute
+				return false;
+
+			SWBuf filepath;
+			if (userData->module) {
+				filepath = userData->module->getConfigEntry("AbsoluteDataPath");
+				if ((filepath.size()) && (filepath[filepath.size()-1] != '/') && (src[0] != '/'))
+					filepath += '/';
+			}
+			filepath += src;
+
+			// images become clickable, if the UI supports showImage.
+			outText("<a href=\"passagestudy.jsp?action=showImage&value=", buf, u);
+			outText(URL::encode(filepath.c_str()).c_str(), buf, u);
+			outText("&module=", buf, u);
+			outText(URL::encode(u->version.c_str()).c_str(), buf, u);
+			outText("\">", buf, u);
+
+			outText("<img src=\"file:", buf, u);
+			outText(filepath, buf, u);
+			outText("\" border=\"0\" />", buf, u);
+
+			outText("</a>", buf, u);
+		}
+
+		// ok to leave these in
+		else if (!strcmp(tag.getName(), "div")) {
+			buf += tag;
+		}
+		else if (!strcmp(tag.getName(), "span")) {
+			buf += tag;
+		}
+		else if (!strcmp(tag.getName(), "br")) {
+			buf += tag;
+		}
+
+		else {
+		      return false;  // we still didn't handle token
+		}
+	}
+	return true;
+}
+
+
+SWORD_NAMESPACE_END

Modified: trunk/src/modules/filters/thmlwebif.cpp
===================================================================
--- trunk/src/modules/filters/thmlwebif.cpp	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/src/modules/filters/thmlwebif.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -31,7 +31,6 @@
 SWORD_NAMESPACE_START
 
 ThMLWEBIF::ThMLWEBIF() : baseURL(""), passageStudyURL(baseURL + "passagestudy.jsp") {
-  //all's done in ThMLHTMLHREF
 }
 
 bool ThMLWEBIF::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
@@ -100,7 +99,7 @@
 			}
 		}
 		else {
-			return ThMLHTMLHREF::handleToken(buf,token,userData);
+			return ThMLXHTML::handleToken(buf, token, userData);
 		}
 	}
 	return true;

Added: trunk/src/modules/filters/thmlxhtml.cpp
===================================================================
--- trunk/src/modules/filters/thmlxhtml.cpp	                        (rev 0)
+++ trunk/src/modules/filters/thmlxhtml.cpp	2011-06-28 20:40:34 UTC (rev 2628)
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * thmlxhtml -	ThML to classed XHTML
+ *
+ *
+ * Copyright 2011 CrossWire Bible Society (http://www.crosswire.org)
+ *	CrossWire Bible Society
+ *	P. O. Box 2528
+ *	Tempe, AZ  85280-2528
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <thmlxhtml.h>
+#include <swmodule.h>
+#include <utilxml.h>
+#include <utilstr.h>
+#include <versekey.h>
+#include <url.h>
+
+SWORD_NAMESPACE_START
+ 
+const char *ThMLXHTML::getHeader() const {
+	return "\
+	";
+}
+
+ThMLXHTML::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {
+	if (module) {
+		version = module->Name();
+		BiblicalText = (!strcmp(module->Type(), "Biblical Texts"));
+		SecHead = false;
+	}	
+}
+
+
+ThMLXHTML::ThMLXHTML() {
+	setTokenStart("<");
+	setTokenEnd(">");
+
+	setEscapeStart("&");
+	setEscapeEnd(";");
+
+	setEscapeStringCaseSensitive(true);
+	setPassThruNumericEscapeString(true);
+
+	addAllowedEscapeString("quot");
+	addAllowedEscapeString("amp");
+	addAllowedEscapeString("lt");
+	addAllowedEscapeString("gt");
+
+	addAllowedEscapeString("nbsp");
+	addAllowedEscapeString("brvbar"); // "Š"
+	addAllowedEscapeString("sect");   // "§"
+	addAllowedEscapeString("copy");   // "©"
+	addAllowedEscapeString("laquo");  // "«"
+	addAllowedEscapeString("reg");    // "®"
+	addAllowedEscapeString("acute");  // "Ž"
+	addAllowedEscapeString("para");   // "¶"
+	addAllowedEscapeString("raquo");  // "»"
+
+	addAllowedEscapeString("Aacute"); // "Á"
+	addAllowedEscapeString("Agrave"); // "À"
+	addAllowedEscapeString("Acirc");  // "Â"
+	addAllowedEscapeString("Auml");   // "Ä"
+	addAllowedEscapeString("Atilde"); // "Ã"
+	addAllowedEscapeString("Aring");  // "Å"
+	addAllowedEscapeString("aacute"); // "á"
+	addAllowedEscapeString("agrave"); // "à"
+	addAllowedEscapeString("acirc");  // "â"
+	addAllowedEscapeString("auml");   // "ä"
+	addAllowedEscapeString("atilde"); // "ã"
+	addAllowedEscapeString("aring");  // "å"
+	addAllowedEscapeString("Eacute"); // "É"
+	addAllowedEscapeString("Egrave"); // "È"
+	addAllowedEscapeString("Ecirc");  // "Ê"
+	addAllowedEscapeString("Euml");   // "Ë"
+	addAllowedEscapeString("eacute"); // "é"
+	addAllowedEscapeString("egrave"); // "è"
+	addAllowedEscapeString("ecirc");  // "ê"
+	addAllowedEscapeString("euml");   // "ë"
+	addAllowedEscapeString("Iacute"); // "Í"
+	addAllowedEscapeString("Igrave"); // "Ì"
+	addAllowedEscapeString("Icirc");  // "Î"
+	addAllowedEscapeString("Iuml");   // "Ï"
+	addAllowedEscapeString("iacute"); // "í"
+	addAllowedEscapeString("igrave"); // "ì"
+	addAllowedEscapeString("icirc");  // "î"
+	addAllowedEscapeString("iuml");   // "ï"
+	addAllowedEscapeString("Oacute"); // "Ó"
+	addAllowedEscapeString("Ograve"); // "Ò"
+	addAllowedEscapeString("Ocirc");  // "Ô"
+	addAllowedEscapeString("Ouml");   // "Ö"
+	addAllowedEscapeString("Otilde"); // "Õ"
+	addAllowedEscapeString("oacute"); // "ó"
+	addAllowedEscapeString("ograve"); // "ò"
+	addAllowedEscapeString("ocirc");  // "ô"
+	addAllowedEscapeString("ouml");   // "ö"
+	addAllowedEscapeString("otilde"); // "õ"
+	addAllowedEscapeString("Uacute"); // "Ú"
+	addAllowedEscapeString("Ugrave"); // "Ù"
+	addAllowedEscapeString("Ucirc");  // "Û"
+	addAllowedEscapeString("Uuml");   // "Ü"
+	addAllowedEscapeString("uacute"); // "ú"
+	addAllowedEscapeString("ugrave"); // "ù"
+	addAllowedEscapeString("ucirc");  // "û"
+	addAllowedEscapeString("uuml");   // "ü"
+	addAllowedEscapeString("Yacute"); // "Ý"
+	addAllowedEscapeString("yacute"); // "ý"
+	addAllowedEscapeString("yuml");   // "ÿ"
+
+	addAllowedEscapeString("deg");    // "°"
+	addAllowedEscapeString("plusmn"); // "±"
+	addAllowedEscapeString("sup2");   // "²"
+	addAllowedEscapeString("sup3");   // "³"
+	addAllowedEscapeString("sup1");   // "¹"
+	addAllowedEscapeString("nbsp");   // "º"
+	addAllowedEscapeString("pound");  // "£"
+	addAllowedEscapeString("cent");   // "¢"
+	addAllowedEscapeString("frac14"); // "Œ"
+	addAllowedEscapeString("frac12"); // "œ"
+	addAllowedEscapeString("frac34"); // "Ÿ"
+	addAllowedEscapeString("iquest"); // "¿"
+	addAllowedEscapeString("iexcl");  // "¡"
+	addAllowedEscapeString("ETH");    // "Ð"
+	addAllowedEscapeString("eth");    // "ð"
+	addAllowedEscapeString("THORN");  // "Þ"
+	addAllowedEscapeString("thorn");  // "þ"
+	addAllowedEscapeString("AElig");  // "Æ"
+	addAllowedEscapeString("aelig");  // "æ"
+	addAllowedEscapeString("Oslash"); // "Ø"
+	addAllowedEscapeString("curren"); // "€"
+	addAllowedEscapeString("Ccedil"); // "Ç"
+	addAllowedEscapeString("ccedil"); // "ç"
+	addAllowedEscapeString("szlig");  // "ß"
+	addAllowedEscapeString("Ntilde"); // "Ñ"
+	addAllowedEscapeString("ntilde"); // "ñ"
+	addAllowedEscapeString("yen");    // "¥"
+	addAllowedEscapeString("not");    // "¬"
+	addAllowedEscapeString("ordf");   // "ª"
+	addAllowedEscapeString("uml");    // "š"
+	addAllowedEscapeString("shy");    // "­"
+	addAllowedEscapeString("macr");   // "¯"
+
+	addAllowedEscapeString("micro");  // "µ"
+	addAllowedEscapeString("middot"); // "·"
+	addAllowedEscapeString("cedil");  // "ž"
+	addAllowedEscapeString("ordm");   // "º"
+	addAllowedEscapeString("times");  // "×"
+	addAllowedEscapeString("divide"); // "÷"
+	addAllowedEscapeString("oslash"); // "ø"
+
+	setTokenCaseSensitive(true);
+//	addTokenSubstitute("scripture", "<i> ");
+	addTokenSubstitute("/scripture", "</i> ");
+}
+
+
+bool ThMLXHTML::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
+	if (!substituteToken(buf, token)) { // manually process if it wasn't a simple substitution
+		MyUserData *u = (MyUserData *)userData;		
+
+		XMLTag tag(token);
+		if ((!tag.isEndTag()) && (!tag.isEmpty()))
+			u->startTag = tag;
+
+		if (tag.getName() && !strcmp(tag.getName(), "sync")) {
+			SWBuf value = tag.getAttribute("value");
+			if (tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "morph")) { //>
+				if(value.length())
+					buf.appendFormatted("<small><em class=\"morph\">(<a href=\"passagestudy.jsp?action=showMorph&type=Greek&value=%s\" class=\"morph\">%s</a>)</em></small>", 
+						URL::encode(value.c_str()).c_str(),
+						value.c_str());
+			}
+			else if (tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "lemma")) { //>
+				if(value.length())
+					// empty "type=" is deliberate.
+					buf.appendFormatted("<small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=&value=%s\" class=\"strongs\">%s</a>></em></small>", 
+						URL::encode(value.c_str()).c_str(),
+						value.c_str());
+			}
+			else if (tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "Strongs")) {
+				char ch = *value;
+				value<<1;
+				buf.appendFormatted("<small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=%s&value=%s\" class=\"strongs\">",
+						    ((ch == 'H') ? "Hebrew" : "Greek"),
+						    URL::encode(value.c_str()).c_str());
+				buf += (value.length()) ? value.c_str() : "";
+				buf += "</a>></em></small>";
+			}
+			else if (tag.getAttribute("type") && !strcmp(tag.getAttribute("type"), "Dict")) {
+				buf += (tag.isEndTag() ? "</b>" : "<b>");
+			}
+				
+		}
+		// <note> tag
+		else if (!strcmp(tag.getName(), "note")) {
+			if (!tag.isEndTag()) {
+				if (!tag.isEmpty()) {
+					SWBuf type = tag.getAttribute("type");
+					SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
+					VerseKey *vkey = NULL;
+					// see if we have a VerseKey * or descendant
+					SWTRY {
+						vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+					}
+					SWCATCH ( ... ) {	}
+					if (vkey) {
+						// leave this special osis type in for crossReference notes types?  Might thml use this some day? Doesn't hurt.
+						char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+						buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c</sup></small></a>", 
+							ch, 
+							URL::encode(footnoteNumber.c_str()).c_str(), 
+							URL::encode(u->version.c_str()).c_str(), 
+							URL::encode(vkey->getText()).c_str(), 
+							ch,
+							ch);
+					}
+					else {
+						char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+						buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c</sup></small></a>", 
+							ch, 
+							URL::encode(footnoteNumber.c_str()).c_str(), 
+							URL::encode(u->version.c_str()).c_str(), 
+							URL::encode(u->key->getText()).c_str(),  
+							ch,
+							ch);
+					}
+					u->suspendTextPassThru = true;
+				}
+			}
+			if (tag.isEndTag()) {
+				u->suspendTextPassThru = false;
+			}
+		}
+		else if (!strcmp(tag.getName(), "scripture")) {
+			buf += (tag.isEndTag() ? "</i>" : "<i>");
+		}
+		// <scripRef> tag
+		else if (!strcmp(tag.getName(), "scripRef")) {
+			if (!tag.isEndTag()) {
+				if (!tag.isEmpty()) {
+					u->suspendTextPassThru = true;
+				}
+			}
+			if (tag.isEndTag()) {	//	</scripRef>
+				if (!u->BiblicalText) {
+					SWBuf refList = u->startTag.getAttribute("passage");
+					if (!refList.length())
+						refList = u->lastTextNode;
+					SWBuf version = tag.getAttribute("version");
+					
+					buf.appendFormatted("<a href=\"passagestudy.jsp?action=showRef&type=scripRef&value=%s&module=%s\">",
+						(refList.length()) ? URL::encode(refList.c_str()).c_str() : "", 
+						(version.length()) ? URL::encode(version.c_str()).c_str() : "");
+					buf += u->lastTextNode.c_str();
+					buf += "</a>";
+				}
+				else {
+					SWBuf footnoteNumber = u->startTag.getAttribute("swordFootnote");
+					VerseKey *vkey = NULL;
+					// see if we have a VerseKey * or descendant
+					SWTRY {
+						vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+					}
+					SWCATCH ( ... ) {}
+					if (vkey) {
+						// leave this special osis type in for crossReference notes types?  Might thml use this some day? Doesn't hurt.
+						//buf.appendFormatted("<a href=\"noteID=%s.x.%s\"><small><sup>*x</sup></small></a> ", vkey->getText(), footnoteNumber.c_str());
+						buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=x&value=%s&module=%s&passage=%s\"><small><sup class=\"x\">*x</sup></small></a>",
+							URL::encode(footnoteNumber.c_str()).c_str(), 
+							URL::encode(u->version.c_str()).c_str(),
+							URL::encode(vkey->getText()).c_str());
+					
+					}
+				}
+
+				// let's let text resume to output again
+				u->suspendTextPassThru = false;
+			}
+		}
+		else if (tag.getName() && !strcmp(tag.getName(), "div")) {
+			if (tag.isEndTag() && u->SecHead) {
+				buf += "</i></b><br />";
+				u->SecHead = false;
+			}
+			else if (tag.getAttribute("class")) {
+				if (!stricmp(tag.getAttribute("class"), "sechead")) {
+					u->SecHead = true;
+					buf += "<br /><b><i>";
+				}
+				else if (!stricmp(tag.getAttribute("class"), "title")) {
+					u->SecHead = true;
+					buf += "<br /><b><i>";
+				}
+				else {
+					buf += tag;
+				}
+			}
+			else {
+				buf += tag;
+			}
+		}
+		else if (tag.getName() && (!strcmp(tag.getName(), "img") || !strcmp(tag.getName(), "image"))) {
+			const char *src = strstr(token, "src");
+			if (!src)		// assert we have a src attribute
+				return false;
+
+			const char *c, *d;
+			if (((c = strchr(src+3, '"')) == NULL) ||
+			    ((d = strchr( ++c , '"')) == NULL))	// identify endpoints.
+				return false;			// abandon hope.
+
+			SWBuf imagename = "file:";
+			if (*c == '/')				// as below, inside for loop.
+				imagename += userData->module->getConfigEntry("AbsoluteDataPath");
+			while (c != d)				// move bits into the name.
+			    imagename += *(c++);
+
+			// images become clickable, if the UI supports showImage.
+			buf.appendFormatted("<a href=\"passagestudy.jsp?action=showImage&value=%s&module=%s\"><",
+					    URL::encode(imagename.c_str()).c_str(),
+					    URL::encode(u->version.c_str()).c_str());
+
+			for (c = token; *c; c++) {
+				if ((*c == '/') && (*(c+1) == '\0'))
+					continue;
+				if (c == src) {
+					for (;((*c) && (*c != '"')); c++)
+						buf += *c;
+
+					if (!*c) { c--; continue; }
+
+					buf += '"';
+					if (*(c+1) == '/') {
+						buf += "file:";
+						buf += userData->module->getConfigEntry("AbsoluteDataPath");
+						if (buf[buf.length()-2] == '/')
+							c++;		// skip '/'
+					}
+					continue;
+				}
+				buf += *c;
+			}
+               buf += " border=0 /></a>";
+		}
+		else {
+			buf += '<';
+			/*for (const char *tok = token; *tok; tok++)
+				buf += *tok;*/
+			buf += token;
+			buf += '>';
+			//return false;  // we still didn't handle token
+		}
+	}
+	return true;
+}
+
+
+SWORD_NAMESPACE_END

Modified: trunk/tests/Makefile.am
===================================================================
--- trunk/tests/Makefile.am	2011-06-23 10:24:07 UTC (rev 2627)
+++ trunk/tests/Makefile.am	2011-06-28 20:40:34 UTC (rev 2628)
@@ -7,7 +7,7 @@
 
 SUBDIRS = cppunit
 
-noinst_PROGRAMS = utf8norm ciphertest keytest mgrtest parsekey versekeytest vtreekeytest versemgrtest listtest casttest \
+noinst_PROGRAMS = utf8norm ciphertest keytest mgrtest parsekey versekeytest vtreekeytest versemgrtest listtest casttest bibliography\
 modtest compnone complzss localetest introtest indextest configtest keycast \
 romantest testblocks filtertest rawldidxtest lextest swaptest \
  swbuftest xmltest webiftest striptest
@@ -66,8 +66,8 @@
 webiftest_SOURCES = webiftest.cpp
 striptest_SOURCES = striptest.cpp
 xmltest_SOURCES = xmltest.cpp
+bibliography_SOURCES = bibliography.cpp
 
-
 EXTRA_DIST = 
 include bcppmake/Makefile.am
 include testsuite/Makefile.am




More information about the sword-cvs mailing list