<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="2.0">  <channel>
    <title>Cyan Canyon</title>
    <link>http://www.cyancanyon.com</link>
    <description>Cyan Canyon News, Updates, Bias</description>
    <language>en-us</language>
    <lastBuildDate>Tue, 21 Jun 2011 00:00:00</lastBuildDate>
	  <item>
      <title>A Simple AJAX Engine</title>
      <link>http://www.cyancanyon.com/news/?id=20</link>
      <description><![CDATA[<p>This is my offering to the world relating to asynchronous web programming, or generically, AJAX. I copy this code to nearly every web project that I write anymore, and I'm always glad I have it. I've become accustomed to using JSON from my web services as it is very easy to read and write, so this code has primary support for that. Feel free to use it in any way you see fit. Please credit me if you do, I enjoy having my name everywhere. I am not opposed to receiving large sums of money either.</p><p>sendAjax is the return value of a self-invoking function, so everything is initialized when the code is loaded (you don't have to remember to do it in some 'main' function). The benefit of this is that sendAjax becomes a callable function with all the supporting information cached. And you call it like so...</p><code>sendAjax("your_server_script.php", function(result) {<br>&nbsp;&nbsp;&nbsp;&nbsp;if (result) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//do whatever with the result...</span><br>&nbsp;&nbsp;&nbsp;&nbsp;} <span class="comment">//otherwise an error occurred.</span><br>}, [<span class="value">"id"</span>, <span class="value">"data"</span>], [idvalue, datavalue]);</code><p>Explanation is in the comments as well as information on configuration and options. Enjoy!</p><code>var sendAjax = function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//DEFAULTS...</span><br>&nbsp;&nbsp;&nbsp;&nbsp;var defPath = <span class="value">""</span>; <span class="comment">//the 'command' parameter will be appended to defPath to form the complete URL. Override defPath by specifying the path in the 'command' parameter. If defPath is blank, the full URL must be specified in the 'command' parameter. Example: var defPath = "http://www.yoursite.com/services/";</span><br>&nbsp;&nbsp;&nbsp;&nbsp;var defMethod = <span class="value">"GET"</span>; <span class="comment">//defMethod should be "GET" or "POST", but may be overridden.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;var defAsync = true; <span class="comment">//defAsync should be true or false, but may be overridden.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;var defErrorHandler = function(txt) { throw new Error(txt); }; <span class="comment">//defErrorHandler should be a function taking the text of the error as a parameter. Assign null to defErrorHandler to send to the receiver no matter what.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;var defExpectJSON = true; <span class="comment">//defExpectJSON should be true or false. If false, the result is treated as plain text.</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//Get the AJAX object that transmits and receives the data. Modern browsers all use XMLHttpRequest these days, but we can still allow the hold-outs to play too.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;function getAjaxObj() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var ajaxobj = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj = new XMLHttpRequest(); <span class="comment">//accepted standard</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj = new ActiveXObject(<span class="value">"Msxml2.XMLHTTP"</span>); <span class="comment">//old ie</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj = new ActiveXObject(<span class="value">"Microsoft.XMLHTTP"</span>); <span class="comment">//really old ie</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return ajaxobj;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//If 'resulttext' is JSON, it is trimmed, cleaned up and returned ready to be exec'ed into an object. Otherwise, the plain text is just sent back.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;function extractJSON(resulttext) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var startpos = resulttext.indexOf(<span class="value">"{"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var endpos = resulttext.lastIndexOf(<span class="value">"}"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (startpos &gt;= <span class="value">0</span> && endpos &gt;= <span class="value">0</span>) var datavalue = resulttext.substring(startpos, endpos + <span class="value">1</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else datavalue = resulttext;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;datavalue = datavalue.replace(<span class="value">/\&amp;/g</span>, <span class="value">"&"</span>); <span class="comment">//in case it was wrapped in xml, as is often the case with .NET web services</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return datavalue;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//This is the function that is actually called when you use sendAjax().<br>&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;//REQUIRED PARAMETERS...<br>&nbsp;&nbsp;&nbsp;&nbsp;//command {string}: the path to the page or web service that you are calling. This will be appended to defPath UNLESS it contains a path itself, in which case it will be used as-is.<br>&nbsp;&nbsp;&nbsp;&nbsp;//handler {function}: the function that is called with the result when it has returned. The parameters that will be sent are &lt;result&gt;, &lt;command&gt;, &lt;names&gt;, &lt;values&gt;. 'command', 'names' and 'values' are given just as they were provided. If an error occurs and no error handler is provided, the error text will be sent as a fifth parameter (and result will be null).<br>&nbsp;&nbsp;&nbsp;&nbsp;//names {array}: an array of strings that will be paired with 'values' to form the data content.<br>&nbsp;&nbsp;&nbsp;&nbsp;//values {array}: an array of strings that will be paired with 'names' to form the data content. 'names' and 'values' must have the same number of elements.<br>&nbsp;&nbsp;&nbsp;&nbsp;//<br>&nbsp;&nbsp;&nbsp;&nbsp;//OPTIONAL PARAMETERS. May be specified in any order after 'values'...<br>&nbsp;&nbsp;&nbsp;&nbsp;//method {string}: may be "GET" or "POST" to override defMethod.<br>&nbsp;&nbsp;&nbsp;&nbsp;//async {boolean}: may be true or false to override defAsync.<br>&nbsp;&nbsp;&nbsp;&nbsp;//error handler {function}: may be provided to override defErrorHandler. The parameters are the same as for 'handler' except the error message will be send instead of the result.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;return function(command, handler, names, values) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!command || typeof(command) != <span class="value">"string"</span>) throw new Error(<span class="value">"sendAjax: first argument must be a string"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!handler || typeof(handler) != <span class="value">"function"</span>) throw new Error(<span class="value">"sendAjax: second argument must be a function"</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//parse the parameters...</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var path = defPath;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (command.indexOf(<span class="value">"/"</span>) &gt;= <span class="value">0</span>) path = <span class="value">""</span>; <span class="comment">//if a path is specified, it takes the place of defPath</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var method = defMethod;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var async = defAsync;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var errorhandler = defErrorHandler;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (var i = <span class="value">4</span>; i &lt; arguments.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (typeof(arguments[i]) == <span class="value">"string"</span> && (arguments[i].toUpperCase() == <span class="value">"GET"</span> || arguments[i].toUpperCase() == <span class="value">"POST"</span>)) method = arguments[i].toUpperCase();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (typeof(arguments[i]) == <span class="value">"boolean"</span>) async = arguments[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else if (typeof(arguments[i]) == <span class="value">"function"</span> || arguments[i] == null) errorhandler = arguments[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//create an AJAX object. Each call to sendAjax() will maintain its own AJAX object so multiple calls will not interfere with each other.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var ajaxobj = getAjaxObj();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!ajaxobj) throw new Error(<span class="value">"sendAjax: could not create XMLHttpRequest."</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//the localhandler will take care of the interpretation of the AJAX result. It is either called explicitly for synchronous mode or assigned to XMLHttpRequest.onreadystatechange for asynchronous mode.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var localhandler = function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (ajaxobj.readyState == <span class="value">4</span>) { <span class="comment">//4 = finished.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (ajaxobj.status != <span class="value">"200"</span>) { <span class="comment">//200 = OK, nothing broke on the server end.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (errorhandler) errorhandler(<span class="value">"error encountered ("</span> + ajaxobj.status + <span class="value">"): "</span> + ajaxobj.responseText, command, names, values);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else handler(null, command, names, values, ajaxobj.responseText);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (defExpectJSON) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//try to make a JSON object out of the result. If it turns out not to be JSON, use the result as plain text.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var dataobject = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var datavalue = extractJSON(ajaxobj.responseText);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (datavalue.length &gt; <span class="value">0</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (datavalue.indexOf(<span class="value">"{"</span>) == <span class="value">0</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataobject = eval(<span class="value">"("</span> + datavalue + <span class="value">")"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dataobject.sourceJSON = datavalue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else dataobject = datavalue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var dataobject = ajaxobj.responseText;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//call your handler with the result.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler(dataobject, command, names, values);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//most likely there was either a javascript error in 'handler' or there was a problem parsing the result.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (errorhandler) errorhandler(<span class="value">"error encountered: "</span> + ex.message, command, names, values);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else throw new Error(<span class="value">"sendAjax: ajax response received but an error was thrown: "</span> + ex.message);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.onreadystatechange = localhandler;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//prepare the URI content string with the names and values to be sent.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var content = <span class="value">""</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (names && values && names.length &gt; <span class="value">0</span> && values.length &gt; <span class="value">0</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (var i = <span class="value">0</span>; i &lt; Math.min(names.length, values.length); i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (content.length &gt; <span class="value">0</span>) content += <span class="value">"&"</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content += names[i] + <span class="value">"="</span> + encodeURIComponent(values[i] != null ? values[i] : <span class="value">""</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//finally, make the call. If synchronous mode was specified, call localhandler after the AJAX call returns.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (method == <span class="value">"GET"</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.open(<span class="value">"GET"</span>, path + command + (content.length &gt; <span class="value">0</span> ? <span class="value">"?"</span> + content : <span class="value">""</span>), async);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.send(null);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!async) localhandler();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else if (method == <span class="value">"POST"</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.open(<span class="value">"POST"</span>, path + command, async);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.setRequestHeader(<span class="value">"Content-type"</span>, <span class="value">"application/x-www-form-urlencoded"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.setRequestHeader(<span class="value">"Content-length"</span>, content.length);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.setRequestHeader(<span class="value">"Connection"</span>, <span class="value">"close"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.send(content);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!async) localhandler();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else throw new Error(<span class="value">"sendAjax: method must be either GET or POST, not "</span> + method);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//sendAjax() returns a function that can be used to cancel an asynchronous request.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ajaxobj.abort();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}();<br></code>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=20</guid>
      <pubDate>Tue, 21 Jun 2011 00:00:00</pubDate>
    </item>
	  <item>
      <title>Blasted Thieves v1.0</title>
      <link>http://www.cyancanyon.com/news/?id=18</link>
      <description><![CDATA[<p>Here it is! the Android game that the elite of the world have been anxiously awaiting for the past 14+ months!</p><p style="text-align:center"><a href="https://market.android.com/details?id=com.cyancanyon.blasted"><img src="./images/blasted_promo.png" alt="Blasted Thieves" title="Blasted Thieves" /></a></p><p>This is tower defense at its finest, with 10 towers to stop 15 types of thieves from getting to your precious loot on 25 different battlefields. You'll need a solid strategy, making good use of the tower upgrades and specialized researches. Check the almanac for hints on which towers are most effective for fighting which thieves. Watch out for the wizards and ghost pirates who don't steal your loot, but create loads of havoc instead. Press on through the campaign to unlock all of the towers and upgrades, so that you can earn your trophies and top off the global high score list.</p>Download <a href="https://market.android.com/details?id=com.cyancanyon.blasted">Blasted Thieves</a> from the Android Market or test drive it for free with <a href="https://market.android.com/details?id=com.cyancanyon.blasted_lite">Blasted Thieves Lite</a>.]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=18</guid>
      <pubDate>Mon, 30 May 2011 00:00:00</pubDate>
    </item>
	  <item>
      <title>A Different Way to GetFiles</title>
      <link>http://www.cyancanyon.com/news/?id=17</link>
      <description><![CDATA[<p>There comes a time in the life of every man, woman and cylon that they must retrieve a list of the files and/or subdirectories from a folder using .NET. Most of us use the accessible static methods from the Directory class...</p><code>string[] files = Directory.GetFiles(@"C:\path");<br>string[] subdirs = Directory.GetDirectories(@"C:\path");</code><p>These are all fine and good for everyone... until you want to filter by file attributes or filter by a regular expression or filter by file size or filter by file date or get files and subdirectories at the same time or iterate through the results instead of just returning the whole lot or doing any of these things quickly. Then you need something else.</p><p>The WIN32 API provides a much more versatile way of getting at the contents of a directory and it can be accessed pretty easily using p/invoke. It exists in kernel32.dll on Windows and coredll.dll on Windows Mobile. I'm going to demonstrate the Windows Mobile specifics because that is how I have used them and there are many googleable examples of the Windows desktop version.</p><p>There are 3 functions we'll need...</p><code>using System.Runtime.InteropServices;<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914391.aspx">http://msdn.microsoft.com/en-us/library/aa914391.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern IntPtr FindFirstFile(String lpFileName, ref WIN32_FIND_DATA lpFindFileData);<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914380.aspx">http://msdn.microsoft.com/en-us/library/aa914380.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364428(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364428(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern bool FindNextFile(IntPtr hFindFile, ref WIN32_FIND_DATA lpFindFileData);<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa911929.aspx">http://msdn.microsoft.com/en-us/library/aa911929.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa364413(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa364413(VS.85).aspx</a></span><br>[DllImport("coredll.dll", CharSet = CharSet.Auto)]<br>private static extern bool FindClose(IntPtr hFindFile);</code><p>The basics here are that you call FindFirstFile with the path and you get back a handle and data for the first file or directory. You then call FindNextFile with the handle to get each subsequent file or directory until they are all gone or you are done. Then you wrap it up nicely by calling FindClose to clean up.</p><p>The FindFirstFile function expects its path parameter to end in '\*' as in 'C:\path\*'. If you leave off the '*', you will get an invalid handle and no data. If you leave off the '\*' altogether as in 'C:\path', you will get data for the 'path' directory and nothing else; no files, no subdirectories.</p><p>We'll need a few constants and a couple data structures...</p><code>private const int MAX_PATH = 260; <span class="comment">//maximum length of a file name</span><br>private const int INVALID_HANDLE_VALUE = -1;<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa915351.aspx">http://msdn.microsoft.com/en-us/library/aa915351.aspx</a></span><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx">http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct FILETIME {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwLowDateTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwHighDateTime;<br>}<br><br><span class="comment">//Windows Mobile: <a href="http://msdn.microsoft.com/en-us/library/aa914427.aspx">http://msdn.microsoft.com/en-us/library/aa914427.aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct WIN32_FIND_DATA {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwFileAttributes;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftCreationTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastAccessTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastWriteTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeHigh;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeLow;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwOID;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cFileName;<br>}</code><p>It is important to note that the layout for WIN32_FIND_DATA is slightly different in Windows. If you are using the wrong definition in your Windows Mobile application, you'll be able to tell because the first two characters of all your file names will be missing.</p> <p>Here is the Windows version...</p><code>private const int MAX_ALTERNATE = 14;<br><br><span class="comment">//Windows: <a href="http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx</a></span><br>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]<br>private struct WIN32_FIND_DATA {<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwFileAttributes;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftCreationTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastAccessTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public FILETIME ftLastWriteTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeHigh;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint nFileSizeLow;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwReserved0;<br>&nbsp;&nbsp;&nbsp;&nbsp;public uint dwReserved1;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cFileName;<br>&nbsp;&nbsp;&nbsp;&nbsp;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]<br>&nbsp;&nbsp;&nbsp;&nbsp;public string cAlternateFileName;<br>}</code><p>The filename you get out does not include the path. FileSizeLow is the file size you want in most cases. If FileSizeHigh has something in it, then your file is huge (4GB+) and you need to combine it with FileSizeLow to make an 8-byte integer. Everything else should be pretty self-explanatory or easy to interpret from MSDN.</p><p>Now then, the function to actually get the files is pretty simple, but varies greatly depending on what you want out of it. We don't want to have to remember to add the '\*' to the path every time we call this function so we'll take care of all that here.</p><code><span class="comment">//function signature will vary with the body.</span><br>public static void GetFiles(string path) {<br>&nbsp;&nbsp;&nbsp;&nbsp;IntPtr handle;<br>&nbsp;&nbsp;&nbsp;&nbsp;WIN32_FIND_DATA filedata;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//we want path to have something useful that we can add to the filename</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//in case we want the entire path, so lets drop the final '*' if it exists</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//and add it later when we need it.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;if (path.EndsWith("*")) path = path.Substring(0, path.Length - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//make sure the path ends with the backslash though.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;if (!path.EndsWith(@"\")) path += @"\";<br><br>&nbsp;&nbsp;&nbsp;&nbsp;filedata = new WIN32_FIND_DATA();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//get the first file, get the rest of the files, and close.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;handle = FindFirstFile(path + "*", ref filedata);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (handle.ToInt32() != INVALID_HANDLE_VALUE) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (filedata.cFileName != "." &amp;&amp; filedata.cFileName != "..") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//filtering and storage logic here</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} while (FindNextFile(handle, ref filedata));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} finally {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FindClose(handle);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>All your filtering just fits inside. The most basic filter, checking for a directory would be something like this...</p><code>List&lt;string&gt; files = new List&lt;string&gt;();<br>uint directoryattribute = (uint)FileAttributes.Directory; <span class="comment">//16 or Hx10</span><br><br>...<br><br>if ((filedata.dwFileAttributes &amp; directoryattribute) != directoryattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>}</code><p>Combine this with a filter based on a regular expression passed in to the function...</p><code>Regex regex = new Regex(@"\.txt");<br><br>...<br><br>if ((filedata.dwFileAttributes &amp; directoryattribute) != directoryattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;if (regex == null || regex.Match(filedata.cFileName).Success) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>If you are going for speed on multiple calls, do your best not to create any class instances in your function. Pass in your regex and result container.</p><p>Here is the final function I ended up using...</p><code>public static void GetFiles(string path, List&lt;string&gt; files, Regex fileregex, List&lt;string&gt; subdirs, Regex subdirregex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;IntPtr handle;<br>&nbsp;&nbsp;&nbsp;&nbsp;WIN32_FIND_DATA filedata;<br>&nbsp;&nbsp;&nbsp;&nbsp;uint dirattribute = (uint)FileAttributes.Directory;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;if (path.EndsWith("*")) path = path.Substring(0, path.Length - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (!path.EndsWith(@"\")) path += @"\";<br><br>&nbsp;&nbsp;&nbsp;&nbsp;filedata = new WIN32_FIND_DATA();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;handle = FindFirstFile(path + "*", ref filedata);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (handle.ToInt32() != -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (filedata.cFileName != "." &amp;&amp; filedata.cFileName != "..") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ((filedata.dwFileAttributes &amp; dirattribute) == dirattribute) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (subdirs != null &amp;&amp; (subdirregex == null || subdirregex.Match(filedata.cFileName).Success)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subdirs.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (files != null &amp;&amp; (fileregex == null || fileregex.Match(filedata.cFileName).Success)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files.Add(path + filedata.cFileName);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} while (FindNextFile(handle, ref filedata));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} finally {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FindClose(handle);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</code><p>...called from something like this to get all images in a directory tree...</p><code>public static List&lt;string&gt; FindImages(string path) {<br>&nbsp;&nbsp;&nbsp;&nbsp;List&lt;string&gt; files, subdirs;<br>&nbsp;&nbsp;&nbsp;&nbsp;Regex regex;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;files = new List&lt;string&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;subdirs = new List&lt;string&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;regex = new Regex(@"\.(png|jpg|jpeg|gif)$");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;subdirs.Add(path);<br>&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; subdirs.Count; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//since subdirectories may be added to the list with each iteration,</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//we end up searching the entire tree level by level</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetFiles(subdirs[i], files, regex, subdirs, null); <span class="comment">//no filter on the subdirectories</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;return files;<br>}</code><p>Finally, if you don't want to wait for the entire list to be returned before you start processing, you can use the yield statement to create a file iterator... but I'm not going to tell you how to do that. There is a great article over on <a href="http://www.codeproject.com/KB/files/FileSystemEnumerator.aspx">codeproject.com</a> that outlines this. They use a SafeHandle that is not available on Windows Mobile, but the basic idea is the same.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=17</guid>
      <pubDate>Sun, 15 Mar 2009 00:00:00</pubDate>
    </item>
	  <item>
      <title>AAAA for iPod</title>
      <link>http://www.cyancanyon.com/news/?id=16</link>
      <description><![CDATA[<p>I just spent the last couple hours in a skirmish with my 160GB classic and iTunes 8 on my mac. I'm not one of those people who obsessively searches for the happy in every pile of shit, but I do feel like I learned something useful on this one.</p><p>If you've ever scoured the file structure on your iPod then you'll know that there is a hidden directory called iPod_Control where iTunes places all your media files. These files are split up into a series of F## folders and given ambiguous names like JFUD.mp3.</p><p>Well my little debacle tonight led me to the conclusion that files specifically named AAAA.*, AAAA 1.*, AAAA 2.* etc. have significance. That is, they are wasting your precious drive space. Now I haven't tested this theory on any other iPod or in any other situation, but here's how it went...</p><p>iTunes 8 was causing me trouble in a variety of ways. One was a computer authorization issue that I don't think had anything to do with this tale. The other issue was syncing problems with my iPod. I store lots of movies and TV shows on my iPod and tonight I wanted to copy over <span style="font-style: italic">Apocalypto</span> and <span style="font-style: italic">Primer</span>. To my dismay I was out of space, something had to go. Oh look, the girlfriend's boring chick movies that she wanted me to put on there and then she never watched them. DELETE. OK, space acquired... Copy up my mp4 files, looks good... Eject the iPod, make sure they show up in the movies list... Damn. They aren't there. Hooked it back up and checked the movie list in iTunes, now the three chick movies are back and no Mel Gibson or brilliant indies. WTF? Try again, delete the movies, this time just upload <span style="font-style: italic">Primer</span> since its a much smaller file... Same thing. So after some time of alternating between futile googling, unhelpful Apple support pages and the rebooting of all involved devices and software I got the thing to start working right again. Or so I thought.</p><p>I copied up <span style="font-style: italic">Primer</span> and everything went just fine. Girly movies gone and entrepreneurial time-travel present and accounted for. Then I tried to sync my podcasts since I'm a bit behind on <span style="font-style: italic">The Onion</span> and I hit a new roadblock: Disk Is Full. My "Other" file types were taking up a full 5.5 GB of disk space. I've had these mystery files (and by mystery I mean album art and crap like that) take a few hundred MB before but this is just stupid. I'm sure its those blasted chick movies that just won't give up and DIE! So I open up Omni DiskSweeper and scan away. Nothings hiding in the trash or anywhere else, its all in the F## folders. I'm guessing that iTunes just removed the references to the video files and couldn't delete them, so I went through checking everything over 1GB. It turned out that there was not one, but three copies of <span style="font-style: italic">Primer</span> on the iPod, though it was only listed once. This is where I noticed that although the three files were in three different directories, one was named some combination of random letters but the other two were both called AAAA.mp4. Suspicious, I went looking for more AAAA files, and found many. There was an AAAA.mp4 that was the lost <span style="font-style: italic">Apocalypto</span> file, a <span style="font-style: italic">BSG</span> episode that I had tried to upload during the whole thing, as well as a ton of Doyle Redland reporting from my many unsuccessful attempts.</p><p>Realizing that this was an iPod restore or not-restore situation and being that I couldn't specifically remember ever being wrong, I deleted all files in the F## folders that began with AAAA. I am now happy to report that my iPod is back to its normal level of "Other" file space, devoid of chick flicks, brimming with the talent of Carruth and teeming with questionable Cylon mind games.</p><p>Me: 1, iPod: 0</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=16</guid>
      <pubDate>Tue, 16 Sep 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Skipping the Mac Trash</title>
      <link>http://www.cyancanyon.com/news/?id=13</link>
      <description><![CDATA[<p>As much as I think that Mac OS X is a generally superior operating system, there is a thing or two that I gripe about. One is the Windows luxury of being able to yank out my USB key at any random time and without any prerequisite action, but I'm not going to talk about that one today. Another is the lack of a skip-the-trash delete command similar to shift-delete in Windows.</p><p>I know its easy to send something to the trash and then empty the trash and people have even automated this, but its not what I'm looking for. I want to completely delete a file without also deleting everything that just happened to be in the trash too. I also want to do it with a keyboard shortcut, but I haven't gotten there yet.</p><p>Here is my dock...</p><img alt="dock with toilet next to trash" title="toilet icon" src="http://www.cyancanyon.com/news/images/docktoilet.png"><p>Oh yeah, theres a toilet on my dock. Drag a file to the trash and it gets marked for deletion. Drag it to the toilet, and you get...</p><img alt="delete notification" title="delete notification" src="http://www.cyancanyon.com/news/images/toiletconfirm.png"><p>This is done with some AppleScript which calls some shell script together with configurable folder actions.</p><p>NOTE: This is all done on OS X Tiger, please let me know if there are caveats for (Snow)?Leopard.</p><p>The UI is just a folder where the icon has been set to the toilet pic. Then it is dragged down into the dock for easy access.</p><p>Next there is the AppleScript. This routine gets the list of files in a directory. For each file, it checks to make sure its not the .DS_Store or the icon file and if not, calls the shell command to delete it.</p><code><span>on adding folder items to this_folder after receiving these_items</span><br>&nbsp;&nbsp;<span>set the item_count to the count of these_items</span><br><br>&nbsp;&nbsp;<span class="comment">--confirm the deletion</span><br>&nbsp;&nbsp;<span>set the_choice to display dialog (item_count as string) &amp; " items are set to be deleted." buttons {"Delete", "Move to Trash"} default button 1</span><br>&nbsp;&nbsp;<span>set the_result to button returned of the_choice</span><br>&nbsp;&nbsp;<span>if the the_result is equal to "Delete" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>repeat with filename in these_items</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--don't process the .DS_Store or the icon file</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if ((filename as string) ends with ":.DS_Store") or ((filename as string) ends with ":Icon") or ((filename as string) ends with ":Icon ") then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--do nothing</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>try</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the delimiter is a colon, so we have to replace it with a slash so the shell command can read it</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set tid to text item delimiters</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to missing value</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {":"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set fnstring to text items of (filename as text)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to {"/"}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>if last item of fnstring is equal to "" then</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -2 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set new_filename to "/" &amp; ((items 2 thru -1 of fnstring) as string)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>set text item delimiters to tid</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>on error msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>display dialog msg</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end try</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--call the shell command</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'r' flag indicates that if the pathname points to a directory, it should be deleted as well as all files and folders it contains</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--the 'f' flag indicates that the deletion is forced, nonexistent files are ignored and there are no prompts</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>do shell script "rm -rf \"" &amp; new_filename &amp; "\""</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>end if</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>end repeat</span><br>&nbsp;&nbsp;<span>else</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">--If you changed your mind, move it to the trash where it will be recoverable</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span>tell application "Finder" to delete these_items</span><br>&nbsp;&nbsp;<span>end if</span><br><span>end adding folder items to</span></code><p>And then we just attach the script as a folder action to the toilet folder. Then, any time files are added to the folder (for example, by dragging them onto the dock icon) the script is fired off and the files deleted.</p>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=13</guid>
      <pubDate>Tue, 10 Jun 2008 00:00:00</pubDate>
    </item>
	  <item>
      <title>Great Circle Distances</title>
      <link>http://www.cyancanyon.com/news/?id=12</link>
      <description><![CDATA[<p>Point to point distances are needed all throughout applications we've written, most notably, Smache. So getting accurate distance values was obviously very important. Unfortunately, the pythagorean theorem doesn't cut it when we're looking for distances across the surface of a planet, so a slightly more complicated function had to be used.</p><p>What we wanted was the great circle distance. Great circles are lines that encircle a sphere leaving equal-sized halves on either side. Any two points on a sphere can be connected with one and only one great circle, except for the cases where the points are exactly opposite each other or exactly the same in which infinite great circles can pass through the two points. To come up with the distance between two points along their respective great circle, we need a little trig and the <a href="http://en.wikipedia.org/wiki/Haversine_formula">haversine formula</a>.</p><p>NOTE: Smache was written in .NET with C# so my examples are also, but I included the javascript representation too because, well, javascript rocks.</p><p>The input parameters to the distance calculation function are the latitude and longitude for point A and the latitude and longitude for point B. We also needed a radius constant which was set at 6372 kilometers, widely agreed upon as the average radius of the earth. Note that because of the irregular shape of the earth, the resulting distance may be off by as much as half a percent.</p><code><span>const double EARTH_RADIUS = 6372;</span><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) { }</span></code><p>The haversine formula expects values to be in radians rather than degrees, so the first task is to make that conversion, simply dividing by 180 over pi.</p><code><span>double latARad = latA / (180 / Math.PI);</span><br><span>double longARad = longA / (180 / Math.PI);</span><br><span>double latBRad = latB / (180 / Math.PI);</span><br><span>double longBRad = longB / (180 / Math.PI);</span></code><p>The formula will also use delta values, so in the interest of comprehension we'll create those variables as well.</p><code><span>double latDelta = latBRad - latARad;</span><br><span>double longDelta = longBRad - longARad;</span><br></code><p>Next is where the haversine comes in. In order to eliminate duplicate equations I've split the formula into two statements.</p><code><span>double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2) + Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br></code><p>Finally we simply need to multiply by the radius to get the final distance.</p><code><span>double finalDistance = haversineB * EARTH_RADIUS;</span></code><p>So the function ends up like so...</p><code><span>const double EARTH_RADIUS = 6372;</span><br><br><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineA = Math.Pow(Math.Sin(latDelta / 2), 2) + Math.Cos(latARad) * Math.Cos(latBRad) * Math.Pow(Math.Sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double haversineB = 2 * Math.Atan2(Math.Sqrt(haversineA), Math.Sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;double finalDistance = haversineB * EARTH_RADIUS;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><p>or in javascript...</p><code><span>var EARTH_RADIUS = 6372;</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latARad = latA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longARad = longA / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latBRad = latB / (180 / Math.PI);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longBRad = longB / (180 / Math.PI);</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var latDelta = latBRad - latARad;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var longDelta = longBRad - longARad;</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineA = Math.pow(Math.sin(latDelta / 2), 2) + Math.cos(latARad) * Math.cos(latBRad) * Math.pow(Math.sin(longDelta / 2), 2);</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var haversineB = 2 * Math.atan2(Math.sqrt(haversineA), Math.sqrt(1 - haversineA));</span><br><br><span>&nbsp;&nbsp;&nbsp;&nbsp;var finalDistance = haversineB * EARTH_RADIUS;</span><br><br> <span>&nbsp;&nbsp;&nbsp;&nbsp;return finalDistance;</span><br><span>}</span></code><br><p>And if you're into one-liners...</p><code><span>double GreatCircleDistance(double latA, double longA, double latB, double longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.Sqrt(1 - (Math.Pow(Math.Sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.Cos((latA / (180 / Math.PI))) * Math.Cos((latB / (180 / Math.PI))) * Math.Pow(Math.Sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span><br><br><span>function GreatCircleDistance(latA, longA, latB, longB) {</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp;return 6372 * 2 * Math.atan2(Math.sqrt(Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2)), Math.sqrt(1 - (Math.pow(Math.sin(((latB / (180 / Math.PI)) - (latA / (180 / Math.PI))) / 2), 2) + Math.cos((latA / (180 / Math.PI))) * Math.cos((latB / (180 / Math.PI))) * Math.pow(Math.sin(((longB / (180 / Math.PI)) - (longA / (180 / Math.PI))) / 2), 2))));</span><br><span>}</span></code>]]></description>
      <guid isPermaLink="true">http://www.cyancanyon.com/news/?id=12</guid>
      <pubDate>Sat, 07 Jun 2008 00:00:00</pubDate>
    </item>
  </channel>
</rss>

