<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linden LAN &#187; Programming</title>
	<atom:link href="http://www.lindenlan.net/tag/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.lindenlan.net</link>
	<description></description>
	<lastBuildDate>Sat, 29 Nov 2014 04:54:20 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	
	<item>
		<title>How To Build An iPhoto Image Flipper Using The jQuery Cycle Plugin</title>
		<link>http://www.lindenlan.net/2010/04/07/how-to-build-an-iphoto-image-flipper-using-the-jquery-cycle-plugin/</link>
		<comments>http://www.lindenlan.net/2010/04/07/how-to-build-an-iphoto-image-flipper-using-the-jquery-cycle-plugin/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 06:13:57 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/?p=375</guid>
		<description><![CDATA[My favorite jQuery plugin by far has to be the jQuery Cycle Plugin. It’s a generic slideshow plugin, but it’s versatile enough that I’ve used it to build a slider, a portfolio, and just recently, an iPhoto-like image flipper. This was inspired from the CJ Image FlipBox. Unfortunately, the way the plugin is designed, you [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>My favorite jQuery plugin by far has to be the <a href="http://malsup.com/jquery/cycle/">jQuery Cycle Plugin</a>.  It’s a generic slideshow plugin, but it’s versatile enough that I’ve used it to build a slider, a portfolio, and just recently, an iPhoto-like image flipper.  This was inspired from the <a href="http://www.cjboco.com/blog.cfm/post/a-nice-jquery-plug-in-that-mimics-apple-s-iphoto-app-to-display-a-group-of-images">CJ Image FlipBox</a>.  Unfortunately, the way the plugin is designed, you can’t mesh it with <a href="http://fancybox.net/">Fancybox</a> because the plugin creates an anchor and an image which intercept the mouseclicks preventing the Fancybox from activating.  So to get around the problem, I used the Cycle plugin to create my own.</p>
<p><span id="more-375"></span></p>
<pre class="brush: html">
&lt;div id=&quot;gallery1&quot; class=&quot;gallery&quot;&gt;
    &lt;a rel=&quot;gallery1&quot; href=&quot;/path/to/fullsize1.jpg&quot;&gt;&lt;img src=&quot;/path/to/thumbnail1.jpg&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
    &lt;a rel=&quot;gallery1&quot; href=&quot;/path/to/fullsize2.jpg&quot;&gt;&lt;img src=&quot;/path/to/thumbnail2.jpg&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
    &lt;a rel=&quot;gallery1&quot; href=&quot;/path/to/fullsize3.jpg&quot;&gt;&lt;img src=&quot;/path/to/thumbnail3.jpg&quot; width=&quot;50&quot; height=&quot;50&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;
</pre>
<p>The HTML is rather straight forward.  You’ve got a &lt;div&gt; as a container for a bunch of thumbnails, each linked to a matching fullsize image.   The CSS on the gallery has fixed dimensions matching the dimensions of the thumbnails with overflow set to hidden.  </p>
<pre class="brush: javascript">
$(&quot;.gallery&quot;).cycle({ 
    speed:      0,
    timeout:    0
});

$(&quot;.gallery&quot;).mousemove(function(e){
    var n = $(this).children().length;
    var w = $(this).width();
    var x = e.pageX - this.offsetLeft;
    var i = Math.floor(x/w * n);

    $(this).cycle(i);
}); 

$(&quot;.gallery a&quot;).fancybox();
</pre>
<p>As for the JavaScript, it’s refreshingly short, which just goes to show how much heavylifting jQuery and the two plugins do.  Let’s start with the Cycle activation.   The speed parameter is set to 0.  This means that the transition is instantaneous when an image flips.  Setting the timeout parameter to 0 prevents the Cycle plugin from auto-advancing.  So far nothing new for anyone who’s used the Cycle plugin.</p>
<p>The next part actually sets up the effect.  Just like when you mouseover an Event group in iPhoto, whenever the mouse moves within the gallery div, the position of the mouse determines the index of the image to display.  First we count the number of images within the gallery.  Next, we get the width of the gallery.  Then we get the horizontal position of the mouse within the gallery’s coordinate system.  Finally, we use those three values to calculate the index.  Using this index, we tell the Cycle plugin which image to display.</p>
<p>Now that the iPhoto effect is working, all that’s left is to activate the Fancybox plugin and we’re done. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2010/04/07/how-to-build-an-iphoto-image-flipper-using-the-jquery-cycle-plugin/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>How To Simultaneously Add Multiple Products To A Magento Shopping Cart</title>
		<link>http://www.lindenlan.net/2009/09/27/how-to-simultaneously-add-multiple-products-to-a-magento-shopping-cart/</link>
		<comments>http://www.lindenlan.net/2009/09/27/how-to-simultaneously-add-multiple-products-to-a-magento-shopping-cart/#comments</comments>
		<pubDate>Mon, 28 Sep 2009 02:01:38 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/?p=349</guid>
		<description><![CDATA[This post is a bit overdue considering the project for which I did this was completed months ago. I finally got around to documenting it. Magento’s product list view lets customers add products to the shopping cart one at a time. The client wanted customers to be able to add multiple products to the shopping [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>This post is a bit overdue considering the project for which I did this was completed months ago.  I finally got around to documenting it.  Magento’s product list view lets customers add products to the shopping cart one at a time.  The client wanted customers to be able to add multiple products to the shopping cart simultaneously.  Given the time constraints for the projects, I created an ad hoc AJAX method to accomplish this feature request.</p>
<p><span id="more-349"></span></p>
<p>Adding a product to a Magento (ver. 1.3.1) shopping cart is accomplished through an HTTP GET request.  It will look like or similar to this:</p>
<pre class="brush: sh">
/path/to/app/checkout/cart/add?product=[id]&amp;qty=[qty]
</pre>
<p>That URL is output by the template helper:</p>
<pre class="brush: php">
$this-&gt;getAddToCartUrl($_product)
</pre>
<p>Since adding a product to the shopping cart is nothing more than a GET request, then all that needs to be done is queue up the URLs of the desired products, make each request in order, and then reload the page when done.</p>
<p>First, in app/design/frontend/
<package>/
<theme>/template/catalog/product/list.html, I first added checkboxes to allow customers to select which products they want and also hidden fields for storing the URLs to add the product and text fields for the quantity. </p>
<pre class="brush: php">
&lt;input type=&quot;checkbox&quot; class=&quot;input-checkbox add&quot; name=&quot;add_&lt;?php echo $_iterator; ?&gt;&quot; id=&quot;add_&lt;?php echo $_iterator; ?&gt;&quot; /&gt;
&lt;input type=&quot;hidden&quot; name=&quot;url_&lt;?php echo $_iterator; ?&gt;&quot; id=&quot;url_&lt;?php echo $_iterator; ?&gt;&quot; value=&quot;&lt;?php echo $this-&gt;getAddToCartUrl($_product) ?&gt;&quot; /&gt;
&lt;?php if(!$_product-&gt;isGrouped()): ?&gt;
     &lt;input type=&quot;text&quot; class=&quot;input-text qty&quot; name=&quot;qty_&lt;?php echo $_iterator; ?&gt;&quot; id=&quot;qty_&lt;?php echo $_iterator; ?&gt;&quot; maxlength=&quot;12&quot; value=&quot;&lt;?php echo $this-&gt;getMinimalQty($_product) ?&gt;&quot; /&gt;
&lt;?php endif; ?&gt;
</pre>
<p>I added this code within the loop that generate the HTML for the product line items for the list.  Next, I added the JavaScript that does the actual processing, also within the list section right after the script block that contains: <code>decorateList('products-list', 'none-recursive')</code>.</p>
<pre class="brush: javascript">
&lt;script type=&quot;text/javascript&quot;&gt;
    function processNext(urls, i) {
        var next = i + 1;
        $(&#039;processing-text&#039;).update(&#039;Processing item &#039; + next);
        if (next &lt; urls.size()) {
            new Ajax.Request(urls[i], {
              method: &#039;get&#039;,
              onComplete: function(transport) {
                processNext(urls, next);
              }
            });
        } else {
            new Ajax.Request(urls[i], {
              method: &#039;get&#039;,
              onComplete: function(transport) {
                window.location.reload();
              }
            });
        }
    }

    function addItemsToCart() {
        $(&#039;add-items-to-cart&#039;).hide();
        $(&#039;waiting&#039;).show();
        
        var addToCartUrls = [];
        $$(&#039;input.add&#039;).each(function(e){ 
            if(e.checked == true) {
                var id = e.readAttribute(&#039;id&#039;).split(&#039;_&#039;)[1];
                var qty = Number($(&#039;qty_&#039; + id).value);
                if (qty &lt; 1) qty = 1;
                addToCartUrls.push($(&#039;url_&#039; + id).value + &#039;qty/&#039; + qty);
            }
        });
        
        if (addToCartUrls.size() &gt; 0) {
            processNext(addToCartUrls, 0);
        } else {
            $(&#039;add-items-to-cart&#039;).show();
            $(&#039;waiting&#039;).hide();
            alert(&#039;Please check off the items you want to add.&#039;);
        }
    }
&lt;/script&gt;
</pre>
<p>At the bottom of the list I added a submit button.</p>
<pre class="brush: php">
&lt;div style=&quot;margin-bottom:5px; text-align:right;&quot;&gt;&lt;button id=&quot;add-items-to-cart&quot; class=&quot;form-button&quot; onclick=&quot;addItemsToCart()&quot;&gt;&lt;span&gt;&lt;?php echo $this-&gt;__(&#039;Add Items to Cart&#039;) ?&gt;&lt;/span&gt;&lt;/button&gt;&lt;div id=&quot;waiting&quot; style=&quot;height:22px; display:none; line-height:22px;&quot;&gt;&lt;span id=&quot;processing-text&quot;&gt;Processing...&lt;/span&gt; &lt;img src=&quot;&lt;?php echo $this-&gt;getSkinUrl().&#039;images/wait22trans.gif&#039;; ?&gt;&quot; width=&quot;22&quot; height=&quot;22&quot; style=&quot;display:inline; vertical-align:middle;&quot;/&gt;&lt;/div&gt;&lt;/div&gt;
</pre>
<p>Clicking the button calls the function <code>addItemsToCart()</code>.  The function hides the button to prevent a double click and unhides the status message.  Next, the function determines which checkboxes are checked.  For each checked checkbox, the function finds the corresponding URL field and quantity field, concatenates the two values, and stores the new URL in an array.  If the length of the array is greater than 0, then the function calls <code>processNext()</code>, otherwise it displays an error message and resets the submit button.</p>
<p>The function <code>processNext()</code> first updates the processing message.  The function takes the array of URLs and an index, and then creates an AJAX GET request using the URL at the given index in the array.  If the AJAX request completes, it calls processNext() with the same array but with an incremented index while the index is less than the array length.  If the incremented index is greater than the array length, then that ends the processing and the function reloads the page.</p>
<p>That’s it.  If there is anything wrong with the code, it assumes that all the GET requests will complete.  Unfortunately, given the time constraints there was no time to account for the scenario where a GET request fails.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2009/09/27/how-to-simultaneously-add-multiple-products-to-a-magento-shopping-cart/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>iPhone Says Hello</title>
		<link>http://www.lindenlan.net/2009/03/18/iphone-says-hello/</link>
		<comments>http://www.lindenlan.net/2009/03/18/iphone-says-hello/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 16:42:29 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Objective-C]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/?p=302</guid>
		<description><![CDATA[I finally got around to downloading the iPhone SDK to see what I can do with it. Since I’ve dabbled in game development back when I worked with Flash and Director, I figured I’d see what I can do with the iPhone. I wanted to start with a framework and I looked at Torque and [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>I finally got around to downloading the iPhone SDK to see what I can do with it.  Since I’ve dabbled in game development back when I worked with Flash and Director, I figured I’d see what I can do with the iPhone.  I wanted to start with a framework and I looked at <a href="http://www.garagegames.com/products/torque-2d/iphone">Torque</a> and <a href="http://unity3d.com/unity/features/iphone-publishing.html">Unity</a>.  Though inexpensive by game engine standards, during a recessionary time of cost cutting, it’s hard to validate a nearly $1000 purchase, especially if you don’t know yet if that investment would pay off.  So I finally settled on the <a href="http://code.google.com/p/cocos2d-iphone/">cocos2d-iphone</a> framework which is based on the <a href="http://cocos2d.org/">cocos2d</a> framework for Python.  There is little to no documentation, so be prepared to read source code.  <a href="http://monoclestudios.com/cocos2d_whitepaper.html">Monocle Studio’s whitepaper</a> will definitely help you bootstrap your project.  In the spirit of that paper, I thought I’d share my translation of the <a href="http://cocos2d.org/doc/programming_guide/hello_world.html">cocos2d “Hello, World” example</a> adapted for the cocos2d-iphone framework.</p>
<p><span id="more-302"></span></p>
<p>First step is to setup your project according to <a href="http://monoclestudios.com/cocos2d_whitepaper.html">Monocle Studio’s whitepaper</a> through “Cleaning up the project” right before “Creating a main menu”.  That’ll create the base project.  You’ll probably want to change the name to something else (e.g. CocosHelloWorld) instead of SimpleGame.  Make sure to change the name where appropriate, for example SimpleGameAppDelegate should be YourAppNameAppDelegate.</p>
<h2>Create the HelloWorld class</h2>
<ol>
<li>Click the Classes group in Groups &amp; Files pane.</li>
<li>Click the Action button</li>
<li>Select Add-&gt;New File…</li>
<li>Choose NSObject subclass</li>
<li>Name the class “HelloWorld”</li>
</ol>
<p><br/></p>
<p>Open HelloWorld.h and change the code to look like the following.  This says that the HelloWorld class is derived from the Layer class (which is derived from the CocosNode class).  </p>
<pre class="brush: cpp">
#import &lt;UIKit/UIKit.h&gt;
#import &quot;cocos2d.h&quot;

@interface HelloWorld : Layer {

}
@end
</pre>
<p>Open HelloWorld.m and change the code to the following.  This says that the HelloWorld class uses its parent class for initialization.  If the initialization is successful, then create a new label, place it in the center, and add it to the layer.  Finally, it returns itself.</p>
<pre class="brush: cpp">
#import &quot;HelloWorld.h&quot;

@implementation HelloWorld

- (id) init {
    self = [super init];
    if (self != nil) {
		Label *label = [Label labelWithString:@&quot;Hello, World!&quot; 
									 fontName:@&quot;Times New Roman&quot; 
									 fontSize:32];
		label.position = cpv(240,160);
		[self add:label];
    }
    return self;	
}

@end
</pre>
<h2>Setup the Director</h2>
<p>Open up YourAppNameAppDelegate.h and change the include declarations to:</p>
<pre class="brush: cpp">
#import &lt;UIKit/UIKit.h&gt;
#import &quot;cocos2d.h&quot;
#import &quot;HelloWorld.h&quot;
</pre>
<p>Next open YourAppNameAppDelegate.m and change it to the following.  The difference between this and the corresponding method in SimpleGame are the last two lines.  The first creates a Scene node and adds a HelloWorld node to the scene.  The next line starts the Director using the just created Scene as the initial scene.  The previous set of lines prepare the iPhone display by initializing a window, enabling the UI for the window, setting the Director to display in landscape mode, then attach the Director to the window, and finally making the window the main window and turning it on.</p>
<pre class="brush: cpp">
#import &quot;CocosHelloWorldAppDelegate.h&quot;

@implementation CocosHelloWorldAppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [window setUserInteractionEnabled:YES];
    [window setMultipleTouchEnabled:YES];
    [[Director sharedDirector] setLandscape: YES];
    [[Director sharedDirector] attachInWindow:window];
	
    [window makeKeyAndVisible];
	
    Scene *s = [[Scene node] add:[HelloWorld node]];
	
    [[Director sharedDirector] runWithScene:s];	
}

@end
</pre>
<p>If all is well, if you click Build &amp; Go the iPhone simulator should display “Hello, World!”.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2009/03/18/iphone-says-hello/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Respect My Space</title>
		<link>http://www.lindenlan.net/2009/01/04/respect-my-space/</link>
		<comments>http://www.lindenlan.net/2009/01/04/respect-my-space/#comments</comments>
		<pubDate>Mon, 05 Jan 2009 02:11:45 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[CSS]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/?p=283</guid>
		<description><![CDATA[Getting CSS rules to work across all browsers is a continuous fight. Thankfully, there are workarounds and DHTML methods to correct many of these problems, and if those fail, reluctantly there are hacks available. Unfortunately, it seems I’ve run into a difference of opinion between the browsers, most likely as a result of the W3 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Getting CSS rules to work across all browsers is a continuous fight.  Thankfully, there are workarounds and DHTML methods to correct many of these problems, and if those fail, reluctantly there are hacks available.  Unfortunately, it seems I’ve run into a difference of opinion between the browsers, most likely as a result of the W3 Consortium failing (yet again) to be explicit with their standards.  The problem is the difference between the way Firefox and Opera interpret the “word-spacing” CSS property versus the way Safari and IE interpret the property.  More specifically each browser treats the non-breaking space differently.  FF/Opera both respect the non-breaking space and Safari/IE both ignore the space.  For example, if the text is “first foo bar last”, FF/Opera would like:</p>
<pre>
first        foo bar       last
</pre>
<p>whereas in Safari/IE the text would appear like:</p>
<pre>
first        foo        bar        last
</pre>
<p>Personally, I agree with the FF/Opera developers and feel that the non-breaking space should be respected since I’m explicitly telling the browser that words to either side of the non-breaking space should be treated as a unit.   So now I’m going to have to put each phrase in some sort of HTML block and depend on a combination of padding and margins to accomplish the same effect in order for it to be cross-browser compatible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2009/01/04/respect-my-space/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Restyle Google Calendar 4</title>
		<link>http://www.lindenlan.net/2008/12/04/restyle-google-calendar-4/</link>
		<comments>http://www.lindenlan.net/2008/12/04/restyle-google-calendar-4/#comments</comments>
		<pubDate>Thu, 04 Dec 2008 07:00:55 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/?p=252</guid>
		<description><![CDATA[As a few users of MyGoogleCal3 have already pointed out, the script no longer works properly. That’s the bad news. The good news is version 4 does work. In fact, with the latest changes Google made to their code, ALL the features that were broken in 3 appear to be working now—Agenda/Week view, the calendar [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>As a few users of <a href="http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/">MyGoogleCal3</a> have already pointed out, the script no longer works properly.  That’s the bad news.  The good news is version 4 does work.  In fact, with the latest changes Google made to their code, ALL the features that were broken in 3 appear to be working now—Agenda/Week view, the calendar navigation buttons, and IE is fixed.   Please download and test out the latest version, and report any bugs in the comments and I’ll try to resolve them as time allows.</p>
<p><span id="more-252"></span><br />
<strong>UPDATE (2009 July 5):</strong> <a href="http://www.lindenlan.net/2009/07/06/mygooglecal-is-now-restylegc/">MyGoogleCal is now RESTYLEgc</a>.</p>
<p><strong>UPDATE (2009 June 23):</strong> Google’s recent update broke the jQuery injection.  In fact, the update is incompatible with injecting jQuery, Prototype, and MooTools.  The only JavaScript framework that works is Dojo.  I updated the code to inject that framework instead.  For those people who value stability over having the latest and greatest features, I also added a comment to MyGoogleCal4js.php that explains how to freeze the calendar version.  That should hopefully help all you fellow developers who tire of your clients calling you when their calendar breaks.</p>
<p><strong>UPDATE (2009 May 3):</strong> <span style="text-decoration: line-through;">MyGoogleCal4 now supports jQuery manipulation of the DOM.</span>  This gives the ability to apply styles after page load.  As an example, the default code removes the inline style on the body tag allowing the stylesheet to set the background color.  The default code also removes the width property from the inline style of the bubble class so that its width can be set in the stylesheet as well.  This should help overcome many of the problems that can’t be solved directly using a stylesheet.  You can thank <a href="http://techtriad.com/">TechTriad.com</a> for funding this new feature.</p>
<p>Download: <a href='http://www.lindenlan.net/wp-content/uploads/2008/12/mygooglecal4.zip'>mygooglecal4.zip</a>.  Contains MyGoogleCal4.php, MyGoogleCal4js.php, mygooglecal4.css, and images.  The CSS file is not guaranteed to be up-to-date so you may have to follow the instructions described in the PHP file to download the latest version from Google.  phpinfo.php is used for diagnostic purposes to make sure your PHP installation is working and that it has the required plugins.  In the archive folder, you can find previous versions of Google’s Javascript and CSS files, in case you want to use those (see MyGoogleCal4js.php).</p>
<pre class="brush: php">
&lt;?php
/*******************************************************************************
 * FILE: MyGoogleCal4.php
 *
 * DESCRIPTION:
 *  This script is an intermediary between an iframe and Google Calendar that
 *  allows you to override the default style.
 *
 * USAGE:
 *  &lt;iframe src=&quot;MyGoogleCal4.php?src=user%40domain.tld&quot;&gt;&lt;/iframe&gt;
 *
 *  where user@domain.tld is a valid Google Calendar account.
 *
 * VALID QUERY STRING PARAMETERS:
 *    title:         any valid url encoded string 
 *                   if not present, takes title from first src
 *    showTitle:     0 or 1 (default)
 *    showNav:       0 or 1 (default)
 *    showDate:      0 or 1 (default)
 *    showTabs:      0 or 1 (default)
 *    showCalendars: 0 or 1 (default)
 *    mode:          WEEK, MONTH (default), AGENDA
 *    height:        a positive integer (should be same height as iframe)
 *    wkst:          1 (Sun; default), 2 (Mon), or 7 (Sat)
 *    hl:            en, zh_TW, zh_CN, da, nl, en_GB, fi, fr, de, it, ja, ko, 
 *                   no, pl, pt_BR, ru, es, sv, tr
 *                   if not present, takes language from first src
 *    bgcolor:       url encoded hex color value, #FFFFFF (default)
 *    src:           url encoded Google Calendar account (required)
 *    color:         url encoded hex color value     
 *                   must immediately follow src
 *    
 *    The query string can contain multiple src/color pairs.  It&#039;s recommended 
 *    to have these pairs of query string parameters at the end of the query 
 *    string.
 *
 * HISTORY:
 *   03 December 2008 - Original release
 *                      Uses technique from MyGoogleCal2 for all browsers,
 *                      rather than giving IE special treatment.
 *   16 December 2008 - Modified MyGoogleCal4js.php so that the regex does a
 *                      general match rather than specifically look for the
 *                      variable &#039;Ac&#039;.
 *   Mar--Apr    2009 - Added jQuery for modifying the style after page load
 *   23 June     2009 - Replaced jQuery with Dojo since jQuery, Prototype, and
 *                      MooTools are not compatible
 *   05 July     2009 - Fixed bug to remove width style from bubble
 *                      
 *   
 * ACKNOWLEDGMENTS:
 *   Michael McCall (http://www.castlemccall.com/) for pointing out &quot;htmlembed&quot;
 *   Mike (http://mikahn.com/) for the link to the online CSS formatter
 *   TechTriad.com (http://techtriad.com/) for requesting and funding the 
 *       Javascript code to edit CSS properties and for selflessly letting the
 *       code be published for everyone&#039;s use and benefit.
 *   
 *
 * copyright (c) by Brian Gibson
 * email: bwg1974 yahoo com
 ******************************************************************************/

/* URL for overriding stylesheet
 * The best way to create this stylesheet is to 
 * 1) Load &quot;http://www.google.com/calendar/embed?src=user%40domain.tld&quot; in a
 *    browser,
 * 2) View the source (e.g., View-&gt;Page Source in Firefox),
 * 3) Copy the relative URL of the stylesheet (i.e., the href value of the 
 *    &lt;link&gt; tag), 
 * 4) Load the stylesheet in the browser by pasting the stylesheet URL into 
 *    the address bar so that it reads similar to:
 *    &quot;http://www.google.com/calendar/d003e2eff7c42eebf779ecbd527f1fe0embedcompiled.css&quot;
 * 5) Save the stylesheet (e.g., File-&gt;Save Page As in Firefox)
 * Edit this new file to change the style of the calendar.
 *
 * As an alternative method, take the URL you copied in Step 3, and paste it
 * in the URL field at http://mabblog.com/cssoptimizer/uncompress.html.
 * That site will automatically format the CSS so that it&#039;s easier to edit.
 */
$stylesheet = &#039;mygooglecal4.css&#039;;

/*******************************************************************************
 * DO NOT EDIT BELOW UNLESS YOU KNOW WHAT YOU&#039;RE DOING
 ******************************************************************************/

// URL for the calendar
$url = &quot;&quot;;
if(count($_GET) &gt; 0) {
  $url = &quot;http://www.google.com/calendar/embed?&quot; . $_SERVER[&#039;QUERY_STRING&#039;];
}

// Request the calendar
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$buffer = curl_exec($ch);
curl_close($ch);

// Point stylesheet and javascript to custom versions
$pattern = &#039;/(&lt;link.*&gt;)/&#039;;
$replacement = &#039;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&#039; . $stylesheet . &#039;&quot; /&gt;&#039;;
$buffer = preg_replace($pattern, $replacement, $buffer);

$pattern = &#039;/src=&quot;(.*js)&quot;/&#039;;
$replacement = &#039;src=&quot;MyGoogleCal4js.php?$1&quot;&#039;;  
$buffer = preg_replace($pattern, $replacement, $buffer);

// Add a hook to the window onload function
$pattern = &#039;/}\);}&lt;\/script&gt;/&#039;;
$replacement = &#039;}); myGoogleCal();}&lt;/script&gt;&#039;;
$buffer = preg_replace($pattern, $replacement, $buffer);

// Use DHTML to modify the DOM after the calendar loads
$pattern = &#039;/(&lt;\/head&gt;)/&#039;;
$replacement = &lt;&lt;&lt;MGC
&lt;script type=&quot;text/javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/dojo/1.3.1/dojo/dojo.xd.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function myGoogleCal() {
    // remove inline style from body so background-color can be set using the stylesheet
    dojo.removeAttr(dojo.body(),&#039;style&#039;);

    // iterate over each bubble and remove the width property from the style attribute
    // so that the width can be set using the stylesheet
    dojo.query(&#039;.bubble&#039;).forEach(function(node){
        dojo.attr(node, {style:{&#039;width&#039;: &#039;&#039;}});
    });

    // see Dojo documentation for other ways to edit DOM
    // http://dojotoolkit.org/
}
&lt;/script&gt;
&lt;/head&gt;
MGC;
$buffer = preg_replace($pattern, $replacement, $buffer);

// display the calendar
print $buffer;
?&gt;

?&gt;
</pre>
<p>If you look at lines 39–41 in MyGoogleCal4js.php, you’ll see how simple the fix is.  No more XmlHttpRequest bugs.  Thank you, Google.</p>
<pre class="brush: php">
&lt;?php header(&quot;Content-type: application/x-javascript&quot;);
/*******************************************************************************
 * FILE: MyGoogleCal4js.php
 *
 * DESCRIPTION:
 *  Companion file for MyGoogleCal4.php to edit the javascript file that
 *  generates the Google Calendar.
 *   
 * USAGE:
 *  There are no user-editable parameters.
 *
 * copyright (c) by Brian Gibson
 * email: bwg1974 yahoo com
 ******************************************************************************/
// URL for the javascript
$url = &quot;&quot;;
if(count($_GET) &gt; 0) {
  $url = &quot;http://www.google.com/calendar/&quot; . $_SERVER[&#039;QUERY_STRING&#039;];
}

/* If you would like to freeze the calendar version, download the Javascript
 * file using the same method for downloading the CSS file, as described in
 * the main script.  You can find some previous versions in the archive folder.
 * NOTE: You should use the corresponding CSS file as well.
 *
 * Edit and uncomment the following line to freeze the calendar version.
 */
//$url = &quot;http://myserver.tld/path/to/archive/6a3eb8ba4a07edb76f79a18d6bdb8933embedcompiled__en.js&quot;;

// Request the javascript
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$buffer = curl_exec($ch);
curl_close($ch);

// Fix URLs in the javascript
$pattern = &#039;/this\.[a-zA-Z]{2}\+&quot;calendar/&#039;;
$replacement = &#039;&quot;http://www.google.com/calendar&#039;;
$buffer = preg_replace($pattern, $replacement, $buffer);

// Display the javascript
print $buffer;
?&gt;
</pre>
<p>The installation of MyGoogleCal4 is the same as the <a href="http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/">installation for MyGoogleCal2</a> except you download the zip file above.  You can ignore step 3 regarding the .htaccess file.  As far as I can tell, it’s no longer needed.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2008/12/04/restyle-google-calendar-4/feed/</wfw:commentRss>
		<slash:comments>134</slash:comments>
		</item>
		<item>
		<title>Easy_Update Upgrade All</title>
		<link>http://www.lindenlan.net/2008/08/08/easy_update-upgrade-all/</link>
		<comments>http://www.lindenlan.net/2008/08/08/easy_update-upgrade-all/#comments</comments>
		<pubDate>Fri, 08 Aug 2008 16:53:48 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/2008/08/08/easy_update-upgrade-all/</guid>
		<description><![CDATA[I like Ruby, but I don’t like its performance, so I’m really anticipating Ruby 2.0. Meanwhile, I’ve been dabbling in Python. Learning a new language and comparing and contrasting them, really helps with determining each language’s strengths and weaknesses. For example, you can easily update all your Ruby gems. Unfortunately, python doesn’t have a direct [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>I like <a href="http://www.ruby-lang.org/">Ruby</a>, but I don’t like its performance, so I’m really anticipating Ruby 2.0.  Meanwhile, I’ve been dabbling in <a href="http://www.python.org/">Python</a>.  Learning a new language and comparing and contrasting them, really helps with determining each language’s strengths and weaknesses.  For example, you can easily update all your Ruby gems.  Unfortunately, python doesn’t have a direct way of doing this using easy_install.  Very odd.  I tried using the <a href="http://semanticmikeal.com/2007/04/easyinstall-upgrade-all.html">shell script method</a>, but for whatever reason it just didn’t work as advertised.  So I wrote a bash alias to take care of it.</p>
<p><span id="more-61"></span></p>
<pre class="brush: python">
alias easy_install_upgrade_all=&#039;python -c &quot;for dist in __import__(&#039;\&#039;&#039;pkg_resources&#039;\&#039;&#039;).working_set: print dist.project_name&quot; | while read package; do sudo easy_install -U &quot;${package}&quot; ; done&#039;
</pre>
<p>Put that alias in your <code>~/.bash_aliases</code> file, run <code>exec bash</code>, and you should be good to go (assuming your <code>.bashrc</code> file is setup to automatically load your <code>.bash_aliases</code> file).  Note the use of escaped single-quotes around “pkg_resources”.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2008/08/08/easy_update-upgrade-all/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Restyle Google Calendar 3</title>
		<link>http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/</link>
		<comments>http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/#comments</comments>
		<pubDate>Thu, 24 Apr 2008 05:46:09 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/</guid>
		<description><![CDATA[MyGoogleCal2 no longer works 100% in Internet Explorer. A runtime error occurs when navigating month-to-month or when switching to Agenda mode. The workaround is to simply hide the navigation interface. Now, I don’t know if this bug was introduced when Google updated the code last month, or if it’s always been there and I just [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/">MyGoogleCal2</a> no longer works 100% in Internet Explorer.  A runtime error occurs when navigating month-to-month or when switching to Agenda mode.  The workaround is to simply hide the navigation interface.  Now, I don’t know if this bug was introduced when Google updated the code last month, or if it’s always been there and I just never noticed.  In any case, the runtime error occurs when <code>//"+a.host+"/calendar</code> is replaced by <code>//www.google.com/calendar</code>.  Given the poor debugging available in IE, I didn’t get very far with figuring out why IE breaks.  I suspect that when IE makes an XmlHttpRequest, it’s double checking that the request URL matches up with the server host, or something to that effect.  Since Google obfuscates the Javascript code, it’s just way too hard to try and fix it.  Instead, I’ve opted to create a new version of MyGoogleCal that uses the <a href="http://www.lindenlan.net/2006/10/08/restyle-google-calendar/">original technique</a> for IE but uses the technique from MyGoogleCal2 for all other browsers.</p>
<p><span id="more-57"></span></p>
<p><strong>UPDATE:</strong> MyGoogleCal now has a <a href="http://www.lindenlan.net/2008/12/04/restyle-google-calendar-4/">fourth version</a>.</p>
<p>Download: <a href='http://www.lindenlan.net/wp-content/uploads/2008/04/mygooglecal3.zip' title='MyGoogleCal3 Zip file'>mygooglecal3.zip</a>.  Contains MyGoogleCal3.php, MyGoogleCal3js.php, mygooglecal3.css, mygooglecal3ie.css, and an .htaccess file.  The CSS files are not guaranteed to be up-to-date so you may have to follow the instructions described in the PHP file to download the latest version from Google.</p>
<pre class="brush: php">
&lt;?php
/*******************************************************************************
 * FILE: MyGoogleCal3.php
 *
 * DESCRIPTION:
 *  This script is an intermediary between an iframe and Google Calendar that
 *  allows you to override the default style.
 *
 * USAGE:
 *  &lt;iframe src=&quot;MyGoogleCal3.php?src=user%40domain.tld&quot;&gt;&lt;/iframe&gt;
 *
 *  where user@domain.tld is a valid Google Calendar account.
 *
 * VALID QUERY STRING PARAMETERS:
 *    title:         any valid url encoded string 
 *                   if not present, takes title from first src
 *    showTitle:     0 or 1 (default)
 *    showNav:       0 or 1 (default)
 *    showDate:      0 or 1 (default)
 *    showTabs:      0 or 1 (default)
 *    showCalendars: 0 or 1 (default)
 *    mode:          WEEK, MONTH (default), AGENDA
 *    height:        a positive integer (should be same height as iframe)
 *    wkst:          1 (Sun; default), 2 (Mon), or 7 (Sat)
 *    hl:            en, zh_TW, zh_CN, da, nl, en_GB, fi, fr, de, it, ja, ko, 
 *                   no, pl, pt_BR, ru, es, sv, tr
 *                   if not present, takes language from first src
 *    bgcolor:       url encoded hex color value, #FFFFFF (default)
 *    src:           url encoded Google Calendar account (required)
 *    color:         url encoded hex color value     
 *                   must immediately follow src
 *    
 *    The query string can contain multiple src/color pairs.  It&#039;s recommended 
 *    to have these pairs of query string parameters at the end of the query 
 *    string.
 *
 * HISTORY:
 *   21 June     2008 - Reverted to an older custom JavaScript file
 *   18 May      2008 - Corrected a bunch of typos 
 *   24 April    2008 - Original release
 *                      Uses the technique from MyGoogleCal for IE browsers and
 *                      the technique from MyGoogleCal2 for the rest.
 *   
 * ACKNOWLEDGMENTS:
 *   Michael McCall (http://www.castlemccall.com/) for pointing out &quot;htmlembed&quot;
 *   Mike (http://mikahn.com/) for the link to the online CSS formatter
 *
 * copyright (c) by Brian Gibson
 * email: bwg1974 yahoo com
 ******************************************************************************/

/* URL for overriding stylesheet
 * The best way to create this stylesheet is to 
 * 1) Load &quot;http://www.google.com/calendar/embed?src=user%40domain.tld&quot; in a
 *    browser,
 * 2) View the source (e.g., View-&gt;Page Source in Firefox),
 * 3) Copy the relative URL of the stylesheet (i.e., the href value of the 
 *    &lt;link&gt; tag), 
 * 4) Load the stylesheet in the browser by pasting the stylesheet URL into 
 *    the address bar so that it reads similar to:
 *    &quot;http://www.google.com/calendar/embed/d003e2eff7c42eebf779ecbd527f1fe0embedcompiled.css&quot;
 * 5) Save the stylesheet (e.g., File-&gt;Save Page As in Firefox)
 * Edit this new file to change the style of the calendar.
 *
 * As an alternative method, take the URL you copied in Step 3, and paste it
 * in the URL field at http://mabblog.com/cssoptimizer/uncompress.html.
 * That site will automatically format the CSS so that it&#039;s easier to edit.
 */
$stylesheet = &#039;mygooglecal3.css&#039;;

/* For the IE stylesheet replace &quot;embed&quot; with &quot;htmlembed&quot; in step (1) 
 */
$stylesheet_ie = &#039;mygooglecal3ie.css&#039;;

/*******************************************************************************
 * DO NOT EDIT BELOW UNLESS YOU KNOW WHAT YOU&#039;RE DOING
 ******************************************************************************/

// URL for the calendar
$url = &quot;&quot;;
$is_ie = FALSE;
if(count($_GET) &gt; 0) {
  if(stristr($_SERVER[&#039;HTTP_USER_AGENT&#039;], &#039;msie&#039;) === FALSE) {
    $url = &quot;http://www.google.com/calendar/embed?&quot; . $_SERVER[&#039;QUERY_STRING&#039;];
  } else {
    $url = &quot;http://www.google.com/calendar/htmlembed?&quot; . $_SERVER[&#039;QUERY_STRING&#039;];
    $is_ie = TRUE;
  }
}

// Request the calendar
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$buffer = curl_exec($ch);
curl_close($ch);

if($is_ie) {
  // Fix hrefs, image sources, and stylesheet
  $pattern = &#039;/(href=&quot;render)/&#039;;
  $replacement = &#039;href=&quot;http://www.google.com/calendar/render&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);

  $pattern = &#039;/(href=&quot;event)/&#039;;
  $replacement = &#039;href=&quot;http://www.google.com/calendar/event&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);

  $pattern = &#039;/(http:\/\/www.google.com\/calendar\/htmlembed)/&#039;;
  $replacement = &#039;MyGoogleCal3.php&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);

  $pattern = &#039;/(src=&quot;images)/&#039;;
  $replacement = &#039;src=&quot;http://www.google.com/calendar/images&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);

  $pattern = &#039;/(&lt;link.*&gt;)/&#039;;
  $replacement = &#039;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&#039; . $stylesheet_ie . &#039;&quot; /&gt;&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);
} else {
  // Point stylesheet and javascript to custom versions
  $pattern = &#039;/(&lt;link.*&gt;)/&#039;;
  $replacement = &#039;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&#039; . $stylesheet . &#039;&quot; /&gt;&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);

  $pattern = &#039;/src=&quot;(.*js)&quot;/&#039;;
  $replacement = &#039;src=&quot;MyGoogleCal3js.php?$1&quot;&#039;;
  $buffer = preg_replace($pattern, $replacement, $buffer);
}

// display the calendar
print $buffer;
?&gt;
</pre>
<p>The installation of MyGoogleCal3 is more or less the same as the <a href="http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/">installation for MyGoogleCal2</a> except you download the zip file above.  Also MyGoogleCal3 uses two stylesheets, one for IE and one for all the other browsers.</p>
<p>There is a minor bug.  Despite changing the URL from <code>../images/ext/poweredby.png</code> to <code>images/poweredby.png</code> in the IE CSS file, this does not make the Powered by Google logo appear in the lower right.  I think that’s OK since it doesn’t appear on Google’s site either.</p>
<p><strong>Update (21 Jun 2008):</strong> Google changed the JavaScript once again and this time very radically.  I’ve been keeping snapshots of their JavaScript code, so I’ve changed the script to load an older customized .js file.  This is a quick fix.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/feed/</wfw:commentRss>
		<slash:comments>35</slash:comments>
		</item>
		<item>
		<title>How To Install MyGoogleCal2.php</title>
		<link>http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/</link>
		<comments>http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/#comments</comments>
		<pubDate>Sun, 23 Mar 2008 05:12:37 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/</guid>
		<description><![CDATA[One of my readers asked for a step-by-step set of instructions to install MyGoogleCal2.php. My original posts for restyling Google Calendar did assume a certain level of expertise. This should hopefully help those who are still confused. Download mygooglecal2.zip. Unzip the file on your server, typically into a subfolder named ‘mygooglecal’. If your web server [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>One of my readers asked for a step-by-step set of instructions to install <a href="http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/">MyGoogleCal2.php</a>.  My original posts for restyling Google Calendar did assume a certain level of expertise.  This should hopefully help those who are still confused.</p>
<p><span id="more-54"></span></p>
<ol>
<li>
<p>Download <a href='http://www.lindenlan.net/wp-content/uploads/2007/10/mygooglecal2.zip' title='MyGoogleCal2 zip file'>mygooglecal2.zip</a>.</p>
</li>
<li>
<p>Unzip the file on your server, typically into a subfolder named ‘mygooglecal’.</p>
</li>
<li>
<p>If your web server supports .htaccess files and your site does not already have one, just move the .htaccess file that was in the zip file to the root of your site.  If your web site already has an .htaccess file, then you will need to copy the mod_rewrite rules from the the zip file’s .htaccess file to the existing one.</p>
</li>
<li>
<p>If you have not already done so, go to the <a href="http://www.google.com/calendar/embed/EmbedHelper_en.html">Embeddable Calendar Helper</a> and generate the HTML code for the calendar.  Place this code into the page that will display the calendar.</p>
</li>
<li>
<p>The HTML code for the Google Calendar is an &lt;iframe&gt; tag.  You need to edit the src attribute to point to MyGoogleCal2.php <em>on your server</em> and not http://www.google.com/calendar/embed.  See ‘USAGE:’ in the MyGoogleCal2.php file for an example.</p>
</li>
<li>
<p>That’s the basic installation.  You should be able to edit the included CSS file.  If you want to use a different CSS file or move it somewhere else, make sure to update the $stylesheet variable in MyGoogleCal2.php.  Remember, the CSS file is not guaranteed to be up-to-date so you may have to download your own copy.  Just follow the directions listed in MyGoogleCal2.php.</p>
</li>
</ol>
<p>Some additional notes:</p>
<ul>
<li>
<p>Some of the colors that the Google Calendar uses are: #c3d9ff, #bcd, #cde, #e8eef7, #e5ecf9, and #112abb.  That should help when editing the CSS file.  I highly recommend using Firefox and the <a href="https://addons.mozilla.org/en-US/firefox/addon/1843">Firebug extension</a>.  The extension has an Inspect feature that let’s you select a DOM object and see its element ID, class, and style attributes.  That will help identify which CSS rule you need change.</p>
</li>
<li>
<p>If the display of the calendar is blank, that usually means your PHP configuration is wrong and most likely the curl module is not installed or disabled.  Open the phpinfo.php file included in the zip file.  This will list a bunch of debug information about your web server’s PHP configuration.  If you do not see curl listed or curl is listed but disabled, then you will need to talk to your system administrator to fix it.</p>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2008/03/22/how-to-install-mygooglecal2php/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>RSpec, Autotest, and Ruby-Libnotify</title>
		<link>http://www.lindenlan.net/2008/03/03/rspec-autotest-and-ruby-libnotify/</link>
		<comments>http://www.lindenlan.net/2008/03/03/rspec-autotest-and-ruby-libnotify/#comments</comments>
		<pubDate>Tue, 04 Mar 2008 04:35:37 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/2008/03/03/rspec-autotest-and-ruby-libnotify/</guid>
		<description><![CDATA[After many false starts, I decided that I really needed to wrap my head around unit testing when writing Rails applications. I more or less completed a Rails 1.2.3 application without any formal tests, and I would like to upgrade it to 2.0.2 and make it RESTful in the process. At the same time, I’ve [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>After many false starts, I decided that I really needed to wrap my head around unit testing when writing Rails applications.  I more or less completed a Rails 1.2.3 application without any formal tests, and I would like to upgrade it to 2.0.2 and make it RESTful in the process.  At the same time, I’ve moved from a Windows development environment to a *nix one after installing Xubuntu on my laptop (an old Compaq Presario).  I’ve also switched from Cream to Emacs.  Despite my Windows desktop being more than twice as fast as my laptop, I just could not stand not being in a true *nix environment.  Too much of the Windows idiosyncrasies got on my nerves.  And my switch from Cream to Emacs was because I just didn’t like the instability of the hacks required to make Vim less of a modal editor.  If I tire of Emacs, I may try pure Vim instead, but I remember <a href="http://www.lindenlan.net/2006/10/27/i-have-cream-but-could-use-more-sugar/">installing Cream</a> simply because I didn’t like pure Vim to start with.  So with these various changes going on with my Rails programming environment, I figured it was an ideal time to learn to formally test my applications.  Of course the first part is setting up the testing environment so that it is easy to use, stays out of your way, and is informative.</p>
<p><span id="more-52"></span></p>
<p>RSpec is the current black when it comes to testing.  Seeing how it’s adopted by <a href="http://merbivore.com/">merb</a> by default and I’m targetting merb for an old app I shelved because Rails just didn’t have the high performance yet to pull it off, I figured it would be the test method I should learn.  It also helps that RSpec is just more readable than Test::Unit.  Installation is more or less trivial and is covered succinctly in the <a href="http://rspec.info/documentation/rails/install.html">RSpec documentation</a>.  Just remember to install RSpec as a plugin and not as a gem.  Autotest will puke otherwise.  You can also use the <code>-x</code> option if you want to install it as an svn external.  (Another option would be <a href="http://piston.rubyforge.org/">piston</a> another gem I need to look at.)</p>
<pre class="brush: bash">
ruby script/plugin install http://rspec.rubyforge.org/svn/tags/CURRENT/rspec
ruby script/plugin install http://rspec.rubyforge.org/svn/tags/CURRENT/rspec_on_rails
</pre>
<p>Then you bootstrap the plugin which installs the generators and rake tasks.</p>
<pre class="brush: bash">
ruby script/generate rspec
ruby script/generate rspec_model foo
rake db:migrate
rake spec
</pre>
<p>If all goes well, rspec should run and you should get a result similar to this:</p>
<pre class="brush: bash">
.
Finished in 0.313 seconds
1 example, 0 failures
</pre>
<p>Now that RSpec is running properly, we want it to run automatically.  That’s where ZenTest comes in.  (The capitalization is intentional.)</p>
<pre class="brush: bash">
sudo gem install ZenTest
</pre>
<p>ZenTest contains autotest.  You start autotest from the root of your Rails application.</p>
<pre class="brush: bash">
autotest
</pre>
<p>Autotest will then sit there continually checking your app folders for any file changes.  When one is detected that requires RSpec to run, autotest will fire off the rake task to do so.  Go ahead and edit <code>./app/models/foo.rb</code> by adding a blank line and then save it.  When you return to the terminal window you should see that the test ran and you got the same result as before.</p>
<p>There is a prettier and more informative alternative than the results provided in the terminal.  RSpec can produce HTML output as well.  Create a folder <code>./doc/spec/</code> for the output folder.  Now open up <code>./spec/spec.opts</code> in your editor and add the following lines.</p>
<pre class="brush: bash">
--format 
html:doc/spec/report.html
</pre>
<p>This will cause an HTML report to be generated every time a test is run, in addition to the terminal output.  The HTML report not only tells you what tests failed and passed but also where the failures occurred.  The HTML format is much easier to navigate than the terminal output.</p>
<p>Mac OS X users have a notification program called <a href="http://growl.info/">Growl</a> that autotest hooks into.  On Windows there’s <a href="http://www.fullphat.net/">Snarl</a>.  For Linux, there’s libnotify.  libnotify is not a standalone application like Growl or Snarl, but a library around which an application, or in this case, an applet must be written.  There’s a handful of how-tos that walk you thru hooking autotest to libnotify, but the one I considered the best was this blog post, <a href="http://www.ikhono.net/2007/12/16/gnome-autotest-notifications">Gnome autotest Notifications</a>.  If you follow those instructions, you should get a working notifier, but I would recommend replacing his .autotest file with my <a href='http://www.lindenlan.net/wp-content/uploads/2008/03/autotest.txt' title='Autotest File'>.autotest file</a>:</p>
<pre class="brush: ruby">
require &#039;rnotify&#039;
require &#039;gtk2&#039;
require &#039;launchy&#039;

module Autotest::RNotify
  class Notification
    attr_accessor :verbose, :image_root, :tray_icon, :notification, 
                  :image_pass, :image_pending, :image_fail,
                  :image_file_pass, :image_file_pending, :image_file_fail,
                  :status_image_pass, :status_image_pending, :status_image_fail
    
    def initialize(timeout = 5000, 
                   image_root = &quot;#{ENV[&#039;HOME&#039;]}/.autotest_images&quot; , 
                   report_url = &quot;doc/spec/report.html&quot;,
                   verbose = false)
      self.verbose = verbose
      self.image_root = image_root
      self.image_file_pass = &quot;#{image_root}/pass.png&quot;
      self.image_file_pending = &quot;#{image_root}/pending.png&quot;
      self.image_file_fail = &quot;#{image_root}/fail.png&quot;

      raise(&quot;#{image_file_pass} not found&quot;) unless File.exists?(image_file_pass)
      raise(&quot;#{image_file_pending} not found&quot;) unless File.exists?(image_file_pending)
      raise(&quot;#{image_file_fail} not found&quot;) unless File.exists?(image_file_fail)
      
      puts &#039;Autotest Hook: loading Notify&#039; if verbose
      Notify.init(&#039;Autotest&#039;) || raise(&#039;Failed to initialize Notify&#039;)

      puts &#039;Autotest Hook: initializing tray icon&#039; if verbose
      self.tray_icon = Gtk::StatusIcon.new
      tray_icon.pixbuf = Gdk::Pixbuf.new(image_file_pending,22,22)
      tray_icon.tooltip = &#039;RSpec Autotest&#039;

      puts &#039;Autotest Hook: Creating Notifier&#039; if verbose
      self.notification = Notify::Notification.new(&#039;X&#039;, nil, nil, tray_icon)

      notification.timeout = timeout

      puts &#039;Autotest Hook: Connecting mouse click event&#039; if verbose
      tray_icon.signal_connect(&quot;activate&quot;) do
        Launchy::Browser.new.visit(report_url)
      end
      
      Thread.new { Gtk.main }
      sleep 1
      tray_icon.embedded? || raise(&#039;Failed to set up tray icon&#039;)
    end
    
    def notify(icon, tray, title, message)
      notification.update(title, message, nil)
      notification.pixbuf_icon = icon
      tray_icon.tooltip = &quot;Last Result: #{message}&quot;
      tray_icon.pixbuf = tray
      notification.show
    end
    
    def passed(title, message)
      self.image_pass ||= Gdk::Pixbuf.new(image_file_pass, 48, 48)
      self.status_image_pass ||= Gdk::Pixbuf.new(image_file_pass, 22, 22)
      notify(image_pass, status_image_pass, title, message)
    end
    
    def pending(title, message)
      self.image_pending ||= Gdk::Pixbuf.new(image_file_pending, 48, 48)
      self.status_image_pending ||= Gdk::Pixbuf.new(image_file_pending, 22, 22)
      notify(image_pending, status_image_pending, title, message)
    end
    
    def failed(title, message) 
      self.image_fail ||= Gdk::Pixbuf.new(image_file_fail, 48, 48)
      self.status_image_fail ||= Gdk::Pixbuf.new(image_file_fail, 22, 22)
      notify(image_fail, status_image_fail, title, message)
    end
    
    def quit
      puts &#039;Autotest Hook: Shutting Down...&#039; if verbose
      #Notify.uninit
      Gtk.main_quit
    end
  end

  Autotest.add_hook :initialize do |at|
    @notify = Notification.new
  end

  Autotest.add_hook :ran_command do |at|
    results = at.results.last

    unless results.nil?
      output = results[/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+pending)?/]
      if output
        failures = $~[2].to_i
        pending = $~[4].to_i
      end
      
      if failures &gt; 0
        @notify.failed(&quot;Tests Failed&quot;, output)
      elsif pending &gt; 0
        @notify.pending(&quot;Tests Pending&quot;, output)
      else
        unless at.tainted
          @notify.passed(&quot;All Tests Passed&quot;, output)
        else
          @notify.passed(&quot;Tests Passed&quot;, output)
        end
      end
    end
  end

  Autotest.add_hook :quit do |at|
    @notify.quit
  end
end
</pre>
<p>The improvements I made are to (a) forego using themeable icons and stick strictly with the <a href="http://www.thelucid.com/articles/2007/07/30/autotest-growl-fail-pass-smilies">Autotest Growl Fail/Pass Smilies</a> (b) run a check to make sure the images exist otherwise raise an error, and © load the RSpec HTML report when you click the icon in the system tray.  In order for the onclick event to work, you need to install the launchy gem.</p>
<pre class="brush: bash">
sudo gem install launchy
</pre>
<p>After all that is up and running, you should have a fully functioning test environment that will automatically run (after starting autotest in a terminal) with a notification applet running in your system tray that you can click to load a well-formatted HTML report in your browser.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2008/03/03/rspec-autotest-and-ruby-libnotify/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Restyle Google Calendar 2</title>
		<link>http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/</link>
		<comments>http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/#comments</comments>
		<pubDate>Mon, 15 Oct 2007 04:38:54 +0000</pubDate>
		<dc:creator><![CDATA[Brian]]></dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/</guid>
		<description><![CDATA[The long awaited new version of MyGoogleCal is here. It supports the new AJAX-enabled Javascript method Google switched to last month. As a bonus, the new version has native support for multiple calendars each with their own colors! Other changes to the code include officially switching from fopen to curl to retrieve the data. Furthermore, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>The long awaited new version of <a href="http://www.lindenlan.net/2006/10/08/restyle-google-calendar/">MyGoogleCal</a> is here.  It supports the new AJAX-enabled Javascript method Google switched to last month.  As a bonus, the new version has native support for multiple calendars each with their own colors!  Other changes to the code include officially switching from fopen to curl to retrieve the data.  Furthermore, the code replaces much less HTML since it’s encapsulated in Javascript.  Without further ado…</p>
<p><span id="more-39"></span></p>
<p><strong>UPDATE:</strong> MyGoogleCal now has a <a href="http://www.lindenlan.net/2008/04/23/restyle-google-calendar-3/">third version</a>.</p>
<p>Download: <a href='http://www.lindenlan.net/wp-content/uploads/2007/10/mygooglecal2.zip' title='mygooglecal2.zip'>mygooglecal2.zip</a>.  Contains MyGoogleCal2.php, mygooglecal2.css, MyGoogleCal2js.php, and an .htaccess file.  The CSS file is not guaranteed to be up-to-date so you may have to follow the instructions described in the PHP file to download the latest version from Google.</p>
<pre class="brush: php">
&lt;?php
/*******************************************************************************
 * FILE: MyGoogleCal2.php
 *
 * DESCRIPTION:
 *  This script is an intermediary between an iframe and Google Calendar that
 *  allows you to override the default style.
 *
 * USAGE:
 *  &lt;iframe src=&quot;MyGoogleCal2.php?src=user%40domain.tld&quot;&gt;&lt;/iframe&gt;
 *
 *  where user@domain.tld is a valid Google Calendar account.
 *
 * VALID QUERY STRING PARAMETERS:
 *    title:         any valid url encoded string 
 *                   if not present, takes title from first src
 *    showTitle:     0 or 1 (default)
 *    showNav:       0 or 1 (default)
 *    showDate:      0 or 1 (default)
 *    showTabs:      0 or 1 (default)
 *    showCalendars: 0 or 1 (default)
 *    mode:          WEEK, MONTH (default), AGENDA
 *    height:        a positive integer (should be same height as iframe)
 *    wkst:          1 (Sun; default), 2 (Mon), or 7 (Sat)
 *    hl:            en, zh_TW, zh_CN, da, nl, en_GB, fi, fr, de, it, ja, ko, 
 *                   no, pl, pt_BR, ru, es, sv, tr
 *                   if not present, takes language from first src
 *    bgcolor:       url encoded hex color value, #FFFFFF (default)
 *    src:           url encoded Google Calendar account (required)
 *    color:         url encoded hex color value     
 *                   must immediately follow src
 *    
 *    The query string can contain multiple src/color pairs.  It&#039;s recommended 
 *    to have these pairs of query string parameters at the end of the query 
 *    string.
 *
 * HISTORY:
 *   22 March    2008 - Replaced the custom javascript with a PHP script that 
 *                      uses the same technique as MyGoogleCal.php
 *   14 March    2008 - Update to match Google changes to javascript code
 *   14 January  2008 - Fixed Add to Calendar URL to point to Google
 *    3 January  2008 - Reverted back to customized javascript code to fix
 *                      bug where no data is displayed when navigating
 *                      into future or past
 *                      Note: imported calendars (e.g. iCal) should work again
 *   29 October  2007 - Update to match Google changes to javascript code
 *                      Note: imported calendars (e.g. iCal) do not work
 *   14 October  2007 - Original release
 *   
 * copyright (c) by Brian Gibson
 * email: bwg1974 yahoo com
 ******************************************************************************/

/* URL for overriding stylesheet
 * The best way to create this stylesheet is to 
 * 1) Load &quot;http://www.google.com/calendar/embed?src=user%40domain.tld&quot; in a
 *    browser,
 * 2) View the source (e.g., View-&gt;Page Source in Firefox),
 * 3) Copy the relative URL of the stylesheet (i.e., the href value of the 
 *    &lt;link&gt; tag), 
 * 4) Load the stylesheet in the browser by pasting the stylesheet URL into 
 *    the address bar so that it reads similar to:
 *    &quot;http://www.google.com/calendar/embed/d003e2eff7c42eebf779ecbd527f1fe0embedcompiled.css&quot;
 * 5) Save the stylesheet (e.g., File-&gt;Save Page As in Firefox)
 * Edit this new file to change the style of the calendar.
 *
 * As an alternative method, take the URL you copied in Step 3, and paste it
 * in the URL field at http://mabblog.com/cssoptimizer/uncompress.html.
 * That site will automatically format the CSS so that&#039;s more easily editable.
 */
$stylesheet = &#039;mygooglecal2.css&#039;;

/*******************************************************************************
 * DO NOT EDIT BELOW UNLESS YOU KNOW WHAT YOU&#039;RE DOING
 ******************************************************************************/

// URL for the calendar
$url = &quot;&quot;;
if(count($_GET) &gt; 0) {
  $url = &quot;http://www.google.com/calendar/embed?&quot; . $_SERVER[&#039;QUERY_STRING&#039;];
}

// Request the calendar
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$buffer = curl_exec($ch);
curl_close($ch);

// Point stylesheet and javascript to custom versions
$pattern = &#039;/(&lt;link.*&gt;)/&#039;;
$replacement = &#039;&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;&#039; . $stylesheet . &#039;&quot; /&gt;&#039;;
$buffer = preg_replace($pattern, $replacement, $buffer);

$pattern = &#039;/src=&quot;(.*js)&quot;/&#039;;
$replacement = &#039;src=&quot;MyGoogleCal2js.php?$1&quot;&#039;;
$buffer = preg_replace($pattern, $replacement, $buffer);

// display the calendar
print $buffer;
?&gt;
</pre>
<p>Use is similar to the original MyGoogleCal.php.  To help create the &lt;iframe&gt;, you can use the <a href="http://www.google.com/calendar/embed/EmbedHelper_en.html">Embeddable Calendar Helper</a>.  Just remember to update the src attribute to point to MyGoogleCal2.php and not http://www.google.com/calendar/embed.  </p>
<p><span style="text-decoration: line-through;">In addition to a custom CSS file the script makes use of a custom Javascript file.  Each URL is set after the script’s header and includes instructions on how to download versions of these files from Google.  This is important since it appears that the Javascript file is localized.  (I included copies that I downloaded in the above Zip file.)</span> </p>
<p><strong>Update (22 Mar 08):</strong> I created a new PHP file, MyGoogleCal2js.php, which downloads the JavaScript code and edits the code on-the-fly.  This negates the need to maintain a customized version of the JavaScript code on the same server as the MyGoogleCal.php script.   This should also lessen the chance that MyGoogleCal2.php will break when Google updates the code as long as the changes are minor.</p>
<p>The UI images are no longer contained in the CSS file.  You have two options on how to handle this.  If your site runs off of Apache, you can create a rewrite rule to hotlink to the Google images.  (I included an .htaccess file in the Zip file above that has a sample rule.)  The other, more conventional option is to create an “images” subfolder where the script is located and place copies of these images there.  You will definitely have to do this if you want to replace those images with customized versions. (I included images in the Zip file.)</p>
<p>What I’m hung-up on right now is figuring out a way to not have to save and rewrite the Javascript file.  The Javascript file is responsible for drawing the calendar, retrieving the XML data from Google, and then putting that data into the calendar.   The <code>_init()</code> function dynamically creates the URL from which to retrieve the XML data by using the browser’s window.location.host property.  Since the browser will presumably be pointing to your site, the host property will not be www.google.com.  As a result, the Javascript <span style="text-decoration: line-through;">hangs at the XML loading stage</span> fails to retrieve data when navigating into the future or past.  My attempts at circumventing this problem has been met with little success.</p>
<p>I tried the passthru directive in .htaccess file like so:</p>
<pre class="brush: xhtml">
RewriteRule ^(calendar/feeds/.*)$ http://www.google.com/$1 [P]
</pre>
<p>The browser spits out a 502 error.    </p>
<p>A redirect, [R] instead of [P], only works half-way.  The XmlHttpRequest goes thru but the data is rejected because it’s considered insecure since you can’t load data retrieved from an external domain.</p>
<p>I also tried ProxyPass/ProxyPassReverse to map</p>
<pre class="brush: xhtml">
/calendar/
</pre>
<p>to</p>
<pre class="brush: xhtml">

http://www.google.com/calendar

</pre>
<p>but it simply results in an error.  I’ll have to consult with my host to figure if the error is the configuration on their end or mine.</p>
<p><strong>Update (03 Jan 08):</strong> <em>After talking with my hosting provider it turns out this error is the fault of my virtual server and it won’t be fixed since they’re retiring the hardware.  I’ve been placed in the queue to migrate my account over to a new (hopefully problem-free) server.  In any case, I’ll definitely revisit this solution after the migration is over and test it then.</em></p>
<p>For more info, checkout: <a href="http://www.xml.com/pub/a/2005/11/09/fixing-ajax-xmlhttprequest-considered-harmful.html">Fixing AJAX: XMLHttpRequest Considered Harmful</a> or <a href="http://developer.yahoo.com/javascript/howto-proxy.html">Use a Web Proxy for Cross-Domain XMLHttpRequest Calls</a>.  The last one has pretty pictures.</p>
<p>Failed experiments include:</p>
<ul>
<li>
<p>Use the Javascript prototype mechanism to somehow override the window.location.host property to return a string of my choosing.</p>
</li>
<li>
<p>Rewrite a request to www.google.com to point to MyGoogleCal2.php so that I could inject code into the calendar page.  I thought this had a lot promise up until the point I realized I couldn’t put an .htaccess file at google.com. This was one of those “Duh!” moments.</p>
</li>
<li>
<p>Manipulate the &lt;iframe&gt; DOM in order to change its document.styleSheets property directly.  Due to security reasons, you can’t modify, let alone read, the DOM properties across different domains.</p>
</li>
<li>
<p>Proxy the XmlHttpRequest thru a curl script.  This almost worked.  The curl request succeeded in getting the JSON data, but for whatever reason, the Javascript code failed to do anything with it.  Depending on the content-type requested for the response, either nothing happening or the browser would spit out a Javascript error.  I’m sure it had to with an incomplete request header or returning just the response data instead of the entire response.  But whatever reason it was, this solution seemed more trouble than it was worth.</p>
</li>
</ul>
<p><span style="text-decoration: line-through;"><strong>Update (29 Oct 07):</strong> <em>Google’s new version of the Javascript code does not require any customization.  Do you suppose some Google developer read my post? <img src="http://www.lindenlan.net/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /> However, a side-effect is that imported calendars do not work.  I’m betting it has to do with XmlHttpRequest permissions, but I won’t know for certain without further investigation.</em></span> </p>
<p>When you’re done restyling your calendar, <a href="http://www.lindenlan.net/2008/01/10/calendar-casting-call/">show it off</a>!  Oh and don’t forget to Digg the article.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lindenlan.net/2007/10/14/restyle-google-calendar-2/feed/</wfw:commentRss>
		<slash:comments>79</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.569 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2026-04-28 20:00:27 -->

<!-- Compression = gzip -->