<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>curlr articles</title>
    <link>http://www.curlr.org/</link>
    <description>Connecting the web with Curl</description>
    <language>en-us</language>           
    <generator>Nucleus CMS v3.23</generator>
    <copyright>©</copyright>             
    <category>Weblog</category>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>http://www.curlr.org//nucleus/nucleus2.gif</url>
      <title>curlr articles</title>
      <link>http://www.curlr.org/</link>
    </image>
    <item>
 <title>CurlrObject - Curl RTE detection</title>
 <guid>http://www.curlr.org/index.php?itemid=43</guid>
 <link>http://www.curlr.org/index.php?itemid=43</link>
<description><![CDATA[<p>Once you've taken the trouble to write a Curl applet, there's a good 
chance you'll want to include it in a web page; at that point you'll need some way to 
detect the Curl RTE and check its version on different platforms and browsers.
</p>

<p>If you've ever included a Flash movie on a page this will be a familiar issue to you. 
The question of how best to embed the Flash plugin has a long history - the various browsers have made plugin 
detection notoriously awkward, but over the years a standard practice of sorts, has developed to the point 
where you can find numerous helpful scripts, and Adobe now provide a 
<a href="http://www.adobe.com/products/flashplayer/download/detection_kit/" target="_blank">Flash Player Detection kit</a> along
with guidelines for detection.
</p>

<p>Unfortunately, Curl doesn't provide an equivalent script for doing this so you've 
been on your own - until now, that is. I've written some Javascript that will do the 
job for you, based on the best example I could find for Flash - <a href="http://blog.deconcept.com/swfobject/">SWFObject script </a>
developed by Geoff Stearns. In the spirit of the original, I've called it <tt>CurlrObject</tt>, and it is a 
Javascript-only detection, avoiding the use of VBasic on PCs.
As an added bonus, I've included in the script a way of 
specifying parameters for the applet - to answer another frequently asked question.
</p>

<p>If you're in a hurry you can <a href="http://www.curlr.org/curl/tutorials/CurlrObject/curlrobject_source.js" target="_blank">view the code</a> and you can download it from the <a href="http://www.curlr.org/nusw/code.php">Code pag</a>e. Read on for usage notes.</p>
<p><tt>CurlrObject</tt> is surprisingly easy to use. Just include the script file in your web page, add a couple of lines of 
Javascript and identify a HTML element to contain the Curl applet. For example:</p>

<textarea rows = "15" cols = "80">
<script type="text/javascript" src="curlrobject_source.js"></script>
...

<div id="curlrcontent">
<bold>You need v5.0 of Curl to run this application.</bold>
</div>

<script language ="JavaScript">
  var co = new CurlrObject("sampleCurlrObject.curl", "curlrcontent", "100%", "100%", "5.0");
  co.write("curlrcontent");
</script>

</textarea>

<p>The parameters for <tt>CurlrObject</tt> are:<p>
<table class="downloadsTable" width="50%">
<tr>
<th>parameter</th>
<th>Description</th>
</tr>
<tr>
<td>appletUrl</td>
<td>The location of the Curl applet to be run.</td>
</tr>
<td>width</td>
<td>The width of the Curl applet</td>
</tr>
<tr>
<td>height</td>
<td>The height of the Curl applet</td>
</tr>
<tr>
<td>version</td>
<td>The required version of the Curl RTE</td>
</tr>
<tr>
<td>id</td>
<td>(optional) The id to be used for the embed tag.</td>
</tr>
<tr>

<tr>
<td>redirectUrl</td>
<td>(optional) The location for the RTE install. Defaults to
http://www.curl.com/download/index.php.</td>
</tr>
</table>
<p>Like the SWFObject the net result of successful detection is to place and populate an <tt>&lt;embed&gt;</tt> node
into the target element. In other respects <tt>CurlrObject</tt> has behavior that is a little different from SWFObject; in particular
 if the script cannot find the Curl RTE
it writes a message to the target HTML element that includes a link to the Curl download
page - or to the redirectUrl. Other diiferences are: the <tt>id</tt> parameter is optional, and no <tt>detectkey</tt> 
is specifiable. 

<p>One reason you may want to use a different <tt>redirectUrl</tt> from the default arises when require an older version of the RTE
than is current - these are found at a <a href="http://www.curl.com/download/old_rte/" target="_blank">different location</a>
 on the Curl web site.</p>

If the browser has disabled the RTE a different message will be written - but I haven't yet 
found a way to distinguish between these two situations in IE.</p>
<p>If you're familiar with Javascript, then you will probably feel comfortable customizing the functions
<tt>getINSTALLCURLHTML</tt> and <tt>getDISABLEDCURLHTML</tt> to change the message or the behavior of the script
when the RTE is not found.</p>


<h2>Specifying parameters for the Curl applet.</h2>
<p>You can use the <tt>CurlrObject</tt> to specify startup parameters for the applet; e.g.</p>
<textarea rows = "7" cols = "80">
<script language ="JavaScript">
  var co = new CurlrObject("hello.curl", "curlrcontent", "100%", "100%", "5.0");
  co.addVariable("testvar1", "value 1");
  co.addVariable("testvar2", "value 2");
  co.addVariable("testvar3", "value 3");
  co.write("curlrcontent");
</script>

</textarea>

<p>This builds a query args list and attaches it to the applet Url. To extract the name value pairs in your
Curl applet you'll need code like this procedure, which constructs an Arguments object from the
applet's query arguments.</p>
<textarea rows = "15" cols = "80">
{define-proc public {get-query-args query-url:Url}:Arguments
    || the returned arguments
    let ret-args:Arguments = {Arguments}
    || A read/write buffer for the query string - including the "?"
    let query-str:StringBuf = {StringBuf query-url.query}

    {if query-str.size <= 0 then {return ret-args}}
    
    || remove the '?'
    {query-str.remove 0}
    || split at the '&'
    let nv-pairs:StringArray = {query-str.split split-chars="&"}
    || build the argument list
    {for nv in nv-pairs do
        || nv has the form name = value - split at the '='
        let pair:StringArray = {nv.split split-chars="="}
        || add name and value to the argument list
        {ret-args.append
            {pair[1].trim-clone},
            keyword = {url-decode-string {pair[0].trim-clone} }
        }
    }
    || return the result
    {return ret-args}
}
</textarea>

<p>To see it in action you can run the <a href="http://www.curlr.org/curl/tutorials/CurlrObject/sampleCurlrObject.html" target="_blank"><b>sample page</b></a> with three Curl applets requiring different RTE versions. (You don't have to install all the versions if you don't want to).</p>

<p>I've done some basic testing - but I'm not at all sure if the case of the RTE installed but not enabled works on
all browsers - it doesn't in IE. If you use it and find problems or have improvements
to suggest, let me know.</p>]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=43</comments>
 <pubDate>Thu, 14 Sep 2006 19:31:17 -0400</pubDate>
</item><item>
 <title>Using sadXMLHttpRequest</title>
 <guid>http://www.curlr.org/index.php?itemid=39</guid>
 <link>http://www.curlr.org/index.php?itemid=39</link>
<description><![CDATA[<p>Ajax customarily means Asynchronous Javascript and XML - and its increasing use has led to a more widespread appreciation of what can
be achieved with a rich internet application. The "asynchronous" and "xml" is often provided by the XMLHttpRequest object provided by a browser 
or as an ActiveX control, and there are planty of references for building applications with Ajax. Though the "A" and the "X" can be obtained
in other technologies used to build web applications,  there doesn't seem to yet be an attempt to broaden the term to, say, Awax (Asynchronous Web Applications
using XML - or Ajax Without Javascript). Since we're using Curl, not javascript, and since Acax doesn't exacly roll off the tongue, I'll be using <a href="http://www.curlr.org/nusw/index.php?itemid=40">Awax</a>
from now on to mean an Ajax-like application without javascript.</p>
<br/>
<p>The curlr package <code>COM.CURLR.PACKAGE</code> provides a basic techology stack that can get you started building Awax applications with Curl. 
Previously I introduced the XML parser and the XML node classes and showed their use in reading and transforming an XML stream into a 
searchable hierarchy. In this article I'll examine the classes implementing the XML DOM and a Curl implementation of the XMLHttpRequest 
functionality and give tutorial on their use.</p>
<br/>
<p>The reason for my providing this object is to enable some of the familiar and increasingly widespread Ajax programming patterns to 
be implemented using Curl with vey similar code. So similar, in fact that we can adapt the standard Ajax tutorial that's used to 
introduce the XMLHttpRequest object with very little change.</p>
<h2><code>sadXMLHttpRequest tutorial</code></h2>
<p>The basic tutorial shows the use of the asynchronous part of Awax, and provides a programming template for
using the <code>sadXMLHttpRequest</code> object. Obviously there are many variations that you could implement using Curl - </p>
<dl>
<dt>Step 1: Construct the XMLHttpRequestObject</dt>
<dd> First you are taught to construct the object. This is trivial in Curl code, and of course is 
browser-independent:<br />
<textarea rows="2" cols="80">
    || Step 1. Create a sadXMLHttpRequest object
    let xmlhttp:sadXMLHttpRequest = {new sadXMLHttpRequest}
</textarea>
</dd>
<dt>Step 2: Create Event handler</dt>
<dd>Next you are told to create an event handler for your page to be called as a result of a user action
and which will invoke methods on the XMLHttpREquest object. Curl applets do not usually consist of a single page 
 - though this and other tutorials do - and event handlers can be placed on any Curl GUI object displayed in the 
graphics hierarchy. for the purpose of this tutorial, we will create a button that will initiate a 
request for the headers of a page:<br />
<textarea rows="8" cols="80">
    || Step 2. Create an event handler that will issue a request 
    let request-button:CommandButton =
        {CommandButton
            label="get headers",
            {on Action do
                {xmlhttp.open HttpRequestMethod.head, {url "http://www.curlr.org/"}, async? = true}
            }
        }
</textarea>
</dd>
<dt>Step 3: Define state change actions.</dt>
<dd>The third step is to declare the set of actions to be performed when the state of the XMLHttpRequest object
changes. This is the "Asynchronous Javascript" part of Ajax- since we are using Curl, its the asynchronous web application. <br />
<textarea rows="24" cols="80">
    || Step 3. Create a procedure to be called when the state of the sadXMLHttpRequest object changes.
    || first make a Frame to put the returned headers
    let display-frame:Frame = {Frame}
    set xmlhttp.onreadystatechange =
        || We could also use a static procedure
        {proc {}:void
            || use a switch statement on the state
            {switch xmlhttp.readyState
             case readyStateEnum.Open do
                || we could make the send call immediately after the open call,
                || doing it here shows the handling of the state change
                {xmlhttp.send}
             case readyStateEnum.Loaded do
                {if-non-null hdrs = {xmlhttp.getAllResponseHeaders} then
                    || headers are returned as a string
                    || show them in the frame
                    {display-frame.add replace? = true,
                        {pre {value hdrs}}
                    }
                }
            }            
        }
</textarea>
</dd>
</dl>
<p>Complete the example with a display consiting of the button and the display frame.</p>
<textarea rows="5" cols="80">
    || display the command button and the Frame
    {VBox
        request-button,
        display-frame
    }
</textarea>
<br />
<p>Note that the tutorial uses a common pattern for changing part of the display of an application - the <code>Frame</code> container
is placed where the dynamic content is to appear, and the method <code>add</code> is used with the parameter 
<code>replace?=true</code>.</p>
<p><b><a href="http://www.curlr.org/curl/tutorials/xmlhttprequest-tutorial.curl" target="_blank" >tutorial app</a></b> - this runs the tutorial application in a new window - click the button to display the headers - </p> 

<p>The curlr implementation of the <code>XMLHttpRequest</code> object mostly follows the W3C spec - the source file details the differences,
but they are summarized as follows:</p>
<ul>
	<li><code>readyState</code> is defined from the enumeration <code>readyStateEnum</code>.</li>
    <li><code>onreadystatechange</code> is called when the <code>readyState</code> setter is called - not when a DOM event is raised.</li>
	<li> the check for xml content is made by searching the mime-type for the string "xml"</li>
	<li> uses a look-up table to find the <code>statusText</code></li>
	<li> raises excpetion sadBadStateException for unexpected <code>readyState</code></li>
	<li> does not support Http PUT and DELETE, does not support <code>userinfo</code>.</li>
</ul>
<h2>Using XML and sadXMLDom</h2>
<p>The pattern we've just seen enables application behavior to be determined by the change in state of the
XMLHttpRequest object. The interesting usage starts when the data returned by the request is in the
form of XML.</p>

<p>The simple <code>sadXMLDom</code> provides a XML Document Object Model, with a root <code>sadXMLNode</code> through which the elements
can be accessed. The sadXMLDom object also has a sadXMLParser object, which operates on thr Url or StringBuf defined
as input to one of the factories of sadXMLDom; this can make the code less verbose than using the parser directly. 
For example, the feed code of the previous article could be written 
using the sadXMLDom as</p>
<textarea rows="12" cols="80">
    || Url for the Flickr Central group RSS feed
    let rss-url:Url = ...
    
    || create a DOM from the Url
    let xmldom:sadXMLDom =
        {sadXMLDom.from-location rss-url}
        
    || get the title nodes
    let item-nodes:{Array-of sadXMLNode} = 
        {xmldom.getElementsByTagName "item"}

	...
</textarea>

<p>When the <code>sadXMLHttpRequest</code> object is used to query an  XML source the <code>responseXML</code> accessor prodives the parsed 
document as a <code>sadXMLDom</code> which can be used by the <code>onreadystatechange</code>
handler, e.g.</p>
<textarea rows="4" cols="80">
	...		
    case readyStateEnum.Loaded do
        let xmldom:#sadXMLDom = xmlhttp.responseXML
		...
</textarea>

<h2>A Simple Flickr RSS Viewer</h2>
<p>With the information we now have about <code>sadXMLHttpRequest</code> and <code>sadXMLDom</code> we can put together an example 
of a RSS viewer.</p>

<p>We'll build the viewer as a simple Curl Dialog (see code page) to which we can add a Commit action, and which has these three imterfaces
</p><dl>
    <dt>current-feed-url - the Url of the selected feed</dt>
    <dd></dd>
    <dt>display-feed - a method that takes an sadXMLDom to pouplate the display</dt>
    <dd></dd>
    <dt>display-text - method which will display an input string</dt>
    <dd></dd>
</dl> 


<p>Using this object we can write code very similar in structure to the
tutorial, to create the viewer:</p>

<textarea rows="30" cols="80">
    || Step 1. Create a sadXMLHttpRequest object
    let xmlhttp:sadXMLHttpRequest = {new sadXMLHttpRequest}

    || Step 2. Create an event handler that will issue a request 
    || Use the simple viewer class
    let rss-viewer:sadSimpleFlickrRSSViewer =
        {sadSimpleFlickrRSSViewer
            || event handler for a change in the group selection
            {on Commit do
                || start the request for the selected group Url
                {xmlhttp.open HttpRequestMethod.get, rss-viewer.current-feed-url, async? = true}
            }
        }
    
    || Step 3. Create a procedure to be called when the state of the sadXMLHttpRequest object changes.
    set xmlhttp.onreadystatechange =
        {proc {}:void
            {switch xmlhttp.readyState
             case readyStateEnum.Open do
                || we could make the send call immediately after the open call,
                || but this shows the handling of the state change
                {xmlhttp.send}
             case readyStateEnum.Loaded do
                || get the response headers for reporting
                let hdrs:String = 
                    {non-null {xmlhttp.getAllResponseHeaders}}
                
                || consider anything other than 200 is an error
                {if xmlhttp.status == 200 then
                    || get the XML response and update the display
                    {if-non-null feed-xml = xmlhttp.responseXML then
                        {rss-viewer.display-feed feed-xml}
                        {return}
                    }
                }
                || show headers if there's a problem
                {rss-viewer.display-text hdrs}
            }            
        }
    
    || show the viewer
    rss-viewer
</textarea>

<p><b><a href="http://www.curlr.org/curl/apps/flickrRSS/sadman_flickr_rss.curl" target="_blank">Flickr RSS Viewer</a></b> - this runs the result in a new window - simply select a feed in the dropdown list, then click on a thumbnail to see a larger version of the picture: 
</p>



<h2>Reference</h2>

<p>The methods and properties of sadXMLHttpRequest are summarized in this table</p> 
<table class = "downloadsTable" width="90%">
<tr>
<th>Method / Property</th>
<th>Description</th>
</tr>
<tr>
<td valign="top"><tt>abort</tt></td>
<td>Cancels the current file operations and resets the sadXMLHttpRequest object.
The object state is returned to Uninitialized, and all request fields are cleared</td>
</tr>
<tr>
<td valign="top"><tt>open</tt></td>
<td>Initialize the XMLHttpRequest.Aborts current operations and resets the object using 
abort. Sets the readyState to Open.</td>
</tr>
<tr>
<td valign="top"><tt>send</tt></td>
<td>Handles the Http request. Throws exception if the readyState is not Open.</td>
</tr>
<tr>
<td valign="top"><tt>getAllResponseHeaders</tt></td>
<td>Returns the response headers as a String.Returns null if the readyState is not Receiving or Loaded</td>
</tr>
<tr>
<td valign="top"><tt>getResponseHeader</tt></td>
<td>Returns the requested response header.Returns the empty string is readyState is not Receiving or Loaded.</td>
</tr>
<tr>
<td valign="top"><tt>setRequestHeader</tt></td>
<td>Add a request header.</td>
</tr>

<td><tt>onreadystatechange</tt></td>
<td>A procedure that is called whenever the readyState changes - {proc-type {}:void}</td>
</tr>
<tr>
<td valign="top">
<tt>readyState</tt></td>
<td>Returns the state of the object as an enumerated type readyStateEnum - values are:
<p>readyStateEnum.uninitialized, readyStateEnum.open, readyStateEnum.sent, readyStateEnum.receiving and readyStateEnum.loaded.</p>
</td>
</tr>
<tr>
<td><tt>responseText</tt></td>
<td>Returns the response as a String.</td>
</tr>
<tr>
<td><tt>responseXML</tt></td>
<td>Returns the response as a sadXMLDom object.</td>
</tr>
<tr>
<td><tt>status</tt></td>
<td>Returns the status as an integer (e.g. 404 for "Not Found" and 200 for "OK").</td>
</tr>
<tr>
<td><tt>statusText</tt></td>
<td>Returns the RFC 2068 response text associated with the status code.</td>
</tr>
</table>]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=39</comments>
 <pubDate>Sun, 3 Sep 2006 03:42:51 -0400</pubDate>
</item><item>
 <title>COM.CURLR.XML - A lightweight XML parser</title>
 <guid>http://www.curlr.org/index.php?itemid=35</guid>
 <link>http://www.curlr.org/index.php?itemid=35</link>
<description><![CDATA[<p>The guiding principal behind <b>curlr</b>, and this web site, is to advance the use of Curl for the many web apis 
that are being published, particularly by Google, Yahoo and various other services. Many use XML as an exchange format and with the widespread adoption of AJAX, it is evident that 
XML processing is a central part of client side web applications (notwithstanding the increasing use of JSON, and Google's habit of wrapping their services in Javascript libraries).
</p> <br/>
<p>The Curl language provides a SAX parser, and the not yet published Curl web services developer's kit 
builds on it to provide  a stack of standards compliant XML and web services utilities - which I strongly 
recommend for enterprise application development.
</p><br/>
<p>For many applications, however, we don't need the rigor of a validating XML parser, or the type definitions of a DTD.
Particularly when XML is being used only as an exchange format, we simply need a mechansim for transforming the XML data stream
into structures that can be queried for the required information. The more efficient and less verbose this 
is, the better for the developer, for whom the XML data is simply a means to the desired end of building a useful and interesting application.</p> <br/>
<p>This, then, is the purpose of the code that I've implemented in the package <code>COM.CURLR.XML</code> - namely to provide a simple and lightweight set of classes that are easy to use in application code. The beta version of the package is now checked in to the <a href="http://curlr.googlecode.com/svn/trunk/lib/xml/">curlr repository</a>.
</p><br/>

<p>I had been wanting to implement a simple XML parser for some time - but as there's not yet a Curl implementation of lex / yacc or a general purpose scanner / parser (a project for another day), this was always going to be a handcrafted piece of code, and one I hadn't had time to write.
</p><br/>
<p>After some searching I found this <a href="http://www.javaworld.com/javatips/jw-javatip128_p.html" target="_blank">Java Quick and Dirty XML parser</a>, intended at the time it was written for mobile implementations but clearly suitable now for handling XML in many client applications. This is a very lightweight implementation of the basic XML standard, but the original code would be inefficient if implemented "as is" in Curl. Also, the parser is written as an event handler, with call-backs for start / end document, and start / end element events, these are also not necessary in the curlr implementation, which incrementally creates a tree structure of nodes, with a single root element. 
</p><br/>
<p>So, the particulars differ from the original, but the principles still apply. The parser is non-validating, and does not handle the xml header - so the encoding must be known to the caller, and should be set correctly when the input stream is opened. The <code>DOCUMENT</code> tag is treated as a comment, but there are <code>DOCUMENT</code> definitions that the parser will not handle. This emphasizes the main usage constraint of the parser - that the content of the XML is expected by the parser.</p><br/>

<p>There are four important classes included in the package:</p><br/>
<ul>
<li><code>sadXMLParser</code> - parses a stream of XML format data into a tree of nodes</li>
<li><code>sadXMLNode</code> - a node representing a tag in an XML document, it has search functions</li>
<li><code>sadXMLDom</code> - a DOM representing the XML document, supporting the basic W3C search methods</li>
<li><code>sadXMLHttpRequest</code> - a Curl implementation of the XMLHttpRequest object used extensively in AJAX applications.</li>
</ul>
<p>In this article I'll introduce the first two classes, essential for all XML handling, and which I have used to generate and implement the Flickr api - so we can be reasonably sure that it works well enough. The other two are intended to parallel the usage of the AJAX <code>XmlHttpRequest</code> object; I'll present these in my next article, and show how you can implement AJAX applications with very similar Curl code. </p><p>The parser, <code>sadXMLParser</code>, has only one public class to be used called, unsurprisingly, <code>parse</code>. This takes a TextInputStream as an input argument, and returns the root node of a hierarchy on successful completion; an exception is thrown if the XML is badly formed.</p>
<p>The returned tree node structure is implemented by <code>sadXMLNode</code>, and is as simple as possible; a node has a parent (unless it's the root node), and nullable fields are assigned to the table of attributes (they are actually stored as an <code>Arguments</code> object), and the array of child nodes. All attributes are defined as name / value pairs with the value stored as a String.</p>
<p>The node class has several methods useful for searching the tree structure and for managing the tree structure. For familiarity of programming - but not compliance - I've included getters equivalent to those in the <a href="http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-BBACDC08">W3C level 1 spec</a>. Here's the skeleton of the code:</p>
<textarea rows="30" cols="100">
{define-class public sadXMLNode 
  || root document
  field public document:#sadXMLDom
  || parent node
  field public parent:#sadXMLNode
  || child nodes
  field public children:#{Array-of sadXMLNode}
  || level in the hierarchy of this node
  field public depth:int
  || anme value pairs of attributes
  field public attrs:#Arguments
  || the text value of the node
  field public text:String
  || the local name of the node
  field public local-name:String
  
  {constructor public {default 
                          || the given name of the node
                          name:String, 
                          || optional depth in hierarchy
                          depth:int = 0,
                          || parent
                          parent:#sadXMLNode = null,
                          || document
                          document:#sadXMLDom = null,
                          || text value for the node
                          text:String = "", 
                          || attributes, if any
                          attrs:#Arguments = null, 
                          || children, if any
                          children:#{Array-of sadXMLNode} = null
                      }
    set self.parent = parent
    set self.document = document
    set self.local-name = name
    set self.depth = depth
    set self.text  = text
    set self.attrs = attrs
    set self.children   = children
  }
  
  || tree node functions
  
  || add a node (and its sub-tree) as a child
  {method public {append node:sadXMLNode}:void
  }
  
  || remove a child node - this also detaches the sub-tree below the node
  {method public {remove node:sadXMLNode}:void
  }
  
  || XML query functions
  
  || the text value of the node
  {method public {get-text}:String
    {return self.text}
  }
  
  || return the attribute value for a specified name, return the empty atring if
  || the attribute is not found
  {method public {get-attribute attr-name:String}:String
  }
  
  || find child node with a name
  {method public {get-child node-name:String}:#sadXMLNode
  }
  
  || find descendant nodes with a node name 
  || equivalent to aearching for  a tag - does not include self in the search
  {method public {search-children 
                     node-name:String, 
                     include-descendants?:bool = false
                 }:{Array-of sadXMLNode}  
  }
  
  || when the proc returns true, the walk is halted
  || proc tests on self first, then on children
  {method public {walk-nodes 
                     action-proc:{proc-type {node:sadXMLNode}:bool}
                 }:bool
  }

  {method public {to-String}:String
  }
  
  || W3C DOM level 1 getters
  {getter public {nodeName}:String
  }
  {getter public {parentNode}:#sadXMLNode
  }
  {getter public {childNodes}:#{Array-of sadXMLNode}
  }
  {getter public {firstNode}:#sadXMLNode
  }
  {getter public {lastNode}:#sadXMLNode
  }
  {getter public {previousSibling}:#sadXMLNode
  }
  {getter public {nextSibling}:#sadXMLNode
  }
  {getter public {attributes}:StringStringTable
  }
  {getter public {ownerDocument}:#sadXMLDom
  }
  
} || class sadXMLNode
</textarea>

<p>The combination of the parser and the search methods of the tree nodes give us all we need to handle most application scenarios - the root node is, in effect, our XML Dom. If you're happier working with a Dom object using the standard search functions like <code>getElementById</code>, then you'll prefer to use <code>sadXMLDom</code>.</p>

<p>Let's look first at using the classes to examine the XML from a Flickr group's RSS feed - we'll be creating a simple RSS viewer in the next article, so this has some relevance. The following code parses the XML from an RSS feed from the Flickr Central group.</p>
<textarea rows="17" cols="80">
    || Url for the Flickr Central group RSS feed
    let rss-url:Url = {url "http://api.flickr.com/services/feeds/groups_pool.gne?id=34427469792@N01&amp;format=rss_200"}
    
    || get a handle to a file object for the feed
    let http-file:HttpFile = {rss-url.resolve} asa HttpFile
    
    || open a stream
    let http-input-stream:HttpTextInputStream =
        {http-file.http-read-open}
    
    || create a parser
    let xml-parser:sadXMLParser = {sadXMLParser}
    
    || parse the stream
    let rss-root:sadXMLNode = {xml-parser.parse http-input-stream}
    
    || display the xml as read
    {pre {value {rss-root.to-String}}}
</textarea>

<p>The code is quite simple - first we create a readable stream for the feed. The example chooses to do this by resolving the url into a HttpFile object. If this was unsuccessful, a <code>MissingFileException</code> would be thrown - so obviously the example is missing some defensive try/catch blocks. Once the file handle is obtained, we create the input stream with a call to the synchronous method <code>http-read-open</code>. This approach gives us access to the http response headers, and allows us to set the http request headers in the http-read-open call. If this isn't necessary, then the following even simpler code, using the <code>read-open</code> procedure will be sufficient to give a readable <code>TextInputStream</code>. In either case, the read open is where we would set the character encoding for the stream.</p>
<textarea rows="8" cols="80">
    || Url for the Flickr Central group RSS feed
    let rss-url:Url = {url "http://api.flickr.com/services/feeds/groups_pool.gne?id=34427469792@N01&amp;format=rss_200"}

    || open a TextInputStream
    let http-input-stream:TextInputStream =
        {read-open rss-url}
</textarea>

<p>The parser is reentrant, so we would need only the one object for all subsequent XML processing. The parse method takes any TextInputStream as a parameter, and returns the root <code>sadXMLNode</code> of the parsed tree. The final statement using the <code>to-String</code> method will simply display the XML tree as text - minus the XML header, with an extra root node; this will be quite ugly, since the text of picture descriptions will contain HTML markup.
</p>
<p>What next? Well, as with many applications, the XML itself is of not much use by itself, so we need to extract something that can be used or displayed. To get a list of the item titles we can write:</p>
<textarea rows="15" cols="80">
    || get the title nodes
    let item-nodes:{Array-of sadXMLNode} = 
        {rss-root.search-children "item", include-descendants? = true}
    
    || create an array to hold the titles
    let titles:StringArray = {StringArray}
    
    || loop over the item nodes
    {for item-node in item-nodes do
        || find the child called "title"
        let t-node:#sadXMLNode = {item-node.get-child "title"}
        {if-non-null t-node then
            || add the title to the array
            {titles.append t-node.text}
        }
    } 
</textarea>
<p>Which is mostly self-explanatory; search-children returns an array of sadXMLNodes, and we need to check that the return from <code>get-child</code> is not <code>null</code> - note that the content of a node is obtained from <code>sadXMLNode.text</code>, which will be the empty string if there is no conent.</p>
<p>The <code>walk-nodes</code> method provides a more powerful mechanism for searching the tree and building sets of nodes. It takes an anonymous procedure as input - which is applied to all nodes in the tree; when the procedure returns true, the traversal of the hierarchy is stopped. For the simple case of getting picture titles, we would use <code>walk-nodes</code> with a procedure as follows:</p>
<textarea rows="12" cols="80">
    let titles:StringArray = {StringArray}
    {rss-root.walk-nodes
        {proc {node:sadXMLNode}:bool
            {if node.local-name == "item" then
                {if-non-null t-node = {node.get-child "title"} then
                    {titles.append t-node.text}
                }
            }
            || always continue the search - so return false
            {return false}
        }
    }
</textarea>
<p>For the simple search <code>walk-nodes</code> provides no advantages, but it is useful for more complex searches - for example finding all nodes that have an attribute with a specified name or value.</p>
<p>So that's the 10 cent tour. Use the <code>sadXMLParser</code> and <code>sadXMLNode</code> classes to transform your XML into a tree of nodes, and use the node search methods to extract the data you need from the XML. 
</p>
<p>Next to come, we'll show the use of the <code>sadXMLHttpRequest</code> to build a simple RSS viewer.</p>]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=35</comments>
 <pubDate>Mon, 28 Aug 2006 19:39:57 -0400</pubDate>
</item><item>
 <title>COM.CURLR.CRYPTO hash functions</title>
 <guid>http://www.curlr.org/index.php?itemid=34</guid>
 <link>http://www.curlr.org/index.php?itemid=34</link>
<description><![CDATA[<p>With apologies that this is the fourth consecutive entry concerning the less than exciting subject of hash functions, I went to the effort of implementing them,so  I guess I should make some attempt to describe the use of the curlr classes. </p>
<br/>
<p>First, some background for the newcomer - if this is old news you can skip this primer and go straight  to the details below the fold. This started with the need for an MD5 implmentation for the Flickr api. MD5 is the acronym for Message Digest alogrithm 5, which is a 128-bit cryptographic hash function defined in <a href="http://tools.ietf.org/html/rfc1321">RFC1321</a>. </p>
<br/>
<p>A hash function produces a fixed-length string - called a message digest -  from a string (message) of any length; the hash function produces a very different message digest as the result of a small change in the message. There are several families of algrorithm for calculating a digest, and the vulnerability of the produced digest to a security attack is essentially related to its length - in <a href="http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf">FIPS 180 </a>the number of security bits is half the size of the digest. The MD5 digest is short enough to be attacked - and the SHA-1 algorithm of 160 bits is now considered vulnerable too. The SHA-2 functions produce longer digests, and the SHA-384 and SHA-512 handle longer message lengths too - up to  2^128 bits.</p>
<br/>
<p>The MD5 hash function is used by the Flickr api to specify a signature for methods that require <a href="http://www.flickr.com/services/api/auth.spec.html">authentication</a>. For a Flickr call, the signature is the MD5 digest of a string comprised of the method parameters and the shared secret for the api key issued for the application.</p>
<br/>
<p>This is a typical use of a hash function - creating a digital signature from a key to verify the authenticity of the message; the recipient of the signature can confirm that it was built from the key by recreating the signature using the same function. Similarly, the integrity of a file's contents can be verified by generating and comparing a message digest of a checksum for the file. </p>
<br/>
<p>Related to this use of hash functions are Message Authentication Codes (MACs). A MAC is generated from a secret key and the (abitrarily long) message to be authenticated. The keyed-hash message authentication code (HMAC) uses a hash function and a key in the algortihm described in FIPS PUB 198 and <a href="http://tools.ietf.org/html/rfc2104">RFC 2104 </a>. The construction of the HMAC iteratively compresses blocks of the message and makes a digest of them. </p>
<br/>
<p>As announced earlier, the COM.CURLR.CRYPTO package implements the hash functions for MD5 and the SHA-1 and SH-2 algorithms, and the HMAC functions for these. Read on for notes on their usage.</p>
<br/><p>The COM.CURLR.CRYPTO hash functions are implemented using the class structure of Curl's MSgDigester and MsgDigest base classes. The follwing code shows how to create the MD5 message digest for a string "abc", and how to create it when reading from a file and when writing to a file:</p>
<br/>
<textarea rows="30" cols = "100">
    || create MD5 digester
    let md5:sadMD5-Digester = {sadMD5-Digester}
    
    || create a MD5 Digest from a String (use similar code for the other hash functions
    let md5-digest:sadMD5-Digest = {md5.digest-from-string "abc"}
    let result1:String = {md5-digest.to-lower-String}
    
    || if you don't want to preserve the digest object you can create it inline
    let result2:String = {{md5.digest-from-string "abc"}.to-lower-String}
    
    || you can digest a file by opening it as a ByteInputStream, e.g.
    let md5-bis:sadMD5-ByteInputStream =
        {sadMD5-ByteInputStream 
            {read-open-byte {url "input-file-name.txt"}}
        }
    || read the contents of the file one byte at a time
    let eof?:bool = false
    let b:byte = 0
    {while not eof? do
        set (b, eof?) = {md5-bis.read-one}
    }
    || close the stream and get the digest
    {md5-bis.close}
    let result3:String = {md5-bis.digest.to-lower-String}
    
    || you can digest a file as it is being written
    let md5-bos:sadMD5-ByteOutputStream =
        {sadMD5-ByteOutputStream 
            {write-open-byte {url "output-file-name.txt"}}
        }
    || write the string to the file using the sadMD5-ByteOutputStream
    let sb:StringBuf = {StringBuf "abc"}
    {for c in sb do
        {md5-bos.write-one c asa byte}
    }
    || close the stream and get the digest
    {md5-bos.close}
    let result4:String = {md5-bos.digest.to-lower-String}
    
    || expect results to be the same
    let ok?:bool = (result1 == result2 and result2 == result3 and result3 == result4)
</textarea>
<br/>
<p>The class structure for each hash function is the same, so you would just replace the MD5 with SHA-1 etc.. to have the equivalent code for the other algorithms. (For usage consistency, there's an implementation of SHA-1 in the package - but obviously you can use the digester supplied in the Curl product). Clearly, you would need to add your own error handling on the file i/o, and consider such niceties as character encoding to use this in an application.</p>
<br/>
<p>The HMAC functions are simple to use as well, but require a little more thought about what is being hashed - for example, the test cases from <a href="http://tools.ietf.org/html/rfc2202">RFC 2202 </a>specify the key as a hex string. The curlr function digest-from-ByteVec  takes ByteVecs not Strings as its arguments, and returns the digest as a ByteVec, which in the test is converted back to a hex string - note that the digest is also available as a field in the HMAC class, but its to-lower-String method will not give a hex string.</p>
<br/>
<textarea rows = "30" cols = "100">
    || create the HMAc for MD5
    let hmac-md5:HMAC-MD5 = {HMAC-MD5}
    
    || test case from RFC 2202
|#
test_case =     1
key =           0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
key_len =       16
data =          "Hi There"
data_len =      8
digest =        0x9294727a3638bb1c13f48ef8158bfc9d
#|
    || the test case uses a hex string - convert this to a ByteVec
    let key-bv:ByteVec = {ByteVec-from-hex-string "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"}
    || the message is a String, convert to a ByteVec using utf8 encoding
    let msg:String = "Hi There"
    let msg-bv:ByteVec = {ByteVec.from-size msg.size , 0}
    {encode-characters  msg, msg-bv, CharEncoding.utf8, out-start=0}
    
    || calculate the digest as a ByteVec, and convert to  a hex string for comparison
    let hex-string:String =
        {ByteVec-to-hex-string
            {hmac-md5.digest-from-ByteVec
                key-bv,
                msg-bv
            }
        }
    
    let ok?:bool = {hex-string.to-lower-clone} == "9294727a3638bb1c13f48ef8158bfc9d"
</textarea>
<br/>
<p>As an added extra, there are some useful byte and bitwise functions in the package, including bit rotations functions
(bit-rol, bit-ror, bit64-rol, bit64-rol) and byte conversions (int32-to-ByteVec, ByteVec-to-int32, ByteVec-to-int64, ByteVec-from-hex-string, ByteVec-to-hex-string).</p>]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=34</comments>
 <pubDate>Sat, 26 Aug 2006 18:49:52 -0400</pubDate>
</item><item>
 <title>SHA-384 , SHA-512, SHAVS and HMACVS</title>
 <guid>http://www.curlr.org/index.php?itemid=33</guid>
 <link>http://www.curlr.org/index.php?itemid=33</link>
<description><![CDATA[<p>I don't know who will particularly care about this, but I've completed the SHA-2 suite of hashing algorithms for COM.CURLR.CRYPTO with the addition of the SHA-384 and SHA-512 classes, and their HMAC equivalents.</p>
<br/>
<p>I've also implemented the pseudo random tests for the SHAVS testing, along with the HMACVS tests, so now COM.CURLR.CRYPTO passes all the pre-validation tests provided by NIST. This is for hashing whole bytes, so the bit tests will not be passed any time soon. The code and tests are all checked in at the <a href="http://http://curlr.googlecode.com/svn/trunk/lib/crypto/">curlr repository</a>. The pcurl'd package is available at the <a href="http://groups.yahoo.com/group/curlr/files/curlr%20packages/version%204.0/">curlr forum</a> if you're a member, and will be posted on this site soon.




Usage notes to follow later.]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=33</comments>
 <pubDate>Mon, 21 Aug 2006 15:58:02 -0400</pubDate>
</item><item>
 <title>More hashing functions - SHA-1, SHA-224, SHA-256 and HMAC</title>
 <guid>http://www.curlr.org/index.php?itemid=32</guid>
 <link>http://www.curlr.org/index.php?itemid=32</link>
<description><![CDATA[<p>I've been distracted by implementing hashing algorithms. I've extended the COM.CURLR.CRYPTO package with classes for SHA-224 and SHA-256 (plus my own SHA-1 for consistency) hashing algorithms. And I've included classes for creating HMACs (<a href="http://en.wikipedia.org/wiki/HMAC">keyed-hash message authentication codes</a>) for all these and for MD5.</p>
<br/>
<p>The Security Hash Standards (SHS) are comprehensively documented in <a href="http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf">FIPS PUB 180-2</a>, which you can find at <a href="http://csrc.nist.gov/cryptval/">NIST</a>, along with much else concerning cryptographic standards. There's a validation standard too, and though I don't think I'll get the code certified, the implementations do pass the sample tests for byte implementations. I've included those tests in the curlr repository - with the caveat that the data files are from NIST, not me.</p>]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=32</comments>
 <pubDate>Sun, 20 Aug 2006 20:05:56 -0400</pubDate>
</item><item>
 <title>MD5 Digest</title>
 <guid>http://www.curlr.org/index.php?itemid=30</guid>
 <link>http://www.curlr.org/index.php?itemid=30</link>
<description><![CDATA[<p>I've just finished a Curl implementation of the <a href="http://tools.ietf.org/html/rfc1321" target="_blank">MD5 </a>hash function - used by the Flickr api for creating the signature for a call. This replaces the previously <a href="http://www.curlr.org/nusw/nucleus/index.php?itemid=8">lamented dll implementation</a>, and makes the Curlr api an all-Curl affair; so now the only reason the Flickr apps need to be granted privilege is the absence of a curl-access.txt file on their server. I'm working on that.</p>

<p>The code is to be found in the <a href="http://curlr.googlecode.com/svn/trunk/trunk/lib/crypto/" target="_blank">curlr repository</a> which is open for downloads - but not yet for modification of the source. The source is a little rough, but it passes the basic set of tests, and my Flickr api seems to work correctly when using it. You can try it out (if creating MD5 hashes is your idea of fun) <a href="http://www.curlr.org/curl/apps/md5/md5-test.curl" target="_blank">here</a>.</p>

You'll find some basic usage notes below the fold.<p>The MD5 code is probably more widely useful than in just the Flickr api, so it resides in a crypto package, with the intent that other useful implementations will live there too - the package is COM.CURLR.CRYPTO. The code is an implementation of the Curl base classes MsgDigest and MsgDigester, and provides similar functions to the native SHA-1 digests. I've also included implementations of MD5-ByteOutputStream and MD5-ByteInputStream to build digests for ouput and input streams - though I've not tested these yet.</p>

<p>I've added a couple of utility methods, so it's simple enough to use the digest as a lower or uper-case string. Note the naming convention; class names are prefixed sad, partly to singify my authorship but mostly to distinguish these implementations from any other Curl versions. </p>

<textarea rows="20" cols="80">
|| import the package
{import * from COM.CURLR.CRYPTO.MD5}

...

|| create a MD5 digester to create the digests
let md5-digester:sadMD5-Digester = {sadMD5-Digester}

|| create a digest for a message string
let digest:sadMD5-Digest = {md5-digester.digest-from-string "message string"}

|| create a lower case digest string
let digest-str:String = {digest.to-lower-String}

...

</textarea>

<p>The one issue I know of so far is that the method  MD5-Digest.to-base64-String blows up becaase of a bug in the Curl base class- so don't use it, and use to-lower-String instead.</p>


]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=30</comments>
 <pubDate>Wed, 16 Aug 2006 19:26:15 -0400</pubDate>
</item><item>
 <title>Curlr API Implementation - part 3</title>
 <guid>http://www.curlr.org/index.php?itemid=26</guid>
 <link>http://www.curlr.org/index.php?itemid=26</link>
<description><![CDATA[<p>As you can see, I've been diverted from this series by getting the web site together, and <a href="http://curlr.org/nusw/index.php?itemid=24">trying out</a> the Flickr API - a lot more fun than documenting and testing it. But it's time to put the manual touches to the generated api.</p>

<p>The main idea has been that there is a separate class for each Flickr method, with the intent that a single object handles the call and the interpretation of the response. Using the Curl IDE we get the bonus of auto-completion for the arguments of the request method, so the basic programming is straight forward enough with the api decribed previously. But there are 88 classes, and they all have very similar constructor signatures - requiring the secret and api_key. It will get to be tedious having to type these values in more than once, and a programm may well need to set / reset the authorization token on many of these objects. There may be other tasks that require handling all of the constructed  API objects, so we need an organizing entity to make life a little easier.</p><p>The shape of an organizing class is simple, once we know what it is that we need. The base class can be generated by the build process - ensuring that it is complete - and looks like this:</p>
<textarea rows = "30" cols = "80" readonly="readonly">
{define-class public BaseFlickrAPI
  || table of Flickr methods
  || methods are keyed by their Flickr name (e.g. flickr.auth.getToken), and getters
  || allow direct access using a Curl version of the name e,g, api.flickr-auth-getToken.
  || The table enables common operations across objects - e.g. settings the token on all methods.
  field public constant methods:{HashTable-of String, BaseFlickrMethod} = 
      {new {HashTable-of String, BaseFlickrMethod}}
  
  || Input paramters - these are applied to all methods
  || secret and api_ley are constant for an 
  field public-get _auth_token:String

  {getter public {auth_token}:String
    {return self._auth_token}
  }
  {setter public {auth_token token:String}:void
    set self._auth_token = token
    {for f-method in self.methods do
        set f-method.auth_token = token
    }    
  }
  
  || the endpoint Url, secret and api_key are not changeable for an instance of the API
  field public constant secret:String
  field public constant api_key:String
  field public constant endpoint-url:Url
  
  {constructor public {default
                          secret:String,
                          api_key:String,
                          endpoint:FlickrEndpointEnum,
                          auth_token:String = ""
                      }

    || assign constant fields
    set self.secret = secret
    set self.api_key = api_key
    set self.endpoint-url = flickr-endpoints[endpoint]
    || set the token, if any
    set self._auth_token = auth_token
    
    || create an instance of each Flickr method class
    set self.methods["flickr.auth.checkToken"] = {flickr-auth-checkToken secret, api_key, endpoint=self.endpoint-url, auth_token=auth_token}
    set self.methods["flickr.auth.getFrob"] = {flickr-auth-getFrob secret, api_key, endpoint=self.endpoint-url, auth_token=auth_token}
    set self.methods["flickr.auth.getFullToken"] = {flickr-auth-getFullToken secret, api_key, endpoint=self.endpoint-url, auth_token=auth_token}
    set self.methods["flickr.auth.getToken"] = {flickr-auth-getToken secret, api_key, endpoint=self.endpoint-url, auth_token=auth_token}
    ...
  }
  
  || accessors for the method objects
  
  {getter public {flickr-auth-checkToken}:flickr-auth-checkToken
    {return
        self.methods["flickr.auth.checkToken"] asa flickr-auth-checkToken
    }
  }
  {getter public {flickr-auth-getFrob}:flickr-auth-getFrob
    {return
        self.methods["flickr.auth.getFrob"] asa flickr-auth-getFrob
    }
  }
  {getter public {flickr-auth-getFullToken}:flickr-auth-getFullToken
    {return
        self.methods["flickr.auth.getFullToken"] asa flickr-auth-getFullToken
    }
  }
  {getter public {flickr-auth-getToken}:flickr-auth-getToken
    {return
        self.methods["flickr.auth.getToken"] asa flickr-auth-getToken
    }
  }
  
  ...
} || class BaseFlickrAPI

</textarea>

<p>I've shown the pattern for only the authorization methods, but it's identical for all the other methods. So for a very small startup oveherhead, a BaseFlickrAPI object creates instances of each of the methods and stores them in a HashTable. The setter code for the auth_token shows how this is used to perform global assignments. </p>
<p>It isn't my intention that BaseFlickrAPI is used directly  in an application - we'll actually be using a subclass - if it was, a request might look like this:</p>
<textarea rows = "20" cols = "80" readonly="readonly">
    ...
    
    || create an instance of all the Flickr method objects
    let curlr-api:BaseFlickrAPI =
        {BaseFlickrAPI
            secret,
            api_key
        }
    
    ...
    || the two ways of making a request are equivalent
    || use the accessor
    let status?:bool =
        {curlr-api.flickr-auth-get-token.request}
    
    || or use the entry in the table
    let status?:bool =
        {curlr-api.methods["flickr.auth.get-token"].request}
    
    ...
</textarea>

<p>The class helps organize the methods, and is useful for implementing higher level functionality.</p>

]]></description>
 <category>Curlr</category>
<comments>http://www.curlr.org/index.php?itemid=26</comments>
 <pubDate>Fri, 11 Aug 2006 11:58:39 -0400</pubDate>
</item><item>
 <title>sadAquarium</title>
 <guid>http://www.curlr.org/index.php?itemid=25</guid>
 <link>http://www.curlr.org/index.php?itemid=25</link>
<description><![CDATA[Here's an older experiment I tried for an aquarium using some of <a href="http://www.red3d.com/cwr/boids/" target="_blank">Craig Reynolds</a> rules for flocking.<p> I started to build a rules engine with the hope of playing with different combinations of behavior, but didn't get further than what you see. Get started by adding a shoal - just click the <b>add</b> button. The fish will swim around the tank avoiding each other and the tank walls, but will also try to keep together.</p>
<p>The display is a scaled 3D scene of a 1m cube tank;it's interactive in that you can move your view around the wireframe "tank" by moving the mouse while holding down the left button. And you can zoom in and out by holding down the right mouse button and moving the mouse. Other than that, the control box is pretty obvious - except the parameters actually used are distributed around the values you specify according to the variation factor. This is mostly so that the fish appear  with a range of sizes. I'll post the code soon.</p>
<p>It's not very efficient, so expect to see your cpu usage go way up, especially if there are large shoals. But with a high frame rate and the right combination of values you can pursuade yourself that the fish are actually swimming around!</p>
<div>
<embed src="http://www.curlr.org/curl/apps/sadAquarium/sadAquarium.curl" height = 500px width = 100% type="text/vnd.curl"></embed>
</div>
]]></description>
 <category>applet</category>
<comments>http://www.curlr.org/index.php?itemid=25</comments>
 <pubDate>Thu, 10 Aug 2006 21:28:34 -0400</pubDate>
</item><item>
 <title>Interestingness Applet</title>
 <guid>http://www.curlr.org/index.php?itemid=24</guid>
 <link>http://www.curlr.org/index.php?itemid=24</link>
<description><![CDATA[The first applet based on the Curlr api is a simple viewer of the Flickr interestingness list. You'll need the <a href="http://www.curl.com/products/download.php">Curl runtime</a>, of course, and then you need to <a href="http://www.curlr.org/nusw/index.php?itemid=23">grant this site privilege</a> to run the applet. <p>This is a combination of some photo utilities using the Curlr api. This is an embedded applet so you will need to first click on the box to activate it. If you prefer, you can run the app <a href="http://www.curlr.org/curl/apps/interestingness/curlr-index.curl" target = "_blank"> in its own browser</a>, and then you won't have to do anything. More interestingly, if you're using IE you can run it as a <a href="curl://launch/http://www.curlr.org/curl/apps/interestingness/curlr-index.dcurl" >standalone applet</a>.<p>
<p>It's simple to use - and more features will be added. Navigate using the prev, next and arrow buttons -  all of which do the obvious thing - and have tooltips to prove it. The numbers are indexes of photos andl the indexed photo will be displayed when you click on them. 
<div>
<embed src="http://www.curlr.org/curl/apps/interestingness/curlr-index.curl" height = 700px width = 100% type="text/vnd.curl"></embed>
</div>
]]></description>
 <category>applet</category>
<comments>http://www.curlr.org/index.php?itemid=24</comments>
 <pubDate>Sun, 6 Aug 2006 13:31:50 -0400</pubDate>
</item>
  </channel>
</rss>
