Alaska Software Inc. - Something Yummy from Steffen's kitchen
Username: Password:
AuthorTopic: Something Yummy from Steffen's kitchen
Bruce AndersonSomething Yummy from Steffen's kitchen
on Wed, 04 Mar 2009 14:17:00 -0600
In mid February, Steffen showed a way to use the Prototype.js functions 
withWAA.  It is, hands down, the easiest way to incorporate AJAX into a 
webpage.  Here is an example I have developed.  This shows both a repeating 
call to a function in a WAA dll and a simple pushbutton executed function 
call.  Because the calls are to WAA functions, this gives us a simple way to 
put a window on the web page which is continously updated from the database.

THIS IS THE BASIC PAGE...
<html>
<head>
<title>TESTAJAX</title>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="-1">
<script src='/PROTOTYPE.js' type='text/javascript' ></script>

<script language='JavaScript' type='text/javascript'>
function MAGIC(){
  var url = "/cgi-bin/waa1gate.isa";
  var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO";
  var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
                               url,
                               {method:'post', parameters:params});
}
function MORE_MAGIC(){
  var url = "/cgi-bin/waa1gate.isa";
  var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO2";
  var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
                               url,
                               {method:'post', parameters:params});
}
</script>
</head>
<body onload="setInterval('MAGIC()', 1000);">
<div id="top" >
<p>this is top text</p>
</div>
<div id="ajaxstuff" >
<p>Item will load here...</p>
</div>
<div id="bottom" >
<p>this is bottom text<br />
<input type="button" name="mButton" value="Do More Magic" 
onClick="MORE_MAGIC();" /></p>
</div>
</body>
</html>


THESE ARE THE WAA FUNCTIONS...

FUNCTION  STUFF_INTRO( oHtml, oContext )
   LOCAL  cTime := time()
   oHtml:put([<p>] + cTime + [</p>])
return (.T.)

FUNCTION  STUFF_INTRO2( oHtml, oContext )
   LOCAL  cDate := dtoc(date())
   oHtml:put([<p>] + cDate + [</p>])
return (.T.)
Allen Lee Something Yummy from Steffen's kitchen
on Fri, 06 Mar 2009 18:55:45 -0800
Hi Bruce;
It's exciting to see an uncomplicated AJAX implementation.
Unfortunately, the demo needs the Prototype.js file to work.
Would you mind posting it for us, please?
Bruce AndersonRe: Something Yummy from Steffen's kitchen
on Sat, 07 Mar 2009 09:42:35 -0600
That would be diminuitive of me.  Here are your links.
This is where you can get the file: http://www.prototypejs.org/.  I simply 
shortened the name of the current version of the file.
This is one of the better quick and dirty manuals I have stumbled onto: 
http://www.sergiopereira.com/articles/prototype.js.html.  I found it when I 
went looking for the background on  Ajax.Updater.
Post some example when you plow ahead, please?
Allen Lee Something Yummy from Steffen's kitchen
on Tue, 10 Mar 2009 21:49:29 -0700
Hi Bruce;
Your example works nicely!
Let's say that you want to pass a variable to a new function named 
STUFF_INTRO3

1. So you add this to your header:
function Gofindit(){
  var searchfor = document.formsearch.findthis.value;
  var url = "/cgi-bin/waa1gate.isa";
  var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO3&WAA_VAR=" + 
searchfor ;
  var ajax = new 
Ajax.Updater({success:"ajaxstuff"},url,{method:"post",parameters:params});
{

2. And you add this to your body:
<form name="formsearch" action="">
<p>Search inventory for:
<input name="findthis" size="15" type="text">
<input type="button" value="Go Find It" onClick="Gofindit()">
</form>

3. And you add this to the WAA functions:
function  STUFF_INTRO3( oHtml, oContext )
  local  cVar := oHtml:getVar('WAA_PAR')
  if valtype(cVar)='U'
   oHtml:put([<p>The variable cVar is undefined</p>])
  else
   oHtml:put([<p>The variable is ] + cVar + [</p>])
  endif
  return (.T.)

The result is that cVar is undefined.
Without studying Prototype.js, will it allow you to pass variables?

Bruce Anderson wrote:
> In mid February, Steffen showed a way to use the Prototype.js functions 
> withWAA.  It is, hands down, the easiest way to incorporate AJAX into a 
> webpage.  Here is an example I have developed.  This shows both a 
> repeating call to a function in a WAA dll and a simple pushbutton 
> executed function call.  Because the calls are to WAA functions, this 
> gives us a simple way to put a window on the web page which is 
> continously updated from the database.
> 
> THIS IS THE BASIC PAGE...
> <html>
> <head>
> <title>TESTAJAX</title>
> <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
> <META HTTP-EQUIV="Expires" CONTENT="-1">
> <script src='/PROTOTYPE.js' type='text/javascript' ></script>
> 
> <script language='JavaScript' type='text/javascript'>
> function MAGIC(){
>  var url = "/cgi-bin/waa1gate.isa";
>  var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO";
>  var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
>                               url,
>                               {method:'post', parameters:params});
> }
> function MORE_MAGIC(){
>  var url = "/cgi-bin/waa1gate.isa";
>  var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO2";
>  var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
>                               url,
>                               {method:'post', parameters:params});
> }
> </script>
> </head>
> <body onload="setInterval('MAGIC()', 1000);">
> <div id="top" >
> <p>this is top text</p>
> </div>
> <div id="ajaxstuff" >
> <p>Item will load here...</p>
> </div>
> <div id="bottom" >
> <p>this is bottom text<br />
> <input type="button" name="mButton" value="Do More Magic" 
> onClick="MORE_MAGIC();" /></p>
> </div>
> </body>
> </html>
> 
> 
> THESE ARE THE WAA FUNCTIONS...
> 
> FUNCTION  STUFF_INTRO( oHtml, oContext )
>   LOCAL  cTime := time()
>   oHtml:put([<p>] + cTime + [</p>])
> return (.T.)
> 
> FUNCTION  STUFF_INTRO2( oHtml, oContext )
>   LOCAL  cDate := dtoc(date())
>   oHtml:put([<p>] + cDate + [</p>])
> return (.T.)
> 
>
Bruce AndersonRe: Something Yummy from Steffen's kitchen
on Wed, 11 Mar 2009 07:58:50 -0500
You have a typo.  
In the Ajax call, you name the passed var "WAA_VAR".  
In STUFF_INTRO3, you ask for "WAA_PAR".   
Correct this and it works.
Les C. Cseh (ASAP Checks)Re: Something Yummy from Steffen's kitchen
on Fri, 13 Mar 2009 17:42:22 -0400
Thanks for these great examples! I hope to experiment with this in the 
coming weeks.

Les
Bruce AndersonRe: Something Yummy from Steffen's kitchen
on Sat, 14 Mar 2009 12:19:24 -0500
Here is one more example.  This is a cascade of three <select> controls, 
country->customer->product, where the options can depend on the previous 
select, just insert the appropriate database coding.

Bruce Anderson
Graphical Database Programs


TESTAJAX.2a.txt
Allen Lee Something Yummy from Steffen's kitchen
on Mon, 16 Mar 2009 09:14:28 -0700
Thank you, Bruce.
I was working on the same implementation except using tables with 
buttons and onclick events on every row.
However, your example is very impressive!
I congratulate you, sir, for opening a new area of WAA developement.
After a couple of futile years of casually researching ajax, I had not 
seen anything as elegant as your examples.
How did you discover this?

Bruce Anderson wrote:
> Here is one more example.  This is a cascade of three <select> controls, 
> country->customer->product, where the options can depend on the previous 
> select, just insert the appropriate database coding.
> 
> Bruce Anderson
> Graphical Database Programs #include "CDXDBE.CH"
> #include "COLLAT.CH"
> #include "COMMON.CH"
> #include "DBFDBE.CH"
> #include "DBSTRUCT.CH"
> #include "DLL.CH"
> #include "DMLB.CH"
> #include "ERROR.CH"
> #include "FILEIO.CH"
> #include "FONT.CH"
> #include "FOXDBE.CH"
> #include "INKEY.CH"
> #define CRLF  chr(13)+chr(10)
> 
> FUNCTION _register( oPackage )
>   LOCAL  oDlg
>   LOCAL  oList
>   LOCAL  oStat
>   LOCAL  oStat2
> 
>   oPackage:registerForm( "INTRO_TESTAJAX" )
>   oPackage:registerForm( "STUFF_CUSTOMER" )
>   oPackage:registerForm( "STUFF_PRODUCT" )
>   oPackage:registerForm( "NOTDEF" )
>   oPackage:registerForm( "SAVE_TESTAJAX" )
> 
> RETURN .T.
> 
> FUNCTION _version()
> RETURN "Version 2.5"
> 
> FUNCTION _Copyright()
> RETURN  "Graphical Database Programs, (c) 2008. All rights reserved."
> 
> FUNCTION  INTRO_TESTAJAX( oHtml, oContext )
>   LOCAL  aCountries := { "", "England", "France", "Germany", "USA" }
>   LOCAL  n
>   LOCAL  cId
>   LOCAL  cValue
> 
>   oHtml:put( strtran( doctype(), "$1$", procname()))
>   oHtml:put([<script language='JavaScript' type='text/javascript'>])
> 
>   oHtml:put([function SET_CUSTOMER( country ){])
>   oHtml:put([  var url = "/cgi-bin/waa1gate.isa";])
>   oHtml:put([  var params = 
> "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_CUSTOMER&COUNTRY=" + country; ])
>   oHtml:put([  var ajax = new Ajax.Updater( {success: 'customer'},])
>   oHtml:put([                               url,                   ])
>   oHtml:put([                               {method:'post', 
> parameters:params});])
>   oHtml:put([  params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=NOTDEF"; ])
>   oHtml:put([  ajax = new Ajax.Updater( {success: 'product'},])
>   oHtml:put([                           url,                   ])
>   oHtml:put([                           {method:'post', 
> parameters:params});])
>   oHtml:put([}])
> 
>   oHtml:put([function SET_PRODUCT( customer ){])
>   oHtml:put([  var url = "/cgi-bin/waa1gate.isa";])
>   oHtml:put([  var params = 
> "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_PRODUCT&CUSTOMER=" + customer; ])
>   oHtml:put([  var ajax = new Ajax.Updater( {success: 'product'},])
>   oHtml:put([                               url,                   ])
>   oHtml:put([                               {method:'post', 
> parameters:params});])
>   oHtml:put([}])
> 
>   oHtml:put([</script>])
>   oHtml:put([</head>])
>   oHtml:put([<body>])
>   oHtml:put([<form name="fS" id="fS" onsubmit="return false" 
> ACTION="/cgi-bin/waa1gate.isa" METHOD=POST>])
>   oHtml:put([<input type="hidden" name="WAA_PACKAGE" id="WAA_PACKAGE" 
> value="TESTAJAX" /> ])
>   oHtml:put([<input type="hidden" name="WAA_FORM" id="WAA_FORM" 
> value="SAVE_TESTAJAX" />])
> 
>    country select
>   oHtml:put([<div id="top" >])
>      oHtml:put([Pick a Country&nbsp;])
>      oHtml:put([<select name="COUNTRY" id="COUNTRY" size="1" 
> onChange="SET_CUSTOMER( this.value );" >])
>         for n := 1 to len( aCountries )
>            cId := aCountries[n]
>            cValue := aCountries[n]
>            oHtml:put([<option value="] + cId + [">] + cValue )
>         next
>      oHtml:put([</select>])
>   oHtml:put([</div>])
> 
>    customer select
>   oHtml:put([<div id="customer" >])
>      oHtml:put([<p>Customer Select will display here...</p>])
>   oHtml:put([</div>])
> 
>    product select
>   oHtml:put([<div id="product" >])
>      oHtml:put([<p>Product Select will display here...</p>])
>   oHtml:put([</div>])
> 
>   oHtml:put([<div id="bottom" >])
>   oHtml:put([<input type="button" name="SAVE" id="SAVE" value="CONTINUE" 
> onClick="document.getElementById('fS').submit();" />])
>   oHtml:put([</div> ])
>   oHtml:put([</body>])
>   oHtml:put([</html>])
> return (.T.)
> 
> FUNCTION  STUFF_CUSTOMER( oHtml, oContext )     this is the function 
> called by Ajax.Updater
>   LOCAL  aCustomers
>   LOCAL  cCountry
>   LOCAL  cId
>   LOCAL  cValue
>   LOCAL  n
> 
>   cCountry := oHtml:getVar("COUNTRY" )
>   aCustomers := { "AACME", "BACME", "CACME", "DACME"}
> 
>   oHtml:put([Now, pick a Customer from ] + cCountry + [&nbsp;])
>   oHtml:put([<select name="CUSTOMER" id="CUSTOMER" size="1" 
> onChange="SET_PRODUCT( this.value );">])
>      for n := 1 to len( aCustomers )
>         cId := aCustomers[n]
>         cValue := aCustomers[n]
>         oHtml:put([<option value="] + cId + [">] + cValue )
>      next
>   oHtml:put([</select>])
> return (.T.)
> 
> FUNCTION  STUFF_PRODUCT( oHtml, oContext )     this is the function 
> called by Ajax.Updater
>   LOCAL  aProducts
>   LOCAL  cCustomer
>   LOCAL  cId
>   LOCAL  cValue
>   LOCAL  n
> 
>   cCustomer := oHtml:getVar("CUSTOMER" )
>   aProducts := { "AWIDGET", "BWIDGET", "CWIDGET", "DWIDGET"}
> 
>   oHtml:put([Now, pick a Product for ] + cCustomer + [&nbsp;])
>   oHtml:put([<select name="PRODUCT" id="PRODUCT" size="1" >])
>      for n := 1 to len( aProducts )
>         cId := aProducts[n]
>         cValue := aProducts[n]
>         oHtml:put([<option value="] + cId + [">] + cValue )
>      next
>   oHtml:put([</select>])
> return (.T.)
> 
> FUNCTION  NOTDEF( oHtml, oContext )
>   oHtml:put([<p>Product Select will display here...</p>])
> return (.T.)
> 
> FUNCTION  SAVE_TESTAJAX( oHtml, oContext )
>   LOCAL  aValues
>   LOCAL  n
>   LOCAL  cField
>   LOCAL  cValue
>   oHtml:put( strtran( doctype(), "$1$", procname()))
>   oHtml:put([</head>])
>   oHtml:put([<body>])
>   aValues := oHtml:getAllVars()     each element of aValues is { "var 
> name", {array of var values} }
>   for n := 1 to len( aValues )
>      cField := aValues[n][1]
>      cValue := aValues[n][2][1]
>      oHtml:put([<p>] + cField + " = " + cValue + [</p>])
>   next
>   oHtml:put([</body>])
>   oHtml:put([</html>])
> return (.T.)
> 
> FUNCTION  DOCTYPE()
> return ([<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"] 
> + CRLF + ;
>        ["http://www.w3.org/TR/html4/loose.dtd">] + CRLF + ;
>        [<html>] + CRLF + ;
>        [<head>] + CRLF + ;
>        [<link type='text/css' href='/TESTAJAX.css' rel=stylesheet>] + 
> CRLF + ;
>        [<title>$1$</title>] + CRLF + ;
>        [<META HTTP-EQUIV="Pragma" CONTENT="no-cache">] + CRLF + ;
>        [<META HTTP-EQUIV="Expires" CONTENT="-1">] + CRLF + ;
>        [<script src='/PROTOTYPE.js' type='text/javascript' ></script>] + 
> CRLF )
> 
>
Anand Kumar GuptaRe: Something Yummy from Steffen's kitchen
on Thu, 19 Mar 2009 12:39:50 +0530
Bruce

Is there a possibility that you can host the demo as well ?

I would love to see the demo as well. Since I have not worked on WAA at-all 
(other then playing with it when it was initially launched with 1.3 or so).

Sorry for bothering you !

Anand

"Bruce Anderson" <banderson@graphical-db.com> wrote in message 
news:7f34658a$76b8ec2c$e9e@news.alaska-software.com...
> Here is one more example.  This is a cascade of three <select> controls,
> country->customer->product, where the options can depend on the previous
> select, just insert the appropriate database coding.
>
> Bruce Anderson
> Graphical Database Programs
>
Bruce AndersonRe: Something Yummy from Steffen's kitchen
on Thu, 19 Mar 2009 09:10:41 -0500
I will work up something and put it on my server.  Should not take too long. 
Stay tuned.
Boris Borzic Re: Something Yummy from Steffen's kitchen
on Wed, 01 Apr 2009 21:33:32 +0200
Bruce,

Here is an Xb2.NET version of your simple AJAX sample:
Assuming xb2.net webserver is running on same machine, url to react test 
function is: http://localhost/ajaxtest?

PROCEDURE WEB_AjaxTest( nAction )
Static cHtml
Local cResponse

if cHtml == nil
   TEXT INTO cHtml TRIMMED
   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   <html>
   <head>
   <title>Xb2.NET AJAX Test</title>
   <script src='prototype.js' type='text/javascript' ></script>
   <script language='javascript' type='text/javascript'>

   function ServerTime(){
     var ajax = new Ajax.Updater({success: 'ajaxstuff'},
      'AjaxTest(1)?', {method:'get', parameters:''});
   }

   function ServerDate(){
     var ajax = new Ajax.Updater({success: 'ajaxstuff'},
      'AjaxTest(2)', {method:'post'});
   }
   </script>
   </head>

   <body onload="setInterval('ServerTime()', 1000);">
   <div id="top" ><br>this is top text</div>
   <div id="ajaxstuff" ><br>Ajax data will load here...</div>
   <div id="bottom" ><br>this is bottom text
   <input type="button" value="get server date" onclick="ServerDate()" />
   </div>
   </body>
   </html>
   ENDTEXT
endif

do case
case empty(nAction) .or. valtype(nAction) != "N"
   cResponse := cHtml
case nAction == 1
   cResponse := "<br>" + time()
case nAction == 2
   cResponse := "<br>" + dtos(date())
endcase

ThreadObject():HTTPResponse:Content := cResponse
Return

Best regards,
Boris Borzic

http://xb2.net
http://sqlexpress.net
industrial strength Xbase++ development tools


"Bruce Anderson" <banderson@graphical-db.com> wrote in
news:7b652302$492fcae5$1ee@news.alaska-software.com: 

> In mid February, Steffen showed a way to use the Prototype.js
> functions withWAA.  It is, hands down, the easiest way to incorporate
> AJAX into a webpage.  Here is an example I have developed.  This shows
> both a repeating call to a function in a WAA dll and a simple
> pushbutton executed function call.  Because the calls are to WAA
> functions, this gives us a simple way to put a window on the web page
> which is continously updated from the database. 
> 
> THIS IS THE BASIC PAGE...
><html>
><head>
><title>TESTAJAX</title>
><META HTTP-EQUIV="Pragma" CONTENT="no-cache">
><META HTTP-EQUIV="Expires" CONTENT="-1">
><script src='/PROTOTYPE.js' type='text/javascript' ></script>
> 
><script language='JavaScript' type='text/javascript'>
> function MAGIC(){
>   var url = "/cgi-bin/waa1gate.isa";
>   var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO";
>   var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
>                                url,
>                                {method:'post', parameters:params});
> }
> function MORE_MAGIC(){
>   var url = "/cgi-bin/waa1gate.isa";
>   var params = "WAA_PACKAGE=TESTAJAX&WAA_FORM=STUFF_INTRO2";
>   var ajax = new Ajax.Updater( {success: 'ajaxstuff'},
>                                url,
>                                {method:'post', parameters:params});
> }
></script>
></head>
><body onload="setInterval('MAGIC()', 1000);">
><div id="top" >
><p>this is top text</p>
></div>
><div id="ajaxstuff" >
><p>Item will load here...</p>
></div>
><div id="bottom" >
><p>this is bottom text<br />
><input type="button" name="mButton" value="Do More Magic" 
> onClick="MORE_MAGIC();" /></p>
></div>
></body>
></html>
> 
> 
> THESE ARE THE WAA FUNCTIONS...
> 
> FUNCTION  STUFF_INTRO( oHtml, oContext )
>    LOCAL  cTime := time()
>    oHtml:put([<p>] + cTime + [</p>])
> return (.T.)
> 
> FUNCTION  STUFF_INTRO2( oHtml, oContext )
>    LOCAL  cDate := dtoc(date())
>    oHtml:put([<p>] + cDate + [</p>])
> return (.T.)
Phil Ide
Re: Something Yummy from Steffen's kitchen
on Thu, 02 Apr 2009 17:49:11 +0100
Bruce, can I point out that using AJAX in xbHCL is extremely trivial, and
the code is portable between WAA and Xb2.NET.  Additionally, since the web
page is created dynamically at runtime, you can change both the (static)
contents of the page and the AJAX functions called by the page to suit the
user (e.g. language, frequency of ajax refresh etc.).

Since xbHCL incorporate the entire HTML v4.x syntax into Xbase++, plus
allows you to code your jscript routines within your Xbase++ code, you can
actually generate a bespoke jscript routine for individual users.

All this and the same code runs without changes in WAA and Xb2.NET

Regards,

Phil Ide

---------------------
www.xbhcl.com
www.pbih.eu
www.idep.nl
---------------------
This Tagline for Sale
Brian L. WolfsohnRe: Something Yummy from Steffen's kitchen
on Thu, 02 Apr 2009 21:28:29 +0200
Phil Ide <phil@pbih.eu> wrote in
news:1q2m0uqo2wam3$.185r8u4f69wxq.dlg@40tude.net: 

> Bruce, can I point out that using AJAX in xbHCL is extremely trivial,
> and the code is portable between WAA and Xb2.NET.  Additionally, since
> the web page is created dynamically at runtime, you can change both
> the (static) contents of the page and the AJAX functions called by the
> page to suit the user (e.g. language, frequency of ajax refresh etc.).
> 
> Since xbHCL incorporate the entire HTML v4.x syntax into Xbase++, plus
> allows you to code your jscript routines within your Xbase++ code, you
> can actually generate a bespoke jscript routine for individual users.
> 
> All this and the same code runs without changes in WAA and Xb2.NET

Phil,

It's really great to have you "back in the saddle" so to speak..  

I've looked at your ajax stuff in the past, and we still have a need for 
it in our system.  What's the status on pushing info only if it's needed 
i.e. only if the info has changed ??

best regards,

Brian
Thomas Braun
Re: Something Yummy from Steffen's kitchen
on Fri, 03 Apr 2009 09:47:28 +0200
Brian L. Wolfsohn wrote:

> I've looked at your ajax stuff in the past, and we still have a need for 
> it in our system.  What's the status on pushing info only if it's needed 
> i.e. only if the info has changed ??

I don't think this is something AJAX can be used for - "pushing" implies
the server sending information to the browser without getting a request
first... which is impossible with HTTP, as it is "stateless". 

The server gets a request, sends the answer and that's it.

The user might have closed his browser meanwhile, even the IP address might
have changed (yes, there are ISPs where the external IP changes while being
connected) - let alone all kinds of problems with NATting routers and
firewalls between your server and the client. So (with HTTP) there is no
way how this probably could work.

AJAX could be used in this context to periodically send a request from the
browser to ask the server for changed data.

There are even javascript frameworks where you can define that the
intervals between polls is increased when no new data is received for a
defineable period. (I hope it is clear what I mean 

regards
thomas
Boris Borzic Re: Something Yummy from Steffen's kitchen
on Fri, 03 Apr 2009 15:21:24 +0200
Thomas Braun <spam@software-braun.de> wrote in
news:16hwtzls81epj$.m4ghuimnvzi4.dlg@40tude.net: 

> I don't think this is something AJAX can be used for - "pushing"
> implies the server sending information to the browser without getting
> a request first... which is impossible with HTTP, as it is
> "stateless". The server gets a request, sends the answer and that's
> it. 

Yes, but there is nothing to say that you can't keep the "answer"
coming. The trick is to not terminate the response, but rather keep
sending small bits of data using chunked transfer encoding. 

There are two examples included with Xb2.NET showing how to do that. If 
you have Xb2.NET, run the sample WEBSERVE.EXE server and navigate to the 
following URL's:

http://localhost/SendChunks?
http://localhost/ProgressBar?

> The user might have closed his browser meanwhile, even the IP address
> might have changed (yes, there are ISPs where the external IP changes
> while being connected) - let alone all kinds of problems with NATting
> routers and firewalls between your server and the client. So (with
> HTTP) there is no way how this probably could work.

This is no problem. As long as the connection is kept active, the IP will 
not change. Firewalls are also no problem since this is standard HTTP. 

If the user closes his browser, pushes the stop button, unplugs the 
network cable, etc... then the Xb2.NET server will receive a socket error 
as soon as the next chunk is sent. This is your signal to stop sending 
chunks and terminate the worker thread.

> AJAX could be used in this context to periodically send a request from
> the browser to ask the server for changed data.

This is the traditional way because it is difficult to continualy send 
chunked data in a ISAPI or CGI (not sure it's possible) type of 
application. With Xb2.NET, this is trivial because your application has a 
direct socket connection to the client. Here's the SendChunks sample:

//------------------------
PROCEDURE WEB_SendChunks()
Local i, cHtml, oResp

 get reference to current xbHTTPResponse object. ThreadObject()
 will return a reference to the current xbHTTPThread object
oResp := ThreadObject():HTTPResponse

TEXT INTO cHtml TRIMMED
   <html><head>
   <title>Xb2.NET - Sending Chunks</title>
   <link rel="stylesheet" type="text/css" href="Style.css"></head>
   <body>
   <b>
   This sample shows how to send dynamic content in discrete chunks.<br>
   The server will pause for one second after sending each chunk of data:
   </b>
   <br><br><ul>
ENDTEXT

 add a transfer-encoding header field and set it to "chunked"
oResp:TransferEncoding('chunked')

 send the message header (the content will be sent later)
oResp:Send()

 send first chunk
oResp:SendChunk(cHtml)

for i := 1 to 10
   if oResp:SendChunk('This is chunk # '+NTrim(i)+'<br>') == SOCKET_ERROR
       connection terminated
      Return
   endif
    pause for one second before sending the next chunk
   sleep(100)
next

oResp:SendChunk('</ul><br><hr>All done</body></html>')
 send an empty chunk to signal end of content
oResp:SendChunk()
Return

Best regards,
Boris Borzic

http://xb2.net
http://sqlexpress.net
industrial strength Xbase++ development tools
Phil Ide
Re: Something Yummy from Steffen's kitchen
on Tue, 14 Apr 2009 16:28:38 +0100
There you go, two different ways to do it, each with their own benefits.

Incidentally, Boris' technique of using chunked transfers is particularly
useful when viewing a scrolling 'window' of a dataset.

Regards,

Phil Ide

---------------------
www.xbhcl.com
www.pbih.eu
www.idep.nl
---------------------
What do you mean, you formatted the cat?