<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CEEGSX8zcSp7ImA9WhRUF00.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904</id><updated>2012-01-27T22:17:08.189Z</updated><category term="Space-time" /><category term="Safari 4" /><category term="C-boundary" /><category term="Duplex" /><category term="MVVM" /><category term="MVC" /><category term="Offline" /><category term="chubby client" /><category term="Last Theorem" /><category term="Prism" /><category term="Bootstrap" /><category term="WLW" /><category term="AJAX" /><category term="MAKECERT" /><category term="Windows Live Writer" /><category term="UIAlertView" /><category term="Net.TCP Bindings" /><category term="Number Theory" /><category term="WebKit" /><category term="ASPX" /><category term="RIA" /><category term="OSX86" /><category term="Leopard" /><category term="HTTP" /><category term="C++" /><category term="OPOS" /><category term="IActiveAware" /><category term="Graphics" /><category term="Deployment Project" /><category term="Fermat" /><category term="iPhoto" /><category term="user interface" /><category term="Cosy Orbs" /><category term="natural base" /><category term="Objective-C" /><category term="Mac" /><category term="PC" /><category term="LINQ to SQL" /><category term="JSON" /><category term="UIImagePickerController" /><category term="touch" /><category term="Carving" /><category term="OpenGL" /><category term="Quartz" /><category term="HTML5" /><category term="Windows 7" /><category term="Threading" /><category term="LINQ" /><category term="Service" /><category term="cocos2d" /><category term="jQuery" /><category term="Modal Dialog" /><category term="Polling Duplex Service" /><category term="iATKOS" /><category term="REST" /><category term="Cloud Computing" /><category term=".manifest" /><category term="Silverlight 4" /><category term="SharePoint" /><category term="XML" /><category term="RDBMS" /><category term="ERP" /><category term="Java" /><category term="INavigationAware" /><category term="jQTouch" /><category term="Snow Leopard" /><category term="C#" /><category term="PHP" /><category term="IExpress" /><category term="Canvas" /><category term="Reflection" /><category term="Sockets" /><category term="iLife" /><category term="iPhone" /><category term="Click Once" /><category term="XML-RPC" /><category term="WWW.FAR2MUCHMAIL.COM" /><category term="WCF" /><category term="SharePoint Designer" /><category term="Out Of Browser" /><category term="Dynamic" /><category term="mathematics" /><category term="Samsung" /><category term="Quartz Extreme" /><category term="Wiki" /><category term="JavaScript" /><category term="Julian Guy Tulloch" /><category term="Silverlight" /><category term=".NET" /><title>Not Now Nigel</title><subtitle type="html">...it may be irritating, but there are some things that have to be said.

I will be using a classification in this blog to help readers work their way through my thoughts. 'Abstract' and 'Concrete' will be used to describe the real world or practical relevance of my thoughts. 'Important' and 'Trivial' will be used to indicate how important I think the message is.

Hope you find some of my ramblings useful and interesting...Or maybe plain irritating...Not now Nigel.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://not-now-nigel.blogspot.com/" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/NotNowNigel" /><feedburner:info uri="notnownigel" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CEEGSX8yfSp7ImA9WhRUF00.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-1003340808665724678</id><published>2012-01-27T18:15:00.001Z</published><updated>2012-01-27T22:17:08.195Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-27T22:17:08.195Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="LINQ to SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Dynamic" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Reflection" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term="LINQ" /><title>LINQ to SQL Dynamic Stored Procedures</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ApHF22PktdC7MTd9yqY5z6AL708/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ApHF22PktdC7MTd9yqY5z6AL708/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ApHF22PktdC7MTd9yqY5z6AL708/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ApHF22PktdC7MTd9yqY5z6AL708/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh6.ggpht.com/-KO-WOD7GpsQ/TyMO7XwloSI/AAAAAAAAAR0/VQ3kBhXplY8/s1600-h/linqpadlogo%25255B3%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="linqpadlogo" border="0" alt="linqpadlogo" align="left" src="http://lh6.ggpht.com/-OqMfZ9gIINs/TyMO7yTnZ7I/AAAAAAAAAR8/sZ_kJdYfYD0/linqpadlogo_thumb%25255B1%25255D.png?imgmax=800" width="152" height="151"&gt;&lt;/a&gt;[Concrete/Little bit interesting]&lt;/strong&gt; I wanted to call stored procedures by name using a string parameter. Sounds simple and with ADO it's a breeze. But with LINQ to SQL, life is a little more complicated. Here is how I cracked the problem.&lt;/p&gt; &lt;h3&gt;&amp;nbsp;&lt;/h3&gt; &lt;h3&gt;&amp;nbsp;&lt;/h3&gt; &lt;h3&gt;&amp;nbsp;&lt;/h3&gt; &lt;h3&gt;LINQ to SQL&lt;/h3&gt; &lt;p&gt;In Visual Studio, when you add LINQ to SQL Classes to you project, you are creating a class that provides a convenient set of classes for managing tables, views, functions and stored procedures:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-8DKkSQM1DdY/TyMO8m8Z3bI/AAAAAAAAASE/lSvniVlttqI/s1600-h/image%25255B3%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-W9OmLvQPwMw/TyMO9c37dbI/AAAAAAAAASM/jRc_exK3B84/image_thumb%25255B1%25255D.png?imgmax=800" width="522" height="369"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;When you add LINQ to SQL Classes to your project, Visual Studio creates a .DBML file set. These files describe the managed entities with partial class definitions. The main class is derived from &lt;strong&gt;System.Data.Linq.DataContext&lt;/strong&gt; and provides the link between your code and the database connection. LINQ to SQL works through defining class members that maps LINQ onto the entity and makes use of attributes to map columns, parameters and returns to .Net classes. &lt;/p&gt; &lt;p&gt;LINQ to SQL Classes takes a lot of code out of your application and is easy to use. For example, to call a stored procedure, simply open the .DBML, drag a stored procedure from a data connection on the Server Explorer or Toolbox over onto the Object Relational Designer: this creates the class entries that maps the entity onto LINQ, then from your code, call the stored procedure as shown below:&lt;/p&gt; &lt;p&gt; &lt;div id="codeSnippetWrapper"&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (CatalogueDataContext db = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CatalogueDataContext())    &lt;br&gt;{&lt;br&gt;     db.usp_Biometrics_Update_OperatorFingerprint(input.OperatorId, input.Fingerprint, &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;);&lt;br&gt;}&lt;br&gt;&lt;/pre&gt;&lt;br&gt;&lt;/div&gt;In the above example, I’m calling the stored procedure &lt;strong&gt;usp_Biometrics_Update_OperatorFingerprint&lt;/strong&gt; which is defined in a database called Catalogue, LINQ to SQL provides me with the data context on which the stored procedures are defined. All I need to do us use an instance of &lt;strong&gt;CatalogueDataContext&lt;/strong&gt; and from this, call the stored procedure. Because it’s all about classes, Visual Studio can help me with the parameters and returns through Intellisense.&amp;nbsp; &lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Under most circumstances calling a stored procedure as described above is sufficient. But there are times when you need to call a stored procedure by name, not by class method. Say for example you have a bunch of stored procedures called:&lt;/p&gt;
&lt;p&gt;usp_get_&lt;em&gt;TableName_&lt;/em&gt;As_XML&lt;/p&gt;
&lt;p&gt;where &lt;em&gt;TableName&lt;/em&gt; is a name of a table, any table. The implementation of this stored procedure for any given named table may be complex, for example, they may include selecting output from child tables where foreign key are present. So in this example, a table called Customer may return the following XML:&lt;/p&gt;
&lt;div&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;rows&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;row&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;customerId&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;1&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;customerId&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;customerName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;Nigel&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;customerName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;addresses&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;address&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;First Postal Address&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;address&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;address&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;Second Postal Address&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;address&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;addresses&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;row&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;rows&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;So for each row of Customer data, there may be multiple address rows. &lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;For another table called Car the data is simpler:&lt;/div&gt;
&lt;div id="codeSnippetWrapper"&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;rows&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;row&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;registration&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;AAAAAAA&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;registration&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;make&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;Chrysler&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;make&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;model&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;Grand Voyager&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;model&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;row&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;rows&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br&gt;&lt;/div&gt;
&lt;div&gt;So for each table, I need a specific stored procedure that returns table data as XML. Each one of these stored procedure returns the XML data as an output parameter and all the stored procedures have the same signature. Below is an illustration of the stored procedure for the table Car:&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div id="codeSnippetWrapper"&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;Create&lt;/span&gt; &lt;span style="color: #0000ff"&gt;Procedure&lt;/span&gt; usp_get_Car_As_XML&lt;br&gt;    @xml XML &lt;span style="color: #0000ff"&gt;OUTPUT&lt;/span&gt;&lt;br&gt;&lt;span style="color: #0000ff"&gt;AS&lt;/span&gt;&lt;br&gt;&lt;span style="color: #0000ff"&gt;BEGIN&lt;/span&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;SET&lt;/span&gt; NOCOUNT &lt;span style="color: #0000ff"&gt;ON&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;set&lt;/span&gt; @xml = (&lt;br&gt;        &lt;span style="color: #0000ff"&gt;Select&lt;/span&gt;&lt;br&gt;            registration, make, model&lt;br&gt;        &lt;span style="color: #0000ff"&gt;From&lt;/span&gt;&lt;br&gt;            Car&lt;br&gt;    )&lt;br&gt;    &lt;span style="color: #0000ff"&gt;For&lt;/span&gt; XML RAW(&lt;span style="color: #006080"&gt;'row'&lt;/span&gt;), Root(&lt;span style="color: #006080"&gt;'rows'&lt;/span&gt;))&lt;br&gt;&lt;br&gt;    &lt;span style="color: #0000ff"&gt;Return&lt;/span&gt; &lt;span style="color: #cc6633"&gt;@@Error&lt;/span&gt;&lt;br&gt;&lt;span style="color: #0000ff"&gt;END&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;br&gt;&lt;/div&gt;So the problem is, from code, how do I call the stored procedure for a given table name, returning the XML representation of that table?&lt;br&gt;&lt;/div&gt;
&lt;h3&gt;The Solution&lt;/h3&gt;
&lt;p&gt;Well of course I could use some sort of if…elseif… or big switch statement. This will work but I’m not keen on big switch statements. What I really want is to call the stored procedure by name. Fortunately, we can use .Net reflection to find the method in the data context and execute this method:&lt;/p&gt;
&lt;div&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (CatalogueDataContextEx db = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CatalogueDataContextEx())&lt;br&gt;{&lt;br&gt;    XElement xml = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;br&gt;    String script = String.Format(&lt;span style="color: #006080"&gt;"usp_get_{0}_As_XML"&lt;/span&gt;, tableName);&lt;br&gt;    db.CallScriptByName(script, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; xml);&lt;br&gt;    &lt;br&gt;    &lt;span style="color: #008000"&gt;//handle the results in xml variable&lt;/span&gt;&lt;br&gt;}&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;Notice that in this example, the data context is called &lt;strong&gt;CatalogueDataContextEx&lt;/strong&gt;. This is a derived class based on &lt;strong&gt;CatalogueDataContext&lt;/strong&gt; and implements the &lt;strong&gt;CallScriptByName&lt;/strong&gt;() method:&lt;/div&gt;
&lt;div id="codeSnippetWrapper"&gt;&lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;partial&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; CatalogueDataContextEx : CatalogueDataContext&lt;br&gt;{&lt;br&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; CallScriptByName(String script, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; XElement xml)&lt;br&gt;    {&lt;br&gt;        var sp = &lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(CatalogueDataContext).GetMethod(script);&lt;br&gt;        var result = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.ExecuteMethodCall(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;, sp, xml);&lt;br&gt;        xml = (XElement)result.GetParameterValue(0);&lt;br&gt;    }&lt;br&gt;}  &lt;/pre&gt;&lt;br&gt;&lt;/div&gt;
&lt;p&gt;So what is going on here?&lt;/p&gt;
&lt;p&gt;In my derived class I have access to all public and protected methods of the data context. This is important as I want to call the &lt;strong&gt;ExecuteMethodCall&lt;/strong&gt;() method on the class, which is protected.&lt;/p&gt;
&lt;p&gt;I’m using reflection to get a reference to the stored procedure by name, This is then executed using the &lt;strong&gt;ExecuteMethodCall&lt;/strong&gt;. This call takes a list of the parameters to this stored procedure. In our case, there is only a single output parameter for the XML. &lt;/p&gt;
&lt;p&gt;Once the call is made, the results are passed back in a &lt;strong&gt;IExecuteResult&lt;/strong&gt; return parameter. This return can be used to access the call’s parameters, in our case the xml output parameter. &lt;/p&gt;
&lt;p&gt;And that’s it.&lt;/p&gt;
&lt;h3&gt;Future Improvements&lt;/h3&gt;








&lt;p&gt;This implementation is not completely dynamic. Specifically, the &lt;strong&gt;CatalogueDataContext&lt;/strong&gt; must have definitions for the stored procedures I’m going to call. A better implementation would use a generic data context and build up the method to call. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-1003340808665724678?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/Tslj1Na00Xs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/1003340808665724678/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2012/01/linq-to-sql-dynamic-stored-procedures.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/1003340808665724678?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/1003340808665724678?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/Tslj1Na00Xs/linq-to-sql-dynamic-stored-procedures.html" title="LINQ to SQL Dynamic Stored Procedures" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-OqMfZ9gIINs/TyMO7yTnZ7I/AAAAAAAAAR8/sZ_kJdYfYD0/s72-c/linqpadlogo_thumb%25255B1%25255D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2012/01/linq-to-sql-dynamic-stored-procedures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IBRHcycSp7ImA9WhdVFkw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-3330147595476730786</id><published>2011-09-21T16:29:00.001+01:00</published><updated>2011-09-21T16:39:15.999+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-21T16:39:15.999+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="MVVM" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Prism" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term="IActiveAware" /><category scheme="http://www.blogger.com/atom/ns#" term="INavigationAware" /><title>Using IActiveAware and INavigationAware</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/J9UMlWdYC2S6_vsI0kVFTe6QRs4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J9UMlWdYC2S6_vsI0kVFTe6QRs4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/J9UMlWdYC2S6_vsI0kVFTe6QRs4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J9UMlWdYC2S6_vsI0kVFTe6QRs4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh6.ggpht.com/-RStUpYCBGKU/TnoFIZTi86I/AAAAAAAAARs/uyGQNhWNGTA/s1600-h/prism%25255B9%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 20px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top: 0px; border-right: 0px; padding-top: 0px" title="prism" border="0" alt="prism" align="left" src="http://lh3.ggpht.com/-qz8I2_G1O1I/TnoFIwSl6vI/AAAAAAAAARw/2UbpPKUHdZc/prism_thumb%25255B6%25255D.jpg?imgmax=800" width="211" height="154" /&gt;&lt;/a&gt;[Concrete/Little bit interesting] &lt;/strong&gt;The Microsoft Prism framework provides a couple of very useful interfaces for managing awareness of view activation and navigation.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h3&gt;IActiveAware&lt;/h3&gt;  &lt;p&gt;IActiveAware is a simple interface you can implement on your views to indicate that you want the view to be notified when it is made active or inactive. It looks like this:&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;interface&lt;/span&gt; IActiveAware
{
    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Gets or sets a value indicating whether the object is active.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;value&amp;gt;&amp;lt;see langword=&amp;quot;true&amp;quot; /&amp;gt; if the object is active; otherwise &amp;lt;see langword=&amp;quot;false&amp;quot; /&amp;gt;.&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsActive { get; set; }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// Notifies that the value for &amp;lt;see cref=&amp;quot;IsActive&amp;quot;/&amp;gt; property has changed.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;event&lt;/span&gt; EventHandler IsActiveChanged;
}&lt;/pre&gt;
&lt;style type="text/css"&gt;




.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;p&gt;The IsActive flag lets you know if your view is active, and the IsActiveChanged event will fire when that state changes.&lt;/p&gt;

&lt;p&gt;If you implement this on your view class, then you need to ensure the event gets fired when the state is changed by the region behaviour. You do this by adding the IActiveAware interface to your View-Model:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; LoginViewModel : NotificationObject, &lt;strong&gt;IActiveAware&lt;/strong&gt;, INavigationAware
{ 
}&lt;/pre&gt;

&lt;p&gt;An example of the implemented interface is shown here:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="preproc"&gt;#region&lt;/span&gt; ViewModel activation
&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; _IsActive = &lt;span class="kwrd"&gt;false&lt;/span&gt;;
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsActive
{
    get
    {
        &lt;span class="kwrd"&gt;return&lt;/span&gt; _IsActive;
    }
    set
    {
        _IsActive = &lt;span class="kwrd"&gt;value&lt;/span&gt;;
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;value&lt;/span&gt;)
            OnActivate();
        &lt;span class="kwrd"&gt;else&lt;/span&gt;
            OnDeactivate();
    }
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;event&lt;/span&gt; EventHandler IsActiveChanged;
&lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;In the above example I use the changing state of IsActive to trigger the private methods OnActivate() and OnDeactivate(). These methods are thus called when the view active state changes.&lt;/p&gt;

&lt;h3&gt;IActiveAware Example - Managing Devices&lt;/h3&gt;

&lt;p&gt;A good example of the use of IActiveAware is the subscription to an control of devices. We use event aggregation to interface with physical devices, that is, we subscribe to device notifications. The simplest scheme is to subscribe to all the device events in the View-Model constructor:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="rem"&gt;//Subscribe to device events&lt;/span&gt;
eventAggregator.GetEvent&amp;lt;MagneticStripeReaderEvent&amp;gt;().Subscribe(MagneticStripeReaderInputReceived);
eventAggregator.GetEvent&amp;lt;ScannerEvent&amp;gt;().Subscribe(ScannerInputReceived);&lt;/pre&gt;

&lt;p&gt;Of course, this means that the View-Model will be called back on device events during the entire lifetime of the module housing the View-Model and the associated views.&lt;/p&gt;

&lt;p&gt;To ensure you only act on the device events when your view is the active view, simply use the IsActive property appropriately in the event callback:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ScannerInputReceived(ScannerEventParameter param)
{
    &lt;span class="rem"&gt;//Handle input if active and OperatorId has focus&lt;/span&gt;
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (IsActive == &lt;span class="kwrd"&gt;false&lt;/span&gt; || OperatorHasFocus == &lt;span class="kwrd"&gt;false&lt;/span&gt;)
        &lt;span class="kwrd"&gt;return&lt;/span&gt;;

    &lt;span class="kwrd"&gt;if&lt;/span&gt; (!&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(param.Barcode))
    {
        OperatorId = param.Barcode;
        ValidateLoginInput(&lt;span class="kwrd"&gt;false&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);
    }
    &lt;span class="kwrd"&gt;else&lt;/span&gt;
    {
        ProgressMessage = Resources.Message_InvalidInput;
    }
}&lt;/pre&gt;

&lt;h3&gt;OnActivate() and OnDeactivate()&lt;/h3&gt;

&lt;p&gt;These methods when implemented should be used to handle the View-Model initialisation and termination state. For example, in a Login screen, during OnActivate(), the View-Model should initialise the various entry fields and properties used to control the view:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnActivate()
{
    eventAggregator.GetEvent&amp;lt;ChangeTitleEvent&amp;gt;().Publish(Properties.Resources.Title_Login);
    ProgressMessage = String.Empty;
    ChangingPassword = &lt;span class="kwrd"&gt;false&lt;/span&gt;;
    NewBiometricEntry = &lt;span class="kwrd"&gt;false&lt;/span&gt;;
    LoginWithPassword = configurationManager.GetBool(ConfigurationNames.LoginWithPassword);
    ResetOperatorPassword();

    LogoutCurrentOperator();
}&lt;/pre&gt;

&lt;p&gt;Here we are setting the title through a published event, clearing the progress message, clearing some flags (used to control the visibility state of page elements), resetting the OperatorId and Password and ensuring we are logged off. These operations will be performed every time a view in this module is activated.&lt;/p&gt;

&lt;h3&gt;IActiveAware Limitations and INavigationAware&lt;/h3&gt;

&lt;p&gt;Responding to IActiveAware works fine in a module that supports a single view. In this scenario, the View-Model activation is one-to-one with the View activation. However, if there are more than one view associated with a View-Model, you will have to implement INotificationAware:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;interface&lt;/span&gt; INavigationAware
{
    &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsNavigationTarget(NavigationContext navigationContext);
    &lt;span class="kwrd"&gt;void&lt;/span&gt; OnNavigatedFrom(NavigationContext navigationContext);
    &lt;span class="kwrd"&gt;void&lt;/span&gt; OnNavigatedTo(NavigationContext navigationContext);
}&lt;/pre&gt;

&lt;p&gt;This interface when implemented provides callbacks to handle the navigationContext switch in the form of OnNavigatedFrom() and OnNavigatedTo(). A typical implementation here is to keep a copy of the navigationContext passed to the OnNavigatedTo(). This can be used later to perform view-to-view navigation:&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="preproc"&gt;#region&lt;/span&gt; Navigation Aware &lt;span class="kwrd"&gt;for&lt;/span&gt; view switching &lt;span class="kwrd"&gt;in&lt;/span&gt; &lt;span class="kwrd"&gt;this&lt;/span&gt; module
&lt;span class="kwrd"&gt;private&lt;/span&gt; IRegionNavigationService navigationService;

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; IsNavigationTarget(NavigationContext navigationContext)
{
    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnNavigatedFrom(NavigationContext navigationContext)
{
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnNavigatedTo(NavigationContext navigationContext)
{
   navigationService = navigationContext.NavigationService;
}
&lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;and to perform view-to-view navigation (for example, Go Back):&lt;/p&gt;

&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; onGoBackHandler()
{
    &lt;span class="kwrd"&gt;if&lt;/span&gt; (navigationService.Journal.CanGoBack)
    {
       navigationService.Journal.GoBack();
    }
}&lt;/pre&gt;

&lt;p&gt;Note that in the above implementation of the interface we are returning true from the IsNavigationTarget() method. This method is called by the framework to determine if the current views should be reused for the navigation target. Returning true indicated the views should be reused. Since we pre-create all our views, you should return true. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-3330147595476730786?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/3Ld4zF8XqhY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/3330147595476730786/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2011/09/using-iactiveaware-and-inavigationaware.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3330147595476730786?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3330147595476730786?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/3Ld4zF8XqhY/using-iactiveaware-and-inavigationaware.html" title="Using IActiveAware and INavigationAware" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-qz8I2_G1O1I/TnoFIwSl6vI/AAAAAAAAARw/2UbpPKUHdZc/s72-c/prism_thumb%25255B6%25255D.jpg?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2011/09/using-iactiveaware-and-inavigationaware.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cDRXc8fCp7ImA9WhdVFkw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-4142683789684680844</id><published>2011-09-08T13:50:00.001+01:00</published><updated>2011-09-21T16:31:14.974+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-21T16:31:14.974+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Wiki" /><category scheme="http://www.blogger.com/atom/ns#" term="Windows Live Writer" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint Designer" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="WLW" /><title>This is a test Article on the blog</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/C9zTlYsPB3rM0BU0Zz06T5IX1L0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C9zTlYsPB3rM0BU0Zz06T5IX1L0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/C9zTlYsPB3rM0BU0Zz06T5IX1L0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C9zTlYsPB3rM0BU0Zz06T5IX1L0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh4.ggpht.com/-As22U_cVRYU/Tmi6IZwGTrI/AAAAAAAAARc/zupNVtSPwPc/s1600-h/203-sixtoes%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="203-sixtoes" border="0" alt="203-sixtoes" align="left" src="http://lh6.ggpht.com/-3IvCNt7ONWA/Tmi6IygSb1I/AAAAAAAAARg/HxgD0HYQoyA/203-sixtoes_thumb%25255B4%25255D.jpg?imgmax=800" width="260" height="186" /&gt;&lt;/a&gt;[Concrete/Interesting]&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h4&gt;Here is a picture&lt;/h4&gt;  &lt;h4&gt;Like it?&lt;/h4&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Actually, this is a test for using Windows Live Writer to publish to SharePoint Wiki. Which sounds easy, but you guessed it, it’s not.&lt;/p&gt;  &lt;p&gt;I’ll let you know how I get on.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-5gYtgF-ijZ8/TmpgmdjLYPI/AAAAAAAAARU/lHrRxfq-kxw/s1600-h/newrule%25255B2%25255D.gif"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="newrule" border="0" alt="newrule" src="http://lh3.ggpht.com/-ztYOirBFiQs/Tmpgm0yhxYI/AAAAAAAAARY/RDtY-dAGc10/newrule_thumb.gif?imgmax=800" width="239" height="27" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Frankly, it’s all rather disappointing. I guess someone at Microsoft, looking to make SharePoint a little more useful thought “well it’s good at lists and HTML docs, if you add a template with an on-page editor, hey presto, you’ve got a Wiki”. Only, it’s not that simple and the editor is far too lightweight. &lt;/p&gt;  &lt;p&gt;Windows Live Write (WLW) on the other hand is a well thought out, powerful tool. Clearly the best Blog editor in my mind. It’s the reason I have a Windows VM on my Mac. &lt;/p&gt;  &lt;p&gt;So I gave it a go and WLW doesn’t want to talk to the Wiki because it’s not a blog, doesn’t support the APIs, just a list with an editor. &lt;/p&gt;  &lt;p&gt;However, it was suggested that I could author my pages against my SharePoint blog. Great. It worked brilliantly. WLW talks to SharePoint blogs and does an excellent job of it.&lt;/p&gt;  &lt;p&gt;Once published, all you have to do is open the blog in SharePoint for editing, copy the raw HTML and paste it to the Wiki. &lt;/p&gt;  &lt;h3&gt;It works, but it’s far from perfect&lt;/h3&gt;  &lt;p&gt;The main advantage of this is the editor in WLW. It’s excellent, support plug-in for styles, adopts site templates. I’m using it now and what I type is what I will see on the blog site. &lt;/p&gt;  &lt;p&gt;A very useful benefit of this approach is image publishing. It’s a royal pain in the arse when editing on the SharePoint Wiki. Using this method, images are published to my blog and can be referenced in the Wiki without need to upload anything or mess around.&lt;/p&gt;  &lt;p&gt;But the main problem is I’m publishing to a blog and have a second copy in the Wiki. I may even have a third local copy on my Mac saved by WLW. These copies can easily get out of step.&lt;/p&gt;  &lt;p&gt;Worse still, the images are associated with a my blog and my account, not the Wiki. If my account gets deleted, I guess these resources will disappear.&lt;/p&gt;  &lt;p&gt;Finally, the point of a Wiki is that the documents are live and through collaboration, the Wiki will change over time. So my copy on my blog is very likely to be out of step. This could result in changes being lost as the WLW view of the document is that of the blog, not the Wiki.&lt;/p&gt;  &lt;p&gt;You could of course create a shared blog and keep this as the master document site. I thought of this and it occurred to me that if I was to do this, I might as well try to use that as my Wiki. Well that made the whole exercise seem pointless.&lt;/p&gt;  &lt;h3&gt;Improved on-page Editing&lt;/h3&gt;  &lt;p&gt;You can of course change the Wiki template to use a more advanced editor. This is a very satisfactory solution. One of the recommended ones is the Telerik RadEditor that can be integrated into SharePoint 2010:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.telerik.com/help/aspnet-ajax/moss-installing_radeditor__radgrid_web_parts_on_sharepoint_2010.html" href="http://www.telerik.com/help/aspnet-ajax/moss-installing_radeditor__radgrid_web_parts_on_sharepoint_2010.html"&gt;http://www.telerik.com/help/aspnet-ajax/moss-installing_radeditor__radgrid_web_parts_on_sharepoint_2010.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.telerik.com/help/aspnet-ajax/media/installer1.png" href="http://www.telerik.com/help/aspnet-ajax/media/installer1.png"&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="http://www.telerik.com/help/aspnet-ajax/media/installer1.png" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Problem is, it cost about $800. And I’ve already spent half that purchasing a better Forum web part because the out of the box solution from Microsoft is just rubbish. Starting to sound like throwing good money after bad,&lt;/p&gt;  &lt;h3&gt;Microsoft SharePoint Designer 2010&lt;/h3&gt;  &lt;p&gt;Ok, time to bring the big guns out. I’m only trying to update pages on the Wiki!&lt;/p&gt;  &lt;p&gt;Well, this is a powerful tool. It’s also typical of the development/management tools from Microsoft. Yes, I can edit the Wiki, but it’s not easy. Ultimately, this tool provides a Visual Studio / Microsoft Expression type of view on the site and site document. Not really what you want.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-U7Lg-FI7jMc/TmqdhkVi1tI/AAAAAAAAARk/BYy15NoUxbY/s1600-h/newrule%25255B5%25255D.gif"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="newrule" border="0" alt="newrule" src="http://lh3.ggpht.com/-g-YG3kttmQQ/TmqdiZRTLrI/AAAAAAAAARo/tgXomik7law/newrule_thumb%25255B1%25255D.gif?imgmax=800" width="239" height="27" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Conclusion&lt;/h3&gt;  &lt;p&gt;I’ll keep looking for a better solution to editing on-page in SharePoint. Maybe Microsoft will provide a link between WLW and SharePoint Wiki.&lt;/p&gt;  &lt;p&gt;However, until a solution is found, the SharePoint Wiki experience will be very disappointing. &lt;/p&gt;  &lt;p&gt;Given that for a Wiki, the ease of authoring and content creation is as important as the content itself, you might just decide that SharePoint doesn’t even qualify as a Wiki.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-4142683789684680844?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/5zSnRB8qRYY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/4142683789684680844/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2011/09/this-is-test-article-on-blog.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4142683789684680844?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4142683789684680844?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/5zSnRB8qRYY/this-is-test-article-on-blog.html" title="This is a test Article on the blog" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-3IvCNt7ONWA/Tmi6IygSb1I/AAAAAAAAARg/HxgD0HYQoyA/s72-c/203-sixtoes_thumb%25255B4%25255D.jpg?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2011/09/this-is-test-article-on-blog.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMNQXY_cCp7ImA9WhZVF0w.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-729900184259571041</id><published>2011-05-30T00:58:00.001+01:00</published><updated>2011-05-30T00:58:10.848+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-30T00:58:10.848+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Last Theorem" /><category scheme="http://www.blogger.com/atom/ns#" term="Fermat" /><category scheme="http://www.blogger.com/atom/ns#" term="mathematics" /><title>Fermat’s Last Theorem</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/vS_A236gtc41MhxZzqpe2a4S5iQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vS_A236gtc41MhxZzqpe2a4S5iQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/vS_A236gtc41MhxZzqpe2a4S5iQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vS_A236gtc41MhxZzqpe2a4S5iQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-AlTSc_H26JI/TeLdiMaJ1AI/AAAAAAAAAQY/FC4lNWQMfOk/s1600-h/image%25255B7%25255D.png"&gt;&lt;strong&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px 10px 10px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" align="left" src="http://lh4.ggpht.com/-LlsCRHJ_iQE/TeLdixcWD7I/AAAAAAAAAQc/YxAtcyKXoTs/image_thumb%25255B5%25255D.png?imgmax=800" width="223" height="195" /&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;[Concrete/Interesting] &lt;/strong&gt;A while back I saw a BBC Horizon programme about Fermat’s Last Theorem and the mathematically heroic work by Andrew Wiles in raising the Taniyama-Shimura conjecture from a mere conjecture to a theorem and using this to show a contradiction between the predictions of the epsilon conjecture and Wiles’ proof that all such elliptic curves must be modular. This contraction implies there are no solutions to Fermat’s equation, hence Fermat’s equation is true.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Like many, I was excited about this proof and although difficult to follow (Elliptic Curves are hard to understand, no kidding), the proof was without a doubt a clever piece of late 20th century mathematical wizardry, full of deep insight and imagination. &lt;/p&gt;  &lt;p&gt;But something was wrong. It was all to clever, all too complex, all to contemporary. So to mark the birth of my new baby boy, Khaliq, I decided to provide a simple proof, maybe not complete in mathematical rigor and probably nowhere near robust enough for Cambridge Mathematics, possibly not even correct, but something I think 17th century mathematicians would have found interesting. See what you think.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/p&gt;  &lt;p align="center"&gt;&lt;em&gt;&lt;font size="3"&gt;To my son, Khaliq George Mahmood, born 26th May at 11.24pm (7lb, 12oz). Hope you find life as fascinating as I have.&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/p&gt;  &lt;p&gt;Fermat’s conjecture is so tidy, so elegant and simply described as:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-f8uziwLVl6U/TeLdjILmz_I/AAAAAAAAAQg/HNaSij7zJy8/s1600-h/fermat%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px; padding-top: 0px" title="fermat" border="0" alt="fermat" src="http://lh5.ggpht.com/-sPZrXPldY7A/TeLdjdOdD3I/AAAAAAAAAQk/Xo78Q3FbpVE/fermat_thumb%25255B3%25255D.png?imgmax=800" width="214" height="47" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;That is to say, for all integers, except 2, the equation has no non-zero integer solutions. For 2, where we square x and y, the sum of the squares of x and y is equal to the square of z &lt;/p&gt;  &lt;p&gt;For n = 2, this may be familiar to some of you, and is known as The theorem of Pythagoras. Pythagoras, mathematician, philosopher and scientist, provided a proof for his equation based on logic and simple geometry. In fact, the equation was known to many at the time, but is was the application of mathematical rigor that gave Pythagoras the title over the equation.&lt;/p&gt;  &lt;p&gt;It is from the relationship between the equation and a right angle triangle that I will start my ‘proof’’.&lt;/p&gt;  &lt;h3&gt;Mathematical Rigor and Proof&lt;/h3&gt;  &lt;p&gt;Mathematics is complicated, at least for the majority of us.I don’t want to pretend I am a mathematician or blessed with anything but a cursory level of competence in mathematics. &lt;/p&gt;  &lt;p&gt;For a proof to stand on it’s own two legs it must be a convincing argument and this must be based on deductive reasoning which shows a statement to be true in all cases. Ok, so I don’t think I’m going to be able to do that. But I hope I can present an somewhat unproven proposition that I believe to be true – that, at least is a conjecture, and is something others may be able to prove. &lt;/p&gt;  &lt;p&gt;So away with the shackles of mathematical rigor, eyes down for what I hope is an interesting conjecture:&lt;/p&gt;             &lt;h3&gt;Not Now Nigel’s Conjecture&lt;/h3&gt;  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Above n=2 where n is the geometric dimension, there are no simple object with 3 ‘flat’ boundaries.&lt;/em&gt;&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;By way of explanation, for n=2, the equation maps to a 2 dimensional polygon with three flat or straight line boundaries, commonly known as a Triangle:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/--fSZ0vyDmIc/TeLdj_41HiI/AAAAAAAAAQo/RHWjgaJkFw4/s1600-h/Pythagoras_theorem%25255B3%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Pythagoras_theorem" border="0" alt="Pythagoras_theorem" src="http://lh6.ggpht.com/-VLfxumY-VKw/TeLdkjaNHJI/AAAAAAAAAQs/aJjrPMBtskI/Pythagoras_theorem_thumb%25255B1%25255D.png?imgmax=800" width="222" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;But for higher dimensions, there are no object with 3 ‘flat’ sides. For example, for 3 dimensions, there is no polyhedron with 3 planar surfaces. The nearest is something like a Tetrahedron with 4 sides. &lt;/p&gt;  &lt;p&gt;In proving this conjecture and in saying that Fermat’s equation is represented by this geometry, there lies a simple proof to Fermat’s Last Theorem. Q.E.D.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-729900184259571041?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/PVEDZs-69Sk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/729900184259571041/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2011/05/fermats-last-theorem.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/729900184259571041?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/729900184259571041?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/PVEDZs-69Sk/fermats-last-theorem.html" title="Fermat’s Last Theorem" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/-LlsCRHJ_iQE/TeLdixcWD7I/AAAAAAAAAQc/YxAtcyKXoTs/s72-c/image_thumb%25255B5%25255D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2011/05/fermats-last-theorem.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYCRXw5eCp7ImA9Wx9WEE0.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-2331236571843425009</id><published>2010-12-25T16:31:00.001Z</published><updated>2011-01-14T10:16:04.220Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-14T10:16:04.220Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Julian Guy Tulloch" /><category scheme="http://www.blogger.com/atom/ns#" term="Carving" /><title>Carving Rings</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/CH58oN02py9BRh6c86oaaMya5dM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CH58oN02py9BRh6c86oaaMya5dM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/CH58oN02py9BRh6c86oaaMya5dM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CH58oN02py9BRh6c86oaaMya5dM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYbgD7qi8I/AAAAAAAAALI/JTZCFY6qtjY/s1600-h/45614449%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px 19px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="45614449" border="0" alt="45614449" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYbgpbQtnI/AAAAAAAAALM/CoH_clRoZ7E/45614449_thumb%5B5%5D.jpg?imgmax=800" width="154" height="205" /&gt;&lt;/a&gt;[Abstract/ Sharing]&lt;/strong&gt;&amp;#160; To close off 2010, I’m posting an article on how to carve a pair of linked rings from a single block of wood. It’s very simple so if you have a chance, why not have a go yourself. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;This post is dedicated to the memory of &lt;strong&gt;Julian Guy Tulloch&lt;/strong&gt;, who died peacefully on the 12th December 2010, aged 78. &lt;/p&gt;  &lt;p&gt;Julian first introduced me to carving.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/p&gt;  &lt;p&gt;We start with a small block of wood, 5cm by 5cm by 10cm. The exact dimensions are not too important, you just want a block of wood that is rectangular in nature with a square end:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbhLvsXBI/AAAAAAAAALQ/cEiLQQoIVrY/s1600-h/IMG_0715%5B9%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0715" border="0" alt="IMG_0715" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbhpZG3LI/AAAAAAAAALU/1Bz800pL5TU/IMG_0715_thumb%5B6%5D.jpg?imgmax=800" width="432" height="336" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I’m using Tilia or Basswood which is good for carving – not too dense and very little grain. You can find this type of wood at craft shops, often described as ‘Carving Blocks’.&lt;/p&gt;  &lt;h3&gt;Extruded Cross&lt;/h3&gt;  &lt;p&gt;The first step is to mark out an extruded cross using a pencil:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbiEnn4rI/AAAAAAAAALY/8YfUtJbvx_E/s1600-h/IMG_0716%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0716" border="0" alt="IMG_0716" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbi2y61YI/AAAAAAAAALc/WKgn-9LmEkE/IMG_0716_thumb%5B2%5D.jpg?imgmax=800" width="427" height="330" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I made the cross 1cm wide. The cross is drawn on the two square ends of the block of wood and joined up over the length of the block. Note, the extruded cross is the shaded area on the diagram. &lt;/p&gt;  &lt;p&gt;Now, using a small Tenon Saw, you should remove the four corner pieces. These are the un-shaded areas in the diagram above. This should leave the extruded cross:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbj1gCmsI/AAAAAAAAALg/45eT0UQhR4I/s1600-h/IMG_0711%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0711" border="0" alt="IMG_0711" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbkrAbJNI/AAAAAAAAALk/XR_OeT2aJ3c/IMG_0711_thumb%5B1%5D.jpg?imgmax=800" width="214" height="293" /&gt;&lt;/a&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYblJdxWNI/AAAAAAAAALo/HIjQQXY7wUk/s1600-h/IMG_0712%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0712" border="0" alt="IMG_0712" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYblhKfKcI/AAAAAAAAALs/vS1ccehspaE/IMG_0712_thumb%5B1%5D.jpg?imgmax=800" width="214" height="293" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice that I don’t cut right up to the line. I leave a little margin for error when using the saw. To finish off the extruded cross, I use a carving/milling tip on my Dremel hand tool:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbm5_dHWI/AAAAAAAAALw/Hyh2q5DTJvI/s1600-h/IMG_0714%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0714" border="0" alt="IMG_0714" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbnWxr3jI/AAAAAAAAAL0/GTtV69LqaJ8/IMG_0714_thumb%5B3%5D.jpg?imgmax=800" width="218" height="310" /&gt;&lt;/a&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYboajPfxI/AAAAAAAAAL4/FEFAvxEeuRw/s1600-h/IMG_0713%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0713" border="0" alt="IMG_0713" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbo7xO2bI/AAAAAAAAAL8/tTusP0fMeTQ/IMG_0713_thumb%5B4%5D.jpg?imgmax=800" width="214" height="310" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So far, so good. Now for a little vacuuming!&lt;/p&gt;  &lt;h3&gt;Overlapping Discs&lt;/h3&gt;  &lt;p&gt;Next we need to transform our extruded cross into two overlapping discs. The discs are inside the cross, you can see them on the diagram below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYbpi0EnaI/AAAAAAAAAMA/U_mYUQXmRJY/s1600-h/IMG_0720%5B13%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0720" border="0" alt="IMG_0720" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbqMZB-8I/AAAAAAAAAME/a5ieWCF0no0/IMG_0720_thumb%5B10%5D.jpg?imgmax=800" width="420" height="356" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Because my cross was 5cm across the face, the discs are 5cm in diameter:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbq7-5jII/AAAAAAAAAMI/6cpYfYRqTlU/s1600-h/IMG_0719%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0719" border="0" alt="IMG_0719" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbrnVBqqI/AAAAAAAAAMM/kZtv9z8nGkA/IMG_0719_thumb%5B2%5D.jpg?imgmax=800" width="427" height="330" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The important bit is the overlap. One disc needs to overlap the other. There needs to be sufficient overlap to carve out the rings, one from each disc. I have decided to have each disc overlap half way through the other disc.&lt;/p&gt;  &lt;p&gt;Again, I will use my Tenon Saw to cut away the excess wood. First step is to remove two end pieces – ‘A’ and ‘B’ on the diagram below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbsYwgF2I/AAAAAAAAAMQ/3c_81jlc59A/s1600-h/IMG_0721%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0721" border="0" alt="IMG_0721" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbszSl77I/AAAAAAAAAMU/xUWtAwYnvYs/IMG_0721_thumb%5B3%5D.jpg?imgmax=800" width="425" height="336" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;On the carving, the areas to remove are shown marked A and B:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbt-QdSCI/AAAAAAAAAMY/xa3cDkxO3uM/s1600-h/IMG_0722%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0722" border="0" alt="IMG_0722" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbuVJKfPI/AAAAAAAAAMc/t0VR7YFYkEU/IMG_0722_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbvR84uNI/AAAAAAAAAMg/M20GXb2dnOs/s1600-h/IMG_0723%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0723" border="0" alt="IMG_0723" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbvw15ZfI/AAAAAAAAAMk/_oBK8-WF8EQ/IMG_0723_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbwhM18fI/AAAAAAAAAMo/yQMVYpYiBc0/s1600-h/IMG_0724%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0724" border="0" alt="IMG_0724" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYbxFLsgPI/AAAAAAAAAMs/VmNjJTFz7vI/IMG_0724_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;After cutting away the wood with the saw we have the following:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYbyE7VyEI/AAAAAAAAAMw/Xd_vWczEp9g/s1600-h/IMG_0725%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0725" border="0" alt="IMG_0725" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbyjAt2aI/AAAAAAAAAM0/mX6z3Z-uK5I/IMG_0725_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYbzlOdzTI/AAAAAAAAAM4/_KiioYeVEBI/s1600-h/IMG_0727%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0727" border="0" alt="IMG_0727" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYb0Nus8bI/AAAAAAAAAM8/dy6tLEV7eKA/IMG_0727_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYb1AYFJGI/AAAAAAAAANA/dipkbG6SBrQ/s1600-h/IMG_0728%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0728" border="0" alt="IMG_0728" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYb1kIEfrI/AAAAAAAAANE/DhtTYYCQ8xk/IMG_0728_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now we have to remove the rest of the excess wood. In the diagram below, this is marked ‘C’, ‘D’ and ‘E’:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYb2VMDkAI/AAAAAAAAANI/tLgXld4igjg/s1600-h/IMG_0729%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0729" border="0" alt="IMG_0729" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYb243c68I/AAAAAAAAANM/v4SPK5p4VXo/IMG_0729_thumb%5B2%5D.jpg?imgmax=800" width="416" height="322" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYb3W3kA3I/AAAAAAAAANQ/Hr5o7tXdvgk/s1600-h/IMG_0730%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0730" border="0" alt="IMG_0730" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYb4MCNdQI/AAAAAAAAANU/zcgCIuEfbZY/IMG_0730_thumb%5B2%5D.jpg?imgmax=800" width="343" height="479" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;‘C’ is a complete bit of the extruded cross and we remove this using the saw:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYb5t2vSnI/AAAAAAAAANY/UUlc04OTRI0/s1600-h/IMG_0731%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0731" border="0" alt="IMG_0731" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYb6KazRuI/AAAAAAAAANc/1hdwFPSEtCc/IMG_0731_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYb7AAffLI/AAAAAAAAANg/vj2fg49jlaQ/s1600-h/IMG_0733%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0733" border="0" alt="IMG_0733" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYb7qs4rHI/AAAAAAAAANk/9SLDg5Zd17I/IMG_0733_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Can&amp;#160; you see the two overlapping discs yet?&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYb8WyzMWI/AAAAAAAAANo/IFt6RF_Icxw/s1600-h/IMG_0732%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0732" border="0" alt="IMG_0732" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYb9OTOMYI/AAAAAAAAANs/oy3NWo7S7zc/IMG_0732_thumb%5B4%5D.jpg?imgmax=800" width="372" height="537" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Ok, now remove the little pieces ‘D’ and ‘E’. This is the same as ‘A’ and ‘B’. Look, two overlapped blocks:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYb-UMSyyI/AAAAAAAAAN0/e5t8PqpOim4/s1600-h/IMG_0734%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0734" border="0" alt="IMG_0734" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYb_NxTtGI/AAAAAAAAAN4/0fF_4DhcXS0/IMG_0734_thumb%5B3%5D.jpg?imgmax=800" width="425" height="338" /&gt;&lt;/a&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYcAPCOo4I/AAAAAAAAAN8/kFvzUGtDY8g/s1600-h/IMG_0735%5B13%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0735" border="0" alt="IMG_0735" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcAhV_L3I/AAAAAAAAAOA/1uSMk9Orzlw/IMG_0735_thumb%5B10%5D.jpg?imgmax=800" width="428" height="345" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;It is worth marking out the discs and removing the excess wood to reveal the discs. First, mark out the discs with a pencil. I’m draw these by hand:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcBn1b1xI/AAAAAAAAAOE/Cd3JT0tzMtA/s1600-h/IMG_0736%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0736" border="0" alt="IMG_0736" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYcCKTnLzI/AAAAAAAAAOI/z3le1qkStok/IMG_0736_thumb%5B3%5D.jpg?imgmax=800" width="209" height="298" /&gt;&lt;/a&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYcDJRiqGI/AAAAAAAAAOM/DQrK3hxwMfw/s1600-h/IMG_0737%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0737" border="0" alt="IMG_0737" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcDouP4iI/AAAAAAAAAOQ/qc76HF9j2cE/IMG_0737_thumb%5B4%5D.jpg?imgmax=800" width="212" height="298" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Then remove the excess wood. I’ve used my Dremel for this but carving chisels or even sanding will work. And now we have two interlinked discs:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcEsS7yLI/AAAAAAAAAOU/f8sFQjdgN2M/s1600-h/IMG_0741%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0741" border="0" alt="IMG_0741" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYcFOi_8jI/AAAAAAAAAOY/nTFFQad9AgM/IMG_0741_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Again, let’s tidy up.&lt;/p&gt;  &lt;h3&gt;Finding the Rings&lt;/h3&gt;  &lt;p&gt;My discs are 1cm wide. Because of this, my rings are going to be roughly 1cm in diameter before any sanding. &lt;/p&gt;  &lt;p&gt;At this point the rings are embedded in the edge of the discs. We need to define the rings by removing the centre of the discs. We are going to do this in two steps. The first step is to clear the majority of the centre of each disc. We will still be left with ring connected together. The second step is to finally free the rings.&lt;/p&gt;  &lt;p&gt;For each disc we want to remove the shaded area ‘A’ from as shown in the diagram below:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYcFjDcADI/AAAAAAAAAOc/rfEosLd_1jk/s1600-h/IMG_0743%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0743" border="0" alt="IMG_0743" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYcGbY1PDI/AAAAAAAAAOg/oWLKP26GZ08/IMG_0743_thumb%5B4%5D.jpg?imgmax=800" width="426" height="329" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You must be very careful not to remove any of the ring or any of the second ring. The result should look like this:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYcHDX2bkI/AAAAAAAAAOk/GqAHvGb1KBc/s1600-h/IMG_0747%5B7%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0747" border="0" alt="IMG_0747" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYcHpEWmUI/AAAAAAAAAOo/_l07bkIsNsQ/IMG_0747_thumb%5B4%5D.jpg?imgmax=800" width="445" height="343" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;OUCH!!! I cut my hand – be careful.&lt;/p&gt;  &lt;p&gt;Ok, tricky&amp;#160; bit coming up so time to tidy up and have a rest.&lt;/p&gt;  &lt;h3&gt;Tricky Bit – Free the Rings&lt;/h3&gt;  &lt;p&gt;This bit is a little tricky and can go badly wrong. If you remove too much wood, you may break the rings. The two rings are joined, but only by a small wall of wood. Take a close look at the rings and you can make this out.&lt;/p&gt;  &lt;p&gt;The process for removing this wall and separating the rings is a little hard to describe but I will give it a go:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcItZfUnI/AAAAAAAAAOs/jnUESxZTmQk/s1600-h/IMG_0756%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0756" border="0" alt="IMG_0756" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcJGSN3iI/AAAAAAAAAOw/efVMQZNHIAA/IMG_0756_thumb%5B1%5D.jpg?imgmax=800" width="368" height="504" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In the diagram above I’m looking down on one of the rings. There are four points on the top surface that need to be chiselled – ‘A’, ‘B’, ‘C’ and ‘D’ Similarly, from the bottom side of this ring, there are four more points – ‘E’, ‘F’, ‘G’ and ‘H’.&lt;/p&gt;  &lt;p&gt;Notice that these 8 points are the 8 corners of a cube. Not a normal cube with straight sides, rather one with concave sides:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYcKQ1ahVI/AAAAAAAAAO0/pkqItdk2sKM/s1600-h/IMG_0762%5B4%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0762" border="0" alt="IMG_0762" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYcK82YF6I/AAAAAAAAAO4/HZ12C62FijU/IMG_0762_thumb%5B1%5D.jpg?imgmax=800" width="384" height="526" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Try to see this in the rings before you start to carve. You want to chisel into these eight points as evenly as possible and when chiselling, you need to chisel diagonally towards the centre of the cube. Bit, by bit you have to remove the cube from the corners, outside to the centre. When the cube has been chiselled away, the rings will magically free:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYcLsue84I/AAAAAAAAAO8/oTIJ6Zveq0M/s1600-h/IMG_0758%5B6%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0758" border="0" alt="IMG_0758" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcMFk9IhI/AAAAAAAAAPA/4GEUvOPAvqk/IMG_0758_thumb%5B3%5D.jpg?imgmax=800" width="429" height="606" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Remember, don’t rush it and it should work.&lt;/p&gt;  &lt;p&gt;Another tidy up.&lt;/p&gt;  &lt;p&gt;Last step – Sanding and Varnish&lt;/p&gt;  &lt;p&gt;To complete the rings, you need to sand away excess wood to reveal the rings. You can sand away until you are happy with the results. Again, be careful as you don’t want to spoil the rings at this stage. Once you have sanded to reveal the rings, you need to varnish the rings. &lt;/p&gt;  &lt;p&gt;The trick with varnishing is to apply a coat, then sand away the coat. Then repeat. The more times you repeat this, the harder the finish.&lt;/p&gt;  &lt;p&gt;Here are my rings, not yet varnished but fully revealed:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcNVio-zI/AAAAAAAAAPE/ZCUrfHmZius/s1600-h/IMG_0764%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0764" border="0" alt="IMG_0764" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcOOnV7FI/AAAAAAAAAPI/PCVg2Hq6cuc/IMG_0764_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TRYcO1wMwFI/AAAAAAAAAPM/vI-x-p4o9u0/s1600-h/IMG_0768%5B3%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0768" border="0" alt="IMG_0768" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYcPd8PcxI/AAAAAAAAAPQ/pfFdjfyGPkg/IMG_0768_thumb.jpg?imgmax=800" width="183" height="244" /&gt;&lt;/a&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TRYcQYP8qdI/AAAAAAAAAPU/RXZg6CJNtHk/s1600-h/IMG_0770%5B5%5D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="IMG_0770" border="0" alt="IMG_0770" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TRYcQ5RhowI/AAAAAAAAAPY/dnc3VzH8m90/IMG_0770_thumb%5B2%5D.jpg?imgmax=800" width="369" height="285" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;&lt;strong&gt;And after Varnishing&lt;/strong&gt;&lt;/h3&gt;  &lt;p&gt;After more than 15 repeated varnish and sanding cycles, here is the finished article. Very hard finish with the wood grain showing through like Tiger Stone:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TTAiXYzFSrI/AAAAAAAAAPo/bvUPthnFd40/s1600-h/IMG_0791%5B5%5D.jpg"&gt;&lt;img style="display: inline" title="IMG_0791" alt="IMG_0791" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TTAiYCDBbPI/AAAAAAAAAPs/Q2Kh5vUZb8k/IMG_0791_thumb%5B2%5D.jpg?imgmax=800" width="457" height="352" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I’m very pleased with the results&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TTAiYyehZKI/AAAAAAAAAPw/4ZYIfw7NHH0/wlEmoticon-smile%5B2%5D.png?imgmax=800" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Have fun and Happy New Year.&lt;/strong&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-2331236571843425009?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/MVh-FMylU8Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/2331236571843425009/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/12/carving-rings.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/2331236571843425009?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/2331236571843425009?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/MVh-FMylU8Q/carving-rings.html" title="Carving Rings" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/TRYbgpbQtnI/AAAAAAAAALM/CoH_clRoZ7E/s72-c/45614449_thumb%5B5%5D.jpg?imgmax=800" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/12/carving-rings.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEANQ3g9fip7ImA9Wx5WGE0.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-1974508284859455118</id><published>2010-09-24T01:30:00.003+01:00</published><updated>2010-09-30T01:33:12.666+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-30T01:33:12.666+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Out Of Browser" /><category scheme="http://www.blogger.com/atom/ns#" term="AJAX" /><category scheme="http://www.blogger.com/atom/ns#" term="Service" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term="Duplex" /><category scheme="http://www.blogger.com/atom/ns#" term="Silverlight 4" /><category scheme="http://www.blogger.com/atom/ns#" term="OPOS" /><category scheme="http://www.blogger.com/atom/ns#" term="chubby client" /><category scheme="http://www.blogger.com/atom/ns#" term="Threading" /><category scheme="http://www.blogger.com/atom/ns#" term="Net.TCP Bindings" /><category scheme="http://www.blogger.com/atom/ns#" term="HTTP" /><category scheme="http://www.blogger.com/atom/ns#" term="Sockets" /><category scheme="http://www.blogger.com/atom/ns#" term="Polling Duplex Service" /><category scheme="http://www.blogger.com/atom/ns#" term="RIA" /><category scheme="http://www.blogger.com/atom/ns#" term="Silverlight" /><category scheme="http://www.blogger.com/atom/ns#" term="WCF" /><title>WCF Self Hosted Duplex Service for Chubby Client</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/lh93BYq6cLA9N_tAu25WBPR6UBQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/lh93BYq6cLA9N_tAu25WBPR6UBQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/lh93BYq6cLA9N_tAu25WBPR6UBQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/lh93BYq6cLA9N_tAu25WBPR6UBQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TJvxHDz34VI/AAAAAAAAAKc/TZ0sa6tmQT4/s1600-h/barcodescanner12.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="barcodescanner" border="0" alt="barcodescanner" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TJvxHUp_rXI/AAAAAAAAAKg/Klj2Fl4lgZ8/barcodescanner_thumb10.jpg?imgmax=800" width="148" height="175" /&gt;&lt;/a&gt;&lt;strong&gt;[Concrete/Interesting]&lt;/strong&gt; I received an email from a friend of mine. He was trying to build a Chubby Client, following some of the ideas I put down in my previous article. What was needed in this case was a Duplex Service that send data back from a thread outside of the main thread. My friend sent me his code and declared it couldn’t be done. &lt;/p&gt;&lt;p&gt;That sounded like a ‘dare’ to me, so I thought I’d have a go. &lt;/p&gt;&lt;p&gt;In this article I will take you through some of the major steps and discuss the overall architecture and solution. If you want the code, I've published this to Google Code so feel free to download and take a look: &lt;a href="http://deviceservices.googlecode.com"&gt;http://deviceservices.googlecode.com&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;The Goal&lt;/h3&gt;&lt;p&gt;The main goal was to develop a device service that can be called from a Silverlight in or out of browser client. The device service would have initialization and action methods similar to those supported by an Windows OPOS device such as a scanner, printer, cash draw, customer display or MSR. For more details about Microsoft OPOS and POS device support, see &lt;a href="http://msdn.microsoft.com/en-us/library/ms828083(WinEmbedded.10).aspx"&gt;Microsoft POS for .NET Overview&lt;/a&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;The full implementation is a chubby client with a Silverlight browser client capable of running in or out of browser. This client would be launched from a console or service based service host. The host provides all application and devices services for the client through WCF Duplex Services. Within the host, the device services are responsible for abstracting and supporting the OPOS devices, application services provide APIs to application functions and databases:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TJvxIapja2I/AAAAAAAAAKk/MYOYIH-LFsw/s1600-h/apparchitecture7.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="apparchitecture" border="0" alt="apparchitecture" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TJvxJBJWhOI/AAAAAAAAAKo/6sF4cRXNLNA/apparchitecture_thumb5.png?imgmax=800" width="430" height="413" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;h3&gt;Duplex Services&lt;/h3&gt;&lt;p&gt;The key problem we need to address is handling asynchronous events at the service. Why? Well we want to build a Silverlight client. We want this because we may want to run in a thin client configuration with the client hosted in a browser calling network or web services. For the application services, a normal one way web service or REST service would be fine. The client would simply call the service and the service would return data. But there are some application services where asynchronous events are a better way forward. Take notifications in workflow as an example. Here you don’t want to hold up the client and wait for the service to notify you of a state change. This state change could come at any time. Another example is large list data. You don’t want to call and have returned back all the data to the client and bound to some UI element. The data transfers could be huge and the client would be required to hold all this data. &lt;/p&gt;&lt;p&gt;Now considering the support of devices in our service, we again need to think about an asynchronous notification architecture as we request an action at the device such as ‘Scan when a barcode is available’ and a some time in the future, when a barcode is presented to the scanner, the device wants to tell us ‘here is a barcode I just scanned’. A further complication here is that the event notifications are typically not raised in the same thread as that used to handle the client request.&amp;#160; &lt;/p&gt;&lt;p&gt;In ASP.Net we would look towards AJAX to solve these problems. In the world of services, we need to implement Duplex or dual channel services. The diagram below illustrates this for a service supporting a POS scanner:&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TJvxKX0QZtI/AAAAAAAAAKs/yY3vwxVemlU/s1600-h/opos23.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="opos" border="0" alt="opos" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TJvxLKXSVCI/AAAAAAAAAKw/krU2W0ujVNI/opos_thumb21.png?imgmax=800" width="422" height="352" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;WCF Duplex Services allow us to define a client callback contract again the service contract. What this means is the client exchanges a service contract with the service; this allows the service to notify the client of service events.&lt;/p&gt;&lt;h3&gt;Duplex Service Options&lt;/h3&gt;&lt;p&gt;WCF provides a number of ways to implement duplex services against Silverlight 4 clients:&lt;/p&gt;&lt;h5&gt;HTTP Polling Duplex Service&lt;/h5&gt;&lt;p&gt;This is the one I’ll be looking at in more detail. It is suitable for Chubby Clients, however it doesn’t scale out client connections well and has trouble working across network boundaries. However, out of the box, this is the simplest duplex service to code and support in in a client.&lt;/p&gt;&lt;h5&gt;Duplex Service using Sockets &lt;/h5&gt;&lt;p&gt;You can also use private sockets to set up a duplex channel between Silverlight and the service. Very fast, scales well but only works on a limited range of ports (Silverlight sandbox restriction) and a lot of code needs to be hand forged both service and client side.&lt;/p&gt;&lt;h5&gt;Duplex Service with Net.TCP Bindings&lt;/h5&gt;&lt;p&gt;Ultimately, this is the best balance between non-functional like scalability, performance and network awareness. However, the code is more involved so I won’t be illustrating it here.&lt;/p&gt;&lt;h3&gt;Proof of Concept&lt;/h3&gt;&lt;p&gt;In the proof of concept, focus is given to the Device abstraction layer and support for events raised back to the client. These events would normally be raised outside of the main thread and the purpose of the proof of concept is to illustrate this can be done in a chubby Silverlight-WCF architecture. For this, I’m going to use a System.Threading.Timer to raise the client notifications.&lt;/p&gt;&lt;p&gt;To keep things simple, the implementation will also restrict itself to one client pre-service. Finally, no work will be done to provide application services or actual integration with Microsoft OPOS. I will leave that as an exercise for the reader.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TJvxL_52_4I/AAAAAAAAAK0/FpApInXHuMk/s1600-h/DeviceService5.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="DeviceService" border="0" alt="DeviceService" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TJvxMhBX4WI/AAAAAAAAAK4/pSwi-7HYzZs/DeviceService_thumb3.png?imgmax=800" width="392" height="466" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Self Hosted Service&lt;/h3&gt;&lt;p&gt;I’ll be honest, I like to see how things work, at least I do the first few times I do something.&lt;/p&gt;&lt;p&gt;Self Hosted WCF services let you get into the details as you have to write the service, not press a few buttons on a wizard. &lt;/p&gt;&lt;p&gt;Also, a self hosted service, all the bindings and endpoint behaviours are down to you. You have to code it rather than define it is a CONFIG file.&lt;/p&gt;&lt;p&gt;Lastly, we are building a console application so there is full control over the running, debugging and management of the application.&lt;/p&gt;&lt;p&gt;Of course, for a production system you may consider IIS hosted. This will provide the best performance and scalability and ease of deployment. &lt;/p&gt;&lt;p&gt;For chubby clients, self-hosted, either as console or service probably remains the choice solution as you need to package and deploy service and client together and for each service, there is likely to be only one client.&lt;/p&gt;&lt;h5&gt;The Host&lt;/h5&gt;&lt;p&gt;As ever, the host is simplicity itself:&lt;/p&gt;&lt;pre class="brush: csharp; wrap-lines: false"&gt;class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(&amp;quot;Starting WCF Host for DuplexDeviceEvent...&amp;quot;);
        string baseAddress = &amp;quot;http://localhost:8000&amp;quot;;
        DeviceEventRunner runner = new DeviceEventRunner();

        using (ServiceHost host = new ServiceHost(typeof(DeviceEventService), new Uri(baseAddress)))
        {
            host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), &amp;quot;&amp;quot;).Behaviors.Add(new WebHttpBehavior());
            host.AddServiceEndpoint(typeof(IDeviceEventService), new PollingDuplexHttpBinding(), &amp;quot;DeviceEventService&amp;quot;);

            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            host.Description.Behaviors.Add(smb);
            host.Open();

            Console.WriteLine(&amp;quot;WCF Host Running...&amp;quot;);
            Console.WriteLine(&amp;quot;Press &lt;enter&gt; to shutdown&amp;quot;);
            Console.ReadLine();

            host.Close();
        }
    }
}&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;All that’s going on here is a service host is being initialized and run. Since we are calling this host from Silverlight we need to ensure there is a default Policy available. Beyond that, we are creating an endpoint for the service that follows the PollingDuplexHTTPBinding&lt;strong&gt; &lt;/strong&gt;protocol&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;&lt;h5&gt;ServiceMetadataBehaviour&lt;/h5&gt;&lt;p&gt;As a side note, this little bit of code enables the service to publish itself. This is very useful as it will allow us to create a service reference in our client.&lt;/p&gt;&lt;h5&gt;Device Runner&lt;/h5&gt;&lt;p&gt;You will see a static object defined in the programs main method. This is the device runner that will simulate the device events. The code behind this class simply starts a System.Threading.Timer. In the timer’s callback, if a client is connected, the callback event for the service is executed:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;pre class="brush: csharp; wrap-lines: false"&gt;private static void updateClient(Object stateinfo)
{
    try
    {
        if (theClient != null)
        {
            Console.WriteLine(&amp;quot;Updating Client {0}&amp;quot;, clientId);
            DeviceEventData eventData = new DeviceEventData();
            eventData.message = &amp;quot;Test Data&amp;quot;;
            eventData.timestamp = DateTime.Now;
            theClient.SendDeviceEventData(eventData);
        }
    }
    catch (CommunicationException ex)
    {
        //client error so dispose of client
        Console.WriteLine(&amp;quot;Client Error: {0}&amp;quot;, ex.Message);
        Console.WriteLine(&amp;quot;Closing connection to client {0}&amp;quot;, clientId);
        theClient = null;
        clientId = String.Empty;
    }
}

public static void Register(IDeviceEventServiceClient client, string id)
{
    theClient = client;
    clientId = id;
    Console.WriteLine(&amp;quot;Client {0} registered.&amp;quot;, id);
}&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The service runner is also responsible for accepting the client registration through the &lt;strong&gt;Connect&lt;/strong&gt; interface.&lt;/p&gt;&lt;p&gt;Notice that exceptions can be raised during the callback processing. The main exception is a failure to reach the client during callback, typically because the client has gone. This is picked up as a &lt;strong&gt;CommunicationException.&lt;/strong&gt; In this implementation the service runner simply removed its reference to the client.&lt;/p&gt;&lt;h5&gt;Service Contracts and Implementation&lt;/h5&gt;&lt;p&gt;When we define the service contract we also name the callback contract. This is defined in the interface for the service:&lt;/p&gt;&lt;pre class="brush: csharp; wrap-lines: false"&gt;[ServiceContract(Namespace = &amp;quot;Silverlight&amp;quot;, CallbackContract = typeof(IDeviceEventServiceClient))]
public interface IDeviceEventService
{
    [OperationContract(IsOneWay = true)]
    void Connect(string id);
}

[ServiceContract]
public interface IDeviceEventServiceClient
{
    [OperationContract(IsOneWay = true)]
    void SendDeviceEventData(DeviceEventData data);
}&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Two things happen when the client connects. First, a callback interface is created against the callback channel. This callback interface is then passed to the service runner where is is remembered and used during the service callback processing.&lt;/p&gt;&lt;h5&gt;Callback data&lt;/h5&gt;&lt;p&gt;In this example, the callback to the client is responsible for sending data back to the client for display. In this case, the data is a timestamp identifying when the event was fired and some test data. This is packed into a data structure called &lt;strong&gt;DeviceEventData&lt;/strong&gt;&lt;/p&gt;&lt;pre class="brush: csharp; wrap-lines: false"&gt;public class DeviceEventData
{
    public DateTime timestamp { get; set; }
    public string message{ get; set; }
}

...

DeviceEventData eventData = new DeviceEventData();
eventData.message = &amp;quot;Test Data&amp;quot;;
eventData.timestamp = DateTime.Now;
theClient.SendDeviceEventData(eventData);&lt;/pre&gt;&lt;p&gt;To get this data to the client, all that we need to do is create a data object, initialize it and pass the data as a parameter in the service callback interface.&lt;/p&gt;&lt;h3&gt;Building a test client&lt;/h3&gt;&lt;p&gt;Silverlight 4 really does make life easy, even when talking to duplex services. The key to this is to create a service reference.&lt;/p&gt;&lt;p&gt;The service reference takes care of all the low level stuff, including handling all asynchronous aspects of the relationship between the client and service.&lt;/p&gt;&lt;p&gt;Once we have a service reference, we can hook up to the service and service callback:&lt;/p&gt;&lt;pre class="brush: csharp; wrap-lines: false"&gt;public partial class MainPage : UserControl
{
    ObservableCollection&lt;deviceeventservice.deviceeventdata&gt; eventData;

    public MainPage()
    {
        InitializeComponent();

        eventData = new ObservableCollection&lt;deviceeventservice.deviceeventdata&gt;();

        EndpointAddress address = new EndpointAddress(&amp;quot;http://localhost:8000/DeviceEventService&amp;quot;);

        CustomBinding binding = new CustomBinding(
            new PollingDuplexBindingElement(),
            new BinaryMessageEncodingBindingElement(),
            new HttpTransportBindingElement());

        DeviceEventService.DeviceEventServiceClient client = new DeviceEventService.DeviceEventServiceClient(binding, address);
        client.SendDeviceEventDataReceived += new EventHandler&lt;deviceeventservice.senddeviceeventdatareceivedeventargs&gt;(client_SendDeviceEventReceived);
        client.ConnectAsync(Guid.NewGuid().ToString());
        EventListBox.ItemsSource = eventData;
    }

    void client_SendDeviceEventReceived(object sender, DeviceEventService.SendDeviceEventDataReceivedEventArgs e)
    {
        if (!eventData.Contains(e.data))
        {
            eventData.Add(e.data);
        }
    }
}&lt;/pre&gt;&lt;p&gt;As you can see, the client is creating an &lt;strong&gt;ObservableCollection&lt;/strong&gt; to hold the event data returned by the service callback. Next, the client connects up to the service endpoint, creates an implementation object for the service and initializes this object. Initialization means:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Associating the object with the endpoint binding,&lt;/li&gt;
&lt;li&gt;Setting up a delegate to process the callback event and&lt;/li&gt;
&lt;li&gt;Call the Connect service method. Note this call has an asynchronous implementation.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Note that the event data &lt;strong&gt;ObservableCollection&lt;/strong&gt; is then bound to the clients visual component, in this case a &lt;strong&gt;ListBox&lt;/strong&gt;. &lt;/p&gt;&lt;p&gt;Now when the callback delegate is fired, all that we need to do is add the event data to the &lt;strong&gt;ObservableCollection&lt;/strong&gt;, and because this collection is observable, the &lt;strong&gt;ListBox&lt;/strong&gt; is told about it. &lt;/p&gt;&lt;p&gt;In the XAML, the listbox binds the data to the list items:&lt;/p&gt;&lt;pre class="brush: xml; wrap-lines: false"&gt;&amp;lt;ListBox Grid.Row="1" x:Name="EventListBox"&amp;gt;
    &amp;lt;ListBox.ItemTemplate&amp;gt;
        &amp;lt;DataTemplate&amp;gt;
            &amp;lt;Grid Width="770"&amp;gt;
                &amp;lt;Grid.RowDefinitions&amp;gt;
                    &amp;lt;RowDefinition&amp;gt;&amp;lt;/RowDefinition&amp;gt;
                &amp;lt;/Grid.RowDefinitions&amp;gt;
                &amp;lt;Grid.ColumnDefinitions&amp;gt;
                    &amp;lt;ColumnDefinition&gt;&amp;lt;/ColumnDefinition&amp;gt;
                    &amp;lt;/Grid.ColumnDefinitions&amp;gt;
                &amp;lt;TextBlock Grid.Row="1" Text="{Binding timestamp}"&gt;&amp;lt;/TextBlock&amp;gt;
                &amp;lt;TextBlock Grid.Column="1" Text="{Binding message}" FontWeight="Bold" Foreground="Red" HorizontalAlignment="Right"&amp;gt;&amp;lt;/TextBlock&amp;gt;
            &amp;lt;/Grid&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/ListBox.ItemTemplate&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/pre&gt;&lt;p&gt;and hey presto…&lt;/p&gt;&lt;p&gt;Wow, that was easy, Silverlight 4 seems to be doing all the work!&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;As you can see, its easy to build duplex service under WCF. What’s more, Silverlight 4 is well matched to this technology, providing simple binding against the callback event. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-1974508284859455118?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/M_x0GDGAaGo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/1974508284859455118/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/09/wcf-self-hosted-duplex-service-for.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/1974508284859455118?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/1974508284859455118?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/M_x0GDGAaGo/wcf-self-hosted-duplex-service-for.html" title="WCF Self Hosted Duplex Service for Chubby Client" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_dRqAiVqJ-R8/TJvxHUp_rXI/AAAAAAAAAKg/Klj2Fl4lgZ8/s72-c/barcodescanner_thumb10.jpg?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/09/wcf-self-hosted-duplex-service-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8BQHYyfip7ImA9Wx5RFEw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-4184679947561310230</id><published>2010-08-21T19:54:00.001+01:00</published><updated>2010-08-21T19:54:11.896+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-21T19:54:11.896+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="chubby client" /><category scheme="http://www.blogger.com/atom/ns#" term="Out Of Browser" /><category scheme="http://www.blogger.com/atom/ns#" term="MVC" /><category scheme="http://www.blogger.com/atom/ns#" term="JSON" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="HTTP" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="RIA" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term="Silverlight" /><category scheme="http://www.blogger.com/atom/ns#" term="WCF" /><title>Chubby RESTful Clients using Silverlight and WCF</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/77j1McIP7Eg7xtx0pUAVBCSo8hk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/77j1McIP7Eg7xtx0pUAVBCSo8hk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/77j1McIP7Eg7xtx0pUAVBCSo8hk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/77j1McIP7Eg7xtx0pUAVBCSo8hk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_AgiQdJsI/AAAAAAAAAIY/khU3L_-IIJw/s1600-h/wcfsilverlight14.jpg"&gt;&lt;img style="border-right-width: 0px; margin: 0px 8px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="wcfsilverlight" border="0" alt="wcfsilverlight" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_AhYWgEwI/AAAAAAAAAIc/tyGnW_mTF2U/wcfsilverlight_thumb12.jpg?imgmax=800" width="139" height="162" /&gt;&lt;/a&gt;&amp;#160; [Concrete/Interesting]&lt;/strong&gt; Many years ago I was working on a project. We needed a application that worked in a browser but everything came from the local machine. Later I started to notice devices like Routers that had a web interface…just connect using HTTP to the device IP address and there you are. &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;I used a similar technique in my Ghostfiles Service application (&lt;a title="http://www.lowrieweb.com/ghostfiles.htm" href="http://www.lowrieweb.com/ghostfiles.htm"&gt;http://www.lowrieweb.com/ghostfiles.htm&lt;/a&gt;). &lt;/p&gt;  &lt;h3&gt;Chubby Client Architecture&lt;/h3&gt;  &lt;p&gt;Ghostfiles is a Windows Service Application that runs as a lightweight HTTP server on a private HTTP port. If you point your browser at it, the service returns back application pages. It’s just like having Apache or IIS running, only lightweight with only the pages of the application supported. This is what I called a Chubby Client, a thin client interface delivered to a browser by a local web and application server. &lt;/p&gt;  &lt;p&gt;The basic architecture used in Ghostfiles is shown here:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_AicfWLDI/AAAAAAAAAJ8/wf-0RjDoUng/s1600-h/image%5B1%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_AjY1QQMI/AAAAAAAAAKA/-0otAhSjUh4/image_thumb.png?imgmax=800" width="428" height="344" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Page requests (GET/PUT) from the browser are handled by the light HTTP Web Server. This directs the calls to Page Service. &lt;/p&gt;  &lt;p&gt;Page Services are responsible for mapping the request against a template page. The template defines the basic HTML resources associated with this request. The template can contain standard mark-up as well as JavaScript. &lt;/p&gt;  &lt;p&gt;So far so good. The template may also contain meta-tags that are interpreted by the meta-tag handler. This sub-system is responsible for mapping the tags to calls down to the business services. So for example, a table on the page is defined by a meta-tag, &lt;strong&gt;&amp;lt;APP:DoSomething&amp;gt;&amp;lt;/APP:DoSomething&amp;gt;.&lt;/strong&gt; This maps to a business function &lt;strong&gt;DoSomething();&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Looks familiar – well it’s very similar to PHP or old style ASP in how it works. And it works well from an application and UI point of view. However, the code is hard to maintain, adding new user interfaces is an effort and the implementation is limited to HTML and JavaScript, so not an ideal platform going forward.&lt;/p&gt;  &lt;h3&gt;Chubby Client Grows Up&lt;/h3&gt;  &lt;p&gt;.Net provides us with a way to take the Chubby Client into the realm of Rich Internet Applications (RIA). To achieve this we need to look at two core technologies, Silverlight Out Of Browser Clients and Self Hosted WCF:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TG_AkEBSvII/AAAAAAAAAIo/MOn7sEuwiyM/s1600-h/image14.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_AlKt99dI/AAAAAAAAAIs/QpDwZ4NuYoA/image_thumb8.png?imgmax=800" width="321" height="359" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h3&gt;WCF Self Hosting&lt;/h3&gt;  &lt;p&gt;Windows Communication Foundation is a well balanced communication framework. One feature of WCF is the range of hosting options. A couple of these allow us to deploy a service implementation without the overhead of IIS. WCF Self Hosting enables us to deploy communication services fully encapsulated as a standalone executable or if preferred, Windows Service Application. &lt;/p&gt;  &lt;p&gt;I’m going to focus on RESTful web services for HTTP JSON clients. For the purpose of this blog, I’m going to create a Console based REST service that exposes one simple interface called &lt;strong&gt;Colour&lt;/strong&gt;:&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="83"&gt;URI:&lt;/td&gt;        &lt;td valign="top" width="317"&gt;&lt;a href="http://exmple.com/resource/colour"&gt;&lt;/a&gt;&lt;a title="http://localhost:8080/chubby" href="http://localhost:8000/Chubby/Colour"&gt;http://localhost:8000/Chubby/&lt;strong&gt;Colour&lt;/strong&gt;&lt;/a&gt;&lt;/a&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="83"&gt;MIME:&lt;/td&gt;        &lt;td valign="top" width="317"&gt;application/json&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="83"&gt;HTTP Method:&lt;/td&gt;        &lt;td valign="top" width="317"&gt;GET&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;The response to this call is a random colour reference in the form of:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;{ “Colour” : “#FFRRGGBB” }&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The steps to create the service are very easy under Visual Studio 2010:&lt;/p&gt;  &lt;h5&gt;Step 1&lt;/h5&gt;  &lt;p&gt;Create a new project based on a C# Console Application:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_Al73E5II/AAAAAAAAAI0/fNsvAinU_8k/s1600-h/image19.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_AmsgJf-I/AAAAAAAAAI4/SYP5LGT70xM/image_thumb111.png?imgmax=800" width="428" height="261" /&gt;&lt;/a&gt; Next add references to &lt;strong&gt;System.ServiceModel, System.ServiceModel.Web&lt;/strong&gt; and &lt;strong&gt;System.Runtime.Serialization&lt;/strong&gt; to the console application and add the following statements to the top of your &lt;strong&gt;Program.cs&lt;/strong&gt; file:&lt;/p&gt;  &lt;pre class="brush: csharp; wrap-lines: false"&gt;using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System.IO;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You should check the &lt;strong&gt;Target Framework&lt;/strong&gt; for the project, in Project Properties. This should be set to .NET Framework 4 to ensure the Reference filters are correctly set: &lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TG_AnMfuoKI/AAAAAAAAAI8/-Z7-lhrSWy4/s1600-h/image5.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_AoJQ8ZBI/AAAAAAAAAJA/YZBH9a0_1I4/image_thumb2.png?imgmax=800" width="440" height="296" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h5&gt;Step 2&lt;/h5&gt;

&lt;p&gt;Define and implement the service contracts. This defines the JSON services that will be exposed by this console application and the implementation behind these services. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: As well as the Chubby Service, we also need to define and implement Cross Domain Policies for our service. This is important because we are calling the service from Silverlight and Silverlight will need to be reassured that it can use the service:&lt;/p&gt;

&lt;pre class="brush: csharp wrap-lines: false"&gt;#region Service Contract

    [ServiceContract]
    public interface IChubbyServices
    {
        [OperationContract]
        [WebGet(UriTemplate = &amp;quot;Colour&amp;quot;, BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
        JSONColour GetColour();
    }
    #endregion

    #region Service Contract for Cross Domain Policies

    [ServiceContract]
    public interface IPolicyRetriever
    {
        [OperationContract, WebGet(UriTemplate = &amp;quot;/clientaccesspolicy.xml&amp;quot;)]
        Stream GetSilverlightPolicy();
        [OperationContract, WebGet(UriTemplate = &amp;quot;/crossdomain.xml&amp;quot;)]
        Stream GetFlashPolicy();
    }
    #endregion
    
    #region Data Contracts
    
    [DataContract]
    public class JSONColour
    {
        [DataMember]
        public string colour = String.Empty;
    }
    #endregion

    #region Service Implementation

    public class ChubbyServices : IChubbyServices, IPolicyRetriever
    {
        public JSONColour GetColour()
        {
            //Return a random colour from the list below
            string[] colours = { &amp;quot;#FFFF0000&amp;quot;, &amp;quot;#FF00FF00&amp;quot;, &amp;quot;#FF0000FF&amp;quot;, &amp;quot;#FFFFFF00&amp;quot;, &amp;quot;#FFFF00FF&amp;quot;, &amp;quot;#FF00FFFF&amp;quot;, &amp;quot;#FF000000&amp;quot; };
            JSONColour colour = new JSONColour();
            Random rand = new Random();
            colour.colour = colours[rand.Next(7)];
            return colour;
        }
        Stream StringToStream(string result)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = &amp;quot;application/xml&amp;quot;;
            return new MemoryStream(Encoding.UTF8.GetBytes(result));
        }

        public Stream GetSilverlightPolicy()
        {
            string result = @&amp;quot;&amp;lt;?xml version=&amp;quot;&amp;quot;1.0&amp;quot;&amp;quot; encoding=&amp;quot;&amp;quot;utf-8&amp;quot;&amp;quot;?&amp;gt;
            &amp;lt;access-policy&amp;gt;
               &amp;lt;cross-domain-access&amp;gt;
                  &amp;lt;policy&amp;gt;
                     &amp;lt;allow-from http-request-headers=&amp;quot;&amp;quot;*&amp;quot;&amp;quot;&amp;gt;
                        &amp;lt;domain uri=&amp;quot;&amp;quot;*&amp;quot;&amp;quot;/&amp;gt;
                     &amp;lt;/allow-from&amp;gt;
                     &amp;lt;grant-to&amp;gt;
                        &amp;lt;resource path=&amp;quot;&amp;quot;/&amp;quot;&amp;quot; include-subpaths=&amp;quot;&amp;quot;true&amp;quot;&amp;quot;/&amp;gt;
                     &amp;lt;/grant-to&amp;gt;
                  &amp;lt;/policy&amp;gt;
               &amp;lt;/cross-domain-access&amp;gt;
            &amp;lt;/access-policy&amp;gt;&amp;quot;;
            return StringToStream(result);
        }

        public Stream GetFlashPolicy()
        {
            string result = @&amp;quot;&amp;lt;?xml version=&amp;quot;&amp;quot;1.0&amp;quot;&amp;quot;?&amp;gt;
            &amp;lt;!DOCTYPE cross-domain-policy SYSTEM &amp;quot;&amp;quot;http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd&amp;quot;&amp;quot;&amp;gt;
            &amp;lt;cross-domain-policy&amp;gt;
               &amp;lt;allow-access-from domain=&amp;quot;&amp;quot;*&amp;quot;&amp;quot; /&amp;gt;
            &amp;lt;/cross-domain-policy&amp;gt;&amp;quot;;
            return StringToStream(result);
        }
    }
    #endregion&lt;/pre&gt;

&lt;p&gt;Quick note about the class JSONColour. The service is designed to respond with Mime type “application/json”. To do this we need to instruct WCF to serialize the data in JSON format. This is defined by the Operation Contract against the method, specifically:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ResponseFormat = WebMessageFormat.Json&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now to ensure there is something for WCF to serialize, we create a Data Contract for serialization and define a structure to be serialized. In our case we call the structure JSONColour. With WFC that is all that is required to ensure correct serialization for JSON.&lt;/p&gt;

&lt;h5&gt;Step 3&lt;/h5&gt;

&lt;p&gt;Now we need to implement the host responsible for running the service:&lt;/p&gt;

&lt;pre class="brush: csharp; wrap-lines: false"&gt;#region Host Implementation

    class Program
    {
        static void Main(string[] args)
        {
            string baseAddress = &amp;quot;http://localhost:8000&amp;quot;;

            using (ServiceHost host = new ServiceHost(typeof(ChubbyServices), new Uri(baseAddress)))
            {
                host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), &amp;quot;&amp;quot;).Behaviors.Add(new WebHttpBehavior());
                host.AddServiceEndpoint(typeof(IChubbyServices), new WebHttpBinding(), &amp;quot;&amp;quot;).Behaviors.Add(new WebHttpBehavior(&amp;quot;Chubby&amp;quot;));
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                host.Description.Behaviors.Add(smb);

                host.Open();

                Console.WriteLine(&amp;quot;WCF Host Running...&amp;quot;);
                Console.WriteLine();

                foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
                    Console.WriteLine(&amp;quot;Supported Endpoint for {0} at address {1}&amp;quot;, endpoint.Binding.GetType().Name, endpoint.Address.Uri);

                Console.WriteLine();
                Console.WriteLine(&amp;quot;Press &lt;enter&gt; to shutdown&amp;quot;);
                Console.ReadLine();

                host.Close();
            }
        }
    }
    #endregion&lt;/pre&gt;

&lt;p&gt;Here we are creating a host and setting up the appropriate &lt;strong&gt;WebHTTPBindings&lt;/strong&gt; as required by REST.&lt;/p&gt;

&lt;h5&gt;Step 4&amp;#160; &lt;/h5&gt;

&lt;p&gt;Now all we need to do is compile, run and test. When run the service should open a console window similar to that shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TG_AowkPfmI/AAAAAAAAAJE/xc3RhJXUZbo/s1600-h/image25.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_Apdt3g8I/AAAAAAAAAJI/y_RFfBJcCUc/image_thumb14.png?imgmax=800" width="452" height="244" /&gt;&lt;/a&gt; The console should show the application started and the host running.&lt;/p&gt;

&lt;p&gt;Since this is a REST host, we should be able to test the service from a browser. Open your browser and go to the URL shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:8000/Chubby/Colour"&gt;http://localhost:8000/Chubby/Colour&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser should respond with the JSON response data. Typically the browser will not recognise the Mime type and will allow you to save the response in a file:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TG_AqBbyBUI/AAAAAAAAAJM/_yuVZimfICQ/s1600-h/image18.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_Aq1vVJqI/AAAAAAAAAJQ/tM-xAp-Tm3Y/image_thumb11.png?imgmax=800" width="442" height="235" /&gt;&lt;/a&gt; If you save the response to a file then open the file it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_Ardp6sxI/AAAAAAAAAJU/eFJvRTxgQtM/s1600-h/image141.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_Ar5ZhuxI/AAAAAAAAAJY/IilUmMksh3A/image_thumb7.png?imgmax=800" width="467" height="236" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Excellent, just what we were looking for. &lt;/p&gt;

&lt;h3&gt;Silverlight Out of Browser&lt;/h3&gt;

&lt;p&gt;The service is half the story. To complete our Chubby Client we need a client that calls on our service. For this we are going to use Silverlight and implement and Out of Browser client. &lt;/p&gt;

&lt;p&gt;Our client will be simple, with a single showing a &lt;strong&gt;TextBlock&lt;/strong&gt; and a &lt;strong&gt;Button&lt;/strong&gt;. The code behind will implement the call to the web service and handle the response. Let’s create the client:&lt;/p&gt;

&lt;h5&gt;Step 1&lt;/h5&gt;

&lt;p&gt;Create the Silverlight Client App. We are going to choose the Silverlight Application Template:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_As5EuvpI/AAAAAAAAAJc/OawT_q7xTPM/s1600-h/image12.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_AuRlXj6I/AAAAAAAAAJg/aUOGhkR0CxY/image_thumb5.png?imgmax=800" width="438" height="268" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;We need a web page to host our apps. Let’s choose an ASP.Net Web Site:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_AvN9wExI/AAAAAAAAAJk/GbIc_pf0Exs/s1600-h/image13.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TG_Av0HRmHI/AAAAAAAAAJo/ikTuaQUHUCo/image_thumb6.png?imgmax=800" width="328" height="340" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;MainPage.xaml&lt;/strong&gt; and create our UI. All we need for this demo is a &lt;strong&gt;TextBlock&lt;/strong&gt; to show the colour returned from our service call and a &lt;strong&gt;Button&lt;/strong&gt; to kick things off:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_Aw6OqkqI/AAAAAAAAAJs/p4jxJhCg1FY/s1600-h/image19%5B1%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_AxpHlaUI/AAAAAAAAAJw/QXCBJS7HeK8/image_thumb10.png?imgmax=800" width="427" height="253" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;pre class="brush: xml; wrap-lines: false"&gt;&amp;lt;usercontrol xmlns=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&amp;quot; xmlns:mc=&amp;quot;http://schemas.openxmlformats.org/markup-compatibility/2006&amp;quot; xmlns:d=&amp;quot;http://schemas.microsoft.com/expression/blend/2008&amp;quot; xmlns:x=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml&amp;quot; x:class=&amp;quot;ChubbyClient.MainPage&amp;quot; mc:ignorable=&amp;quot;d&amp;quot; d:designheight=&amp;quot;300&amp;quot; d:designwidth=&amp;quot;400&amp;quot;&amp;gt;

   &amp;lt;grid x:name=&amp;quot;LayoutRoot&amp;quot; background=&amp;quot;White&amp;quot;&amp;gt;
        &amp;lt;textblock height=&amp;quot;68&amp;quot; horizontalalignment=&amp;quot;Left&amp;quot; margin=&amp;quot;65,74,0,0&amp;quot; name=&amp;quot;textBlock1&amp;quot; text=&amp;quot;TextBlock&amp;quot; verticalalignment=&amp;quot;Top&amp;quot; width=&amp;quot;264&amp;quot; fontfamily=&amp;quot;Comic Sans MS&amp;quot; fontsize=&amp;quot;32&amp;quot; fontweight=&amp;quot;Bold&amp;quot; textalignment=&amp;quot;Center&amp;quot;&amp;gt;&amp;lt;/textblock&amp;gt;
        &amp;lt;button name=&amp;quot;button1&amp;quot; height=&amp;quot;55&amp;quot; horizontalalignment=&amp;quot;Left&amp;quot; margin=&amp;quot;145,187,0,0&amp;quot; verticalalignment=&amp;quot;Top&amp;quot; width=&amp;quot;104&amp;quot; fontsize=&amp;quot;18&amp;quot; content=&amp;quot;Press Me&amp;quot;&amp;gt;
    &amp;lt;/grid&amp;gt;
&amp;lt;/usercontrol&amp;gt;&lt;/pre&gt;

&lt;h5&gt;Step 2&lt;/h5&gt;

&lt;p&gt;Now we can wire up the button. Double click on the button in the IDE to create an event handler in the code behind. In this event handler we are going to call the web service. &lt;/p&gt;

&lt;p&gt;I’m going to use a WebClient() object to make an HTTP GET call to the web service. The data response will be given to us in an asynchronous handler:&lt;/p&gt;

&lt;pre class="brush: csharp; wrap-lines: false"&gt;private void button1_Click(object sender, RoutedEventArgs e)
{
   WebClient client = new WebClient();
   client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
   client.DownloadStringAsync(new Uri(&amp;quot;http://localhost:8000/Chubby/Colour&amp;quot;));
}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
   if (e.Error != null)
      return;
}&lt;/pre&gt;

&lt;p&gt;The results are found in &lt;strong&gt;e.Results&lt;/strong&gt; in JSON encoding. All we need to do now is lightly parse the results, pulling out the colour and apply that colour to our styles. First we have to add a reference for &lt;strong&gt;System.Json&lt;/strong&gt; to the project and a using statement to the code. We will use the &lt;strong&gt;System.Json&lt;/strong&gt; assembly to help us decode the results. &lt;/p&gt;

&lt;p&gt;Now to parse the results and update the view:&lt;/p&gt;

&lt;pre class="brush: csharp; wrap-lines: false"&gt;private Color GetColorFromHexa(string hexaColor)
{
   return Color.FromArgb(
      Convert.ToByte(hexaColor.Substring(1, 2), 16),
      Convert.ToByte(hexaColor.Substring(3, 2), 16),
      Convert.ToByte(hexaColor.Substring(5, 2), 16),
      Convert.ToByte(hexaColor.Substring(7, 2), 16));
   }

   void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
   {
      if (e.Error != null)
         return;

      JsonObject jsonDoc = (JsonObject)JsonObject.Parse(e.Result);
      string colour = jsonDoc[&amp;quot;colour&amp;quot;];

      //Update the view
      textBlock1.Foreground = new SolidColorBrush(GetColorFromHexa(colour));
}&lt;/pre&gt;

&lt;h5&gt;Step 3&lt;/h5&gt;

&lt;p&gt;The last step is to make the Silverlight run Out of Browser. This is very simple. Open up the properties for the Silverlight client and select the &lt;strong&gt;Siverlight&lt;/strong&gt; tab. All we need to do is check the&lt;strong&gt; ‘Enable running application out of browser’&lt;/strong&gt;. You may also want to change the out of browser settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_AyIUl7eI/AAAAAAAAAJ0/tC8h0hX-_Sw/s1600-h/image30.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TG_Ayzow84I/AAAAAAAAAJ4/5nNlgT_Gv1s/image_thumb17.png?imgmax=800" width="443" height="237" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it, job done. &lt;/p&gt;

&lt;h3&gt;Chubby Client Grows Up&lt;/h3&gt;

&lt;p&gt;As you can see, we can build Silverlight out of browser applications that are serviced locally by a WCF console application. &lt;/p&gt;

&lt;p&gt;This concept can be taken further in a number of directions. We can package the WCF service as a windows service. We can make an installer that puts both client and services onto the local machine, registering the client to access the local services. We can take the same client and call a remote service – so chubby client for desktop and thin client for RIA. &lt;/p&gt;

&lt;p&gt;From a code point of view, I’ve done a quick and dirty here. The client should be better architected to follow a MVC or MVVM patterns. The server should be more modular with the service contracts in separate assemblies. I’m sure there is a lot more that can be improved. &lt;/p&gt;

&lt;p&gt;To help you get started, I’ve published the code above to Google Code. &lt;/p&gt;

&lt;p&gt;&lt;a title="http://code.google.com/p/chubbyclient/" href="http://code.google.com/p/chubbyclient/"&gt;http://code.google.com/p/chubbyclient/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have fun.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-4184679947561310230?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/qSxJldGgfKI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/4184679947561310230/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/08/chubby-restful-clients-using.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4184679947561310230?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4184679947561310230?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/qSxJldGgfKI/chubby-restful-clients-using.html" title="Chubby RESTful Clients using Silverlight and WCF" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/TG_AhYWgEwI/AAAAAAAAAIc/tyGnW_mTF2U/s72-c/wcfsilverlight_thumb12.jpg?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/08/chubby-restful-clients-using.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIMQXYyfyp7ImA9Wx5RFE8.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-5239213497920600005</id><published>2010-07-23T21:26:00.000+01:00</published><updated>2010-08-21T21:29:40.897+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-21T21:29:40.897+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Modal Dialog" /><category scheme="http://www.blogger.com/atom/ns#" term="cocos2d" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="UIAlertView" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="UIImagePickerController" /><category scheme="http://www.blogger.com/atom/ns#" term="Quartz" /><category scheme="http://www.blogger.com/atom/ns#" term="OpenGL" /><title>cocos2d as an Application Framework - Part 4</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/JYEuHP-UouADMPjIQW0j_gwPsm4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JYEuHP-UouADMPjIQW0j_gwPsm4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/JYEuHP-UouADMPjIQW0j_gwPsm4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JYEuHP-UouADMPjIQW0j_gwPsm4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEuIKjvzldI/AAAAAAAAAH4/EUGoxzJBdIo/s1600-h/cocos2dmug5.png"&gt;&lt;img style="border-right-width: 0px; margin: 0px 8px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2dmug" border="0" alt="cocos2dmug" align="left" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEuILe6xMJI/AAAAAAAAAH8/VrNNBV9-boI/cocos2dmug_thumb3.png?imgmax=800" width="122" height="115" /&gt;&lt;/a&gt; [Concrete/Interesting]&lt;/strong&gt; So far I think the experiment to use cocos2d as an application framework has been successful. With little effort we have a very nice look and feel, excellent menu behaviour on an iPhone and a charming looking popup dialog. &lt;/p&gt;  &lt;p&gt;Better than that, it seems relatively easy to mix UI framework into cocos2d – UI Views for example seem to behave well when added as nodes to a scene. &lt;/p&gt;  &lt;p&gt;In this last part, I’m going to try something a little more challenging. What I want to build is a sexy About box. I ;want to mix animation, a scrolling text view and a background camera view using &lt;strong&gt;UIImagePickerController&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h3&gt;Containing cocos2d in a &lt;strong&gt;UIView&lt;/strong&gt;&lt;/h3&gt;  &lt;p&gt;To show a camera surface as the background of our cocos2d scene, we are going to have to place the cocos2d as a subview of our window’s main view. We also need to make the cocos2d scene transparent. This is a little tricky but here’s how.&lt;/p&gt;  &lt;p&gt;Look again at the selector &lt;strong&gt;applicationDidFinishLaunching&lt;/strong&gt;: in the application delegate. The delegate manages the application window &lt;strong&gt;UIWindow *window&lt;/strong&gt;, but there is no sign of the initialisation code for this window. That’s because the macro &lt;strong&gt;CC_DIRECTOR_INIT();&lt;/strong&gt; is doing all the work. The code behind this macro is:&lt;/p&gt;  &lt;pre class="brush: cpp; wrap-lines: false"&gt;window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
    [CCDirector setDirectorType:kCCDirectorTypeNSTimer];

CCDirector *__director = [CCDirector sharedDirector];
[__director setDeviceOrientation:kCCDeviceOrientationPortrait];
[__director setDisplayFPS:NO];
[__director setAnimationInterval:1.0/60];

EAGLView *__glView = [EAGLView viewWithFrame:[window bounds] pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO];

[__director setOpenGLView:__glView];
[window addSubview:__glView];
window makeKeyAndVisible];&lt;/pre&gt;

&lt;p&gt;As you can see, an application window is allocated and initialized &lt;strong&gt;madeKeyAndVisible&lt;/strong&gt;. Also a share &lt;strong&gt;CCDirector&lt;/strong&gt; object is created and the OpenGL view is set to an &lt;strong&gt;EAGLView&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With a little modification we can inject a &lt;strong&gt;UIView&lt;/strong&gt; between the window and the OpenGL view:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;- (void) applicationDidFinishLaunching:(UIApplication*)application
{
	window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

	if(![CCDirector setDirectorType:kCCDirectorTypeDisplayLink])
		[CCDirector setDirectorType:kCCDirectorTypeNSTimer];

	CCDirector *director = [CCDirector sharedDirector];
	[director setDeviceOrientation:kCCDeviceOrientationPortrait];
	[director setDisplayFPS:YES];

	[director setAnimationInterval:1.0/60];

	EAGLView *glView = [EAGLView viewWithFrame:[[UIScreen mainScreen] bounds] pixelFormat:kEAGLColorFormatRGBA8 depthFormat:0 preserveBackbuffer:NO];
	[director setOpenGLView:glView];

	[glView setMultipleTouchEnabled:YES];
	glView.opaque = NO;
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	overlay.opaque = YES;
	overlay.backgroundColor = [UIColor blackColor];
	[overlay addSubview: glView];

	[window addSubview:overlay];
	[window makeKeyAndVisible];

	[CCTexture2D setDefaultAlphaPixelFormat: kTexture2DPixelFormat_RGBA8888];
	[director runWithScene: [LoaderScene scene]];
}&lt;/pre&gt;

&lt;p&gt;As you can see, I’m creating a &lt;strong&gt;UIVew&lt;/strong&gt; called overlay and adding this to the window after making the OpenGL view a child of the overlay view. There are a couple of other tricks in this piece of code. By default the OpenGL view is created opaque. If this is the case, we won’t be able to see through the OpenGL view to the &lt;strong&gt;UIVIew&lt;/strong&gt; layer below, so any camera surface rendered to this overlay will be obscured. To fix this we have to set the &lt;strong&gt;glView.opaque = NO&lt;/strong&gt;, set a clear colour with zero alpha and clear the buffer before any rendering. Finally, we need to set the &lt;strong&gt;pixelFormat:kEAGLColorFormatRGBA8&lt;/strong&gt; when creating the &lt;strong&gt;EAGLView&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So now we have an &lt;strong&gt;UIView&lt;/strong&gt; containing the a transparent background &lt;strong&gt;EAGLView&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;Funky About Box&lt;/h3&gt;

&lt;p&gt;Finally, we are ready to put the about box together. I’m going to build three layers. I’m going to put three ‘cloud’ sprites onto the scene and set them animating. Next I will create a &lt;strong&gt;UIImagePickerController&lt;/strong&gt; and attach this to the &lt;strong&gt;Overlay&lt;/strong&gt; view, placing it below the &lt;strong&gt;EAGLView&lt;/strong&gt;. Finally, I will create a &lt;strong&gt;UITextView&lt;/strong&gt; and place this on top of the &lt;strong&gt;EAGLView&lt;/strong&gt;:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(id) init
{
	
	if( (self=[super init] )) {
		
		self.isTouchEnabled  = YES;
		CGSize windowSize = [[CCDirector sharedDirector] winSize];
		
		CCSprite *cloud1 = [CCSprite spriteWithFile:@&amp;quot;cloud.png&amp;quot;];
		cloud1.position = ccp(-64, windowSize.height/2);
		[self addChild:cloud1 z:1];
		id move1 = [CCMoveBy actionWithDuration:20 position:ccp(windowSize.width+128,0)];
		[cloud1 runAction:[CCRepeatForever actionWithAction:[CCSequence actions: move1, [move1 reverse], nil]]];		
		
		CCSprite *cloud2 = [CCSprite spriteWithFile:@&amp;quot;cloud.png&amp;quot;];
		cloud2.position = ccp(80, (windowSize.height/2)+80);
		[self addChild:cloud2 z:2];
		id bounce2 = [CCMoveBy actionWithDuration:.45 position:ccp(0,8)];
		[cloud2 runAction:[CCRepeatForever actionWithAction:[CCSequence actions: bounce2, [bounce2 reverse], nil]]];		
		
		CCSprite *cloud3 = [CCSprite spriteWithFile:@&amp;quot;cloud.png&amp;quot;];
		cloud3.position = ccp(windowSize.width-80, (windowSize.height/2)-80);
		[self addChild:cloud3 z:3];
		id bounce3 = [CCMoveBy actionWithDuration:.55 position:ccp(0,8)];
		[cloud3 runAction:[CCRepeatForever actionWithAction:[CCSequence actions: bounce3, [bounce3 reverse], nil]]];		
				
		UIImagePickerController* picker=[[UIImagePickerController alloc] init];  
		picker.sourceType = UIImagePickerControllerSourceTypeCamera;
		picker.showsCameraControls = NO;
		picker.toolbarHidden = YES;
		picker.navigationBarHidden = YES;
		picker.wantsFullScreenLayout = YES;
		picker.cameraViewTransform = CGAffineTransformScale(picker.cameraViewTransform, 1, 1.4);
				
		KaleidophoneAppDelegate *delegate = (KaleidophoneAppDelegate *)[[UIApplication sharedApplication] delegate];
		[delegate.overlay insertSubview:picker.view belowSubview:[[CCDirector sharedDirector] openGLView]]; 
		
		UITextView *description = [[UITextView alloc] initWithFrame:CGRectMake(20,(windowSize.height/2)-100,windowSize.width-40,200)];
		description.backgroundColor = [UIColor clearColor];
		description.text = @&amp;quot;This is some text that is shown in the text field.\r\rThis text is very long spanning a number of lines.\r\rorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&amp;quot;;
		[description setEditable:NO]; 
		description.font = [UIFont fontWithName:@&amp;quot;Marker Felt&amp;quot; size:24.0f];
		description.textColor = [UIColor whiteColor];
		description.showsHorizontalScrollIndicator = NO;
		description.alwaysBounceVertical = YES;
		[delegate.overlay insertSubview:description aboveSubview:[[CCDirector sharedDirector] openGLView]]; 
		[description release];
		
	}
	return self;
}&lt;/pre&gt;

&lt;h3&gt;The Results&lt;/h3&gt;

&lt;p&gt;Here are the screen shots for the funky About box:&lt;/p&gt;

&lt;table border="0" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEuINxX7WYI/AAAAAAAAAIA/hkEV9iZ7M6E/s1600-h/cocos2dabout14.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="cocos2d-about1" border="0" alt="cocos2d-about1" align="left" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEuIOyffDBI/AAAAAAAAAIE/5lr1a91YktQ/cocos2dabout1_thumb2.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&lt;font size="3"&gt;&lt;strong&gt;Ooh, a camera view of my TV with animated clouds and the About text.&lt;/strong&gt;&lt;/font&gt;&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;font size="3"&gt;&lt;strong&gt;Look, the clouds move.&lt;/strong&gt;&lt;/font&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEuIRMhVfQI/AAAAAAAAAII/WFgHg8xaKyU/s1600-h/cocos2dabout26.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="cocos2d-about2" border="0" alt="cocos2d-about2" align="left" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEuISXrOiVI/AAAAAAAAAIM/-ODoL27sNH8/cocos2dabout2_thumb4.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="200"&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEuIUhZ2mnI/AAAAAAAAAIQ/O7PPw7HGqRo/s1600-h/cocos2dabout312.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="cocos2d-about3" border="0" alt="cocos2d-about3" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEuIVvRi7PI/AAAAAAAAAIU/PtuPiap166g/cocos2dabout3_thumb10.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt;&lt;/td&gt;

      &lt;td valign="top" width="200"&gt;&lt;font size="3"&gt;&lt;strong&gt;And the text scrolls&lt;/strong&gt;&lt;/font&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Ok, not perfect but an excellent start. Some very funky user interface components and at very little cost. Some things need more work, the &lt;strong&gt;EAGLView&lt;/strong&gt; animation freezes when handling the touch events for the &lt;strong&gt;UITextView&lt;/strong&gt;, but I'm sure this can be improved upon.&lt;/p&gt;

&lt;p&gt;For anyone interested, I’ve published the source code for the demo application on code.google.com:&lt;/p&gt;

&lt;p&gt;&lt;a title="http://code.google.com/p/iphone-cocos2d-application-framework/" href="http://code.google.com/p/iphone-cocos2d-application-framework/"&gt;http://code.google.com/p/iphone-cocos2d-application-framework/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have fun experimenting with cocos2d and UIKit.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-5239213497920600005?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/HxnbxazBf64" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/5239213497920600005/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/08/cocos2d-as-application-framework-part-4.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5239213497920600005?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5239213497920600005?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/HxnbxazBf64/cocos2d-as-application-framework-part-4.html" title="cocos2d as an Application Framework - Part 4" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEuILe6xMJI/AAAAAAAAAH8/VrNNBV9-boI/s72-c/cocos2dmug_thumb3.png?imgmax=800" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/08/cocos2d-as-application-framework-part-4.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYHR3g5fip7ImA9WxFaGEw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-5052692683273887450</id><published>2010-07-22T17:45:00.001+01:00</published><updated>2010-07-22T17:45:36.626+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-22T17:45:36.626+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Modal Dialog" /><category scheme="http://www.blogger.com/atom/ns#" term="cocos2d" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="UIAlertView" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="UIImagePickerController" /><category scheme="http://www.blogger.com/atom/ns#" term="Quartz" /><category scheme="http://www.blogger.com/atom/ns#" term="OpenGL" /><title>cocos2d as an Application Framework – Part 3</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/x1GdVBSPMMQ0R6Thy03iDE27zGQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/x1GdVBSPMMQ0R6Thy03iDE27zGQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/x1GdVBSPMMQ0R6Thy03iDE27zGQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/x1GdVBSPMMQ0R6Thy03iDE27zGQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqm4mh4jI/AAAAAAAAAGg/IcmRtWIBL4o/s1600-h/cocos2dmug%5B13%5D.png"&gt;&lt;img style="border-right-width: 0px; margin: 0px 8px 0px 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2dmug" border="0" alt="cocos2dmug" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqoIDkIEI/AAAAAAAAAGk/_qgV_NJrFlY/cocos2dmug_thumb%5B11%5D.png?imgmax=800" width="103" height="99" /&gt;&lt;/a&gt;[Concrete/Interesting]&lt;/strong&gt; It’s all starting to come together. We have a application and a very interesting look and feel for the menu.&lt;/p&gt;  &lt;p&gt;Now I want to really mix it up. I want to display something similar to that provided by &lt;strong&gt;UIAlertView&lt;/strong&gt;, but the one from Apple just doesn’t fit the look and feel of the application under the cocos2d framework. &lt;/p&gt;  &lt;h3&gt;Custom Alert in UI Kit&lt;/h3&gt;  &lt;p&gt;Implementing a modal alert is relatively simple, I need create a XIB file with a single &lt;strong&gt;UIView&lt;/strong&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEh1nm8QaYI/AAAAAAAAAHg/9veIDyDa_yQ/s1600-h/cocos2dalert13.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-alert1" border="0" alt="cocos2d-alert1" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEh1odbVjJI/AAAAAAAAAHk/mYV28300v5M/cocos2dalert1_thumb1.png?imgmax=800" width="220" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;My alert view has a title, message body and two buttons, all in a nice warm look and feel:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEh1pFcuPjI/AAAAAAAAAHo/1e38DEynRPw/s1600-h/cocos2dalert24.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-alert2" border="0" alt="cocos2d-alert2" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEh1p7o61jI/AAAAAAAAAHs/Shecxbggp0M/cocos2dalert2_thumb2.png?imgmax=800" width="435" height="223" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;These tie back to the class definition as show in the code below:&lt;/p&gt;  &lt;pre class="brush: cpp; wrap-lines: false"&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;
#import &amp;quot;ColourfulButton.h&amp;quot;

@interface ModalAlert : UIViewController {
	IBOutlet UILabel *alertTitle;
	IBOutlet UILabel *alertMessage;
	IBOutlet ColourfulButton *alertCancel;
	IBOutlet ColourfulButton *alertOk;
	int nResult;  
}

@property (nonatomic, retain) UILabel *alertTitle;
@property (nonatomic, retain) UILabel *alertMessage;
@property (nonatomic, retain) ColourfulButton *alertCancel;
@property (nonatomic, retain) ColourfulButton *alertOk;
@property (nonatomic) int nResult;

- (IBAction)pressedOk;
- (IBAction)pressedCancel;

@end

int createModalAlert(NSString *title, NSString *msg, NSString *cancel, NSString *ok);  &lt;/pre&gt;

&lt;p&gt;Before I move on, let’s have a little look at this class definition. First, there is a global method called &lt;strong&gt;createModalAlert()&lt;/strong&gt;. This is the entry point that will be called to show an alert. It takes a title string, a message string and two button strings. &lt;/p&gt;

&lt;p&gt;The class is simply an extension to &lt;strong&gt;UIViewController&lt;/strong&gt;, used to manage the &lt;strong&gt;UIView&lt;/strong&gt; and tie the controls to class members. &lt;/p&gt;

&lt;p&gt;This is all standard &lt;strong&gt;UIKit&lt;/strong&gt; stuff.&lt;/p&gt;

&lt;p&gt;To make things look a bit more exciting I done 5 things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use a common application font, &lt;strong&gt;Marker Felt&lt;/strong&gt;. This looks good as an application font. &lt;/li&gt;

  &lt;li&gt;Used a colourful background for my view. &lt;/li&gt;

  &lt;li&gt;Used a &lt;strong&gt;UINavigationBar&lt;/strong&gt; to provide a nice header for the view and a placeholder for the title &lt;/li&gt;

  &lt;li&gt;Used a sub-class for the buttons called &lt;strong&gt;ColourfulButton&lt;/strong&gt;. This gives the buttons a better look and feel. &lt;/li&gt;

  &lt;li&gt;Added rounded corners to the bottom of the dialog &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Integrating with cocos2d&lt;/h3&gt;

&lt;p&gt;Ideally, I want a single modal entry point that loads the dialog, handles any processing, cancels the dialog on pressing a button and returns the button id of the pressed button.&lt;/p&gt;

&lt;p&gt;The entry point &lt;strong&gt;createModalAlert()&lt;/strong&gt; does this for us:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;int createModalAlert(NSString *title, NSString *msg, NSString *cancel, NSString *ok)  
{  
	ModalAlert *view = [[ModalAlert alloc] initWithNibName:@&amp;quot;ModalAlert&amp;quot; bundle:nil];
	CGSize offSize = [UIScreen mainScreen].bounds.size;   
	CGPoint offScreenCenter = CGPointMake(offSize.width / 2, offSize.height /2);   
	view.view.center = offScreenCenter;
	view.view.alpha = 0.0f;
	view.nResult = -1;
	view.view.layer.cornerRadius = 15;
	
	view.alertTitle.text = title;
	view.alertMessage.text = msg;
	
	[view.alertCancel setTitle:cancel forState:UIControlStateNormal];

	if (ok != nil)
		[view.alertOk setTitle:ok forState:UIControlStateNormal];
	else 
		[view.alertOk setHidden:YES];
	
	
	[[CCDirector sharedDirector] stopAnimation];
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:0.5];
	view.view.alpha = 0.95f;
	[[[CCDirector sharedDirector] openGLView] addSubview:view.view];
	[UIView commitAnimations];
	
    	while ((!view.view.hidden) &amp;amp;&amp;amp; (view.view.superview!=nil))  
    	{  
		[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
    	}  
	
	int nResult = view.nResult;
    	[view release];  
	[[CCDirector sharedDirector] startAnimation];
    	return nResult;  
}  &lt;/pre&gt;

&lt;p&gt;The model method does a number of important things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create and position the &lt;strong&gt;UIView&lt;/strong&gt;. I’m loading the view from &lt;strong&gt;ModaAlert.xib&lt;/strong&gt; and locating the centre of the view in the centre of the scene. &lt;/li&gt;

  &lt;li&gt;Setup the text for the title, message and buttons. Note that if the function is passed nil for the second button, this button is hidden. &lt;/li&gt;

  &lt;li&gt;Tell cocos2d to stop all animation. This effectively pauses the application until the dialog has cleared. &lt;/li&gt;

  &lt;li&gt;Transition to show the view. The view is&amp;#160; created with its alpha set to zero.&amp;#160; I then set up a transition to fade in the view, add the view to the scene and commit the animation. &lt;/li&gt;

  &lt;li&gt;Run the modal loop. Because this is a modal dialog, we need to handle the message processing in this method. We use an &lt;strong&gt;NSLoop&lt;/strong&gt; for this purpose and run until the view is hidden or removed. &lt;/li&gt;

  &lt;li&gt;Clean and return. Once the view has been removed, we exit the message loop, pick up the return result, start cocos2d animation and return the result. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The modal loop is controlled by the visibility of the view. To tie this into the button action and to set up the return values, we have the following implementations for the button events:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;- (IBAction)pressedOk {
	nResult = 1;
	[self.view setHidden:YES];
}

- (IBAction)pressedCancel {
	nResult = 0;
	[self.view setHidden:YES];
}&lt;/pre&gt;

&lt;p&gt;That’s it – nothing more is required to present a modal view under cocos2d. As a test, I’m calling the dialog from the &lt;strong&gt;onQuit:&lt;/strong&gt; selector:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(void)onQuit: (id) sender
{
	if (createModalAlert( @&amp;quot;Quit&amp;quot;, @&amp;quot;Are you sure you want to exit?&amp;quot;, @&amp;quot;No&amp;quot;, @&amp;quot;Yes&amp;quot;)==1)	//Exit
		exit(0);
}&lt;/pre&gt;

&lt;p&gt;The results look great:&lt;/p&gt;

&lt;h3&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEh1q6OiCnI/AAAAAAAAAHw/68ZJ1C0cOEI/s1600-h/cocos2d-alert3%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-alert3" border="0" alt="cocos2d-alert3" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEh1r4Os-8I/AAAAAAAAAH0/RB8TTPl68As/cocos2d-alert3_thumb%5B3%5D.png?imgmax=800" width="269" height="494" /&gt;&lt;/a&gt;Transitions&lt;/h3&gt;

&lt;p&gt;Here we are using a &lt;strong&gt;UIKit&lt;/strong&gt; animation to show the dialog. cocos2d supports a number of transition for Sprites, layers and scenes. Transitions include rotates, slides, fades, jumps, zooms and flips.&lt;/p&gt;

&lt;p&gt;A nice technique is to have a splash screen that transitions into the menu. This is very simple. All you need to do is create a scene with a single layer and sprite on that layer. The sprite is set to cover the entire screen:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;#import &amp;quot;LoaderScene.h&amp;quot;
#import &amp;quot;MenuScene.h&amp;quot;

@implementation LoaderScene

+(id) scene
{
	CCScene *scene = [CCScene node];
	LoaderScene *layer = [LoaderScene node];
	[scene addChild: layer];
	return scene;
}

-(id) init
{
	
	if( (self=[super init] )) {
		
		CCSprite *sprite = [CCSprite spriteWithFile:@&amp;quot;Default.png&amp;quot;];
		CGSize size = [[CCDirector sharedDirector] winSize];
		sprite.position = ccp(size.width/2,size.height/2);
		[self addChild: sprite z:0];
		[self schedule: @selector(done:) interval:2.5];
	}
	return self;
}

-(void)done:(ccTime)dt
{
	[self unschedule:@selector(done:)];
	[[CCDirector sharedDirector] replaceScene:[CCFlipAngularTransition transitionWithDuration:0.5f scene:[MenuLayer scene]]];
}

- (void) dealloc
{
	[super dealloc];
}

@end&lt;/pre&gt;

&lt;p&gt;By making this scene the first scene in your application, the scene is loaded, centred and added to the layer and made visible. &lt;/p&gt;

&lt;p&gt;Notice also that after creating and positioning the splash screen sprite, a timer is created. This is done using cocos2d scheduling. It is advisable to use this mechanism rather than &lt;strong&gt;NSTimer&lt;/strong&gt; because cocos2d scheduling is cocos2d aware.&lt;/p&gt;

&lt;p&gt;When the timer fires, I simply stop the timer and change the scene using the&lt;strong&gt; replaceScene:&lt;/strong&gt; selector. This selector allows us to specify a transition to use. I’ve used a &lt;strong&gt;CCFlipAngularTransition&lt;/strong&gt;. Have fun!&lt;/p&gt;

&lt;h3&gt;In Part 4&lt;/h3&gt;

&lt;p&gt;In the next part we will look at a scene that support a &lt;strong&gt;UIImagePickerController&lt;/strong&gt; view. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-5052692683273887450?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/q-omTzbf4wk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/5052692683273887450/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-3.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5052692683273887450?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5052692683273887450?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/q-omTzbf4wk/cocos2d-as-application-framework-part-3.html" title="cocos2d as an Application Framework – Part 3" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqoIDkIEI/AAAAAAAAAGk/_qgV_NJrFlY/s72-c/cocos2dmug_thumb%5B11%5D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-3.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UFSXc_fSp7ImA9WxFaGE4.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-6399434191784831135</id><published>2010-07-21T15:33:00.001+01:00</published><updated>2010-07-22T23:53:38.945+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-22T23:53:38.945+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="cocos2d" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="UIImagePickerController" /><category scheme="http://www.blogger.com/atom/ns#" term="Quartz" /><category scheme="http://www.blogger.com/atom/ns#" term="OpenGL" /><title>cocos2d as an Application Framework – Part 2</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/E-dETaghQTU90GPDirgEGumZH_k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/E-dETaghQTU90GPDirgEGumZH_k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/E-dETaghQTU90GPDirgEGumZH_k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/E-dETaghQTU90GPDirgEGumZH_k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqm4mh4jI/AAAAAAAAAGg/IcmRtWIBL4o/s1600-h/cocos2dmug%5B13%5D.png"&gt;&lt;img style="border-right-width: 0px; margin: 0px 8px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2dmug" border="0" alt="cocos2dmug" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqoIDkIEI/AAAAAAAAAGk/_qgV_NJrFlY/cocos2dmug_thumb%5B11%5D.png?imgmax=800" width="103" height="99" /&gt;&lt;/a&gt;[Concrete/Interesting]&lt;/strong&gt; In the previous blog I looked at cocos2d and showed how a simple menu could be created with very little effort.&lt;/p&gt;  &lt;p&gt;The menu is quite funky but not that exciting. cocos2d supports Sprites, so let’s build a menu with animated sprites, something like the Cover Flow you find in iTunes.&lt;/p&gt;  &lt;h3&gt;Cover Flow Menu&lt;/h3&gt;  &lt;p&gt;For a more exciting look and feel for the menu, I’m going to do a couple of things. First, I’m going to change the visual behaviour of the menu items – give them a sprite as well as text. Second, I’m going to modify the visual and input behaviour of the menu (the container for the menu items) to animate the menu items.&lt;/p&gt;  &lt;p&gt;For the menu items, I have to first create a class that inherits from &lt;strong&gt;CCMenuItem&lt;/strong&gt;:&lt;/p&gt;  &lt;pre class="brush: cpp; wrap-lines: false"&gt;#import &amp;lt;foundation/foundation.h&amp;gt;
#import &amp;quot;cocos2d.h&amp;quot;

@interface MenuItemComplete : CCMenuItem &amp;lt;CCRGBAProtocol&amp;gt; {
	CCLabel *title;
	CCNode&amp;lt;CCRGBAProtocol&amp;gt; *sprite;
	CCNode&amp;lt;CCRGBAProtocol&amp;gt; *spriteDisabled;
	float originalScale_;	
}

@property (nonatomic,readwrite,retain) CCNode&amp;lt;CCRGBAProtocol&amp;gt; *sprite;
@property (nonatomic,readwrite,retain) CCNode&amp;lt;CCRGBAProtocol&amp;gt; *spriteDisabled;
@property (nonatomic,readwrite,retain) CCLabel *title;

-(id) initFromImage:(NSString*)image title:(CCLabel *)title target:(id)target selector:(SEL)selector;
-(void) activate;
-(void) selected;
-(void) unselected;

@end
&lt;/pre&gt;

&lt;p&gt;You can see that our class inherits from &lt;strong&gt;CCMenuItem&lt;/strong&gt; and implements a couple of sprites and a label. One sprite for an enabled menu item, the other for a disabled menu item.&lt;/p&gt;

&lt;p&gt;I’ve also declared an initializing selector called &lt;strong&gt;initFromImage:&lt;/strong&gt;. In the implementation the important stuff is happening in this method and in the drawing method:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(id) initFromImage:(NSString *)image title:(CCLabel *)t target:(id)target selector:(SEL)selector
{
	if( (self=[super initWithTarget:target selector:selector]) ) {
		
		originalScale_ = 1;
		
		self.sprite = [CCSprite spriteWithFile:image];
		self.spriteDisabled = [CCSprite spriteWithFile:image];
		[self.spriteDisabled setOpacity:80];
		[self setContentSize: [self.sprite contentSize]];
		
		self.title = t;
		self.title.position = ccp(self.contentSize.width/2 , -10);
		[self addChild: title z:100];
	}
	return self;	
}

-(void) draw
{
	if(isEnabled_)
		[sprite draw];				
	else
		[spriteDisabled draw];
}&lt;/pre&gt;

&lt;p&gt;In the init method we create the two sprites from and image past to the selector. The disabled sprite is just a copy of the normal sprite but I’ve set the opacity to 80%. Similarly, I’ve created a label from the text passed to the selector. &lt;/p&gt;

&lt;p&gt;I’m using a variable called &lt;strong&gt;originalSize&lt;/strong&gt; – this will be used later when controlling the sprite animation. Finally I’m positioning the label in this node.&lt;/p&gt;

&lt;p&gt;Drawing is very simple, all that we do is draw the normal or disabled sprite according the the menu item’s enabled state.&lt;/p&gt;

&lt;p&gt;We have to implement a few more selectors to support menu item behaviour, specifically, &lt;strong&gt;activate:&lt;/strong&gt;, &lt;strong&gt;selected:&lt;/strong&gt; and &lt;strong&gt;unselected:&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(void) activate {
	if(isEnabled_) {
		[self stopAllActions];
        
		self.scale = originalScale_;
        
		[super activate];
	}
}

-(void) selected
{
	if(isEnabled_) {	
		[super selected];
		[self stopActionByTag:kZoomActionTag];
		originalScale_ = self.scale;
		CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_ * 1.2f];
		zoomAction.tag = kZoomActionTag;
		[self runAction:zoomAction];
	}
}

-(void) unselected
{
	if(isEnabled_) {
		[super unselected];
		[self stopActionByTag:kZoomActionTag];
		CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_];
		zoomAction.tag = kZoomActionTag;
		[self runAction:zoomAction];
	}
}&lt;/pre&gt;

&lt;p&gt;These selectors support the visual behaviour of the menu item. When the menu item is activated, all I need to do is stop all animations, restore the active sprite to its original size and bubble up the event. When a menu item is selected I want the sprite to grow and shrink, so I stop the current animation, create a new animation action, a zoom, and set that going. Finally, unselected restores the default animation action. For a better visual effect, I've also decided to arrange the menu items horizontally through a call to the selector &lt;strong&gt;alignItemsHorizontally:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can use the new class in our menu scene in place of the current menu items. We need to add a few images for our menu items and off we go:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(id) init
{

	if( (self=[super init] )) {
		MenuItemComplete *mnuKaleidoscope = [[MenuItemComplete alloc] initFromImage:@&amp;quot;mnuKaleidoscope.png&amp;quot; title:[CCLabel labelWithString:@&amp;quot;Kaleidoscope&amp;quot; fontName:@&amp;quot;marker felt&amp;quot; fontSize:24] target: self selector: @selector(onKaleidoscope:)];
		MenuItemComplete *mnuAbout = [[MenuItemComplete alloc] initFromImage:@&amp;quot;mnuAbout.png&amp;quot; title:[CCLabel labelWithString:@&amp;quot;About&amp;quot; fontName:@&amp;quot;marker felt&amp;quot; fontSize:24] target: self selector: @selector(onAbout:)];
		MenuItemComplete *mnuQuit = [[MenuItemComplete alloc] initFromImage:@&amp;quot;mnuQuit.png&amp;quot; title:[CCLabel labelWithString:@&amp;quot;Quit&amp;quot; fontName:@&amp;quot;marker felt&amp;quot; fontSize:24] target: self selector: @selector(onQuit:)];
		CCMenu *menu = [CCMenu menuWithItems:mnuKaleidoscope, mnuAbout, mnuQuit, nil];
		[menu alignItemsHorizontally];
		[self addChild:menu];
		
		CCLabel * l = [CCLabel labelWithString:@&amp;quot;Main Menu&amp;quot; fontName:@&amp;quot;marker felt&amp;quot; fontSize:28];
		l.position = ccp(160,420);
		[self addChild:l];
		
	}
	return self;
}&lt;/pre&gt;

&lt;p&gt;The results look pretty good, the menu items behave well when selected, but no menu animation yet:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEgqpv1X0wI/AAAAAAAAAGo/bLp8vOMB5NM/s1600-h/cocos2d-menu2%5B6%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-menu2" border="0" alt="cocos2d-menu2" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEgqq28bngI/AAAAAAAAAGs/LVkyStZoRuE/cocos2d-menu2_thumb%5B4%5D.png?imgmax=800" width="267" height="479" /&gt;&lt;/a&gt;Now I need to add behaviour to the menu. I want the menu to respond to left and right swipes, these will drag the sprites. I want the menu to wrap to give a cylindrical effect. To complete the effect, the icons should shrink and dim as they move away from the centre. &lt;/p&gt;

&lt;p&gt;First I need to create a new class that inherits from &lt;strong&gt;CCMenu.&lt;/strong&gt; At this point I’m going to cheat. João Caxaria has created a very good one that responds to touches and accelerometer. You can read more about it from:&lt;/p&gt;

&lt;p&gt;&lt;a title="http://www.cocos2d-iphone.org/forum/topic/139" href="http://www.cocos2d-iphone.org/forum/topic/139"&gt;http://www.cocos2d-iphone.org/forum/topic/139&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the source for the looping menu from the following link:&lt;/p&gt;

&lt;p&gt;&lt;a title="http://dl.dropbox.com/u/3314174/loopingmenu.zip" href="http://dl.dropbox.com/u/3314174/loopingmenu.zip"&gt;http://dl.dropbox.com/u/3314174/loopingmenu.zip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that now added to the project, all I need to do now is change my scene to use a looping menu as the menu item container. The results are very impressive: &lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEgqrTtCEwI/AAAAAAAAAGw/QnhnB9yFQcA/s1600-h/cocos2d-menu3%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu3" border="0" alt="cocos2d-menu3" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEgqsG35OzI/AAAAAAAAAG0/9C7Ghbeybt8/cocos2d-menu3_thumb.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEgqsn_rgrI/AAAAAAAAAG4/woLoAK9Ryhs/s1600-h/cocos2d-menu4%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu4" border="0" alt="cocos2d-menu4" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEgqtDyWBII/AAAAAAAAAG8/qLEBpe3vWnk/cocos2d-menu4_thumb.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEgquTm0eWI/AAAAAAAAAHA/sCLw0do6V68/s1600-h/cocos2d-menu5%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu5" border="0" alt="cocos2d-menu5" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEgqvWyBzOI/AAAAAAAAAHE/Mq1rTLxgIwA/cocos2d-menu5_thumb.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqwM6Ix3I/AAAAAAAAAHI/Ci6i8yyo9VE/s1600-h/cocos2d-menu6%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu6" border="0" alt="cocos2d-menu6" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEgqw8z_jJI/AAAAAAAAAHM/Afmhp6ol7Nk/cocos2d-menu6_thumb.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqxWg7J-I/AAAAAAAAAHQ/5IAHpIwF__s/s1600-h/cocos2d-menu7%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu7" border="0" alt="cocos2d-menu7" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEgqyCcyoyI/AAAAAAAAAHU/NgrxTqGKFKE/cocos2d-menu7_thumb%5B1%5D.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEgqyx1Kz1I/AAAAAAAAAHY/hnJSIoHhnTI/s1600-h/cocos2d-menu8%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2d-menu8" border="0" alt="cocos2d-menu8" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEgqzavOTlI/AAAAAAAAAHc/b5t60MYLAsg/cocos2d-menu8_thumb.png?imgmax=800" width="164" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; I did need to make a few more changes because &lt;strong&gt;LoopingMenu&lt;/strong&gt; was designed for landscape, but after some simple tweaks, it all works well in portrait.&lt;/p&gt;

&lt;h3&gt;In Part 3&lt;/h3&gt;

&lt;p&gt;In the next part I will start to delve into the world of cocos2d / UI Kit hybrids. I will be implementing a modal dialog that uses standard UI Kit elements like &lt;strong&gt;UIViewController&lt;/strong&gt; and a XIB resource file.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-6399434191784831135?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/56oawds7Zt8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/6399434191784831135/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-2.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/6399434191784831135?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/6399434191784831135?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/56oawds7Zt8/cocos2d-as-application-framework-part-2.html" title="cocos2d as an Application Framework – Part 2" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEgqoIDkIEI/AAAAAAAAAGk/_qgV_NJrFlY/s72-c/cocos2dmug_thumb%5B11%5D.png?imgmax=800" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cNRn44fip7ImA9Wx5TEEw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-7366724975172587881</id><published>2010-07-20T22:58:00.001+01:00</published><updated>2010-07-25T01:51:37.036+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-25T01:51:37.036+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="cocos2d" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="UIImagePickerController" /><category scheme="http://www.blogger.com/atom/ns#" term="Quartz" /><category scheme="http://www.blogger.com/atom/ns#" term="OpenGL" /><title>cocos2d as an Application Framework – Part 1</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Pdd-RxJZ80BXD3rM_saK4IyCr-s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Pdd-RxJZ80BXD3rM_saK4IyCr-s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Pdd-RxJZ80BXD3rM_saK4IyCr-s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Pdd-RxJZ80BXD3rM_saK4IyCr-s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEYcAyb-rnI/AAAAAAAAAGQ/_JN2LBlGFh8/s1600-h/cocos2dmug%5B3%5D.png"&gt;&lt;img style="border-right-width: 0px; margin: 0px 8px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cocos2dmug" border="0" alt="cocos2dmug" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEYcBiavq5I/AAAAAAAAAGU/B5O-ytXYKYc/cocos2dmug_thumb%5B2%5D.png?imgmax=800" width="116" height="111" /&gt;&lt;/a&gt; [Concrete/Interesting]&lt;/strong&gt; cocos2d is a multi-platform games engine written in C/C++. It has been ported to a number of environments including the iPhone under Objective-C. &lt;/p&gt;  &lt;p&gt;Like all developers, I dream of creating the most popular game ever. I’m probably a little too late because Pac Man day’s have come and gone. However, I believe the foundation of a games engine has application outside of the realm of games.&lt;/p&gt;  &lt;p&gt;In this first part I will be discussing the use of the cocos2d games engine as an application framework. In later articles I will expand on this to show how we can mix elements of cocos2d and the iPhone UI Framework.&lt;/p&gt;  &lt;h3&gt;Basic Elements of a Games Engine&lt;/h3&gt;  &lt;p&gt;Take a look at Adobe Air and even Microsoft Silverlight. Under these frameworks applications are better thought of as scenes, with layers and objects (nodes on the scene). These objects may be animation or sprites (for instance a ‘Busy’ animation) or they may be user interface elements like labels, button or menus. Layers are groups of nodes that exist on the same Z-index or plane. This could be backgrounds, foregrounds, even popup dialogs or input boxes. And scenes are the world or landscape upon which everything is displayed, like the application window:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEcA8L7YTOI/AAAAAAAAAF4/4kEJmnvUeRg/s1600-h/cocos2d-layers%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-layers" border="0" alt="cocos2d-layers" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEcA8wErh5I/AAAAAAAAAF8/y3AQ3E3tWwE/cocos2d-layers_thumb%5B3%5D.png?imgmax=800" width="417" height="318" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/TEcA9kaPY-I/AAAAAAAAAGA/PLoMyrS5DY0/s1600-h/cocos2d-kittyani%5B4%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-kittyani" border="0" alt="cocos2d-kittyani" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/TEcA-f5-TaI/AAAAAAAAAGE/BNIi7tJIpyk/cocos2d-kittyani_thumb%5B2%5D.png?imgmax=800" width="235" height="230" /&gt;&lt;/a&gt; cocos2d, like Adobe Air, has a time dimension. That is to say, object can be tested, modified, added or deleted to the scene over a timeline. Certain time functions are static in nature, such as a sprite animation or a transition from scene to scene. Some are less obvious such as the scaling and translations associated with a Carousel Menu. &lt;/p&gt;  &lt;p&gt;Finally, cocos2d and other similar platforms allow you to tie up device events to objects. For example, using the accelerometer on the iPhone to control the direction and speed of a sprite, or making a button respond to touch input. &lt;/p&gt;  &lt;p&gt;For a deeper introduction to cocos2d look at the documentation on their website: &lt;a title="http://www.cocos2d-iphone.org/wiki/doku.php/" href="http://www.cocos2d-iphone.org/wiki/doku.php/"&gt;http://www.cocos2d-iphone.org/wiki/doku.php/&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;An Application Framework &lt;/h3&gt;  &lt;p&gt;So where am I going with all this. Well before I write the best game ever for the iPhone, I thought I’d release a cocos2d application. And I want to use cocos2d as an application framework, to take care of all my user interface management rather than a traditional games framework. &lt;/p&gt;  &lt;p&gt;I also want to mix this with standard UI Framework elements. What I hope to get is a very innovative user interface with very little work.&amp;#160; &lt;/p&gt;  &lt;p&gt;What I will be covering in this article is: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;cocos2d application framework &lt;/li&gt;    &lt;li&gt;New Look Menus and Dialogs &lt;/li&gt;    &lt;li&gt;Integrating UI into the scene &lt;/li&gt;    &lt;li&gt;Responding to device events &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Starting the Application&lt;/h3&gt;  &lt;p&gt;Cocos2D installs a number of templates into Xcode – this makes it easy to start your application. I’m using the standard ‘cocos2d Application’:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEbtTxE2KPI/AAAAAAAAAFw/dqjojoHooD8/s1600-h/cocos2dtemplate4.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-template" border="0" alt="cocos2d-template" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEbtUhjxMZI/AAAAAAAAAF0/SdHlseBkWfM/cocos2dtemplate_thumb2.png?imgmax=800" width="427" height="351" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Running the wizard creates a simple ‘Hello World’ application which will server well as our staring point.&lt;/p&gt;  &lt;p&gt;For the purposes of this article, I’m going to create a simple Kaleidoscope application based on a brilliant piece of code by Nori Nomura: &lt;a title="http://github.com/norio-nomura" href="http://github.com/norio-nomura"&gt;http://github.com/norio-nomura&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;His implementation uses UIImagePickerController, Quartz and OpenGL, so this will test our ability to integrate UI framework into cocos2d and use cocos2d as an application framework.&lt;/p&gt;  &lt;h3&gt;Adding a Menu&lt;/h3&gt;  &lt;p&gt;Before I create a menu, let’s look at the structure of the application I’ve just created. There are two classes of interest. The application delegate and the &lt;strong&gt;HelloWorldScene&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;In the application delegate, take a look at the &lt;strong&gt;applicationDidFinishLaunching:&lt;/strong&gt; selector:&lt;/p&gt;  &lt;pre class="brush: cpp; wrap-lines: false"&gt;- (void) applicationDidFinishLaunching:(UIApplication*)application
{
	// CC_DIRECTOR_INIT()
	//
	// 1. Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer
	// 2. EAGLView multiple touches: disabled
	// 3. creates a UIWindow, and assign it to the &amp;quot;window&amp;quot; var (it must already be declared)
	// 4. Parents EAGLView to the newly created window
	// 5. Creates Display Link Director
	// 5a. If it fails, it will use an NSTimer director
	// 6. It will try to run at 60 FPS
	// 7. Display FPS: NO
	// 8. Device orientation: Portrait
	// 9. Connects the director to the EAGLView
	//
	CC_DIRECTOR_INIT();
	
	// Obtain the shared director in order to...
	CCDirector *director = [CCDirector sharedDirector];
		
	// Turn on display FPS
	[director setDisplayFPS:YES];
	
	// Turn on multiple touches
	EAGLView *view = [director openGLView];
	[view setMultipleTouchEnabled:YES];
	
	// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
	// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
	// You can change anytime.
	[CCTexture2D setDefaultAlphaPixelFormat: kTexture2DPixelFormat_RGBA8888];
		
	[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];
}&lt;/pre&gt;

&lt;p&gt;This is where the framework is initialized and starts the business of showing the first scene. In this case, the scene called ‘Hello World’. &lt;/p&gt;

&lt;p&gt;The ‘Hello World’ scene is implemented in the &lt;strong&gt;HelloWorldScene&lt;/strong&gt; class. The important function to look at is the &lt;strong&gt;init:&lt;/strong&gt; selector:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;// on &amp;quot;init&amp;quot; you need to initialize your instance
-(id) init
{
	// always call &amp;quot;super&amp;quot; init
	// Apple recommends to re-assign &amp;quot;self&amp;quot; with the &amp;quot;super&amp;quot; return value
	if( (self=[super init] )) {
		
		// create and initialize a Label
		CCLabel* label = [CCLabel labelWithString:@&amp;quot;Hello World&amp;quot; fontName:@&amp;quot;Marker Felt&amp;quot; fontSize:64];

		// ask director the the window size
		CGSize size = [[CCDirector sharedDirector] winSize];
	
		// position the label on the center of the screen
		label.position =  ccp( size.width /2 , size.height/2 );
		
		// add the label as a child to this Layer
		[self addChild: label];
	}
	return self;
}&lt;/pre&gt;

&lt;p&gt;Pretty simple stuff. All it’s doing is setting up and positioning a label and adding this to the scene. Of course, under the hood the framework is doing the clever stuff of rendering the scene and the layers and nodes.&lt;/p&gt;

&lt;p&gt;cocos2d supports some primitive node types like layers and labels as well as buttons and menus. I’m going to modify the &lt;strong&gt;HelloWorldScene&lt;/strong&gt; to manage a menu node with menu items. I do this by creating a menu in the &lt;strong&gt;init:&lt;/strong&gt; selector:&lt;/p&gt;

&lt;pre class="brush: cpp; wrap-lines: false"&gt;-(id) init
{
	// always call &amp;quot;super&amp;quot; init
	// Apple recommends to re-assign &amp;quot;self&amp;quot; with the &amp;quot;super&amp;quot; return value
	if( (self=[super init] )) {
		CCMenuItemFont *mnuKaleidoscope = [CCMenuItemFont itemFromString: @&amp;quot;Kaleidoscope&amp;quot; target:self selector:@selector(onKaleidoscope:)];
		CCMenuItemFont *mnuAbout = [CCMenuItemFont itemFromString: @&amp;quot;About&amp;quot; target:self selector:@selector(onAbout:)];
		CCMenuItemFont *mnuQuit = [CCMenuItemFont itemFromString: @&amp;quot;Quit&amp;quot; target:self selector:@selector(onQuit:)];
		CCMenu *menu = [CCMenu menuWithItems:mnuKaleidoscope, mnuAbout, mnuQuit, nil];
		[menu alignItemsVertically];
		[self addChild:menu];
		
		CCLabel * l = [CCLabel labelWithString:@&amp;quot;Main Menu&amp;quot; fontName:@&amp;quot;marker felt&amp;quot; fontSize:24];
		l.position = ccp(160,400);
		[self addChild:l];
		
	}
	return self;
}&lt;/pre&gt;

&lt;p&gt;Ok, not a bad start. The result is a main screen menu with three menu items, displayed vertically. The menu items have some funky behaviour and there is a label ‘Main Menu’ at the top.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/TEcA_E-ieaI/AAAAAAAAAGI/yk_TKQH1gVE/s1600-h/cocos2d-menu1%5B13%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="cocos2d-menu1" border="0" alt="cocos2d-menu1" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEcA_sQKHGI/AAAAAAAAAGM/JerEfjHy0XA/cocos2d-menu1_thumb%5B11%5D.png?imgmax=800" width="208" height="383" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Few things to note, we have a scene with a single layer. On the layer we are adding a label and menu. The label is being positioned relative to the centre of the label. All nodes have origins (their centre) and by default this is in the centre of the bounding rectangle for the node. For the menu, we are adding three menu items, each with an appropriate selector. One final note at this point, when you run the application you will see a number at the bottom left of the screen. This shows the screens per second rendering of the engine. This is useful in debug but you will want to remove this for production. Setting &lt;strong&gt;setDisplayFPS:NO&lt;/strong&gt; in &lt;strong&gt;applicationDidFinishLaunching&lt;/strong&gt; will do this for you.&lt;/p&gt;

&lt;h3&gt;In Part 2…&lt;/h3&gt;

&lt;p&gt;I will look at extending the menu for a more funky look and feel as well as looking at dialogs and popup windows.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-7366724975172587881?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/CL7YQu7-org" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/7366724975172587881/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-1.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/7366724975172587881?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/7366724975172587881?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/CL7YQu7-org/cocos2d-as-application-framework-part-1.html" title="cocos2d as an Application Framework – Part 1" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_dRqAiVqJ-R8/TEYcBiavq5I/AAAAAAAAAGU/B5O-ytXYKYc/s72-c/cocos2dmug_thumb%5B2%5D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/07/cocos2d-as-application-framework-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0AHR388cCp7ImA9WxFbFEw.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-5805357580181473203</id><published>2010-07-06T12:28:00.001+01:00</published><updated>2010-07-06T12:28:56.178+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-06T12:28:56.178+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C-boundary" /><category scheme="http://www.blogger.com/atom/ns#" term="Space-time" /><title>Space and Time</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/fO1zB5qWK2Dgr-OT07ezM60QxRQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fO1zB5qWK2Dgr-OT07ezM60QxRQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/fO1zB5qWK2Dgr-OT07ezM60QxRQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fO1zB5qWK2Dgr-OT07ezM60QxRQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/TDMTPcGSc0I/AAAAAAAAAFg/GWBw3CT_umI/s1600-h/Spacetimediagramillustratingthecausa%5B2%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="Spacetime-diagram-illustrating-the-causal-relationships-with-1" border="0" alt="Spacetime-diagram-illustrating-the-causal-relationships-with-1" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/TDMTPwSC-_I/AAAAAAAAAFk/Yz6hMgPHqD0/Spacetimediagramillustratingthecausa.jpg?imgmax=800" width="161" height="141" /&gt;&lt;/a&gt; [Abstract/ Sharing]&lt;/strong&gt;&amp;#160; Here is a short poem about Space and Time. Someone said the present is brief and precious and we can never get back moment that are lost. I disagree&lt;/p&gt;  &lt;p&gt;This poem offers an alternative view, talking about events in space/time with the present flowing through this landscape. I also make reference to the c-boundary confining all space and time. &lt;/p&gt;  &lt;p&gt;Hope you enjoy:&lt;/p&gt;  &lt;blockquote&gt;&lt;font size="2" face="Tahoma"&gt;     &lt;br /&gt;Events frozen into the fabric of Space and Time,       &lt;br /&gt;The Present rushing past at the Speed of Light.       &lt;br /&gt;      &lt;br /&gt;The Past and Future everywhere, less the edge of the Universe       &lt;br /&gt;Where possibility and history remains void. &lt;/font&gt;&lt;/blockquote&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-5805357580181473203?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/gxaPdKm06sM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/5805357580181473203/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/07/space-and-time.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5805357580181473203?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5805357580181473203?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/gxaPdKm06sM/space-and-time.html" title="Space and Time" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/TDMTPwSC-_I/AAAAAAAAAFk/Yz6hMgPHqD0/s72-c/Spacetimediagramillustratingthecausa.jpg?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/07/space-and-time.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEYBQnw8fSp7ImA9WxFTFEo.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-5238926390705132687</id><published>2010-04-05T13:55:00.001+01:00</published><updated>2010-04-05T13:55:53.275+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-05T13:55:53.275+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Snow Leopard" /><category scheme="http://www.blogger.com/atom/ns#" term="Quartz Extreme" /><category scheme="http://www.blogger.com/atom/ns#" term="OSX86" /><category scheme="http://www.blogger.com/atom/ns#" term="Samsung" /><category scheme="http://www.blogger.com/atom/ns#" term="iLife" /><category scheme="http://www.blogger.com/atom/ns#" term="Mac" /><category scheme="http://www.blogger.com/atom/ns#" term="Windows 7" /><category scheme="http://www.blogger.com/atom/ns#" term="PC" /><category scheme="http://www.blogger.com/atom/ns#" term="iATKOS" /><category scheme="http://www.blogger.com/atom/ns#" term="Leopard" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhoto" /><title>Mac on a PC, are you mad?</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/26vrxs2DDOTEuWYeqqUwL1vqvZs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/26vrxs2DDOTEuWYeqqUwL1vqvZs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/26vrxs2DDOTEuWYeqqUwL1vqvZs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/26vrxs2DDOTEuWYeqqUwL1vqvZs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/S7ndrJU88PI/AAAAAAAAAEI/8ftD2uIoiOo/s1600-h/iATKOS6.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="iATKOS" border="0" alt="iATKOS" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndro74CoI/AAAAAAAAAEM/mx7YTqgJrUM/iATKOS_thumb4.jpg?imgmax=800" width="108" height="108" /&gt;&lt;/a&gt;&lt;/strong&gt;[Concrete/ Quite Interesting]&lt;/strong&gt;&amp;#160; Well probably, but that’s not important now. I came home from a short trip to Malaysia to find my Windows 7 desktop had crashed. We have a lot of laptops at home but the desktop is the family machine. It’s connected to my Samsung 6000 LED TV and we use it for music, pictures and 1080p movies. &lt;/p&gt;  &lt;p&gt;After a day trying to recover a crashed registry, I finally decided it would be simpler to reinstall Windows. The data on this machine is important and lives mostly on a 1TB USB drive, so re-install is not a problem. Then I had a mad idea – why Windows, why not Mac OSX?&lt;/p&gt;  &lt;h5&gt;Mac OS -&amp;#160; Why, because I can&lt;/h5&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndr8gz4rI/AAAAAAAAAEQ/Yaa7BXx9qA8/s1600-h/macvwin3.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="macvwin" border="0" alt="macvwin" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndsQFRQTI/AAAAAAAAAEU/011G0W6pvuo/macvwin_thumb1.jpg?imgmax=800" width="128" height="93" /&gt;&lt;/a&gt; As always, I’m very busy. I’ve got a couple of development projects running, some updates to write, articles, work and with the family away, a golden opportunity to clean the house from top to bottom. But I want to – why, because I can. &lt;/p&gt;  &lt;p&gt;Not just because I can, but also because I like Mac OSX. I’m 90% Microsoft and very comfortable with their operating systems. I’ve worked with Windows since pre-release version 1 in 1984, matured alongside the operating systems, had a deep appreciation for Windows 3.1, briefly flirted with OS/2 and Presentation Manager then back to Windows NT through to the present day Windows 7. But the other 10% has an eye for Mac OS and over time I’ve been more and more seduced by the ‘I don’t know where the operating system is, and I don’t care’ attitude flaunted by Mac OSX.&lt;/p&gt;  &lt;p&gt;Then there are my children – my two older kids only know Microsoft, the two little ones have had the opportunity to play with both operating systems – I think it’s important they all are able to experience the best of both operating systems.&lt;/p&gt;  &lt;h5&gt;iATKOS 7&lt;/h5&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7nds-rrPoI/AAAAAAAAAEY/iBR_VXHeulI/s1600-h/iATKOS13.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="iATKOS1" border="0" alt="iATKOS1" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndtL5eAVI/AAAAAAAAAEc/pbN1ViHtzCs/iATKOS1_thumb1.jpg?imgmax=800" width="136" height="121" /&gt;&lt;/a&gt; So where to start. Well we need to install an x86 version of Mac OSX – known as Mac OSX86. I have a Mac Book running Mac OSX 10.6.3 – a.k.a Snow Leopard. So a single step installation of this operating systems would be good. &lt;/p&gt;  &lt;p&gt;After some research, I settled on &lt;a href="http://www.infinitemac.com/f36/iatkos-v7-t3199/" target="_blank"&gt;iATKOS v7&lt;/a&gt; – this provides a one step installation of OSX 10.5.x on Intel/AMD chipsets. Ok, not Snow Leopard, but Leopard is close enough.&lt;/p&gt;  &lt;p&gt;You can get a copy of iATKOS v7 from the internet, download from Torrent or RapidShare, just search for iATKOS v7. &lt;/p&gt;  &lt;h5&gt;Tip – Solve it with cheap hardware.&lt;/h5&gt;  &lt;p&gt;iATKOS is very good but takes some getting used to. After many days I still had a problem with my network card not being recognized. I tried vanilla install, selecting drivers, installing and applying drivers, following some very detailed hacks, but nothing worked. Either, no network, occasionally, no operating system. &lt;/p&gt;  &lt;p&gt;Then I went out to &lt;a href="http://www.pcworld.co.uk/gbuk/index.html" target="_blank"&gt;PC World&lt;/a&gt; and bought a cheap 10/100 LAN card. The sales assistant treated me like an escaped lunatic but I found a cheap card with a Mac OSX symbol on the side. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/S7ndtnMlS3I/AAAAAAAAAEg/I85hvsVyqnA/s1600-h/mac6.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="mac" border="0" alt="mac" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/S7ndt_Ekt_I/AAAAAAAAAEk/NPesk8gw_Rs/mac_thumb4.jpg?imgmax=800" width="67" height="78" /&gt;&lt;/a&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/S7nduQLMnbI/AAAAAAAAAEo/vN2uWRhDwhg/s1600-h/THN1001_F5D50003.gif"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="THN1001_F5D5000" border="0" alt="THN1001_F5D5000" align="left" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/S7nduwfJCOI/AAAAAAAAAEs/wx7SVfjFRsU/THN1001_F5D5000_thumb1.gif?imgmax=800" width="100" height="100" /&gt;&lt;/a&gt; I chose a Belkin F5D5000 32-bit PCI network card. It cost about £10 and Belkin have Mac OS drivers. Put the card in, did a vanilla install and it worked first time.&lt;/p&gt;  &lt;h5&gt;Almost done – Quartz Support&lt;/h5&gt;  &lt;p&gt;Great – all working. Well not quite. I noticed that screen savers were not running, but wasn’t too concerned since these are not important. I then installed iLife – great application suite, the best photo slideshows I’ve ever seen through iPhoto. &lt;/p&gt;  &lt;p&gt;But iPhoto slideshows didn’t work. After some digging I found out that certain graphics functions require Quartz Extreme support.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Quartz_Compositor#Quartz_Extreme" target="_blank"&gt;Quartz Extreme&lt;/a&gt; is hardware accelerated graphics support based on OpenGL commands. For this you need an AGP 2X graphics card or better. (AGP 4X, 8X and PCI Express). &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/S7ndvcncB1I/AAAAAAAAAEw/oxv_HW91gkU/s1600-h/g_025100434.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="g_02510043" border="0" alt="g_02510043" align="left" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/S7ndvgO6IgI/AAAAAAAAAE0/QrbaCgx5H-U/g_02510043_thumb2.jpg?imgmax=800" width="131" height="113" /&gt;&lt;/a&gt; Again, the simplest solution was a trip to PC World and I picked up an &lt;a href="http://www.nvidia.co.uk/object/geforce_8400_uk.html" target="_blank"&gt;NVIDIA GeForce 8400 GS&lt;/a&gt; for about £25 which did the trick. Simply replaced my graphics card and it worked first time.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h5&gt;The Results&lt;/h5&gt;  &lt;p&gt;So was it worth it. I think so. Take a look at the results:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/S7ndwDyQJfI/AAAAAAAAAE4/oWfyVFClQio/s1600-h/IMG_00853.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="IMG_0085" border="0" alt="IMG_0085" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/S7ndw9PcEdI/AAAAAAAAAE8/97bvzgUHaxQ/IMG_0085_thumb1.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Samsung 6000 LED and Virgin&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/S7ndxhq9nzI/AAAAAAAAAFA/QPohl0w8KB0/s1600-h/IMG_00872.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="IMG_0087" border="0" alt="IMG_0087" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndyLhqzqI/AAAAAAAAAFE/hFUPjEfjvAg/IMG_0087_thumb.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Turn over to Mac OSX86 Leopard&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndy3hrKmI/AAAAAAAAAFI/UAHJgrdJfgc/s1600-h/IMG_00882.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="IMG_0088" border="0" alt="IMG_0088" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/S7ndzeCMF6I/AAAAAAAAAFM/MTYw4CAYeG8/IMG_0088_thumb.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Running iPhoto&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7nd0HxioqI/AAAAAAAAAFQ/mpbb5aQdIyI/s1600-h/IMG_00892.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="IMG_0089" border="0" alt="IMG_0089" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/S7nd0j6LOdI/AAAAAAAAAFU/R1u1TV0LwcQ/IMG_0089_thumb.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;and my favourite, XBMC media centre.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/S7nd1FQXmrI/AAAAAAAAAFY/rwaHehdKMss/s1600-h/macfull3.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="macfull" border="0" alt="macfull" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/S7nd1w6Jv_I/AAAAAAAAAFc/T2kzG0BrClE/macfull_thumb1.jpg?imgmax=800" width="407" height="228" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Give it a try for yourself.&lt;/strong&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-5238926390705132687?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/_So8nGDwZDs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/5238926390705132687/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/04/mac-on-pc-are-you-mad.html#comment-form" title="11 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5238926390705132687?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/5238926390705132687?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/_So8nGDwZDs/mac-on-pc-are-you-mad.html" title="Mac on a PC, are you mad?" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_dRqAiVqJ-R8/S7ndro74CoI/AAAAAAAAAEM/mx7YTqgJrUM/s72-c/iATKOS_thumb4.jpg?imgmax=800" height="72" width="72" /><thr:total>11</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/04/mac-on-pc-are-you-mad.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UHQ3gzeip7ImA9WxBWE0w.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-794547872008433952</id><published>2010-01-28T14:38:00.002Z</published><updated>2010-02-04T20:27:12.682Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-04T20:27:12.682Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Number Theory" /><category scheme="http://www.blogger.com/atom/ns#" term="natural base" /><title>Extract from “Echoes of a Dying Man”</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ivg3RgKTZZbl8M6ToHOPnC-oIsc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ivg3RgKTZZbl8M6ToHOPnC-oIsc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ivg3RgKTZZbl8M6ToHOPnC-oIsc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ivg3RgKTZZbl8M6ToHOPnC-oIsc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://lh3.ggpht.com/_dRqAiVqJ-R8/S2GhcnGL9vI/AAAAAAAAAD0/EOJZk629QF0/s1600-h/echoes%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="echoes" border="0" alt="echoes" align="left" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/S2GhdPbBM7I/AAAAAAAAAD4/6ueJpKMF16E/echoes_thumb%5B2%5D.jpg?imgmax=800" width="139" height="124" /&gt;&lt;/a&gt; [Abstract/A bit interesting]&lt;/strong&gt;&amp;#160; - The book is about a man who dreams of his death and super beings who control his life. &lt;/p&gt;&lt;p&gt;Steeped in his own paranoia, often he misses what is really going on around him, until one day, it is gone.&lt;/p&gt;&lt;p align="justify"&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/S2GhdvrtssI/AAAAAAAAAD8/raY7bw2RNEw/s1600-h/newrule%5B15%5D.gif"&gt;&lt;em&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;I remember Dad saying, “never drink alone” – stupid old bastard. How did he know, maybe he’s one of them? No, not nearly smart enough. Of course, what he really meant was, he drank alone and he was a drunk. If you drink alone, you’re a drunk. He was right.&lt;/em&gt; &lt;/p&gt;&lt;p align="justify"&gt;Rob got off the tube at Kings Cross, but instead of running to catch the crowd on platform 8, he walked out of the station and found himself at a local watering hole, the Kings Arms just off York Way. He had never been there before. This was Rob’s new game. &lt;/p&gt;&lt;p align="justify"&gt;The usual pattern was to stop for a couple of pints on the main platform. Well usual, up until today. Today, Rob had turned over a new leaf. Nothing so grand as actually giving up the drink, but if he was going to drink, he would certainly make the effort of doing it in style. Rob hated patterns, he hated drinking in the same pub and he really hated drinking along side people he recognised. And he knew all the people in the shit-hole on platform 8.&lt;/p&gt;&lt;p align="justify"&gt;Of course, he didn’t really know them, he hadn’t talked to any of them, but he recognised them, they all wore similar clothes, sported a five o’clock shadow. They all looked tired and fucked off - they were all called Rob and none of them wanted to live another minute of the day sober. So a new leaf and green pastures, only problem was, Kings Cross is not that green. But it is close. &lt;/p&gt;&lt;p align="justify"&gt;Rob had no idea what he would find when he stepped through the sticky doors of the Kings Armpit. He was sickened, so much that he almost turned around and walked straight out to find another public house. On the short journey that lies between the station and the pub, Rob’s imagination went wild. By the time he had arrived, he had imagined a quaint old English Pub, the sort of welcoming Inn that Dick Turpin may have frequented, either before, during or after committing his infamous larceny - what a hero.&lt;/p&gt;&lt;p align="justify"&gt;Nope, this place was best served by the title – ‘A Dive’. Maybe that was being generous. It was worse than the ‘shit hole’ on platform 8. Inside, the pub was bright – that is of course a very bad sign, bright pub means ‘beware of the violent crowd’ or ‘kid’s pub’ or both. At least half of the crowd looked violent – full of Builders, huge hands, fat faces and bellies, a sea of blue jeans and a strong stench of sweat, plaster and paint. Another quarter of the pub was filled with what could only be described as old tarts – absolute ‘bow wows’. ‘Maybe this is where they go for a good night out?’ mused Rob. &lt;/p&gt;&lt;p align="justify"&gt;What made Rob nearly turn around and run for his little insignificant life was the last quarter of the pub’s regulars. For a moment, Rob didn’t know whether to cry or throw up. There they were, the 5 o’clock shadow crowd. Yes, he wasn’t the only sad fuck to try the ‘nearest pub to the station’ experiment.&lt;/p&gt;&lt;p align="justify"&gt;‘Fuck’, he thought, ‘well just a quick one here’ and like a moth to a flame, he flew to the bar. The bar was crowded and of course, Rob didn’t want to be too pushy. He wasn’t scared of getting into a fight, not even a fight with a builder, not even a fight with a builder and his bitch. Rob told himself he wasn’t scared of anything, except being sober.&lt;/p&gt;&lt;p align="justify"&gt;Rob had a technique of getting served fast, a technique he dearly now needed. He didn’t push to the bar and he never held the cash out in his hand. These were crude methods and Rob hated being crude in that way. His method was based on ‘appeal’. He knew the staff would see him, ‘good staff’ always knows who’s at the bar and who’s next. All he had to do was look charming and wait and usually, he didn’t wait long. &lt;/p&gt;&lt;p align="justify"&gt;Today was one of those days – the bar staff were just interested in taking money, never mind etiquette. ‘Fuck’ he thought, ‘Fucking typical, I guess this goes with the territory’, Rob’s inner voice seemed rightly annoyed but it was still in control and somewhat resigned to the situation. He changed his tactic towards the crude end of the spectrum. He deemed to show the cash in his hand, leaned forward, and attempted to flash his eyebrows at the nearest member of the staff. But he still wasn’t been served. &lt;/p&gt;&lt;p align="justify"&gt;‘Fucking Madhouse’. A particularly ape like creature was standing next to Rob and in his simple pattern of thought; he too was getting pissed off. ‘Yeah, be our go soon’ Rob replied, Rob didn’t want a conversation about football or plastering, he wanted a pint. Rob thought how getting the first pint in was worse that wanting a piss. When you are really desperate, the closer to the loo you get, the worse the desire to piss your pants, and right now, Rob was ‘busting’ for a pint. Just then, the Neanderthal to Rob’s left got served. ‘Cunt white trash’ thought Rob. He pulled back into himself for a moment, first to check he hadn’t said anything out loud, then to tick himself off for such an bad thought. It wasn’t the thought in itself, rather the locale. &lt;/p&gt;&lt;p align="justify"&gt;The Ape Man placed his order – an extraordinarily large order of spirits and beers, crisps and peanuts, more beer and some ‘alco pops’ for his bitches. Now Rob was determined, he would be served next. As the girl serving returned to the Ape Man with his change, Rob shouted ‘yes love, I’ll have a pint of Stella’. &lt;/p&gt;&lt;p align="justify"&gt;‘Who’s next’ asked the girl. She did this without making eye contact with anyone standing at the bar, the sort of skill you only find in an inner city pub located conveniently close to a mainline station. ‘Yeah Cheers’ shouted Rob. She looked at him and with a fleeting contact of their eyes, ‘Success!’ thought Rob.&lt;/p&gt;&lt;p align="justify"&gt;After all that time and anguish, all Rob could think of was revenge, his need for alcohol was momentarily put aside and his fury was aimed at the bar girl. Rob now played a small game he been forced to learn as a child - ‘Make them feel worse than they made me feel’ a game he had made up to regain his self esteem and control in previous times like these. He would now make the bar girl wait for him, make her beg for forgiveness. ‘Yes Luv?’ – that was her opening gambit. Usually the poor fuck on the other side of the bar would be so ecstatic with being served they would blurt out their order. Not Rob.&amp;#160; He wanted a pint badly, but he was pissed off now. He waited to answer, he wasn’t looking at her but he could feel her confusion. Confused at first, but the confusion would quickly lead to irritation, and then anger and back to confusion -&amp;#160; a type of confusion that would have the girl questioning her very competence as a barmaid. Had the girl forgotten to say ‘Please’ or ‘Sir’? Wait a bit longer and she would be angry again and her anger would show in a pointless outburst like ‘Yes, you’re next’ or ‘Yes, what can I get you!’ or even ‘Yes fucker, what did I ever do to you’ – ‘You made me wait’ would be Rob’s response. &lt;/p&gt;&lt;p align="justify"&gt;Rob may have been crap at getting served in a crowded London bar, but he was the quintessential expert at secretly pissing people off. He was causing her to sweat and in less than two seconds he had put the poor girl on an emotional rollercoaster – in her inner mind she was probably asking herself if she was even fit to do the job. Rob knows about the magic number e – 2.71828182845904. Rob loved Number Theory; he thought certain numbers almost mystic. In his dreams, numbers frequently appeared, almost as signposts pointing to the answer, always cryptic but most definitely there. If you want to make someone uncomfortable, if you want them to doubt themselves, leave between 2 and 3 seconds gap between their first request and your answer, preferably 2.72 seconds. &lt;/p&gt;&lt;p align="justify"&gt;Just before the bar girl had regained her self belief and thought Rob a total Wanker,&amp;#160; milliseconds before Rob had lost access to his first pint and like Snakes and Ladders, had plummeted back to square one, Rob replied, slowly, quietly and deliberately, ‘Yes, Cheer… Can I have a pint of your finest Stella?’&lt;/p&gt;&lt;p align="justify"&gt;‘Wanker’ thought the bar girl as she turned towards the pump. Rob could hear what she was thinking, almost as if she had said it to his face, but it was too late now, Rob had his revenge and soon was to take a sip of his salvation.&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/S2GhdvrtssI/AAAAAAAAAD8/raY7bw2RNEw/s1600-h/newrule%5B15%5D.gif"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="newrule" border="0" alt="newrule" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/S2GheBTyDEI/AAAAAAAAAEA/0ZKuWM3HGmw/newrule_thumb%5B11%5D.gif?imgmax=800" width="200" height="22" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-794547872008433952?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/xPruWGNJxjE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/794547872008433952/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2010/01/extract-from-echoes-of-dying-man.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/794547872008433952?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/794547872008433952?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/xPruWGNJxjE/extract-from-echoes-of-dying-man.html" title="Extract from “Echoes of a Dying Man”" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_dRqAiVqJ-R8/S2GhdPbBM7I/AAAAAAAAAD4/6ueJpKMF16E/s72-c/echoes_thumb%5B2%5D.jpg?imgmax=800" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2010/01/extract-from-echoes-of-dying-man.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQMQnw4cCp7ImA9WxFWGUk.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-4867865587163329283</id><published>2009-12-23T15:45:00.022Z</published><updated>2010-06-07T20:53:03.238+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-07T20:53:03.238+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="MAKECERT" /><category scheme="http://www.blogger.com/atom/ns#" term="IExpress" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="WWW.FAR2MUCHMAIL.COM" /><category scheme="http://www.blogger.com/atom/ns#" term="Bootstrap" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term="Click Once" /><category scheme="http://www.blogger.com/atom/ns#" term="C++" /><category scheme="http://www.blogger.com/atom/ns#" term="Deployment Project" /><title>C++ IS useful - Click Once workaround</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Szh5yQ1tlnpI-AIgoCZQwNzeAxw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Szh5yQ1tlnpI-AIgoCZQwNzeAxw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Szh5yQ1tlnpI-AIgoCZQwNzeAxw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Szh5yQ1tlnpI-AIgoCZQwNzeAxw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a class="image-link" href="http://lh5.ggpht.com/_dRqAiVqJ-R8/SzI672YrUeI/AAAAAAAAADk/Gn53p01OqbA/s1600-h/cpp3.jpg"&gt;&lt;img align="left" alt="cpp" height="139" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/SzI686EHvCI/AAAAAAAAADo/E9V7-A-Ws4w/cpp_thumb1.jpg?imgmax=800" style="display: inline; float: left; margin: 0 10px 10px 0;" title="cpp" width="108" /&gt;&lt;/a&gt;&lt;strong&gt;[Concrete/ Quite Interesting]&lt;/strong&gt; Me and C++, we’re old mates – the sort you find on Friends Reunited. I was fortunate to work with Microsoft and Glockenspiel in the late 80’s, working in MFC 1.0 and beyond. Later I would teach C++ for QA Training and others. I really loved the language and managed to build some sizeable solutions in C++, MFC and ATL.&lt;br /&gt;
&lt;div style="clear: both;"&gt;Then came .Net and C# and for me as with many others, we could now start to focus on the problem domain, design and patterns to solution rather than pointers, memory and language syntax.&lt;/div&gt;&lt;div style="clear: both;"&gt;In C# almost every line of code looks cleaner and addresses the issue at hand, something C++ never seemed to achieve. Look at this simple example taken randomly from the MSDN:&lt;/div&gt;&lt;h5&gt;HTTPWebRequest in C++&lt;/h5&gt;&lt;pre class="brush: cpp; wrap-lines: false"&gt;#using &amp;lt;System.dll&amp;gt;

using namespace System;
using namespace System::Net;
using namespace System::Text;
using namespace System::IO;

// Specify the URL to receive the request.
int main()
{
   array&amp;lt;string^&amp;gt;^args = Environment::GetCommandLineArgs();
   HttpWebRequest^ request = dynamic_cast&amp;lt;httpwebrequest^&amp;gt;(WebRequest::Create( args[ 1 ] ));

   // Set some reasonable limits on resources used by this request
   request-&amp;gt;MaximumAutomaticRedirections = 4;
   request-&amp;gt;MaximumResponseHeadersLength = 4;

   // Set credentials to use for this request.
   request-&amp;gt;Credentials = CredentialCache::DefaultCredentials;
   HttpWebResponse^ response = dynamic_cast&amp;lt;httpwebresponse^&amp;gt;(request-&amp;gt;GetResponse());
   Console::WriteLine( "Content length is {0}", response-&amp;gt;ContentLength );
   Console::WriteLine( "Content type is {0}", response-&amp;gt;ContentType );

   // Get the stream associated with the response.
   Stream^ receiveStream = response-&amp;gt;GetResponseStream();

   // Pipes the stream to a higher level stream reader with the required encoding format. 
   StreamReader^ readStream = gcnew StreamReader( receiveStream,Encoding::UTF8 );
   Console::WriteLine( "Response stream received." );
   Console::WriteLine( readStream-&amp;gt;ReadToEnd() );
   response-&amp;gt;Close();
   readStream-&amp;gt;Close();
}
&lt;/pre&gt;&lt;h5&gt;HTTPWebRequest in C#&lt;/h5&gt;&lt;pre class="brush: cpp; wrap-lines: false"&gt;using System;
using System.Net;
using System.Text;
using System.IO;

public class Test
{
    // Specify the URL to receive the request.
    public static void Main (string[] args)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create (args[0]);

        // Set some reasonable limits on resources used by this request
        request.MaximumAutomaticRedirections = 4;
        request.MaximumResponseHeadersLength = 4;
        // Set credentials to use for this request.
        request.Credentials = CredentialCache.DefaultCredentials;
        HttpWebResponse response = (HttpWebResponse)request.GetResponse ();

        Console.WriteLine ("Content length is {0}", response.ContentLength);
        Console.WriteLine ("Content type is {0}", response.ContentType);

        // Get the stream associated with the response.
        Stream receiveStream = response.GetResponseStream ();

        // Pipes the stream to a higher level stream reader with the required encoding format. 
        StreamReader readStream = new StreamReader (receiveStream, Encoding.UTF8);

        Console.WriteLine ("Response stream received.");
        Console.WriteLine (readStream.ReadToEnd ());
        response.Close ();
        readStream.Close ();
    }
}
&lt;/pre&gt;&lt;div style="clear: both;"&gt;Ok, not a huge difference, but enough of a difference to make C# easier to write, easier to read and so we can focus better on the implementation. I really liked C++ but for me there is no reason to work so hard – C# makes coding more enjoyable and productive.&lt;/div&gt;&lt;div style="clear: both;"&gt;No reason until…&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;h3&gt;Click Once Deployment – sounds simple – NOT&lt;/h3&gt;&lt;div style="clear: both;"&gt;I have been developing an Office Outlook Plug-in for &lt;a href="http://www.far2muchmail.com/"&gt;www.far2muchmail.com&lt;/a&gt;. Far2MuchMail is a solution to help people better manage their emails in Outlook and it’s free. What I wanted was to make it easy for people to download and install the plug-in from the Internet so naturally I thought ‘Click Once Deployment – just the puppy’. WRONG.&lt;/div&gt;&lt;div style="clear: both;"&gt;I want to encourage people to use my plug-in. Did I say it was free. Being free, the last thing I wanted was loads of messages during installation telling the user that the software was from an unknown publisher, couldn’t be trusted, would probably cause a fire in the house, sell your wife and kids to criminals, cause global warming, and so on. I mean, you would never install software, free or not, with that level of scare mongering.&lt;/div&gt;&lt;div style="clear: both;"&gt;I understood why the warnings were there and knew that if I bought a certificate, my problems would be over. But somehow I felt like I was being conned. A certificate felt like one of those Microsoft Taxes – like MSDOS. There had to be another way.&lt;/div&gt;&lt;div style="clear: both;"&gt;I knew I could create a certificate using MAKECERT. but to stop Click Once raising alarming messages, this certificate needed to be installed prior to installing the app.&lt;/div&gt;&lt;div style="clear: both;"&gt;I came across an article that talked about programmatically adding your certificate into the Trusted Installer list on a local machine. Great, all I needed to do was implement this and have it called at the start of installation as a pre-action.&lt;/div&gt;&lt;div style="clear: both;"&gt;But Click Once is locked down so I had to look at an alternative installer. I decided upon a standard Deployment Project in Visual Studio. Finally I was able to install my certificate prior to installing the plug-in. But there was a problem…&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;h3&gt;VS2008 and the Deployment Project Bootstrap.&lt;/h3&gt;&lt;div style="clear: both;"&gt;Unfortunately, in VS2008 Microsoft changed the bootstrap program (setup.exe) as part of their deployment package. The change was subtle but important. Previous versions of the program would remain in memory until the installation ended. This allowed you to use a program like WinZip or IExpress to package the setup and MSI into a single executable. Setup.exe would remain in memory until the setup had completed so you could reliably let WinZip or IExpress wait until the setup had finished then automatically clean up any temporary files. Everything worked. In VS2008, the bootstrap setup.exe was designed to leave memory as soon as it had kicked off the MSI package. The result was the single package created by WinZip or IExpress would think the setup has finished and would start to pull temporary files, causing the setup to fail. Very frustrating.&lt;/div&gt;&lt;div style="clear: both;"&gt;I was nearly there, all I now needed to do was keep the package from terminating while the MSI was installing. C++ to the rescue.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;h3&gt;WaitForProcess - C++ application&lt;/h3&gt;&lt;div style="clear: both;"&gt;&lt;a class="image-link" href="http://lh3.ggpht.com/_dRqAiVqJ-R8/SzI691cNMlI/AAAAAAAAADs/KOmTBbVf8qo/s1600-h/image3.png"&gt;&lt;img align="left" alt="image" border="0" height="189" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/SzI6-gn6bMI/AAAAAAAAADw/J5Tah-Zc39w/image_thumb1.png?imgmax=800" style="display: inline; float: left; margin: 0 10px 10px 0;" title="image" width="244" /&gt;&lt;/a&gt; I decided to use IExpress to package my setup into a single executable deployable from my website. IExpress allowed me to name the components of the installation including the setup.exe bootstrap and MSI package. It also allows you to define post installation steps. It is here I named a program called WaitForProcess, written in C++.&lt;/div&gt;&lt;div style="clear: both;"&gt;WaitForProcess is very simple, it continues to run if a named process is still in memory. So calling WaitForProcess and naming msiexec.exe in the installation package is all I need to do to ensure the IExpress package remain in memory until the MSI package has completed installation.&lt;/div&gt;&lt;div style="clear: both;"&gt;The trick was to write WaitForProcess without any dependencies on .NET which may not be installed until after the setup has completed. Indeed, I needed to rely on as few components as possible. C++ without ATL/MFC only requires standard libraries like USER32 and KERNEL.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div style="clear: both;"&gt;So here is the code to WaitForProcess:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: cpp; wrap-lines: false"&gt;// WaitForProcess.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "WaitForProcess.h"
#include &amp;lt;psapi.h&amp;gt;
#include &amp;lt;shlwapi.h&amp;gt;

bool IsRunning(LPCTSTR pProcessName) //findProcess should be 1,2 or 3 and tells us which of the predefined processes to look for (see searchString array below)
{
    DWORD Processes[1024]; //Buffer array where enumerated processes will go
    TCHAR ProcessPath[MAX_PATH] = _T(""); //Path of the examined process
    HANDLE hProcess = 0; //Handle of the examined process
    DWORD bytesNeeded; //bytes written into buffer array by EnumProcesses
    int nProcesses; //Number of actually enumerated processes calculated from bytesNeeded
    int i=0;
    bool found=false; //Stop examining further processes if found is true
 
    if(EnumProcesses(Processes, sizeof(Processes), &amp;amp;bytesNeeded))
    {
        nProcesses = bytesNeeded / sizeof(DWORD);
        while((i&amp;lt;nProcesses) &amp;amp;&amp;amp; (!found))
        {
            if(Processes[i]!=0)
            {   
                hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]);
    
                if(hProcess)
                {
                    GetProcessImageFileName(hProcess, ProcessPath, sizeof(ProcessPath)/sizeof(TCHAR));
                    PathStripPath(ProcessPath);
                    found=!_tcsicmp(ProcessPath, pProcessName);
                }
            }
            i++;
        }
    }
    return found;
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);

    //TCHAR sMessage[1024] = _T("");
    //wsprintf(sMessage, _T("Process: %s"), lpCmdLine);
    //::MessageBox(0, sMessage, _T("Wait For Process"), MB_OK);
    ::Sleep(5000);

    if (IsRunning(lpCmdLine))
    {
        //::MessageBox(0, _T("Found"), _T("Wait For Process"), MB_OK);

        while(IsRunning(lpCmdLine))
            ::Sleep(500);
    }
    //::MessageBox(0, _T("Done"), _T("Wait For Process"), MB_OK);
    return 0;
}
&lt;/pre&gt;&lt;/div&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;div style="clear: both;"&gt;Well, I still find C# a more productive language but it goes to show, when the going gets tough, the tough turn to C++.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-4867865587163329283?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/1-juVGMIyhk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/4867865587163329283/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/12/c-is-useful-click-once-workaround.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4867865587163329283?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/4867865587163329283?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/1-juVGMIyhk/c-is-useful-click-once-workaround.html" title="C++ IS useful - Click Once workaround" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_dRqAiVqJ-R8/SzI686EHvCI/AAAAAAAAADo/E9V7-A-Ws4w/s72-c/cpp_thumb1.jpg?imgmax=800" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/12/c-is-useful-click-once-workaround.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkAMQXg9cCp7ImA9WxBTGU8.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-2715826858201462064</id><published>2009-12-15T00:20:00.001Z</published><updated>2009-12-16T00:53:00.668Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-16T00:53:00.668Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="touch" /><category scheme="http://www.blogger.com/atom/ns#" term="Java" /><category scheme="http://www.blogger.com/atom/ns#" term="Cosy Orbs" /><category scheme="http://www.blogger.com/atom/ns#" term="Offline" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="HTML5" /><category scheme="http://www.blogger.com/atom/ns#" term="Graphics" /><category scheme="http://www.blogger.com/atom/ns#" term="Canvas" /><category scheme="http://www.blogger.com/atom/ns#" term="user interface" /><category scheme="http://www.blogger.com/atom/ns#" term="jQuery" /><title>Cosy Orbs – Web App Games in HTML5 for the iPhone</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/w5fs8Btf9YCqi_e3W3PHU8CNxNQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/w5fs8Btf9YCqi_e3W3PHU8CNxNQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/w5fs8Btf9YCqi_e3W3PHU8CNxNQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/w5fs8Btf9YCqi_e3W3PHU8CNxNQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_dRqAiVqJ-R8/SybWR0H8eoI/AAAAAAAAADM/OKm25WxnzU0/s1600-h/image%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="image" border="0" alt="image" align="left" src="http://lh6.ggpht.com/_dRqAiVqJ-R8/SybWSnlNz7I/AAAAAAAAADQ/mF7LrdIZBeQ/image_thumb%5B2%5D.png?imgmax=800" width="131" height="240" /&gt;&lt;/a&gt; &lt;strong&gt;[Concrete/Interesting]&lt;/strong&gt; In this article I’m going to talk about HTML5 Canvas and graphics programming for web apps. I’ll be focusing on my experience and as way of example, I will be talking about ‘Cosy Orbs’ – a very simple, interactive graphics web application. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/SybWS8AifEI/AAAAAAAAADU/F4UlM-prICw/s1600-h/icon5.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="http://www.lowrieweb.com/iphone/cosyorbs" border="0" alt="http://www.lowrieweb.com/iphone/cosyorbs" align="left" src="http://lh5.ggpht.com/_dRqAiVqJ-R8/SybWTB3-u-I/AAAAAAAAADY/WEZN4Y7SFZk/icon_thumb3.png?imgmax=800" width="52" height="55" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Installing Cosy Orbs&lt;/h3&gt;  &lt;p&gt;You can install Cosy Orbs onto your iPhone by following the link below in Safari:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.lowrieweb.com/iphone/cosyorbs"&gt;http://www.lowrieweb.com/iphone/cosyorbs&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Once the page has loaded, ‘Add to Home Screen’ by selecting the ‘+’ icon on the navigation bar at the bottom of the Safari page. Cosy Orbs will now be placed on your home screen and can be run by clicking the application icon.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h3&gt;About Cosy Orbs&lt;/h3&gt;  &lt;p&gt;In 1999, the moon was knocked out of its orbit by a nuclear waste explosion, sending all 311 inhabitants of Moonbase Alpha into outer space. Also, like a lot of other people, I was looking at Java and Java Applets. Working my way through the SDK and samples, I came across a sample called &lt;a href="http://java.sun.com/applets/jdk/1.4/demo/applets/GraphLayout/example1.html" target="_blank"&gt;‘Graph Layout’&lt;/a&gt;. I was quite impressed with Java but I was blown over by Graph Layout. It was so cool. Basically, a string parameter to the applet defined a number of nodes on a network graph. So the string ‘Nigel-Aiesha, Aiesha-Sophia, Sophia-Nigel, Sophia-Melina, Melina-Aiesha’ would define a graph with four nodes (Nigel, Aiesha, Sophia and Melina). In addition, it defines the relationship between these nodes. &lt;/p&gt;  &lt;p&gt;The applet would graphically represent these nodes as boxes connected by lines. The lines represent the relationship between the nodes. So, with the example above, the graph would look like:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/SybWU1YavMI/AAAAAAAAADc/ABk-bHWc2v4/s1600-h/image4.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/SybWWaV0WwI/AAAAAAAAADg/-Eol7LBlsK0/image_thumb2.png?imgmax=800" width="416" height="294" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;But that wasn’t the cool bit. &lt;/p&gt;  &lt;p&gt;The cool bit was the applet applies an algorithm to the graph that worked to minimize the stress between the nodes. The stress is defined as the length of the connecting lines, measured against a desired value. So if two nodes have a desired distance of 100 and the actual distance is 150, the stress is 50. &lt;/p&gt;  &lt;p&gt;Every cycle, the stresses are calculated and the nodes are moved relative to the other nodes so as to reduce the stress.The result is the graph gracefully reorganizes itself into the lowest stress state by moving the nodes about each other. &lt;/p&gt;  &lt;p&gt;Because of the nature of the algorithm and the fact that it acts across the entire system of nodes, the process of reducing the stress or ‘relaxing the graph’ is indeed very graceful.&lt;/p&gt;  &lt;p&gt;This applet has sat in a dusty corner of my mind waiting to be re-applied or re-engineered. Then came iPhone, HTML5 and Canvas and Cosy Orbs was born. &lt;/p&gt;  &lt;p&gt;Cosy Orbs uses a similar algorithm to relax the stress across a network of nodes, but:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt; It is written in HTML5, JavaScript, jQuery&lt;/li&gt;    &lt;li&gt; It is an iPhone Offline web app. &lt;/li&gt;    &lt;li&gt; It is more graphical with ‘Orbs’ or shiny spheres rather than boxes with text &lt;/li&gt;    &lt;li&gt; It uses a Touch enabled UI rather than mouse.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;So how’s it all done?&lt;/p&gt;  &lt;h3&gt;Offline Web App&lt;/h3&gt;  &lt;p&gt;This is a must have feature for any web app, especially a game or fun app. No problem on the iPhone, with HTML5 all you need to do is set up a manifest file. Well, not that simple but I’ve blogged about it before so won’t repeat myself here. See &lt;a href="http://not-now-nigel.blogspot.com/2009/11/iphone-offline-web-apps-restful-way.html" target="_blank"&gt;iPhone Offline Web Apps – the RESTful Way – Part 1 of 2&lt;/a&gt;.&lt;/p&gt;  &lt;h3&gt;Initializing the Canvas&lt;/h3&gt;  &lt;p&gt;The Canvas is an HTML5 element that represents a drawing surface. In essence it is a bitmap. HTML5 currently support ‘2D’ drawing operations and some other useful functions over the canvas like streaming the canvas to a string.&lt;/p&gt;  &lt;p&gt;I use the following code to initialize the canvas:&lt;/p&gt;  &lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;myCanvas = document.createElement(&lt;span style="color: #006080"&gt;'canvas'&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;myCanvas.id = &lt;span style="color: #006080"&gt;'myCanvas'&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;myCanvas.width = 300;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;myCanvas.height = 300;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$(&lt;span style="color: #006080"&gt;'#myDrawing'&lt;/span&gt;).append(myCanvas);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;After creating the canvas, the element is appended to a DIV on my page. I also remove a compatibility statement. This statement is shown if you run the app in an incompatible browser, otherwise it is removed:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$(&lt;span style="color: #006080"&gt;&amp;quot;#notice&amp;quot;&lt;/span&gt;).hide();&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;div&lt;/span&gt; &lt;span style="color: #ff0000"&gt;id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;notice&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;style&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;text-align:center&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;strong&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;This application is designed for the iPhone. Link to this page in Safari on your iPhone.&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;strong&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;div&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Preloading the Orbs&lt;/h3&gt;

&lt;p&gt;Unlike the Java Applet, I am representing nodes with lovely coloured spheres. I have five different images for my Orbs; blue, red, green, purple and yellow. So when drawing a node, I will assign it one of the colours. &lt;/p&gt;

&lt;p&gt;When drawing I want to work with Image elements, so I need to preload the images before any drawing starts. This was a bit tricky because loading an image requires going to the server to fetch the image data, so the Image.onload() is asynchronous. Because I wanted to wait for the images to finish loading before starting any drawing, I decided to set up a simple timer:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; imageString = &lt;span style="color: #006080"&gt;'images/orb-blue.png,images/orb-green.png,images/orb-purple.png,images/orb-red.png,images/orb-yellow.png'&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; imagesToLoad = 0;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt; initImages()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  $.each(imageString.split(&lt;span style="color: #006080"&gt;','&lt;/span&gt;), &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(idx, val) {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      imagesToLoad++;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      &lt;span style="color: #0000ff"&gt;var&lt;/span&gt; img = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Image();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      img.onload = &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;() {imagesToLoad--};&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      img.src = val;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      images.push(img);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    setTimeout(&lt;span style="color: #006080"&gt;'completeImageLoad()'&lt;/span&gt;,500);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt; completeImageLoad()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (imagesToLoad &amp;gt; 0)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    setTimeout(&lt;span style="color: #006080"&gt;'completeImageLoad()'&lt;/span&gt;,500);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;else&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// Ready to go&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The images are all named in the string ‘imageString’. When loading, I simply split this string, and for each image named, I create an Image element and load from the source. I also increment a load count, counting each image as it’s loaded. Finally I add the image to the list of preloaded images - this will be used when drawing the images.&lt;/p&gt;

&lt;p&gt;During the Image.onLoad(), which is fired asynchronously once the image has loaded, I decrement the load count, signalling that this images has loaded completely.&lt;/p&gt;

&lt;p&gt;Once all the images have been processed (but not necessarily completed loading), I call a complete method. This is called asynchronously using the setTimeout() method.&lt;/p&gt;

&lt;p&gt;Now all I need to do is poll for the load count to reach zero, which will happen when all the images have completed loading. If the images are still loading, the complete method is called again under timer.&lt;/p&gt;

&lt;p&gt;Once all the images have been loaded, I need to set up the nodes…&lt;/p&gt;

&lt;h3&gt;Initialize the Nodes and off we go&lt;/h3&gt;

&lt;p&gt;Nodes and their relationships are defined by a string called ‘edgeString’. The process of initializing the nodes involves reading this string, and building a list of Node() and Edge() objects. Each Node() object holds the canvas co-ordinates of the node and the image assigned to the node – these are used when drawing the network. Each Edge() object defines the stress between two Nodes(). As a node is created, an image from the list of images is assigned to the node in a round robin fashion.&lt;/p&gt;

&lt;p&gt;Once the Nodes() and Edges() are initialized, the network is rendered to the canvas.&lt;/p&gt;

&lt;p&gt;Finally, a timer is set to refresh the display:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt; drawCanvas()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  setTimeout(&lt;span style="color: #006080"&gt;'drawCanvas()'&lt;/span&gt;, refresh);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  relax();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (myCanvas.getContext) {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;var&lt;/span&gt; ctx = myCanvas.getContext(&lt;span style="color: #006080"&gt;'2d'&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    ctx.fillStyle = &lt;span style="color: #006080"&gt;'rgb(0,0,0)'&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    ctx.lineWidth = 2;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    ctx.fillRect(0,0,myCanvas.width,myCanvas.height);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;for&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; j=0; j&amp;lt;nodes.length; j++)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      ctx.drawImage(nodes[j].img, nodes[j].x-21, nodes[j].y-21);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;When fired, a function is called to Relax() the graph – that is, to process each node’s stresses,&amp;#160; incrementally reducing the stress across the network. Once the stress has been reduced and the network is a little more relaxed, a call is made to render the network to the canvas. This happens every 150ms, which provides a smooth animation with the network slowly relaxing into its lowest stress state.&lt;/p&gt;

&lt;p&gt;Rendering is simple, first, a ‘2d’ context is obtained on the canvas. With this context, the canvas is cleared with a black background through a call to the fillRect() function. Then for each node in our array of Nodes(),&amp;#160; the image on the node is drawn to the canvas using the drawImage() method.&lt;/p&gt;

&lt;h3&gt;What about Touch Events&lt;/h3&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Not much fun if you can’t interact with the Orbs, so I implemented a simple touch interface:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$(&lt;span style="color: #006080"&gt;'#myCanvas'&lt;/span&gt;).bind(&lt;span style="color: #006080"&gt;'touchstart touchmove touchend'&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(evt) {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;var&lt;/span&gt; touch = evt.originalEvent.targetTouches[0];&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;switch&lt;/span&gt; (evt.type) {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; &lt;span style="color: #006080"&gt;'touchstart'&lt;/span&gt;:dragNode = findNodeByPos(touch.pageX-&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.offsetLeft, touch.pageY-&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.offsetTop);&lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; &lt;span style="color: #006080"&gt;'touchend'&lt;/span&gt;:dragNode = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; &lt;span style="color: #006080"&gt;'touchmove'&lt;/span&gt;:&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (dragNode) {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        dragNode.x = touch.pageX-&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.offsetLeft;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        dragNode.y = touch.pageY-&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.offsetTop;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  evt.preventDefault();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;});&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;To do this I have to bind the touch events, touchstart, touchmove and touchend, to a callback function for the named element. jQuery make life very simple through the use of the bind() method.&amp;#160; &lt;/p&gt;

&lt;p&gt;In the event handler, the event type is determined and for touchstart events, the nodes are searched for a match to the touch position. That is to say, the nodes are searched for any node under the user’s finger. &lt;/p&gt;

&lt;p&gt;Touchmove tracks the movement of a finger on the iPhone surface. I use this to update the screen position of the selected node (if one was found during touchstart). Finally,&amp;#160; touchend stops the dragging operation. Job done.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;

&lt;p&gt;CosyOrbs, not exactly a game for the iPhone, but I am impressed as to how easy it is to create interactive graphics under HTML5. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-2715826858201462064?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/IWiotTzQo0U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/2715826858201462064/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/12/cosy-orbs-web-app-games-in-html5-for.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/2715826858201462064?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/2715826858201462064?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/IWiotTzQo0U/cosy-orbs-web-app-games-in-html5-for.html" title="Cosy Orbs – Web App Games in HTML5 for the iPhone" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_dRqAiVqJ-R8/SybWSnlNz7I/AAAAAAAAADQ/mF7LrdIZBeQ/s72-c/image_thumb%5B2%5D.png?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/12/cosy-orbs-web-app-games-in-html5-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4MQX44fip7ImA9Wx9WEE0.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-3140933361956488810</id><published>2009-12-01T23:09:00.002Z</published><updated>2011-01-14T10:29:40.036Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-14T10:29:40.036Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Safari 4" /><category scheme="http://www.blogger.com/atom/ns#" term="AJAX" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="Offline" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="HTML5" /><category scheme="http://www.blogger.com/atom/ns#" term=".manifest" /><category scheme="http://www.blogger.com/atom/ns#" term="Canvas" /><category scheme="http://www.blogger.com/atom/ns#" term="jQuery" /><title>iPhone Offline Web Apps – the RESTful Way – Part 2 of 2</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/OsEwk4GMX1l7-IaMGd8RTuDIY08/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OsEwk4GMX1l7-IaMGd8RTuDIY08/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/OsEwk4GMX1l7-IaMGd8RTuDIY08/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OsEwk4GMX1l7-IaMGd8RTuDIY08/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_dRqAiVqJ-R8/SxWiK2icuuI/AAAAAAAAADE/yjd76QtKXaU/s1600-h/html5REST%5B4%5D.gif"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="html5REST" border="0" alt="html5REST" align="left" src="http://lh4.ggpht.com/_dRqAiVqJ-R8/SxWiLtwg9fI/AAAAAAAAADI/7fDNbXMknzI/html5REST_thumb%5B2%5D.gif?imgmax=800" width="147" height="223" /&gt;&lt;/a&gt; [&lt;strong&gt;Concrete/Very Interesting&lt;/strong&gt;] Ok, so hopefully you got the static application working offline. I was quite satisfied when I got it working, though not nearly as much fun as watching Push in HD on my Samsung 6000 LED TV (thinner than my laptop – wow).     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Now for the RESTful part. We need to make REST calls when online and work from a cache when offline. &lt;/p&gt;  &lt;h4&gt;&amp;#160;&lt;/h4&gt;  &lt;h4&gt;&amp;#160;&lt;/h4&gt;  &lt;h4&gt;&amp;#160;&lt;/h4&gt;  &lt;h4&gt;&amp;#160;&lt;/h4&gt;  &lt;h4&gt;&amp;#160;&lt;/h4&gt;  &lt;h4&gt;Cache Manifest White list&lt;/h4&gt;  &lt;p&gt;As well as defining the resources that are cached, you can specify resources that should not be cached in your manifest file:&lt;/p&gt;  &lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 134px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;CACHE MANIFEST&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;# Version 0.9.4.1129.007&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;NETWORK:&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;/iretailpassportsvc/clientservices.svc&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;CACHE:&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;index.htm&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;images/arrow.png&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Any resources named under the ‘NETWORK:’ label are considered white listed and should not be fetched from the cache. &lt;/p&gt;

&lt;p&gt;Notice that you can name cached resources after a ‘NETWORK:’ section by using the ‘CACHE':’ label.&lt;/p&gt;

&lt;p&gt;Naming white listed resources allows us to access resources in our web app that are not in the cache. This solves a serious problem for us. Imagine trying to add your first service call to an offline application with static only resources. You can’t name the service in the manifest as a cacheable resource. This wouldn’t work the first time you went offline and the app would fail to load with a ‘Internet not available’ error. If you omit the resource, the manifest is wrong and you will end up running the cached app, the one without the service call. Real bummer.&lt;/p&gt;

&lt;p&gt;‘NETWORK:’ specifies a non-cached resource and one without fallback. There is a ‘FALLBACK:’ label that allows us to specify a non-cached resource and cached alternative. Useful if you want to put up a big banner when offline saying ‘Sorry, come back later’ but not what we are trying to do here.&lt;/p&gt;

&lt;p&gt;One final point about the white list before I move on. Unlike the named cached resources, the white list is in fact a resource prefix. So, in the example above, I named my REST service, not the individual methods. This is useful because our methods are likely to have parameters and we don’t know necessarily know what the endpoint looks like until runtime.&lt;/p&gt;

&lt;h4&gt;The HTML5 Database&lt;/h4&gt;

&lt;p&gt;So now to storing the results of our RESTful calls. The solution lies in another HTML5 feature – HTML database. In Safari and on the iPhone this means &lt;a href="http://www.sqlite.org/" target="_blank"&gt;SQLite&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I’m a real fan of SQLite. Great small footprint relational database. I’ve been using it for some time now under .NET as storage for my &lt;a href="http://www.far2muchmail.com/" target="_blank"&gt;Outlook plug in&lt;/a&gt;. &lt;/p&gt;

&lt;h5&gt;Storing Service Results&lt;/h5&gt;

&lt;p&gt;I chose a very simple schema for my database – simple Key/Values in a table using TEXT field types. SQLite, jQuery and HTML5 make it very easy for me:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt; initDatabase()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  db = openDatabase(&lt;span style="color: #006080"&gt;'iRetailPassport'&lt;/span&gt;, &lt;span style="color: #006080"&gt;'1.0'&lt;/span&gt;, &lt;span style="color: #006080"&gt;'iRetailPassport'&lt;/span&gt;, 1000000);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  db.transaction(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(transaction){&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      transaction.executeSql(&lt;span style="color: #006080"&gt;'CREATE TABLE IF NOT EXISTS settings (k TEXT NOT NULL PRIMARY KEY ON CONFLICT REPLACE, v TEXT NOT NULL );'&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  );&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;A couple of important points about the code above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CREATE TABLE IF NOT EXISTS is very useful and part of the zero administration nature of SQLite. This statement means I can create and open a database in a single statement. And the transaction succeeds even if the database already exists. &lt;/li&gt;

  &lt;li&gt;CONFLICT REPLACE is also very useful. It allows me to do an insert even if the record exists. Also knows as an UPSERT or MERGE operation, this save time and code later on. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because I’m making JSON service calls, the call results are both efficient and ready to store in my database:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$.ajax(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  type: &lt;span style="color: #006080"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, url: &lt;span style="color: #006080"&gt;&amp;quot;/iretailpassportsvc/clientservices.svc/passport&amp;quot;&lt;/span&gt;,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  dataType: &lt;span style="color: #006080"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  cache: &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  timeout: 1000,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  success: &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(passport)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    db.transaction(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(transaction){&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        transaction.executeSql(&lt;span style="color: #006080"&gt;&amp;quot;INSERT INTO settings (k,v) VALUES ('passport',?);&amp;quot;&lt;/span&gt;, [passport], &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    );&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  },&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  error: &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    setError(serviceUnavailableString);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;});&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Notice the jQuery AJAX call to the server is marked as JSON. It is also important to set a timeout to ensure this function returns if the server is unavailable.&lt;/p&gt;

&lt;p&gt;The AJAX method has both ‘success:’ and ‘error:’ callbacks. In the error callback, I’m handling the error. A likely implementation here is to start some sort of dialogue with the user when the event is fired and to cancel that dialogue on error. A dialogue could be information on a status or progress bar for example.&lt;/p&gt;

&lt;p&gt;The success callback does the work of posting the JSON response to the database. Because I used ‘CONFLICT REPLACE’ in the database definition, the ‘INSERT’ will be a merge insert.&lt;/p&gt;

&lt;p&gt;The success case would also update the user interface with fresh data but I have omitted this from the example to keep things simple. &lt;/p&gt;

&lt;h5&gt;Storing Images&lt;/h5&gt;

&lt;p&gt;In my application I was making a call to the REST service to get an image of a barcode that represents the users’ unique identifier. My original design was based on making this call as and when required and for the service to generate the image at run time and stream the image to the client. The strategy to generate at run time meant I could keep storage requirements low at the server and meant I didn’t have to worry about any referential integrity between the user identifier and the image. I wanted to keep this strategy for the offline application and reuse the service interface.&lt;/p&gt;

&lt;p&gt;This meant storing an image streamed by the service. Well again, HTML5 comes to the rescue with the &lt;em&gt;&amp;lt;canvas&amp;gt;&lt;/em&gt; element. &lt;/p&gt;

&lt;p&gt;A canvas is a bit oriented drawing area – a bitmap. &lt;/p&gt;

&lt;p&gt;The DOM supports a number of drawing operations to the canvas and also provides methods for serializing the canvas:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; src = &lt;span style="color: #006080"&gt;&amp;quot;/iretailpassportsvc/clientservices.svc/barcode/&amp;quot;&lt;/span&gt; + passport;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; canvasImg = document.createElement(&lt;span style="color: #006080"&gt;&amp;quot;canvas&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; serializedVal = &lt;span style="color: #006080"&gt;'data:,'&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt; img = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Image();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;img.onload = &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;() &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  canvasImg.width = img.width;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  canvasImg.height = img.height;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  canvasImg.getContext(&lt;span style="color: #006080"&gt;&amp;quot;2d&amp;quot;&lt;/span&gt;).drawImage(img, 0, 0);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  serializedVal = canvasImg.toDataURL();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  $(&lt;span style="color: #006080"&gt;&amp;quot;#barcode&amp;quot;&lt;/span&gt;).replaceWith(img);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  db.transaction(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;function&lt;/span&gt;(transaction){&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      transaction.executeSql(&lt;span style="color: #006080"&gt;&amp;quot;INSERT INTO settings (k,v) VALUES ('barcode',?);&amp;quot;&lt;/span&gt;, [serializedVal], &lt;span style="color: #0000ff"&gt;function&lt;/span&gt; (transaction, results){setTimeout(&lt;span style="color: #006080"&gt;&amp;quot;jQT.goBack()&amp;quot;&lt;/span&gt;, 1000)});&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  );&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;};&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;img.src = src;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This is very cool. In the code above I am creating &lt;em&gt;&amp;lt;canvas&amp;gt;&lt;/em&gt; and &lt;em&gt;&amp;lt;img&amp;gt;&lt;/em&gt; elements. I’m then loading the image using a reference to the image service endpoint with &lt;em&gt;img.src = src&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;During the image &lt;em&gt;onload()&lt;/em&gt; event I set up the canvas dimensions to match that of the image (remember, the canvas is really a bitmap) and then draw the image to the canvas with the &lt;em&gt;drawImage()&lt;/em&gt; statement. &lt;/p&gt;

&lt;p&gt;The result of this is the canvas is now a bitmap representation of the image loaded from the server. &lt;/p&gt;

&lt;p&gt;Finally, I serialize the image to a data URL. A data URL is a BASE64 representation of a resource and can be used anywhere a resource is used. So in this case it will be something like:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 29px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;data:image/png;base64,……&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;So now I have a serialized string representation of the image generated at run time by my service. All I need to do now is save it to the database.&lt;/p&gt;

&lt;p&gt;Later on, I can load this string&amp;#160; and use a simple statement like:&lt;/p&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 32px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;img.src = data&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;to display the image.&lt;/p&gt;

&lt;h5&gt;Asynchronous Everywhere&lt;/h5&gt;

&lt;p&gt;Remember that calls to the database and AJAX are asynchronous. Methods around these calls provide callbacks and you need to put your code inside the callback or you will see some very odd behaviour.&lt;/p&gt;

&lt;p&gt;I got caught out when handling the image serialization. The call to load the image is delayed because the source is a network resource. So the &lt;em&gt;img.onload()&lt;/em&gt; may take time to complete. My mistake was to write the serialized canvas data to the database in code following my &lt;em&gt;img.src = URL&lt;/em&gt;. This had very strange results and was saving incomplete data (thank goodness for Web Inspector or I may still be sitting scratching my head).&lt;/p&gt;

&lt;p&gt;The fix was to put all the serialization to database inside the &lt;em&gt;img.onload()&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;Offline/Online Data Strategy&lt;/h4&gt;

&lt;p&gt;Now we know how to white list service calls, where to store the call results and how to store these results. One final point is around the offline/online data strategy. &lt;/p&gt;

&lt;p&gt;A key benefit of a RESTful architecture is to provide fluid network applications without the need for holding vast datasets client side. So, if you need data or need to transition state, you call a service method. An offline model goes against this and necessitates storing data working with aged data. &lt;/p&gt;

&lt;p&gt;But offline working cannot be ignored on mobile devices – just try using the internet on the train or underground. &lt;/p&gt;

&lt;p&gt;The trick is to strike a balance between offline data fetches and online service fetches.&lt;/p&gt;

&lt;p&gt;One strategy for offline/online data is to always make a service call and if the call fails or there is a timeout, read from the database. This helps keep the age of the data low but will result in a choppy user interface with a lot of ‘Please Wait’ timers.&lt;/p&gt;

&lt;p&gt;A better strategy is to do things the other way round. That is, always read from the database. At the same time, issue a service call. If the call comes back with data, update the cache and user interface. To help with usability, you can always display a ‘Refreshing…’ message to the user is a status area.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;As you can see from the this blog, the theory of offline RESTful applications is not a big challenge. The implementation raises a number of pitfalls which I hope I have been able to highlight.&lt;/p&gt;

&lt;p&gt;But when it works, believe me…It’s all worth it.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-3140933361956488810?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/-uBY_Lo6mlM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/3140933361956488810/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/12/iphone-offline-web-apps-restful-way.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3140933361956488810?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3140933361956488810?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/-uBY_Lo6mlM/iphone-offline-web-apps-restful-way.html" title="iPhone Offline Web Apps – the RESTful Way – Part 2 of 2" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_dRqAiVqJ-R8/SxWiLtwg9fI/AAAAAAAAADI/7fDNbXMknzI/s72-c/html5REST_thumb%5B2%5D.gif?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/12/iphone-offline-web-apps-restful-way.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08CR386eip7ImA9WxNaF0Q.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-3311237014664411036</id><published>2009-12-01T12:06:00.005Z</published><updated>2009-12-03T00:24:26.112Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-03T00:24:26.112Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Safari 4" /><category scheme="http://www.blogger.com/atom/ns#" term="PHP" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="Offline" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="HTML5" /><category scheme="http://www.blogger.com/atom/ns#" term=".manifest" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="ASPX" /><category scheme="http://www.blogger.com/atom/ns#" term="jQuery" /><title>iPhone Offline Web Apps – the RESTful Way – Part 1 of 2</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UTpUDnsiX5IVhNZK8flTHKd5ZRk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UTpUDnsiX5IVhNZK8flTHKd5ZRk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UTpUDnsiX5IVhNZK8flTHKd5ZRk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UTpUDnsiX5IVhNZK8flTHKd5ZRk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a href="http://lh4.ggpht.com/_dRqAiVqJ-R8/SxR2JqRlLII/AAAAAAAAAC8/QVALeIkvgUo/s1600-h/html59.gif"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="html5" border="0" alt="html5" align="left" src="http://lh3.ggpht.com/_dRqAiVqJ-R8/SxR2J3TTXpI/AAAAAAAAADA/SCDTBkCnGWY/html5_thumb7.gif?imgmax=800" width="125" height="240" /&gt;&lt;/a&gt; [&lt;strong&gt;Concrete/Very Interesting&lt;/strong&gt;] Whist building a web application for the iPhone, I was presented with a problem. One of my team managed to replicate the work we've been doing using Objective-C. I looked at the code – didn't like it but it worked. The problem was, &lt;em&gt;it worked offline&lt;/em&gt;.   &lt;br /&gt;‘Can't have this’ I thought, so I embarked on making my application offline capable.   &lt;br /&gt;Sounds easy – well in principle, it is. In practice, there were a few pitfalls that I thought I'd better blog about. Of course, I had a cold and had just received an H1N1 vaccination, so maybe on a good day…   &lt;br /&gt;Anyway, here is some background and my tips to Offline Web Apps, the RESTful way.   &lt;br /&gt;  &lt;h4&gt;HTML5 Offline Application Cache&lt;/h4&gt; You can find out lots of good stuff about this on the Internet. The official reference from Apple can be found &lt;a href="http://developer.apple.com/safari/library/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/OfflineApplicationCache/OfflineApplicationCache.html"&gt;here&lt;/a&gt;. The key is to create an cache manifest file naming all your resources - HTML files, Scripts, Style Sheets, images, and so on… You then reference the manifest file (.manifest) in your HTML page(s) in the HTML tag:   &lt;br /&gt;  &lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 31px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;   &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;html&lt;/span&gt; &lt;span style="color: red"&gt;xmlns&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt; &lt;span style="color: red"&gt;manifest&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;app.manifest&amp;quot;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
When the application runs, it follows (roughly) the logic outlined below: 

&lt;br /&gt;

&lt;br /&gt;

&lt;ul&gt;
  &lt;li&gt;Manifest Named – load the server manifest &lt;/li&gt;

  &lt;li&gt;If no response from server, load manifest from cache and all resources named in the manifest from cache &lt;/li&gt;

  &lt;li&gt;If server manifest found, compare to cached manifest – byte by byte comparison &lt;/li&gt;

  &lt;li&gt;If server manifest is different to cached manifest, load all the resources named in the manifest from the server &lt;/li&gt;
&lt;/ul&gt;
Using a manifest and application cache is easy enough but there are some gotchas, so here are some tips to help you along: 

&lt;br /&gt;

&lt;br /&gt;

&lt;h5&gt;Tip1: Test in Safari 4 on your desktop&lt;/h5&gt;
Safari is a really good emulator for the iPhone and is a excellent place to start. It is far easier to test your code, clear the cache, manage resources, and so on, while working on your laptop than it is on the iPhone. And you get Developer Tools which is a real bonus. Web Inspector is a superb debugging tool will give you everything you need to debug your app, resources and even the database. 

&lt;br /&gt;

&lt;br /&gt;

&lt;h5&gt;Tip2: Add the manifest at the start of development&lt;/h5&gt;
If you start with offline, you can quickly ensure you have got the basic structure of the manifest and resources correct. My tip is to implement the offline cache when there are only static resources to worry about, before we complicate matters with service calls. It’s much easier to validate. 

&lt;br /&gt;

&lt;br /&gt;

&lt;h5&gt;Tip3: Version your app and manifest.&lt;/h5&gt;
When working offline, your cache is &lt;em&gt;&lt;strong&gt;only&lt;/strong&gt;&lt;/em&gt; refreshed if the application fetches a newer manifest file from the server. I will say this again – changes to your application won’t be seen, that is, you will read from the existing cache up until the manifest changes. So to keep everything in line, it is a good idea to put a version string into your application, something you can see from the app or a debug menu, and a copy of that version in your manifest file. Every time you make a code change, change the version in the app and in the manifest. 

&lt;br /&gt;

&lt;br /&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 218px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;br /&gt;

  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; height: 167px; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;CACHE MANIFEST&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;# Version 0.9.4.1129.004&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;NETWORK:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;/iretailpassportsvc/clientservices.svc&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;CACHE:&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;index.htm&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;images/arrow.png&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;images/background.png&lt;/pre&gt;
  &lt;/div&gt;

  &lt;br /&gt;&lt;/div&gt;
By doing this you ensure the manifest changes along with the application and because the manifest changes, your cache will update with your changes. 

&lt;br /&gt;

&lt;br /&gt;If you are generating your manifest server side in PHP, ASPX or similar, you should be able to automate this, creating a version with a unique number or date/time stamp (see Tip 5:). 

&lt;br /&gt;

&lt;br /&gt;One other thing. When you deploy and run your app after a change, the change is loaded to your cache for future executions. But the first time running after deployment, &lt;strong&gt;&lt;em&gt;you won’t&lt;/em&gt;&lt;/strong&gt; see a change in your app. Seems odd but it is just like releasing any program update – the update is sent down to your machine, but you only see the update the next time you run. So when you are testing, remember to: 

&lt;br /&gt;

&lt;br /&gt;

&lt;ul&gt;
  &lt;li&gt;Deploy &lt;/li&gt;

  &lt;li&gt;Run to refresh the cache &lt;/li&gt;

  &lt;li&gt;Re-run to see the change (check the app version). &lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;

&lt;h5&gt;&lt;strong&gt;Tip 4: Make sure the manifest file is served with type &amp;quot;text/cache-manifest&amp;quot; &lt;/strong&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/h5&gt;
You have to ensure your web server recognises the manifest file type and associates this with the required MIME type. 

&lt;br /&gt;

&lt;br /&gt;IIS 7 is especially irritating. It uses .manifest for ‘Click Once’ (now back off, don’t start me on that subject). So it recognizes this extension but associates it with the MIME type &amp;quot;application/x-ms-manifest&amp;quot;. This &lt;em&gt;&lt;strong&gt;won't work&lt;/strong&gt;&lt;/em&gt;. You can change this at the server level or application/file level in IIS – either way, change the MIME type to &amp;quot;text/cache-manifest&amp;quot;. 

&lt;br /&gt;

&lt;br /&gt;I found a very cool site to test the HTTP Response Headers – if you request the manifest file, you can check your web server is serving with the correct MIME type. The site is called &lt;a href="http://web-sniffer.net/"&gt;http://web-sniffer.net/&lt;/a&gt; . You can also use tools like &lt;a href="http://www.fiddler2.com/fiddler2/"&gt;Fiddler2&lt;/a&gt;. 

&lt;br /&gt;

&lt;br /&gt;

&lt;h5&gt;Tip5: Make sure the manifest file contents are 100% correct&lt;/h5&gt;
And I do mean 100% correct. You need to name &lt;em&gt;&lt;strong&gt;ALL&lt;/strong&gt;&lt;/em&gt; the resources used to serve up your app. If anything is missing, the app will not load from the cache. Worse still, if it was working and you subsequently make a mistake in the manifest file, it will load from the old cache, and you will not see your changes. You could be puzzled for hours trying to work out why you can't see your changes! 

&lt;br /&gt;

&lt;br /&gt;The simplest approach to ensure you have a correct manifest file is to list all the files in your project, including files in sub-directories. You need to specify the files and their paths relative to the manifest file and the manifest file should be placed in the application root. 

&lt;br /&gt;

&lt;br /&gt;As an alternative strategy, you can dynamically generate the manifest together using server side code (such as PHP or ASPX). 

&lt;br /&gt;

&lt;br /&gt;For example in PHP: 

&lt;br /&gt;

&lt;br /&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 292px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;lt;?php&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$version &lt;span style="color: #006080"&gt;&amp;quot;3.1.0000&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;header(&lt;span style="color: #006080"&gt;'Content-Type: text/cache-manifest'&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;echo&lt;/span&gt; &lt;span style="color: #006080"&gt;&amp;quot;CACHE MANIFEST\n&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;echo&lt;/span&gt; &lt;span style="color: #006080"&gt;&amp;quot;#Version: &amp;quot;&lt;/span&gt;.$version;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$dir = &lt;span style="color: blue"&gt;new&lt;/span&gt; RecursiveDirectoryIterator(&lt;span style="color: #006080"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;foreach&lt;/span&gt;(&lt;span style="color: blue"&gt;new&lt;/span&gt; RecursiveIteratorIterator($dir) &lt;span style="color: blue"&gt;as&lt;/span&gt; $file) &lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;if&lt;/span&gt; ($file-&amp;gt;IsFile() &amp;amp;&amp;amp;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;$file != &lt;span style="color: #006080"&gt;&amp;quot;./manifest.php&amp;quot;&lt;/span&gt; &amp;amp;&amp;amp;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;substr($file-&amp;gt;getFilename(), 0, 1) != &lt;span style="color: #006080"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;echo&lt;/span&gt; $file . &lt;span style="color: #006080"&gt;&amp;quot;\n&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;?&amp;gt;&lt;/pre&gt;
  &lt;/div&gt;

  &lt;br /&gt;&lt;/div&gt;

&lt;br /&gt;Save the file in your app root as manifest.php, then in your HTML file(s): 

&lt;br /&gt;

&lt;br /&gt;

&lt;div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.04%; padding-right: 4px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; height: 65px; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper"&gt;
  &lt;br /&gt;

  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100.82%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; height: 16px; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;html&lt;/span&gt; &lt;span style="color: red"&gt;xmlns&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt; &lt;span style="color: red"&gt;manifest&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;manifest.php&amp;quot;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;

  &lt;br /&gt;&lt;/div&gt;

&lt;h5&gt;Tip 6: Testing in ‘Airplane Mode’&lt;/h5&gt;
You should be able to test your app at this stage. On the iPhone, you can place the phone into “Airplane Mode”. If your offline cache is working, you should be able to load and run you app. 

&lt;br /&gt;

&lt;br /&gt;If it is not, you will get an alert informing you you are not connected to the Internet. 

&lt;br /&gt;

&lt;br /&gt;

&lt;h4&gt;A bit tricky but…&lt;/h4&gt;
Ok, that was a bit tricky – especially if you have “Man Flu”, and part of the problem was I wasn’t convinced this “offline mumbo jumbo stuff’ really worked. But it does. And when it does it opens up a whole world of opportunities for writing applications for the iPhone. 

&lt;br /&gt;

&lt;br /&gt;But that’s not the whole story – I still hadn’t managed to build a offline RESTful app. 

&lt;br /&gt;

&lt;br /&gt;What about your service calls? 

&lt;br /&gt;

&lt;br /&gt;&lt;strong&gt;[Part 2 Discusses a strategy for RESTful caching]. &lt;/strong&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-3311237014664411036?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/TtDwbr75CsU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/3311237014664411036/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/11/iphone-offline-web-apps-restful-way.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3311237014664411036?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3311237014664411036?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/TtDwbr75CsU/iphone-offline-web-apps-restful-way.html" title="iPhone Offline Web Apps – the RESTful Way – Part 1 of 2" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_dRqAiVqJ-R8/SxR2J3TTXpI/AAAAAAAAADA/SCDTBkCnGWY/s72-c/html5_thumb7.gif?imgmax=800" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/11/iphone-offline-web-apps-restful-way.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8GRX0yeyp7ImA9WxNaFk8.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-3827774235634280047</id><published>2009-11-22T22:20:00.002Z</published><updated>2009-12-01T00:37:04.393Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-01T00:37:04.393Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="AJAX" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="jQTouch" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="WebKit" /><category scheme="http://www.blogger.com/atom/ns#" term="XML" /><category scheme="http://www.blogger.com/atom/ns#" term="LINQ" /><category scheme="http://www.blogger.com/atom/ns#" term="jQuery" /><category scheme="http://www.blogger.com/atom/ns#" term="WCF" /><title>RESTful Teamwork – Tips for Agile iPhone Development</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hkg_LVUA7t1drj0s-t2SNGNDuos/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hkg_LVUA7t1drj0s-t2SNGNDuos/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hkg_LVUA7t1drj0s-t2SNGNDuos/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hkg_LVUA7t1drj0s-t2SNGNDuos/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;a style="margin-bottom: 1em; float: left; clear: left; margin-right: 1em; cssfloat: left" href="http://1.bp.blogspot.com/_dRqAiVqJ-R8/Swm4SWKo1oI/AAAAAAAAABQ/G-aV_q9_Jig/s1600/iphonesleep.jpg" imageanchor="1"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/Swm4SWKo1oI/AAAAAAAAABQ/G-aV_q9_Jig/s320/iphonesleep.jpg" yr="true" /&gt;&lt;/a&gt;&lt;strong&gt;[Concrete/Interesting]&lt;/strong&gt; I’ve been working with a small team on an iPhone project. I bet half of you out there are doing something similar. One member of the team is struggling with Objective-C – I need to understand the costs and benefits of native development on the iPhone. The rest of us are working on a WebKit browser app for the iPhone . Naturally, I decided we must use REST to provide a good balance of speed and functionality on the device. I’m using the &lt;a href="http://www.jqtouch.com/" target="_blank"&gt;jQTouch framework&lt;/a&gt; (based around &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery 1.3.2&lt;/a&gt;)with a bit of AJAX on the device framework and the server is developed in C#. The REST service is implemented under WCF, so building server side is a dream (see &lt;a href="http://msdn.microsoft.com/en-us/netframework/cc950529.aspx" target="_blank"&gt;REST in Windows Communication Foundation&lt;/a&gt;).   &lt;br /&gt;  &lt;br /&gt;Team working is a breeze – we have a basic data model and use this for reference. However, under my direction we are building up the solution screen by screen, focusing on the end user application and looking server side when we need data or processor. The team works very well because all we need to do is agree on the REST interface – the server implementation and implementation of the data model comes second. And to test, just dummy up an interface – it proves the workflow and allows the device team to focus on usability and the server team to focus on the service implementation.   &lt;br /&gt;  &lt;br /&gt;I chose this approach for speed – I want to engage our client as we explore the iPhone and to do this, we needed to be agile, developing our prototypes quickly. Web developments are quick, especially if you chose the right framework. REST allows us to focus on the client with our client while perfecting the implementation in a background mode.   &lt;br /&gt;  &lt;br /&gt;So here are my tips for agile iPhone Development:   &lt;br /&gt;  &lt;br /&gt;  &lt;ul&gt;   &lt;li&gt;Develop the client as a web application, it looks good with WebKit extensions and can always be re-implemented as a native application as and when you firm up the implementation. No messing with Apple and Mac – you can even test using Safari under windows. &lt;/li&gt;    &lt;li&gt;Use Web Services and a bit of AJAX– this helps you prototype because interfaces can easily be dummied up, then perfected in the background. And if your services are doing the real work for you, the client takes on a more disposable nature. If you want to rewrite native later on, you don’t have a lot of core implementation to throw away &lt;/li&gt;    &lt;li&gt;REST under WCF allows for fast interface development. Server side in .NET opens up technologies like LINQ and magically, your server side code is a lot more fluent. &lt;/li&gt;    &lt;li&gt;Don’t worry too much about the data model – focus on getting the interface correct. This will start to dictate the model requirements – if you need the data in the client, the XML on the interface provides a schema that will be close to your required data model. It all just unfolds before your eyes. &lt;/li&gt;    &lt;li&gt;Have someone working on a native client in the background – this helps build skills but buffers you from having to experience any pain in front of your client. &lt;/li&gt; &lt;/ul&gt; Finally, it’s RESTful, but is it Agile? Don’t know but my client is happy.     &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-3827774235634280047?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/u_xu9qTd5wc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/3827774235634280047/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/11/restful-teamwork-tips-for-agile-iphone.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3827774235634280047?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/3827774235634280047?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/u_xu9qTd5wc/restful-teamwork-tips-for-agile-iphone.html" title="RESTful Teamwork – Tips for Agile iPhone Development" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_dRqAiVqJ-R8/Swm4SWKo1oI/AAAAAAAAABQ/G-aV_q9_Jig/s72-c/iphonesleep.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/11/restful-teamwork-tips-for-agile-iphone.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQDQn8yfip7ImA9WxNbGU4.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-7408640333414164404</id><published>2009-10-24T21:22:00.008+01:00</published><updated>2009-11-22T22:52:53.196Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-22T22:52:53.196Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ERP" /><category scheme="http://www.blogger.com/atom/ns#" term="RDBMS" /><category scheme="http://www.blogger.com/atom/ns#" term="Cloud Computing" /><title>The Squirrel on Cloud Computing</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Z8xKi9EsQXHxuVRbHNtfxy2YynE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Z8xKi9EsQXHxuVRbHNtfxy2YynE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Z8xKi9EsQXHxuVRbHNtfxy2YynE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Z8xKi9EsQXHxuVRbHNtfxy2YynE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;strong&gt;[Concrete/Important]&lt;/strong&gt; The Squirrel is pulling his tail fur out – I’m surprised he has any fur left. He’s been trying to convince the Bears that Cloud Computing is important and in paw’s reach but the little Acorn ERP is just too big to get up into the clouds.&lt;br /&gt;
&lt;br /&gt;
The first push back came from Little Bear, and the Squirrel was expecting this. “Cloud Computing is not really aimed at us” said Little Bear. &lt;br /&gt;
&lt;br /&gt;
Little Bear is scared, probably has too much on his plate. And he’s been nurturing the Acorn and building this huge Oak Tree. In a way, he’s right, though Squirrel. The Cloud is not for our type of ERP, not today. We need to be lean and we need to change if we’re going to live in the Cloud. And it’s the same for On Premise and Hosting – storage may be cheap, but the more structured the requirements, the higher cost per GB. And it’s costing us an arm and a leg to host our Oak Tree. “No way will we be able to manage a forest of Acorn ERPs, not unless we change...” replied the Squirrel. &lt;br /&gt;
&lt;br /&gt;
“And we are not just chucking images onto a file system,” said Squirrel, “We need a relational store and that costs money. We need to think how we can move down to Structured Storage, where storage is cheaper, in the Cloud or otherwise”. &lt;br /&gt;
&lt;br /&gt;
Now Squirrel has always and still believes that sense will prevail. Just then, Mummy Bear entered the debate. “I’ve talked to a number of bears and we think the Cloud is at least 2 year away” she said. “What the f***?” thought the Squirrel. “Possibly true,” he replied, “but not sure what that has to do with it.” Squirrel now wondered where this was going – it certainly didn’t look like the bears were convinced the Cloud was something they should take seriously. “The Cloud may be a way away, but we still need to get lean with our data, regardless of the Cloud. The point is, if we do, we will be Cloud ready and that has to be good”. Where the hell is Daddy Bear, thought the Squirrel?&lt;br /&gt;
&lt;br /&gt;
Cloud Computing is not a fad, it is a step in the evolution of Software and the commerce of Information Systems. The move towards Software as a Service is driven by the commerce of lowering the total cost of ownership. This means I’m willing to “Pay per Click” because I don’t have to own the software, hardware, operations and support. But software companies will die if they just swap the billing model from “license and support” to “service utilization” while taking on the infrastructure costs. This is especially true of hosting but also for the Cloud. It’s not the license cost, it the cost of operations. And the key to this is data. A huge data set in a relational store requires not just disk space, but CPU and memory. And this means support services around the RDBMSs. Add in resilience and high availability and the costs will soar. By dropping down to unstructured or weakly structures storage, costs can be removed without sacrificing storage capacity, resilience or availability. And your scalability improves.&lt;br /&gt;
&lt;br /&gt;
The thing about the Cloud is it forces you towards this discipline. The Squirrel knows this and at the risk of losing all his fur, he needs to ensure the Bears understand this too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-7408640333414164404?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/1eLus4Z7nxg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/7408640333414164404/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/10/squirrel-on-cloud-computing.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/7408640333414164404?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/7408640333414164404?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/1eLus4Z7nxg/squirrel-on-cloud-computing.html" title="The Squirrel on Cloud Computing" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><thr:total>1</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/10/squirrel-on-cloud-computing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIAQHkzfSp7ImA9WxNbGU4.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-8409775926776270566</id><published>2009-10-22T17:59:00.002+01:00</published><updated>2009-11-22T22:55:41.785Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-22T22:55:41.785Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="WWW.FAR2MUCHMAIL.COM" /><category scheme="http://www.blogger.com/atom/ns#" term="HTTP" /><category scheme="http://www.blogger.com/atom/ns#" term="REST" /><category scheme="http://www.blogger.com/atom/ns#" term="XML-RPC" /><title>Is your code RESTful</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hmCxyp65O6hNWOMXVYZq16fEo08/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hmCxyp65O6hNWOMXVYZq16fEo08/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hmCxyp65O6hNWOMXVYZq16fEo08/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hmCxyp65O6hNWOMXVYZq16fEo08/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;[&lt;strong&gt;Concrete/Important&lt;/strong&gt;]&amp;nbsp;Well nearly, but not 100% - well not yet. &lt;br /&gt;
&lt;br /&gt;
A few years ago I wrote an HTTP GET/POST web service (sounds RESTful) between a Windows CE.Net client on a hand held device and an Apache server running a light SOAP Web Service. It wasn’t hard to write but the messages were specialized, so fell short of being a true RESTful implementation.&lt;br /&gt;
&lt;br /&gt;
REST stands for REpresentational State Transfer and refers to an architectural style or pattern. In essence it describes clients served by services where state transitions or transfers in the client take place through the representation of a new state in the data returned by the service. &lt;br /&gt;
&lt;br /&gt;
Web applications architected in this way have stateless servers and would typically map messages or web service requests onto the HTTP verbs GET/PUT/POST/DELETE.&lt;br /&gt;
&lt;br /&gt;
At its core, my latest project &lt;a href="http://www.far2muchmail.com/"&gt;http://www.far2muchmail.com/&lt;/a&gt; , uses messaging between an Outlook desktop client and a web service. The web service is in PHP and not currently highly scalable. Over the last couple of years I’ve been using XML-RPC and make extensive use of this for messaging between my Outlook plug-in and the web service. However, I’m thinking about making a more RESTful approach.&lt;br /&gt;
&lt;br /&gt;
But XML-RPC is cool. It is well supported by the guys at &lt;a href="http://www.xml-rpc.com/"&gt;http://www.xml-rpc.com/&lt;/a&gt; and others around the world. It provides remote execution interoperable between .Net and PHP and many others like Perl, Python, Ruby, Objective C... I really love it. &lt;br /&gt;
&lt;br /&gt;
Client side under .Net, its asynchronous so you need to think about a RESTfully and this is typically how I use it.&lt;br /&gt;
&lt;br /&gt;
So what’s wrong with XML-RPC, it certainly works for &lt;a href="http://www.far2muchmail.com/"&gt;http://www.far2muchmail.com/&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
Well it does now. However, XML-RPC has a few niggles. First it’s bound to HTTP, not really a problem but it might be if I want to move my services inside the cloud. &lt;br /&gt;
&lt;br /&gt;
XM-RPC is also a bit verbose – the XML message that is. And it gets worse with binary data. This also leads to weak implementations and misinterpretation of the protocol. So if you want to talk to a public service, you may have to code for that particular implementation.&lt;br /&gt;
&lt;br /&gt;
So, if you want to move to a more scalable service architecture, XML-RPC may not be your first choice.&lt;br /&gt;
&lt;br /&gt;
REST based on HTTP verbs is now well established, very lightweight and simple to interpret. With the big players like Microsoft, Google and Amazon adopting the RESTful approach and providing a REST interface to anything that moves, it may be time to start thinking RESTfully. &lt;br /&gt;
&lt;br /&gt;
And as REST builds momentum we are going to see a lot more services out there that speak to REST clients, including services hosted in the cloud (Cloud Services). This should mean that in the near future you should be able to benefit from high scale platforms running scalable services, on premise and in the cloud, ranging from business application services through to storage and bus and communication services... all speaking RESTfully.&lt;br /&gt;
&lt;br /&gt;
So for me at least, I think it is worth taking a more RESTful approach.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-8409775926776270566?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/eDih4U7LBIs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/8409775926776270566/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/10/is-your-code-restful.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/8409775926776270566?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/8409775926776270566?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/eDih4U7LBIs/is-your-code-restful.html" title="Is your code RESTful" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/10/is-your-code-restful.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcHRH48fyp7ImA9WxNaF0w.&quot;"><id>tag:blogger.com,1999:blog-4825554590190698904.post-678224373603256953</id><published>2009-10-22T00:03:00.002+01:00</published><updated>2009-12-02T01:40:35.077Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-02T01:40:35.077Z</app:edited><title>"Better to say nothing and be thought a fool..."</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/9nNP0Ldf37MHxG0CZV7Dq7dV9a0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9nNP0Ldf37MHxG0CZV7Dq7dV9a0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/9nNP0Ldf37MHxG0CZV7Dq7dV9a0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9nNP0Ldf37MHxG0CZV7Dq7dV9a0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;[&lt;strong&gt;Abstract/Important&lt;/strong&gt;] "Better to remain silent and be thought a fool than to speak out and remove all doubt." - I read this on one of my colleague’s Skype. Made me wonder ‘why do people say this sort of thing?’ and I was reminded, this is something my Dad used to say to me. &lt;br /&gt;
&lt;br /&gt;
I guess some people say this because they believe it to be true. Seems reasonable, maybe that’s why Dad used the phrase. Sometimes people use the phrase because they have nothing better to say – now that’s perverse. &lt;br /&gt;
&lt;br /&gt;
Although not every topic attracts or even deserves debate, I believe that if a debate is offered, one almost has a duty to present one's views. More so, at the risk of saying something foolish, in any debate of weight, whether or not your view is solicited, you must speak up. To do otherwise is indeed foolish.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4825554590190698904-678224373603256953?l=not-now-nigel.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/NotNowNigel/~4/zxSDKnsVZkk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://not-now-nigel.blogspot.com/feeds/678224373603256953/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://not-now-nigel.blogspot.com/2009/10/better-to-say-nothing-and-be-thought.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/678224373603256953?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4825554590190698904/posts/default/678224373603256953?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/NotNowNigel/~3/zxSDKnsVZkk/better-to-say-nothing-and-be-thought.html" title="&quot;Better to say nothing and be thought a fool...&quot;" /><author><name>Not Now Nigel</name><uri>http://www.blogger.com/profile/15476793526038989403</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://1.bp.blogspot.com/_dRqAiVqJ-R8/St9gb524nrI/AAAAAAAAAAM/kihUzEX4N9Q/S220/2007-07-17+Aiesha+031.JPG" /></author><thr:total>1</thr:total><feedburner:origLink>http://not-now-nigel.blogspot.com/2009/10/better-to-say-nothing-and-be-thought.html</feedburner:origLink></entry></feed>

