[sword-devel] Sword Web Interface!

John Keiser sword-devel@crosswire.org
22 Jul 2001 02:12:15 -0400


--=-NgjpZVcnkDk4sAoMNofT
Content-Type: text/plain

Well, all this Perl stuff has come to fruition.  I have completed a
fairly nice-looking Web interface to Sword that allows you to search and
compare multiple translations (kind of slow because it actually performs
the search in every translation) and browse chapter by chapter.  Search
terms are highlighted even in browse.  You can enter arbitrary
book/chapter/verse ranges and it will display everything.

And it works with mod_perl, so SWMgr should be staying around!!  I'll
have to check whether it actually is sometime, but mod_perl is supposed
to do something like that.

I have attached the CGI script as well as the image.  All you need is
to:
- install sword, some Bible modules and the Sword-0.3 package I sent
earlier
- put the script in cgi-bin or mod_perl dir somewhere
- make it executable
- put the image somewhere
- set the image_dir (at the top of the script) to where the image is
(from the web--if it's in your docroot)

Or to try it out, go here:
http://cy209696-a.cospgs1.co.home.com/~john/perl/sword.pl
(If name resolution doesn't work:)
http://24.253.114.106/~john/perl/sword.pl

Please don't pound my poor little box too hard though :)

--John


--=-NgjpZVcnkDk4sAoMNofT
Content-Type: text/x-perl
Content-Disposition: attachment; filename=sword.pl
Content-ID: 995782233.2348.11.camel@cy209696-a
Content-Transfer-Encoding: 7bit

#!/usr/bin/perl

use strict;

use Sword;
use CGI;

# CONFIG
my $image_dir="/~john/sword";

my $p = new CGI;

print $p->header;
print <<"EOM";
<HEAD>
<TITLE>Sword Bible Interface</TITLE>
</HEAD>
<BODY BGCOLOR=lightgray>
<CENTER><A HREF=sword.pl><IMG BORDER=0 SRC="$image_dir/sword_web.gif"></A></CENTER>
EOM

sub next_chapter {
	my ($mod, $book, $chap) = @_;

	if($chap >= $mod->get_num_chapters($book)) {
		my @books = $mod->get_books;
		my $book_num = $mod->{BOOKS}{$book}{real_book_num};
		if($book_num == (@books - 1)) {
			return "";
		} else {
			return $books[$book_num + 1] . " 1";
		}
	} else {
		return "$book " . ($chap+1);
	}
}

sub prev_chapter {
	my ($mod, $book, $chap) = @_;

	if($chap <= 1) {
		my @books = $mod->get_books;
		my $book_num = $mod->{BOOKS}{$book}{real_book_num};
		if($book_num == 0) {
			return "";
		} else {
			return $books[$book_num - 1] . " " . $mod->get_num_chapters($book);
		}
	} else {
		return "$book " . ($chap-1);
	}
}

sub print_verse_range {
	my ($p, $mods, @range) = @_;

	my @mods = @{$mods};
	my $search = $p->param("search");

	#
	# Print header
	#
	print "<TABLE BORDER=1>\n";
	print "<TR>\n";
	print "<TD>&nbsp;</TD>\n";
	
	foreach my $mod (@mods) {
		print "<TH>", $mod->get_name(), "</TH>\n";
	}

	print "</TR>\n";
	
	#
	# Set up loop
	#

	my @vis;
	foreach my $mod (@mods) {
		push @vis, $mod->get_iterator();
	}
	my $prev_book;
	my $prev_chapter;

	my $result_ind = 0;
	my ($result_low, $result_high) = Sword::Module::_as_verse_range($range[$result_ind]);
	foreach my $vi (@vis) {
		$vi->go_to_verse($result_low);
	}
	do {
		if($vis[0]->get_book ne $prev_book || $vis[0]->get_chapter_num != $prev_chapter) {
			$prev_book = $vis[0]->get_book;
			$prev_chapter = $vis[0]->get_chapter_num;
			my $q = new CGI($p);
			my $chap = prev_chapter($mods[0], $vis[0]->get_book, $vis[0]->get_chapter_num);
			$q->param("browse", $chap);
			my $prev_url = $q->self_url if $chap;
			$chap = next_chapter($mods[0], $vis[0]->get_book, $vis[0]->get_chapter_num);
			$q->param("browse", $chap);
			my $next_url = $q->self_url if $chap;
			$chap = $vis[0]->get_book . " " . $vis[0]->get_chapter_num;
			$q->param("browse", ($vis[0]->get_book . " " . $vis[0]->get_chapter_num));
			my $chap_url = $q->self_url if $chap;;

			print "<TR>\n";
			print "<TD colspan=", (@vis + 1), "><CENTER><B>";

			if($prev_url) {
				print "<A HREF='$prev_url'>&lt;&lt;</A>&nbsp;&nbsp;";
			}
			if($chap_url) {
				print "<A HREF='$chap_url'>$prev_book $prev_chapter</A>";
			}
			if($next_url) {
				print "&nbsp;&nbsp;<A HREF='$next_url'>&gt;&gt;</A>";
			}
			print "</B></CENTER></TD>\n";
			print "</TR>\n";
		}
		print "<TR>\n";
		print "<TH>", $vis[0]->get_key, "</TH>\n";
		foreach my $vi (@vis) {
			my $verse = $vi->get_verse;
			if($verse =~ s/($search)/<B><I>$1<\/I><\/B>/g) {
				print "<TD BGCOLOR=green>$verse</TD>\n";
			} else {
				print "<TD>$verse</TD>\n";
			}
		}
		print "</TR>\n";

		foreach my $vi (@vis) {
			$vi->next;
		}
		if($mods[0]->verse_greater($vis[0]->get_key(), $result_high)) {
			$result_ind++;
			last if $result_ind >= @range;
			($result_low, $result_high) = Sword::Module::_as_verse_range($range[$result_ind]);
			foreach my $vi (@vis) {
				$vi->go_to_verse($result_low);
			}
		}
	} while($result_ind < @range);

	#
	# Print footer
	#
	print "</TABLE>\n";
}


if($p->param("browse")) {
	my @mod_names = $p->param("mod_name");
	my @mods;
	foreach my $mod_name (@mod_names) {
		my $mod = Sword::get_module($mod_name);
		push @mods, $mod;
	}
	my @verse_range = $mods[0]->parse_verse_range($p->param("browse"), 0, 1);
	print_verse_range $p, \@mods, @verse_range;
} elsif($p->param("search")) {
	my @mod_names = $p->param("mod_name");
	my @search_results;
	my @mods;

	foreach my $mod_name (@mod_names) {
		my $mod = Sword::get_module($mod_name);
		push @mods, $mod;
		my @sr = $mod->search_verses($p->param("search"));
		@search_results = $mod->union_verse_list(\@search_results, \@sr);
	}

	if(!@search_results) {
		print "<H2><FONT COLOR=red>I'm afraid your search didn't turn up anything.  Try again, better luck!</FONT></H2>\n";
	} else {
		print_verse_range $p, \@mods, @search_results;
	}
} else {
# MAIN FORM
	print qq^
<CENTER>
<H2>Search/Browse</H2>

<FORM METHOD=get NAME=mainform>
<TABLE>
<TR>
<TD COLSPAN=2><CENTER>Enter a list of books/chapters and verses you want to look up<BR>
(Enter nothing to perform a normal search.)</CENTER></TD>
</TR>
<TR>
<TD><B>Books/Chapters:</B><BR>
(i.e. Acts 17-18;John 3:16)</TD>
<TD><INPUT TYPE=text NAME=browse></TD>
</TR>
<TR>
<TD COLSPAN=2><CENTER>Enter a search term or a verse you wish to lookup<BR>
(enter a book/chapter to browse and highlight searched terms):</CENTER></TD>
</TR>
<TR>
<TD><B>Search/Verse:</B></TD>
<TD><INPUT TYPE=text NAME=search></TD>
</TR>
<TD>Choose modules:</TD>
<TD>
<SELECT MULTIPLE NAME=mod_name>
^;
	my $first_one = 1;
	foreach my $mod_name (Sword::get_modules) {
		if($first_one) {
			print qq^<OPTION SELECTED VALUE="$mod_name">$mod_name</OPTION>
^;
			$first_one = 0;
		} else {
			print qq^<OPTION VALUE="$mod_name">$mod_name</OPTION>
^;
		}
	}

	print qq^</SELECT>
</TD>
</TR>
<TR>
<TD COLSPAN=2><INPUT TYPE=submit></TD>
</TR>
</TABLE>
</FORM>

</CENTER>
^;
}

print << "EOM";
<BR>
<BR>
<CENTER><ADDRESS><A HREF="http://www.johnkeiser.com">John Keiser</A> was here!  This stuff is under the <A HREF="http://www.gnu.org/copyleft/gpl.html">GPL</A>.  Copyleft (C) 2001.</ADDRESS></CENTER>
</BODY>
</HTML>
EOM


--=-NgjpZVcnkDk4sAoMNofT
Content-Type: image/gif
Content-Disposition: attachment; filename=sword_web.gif
Content-ID: 995782248.2348.12.camel@cy209696-a
Content-Transfer-Encoding: base64

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

--=-NgjpZVcnkDk4sAoMNofT--