Index: gs3-extensions/html-to-expeditee/trunk/src/perllib/ExpediteeFrameIO.pm
===================================================================
--- gs3-extensions/html-to-expeditee/trunk/src/perllib/ExpediteeFrameIO.pm	(revision 24934)
+++ gs3-extensions/html-to-expeditee/trunk/src/perllib/ExpediteeFrameIO.pm	(revision 24934)
@@ -0,0 +1,438 @@
+###########################################################################
+#
+# ExpediteeFrameIO.pm -- 
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2009 New Zealand Digital Library Project
+#
+# This program is free software; you can redistr   te it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+package ExpediteeFrameIO;
+
+use strict;
+
+sub new 
+{
+    my $class = shift(@_);
+    my ($username) = shift(@_) || "greenstone";
+
+    my $self = { 'items' => [], 'lines' => [], 'constraints' => [] };
+
+    $self->{'username'} = $username;
+
+    return bless $self, $class;
+}
+
+sub getFormatedDate
+{
+    my ($opt_mode) = @_;
+
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+    my @mabbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
+
+    $year += 1900;
+
+    my $fdate;
+
+    if ((defined $opt_mode) && ($opt_mode eq "highPrecision")) {
+	$fdate = sprintf("%02d%s%04d[%02d:%0d2.%02d]", 
+			 $mday, $mabbr[$mon],$year,$hour,$min,$sec);
+    }
+    else {
+	$fdate = sprintf("%02d%s%04d[%02d:%0d2]", 
+			 $mday, $mabbr[$mon],$year,$hour,$min);
+    }
+
+    return $fdate;
+}
+
+sub _nextFreeId
+{
+    my $self = shift @_;
+
+    my $items = $self->{'items'};
+    my $lines = $self->{'lines'};
+    my $constraints = $self->{'constraints'};
+    
+    # Ids start at base of 1
+    return 1+(scalar(@$items) + scalar(@$lines) + scalar(@$constraints));
+}
+
+
+sub _addItem
+{
+    my $self = shift @_;
+    my ($type,$attr) = @_;
+
+    # By this point 'attr' is synonymous with being an item
+
+    my $items = $self->{'items'};
+
+    my $next_free_id = $self->_nextFreeId();
+
+    $attr->{'_type'} = $type;
+    $attr->{'_id'} = $next_free_id;
+
+    push(@$items,$attr);
+
+    return ($attr,$next_free_id);
+}
+
+
+
+sub _setBaseDefaultAttributes
+{
+    my $self = shift @_;
+    my ($attr) = @_;
+
+    $attr->{'o'} = $self->{'username'};
+    $attr->{'s'} = getFormatedDate("highPrecision");
+    $attr->{'Q'} = "0";     # gradient
+    $attr->{'v'} = "S";     # dot type
+}
+
+
+sub setPointDefaultAttributes
+{
+    my $self = shift @_;
+    my ($attr) = @_;
+
+    $self->_setBaseDefaultAttributes($attr);
+}
+
+sub setTextDefaultAttributes
+{
+    my $self = shift @_;
+    my ($attr) = @_;
+
+    $self->_setBaseDefaultAttributes($attr);
+
+    $attr->{'d'} = "0 0 0"; # black color
+}
+
+
+sub setRectPointDefaultAttributes
+{
+    my $self = shift @_;
+    my ($attr) = @_;
+
+    $self->setPointDefaultAttributes($attr);
+
+    $attr->{'d'} = "50 100 50"; # green color for rect lines
+    $attr->{'h'} = "2.0";     # line thickness
+}
+
+
+sub addRectPoint
+{
+    my $self = shift @_;
+    my ($x, $y, $attr) = @_;
+
+    my %attr_copy = %$attr; # make a private copy of 'attr'
+
+    $self->setRectPointDefaultAttributes(\%attr_copy);
+
+    my $items = $self->{'items'};
+
+    $attr_copy{'P'} = "$x $y";
+
+    return $self->_addItem("P",\%attr_copy);
+}
+
+sub addText
+{
+    my $self = shift @_;
+    my ($x, $y, $text, $attr) = @_;
+
+    # fudge factor for now (based on default font size used)
+    $y += 16; # y-value of text item in Expeditee is it's base line
+
+    my %attr_copy = %$attr; # make a private copy of 'attr'
+
+    $self->setTextDefaultAttributes(\%attr_copy);
+
+    my $items = $self->{'items'};
+
+    $attr_copy{'P'} = "$x $y";
+    $attr_copy{'T'} = $text;
+
+    return $self->_addItem("T",\%attr_copy);
+}
+
+sub addLine
+{
+    my $self = shift @_;
+
+    my ($item_id1,$item_id2) = @_;
+
+    my $lines = $self->{'lines'};
+    my $line_type = 1;
+
+    my $next_free_id = $self->_nextFreeId();
+
+    my $attr = { 'L' => "$next_free_id $line_type" };
+
+    $attr->{'s'} = "$item_id1 $item_id2";
+
+    push(@$lines,$attr);
+
+    return ($attr,$next_free_id);
+}
+
+
+sub addConstraint
+{
+    my $self = shift @_;
+
+    my ($orientation,$item_id1,$item_id2) = @_;
+
+    my $constraints = $self->{'constraints'};
+
+    my $orientation_type = undef;
+    if ($orientation eq "vertical") {
+	$orientation_type = 2;
+    }
+    else {
+	# assume horizontal for now
+	$orientation_type = 3;
+    }
+
+    my $next_free_id = $self->_nextFreeId();
+
+    my $attr = { 'C' => "$next_free_id $orientation_type" };
+
+    $attr->{'s'} = "$item_id1 $item_id2";
+
+    push(@$constraints,$attr);
+
+    return ($attr,$next_free_id);
+}
+
+
+sub addRect
+{
+    my $self = shift @_;
+
+    my ($xl, $yt, $xr, $yb, $attr) = @_;
+    
+    # do point in same order Expeditee puts them in
+    my ($p_tr,$p_tr_id) = $self->addRectPoint($xr,$yt,$attr);
+    my ($p_tl,$p_tl_id) = $self->addRectPoint($xl,$yt,$attr);
+    my ($p_bl,$p_bl_id) = $self->addRectPoint($xl,$yb,$attr);
+    my ($p_br,$p_br_id) = $self->addRectPoint($xr,$yb,$attr);
+
+    my ($l_t,$l_t_id) = $self->addLine($p_tr_id,$p_tl_id);
+    my ($l_l,$l_l_id) = $self->addLine($p_tl_id,$p_bl_id);
+    my ($l_b,$l_b_id) = $self->addLine($p_bl_id,$p_br_id);
+    my ($l_r,$l_r_id) = $self->addLine($p_br_id,$p_tr_id);
+
+    my ($c_t,$c_t_id) = $self->addConstraint("horizontal",$p_tr_id,$p_tl_id);
+    my ($c_l,$c_l_id) = $self->addConstraint("vertical"  ,$p_tl_id,$p_bl_id);
+    my ($c_b,$c_b_id) = $self->addConstraint("horizontal",$p_bl_id,$p_br_id);
+    my ($c_r,$c_r_id) = $self->addConstraint("vertical"  ,$p_br_id,$p_tr_id);
+
+    $p_tr->{'l'} = "$l_t_id $l_r_id";
+    $p_tl->{'l'} = "$l_t_id $l_l_id";
+    $p_bl->{'l'} = "$l_l_id $l_b_id";
+    $p_br->{'l'} = "$l_b_id $l_r_id";
+
+    $p_tr->{'c'} = "$c_t_id $c_r_id";
+    $p_tl->{'c'} = "$c_t_id $c_l_id";
+    $p_bl->{'c'} = "$c_l_id $c_b_id";
+    $p_br->{'c'} = "$c_b_id $c_r_id";
+
+}
+
+sub writeHeaderSection
+{
+    my $self = shift @_;
+
+    # Example header:
+    #   V 1
+    #   p 4
+    #   U davidb
+    #   D 09Jan2012[13:33]
+    #   M davidb
+    #   d 09Jan2012[13:33]
+    #   Z
+    #   
+
+    # Legend:
+    #   V = version
+    #   p = permision level
+    #   U = username (owner)
+    #   M = last modified by
+    #   D, d = date information
+    #   Z => end of section 
+
+
+    my $username = $self->{'username'};
+
+    my $fdate = getFormatedDate();
+
+    print FOUT "V 1\n";
+    print FOUT "p 4\n";
+    print FOUT "U $username\n";
+    print FOUT "D $fdate\n";
+    print FOUT "M $username\n";
+    print FOUT "d $fdate\n";
+    print FOUT "Z\n\n";
+
+}
+
+
+sub writeItemsSection
+{
+    my $self = shift @_;
+
+    my $items = $self->{'items'};
+
+    foreach my $item (@$items) {
+	my $type = delete $item->{'_type'};
+	my $id   = delete $item->{'_id'};
+
+	print FOUT "S $type $id\n";
+	foreach my $a (keys %$item) {
+	    print FOUT "$a ", $item->{$a}, "\n";
+	}
+	
+	print FOUT "\n";
+    }
+
+    print FOUT "Z\n\n";
+}
+
+sub writeLinesSection
+{
+    my $self = shift @_;
+
+    my $lines = $self->{'lines'};
+
+    foreach my $line (@$lines) {
+
+	print FOUT "L ", $line->{'L'}, "\n";
+	print FOUT "s ", $line->{'s'}, "\n";
+	
+	print FOUT "\n";
+    }
+
+    print FOUT "Z\n\n";
+
+}
+
+sub writeConstraintsSection
+{
+    my $self = shift @_;
+
+    my $constraints = $self->{'constraints'};
+
+    foreach my $constraint (@$constraints) {
+	print FOUT "C ", $constraint->{'C'}, "\n";
+	print FOUT "s ", $constraint->{'s'}, "\n";
+	
+	print FOUT "\n";
+    }
+
+    print FOUT "Z\n\n";
+}
+
+sub writeStatisticsSection
+{
+    my $self = shift @_;
+
+    # Currently do nothing
+}
+
+sub saveFrame
+{
+    my $self = shift @_;
+    my ($filename) = @_;
+
+    my $status = undef;
+
+    if (open(FOUT,">$filename")) {
+	binmode(FOUT,":utf8");
+	$self->writeHeaderSection();
+	$self->writeItemsSection();
+	$self->writeLinesSection();
+	$self->writeConstraintsSection();
+	$self->writeStatisticsSection();
+
+	close(FOUT);
+	$status = 1;
+    }
+    else {
+	print STDERR "ExpediteeFrameIO:: Failed to open $filename for output\n";
+	$status = 0;
+    }
+
+    return $status;
+}
+
+sub buildFrame
+{
+    my $self = shift @_;
+    my ($html_node) = @_;
+ 
+    my $type = $html_node->{'type'};
+
+    if ($type eq "rect") {
+
+	my $rect = $html_node->{'rect'};
+	my $xl = $rect->{'xl'};
+	my $xr = $rect->{'xr'};
+	my $yt = $rect->{'yt'};
+	my $yb = $rect->{'yb'};
+
+	my $attr = {};
+
+	$self->addRect($xl,$yt,$xr,$yb,$attr);
+
+    }
+    elsif ($type eq "text") {
+	my $text = $html_node->{'text'};
+
+	my $x = $html_node->{'x'};
+	my $y = $html_node->{'y'};
+
+	my $attr = {};
+
+	$self->addText($x,$y,$text,$attr);
+    }
+    else {
+	print STDERR "ExpediteeFrameIO::buildFrame(): Warning, unrecognized type '$type'\n";
+    }
+
+    my $childNodes = $html_node->{'childNodes'};
+    foreach my $child_node (@$childNodes) {
+	$self->buildFrame($child_node);
+    }
+}
+
+
+sub saveNextFreeFrame
+{
+    my $self = shift @_;
+    my ($nextFreeFrame) = @_;
+
+    print STDERR "****  saveNextFreeFrame not implemented yet!!\n";
+}
+
+1;
Index: gs3-extensions/html-to-expeditee/trunk/src/perllib/cgiactions/HtmlToExpediteeAction.pm
===================================================================
--- gs3-extensions/html-to-expeditee/trunk/src/perllib/cgiactions/HtmlToExpediteeAction.pm	(revision 24933)
+++ gs3-extensions/html-to-expeditee/trunk/src/perllib/cgiactions/HtmlToExpediteeAction.pm	(revision 24934)
@@ -30,4 +30,5 @@
 use cgiactions::baseaction;
 
+use ExpediteeFrameIO;
 use JSON;
 
@@ -84,5 +85,4 @@
     my $exp_frame_tree = decode_json $json_str_utf8;
 	
-
     my $output_dir = &util::filename_cat($collect_dir,$collect,"export");
 
@@ -92,12 +92,24 @@
     my $frame_filename = &util::filename_cat($output_dir,"$fn.exp");
 
-    if (open(FOUT,">$frame_filename")) {
-	binmode(FOUT,":utf8");
+    my $expeditee_frame_io = new ExpediteeFrameIO();
+    
+    $expeditee_frame_io->buildFrame($exp_frame_tree);
 
-	print FOUT $json_str_utf8;
-	close(FOUT);
+#    if (open(FOUT,">$frame_filename")) {
+#	binmode(FOUT,":utf8");
+
+#	print FOUT $json_str_utf8;
+#	close(FOUT);
+#	$gsdl_cgi->generate_message("html-to-expeditee saved frame $fn");
+#    }
+#    else {
+#	$gsdl_cgi->generate_error("Failed to open $frame_filename for output");
+#    }
+
+    if ($expeditee_frame_io->saveFrame($frame_filename)) {
 	
 	# write out next free frame num
-	
+	$expeditee_frame_io->saveNextFreeFrame($fn+1);
+
 	$gsdl_cgi->generate_message("html-to-expeditee saved frame $fn");
     }
Index: gs3-extensions/html-to-expeditee/trunk/src/src/cgi-bin/html-to-expeditee.pl.in
===================================================================
--- gs3-extensions/html-to-expeditee/trunk/src/src/cgi-bin/html-to-expeditee.pl.in	(revision 24933)
+++ gs3-extensions/html-to-expeditee/trunk/src/src/cgi-bin/html-to-expeditee.pl.in	(revision 24934)
@@ -157,11 +157,13 @@
 	          url = "library.cgi";
 	          url += "?c="+collect +"&a=d&d=" + docOID;
-	     }
-	     else {
+	      }
+	      else {
 	          url = "dev";
 	          url += "?c="+collect +"&a=d&d=" + docOID;
 	          url += "&ed=1&dt=hierarchy";
-	     }
-	      
+	          // url += "&excerptid=gs_content";
+	      }
+	      
+	      /* load iframe with document drawn from Greenstone collection */
 	      iframe.src = url;
 	      
@@ -200,5 +202,14 @@
 		 });
 
-	      var expFrameTree = htmlToExpeditee(iframeDoc.body);
+	      var gsContent = iframeDoc.getElementById("gs_content");
+
+	      /*
+	      var gsContentPos = getElementPosition(gsContent); 
+	      var pxl = gsContentPos.xl;
+	      var pxr = gsContentPos.xr;
+	      */
+
+	      var expFrameTree = htmlToExpeditee(gsContent);
+
 	      var expFrame = JSON.stringify(expFrameTree);
 	      //alert(expFrame);
@@ -240,4 +251,5 @@
 	              url += "?c="+collect +"&a=d&d=" + docOID;
 	              url += "&ed=1&dt=hierarchy";
+	              // url += "&excerptid=gs_content";
 	     	  }
 
@@ -277,8 +289,6 @@
 {
 
-#    $ENV{'QUERY_STRING'} = "a=set-import-metadata&c=espresso-music&d=HASH012d6f72cde5dc48162f4a1d.1&metaname=annotation&metapos=0&metavalue=adfadfad";
-#    $ENV{'QUERYSTRING'} = "a=set-import-metadata&c=espresso-music&d=HASH012d6f72cde5dc48162f4a1d.1&metaname=annotation&metapos=0&metavalue=adfadfad";
+#    $ENV{'QUERY_STRING'} = "a=...";set-import-metadata&c=espresso-music&d=HASH012d6f72cde5dc48162f4a1d.1&metaname=annotation&metapos=0&metavalue=adfadfad";
 #    $ENV{'REQUEST_METHOD'} = "GET";
-
 
     my $gsdl_cgi = new gsdlCGI();
Index: gs3-extensions/html-to-expeditee/trunk/src/src/js/html-to-expeditee.js
===================================================================
--- gs3-extensions/html-to-expeditee/trunk/src/src/js/html-to-expeditee.js	(revision 24933)
+++ gs3-extensions/html-to-expeditee/trunk/src/src/js/html-to-expeditee.js	(revision 24934)
@@ -31,5 +31,5 @@
 	return elem.clip.width;
     } else {
-	if (elem.style.pixelWidth) {
+	if (elem.style && elem.style.pixelWidth) {
 	    return elem.style.pixelWidth;
 	} else {
@@ -44,5 +44,5 @@
 	return elem.clip.height;
     } else {
-	if (elem.style.pixelHeight) {
+	if (elem.style && elem.style.pixelHeight) {
 	    return elem.style.pixelHeight;
 	} else {
@@ -84,11 +84,22 @@
 
 
+function htmlToExpeditee(node) 
+{
+    var nodePos = getElementPosition(node); 
+    var pxl = nodePos.xl;
+    var pyt = nodePos.yt;
+    var pxr = nodePos.xr;
+    var pyb = nodePos.yb;
 
-function htmlToExpeditee(node,depth) 
+    return htmlToExpediteeRec(node, pxl,pyt, pxl,pyt,pxr,pyb, 0);
+}
+
+
+function htmlToExpediteeRec(node,baseX,baseY, pxl,pyt,pxr,pyb, depth) 
 {
     depth = (depth) ? depth : 0;
 
     //var expSyntax = "";
-    var jsonNode = {};
+    var jsonNode = null;
 
     if (node.nodeType == 3) { /* text node */
@@ -99,6 +110,12 @@
 	    //expSyntax += "Text item: " + node.nodeValue + "\n";
 
+    	    jsonNode = {};
+
 	    jsonNode.type = "text";
 	    jsonNode.text = node.nodeValue;
+
+	    jsonNode.x = pxl;
+	    jsonNode.y = pyt;
+
 	}
     }
@@ -108,8 +125,8 @@
         /* need to dig out: text size, l/r/justified, font-face, type, colour */
 
-        var elemName = node.Name;
-	if (elemName != "SCRIPT") {
+        var elemName = node.nodeName.toLowerCase();
 
-	    
+	if (!elemName.match(/script/)) {
+   
 	    var nodePos = getElementPosition(node); 
 	    var xl = nodePos.xl;
@@ -129,8 +146,9 @@
 */
 
-	    var rect = { "xl":xl, "yt":yt, "xr":xr, "yb":yb };
-
+            jsonNode = {};
 	    jsonNode.type = "rect";
     	    jsonNode.elem = node.nodeName;
+
+	    var rect = { "xl":xl, "yt":yt, "xr":xr, "yb":yb };
 	    jsonNode.rect = rect;
 	
@@ -139,8 +157,11 @@
 	    var cnode = node.firstChild; 
 	    while (cnode != null) {
-		//expSyntax += htmlToExpeditee(cnode,depth+1);  
 
-	        var jsonChildNode = htmlToExpeditee(cnode,depth+1);
-		jsonChildNodes.push(jsonChildNode);  
+	        var jsonChildNode 
+		    = htmlToExpediteeRec(cnode,baseX,baseY,xl,yt,xr,yb,depth+1);
+
+	        if (jsonChildNode != null) {
+		    jsonChildNodes.push(jsonChildNode);  
+	        }
 		
 		cnode = cnode.nextSibling;
