DHTML progress bar for glacial rate adding and editing, closes: Bug#1100
authorivan <ivan>
Thu, 27 Jan 2005 10:21:18 +0000 (10:21 +0000)
committerivan <ivan>
Thu, 27 Jan 2005 10:21:18 +0000 (10:21 +0000)
28 files changed:
CREDITS
FS/FS/UID.pm
FS/FS/queue.pm
FS/FS/rate.pm
FS/bin/freeside-queued
JSRS-LICENSE [new file with mode: 0644]
httemplate/docs/install.html
httemplate/edit/process/rate.cgi [new file with mode: 0755]
httemplate/edit/rate.cgi
httemplate/elements/jsrsClient.js [new file with mode: 0644]
httemplate/elements/qlib/box.js [new file with mode: 0644]
httemplate/elements/qlib/boxctrl.js [new file with mode: 0644]
httemplate/elements/qlib/boxres.js [new file with mode: 0644]
httemplate/elements/qlib/button.js [new file with mode: 0644]
httemplate/elements/qlib/buttonres.js [new file with mode: 0644]
httemplate/elements/qlib/control.js [new file with mode: 0644]
httemplate/elements/qlib/counter.js [new file with mode: 0644]
httemplate/elements/qlib/imagelist.js [new file with mode: 0644]
httemplate/elements/qlib/label.js [new file with mode: 0644]
httemplate/elements/qlib/messagebox.js [new file with mode: 0644]
httemplate/elements/qlib/progress.js [new file with mode: 0644]
httemplate/elements/qlib/sound.js [new file with mode: 0644]
httemplate/elements/qlib/sprite.js [new file with mode: 0644]
httemplate/elements/qlib/window.js [new file with mode: 0644]
httemplate/elements/qlib/wndctrl.js [new file with mode: 0644]
httemplate/images/progressbar-empty.png [new file with mode: 0644]
httemplate/images/progressbar-full.png [new file with mode: 0644]
httemplate/misc/progress.html [new file with mode: 0644]

diff --git a/CREDITS b/CREDITS
index 59edea6..02dd9a8 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -113,7 +113,7 @@ Infostreet export.
 Richard Siddall <richard.siddall@elirion.net> sent in Mason fixes and other
 things I'm probably forgetting.
 
 Richard Siddall <richard.siddall@elirion.net> sent in Mason fixes and other
 things I'm probably forgetting.
 
-Contains "JS Calendar" v0.9.3 <http://dynarch.com/mishoo/calendar.epl>
+Contains "JS Calendar" <http://dynarch.com/mishoo/calendar.epl>
 by Mihai Bazon <mishoo@infoiasi.ro> licensed under the terms of the GNU LGPL.
 
 Latex invoice template based on a template from eBills
 by Mihai Bazon <mishoo@infoiasi.ro> licensed under the terms of the GNU LGPL.
 
 Latex invoice template based on a template from eBills
@@ -137,5 +137,13 @@ released with 1.4.2beta1 and 1.5.0pre6.
 Troy Hammonds <troyh@netsignia.net> sent in RADIUS session history viewing
 code and other things I'm probably forgetting.
 
 Troy Hammonds <troyh@netsignia.net> sent in RADIUS session history viewing
 code and other things I'm probably forgetting.
 
+Contains the "Javascript Remote Scripting (JSRS)" client library
+<http://www.ashleyit.com/rs/main.htm> by Brent Ashley <brent@ashleyit.com>
+licensed under the "No Nonsense Copyright and License" (see the included
+JSRS-LICENSE file).
+
+Contains the QLIB JavaScript library <http://qlib.quazzle.com/> by 
+Quazzle.com, Serge Dolgov, licensed under the terms of the GNU GPL.
+
 Everything else is my (Ivan Kohler <ivan@420.am>) fault.
 
 Everything else is my (Ivan Kohler <ivan@420.am>) fault.
 
index 3d893ee..c0c9f7a 100644 (file)
@@ -98,7 +98,7 @@ sub forksuidsetup {
 }
 
 sub myconnect {
 }
 
 sub myconnect {
-  $dbh = DBI->connect( getsecrets, {'AutoCommit' => 0, 'ChopBlanks' => 1, } )
+  DBI->connect( getsecrets, {'AutoCommit' => 0, 'ChopBlanks' => 1, } )
     or die "DBI->connect error: $DBI::errstr\n";
 }
 
     or die "DBI->connect error: $DBI::errstr\n";
 }
 
index dcac3c6..f42d998 100644 (file)
@@ -3,7 +3,7 @@ package FS::queue;
 use strict;
 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
 use Exporter;
 use strict;
 use vars qw( @ISA @EXPORT_OK $DEBUG $conf $jobnums);
 use Exporter;
-use FS::UID;
+use FS::UID qw(myconnect);
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh );
 #use FS::queue;
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh );
 #use FS::queue;
@@ -15,7 +15,6 @@ use FS::cust_svc;
 @EXPORT_OK = qw( joblisting );
 
 $DEBUG = 0;
 @EXPORT_OK = qw( joblisting );
 
 $DEBUG = 0;
-#$DEBUG = 1;
 
 $FS::UID::callback{'FS::queue'} = sub {
   $conf = new FS::Conf;
 
 $FS::UID::callback{'FS::queue'} = sub {
   $conf = new FS::Conf;
@@ -307,6 +306,38 @@ sub depended_delete {
   }
 }
 
   }
 }
 
+=item update_statustext VALUE
+
+Updates the statustext value of this job to supplied value, in the database.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+use vars qw($_update_statustext_dbh);
+sub update_statustext {
+  my( $self, $statustext ) = @_;
+  return '' if $statustext eq $self->statustext;
+  warn "updating statustext for $self to $statustext" if $DEBUG;
+
+  $_update_statustext_dbh ||= myconnect;
+
+  my $sth = $_update_statustext_dbh->prepare(
+    'UPDATE queue set statustext = ? WHERE jobnum = ?'
+  ) or return $_update_statustext_dbh->errstr;
+
+  $sth->execute($statustext, $self->jobnum) or return $sth->errstr;
+  $_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
+  $self->statustext($statustext);
+  '';
+
+  #my $new = new FS::queue { $self->hash };
+  #$new->statustext($statustext);
+  #my $error = $new->replace($self);
+  #return $error if $error;
+  #$self->statustext($statustext);
+  #'';
+}
+
 =back
 
 =head1 SUBROUTINES
 =back
 
 =head1 SUBROUTINES
index b8a6940..7f625a6 100644 (file)
@@ -1,12 +1,14 @@
 package FS::rate;
 
 use strict;
 package FS::rate;
 
 use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs dbh );
+use vars qw( @ISA $DEBUG );
+use FS::Record qw( qsearch qsearchs dbh fields );
 use FS::rate_detail;
 
 @ISA = qw(FS::Record);
 
 use FS::rate_detail;
 
 @ISA = qw(FS::Record);
 
+$DEBUG = 1;
+
 =head1 NAME
 
 FS::rate - Object methods for rate records
 =head1 NAME
 
 FS::rate - Object methods for rate records
@@ -94,13 +96,32 @@ sub insert {
   }
 
   if ( $options{'rate_detail'} ) {
   }
 
   if ( $options{'rate_detail'} ) {
+
+    my( $num, $last, $min_sec ) = (0, time, 5); #progressbar foo
+
     foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
     foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
+
       $rate_detail->ratenum($self->ratenum);
       $error = $rate_detail->insert;
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;
         return $error;
       }
       $rate_detail->ratenum($self->ratenum);
       $error = $rate_detail->insert;
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;
         return $error;
       }
+
+      if ( $options{'job'} ) {
+        $num++;
+        if ( time - $min_sec > $last ) {
+          my $error = $options{'job'}->update_statustext(
+            int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
+          );
+          if ( $error ) {
+            $dbh->rollback if $oldAutoCommit;
+            return $error;
+          }
+          $last = time;
+        }
+      }
+
     }
   }
 
     }
   }
 
@@ -148,8 +169,8 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my @old_rate_detail = ();
-  @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
+#  my @old_rate_detail = ();
+#  @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
 
   my $error = $new->SUPER::replace($old);
   if ($error) {
 
   my $error = $new->SUPER::replace($old);
   if ($error) {
@@ -157,21 +178,67 @@ sub replace {
     return $error;
   }
 
     return $error;
   }
 
-  foreach my $old_rate_detail ( @old_rate_detail ) {
-    my $error = $old_rate_detail->delete;
-    if ($error) {
+#  foreach my $old_rate_detail ( @old_rate_detail ) {
+#
+#    my $error = $old_rate_detail->delete;
+#    if ($error) {
+#      $dbh->rollback if $oldAutoCommit;
+#      return $error;
+#    }
+#
+#    if ( $options{'job'} ) {
+#      $num++;
+#      if ( time - $min_sec > $last ) {
+#        my $error = $options{'job'}->update_statustext(
+#          int( 50 * $num / scalar( @old_rate_detail ) )
+#        );
+#        if ( $error ) {
+#          $dbh->rollback if $oldAutoCommit;
+#          return $error;
+#        }
+#        $last = time;
+#      }
+#    }
+#
+#  }
+  if ( $options{'rate_detail'} ) {
+    my $sth = $dbh->prepare('DELETE FROM rate_detail WHERE ratenum = ?') or do {
       $dbh->rollback if $oldAutoCommit;
       $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-  }
-
-  foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
-    $rate_detail->ratenum($new->ratenum);
-    $error = $rate_detail->insert;
-    if ( $error ) {
+      return $dbh->errstr;
+    };
+  
+    $sth->execute($old->ratenum) or do {
       $dbh->rollback if $oldAutoCommit;
       $dbh->rollback if $oldAutoCommit;
-      return $error;
+      return $sth->errstr;
+    };
+
+    my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
+#  $num = 0;
+    foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
+  
+      $rate_detail->ratenum($new->ratenum);
+      $error = $rate_detail->insert;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+  
+      if ( $options{'job'} ) {
+        $num++;
+        if ( time - $min_sec > $last ) {
+          my $error = $options{'job'}->update_statustext(
+            int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
+          );
+          if ( $error ) {
+            $dbh->rollback if $oldAutoCommit;
+            return $error;
+          }
+          $last = time;
+        }
+      }
+  
     }
     }
+
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -230,6 +297,120 @@ sub rate_detail {
 
 =back
 
 
 =back
 
+=head1 SUBROUTINES
+
+=over 4
+
+=item process
+
+Experimental job-queue processor for web interface adds/edits
+
+=cut
+
+sub process {
+  my $job = shift;
+
+  #my %param = @_;
+
+  my $param = shift;
+  my %param = split(/[;=]/, $param);
+
+  my $old = qsearchs('rate', { 'ratenum' => $param{'ratenum'} } )
+    if $param{'ratenum'};
+
+  my @rate_detail = map {
+
+    my $regionnum = $_->regionnum;
+    if ( $param{"sec_granularity$regionnum"} ) {
+
+      new FS::rate_detail {
+        'dest_regionnum'  => $regionnum,
+        map { $_ => $param{"$_$regionnum"} }
+            qw( min_included min_charge sec_granularity )
+      };
+
+    } else {
+
+      new FS::rate_detail {
+        'dest_regionnum'  => $regionnum,
+        'min_included'    => 0,
+        'min_charge'      => 0,
+        'sec_granularity' => '60'
+      };
+
+    }
+    
+  } qsearch('rate_region', {} );
+  
+  my $rate = new FS::rate {
+    map { $_ => $param{$_} }
+        fields('rate')
+  };
+
+  my $error = '';
+  if ( $param{'ratenum'} ) {
+    warn "$rate replacing $old ($param{'ratenum'})\n" if $DEBUG;
+    $error = $rate->replace( $old,
+                             'rate_detail' => \@rate_detail,
+                             'job'         => $job,
+                           );
+  } else {
+    warn "inserting $rate\n" if $DEBUG;
+    $error = $rate->insert( 'rate_detail' => \@rate_detail,
+                            'job'         => $job,
+                          );
+    #$ratenum = $rate->getfield('ratenum');
+  }
+
+  die $error if $error;
+
+}
+
+# begin JSRPC code...
+
+package FS::rate::JSRPC;
+use vars qw(@ISA $DEBUG);
+use JavaScript::RPC::Server::CGI;
+use FS::UID;
+@ISA = qw( JavaScript::RPC::Server::CGI );
+$DEBUG = 1;
+
+sub process_rate {
+  my $self = shift;
+
+  my %param = @_;
+  warn "FS::rate::JSRPC::process_rate\n".
+       join('', map "  $_ => $param{$_}\n", keys %param )
+    if $DEBUG;
+
+  #progressbar prototype code...  should be generalized
+  
+  #first get the CGI params shipped off to a job ASAP so an id can be returned
+  #to the caller
+  
+  my $job = new FS::queue { 'job' => 'FS::rate::process' };
+  
+  #too slow to insert all the cgi params as individual args..,?
+  #my $error = $queue->insert('_JOB', $cgi->Vars);
+  
+  #my $bigstring = join(';', map { "$_=". scalar($cgi->param($_)) } $cgi->param );
+  my $bigstring = join(';', map { "$_=". $param{$_} } keys %param );
+  my $error = $job->insert('_JOB', $bigstring);
+
+  if ( $error ) {
+    $error;
+  } else {
+    $job->jobnum;
+  }
+  
+}
+
+sub get_new_query {
+  FS::UID::cgi();
+}
+
+# end JSRPC code...
+
 =head1 BUGS
 
 =head1 SEE ALSO
 =head1 BUGS
 
 =head1 SEE ALSO
index e14ddad..a3fb7e5 100644 (file)
@@ -87,7 +87,7 @@ while (1) {
   unless ( dbh && dbh->ping ) {
     warn "WARNING: connection to database lost, reconnecting...\n";
 
   unless ( dbh && dbh->ping ) {
     warn "WARNING: connection to database lost, reconnecting...\n";
 
-    eval { myconnect; };
+    eval { $FS::UID::dbh = myconnect; };
 
     unless ( !$@ && dbh && dbh->ping ) {
       warn "WARNING: still no connection to database, sleeping for retry...\n";
 
     unless ( !$@ && dbh && dbh->ping ) {
       warn "WARNING: still no connection to database, sleeping for retry...\n";
@@ -162,6 +162,7 @@ while (1) {
   #} 
 
   my @args = $ljob->args;
   #} 
 
   my @args = $ljob->args;
+  splice @args, 0, 1, $ljob if $args[0] eq '_JOB';
 
   defined( my $pid = fork ) or do {
     warn "WARNING: can't fork: $!\n";
 
   defined( my $pid = fork ) or do {
     warn "WARNING: can't fork: $!\n";
@@ -184,8 +185,12 @@ while (1) {
 
     forksuidsetup($user);
 
 
     forksuidsetup($user);
 
-    #auto-use export classes...
-    if ( $ljob->job =~ /(FS::part_export::\w+)::/ ) {
+    #auto-use classes...
+    #if ( $ljob->job =~ /(FS::part_export::\w+)::/ ) {
+    if (    $ljob->job =~ /(FS::part_export::\w+)::/
+         || $ljob->job =~ /(FS::\w+)::/
+       )
+    {
       my $class = $1;
       eval "use $class;";
       if ( $@ ) {
       my $class = $1;
       eval "use $class;";
       if ( $@ ) {
diff --git a/JSRS-LICENSE b/JSRS-LICENSE
new file mode 100644 (file)
index 0000000..fd14984
--- /dev/null
@@ -0,0 +1,29 @@
+\r
+No Nonsense Copyright and License for JSRS JavaScript Remote Scripting\r
+======================================================================\r
+\r
+Copyright:\r
+\r
+This JSRS stuff was written by me.  I find it useful.  Others find it useful. \r
+You are welcome to use it, modify it to suit your needs, distribute it as you \r
+see fit.  I'm happy if you use it for personal stuff or for commercial gain.\r
+\r
+The only thing you can't do is to restrict anyone else from using it however \r
+they see fit.  You may not copyright it yourself or change the rules I have \r
+set on how it can be used.\r
+\r
+JSRS Javascript Remote Scripting Copyright (C) 2001 by Brent Ashley\r
+\r
+License:\r
+\r
+You can use this however you like.  I make no guarantees whatsoever that it \r
+will suit your purpose.  You take full responsibility for getting it working \r
+properly and for any implications of its failure or inability to satisfy your \r
+every need.\r
+\r
+======================================================================\r
+\r
+email inquiries: jsrs@megahuge.com\r
+\r
+\r
+\r
index 86e2a6b..898eb9e 100644 (file)
@@ -58,6 +58,7 @@ Before installing, you need:
       <li><a href="http://search.cpan.org/search?dist=NetAddr-IP">NetAddr-IP</a>
       <li><a href="http://search.cpan.org/search?dist=Chart">Chart</a>
       <li><a href="http://search.cpan.org/search?dist=Crypt-PasswdMD5">Crypt::PasswdMD5</a>
       <li><a href="http://search.cpan.org/search?dist=NetAddr-IP">NetAddr-IP</a>
       <li><a href="http://search.cpan.org/search?dist=Chart">Chart</a>
       <li><a href="http://search.cpan.org/search?dist=Crypt-PasswdMD5">Crypt::PasswdMD5</a>
+      <li><a href="http://search.cpan.org/search?dist=JavaScript-RPC">JavaScript::RPC (JavaScript::RPC::Server::CGI)</a>
       <li><a href="http://search.cpan.org/search?dist=ApacheDBI">Apache::DBI</a> <i>(optional but recommended for better webinterface performance)</i>
     </ul>
 </ul>
       <li><a href="http://search.cpan.org/search?dist=ApacheDBI">Apache::DBI</a> <i>(optional but recommended for better webinterface performance)</i>
     </ul>
 </ul>
diff --git a/httemplate/edit/process/rate.cgi b/httemplate/edit/process/rate.cgi
new file mode 100755 (executable)
index 0000000..df7c467
--- /dev/null
@@ -0,0 +1,4 @@
+<%
+my $server = new FS::rate::JSRPC;
+$server->process;
+%>
index 4a4b70e..3443f81 100644 (file)
@@ -1,52 +1,7 @@
 <%
 
 <%
 
-my($rate, $error);
-
-if ( $cgi->param('magic') eq 'process' ) {
-
-  my $ratenum = $cgi->param('ratenum');
-  
-  my $old = qsearchs('rate', { 'ratenum' => $ratenum } ) if $ratenum;
-  
-  my @rate_detail = map {
-    my $regionnum = $_->regionnum;
-    if ( $cgi->param("sec_granularity$regionnum") ) {
-      new FS::rate_detail {
-        'dest_regionnum'  => $regionnum,
-        map { $_ => scalar($cgi->param("$_$regionnum")) }
-            qw( min_included min_charge sec_granularity )
-      };
-    } else {
-      new FS::rate_detail {
-        'dest_regionnum'  => $regionnum,
-        'min_included'    => 0,
-        'min_charge'      => 0,
-        'sec_granularity' => '60'
-      };
-    }
-  } qsearch('rate_region', {} );
-  
-  $rate = new FS::rate ( {
-    map {
-      $_, scalar($cgi->param($_));
-    } fields('rate')
-  } );
-  
-  if ( $ratenum ) {
-    warn "$rate replacing $old ($ratenum)\n";
-    $error = $rate->replace($old, 'rate_detail' => \@rate_detail );
-  } else {
-    warn "inserting $rate\n";
-    $error = $rate->insert( 'rate_detail' => \@rate_detail );
-    $ratenum = $rate->getfield('ratenum');
-  }
-
-  unless ( $error ) {
-    print $cgi->redirect("${p}browse/rate.cgi");
-    myexit;
-  }
-  
-} elsif ( $cgi->keywords ) {
+my $rate;
+if ( $cgi->keywords ) {
   my($query) = $cgi->keywords;
   $query =~ /^(\d+)$/;
   $rate = qsearchs( 'rate', { 'ratenum' => $1 } );
   my($query) = $cgi->keywords;
   $query =~ /^(\d+)$/;
   $rate = qsearchs( 'rate', { 'ratenum' => $1 } );
@@ -77,12 +32,41 @@ END
     ))
 %>
 
     ))
 %>
 
-<% if ( $error ) { %>
-<FONT SIZE="+1" COLOR="#ff0000">Error: <%= $error %></FONT><BR>
-<% } %>
+<!-- <FORM ACTION="<%=$p1%>rate.cgi" NAME="OneTrueForm" METHOD=POST onSubmit="document.OneTrueForm.submit.disabled=true"> -->
+
+<SCRIPT TYPE="text/javascript" SRC="../elements/jsrsClient.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript">
+function process () {
+  document.OneTrueForm.submit.disabled=true;
+
+  var Hash = new Array();
+  var x = 0;
+  var fieldName;
+  for (var i = 0; i<document.OneTrueForm.elements.length; i++) {
+    fieldName = document.OneTrueForm.elements[i].name;
+    if (    (fieldName.indexOf('rate') > -1)
+         || (fieldName.indexOf('min_') > -1) 
+         || (fieldName.indexOf('sec_') > -1) 
+       )
+    {
+        Hash[x++] = fieldName;
+        Hash[x++] = document.OneTrueForm.elements[i].value;
+    }
+  }
+
+  jsrsPOST = true;
+  jsrsExecute( 'process/rate.cgi', myCallback, 'process_rate', Hash );
+
+  function myCallback( jobnum ) {
+    var progressWindow = window.open('../../misc/progress.html?jobnum=' + jobnum + ';url=<%=$p%>browse/rate.cgi', 'progressWindow', 'toolbar=no,location=no,directories=no,scrollbars=no,menubar=no,status=no,width=420,height=128');
+    progressWindow.opener = self;
+    //progressWindow.opener = document;
+  }
+
+}
+</SCRIPT>
 
 
-<FORM ACTION="<%=$p1%>rate.cgi" NAME="OneTrueForm" METHOD=POST onSubmit="document.OneTrueForm.submit.disabled=true">
-<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<FORM NAME="OneTrueForm">
 <INPUT TYPE="hidden" NAME="ratenum" VALUE="<%= $rate->ratenum %>">
 
 Rate plan
 <INPUT TYPE="hidden" NAME="ratenum" VALUE="<%= $rate->ratenum %>">
 
 Rate plan
@@ -135,11 +119,12 @@ Rate plan
 
 </TABLE>
 
 
 </TABLE>
 
-<BR><INPUT NAME="submit" TYPE="submit" VALUE="<%= 
+<BR><INPUT NAME="submit" TYPE="button" VALUE="<%= 
   $rate->ratenum ? "Apply changes" : "Add rate plan"
   $rate->ratenum ? "Apply changes" : "Add rate plan"
-%>">
-Please be patient, <%= $rate->ratenum ? 'editing' : 'adding' %>
-a rate plan can take a few minutes...
+%>" onClick="document.OneTrueForm.submit.disabled=true; process();">
+Please make sure to allow popups from this site in order to view the progress window.
+<!-- Please be patient, <%= $rate->ratenum ? 'editing' : 'adding' %>
+a rate plan can take a few minutes... -->
 
     </FORM>
   </BODY>
 
     </FORM>
   </BODY>
diff --git a/httemplate/elements/jsrsClient.js b/httemplate/elements/jsrsClient.js
new file mode 100644 (file)
index 0000000..3a2572c
--- /dev/null
@@ -0,0 +1,356 @@
+//
+//  jsrsClient.js - javascript remote scripting client include
+//  
+//  Author:  Brent Ashley [jsrs@megahuge.com]
+//
+//  make asynchronous remote calls to server without client page refresh
+//
+//  see license.txt for copyright and license information
+
+/*
+see history.txt for full history
+2.0  26 Jul 2001 - added POST capability for IE/MOZ
+2.2  10 Aug 2003 - added Opera support
+2.3(beta)  10 Oct 2003 - added Konqueror support - **needs more testing**
+*/
+
+// callback pool needs global scope
+var jsrsContextPoolSize = 0;
+var jsrsContextMaxPool = 10;
+var jsrsContextPool = new Array();
+var jsrsBrowser = jsrsBrowserSniff();
+var jsrsPOST = true;
+var containerName;
+
+// constructor for context object
+function jsrsContextObj( contextID ){
+  
+  // properties
+  this.id = contextID;
+  this.busy = true;
+  this.callback = null;
+  this.container = contextCreateContainer( contextID );
+  
+  // methods
+  this.GET = contextGET;
+  this.POST = contextPOST;
+  this.getPayload = contextGetPayload;
+  this.setVisibility = contextSetVisibility;
+}
+
+//  method functions are not privately scoped 
+//  because Netscape's debugger chokes on private functions
+function contextCreateContainer( containerName ){
+  // creates hidden container to receive server data 
+  var container;
+  switch( jsrsBrowser ) {
+    case 'NS':
+      container = new Layer(100);
+      container.name = containerName;
+      container.visibility = 'hidden';
+      container.clip.width = 100;
+      container.clip.height = 100;
+      break;
+    
+    case 'IE':
+      document.body.insertAdjacentHTML( "afterBegin", '<span id="SPAN' + containerName + '"></span>' );
+      var span = document.all( "SPAN" + containerName );
+      var html = '<iframe name="' + containerName + '" src=""></iframe>';
+      span.innerHTML = html;
+      span.style.display = 'none';
+      container = window.frames[ containerName ];
+      break;
+      
+    case 'MOZ':  
+      var span = document.createElement('SPAN');
+      span.id = "SPAN" + containerName;
+      document.body.appendChild( span );
+      var iframe = document.createElement('IFRAME');
+      iframe.name = containerName;
+      iframe.id = containerName;
+      span.appendChild( iframe );
+      container = iframe;
+      break;
+
+    case 'OPR':  
+      var span = document.createElement('SPAN');
+      span.id = "SPAN" + containerName;
+      document.body.appendChild( span );
+      var iframe = document.createElement('IFRAME');
+      iframe.name = containerName;
+      iframe.id = containerName;
+      span.appendChild( iframe );
+      container = iframe;
+      break;
+
+    case 'KONQ':  
+      var span = document.createElement('SPAN');
+      span.id = "SPAN" + containerName;
+      document.body.appendChild( span );
+      var iframe = document.createElement('IFRAME');
+      iframe.name = containerName;
+      iframe.id = containerName;
+      span.appendChild( iframe );
+      container = iframe;
+
+      // Needs to be hidden for Konqueror, otherwise it'll appear on the page
+      span.style.display = none;
+      iframe.style.display = none;
+      iframe.style.visibility = hidden;
+      iframe.height = 0;
+      iframe.width = 0;
+
+      break;
+  }
+  return container;
+}
+
+function contextPOST( rsPage, func, parms ){
+
+  var d = new Date();
+  var unique = d.getTime() + '' + Math.floor(1000 * Math.random());
+  var doc = (jsrsBrowser == "IE" ) ? this.container.document : this.container.contentDocument;
+  doc.open();
+  doc.write('<html><body>');
+  doc.write('<form name="jsrsForm" method="post" target="" ');
+  doc.write(' action="' + rsPage + '?U=' + unique + '">');
+  doc.write('<input type="hidden" name="C" value="' + this.id + '">');
+
+  // func and parms are optional
+  if (func != null){
+  doc.write('<input type="hidden" name="F" value="' + func + '">');
+
+    if (parms != null){
+      if (typeof(parms) == "string"){
+        // single parameter
+        doc.write( '<input type="hidden" name="P0" '
+                 + 'value="[' + jsrsEscapeQQ(parms) + ']">');
+      } else {
+        // assume parms is array of strings
+        for( var i=0; i < parms.length; i++ ){
+          doc.write( '<input type="hidden" name="P' + i + '" '
+                   + 'value="[' + jsrsEscapeQQ(parms[i]) + ']">');
+        }
+      } // parm type
+    } // parms
+  } // func
+
+  doc.write('</form></body></html>');
+  doc.close();
+  doc.forms['jsrsForm'].submit();
+}
+
+function contextGET( rsPage, func, parms ){
+
+  // build URL to call
+  var URL = rsPage;
+
+  // always send context
+  URL += "?C=" + this.id;
+
+  // func and parms are optional
+  if (func != null){
+    URL += "&F=" + escape(func);
+
+    if (parms != null){
+      if (typeof(parms) == "string"){
+        // single parameter
+        URL += "&P0=[" + escape(parms+'') + "]";
+      } else {
+        // assume parms is array of strings
+        for( var i=0; i < parms.length; i++ ){
+          URL += "&P" + i + "=[" + escape(parms[i]+'') + "]";
+        }
+      } // parm type
+    } // parms
+  } // func
+
+  // unique string to defeat cache
+  var d = new Date();
+  URL += "&U=" + d.getTime();
+  // make the call
+  switch( jsrsBrowser ) {
+    case 'NS':
+      this.container.src = URL;
+      break;
+    case 'IE':
+      this.container.document.location.replace(URL);
+      break;
+    case 'MOZ':
+      this.container.src = '';
+      this.container.src = URL; 
+      break;
+    case 'OPR':
+      this.container.src = '';
+      this.container.src = URL; 
+      break;
+    case 'KONQ':
+      this.container.src = '';
+      this.container.src = URL; 
+      break;
+  }  
+}
+
+function contextGetPayload(){
+  switch( jsrsBrowser ) {
+    case 'NS':
+      return this.container.document.forms['jsrs_Form'].elements['jsrs_Payload'].value;
+    case 'IE':
+      return this.container.document.forms['jsrs_Form']['jsrs_Payload'].value;
+    case 'MOZ':
+      return window.frames[this.container.name].document.forms['jsrs_Form']['jsrs_Payload'].value; 
+    case 'OPR':
+      var textElement = window.frames[this.container.name].document.getElementById("jsrs_Payload");
+    case 'KONQ':
+      var textElement = window.frames[this.container.name].document.getElementById("jsrs_Payload");
+      return textElement.value;
+  }  
+}
+
+function contextSetVisibility( vis ){
+  switch( jsrsBrowser ) {
+    case 'NS':
+      this.container.visibility = (vis)? 'show' : 'hidden';
+      break;
+    case 'IE':
+      document.all("SPAN" + this.id ).style.display = (vis)? '' : 'none';
+      break;
+    case 'MOZ':
+      document.getElementById("SPAN" + this.id).style.visibility = (vis)? '' : 'hidden';
+    case 'OPR':
+      document.getElementById("SPAN" + this.id).style.visibility = (vis)? '' : 'hidden';
+      this.container.width = (vis)? 250 : 0;
+      this.container.height = (vis)? 100 : 0;
+      break;
+  }  
+}
+
+// end of context constructor
+
+function jsrsGetContextID(){
+  var contextObj;
+  for (var i = 1; i <= jsrsContextPoolSize; i++){
+    contextObj = jsrsContextPool[ 'jsrs' + i ];
+    if ( !contextObj.busy ){
+      contextObj.busy = true;      
+      return contextObj.id;
+    }
+  }
+  // if we got here, there are no existing free contexts
+  if ( jsrsContextPoolSize <= jsrsContextMaxPool ){
+    // create new context
+    var contextID = "jsrs" + (jsrsContextPoolSize + 1);
+    jsrsContextPool[ contextID ] = new jsrsContextObj( contextID );
+    jsrsContextPoolSize++;
+    return contextID;
+  } else {
+    alert( "jsrs Error:  context pool full" );
+    return null;
+  }
+}
+
+function jsrsExecute( rspage, callback, func, parms, visibility ){
+  // call a server routine from client code
+  //
+  // rspage      - href to asp file
+  // callback    - function to call on return 
+  //               or null if no return needed
+  //               (passes returned string to callback)
+  // func        - sub or function name  to call
+  // parm        - string parameter to function
+  //               or array of string parameters if more than one
+  // visibility  - optional boolean to make container visible for debugging
+
+  // get context
+  var contextObj = jsrsContextPool[ jsrsGetContextID() ];
+  contextObj.callback = callback;
+
+  var vis = (visibility == null)? false : visibility;
+  contextObj.setVisibility( vis );
+
+  if ( jsrsPOST && ((jsrsBrowser == 'IE') || (jsrsBrowser == 'MOZ'))){
+    contextObj.POST( rspage, func, parms );
+  } else {
+    contextObj.GET( rspage, func, parms );
+  }  
+  
+  return contextObj.id;
+}
+
+function jsrsLoaded( contextID ){
+  // get context object and invoke callback
+  var contextObj = jsrsContextPool[ contextID ];
+  if( contextObj.callback != null){
+    contextObj.callback( jsrsUnescape( contextObj.getPayload() ), contextID );
+  }
+  // clean up and return context to pool
+  contextObj.callback = null;
+  contextObj.busy = false;
+}
+
+function jsrsError( contextID, str ){
+  alert( unescape(str) );
+  jsrsContextPool[ contextID ].busy = false
+}
+
+function jsrsEscapeQQ( thing ){
+  return thing.replace(/'"'/g, '\\"');
+}
+
+function jsrsUnescape( str ){
+  // payload has slashes escaped with whacks
+  return str.replace( /\\\//g, "/" );
+}
+
+function jsrsBrowserSniff(){
+  if (document.layers) return "NS";
+  if (document.all) {
+               // But is it really IE?
+               // convert all characters to lowercase to simplify testing
+               var agt=navigator.userAgent.toLowerCase();
+               var is_opera = (agt.indexOf("opera") != -1);
+               var is_konq = (agt.indexOf("konqueror") != -1);
+               if(is_opera) {
+                       return "OPR";
+               } else {
+                       if(is_konq) {
+                               return "KONQ";
+                       } else {
+                               // Really is IE
+                               return "IE";
+                       }
+               }
+  }
+  if (document.getElementById) return "MOZ";
+  return "OTHER";
+}
+
+/////////////////////////////////////////////////
+//
+// user functions
+
+function jsrsArrayFromString( s, delim ){
+  // rebuild an array returned from server as string
+  // optional delimiter defaults to ~
+  var d = (delim == null)? '~' : delim;
+  return s.split(d);
+}
+
+function jsrsDebugInfo(){
+  // use for debugging by attaching to f1 (works with IE)
+  // with onHelp = "return jsrsDebugInfo();" in the body tag
+  var doc = window.open().document;
+  doc.open;
+  doc.write( 'Pool Size: ' + jsrsContextPoolSize + '<br><font face="arial" size="2"><b>' );
+  for( var i in jsrsContextPool ){
+    var contextObj = jsrsContextPool[i];
+    doc.write( '<hr>' + contextObj.id + ' : ' + (contextObj.busy ? 'busy' : 'available') + '<br>');
+    doc.write( contextObj.container.document.location.pathname + '<br>');
+    doc.write( contextObj.container.document.location.search + '<br>');
+    doc.write( '<table border="1"><tr><td>' + contextObj.container.document.body.innerHTML + '</td></tr></table>' );
+  }
+  doc.write('</table>');
+  doc.close();
+  return false;
+}
diff --git a/httemplate/elements/qlib/box.js b/httemplate/elements/qlib/box.js
new file mode 100644 (file)
index 0000000..537aac4
--- /dev/null
@@ -0,0 +1,29 @@
+/**\r
+ * QLIB 1.0 Box Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QBox(parent, name, res, x, y, width, height, body, visible, effects, opacity, zindex) {\r
+    this.init(parent, name);\r
+    if (this.res = res) {\r
+        this.x = x - 0;\r
+        this.y = y - 0;\r
+        this.width = width - 0;\r
+        this.height = (typeof(height) == "number") ? height : null;\r
+        this.body = body || "&nbsp;";\r
+        var j = QBox.arguments.length;\r
+        this.visible = (j > 8) ? visible : true;\r
+        this.effects = (j > 9) ? effects : (res.effects || 0);\r
+        this.opacity = (j > 10) ? opacity : (res.opacity != null ? res.opacity : 100);\r
+        this.zindex  = (j > 11) ? zindex : null;\r
+        this.create();\r
+    } else {\r
+        this.document.write("invalid resource");\r
+    }\r
+}\r
+QBox.prototype = new QBoxCtrl();\r
diff --git a/httemplate/elements/qlib/boxctrl.js b/httemplate/elements/qlib/boxctrl.js
new file mode 100644 (file)
index 0000000..417b204
--- /dev/null
@@ -0,0 +1,48 @@
+/**\r
+ * QLIB 1.0 Box Abstraction\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QBoxCtrl_content() {\r
+    with (this) {\r
+        if (res) {\r
+            this.cwidth = width - res.L - res.R - 8;\r
+            this.cheight = height && (height - res.T - res.B - 8);\r
+            var ec = '"><table border="0" cellspacing="0" cellpadding="0"><tr><td></td></tr></table></td>';\r
+            document.write('<table class="qbox" border="0" cellspacing="0" cellpadding="0" width="' +\r
+                (width - 8) + (height != null ? '" height="' + (height - 8) : '') + '"><tr><td width="' +\r
+                res.L + '" height="' + res.T + '"><img src="' + res.TL.src + '" border="0" width="' +\r
+                res.L + '" height="' + res.T + '"></td><td width="' + cwidth + '" height="' + res.T +\r
+                '" background="' + res.TC.src + ec + '<td width="' + res.R + '" height="' + res.T +\r
+                '"><img src="' + res.TR.src + '" border="0" width="' + res.R + '" height="' + res.T +\r
+                '"></td></tr><tr><td width="' + res.L + (cheight != null ? '" height="' + cheight : '') +\r
+                '" background="' + res.ML.src + ec + '<td width="' + cwidth + '" bgcolor="' + res.bgcolor +\r
+                (cheight != null ? '" height="' + cheight : '') + (res.bgtile ? '" background="' +\r
+                res.bgtile.src : '') + '" align="left" valign="top" class="body" unselectable="on">');\r
+                if (typeof(body) == "function") {\r
+                    this.body();\r
+                } else {\r
+                    document.write(body);\r
+                }\r
+            document.write('</td><td width="' + res.R + (cheight != null ? '" height="' + cheight : '') +\r
+                '" background="' + res.MR.src + ec + '</tr><tr><td width="' + res.L + '" height="' + res.B +\r
+                '"><img src="' + res.BL.src + '" border="0" width="' + res.L + '" height="' + res.B +\r
+                '"></td><td width="' + cwidth + '" height="' + res.B + '" background="' + res.BC.src + ec +\r
+                '<td width="' + res.R + '" height="' + res.B + '"><img src="' + res.BR.src +\r
+                '" border="0" width="' + res.R + '" height="' + res.B + '"></td></tr></table><br>');\r
+        }\r
+    }\r
+}\r
+\r
+function QBoxCtrl() {\r
+    this.res = false;\r
+    this.body = "&nbsp;";\r
+    this.cwidth = this.cheight = 0;\r
+    this.content = QBoxCtrl_content;\r
+}\r
+QBoxCtrl.prototype = new QWndCtrl();\r
diff --git a/httemplate/elements/qlib/boxres.js b/httemplate/elements/qlib/boxres.js
new file mode 100644 (file)
index 0000000..0878172
--- /dev/null
@@ -0,0 +1,42 @@
+/**\r
+ * QLIB 1.0 Box Resource\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QBoxRes(t, r, b, l, tc, tr, mr, br, bc, bl, ml, tl, bgcolor, bgtile, effects, opacity) { \r
+    var args = QBoxRes.arguments.length;\r
+    this.T = t;\r
+    this.R = r;\r
+    this.B = b;\r
+    this.L = l;\r
+    this.TC = new Image();\r
+    this.TC.src = tc;\r
+    this.TR = new Image(r, t);\r
+    this.TR.src = tr;\r
+    this.MR = new Image();\r
+    this.MR.src = mr;\r
+    this.BR = new Image(r, b);\r
+    this.BR.src = br;\r
+    this.BC = new Image();\r
+    this.BC.src = bc;\r
+    this.BL = new Image(l, b);\r
+    this.BL.src = bl;\r
+    this.ML = new Image();\r
+    this.ML.src = ml;\r
+    this.TL = new Image(l, t);\r
+    this.TL.src = tl;\r
+    this.bgcolor = bgcolor || "#FFFFFF";\r
+    if (bgtile) {\r
+        this.bgtile = new Image();\r
+        this.bgtile.src = bgtile;\r
+    } else {\r
+        this.bgtile = false;\r
+    }\r
+    this.effects = (args > 13) ? effects : null;\r
+    this.opacity = (args > 14) ? opacity : null;\r
+}\r
diff --git a/httemplate/elements/qlib/button.js b/httemplate/elements/qlib/button.js
new file mode 100644 (file)
index 0000000..05247d5
--- /dev/null
@@ -0,0 +1,74 @@
+/**\r
+ * QLIB 1.0 Button Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QButton_update() {\r
+    with (this) {\r
+        image.src = ((!enabled && res.imgD) || (value ? res.imgP : res.imgN)).src;\r
+    }\r
+}\r
+\r
+function QButton_doEvent() {\r
+    with (this) {\r
+        if (enabled) {\r
+            if (res.style == 1) {\r
+                this.value = value ? 0 : 1;\r
+                update();\r
+            }\r
+            onClick(value, tag);\r
+        }\r
+    }\r
+    return false;\r
+}\r
+\r
+function QButton_enable(state) {\r
+    this.enabled = state;\r
+    this.update();\r
+}\r
+\r
+function QButton_set(value) {\r
+    if (this.enabled) {\r
+        this.value = value ? 1 : 0;\r
+        this.update();\r
+    }\r
+    return true;\r
+}\r
+\r
+function QButton(parent, name, res, tooltip) {\r
+    this.init(parent, name);\r
+    if (res) {\r
+        this.res = res;\r
+        this.tip = tooltip || "";\r
+        this.enabled = true;\r
+        this.value = 0;\r
+        this.set = QButton_set;\r
+        this.enable = QButton_enable;\r
+        this.update = QButton_update;\r
+        this.doEvent = QButton_doEvent;\r
+        this.onClick = QControl.event;\r
+        with (this) {\r
+            document.write('<a href="#" hidefocus="true" unselectable="on"' +\r
+                (tip ? ' title="' + tip + '"' : '') + ' onClick="return ' + name +\r
+                '.doEvent()" onMouseOver="' + (res.style == 2 ? name + '.set(1);' : '') +\r
+                'window.top.status=' + name + '.tip;return true" onMouseOut="' +\r
+                (!res.style || (res.style == 2) ? name + '.set();' : '') + 'window.top.status=\'\'"' +\r
+                (!res.style ? ' onMouseDown="return ' + name + '.set(1)" onMouseUp="return ' + name + '.set()"' : '') +\r
+                '><img class="qbutton" name="' + id + '" src="' + res.imgN.src + '" border="0" width="' +\r
+                res.width + '" height="' + res.height + '"></a>');\r
+            this.image = document.images[id] || new Image(1, 1);\r
+        }\r
+    } else {\r
+        this.document.write("invalid resource");\r
+    }\r
+}\r
+QButton.prototype = new QControl();\r
+QButton.NORMAL    = 0;\r
+QButton.CHECKBOX  = 1;\r
+QButton.WEB       = 2;\r
+QButton.SIGNAL    = 3;\r
diff --git a/httemplate/elements/qlib/buttonres.js b/httemplate/elements/qlib/buttonres.js
new file mode 100644 (file)
index 0000000..97f6dfc
--- /dev/null
@@ -0,0 +1,23 @@
+/**\r
+ * QLIB 1.0 Button Resource\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QButtonRes(style, width, height, normal, pressed, disabled) {\r
+    this.style = style;\r
+    this.width = width;\r
+    this.height = height;\r
+    this.imgN = new Image(width, height);\r
+    this.imgN.src = normal;\r
+    this.imgP = new Image(width, height);\r
+    this.imgP.src = pressed;\r
+    if (disabled) {\r
+        this.imgD = new Image(width, height);\r
+        this.imgD.src = disabled;\r
+    }\r
+}\r
diff --git a/httemplate/elements/qlib/control.js b/httemplate/elements/qlib/control.js
new file mode 100644 (file)
index 0000000..f50206e
--- /dev/null
@@ -0,0 +1,51 @@
+/**\r
+ * QLIB 1.0 Base Abstract Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QControl_init(parent, name) {\r
+    this.parent = parent || self;\r
+    this.window = (parent && parent.window) || self;\r
+    this.document = (parent && parent.document) || self.document;\r
+    this.name = (parent && parent.name) ? (parent.name + "." + name) : ("self." + name);\r
+    this.id = "Q";\r
+    var h = this.hash(this.name);\r
+    for (var j=0; j<8; j++) {\r
+        this.id += QControl.HEXTABLE.charAt(h & 15);\r
+        h >>>= 4;\r
+    }\r
+}\r
+\r
+function QControl_hash(str) {\r
+    var h = 0;\r
+    if (str) {\r
+        for (var j=str.length-1; j>=0; j--) {\r
+            h ^= QControl.ANTABLE.indexOf(str.charAt(j)) + 1;\r
+            for (var i=0; i<3; i++) {\r
+                var m = (h = h<<7 | h>>>25) & 150994944;\r
+                h ^= m ? (m == 150994944 ? 1 : 0) : 1;\r
+            }\r
+        }\r
+    }\r
+    return h;\r
+}\r
+\r
+function QControl_nop() {\r
+}\r
+\r
+function QControl() {\r
+    this.init = QControl_init;\r
+    this.hash = QControl_hash;\r
+    this.window = self;\r
+    this.document = self.document;\r
+    this.tag = null;\r
+}\r
+QControl.ANTABLE  = "w5Q2KkFts3deLIPg8Nynu_JAUBZ9YxmH1XW47oDpa6lcjMRfi0CrhbGSOTvqzEV";\r
+QControl.HEXTABLE = "0123456789ABCDEF";\r
+QControl.nop = QControl_nop;\r
+QControl.event = QControl_nop;\r
diff --git a/httemplate/elements/qlib/counter.js b/httemplate/elements/qlib/counter.js
new file mode 100644 (file)
index 0000000..72aeddb
--- /dev/null
@@ -0,0 +1,81 @@
+/**\r
+ * QLIB 1.0 Animated Digital Counter\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QCounter_update() {\r
+    with (this) {\r
+        var v = Math.max(value, 0);\r
+        var mod;\r
+        for (var j=0; j<size; j++) {\r
+            mod = Math.floor(v % 10);\r
+            images[j].src = (v >= 1) || (!j) ? res.list[mod].src : res.list[10].src;\r
+            v /= 10;\r
+        }\r
+    }\r
+}\r
+\r
+function QCounter_count(value, step) {\r
+    this._cntt = false;\r
+    this.value += step; \r
+    if ((step * (this.value - value)) >= 0) {\r
+        this.value = value - 0;  // convert to number\r
+    } else {\r
+        this._cntt = setTimeout(this.name + ".count(" + value + "," + step + ")", 50);\r
+    }\r
+    this.update();\r
+}\r
+         \r
+function QCounter_set(value) {\r
+    this.setval = value;\r
+    if (value != this.value) {\r
+        if (this._cntt) {\r
+            clearTimeout(this._cntt);\r
+            this._cntt = false;\r
+        }\r
+        var dv = value - this.value;\r
+        if (this.effect == 2) {\r
+            dv = dv / Math.min(10, Math.abs(dv));\r
+        } else if (this.effect == 3) {\r
+            dv = dv / Math.abs(dv);\r
+        }\r
+        this.count(value, dv);\r
+    }\r
+}\r
+\r
+function QCounter(parent, name, res, size, effect) {\r
+    this.init(parent, name);\r
+    if (res) {\r
+        this.res = res;\r
+        this.setval = this.value = 0;\r
+        this.size = size || 4;\r
+        this.effect = effect || 2;\r
+        this._cntt = false;\r
+        this.images = new Array(this.size);\r
+        this.set = QCounter_set;\r
+        this.update = QCounter_update;\r
+        this.count = QCounter_count;\r
+        with (this) {\r
+            document.write('<table class="qcounter" width="' + (res.width * size) + '" height="' + res.height +\r
+                '" border="0" cellspacing="0" cellpadding="0" unselectable="on"><tr>');\r
+            for (var j=(size - 1); j>=0; j--) {\r
+                document.write('<td width="' + res.width + '" height="' + res.height +\r
+                    '" unselectable="on"><img name="' + id + j + '" src="' + (j ? res.list[10].src : res.list[0].src) +\r
+                    '" border="0" width="' + res.width + '" height="' + res.height + '"></td>');\r
+                images[j] = document.images[id + j] || new Image(1, 1);\r
+            }\r
+            document.write('</tr></table>');\r
+        }\r
+    } else {\r
+        this.document.write("invalid resource");\r
+    }\r
+}\r
+QCounter.prototype = new QControl();\r
+QCounter.INSTANT   = 1;\r
+QCounter.FAST      = 2;\r
+QCounter.SLOW      = 3;\r
diff --git a/httemplate/elements/qlib/imagelist.js b/httemplate/elements/qlib/imagelist.js
new file mode 100644 (file)
index 0000000..9f12de0
--- /dev/null
@@ -0,0 +1,25 @@
+/**\r
+ * QLIB 1.0 ImageList Resource\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QImageList(width, height) {\r
+    var len = QImageList.arguments.length - 2;\r
+    if (len > 0) {\r
+        this.list = new Array(len);\r
+        this.length = len;\r
+        this.width = width;\r
+        this.height = height;\r
+        var im;\r
+        for (var j=0; j<len; j++) {\r
+            im = new Image(width, height);\r
+            im.src = QImageList.arguments[j + 2];\r
+            this.list[j] = im;\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/httemplate/elements/qlib/label.js b/httemplate/elements/qlib/label.js
new file mode 100644 (file)
index 0000000..2d8b1e7
--- /dev/null
@@ -0,0 +1,72 @@
+/**\r
+ * QLIB 1.0 Text Label\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QLabel_set_ie(value) {\r
+    this.label.innerText = (this.value = value) || "\xA0";\r
+}\r
+\r
+function QLabel_set_dom2(value) {\r
+    with (this.label) {\r
+        replaceChild(this.document.createTextNode((this.value = value) || "\xA0"), firstChild);\r
+    }\r
+}\r
+\r
+function QLabel_set_ns4(value) {\r
+    this.value = value || "";\r
+    with (this) {\r
+        document.open();\r
+        document.write('<div class="qlabel">' + (clickable ? '<a href="#" title="' + tooltip + '" onClick="return ' +\r
+            name + '.doEvent()" onMouseOut="window.top.status=\'\'" onMouseOver="window.top.status=' + name +\r
+            '.tooltip;return true">' + value + '</a>' : value) + '</div>');\r
+        document.close();\r
+    }\r
+}\r
+\r
+function QLabel_doEvent() {\r
+    this.onClick(this.value, this.tag);\r
+    return false;\r
+}\r
+\r
+function QLabel(parent, name, value, clickable, tooltip) {\r
+    this.init(parent, name);\r
+    this.value = value || "";\r
+    this.clickable = clickable || false;\r
+    this.tooltip = tooltip || "";\r
+    this.doEvent = QLabel_doEvent;\r
+    this.onClick = QControl.event;\r
+    with (this) {\r
+        if (document.getElementById || document.all) {\r
+            document.write(clickable ? '<div class="qlabel" unselectable="on"><a id="' + id + '" href="#" title="' +\r
+                tooltip + '" onClick="return ' + name + '.doEvent()" onMouseOver="window.top.status=' + name +\r
+                '.tooltip;return true" onMouseOut="window.top.status=\'\'" hidefocus="true" unselectable="on">' +\r
+                (value || '&nbsp;') + '</a></div>' : '<div id="' + id + '" class="qlabel" unselectable="on">' +\r
+                (value || '&nbsp;') + '</div>');\r
+            this.label = document.getElementById ? document.getElementById(id) :\r
+                (document.all.item ? document.all.item(id) : document.all[id]);\r
+            this.set = (label && (label.innerText ? QLabel_set_ie :\r
+                (label.replaceChild && QLabel_set_dom2))) || QControl.nop;\r
+        } else if (document.layers) {\r
+            var suffix = "";\r
+            for (var j=value.length; j<QLabel.TEXTQUOTA; j++) suffix += " &nbsp;";\r
+            document.write('<div><ilayer id="i' + id + '"><layer id="' + id + '"><div class="qlabel">' +\r
+                (clickable ? '<a href="#" title="' + tooltip + '" onClick="return ' + name +\r
+                '.doEvent()" onMouseOver="window.top.status=' + name +\r
+                '.tooltip;return true" onMouseOut="window.top.status=\'\'">' + value + suffix + '</a>' :\r
+                value + suffix) + '</div></layer></ilayer></div>');\r
+            this.label = (this.label = document.layers["i" + id]) && label.document.layers[id];\r
+            this.document = label && label.document;\r
+            this.set = (label && document) ? QLabel_set_ns4 : QControl.nop;\r
+        } else {\r
+            document.write("Object is not supported");\r
+        }\r
+    }\r
+}\r
+QLabel.prototype = new QControl();\r
+QLabel.TEXTQUOTA = 50;\r
diff --git a/httemplate/elements/qlib/messagebox.js b/httemplate/elements/qlib/messagebox.js
new file mode 100644 (file)
index 0000000..2e45839
--- /dev/null
@@ -0,0 +1,57 @@
+/**\r
+ * QLIB 1.0 Message Box Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QMessageBox_alert(msg) {\r
+    if (typeof(msg) == "string") {\r
+        this.label.set(this.value = msg);\r
+    }\r
+    this.center();\r
+    this.focus();\r
+    this.show(true);\r
+}\r
+\r
+function QMessageBox_close() {\r
+    with (this.parent) {\r
+        if (!onClose(tag)) show(false);\r
+    }\r
+}\r
+\r
+function QMessageBox_body() {\r
+    with (this) {\r
+        document.write('<table border="0" width="' + cwidth + '"><tr><td align="left" valign="top" unselectable="on">');\r
+        this.label = new QLabel(this, "label", value);\r
+        document.write('</td></tr><tr><td height="' + (bres.height + 14) + '" align="center" valign="bottom" unselectable="on">');\r
+        this.button = new QButton(this, "button", bres, "Close");\r
+        document.write('</td></tr></table>');\r
+        button.onClick = QMessageBox_close;\r
+    }\r
+}\r
+\r
+function QMessageBox(parent, name, box, btn, msg, effects, opacity) {\r
+    this.init(parent, name);\r
+    if ((this.res = box) && (this.bres = btn)) {\r
+        this.value = typeof(msg) == "string" ? msg : "";\r
+        this.width = Math.max(200, Math.floor(Math.sqrt(555 * this.value.length)));\r
+        this.height = null;\r
+        this.x = this.y = 0;\r
+        this.visible = false;\r
+        this.zindex = null;\r
+        this.body = QMessageBox_body;\r
+        var j = QMessageBox.arguments.length;\r
+        this.effects = j > 5 ? effects : (box.effects != null ? box.effects : 0);\r
+        this.opacity = j > 6 ? opacity : (box.opacity != null ? box.opacity : 100);\r
+        this.create();\r
+        this.alert = QMessageBox_alert;\r
+        this.onClose = QControl.event;\r
+    } else {\r
+        this.document.write("invalid resource");\r
+    }\r
+}\r
+QMessageBox.prototype = new QBoxCtrl();\r
diff --git a/httemplate/elements/qlib/progress.js b/httemplate/elements/qlib/progress.js
new file mode 100644 (file)
index 0000000..2de077e
--- /dev/null
@@ -0,0 +1,73 @@
+/**\r
+ * QLIB 1.0 Progress Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QProgress_update() {\r
+    with (this) {\r
+        var i = low;\r
+        for (var j=0; j<size; j++) {\r
+            images[j].src = i < value ? imgsrc1 : imgsrc0;\r
+            i += delta;\r
+        }\r
+    }\r
+}\r
+\r
+function QProgress_set(value) {\r
+    this.value = value - 0;\r
+    this.update();\r
+}\r
+\r
+function QProgress_setBounds(low, high) {\r
+    this.low = Math.min(low, high);\r
+    this.high = Math.max(low, high);\r
+    this.delta = (this.high - this.low) / this.size;\r
+    this.update();\r
+}\r
\r
+function QProgress(parent, name, res, size, style) {\r
+    this.init(parent, name);\r
+    if (res) {\r
+        this.res = res;\r
+        this.value = 0;\r
+        this.low = 0;\r
+        this.high = 100;\r
+        this.size = size || 10;\r
+        this.delta = 100 / this.size;\r
+        this.style = style || 0;\r
+        this.images = new Array(this.size);\r
+        this.imgsrc0 = res.list[0] && res.list[0].src;\r
+        this.imgsrc1 = res.list[1] && res.list[1].src;\r
+        this.set = QProgress_set;\r
+        this.update = QProgress_update;\r
+        this.setBounds = QProgress_setBounds;\r
+        with (this) {\r
+            var hor = this.style < 2;\r
+            var rev = this.style % 2;\r
+            document.write('<table class="qprogress" border="0"  cellspacing="0" cellpadding="0" unselectable="on" ' +\r
+                (hor ? 'width="' + (size * res.width) + '" height="' + res.height + '"><tr>' : 'width="' + res.width +\r
+                '" height="' + (size * res.height) + '">'));\r
+            for (var j=0; j<size; j++) {\r
+                document.write((hor ? '' : '<tr>') + '<td width="' + res.width + '" height="' + res.height +\r
+                    '" unselectable="on"><img name="' + id + (rev ? size - j - 1 : j) + '" src="' + res.list[0].src +\r
+                    '" border="0" width="' + res.width + '" height="' + res.height + '"></td>' + (hor ? '' : '</tr>'));\r
+            }\r
+            document.write((hor ? '</tr>' : '') + '</table>');\r
+            for (var j=0; j<size; j++) {\r
+                images[j] = document.images[id + j] || new Image(1, 1);\r
+            }\r
+        }\r
+    } else {\r
+        this.document.write("invalid resource");\r
+    }\r
+}\r
+QProgress.prototype = new QControl();\r
+QProgress.NORMAL    = 0;\r
+QProgress.REVERSE   = 1;\r
+QProgress.FALL      = 2;\r
+QProgress.RISE      = 3;\r
diff --git a/httemplate/elements/qlib/sound.js b/httemplate/elements/qlib/sound.js
new file mode 100644 (file)
index 0000000..3d1aaf6
--- /dev/null
@@ -0,0 +1,47 @@
+/**\r
+ * QLIB 1.0 Preloaded Sound\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QSound_play(loop) {\r
+    this._out.loop = loop || 0;\r
+    this._out.src = this._buf.src;\r
+}\r
+\r
+function QSound_stop() {\r
+    this._out.loop = 0;\r
+    this._out.src = "";\r
+}\r
+\r
+function QSound_setVolume(volume) {\r
+    this._out.volume = this.volume = volume;\r
+}\r
\r
+function QSound(parent, name, src, volume) {\r
+    this.init(parent, name);\r
+    this.volume = volume || 0;\r
+    this.play = this.stop = this.setVolume = QControl.nop;\r
+    with (this) {\r
+        document.write('<bgsound id="' + id + '" src="" volume="' + volume + '">');\r
+        if (document.all && document.all.item) {\r
+            this._out = document.all.item(id);\r
+            if (_out && (typeof _out.src != "undefined") && (_out.volume === volume)) {\r
+                document.write('<bgsound id="b' + id + '" src="' + src + '" volume="-10000">');\r
+                this._buf = document.all.item("b" + id);\r
+                if (_buf) {\r
+                    this.play = QSound_play;\r
+                    this.stop = QSound_stop;\r
+                    this.setVolume = QSound_setVolume;\r
+\r
+                    _out.onreadystatechange = new Function("alert(0)");\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
+QSound.prototype = new QControl();\r
diff --git a/httemplate/elements/qlib/sprite.js b/httemplate/elements/qlib/sprite.js
new file mode 100644 (file)
index 0000000..72a68fb
--- /dev/null
@@ -0,0 +1,125 @@
+/**\r
+ * QLIB 1.0 Sprite Object\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QSprite_load(src) {\r
+    if (src) {\r
+        this.face = new Image(this.cwidth, this.cheight);\r
+        this.face.src = src;\r
+        this.valid = false;\r
+    }\r
+}\r
\r
+function QSprite_show(show) {\r
+    if (show && !this.valid && this.face.complete) {\r
+        this._img.src = this.face.src;\r
+        this.valid = true;\r
+    }\r
+    this._show(show);\r
+}\r
+\r
+function QSprite_moveTo(x, y) {\r
+    this.stop();\r
+    this._move(x, y);\r
+}\r
+\r
+function QSprite_slideTo(x, y) {\r
+    this.stop();\r
+    if (this.visible) {\r
+        this.doSlide(++this._spro, x, y);\r
+    } else {\r
+        this.moveTo(x, y);\r
+    }\r
+}\r
+\r
+function QSprite_shake() {\r
+    this.stop();\r
+    if (this.visible) {\r
+        this.doShake(++this._spro, 0, this.x, this.y);\r
+    }\r
+}\r
+\r
+function QSprite_stop() {\r
+    this._spro++;\r
+    if (this._sprt) {\r
+        clearTimeout(this._sprt);\r
+        this._sprt = false;\r
+    }\r
+}\r
+\r
+function QSprite_doSlide(id, x, y) {\r
+    if (this._spro == id) {\r
+        this._sprt = false;\r
+        var dx = Math.round(x - this.x);\r
+        var dy = Math.round(y - this.y);\r
+        if (dx || dy) {\r
+            if (dx) dx = dx > 0 ? Math.ceil(dx/4) : Math.floor(dx/4);\r
+            if (dy) dy = dy > 0 ? Math.ceil(dy/4) : Math.floor(dy/4);\r
+            this._move(this.x + dx, this.y + dy);\r
+            this._sprt = setTimeout(this.name + ".doSlide(" + id + "," + x + "," + y + ")", 30);\r
+        } else {\r
+            this._move(x, y);\r
+        }\r
+    }\r
+}\r
+\r
+function QSprite_doShake(id, phase, x, y) {\r
+    if (this._spro == id) {\r
+        this._sprt = false;\r
+        if (phase < 20) {\r
+            var m = 3 * Math.sin(.16 * phase);\r
+            this._move(x + m * Math.sin(phase), y + m * Math.cos(phase));\r
+            this._sprt = setTimeout(this.name + ".doShake(" + id + "," + (++phase) + "," + x + "," + y + ")", 20);\r
+        } else {\r
+            this._move(x, y);\r
+        }\r
+    }\r
+}\r
+\r
+function QSprite_doClick() {\r
+    if (!this._sprt) {\r
+        this.onClick(this.tag);\r
+    }\r
+    return false;\r
+}\r
+\r
+function QSprite(parent, name, x, y, width, height, src, visible, effects, opacity, zindex) {\r
+    this.init(parent, name);\r
+    this.x = x - 0;\r
+    this.y = y - 0;\r
+    this.width = (this.cwidth = width - 0) + 8;\r
+    this.height = (this.cheight = height - 0) + 8;\r
+    var j = QSprite.arguments.length;\r
+    this.visible = (j > 7) ? visible : true;\r
+    this.effects = (j > 8) ? effects : 0;\r
+    this.opacity = (j > 9) ? opacity : 100;\r
+    this.zindex  = (j > 10) ? zindex : null;\r
+    this.valid = !!src;\r
+    this.content = '<a href="#" title="" onclick="return false" onmousedown="return ' + this.name +\r
+        '.doClick()" onmouseover="window.top.status=\'\';return true" hidefocus="true" unselectable="on"><img name="' +\r
+        this.id + '" src="' + (src || '') + '" border="0" width="' + this.cwidth + '" height="' + this.cheight +\r
+        '" alt="" unselectable="on"></a>';\r
+    this.doClick = QSprite_doClick;\r
+    this.doSlide = QSprite_doSlide;\r
+    this.doShake = QSprite_doShake;\r
+    this.onClick = QControl.event;\r
+    this.create();\r
+    this.face = this._img = this.document.images[this.id] || new Image(1, 1);\r
+    this._spro = 0;\r
+    this._sprt = false;\r
+    this._show = this.show;\r
+    this._move = this.moveTo;\r
+    this.load = QSprite_load;\r
+    this.show = QSprite_show;\r
+    this.moveTo = QSprite_moveTo;\r
+    this.slideTo = QSprite_slideTo;\r
+    this.shake = QSprite_shake;\r
+    this.stop = QSprite_stop;\r
+}\r
+QSprite.prototype = new QWndCtrl();\r
diff --git a/httemplate/elements/qlib/window.js b/httemplate/elements/qlib/window.js
new file mode 100644 (file)
index 0000000..6056fda
--- /dev/null
@@ -0,0 +1,25 @@
+/**\r
+ * QLIB 1.0 Window Control\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QWindow(parent, name, x, y, width, height, content, visible, effects, opacity, zindex) {\r
+    this.init(parent, name);\r
+    this.x = x - 0;\r
+    this.y = y - 0;\r
+    this.width = width - 0;\r
+    this.height = (typeof(height) == "number") ? height : null;\r
+    this.content = content;\r
+    var j = QWindow.arguments.length;\r
+    this.visible = (j > 7) ? visible : true;\r
+    this.effects = (j > 8) ? effects : 0;\r
+    this.opacity = (j > 9) ? opacity : 100;\r
+    this.zindex  = (j > 10) ? zindex : null;\r
+    this.create();\r
+}\r
+QWindow.prototype = new QWndCtrl();\r
diff --git a/httemplate/elements/qlib/wndctrl.js b/httemplate/elements/qlib/wndctrl.js
new file mode 100644 (file)
index 0000000..b3bde4e
--- /dev/null
@@ -0,0 +1,322 @@
+/**\r
+ * QLIB 1.0 Window Abstraction\r
+ * Copyright (C) 2002 2003, Quazzle.com Serge Dolgov\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License\r
+ * as published by the Free Software Foundation; either version 2\r
+ * of the License, or (at your option) any later version.\r
+ * http://qlib.quazzle.com\r
+ */\r
+\r
+function QWndCtrl_center_ie4() {\r
+    var b = this.document.body;\r
+    this.moveTo(b.scrollLeft + Math.max(0, Math.floor((b.clientWidth -\r
+        this.width) / 2)), b.scrollTop + 100);\r
+}\r
+\r
+function QWndCtrl_center_moz() {\r
+    this.moveTo(self.pageXOffset + Math.max(0, Math.floor((self.innerWidth -\r
+        this.width) / 2)), self.pageYOffset + 100);\r
+}\r
+\r
+function QWndCtrl_setEffects_ie4(fx) {\r
+    this.effects = fx;\r
+    with (this.wnd) {\r
+        filters[0].enabled = (fx & 256) != 0;\r
+        filters[1].enabled = (fx & 512) != 0;\r
+        filters[2].enabled = (fx & 1024) != 0;\r
+        filters[4].enabled = (fx & 2048) != 0;\r
+    }\r
+}\r
+\r
+function QWndCtrl_setEffects_moz(fx) {\r
+    this.effects = fx;\r
+}\r
+\r
+function QWndCtrl_setOpacity_ie4(op) {\r
+    this.opacity = Math.max(0, Math.min(100, Math.floor(op - 0)));\r
+    this.wnd.filters[3].opacity = this.opacity;\r
+    this.wnd.filters[3].enabled = (this.opacity < 100);\r
+}\r
+\r
+function QWndCtrl_setOpacity_moz(op) {\r
+    this.opacity = Math.max(0, Math.min(100, Math.floor(op - 0)));\r
+    this.wnd.style.MozOpacity = this.opacity + "%";\r
+}\r
+\r
+function QWndCtrl_setSize_css(w, h) {\r
+    this.wnd.style.width = (this.width = Math.floor(w - 0)) + "px";\r
+    this.wnd.style.height = typeof(h) == "number" ? (this.height = Math.floor(h)) + "px" : "auto";\r
+}\r
+\r
+function QWndCtrl_setSize_ns4(w, h) {\r
+    this.wnd.clip.width = this.width = Math.floor(w - 0);\r
+    if (typeof(h) == "number") {\r
+        this.wnd.clip.height = this.height = Math.floor(h);\r
+    }\r
+}\r
+\r
+function QWndCtrl_focus() {\r
+    this.setZIndex(QWndCtrl.TOPZINDEX++);\r
+}\r
+\r
+function QWndCtrl_setZIndex_css(z) {\r
+    this.wnd.style.zIndex = this.zindex = z || 0;\r
+}\r
+\r
+function QWndCtrl_setZIndex_ns4(z) {\r
+    this.wnd.zIndex = this.zindex = z || 0;\r
+}\r
+\r
+function QWndCtrl_moveTo_css(x, y) {\r
+    this.wnd.style.left = (this.x = Math.floor(x - 0)) + "px";\r
+    this.wnd.style.top = (this.y = Math.floor(y - 0)) + "px";\r
+}\r
+\r
+function QWndCtrl_moveTo_ns4(x, y) {\r
+    this.wnd.moveTo(this.x = Math.floor(x - 0), this.y = Math.floor(y - 0));\r
+}\r
+\r
+function QWndCtrl_fxhandler() {\r
+    this.fxhandler = QControl.nop;\r
+    this.onShow(this.visible, this.tag);\r
+}\r
+\r
+function QWndCtrl_show_ie4(show) {\r
+    if (this.visible != show) {\r
+        var fx = false;\r
+        switch (show ? this.effects & 15 : (this.effects & 240) >>> 4) {\r
+            case 1:\r
+                fx = this.wnd.filters[5];\r
+                break;\r
+            case 2:\r
+                (fx = this.wnd.filters[6]).transition = show ? 1 : 0;\r
+                break;\r
+            case 3:\r
+                (fx = this.wnd.filters[6]).transition = show ? 3 : 2;\r
+                break;\r
+            case 4:\r
+                (fx = this.wnd.filters[6]).transition = show ? 5 : 4;\r
+                break;\r
+            case 5:\r
+                (fx = this.wnd.filters[6]).transition = show ? 14 : 13;\r
+                break;\r
+            case 6:\r
+                (fx = this.wnd.filters[6]).transition = show ? 16 : 15;\r
+                break;\r
+            case 7:\r
+                (fx = this.wnd.filters[6]).transition = 12;\r
+                break;\r
+            case 8:\r
+                (fx = this.wnd.filters[6]).transition = 8;\r
+                break;\r
+            case 9:\r
+                (fx = this.wnd.filters[6]).transition = 9;\r
+        }\r
+        if (fx) {\r
+            fx.apply();\r
+            this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";\r
+            this.fxhandler = QWndCtrl_fxhandler;\r
+            fx.play(0.3);\r
+        } else {\r
+            this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";\r
+            this.onShow(show, this.tag);\r
+        }\r
+    }\r
+}\r
+\r
+function QWndCtrl_fade_moz(op, step) {\r
+    this._wndt = false;\r
+    if (step) {\r
+        op += step;\r
+        if ((op > 0) && (op < this.opacity)) {\r
+            this.wnd.style.MozOpacity = op + "%";\r
+            this._wndt = setTimeout(this.name + ".fade(" + op + "," + step + ")", 50);\r
+        } else {\r
+            if (op <= 0) {\r
+                this.wnd.style.visibility = "hidden";\r
+                this.visible = false;\r
+            }\r
+            this.wnd.style.MozOpacity = this.opacity + "%";\r
+            this.onShow(this.visible, this.tag);\r
+        }\r
+    }\r
+}\r
+\r
+function QWndCtrl_show_moz(show) {\r
+    if (this.visible != show) {\r
+        if (this._wndt) {\r
+            clearTimeout(this._wndt);\r
+            this._wndt = false;\r
+        }\r
+        var step = show ? ((this.effects & 15) == 1) && Math.floor(this.opacity / 5) :\r
+            ((this.effects & 240) == 16) && -Math.floor(this.opacity / 5);\r
+        if (step) {\r
+            if (this.visible) {\r
+                this.fade(this.opacity - 0, step);\r
+            } else {\r
+                this.wnd.style.MozOpacity = "0%";\r
+                this.wnd.style.visibility = "visible";\r
+                this.visible = true;\r
+                this.fade(0, step);\r
+            }\r
+        } else {\r
+            this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";\r
+            this.onShow(show, this.tag);\r
+        }\r
+    }\r
+}\r
+\r
+function QWndCtrl_show_css(show) {\r
+    if (this.visible != show) {\r
+        this.wnd.style.visibility = (this.visible = show) ? "visible" : "hidden";\r
+        this.onShow(show, this.tag);\r
+    }\r
+}\r
+\r
+function QWndCtrl_show_ns4(show) {\r
+    if (this.visible != show) {\r
+        this.wnd.visibility = (this.visible = show) ? "show" : "hidden";\r
+        this.onShow(show, this.tag);\r
+    }\r
+}\r
+\r
+function QWndCtrl_create_dom2() {\r
+    with (this) {\r
+        this.fxhandler = QControl.nop;\r
+        var ie4 = document.body && document.body.filters;\r
+        var moz = document.body && document.body.style &&\r
+            typeof(document.body.style.MozOpacity) == "string";\r
+        document.write('<div unselectable="on" id="' + id +\r
+            (ie4 ? '" onfilterchange="' + name + '.fxhandler()': '') +\r
+            '" style="position:absolute;left:' + x + 'px;top:' + y +\r
+            'px;width:' + width + (height != null ? 'px;height:' + height : '') +\r
+            'px;visibility:' + (visible ? 'visible' : 'hidden') +\r
+            ';overflow:hidden' + (zindex ? ';z-index:' + zindex : '') +\r
+            (ie4 ? ';filter:Gray(enabled=' + (effects & 256 ? '1' : '0') +\r
+            ') Xray(enabled=' + (effects & 512 ? '1' : '0') +\r
+            ') Invert(enabled=' + (effects & 1024 ? '1' : '0') +\r
+            ') alpha(enabled=' + (opacity < 100 ? '1' : '0') + ',opacity=' + opacity +\r
+            ') shadow(enabled=' + (effects & 2048 ? '1' : '0') +\r
+            ',direction=135) BlendTrans(enabled=0) RevealTrans(enabled=0)' : '') +\r
+            (moz && (opacity < 100) ? ';-moz-opacity:' + opacity + '%' : '') +\r
+            '"><div unselectable="on" class="qwindow">');\r
+        if (typeof(content) == "function") {\r
+            this.content();\r
+        } else {\r
+            document.write(content);\r
+        }\r
+        document.write('</div></div>');\r
+        if (this.wnd = document.getElementById ? document.getElementById(id) :\r
+            (document.all.item ? document.all.item(id) : document.all[id])) {\r
+            if (wnd.style) {\r
+                ie4 = ie4 && wnd.filters;\r
+                moz = moz && typeof(wnd.style.MozOpacity) == "string";\r
+                this.moveTo = QWndCtrl_moveTo_css;\r
+                this.setZIndex = QWndCtrl_setZIndex_css;\r
+                this.focus = QWndCtrl_focus;\r
+                this.setSize = QWndCtrl_setSize_css;\r
+                this.show = ie4 ? QWndCtrl_show_ie4 : (moz ? QWndCtrl_show_moz : QWndCtrl_show_css);\r
+                this.fade = moz ? QWndCtrl_fade_moz : QControl.nop;\r
+                this.setOpacity = ie4 ? QWndCtrl_setOpacity_ie4 : (moz ? QWndCtrl_setOpacity_moz : QControl.nop);\r
+                this.setEffects = ie4 ? QWndCtrl_setEffects_ie4 : (moz ? QWndCtrl_setEffects_moz : QControl.nop);\r
+                this.center = self.innerWidth ? QWndCtrl_center_moz :\r
+                    (document.body && document.body.clientWidth ? QWndCtrl_center_ie4 : QControl.nop);\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+function QWndCtrl_create_ns4(finalize) {\r
+    with (this) {\r
+        if (finalize) {\r
+            if (_wnde) {\r
+                parent.window.onload = _wnde;\r
+                parent.window.onload();\r
+            }\r
+            document.open();\r
+            document.write('<div class="qwindow">');\r
+            this.content();\r
+            document.write('</div>');\r
+            document.close();\r
+        } else {\r
+            document.write('<layer id="' + id + '" left="' + x + '" top="' + y +\r
+                '" width="' + width + '" visibility="' + (visible ? 'show' : 'hidden') +\r
+                (height != null ? '" height="' + height + '" clip="' + width + ',' + height : '') +\r
+                (zindex ? '" z-index="' + zindex : '') + (typeof(content) != "function" ?\r
+                '"><div class="qwindow">' + content + '</div></layer>' : '">&nbsp;</layer>'));\r
+            if (this.window = this.wnd = document.layers[id]) {\r
+                if (this.document = wnd.document) {\r
+                    this.show = QWndCtrl_show_ns4;\r
+                    this.moveTo = QWndCtrl_moveTo_ns4;\r
+                    this.setZIndex = QWndCtrl_setZIndex_ns4;\r
+                    this.focus = QWndCtrl_focus;\r
+                    this.center = QWndCtrl_center_moz;\r
+                    this.setSize = QWndCtrl_setSize_ns4;\r
+                    if (typeof(content) == "function") {\r
+                        this._wnde = parent.window.onload;\r
+                        parent.window.onload = new Function(name + ".create(true)");\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+function QWndCtrl_create_na() {\r
+    this.document.write('Object is not supported.');\r
+    this.wnd = null;\r
+}\r
+\r
+function QWndCtrl_create() {\r
+    with (this) {\r
+        this.create = (document.getElementById || document.all) ? QWndCtrl_create_dom2 :\r
+            (document.layers ? QWndCtrl_create_ns4 : QWndCtrl_create_na);\r
+        create();\r
+    }\r
+}\r
+\r
+function QWndCtrl() {\r
+    this.x = this.y = 0;\r
+    this.width = this.height = 0;\r
+    this.content = "";\r
+    this.visible = true;\r
+    this.effects = 0;\r
+    this.opacity = 100;\r
+    this.zindex = null;\r
+    this._wndt = this._wnde = false;\r
+    this.create = QWndCtrl_create;\r
+    this.show = QControl.nop;\r
+    this.focus = QControl.nop;\r
+    this.center = QControl.nop;\r
+    this.moveTo = QControl.nop;\r
+    this.setSize = QControl.nop;\r
+    this.setOpacity = QControl.nop;\r
+    this.setEffects = QControl.nop;\r
+    this.setZIndex  = QControl.nop;\r
+    this.onShow = QControl.event;\r
+}\r
+QWndCtrl.prototype = new QControl();\r
+QWndCtrl.TOPZINDEX = 1000;\r
+QWndCtrl.GRAY      = 256;\r
+QWndCtrl.XRAY      = 512;\r
+QWndCtrl.INVERT    = 1024;\r
+QWndCtrl.SHADOW    = 2048;\r
+QWndCtrl.FADEIN    = 1;\r
+QWndCtrl.FADEOUT   = 16;\r
+QWndCtrl.BOXIN     = 2;\r
+QWndCtrl.BOXOUT    = 32;\r
+QWndCtrl.CIRCLEIN  = 3;\r
+QWndCtrl.CIRCLEOUT = 48;\r
+QWndCtrl.WIPEIN    = 4;\r
+QWndCtrl.WIPEOUT   = 64;\r
+QWndCtrl.HBARNIN   = 5;\r
+QWndCtrl.HBARNOUT  = 80;\r
+QWndCtrl.VBARNIN   = 6;\r
+QWndCtrl.VBARNOUT  = 96;\r
+QWndCtrl.DISSOLVEIN  = 7;\r
+QWndCtrl.DISSOLVEOUT = 112;\r
+QWndCtrl.HBLINDSIN   = 8;\r
+QWndCtrl.HBLINDSOUT  = 128;\r
+QWndCtrl.VBLINDSIN   = 9;\r
+QWndCtrl.VBLINDSOUT  = 144;\r
diff --git a/httemplate/images/progressbar-empty.png b/httemplate/images/progressbar-empty.png
new file mode 100644 (file)
index 0000000..318219c
Binary files /dev/null and b/httemplate/images/progressbar-empty.png differ
diff --git a/httemplate/images/progressbar-full.png b/httemplate/images/progressbar-full.png
new file mode 100644 (file)
index 0000000..863d8e1
Binary files /dev/null and b/httemplate/images/progressbar-full.png differ
diff --git a/httemplate/misc/progress.html b/httemplate/misc/progress.html
new file mode 100644 (file)
index 0000000..cbff08b
--- /dev/null
@@ -0,0 +1,88 @@
+<%
+  my( $jobnum ) = $cgi->param('jobnum');
+  my( $url ) = $cgi->param('url');
+  my $job = '';
+  if ( $jobnum =~ /^(\d+)$/ ) {
+    $job = qsearchs('queue', { 'jobnum' => $jobnum } );
+  }
+  if ( $job && $job->status ne 'failed' ) {
+%>
+
+<HTML>
+  <HEAD>
+    <TITLE>
+      Please wait...
+    </TITLE>
+    <META HTTP-Equiv="Cache-Control" Content="no-cache">
+    <META HTTP-Equiv="Pragma" Content="no-cache">
+    <META HTTP-Equiv="Expires" Content="0"> 
+
+    <!-- could do more JSRS magic and avoid refreshing... but works for now -->
+    <META HTTP-Equiv="Refresh" Content="5">
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+    <P ALIGN="center">Please wait...
+    <script TYPE="text/javascript" src="../elements/qlib/control.js"></script>
+    <script TYPE="text/javascript" src="../elements/qlib/imagelist.js"></script>
+    <script TYPE="text/javascript" src="../elements/qlib/progress.js"></script>
+    <P ALIGN="center">
+    <script TYPE="text/javascript">
+      // Create imagelist
+      SEGS = new QImageList(4, 23, "../images/progressbar-empty.png", "../images/progressbar-full.png");
+
+      // Create bars
+      bar1 = new QProgress(null, "bar1", SEGS, 100);
+      bar1.set(<%= $job->statustext %>);
+      // bar1.update;
+    </script>
+    <BR><%= $job->statustext %>%
+    <P ALIGN="center">(progress of job #<%= $jobnum %>)
+  </BODY>
+</HTML>
+
+<%
+  } elsif ( !$job ) { #handle job gone case : job sucessful
+                      # so close popup, redirect parent window...
+    #eidiot("write success handler (jobnum $jobnum)");
+%>
+
+<HTML>
+  <HEAD>
+    <TITLE>
+      Operation successful
+    </TITLE>
+    <META HTTP-Equiv="Cache-Control" Content="no-cache">
+    <META HTTP-Equiv="Pragma" Content="no-cache">
+    <META HTTP-Equiv="Expires" Content="0"> 
+
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+    <SCRIPT TYPE="text/javascript">
+      top.opener.window.location.href = '<%= $cgi->param('url') %>';
+      window.close();
+    </SCRIPT>
+  </BODY>
+</HTML>
+
+<%
+  } else {
+%>
+
+  <HEAD>
+    <TITLE>
+      Error
+    </TITLE>
+    <META HTTP-Equiv="Cache-Control" Content="no-cache">
+    <META HTTP-Equiv="Pragma" Content="no-cache">
+    <META HTTP-Equiv="Expires" Content="0"> 
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+    <P ALIGN="center"><FONT COLOR="#ff0000">Error: <%= $job ? $job->statustext : $jobnum %></FONT>
+    <P ALIGN="center"><INPUT TYPE="button" onClick="top.opener.document.OneTrueForm.submit.disabled=false; window.close();" VALUE="OK">
+  </BODY>
+</HTML>
+
+<% } %>
+
+
+