ajax-style xmlhttprequest state/county/country selector!
authorivan <ivan>
Sat, 10 Sep 2005 14:50:57 +0000 (14:50 +0000)
committerivan <ivan>
Sat, 10 Sep 2005 14:50:57 +0000 (14:50 +0000)
CREDITS
httemplate/edit/cust_main.cgi
httemplate/edit/cust_main/contact.html
httemplate/edit/cust_main/select-country.html [new file with mode: 0644]
httemplate/edit/cust_main/select-county.html [new file with mode: 0644]
httemplate/edit/cust_main/select-state.html [new file with mode: 0644]
httemplate/elements/xmlhttp.html [new file with mode: 0644]
httemplate/misc/counties.cgi [new file with mode: 0644]
httemplate/misc/states.cgi [new file with mode: 0644]

diff --git a/CREDITS b/CREDITS
index 0e60599..450f267 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -153,5 +153,10 @@ by Erik Bosrup), licensed under the terms of the Artistic license
 Ricardo SIGNES <rjbs+freeside-devel@icgroup.com> has contributed a bunch of
 patches to clean up and refactor various stuff in the module layer.  Thanks!
 
+XMLHttpRequest implementation based on the SAJAX toolkit, licensed under the
+terms of the BSD license.
+(c) copyright 2005 modernmethod, inc
+Perl backend version (c) copyright 2005 Nathan Schmidt
+
 Everything else is my (Ivan Kohler <ivan@420.am>) fault.
 
index a8f202f..d10b21a 100755 (executable)
@@ -173,11 +173,17 @@ function bill_changed(what) {
 <% for (qw( last first company address1 address2 city zip daytime night fax )) { %>
     what.form.ship_<%=$_%>.value = what.form.<%=$_%>.value;
 <% } %>
+
     what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
-    ship_country_changed(what.form.ship_country);
-    what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
-    ship_state_changed(what.form.ship_state);
-    what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+    function fix_ship_state() {
+      what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
+    }
+    ship_country_changed(what.form.ship_country, fix_ship_state );
+
+    function fix_ship_county() {
+      what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+    }
+    ship_state_changed(what.form.ship_state, fix_ship_county );
   }
 }
 function samechanged(what) {
index d3b7b16..e0cd06f 100644 (file)
@@ -14,14 +14,23 @@ $cust_main->set($pre.'state', $statedefault )
   unless $cust_main->get($pre.'state')
          || $cust_main->get($pre.'country') ne $countrydefault;
 
-my($county_html, $state_html, $country_html) =
-  FS::cust_main_county::regionselector( $cust_main->get($pre.'county'),
-                                        $cust_main->get($pre.'state'),
-                                        $cust_main->get($pre.'country'),
-                                        $pre,
-                                        $onchange,
-                                        $disabled,
-                                      );
+#my($county_html, $state_html, $country_html) =
+#  FS::cust_main_county::regionselector( $cust_main->get($pre.'county'),
+#                                        $cust_main->get($pre.'state'),
+#                                        $cust_main->get($pre.'country'),
+#                                        $pre,
+#                                        $onchange,
+#                                        $disabled,
+#                                      );
+
+my %select_hash = (
+  'county'   => $cust_main->get($pre.'county'),
+  'state'    => $cust_main->get($pre.'state'),
+  'country'  => $cust_main->get($pre.'country'),
+  'prefix'   => $pre,
+  'onchange' => $onchange,
+  'disabled' => $disabled,
+);
 
 my $daytime_label = FS::Msgcat::_gettext('daytime') || 'Day Phone';
 my $night_label = FS::Msgcat::_gettext('night') || 'Night Phone';
@@ -75,7 +84,10 @@ my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
     <INPUT TYPE="text" NAME="<%=$pre%>city" VALUE="<%= $cust_main->get($pre.'city') %>" onChange="<%= $onchange %>" <%=$disabled%>>
   </TD>
   <TH ALIGN="right"><%=$r%>State</TH>
-  <TD><%= $county_html. $state_html %></TD>
+  <TD>
+    <%= include('select-county.html', %select_hash ) %>
+    <%= include('select-state.html', %select_hash ) %>
+  </TD>
   <TH><%=$r%>Zip</TH>
   <TD>
     <INPUT TYPE="text" NAME="<%=$pre%>zip" VALUE="<%= $cust_main->get($pre.'zip') %>" SIZE=10 onChange="<%= $onchange %>" <%=$disabled%>>
@@ -84,7 +96,7 @@ my $r = qq!<font color="#ff0000">*</font>&nbsp;!;
 
 <TR>
   <TH ALIGN="right"><%=$r%>Country</TH>
-  <TD><%= $country_html %></TD>
+  <TD><%= include('select-country.html', %select_hash ) %></TD>
 </TR>
 
 <TR>
diff --git a/httemplate/edit/cust_main/select-country.html b/httemplate/edit/cust_main/select-country.html
new file mode 100644 (file)
index 0000000..44f4f0a
--- /dev/null
@@ -0,0 +1,68 @@
+<%
+
+  my %opt = @_;
+  foreach my $opt (qw( county state country prefix onchange disabled )) {
+    $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_});
+  }
+
+  my $conf = new FS::Conf;
+  my $countrydefault = $conf->config('countrydefault') || 'US';
+
+%>
+
+<%= include('/elements/xmlhttp.html', $p.'misc/states.cgi', $opt{'prefix'}. 'get_states') %>
+
+<SCRIPT TYPE="text/javascript">
+
+  function opt(what,value,text) {
+    var optionName = new Option(text, value, false, false);
+    var length = what.length;
+    what.options[length] = optionName;
+  }
+
+  function <%= $opt{'prefix'} %>country_changed(what, callback) {
+
+    country = what.options[what.selectedIndex].text;
+
+    function <%= $opt{'prefix'} %>update_states(states) {
+
+      // blank the current state list
+      for ( var i = what.form.<%= $opt{'prefix'} %>state.length; i >= 0; i-- )
+          what.form.<%= $opt{'prefix'} %>state.options[i] = null;
+
+      // add the new states
+      var statesArray = eval('(' + states + ')' );
+      for ( var s = 0; s < statesArray.length; s++ ) {
+          var stateLabel = statesArray[s];
+          if ( stateLabel == "" )
+              stateLabel = '(n/a)';
+          opt(what.form.<%= $opt{'prefix'} %>state, statesArray[s], stateLabel);
+      }
+
+      //run the callback
+      if ( callback != null ) 
+        callback();
+    }
+
+    // go get the new states
+    <%= $opt{'prefix'} %>get_states( country, <%= $opt{'prefix'} %>update_states );
+
+  }
+
+</SCRIPT>
+
+<SELECT NAME="<%= $opt{'prefix'} %>country" onChange="<%= $opt{'prefix'} %>country_changed(this); <%= $opt{'onchange'} %>" <%= $opt{'disabled'} %>>
+
+<% foreach my $country (
+     sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
+     map { $_->country }
+     qsearch( 'cust_main_county',{}, 'DISTINCT ON ( country ) *', )
+   ) {
+%>
+
+  <OPTION VALUE="<%= $country %>"<%= $country eq $opt{'country'} ? ' SELECTED' : '' %>><%= $country %>
+
+<% } %>
+
+</SELECT>
+
diff --git a/httemplate/edit/cust_main/select-county.html b/httemplate/edit/cust_main/select-county.html
new file mode 100644 (file)
index 0000000..70a8f94
--- /dev/null
@@ -0,0 +1,87 @@
+<%
+
+  my %opt = @_;
+  foreach my $opt (qw( county state country prefix onchange disabled )) {
+    $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_});
+  }
+
+  my $sql = "SELECT COUNT(*) FROM cust_main_county".
+            " WHERE county IS NOT NULL AND county != ''";
+  my $sth = dbh->prepare($sql) or die dbh->errstr;
+  $sth->execute or die $sth->errstr;
+  my $countyflag = $sth->fetchrow_arrayref->[0];
+
+%>
+
+<% if ( $countyflag ) { %>
+
+  <%= include('/elements/xmlhttp.html', $p.'misc/counties.cgi', $opt{'prefix'}. 'get_counties' ) %>
+  
+  <SCRIPT TYPE="text/javascript">
+  
+    function opt(what,value,text) {
+      var optionName = new Option(text, value, false, false);
+      var length = what.length;
+      what.options[length] = optionName;
+    }
+  
+    function <%= $opt{'prefix'} %>state_changed(what, callback) {
+
+      state = what.options[what.selectedIndex].text;
+      country = what.form.<%= $opt{'prefix'} %>country.options[what.form.<%= $opt{'prefix'} %>country.selectedIndex].text;
+  
+      function <%= $opt{'prefix'} %>update_counties(counties) {
+
+        // blank the current county list
+        for ( var i = what.form.<%= $opt{'prefix'} %>county.length; i >= 0; i-- )
+            what.form.<%= $opt{'prefix'} %>county.options[i] = null;
+  
+        // add the new counties
+        var countiesArray = eval('(' + counties + ')' );
+        for ( var s = 0; s < countiesArray.length; s++ ) {
+            var countyLabel = countiesArray[s];
+            if ( countyLabel == "" )
+                countyLabel = '(n/a)';
+            opt(what.form.<%= $opt{'prefix'} %>county, countiesArray[s], countyLabel);
+        }
+
+        //run the callback
+        if ( callback != null ) 
+          callback();
+      }
+  
+      // go get the new counties
+      <%= $opt{'prefix'} %>get_counties( state, country, <%= $opt{'prefix'} %>update_counties );
+  
+    }
+  
+  </SCRIPT>
+
+  <SELECT NAME="<%= $opt{'prefix'} %>county" onChange="<%= $opt{'onchange'} %>" <%= $opt{'disabled'} %>>
+
+  <% foreach my $county (
+       sort
+       map { $_->county }
+       qsearch('cust_main_county', { 'state'   => $opt{'state'},
+                                     'country' => $opt{'country'},
+                                   }
+              )
+     ) {
+  %>
+
+    <OPTION VALUE="<%= $county %>"<%= $county eq $opt{'county'} ? ' SELECTED' : '' %>><%= $county %>
+
+  <% } %>
+
+  </SELECT>
+
+<% } else { %>
+
+  <SCRIPT TYPE="text/javascript">
+    function <%= $opt{'prefix'} %>state_changed(what) {
+    }
+  </SCRIPT>
+
+  <INPUT TYPE="hidden" NAME="<%= $opt{'prefix'} %>county" VALUE="<%= $opt{'county'} %>">
+
+<% } %>
diff --git a/httemplate/edit/cust_main/select-state.html b/httemplate/edit/cust_main/select-state.html
new file mode 100644 (file)
index 0000000..98e685a
--- /dev/null
@@ -0,0 +1,27 @@
+<%
+
+  my %opt = @_;
+  foreach my $opt (qw( county state country prefix onchange disabled )) {
+    $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_});
+  }
+
+%>
+
+<SELECT NAME="<%= $opt{'prefix'} %>state" onChange="<%= $opt{'prefix'} %>state_changed(this); <%= $opt{'onchange'} %>" <%= $opt{'disabled'} %>>
+
+<% foreach my $state (
+     sort
+     map { $_->state }
+     qsearch( 'cust_main_county',
+              { 'country' => $opt{'country'} },
+              'DISTINCT ON ( state ) *',
+            )
+   ) {
+%>
+
+  <OPTION VALUE="<%= $state %>"<%= $state eq $opt{'state'} ? ' SELECTED' : '' %>><%= $state || '(n/a)' %>
+
+<% } %>
+
+</SELECT>
+
diff --git a/httemplate/elements/xmlhttp.html b/httemplate/elements/xmlhttp.html
new file mode 100644 (file)
index 0000000..425e28e
--- /dev/null
@@ -0,0 +1,65 @@
+<%
+  my ( $url, @subs ) = @_;
+
+  $url .= ( ($url =~ /\?/) ? '&' : '?' ).
+          'sub=';
+
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+  function rs_init_object() {
+    var A;
+    try {
+      A=new ActiveXObject("Msxml2.XMLHTTP");
+    } catch (e) {
+      try {
+        A=new ActiveXObject("Microsoft.XMLHTTP");
+      } catch (oc) {
+        A=null;
+      }
+    }
+    if(!A && typeof XMLHttpRequest != "undefined")
+      A = new XMLHttpRequest();
+    if (!A)
+      alert("Can't create XMLHttpRequest object");
+    return A;
+
+  }
+
+  <% foreach my $func (@subs) { 
+
+       my $furl = $url . uri_escape($func);
+       $furl =~ s/\"/\\\\\"/; #javascript escape
+
+  %>
+
+    function <%=$func%>() {
+       // count args; build URL
+       var url = "<%=$furl%>";
+       var a = <%=$func%>.arguments;
+       for (var i = 0; i < a.length-1; i++) 
+           url = url + "&arg=" + escape(a[i]);
+       url = url.replace( /[+]/g, '%2B'); // fix the unescaped plus signs 
+       var xmlhttp = rs_init_object();
+       xmlhttp.open("GET", url, true);
+       xmlhttp.onreadystatechange = function() {
+           if (xmlhttp.readyState != 4) 
+               return;
+           //rs_debug("received " + x.responseText);
+
+           if (xmlhttp.status != 200) {
+             alert(xmlhttp.status + " status connecting to " + url);
+           } else {
+              var data = xmlhttp.responseText;
+              a[a.length-1](data);
+            }
+        }
+        xmlhttp.send(null);
+        //rs_debug("x_$func_name url = " + url);
+        //rs_debug("x_$func_name waiting..");
+    }
+
+  <% } %>
+
+</SCRIPT>
diff --git a/httemplate/misc/counties.cgi b/httemplate/misc/counties.cgi
new file mode 100644 (file)
index 0000000..80ae616
--- /dev/null
@@ -0,0 +1,17 @@
+<%
+
+  my( $state, $country ) = $cgi->param('arg');
+
+  my @counties = 
+     sort
+     map { s/[\n\r]//g; $_; }
+     map { $_->county; }
+     qsearch( 'cust_main_county',
+              { 'state'   => $state,
+                'country' => $country,
+              },
+            )
+  ;
+
+
+%>[ <%= join(', ', map { qq("$_") } @counties) %> ]
diff --git a/httemplate/misc/states.cgi b/httemplate/misc/states.cgi
new file mode 100644 (file)
index 0000000..cff2c97
--- /dev/null
@@ -0,0 +1,16 @@
+<%
+
+  my $country = $cgi->param('arg');
+
+  my @states = 
+     sort
+     map { s/[\n\r]//g; $_; }
+     map { $_->state; }
+     qsearch( 'cust_main_county',
+              { 'country' => $country },
+              'DISTINCT ON ( state ) *',
+            )
+  ;
+
+
+%>[ <%= join(', ', map { qq("$_") } @states) %> ]