Archive for the ‘Fun’ category

Autoloader That Installs PEAR Packages

November 4th, 2009

I had a thought the other night about an autoloader that could install pear packages automatically for you.

Security concerns aside, I thought this would be a fun/juvenile coding experiment, and something that could showcase how easy the pyrus api is for installing PEAR packages. So here it is, pushed out on github – http://github.com/saltybeagle/AutoloadPackage/blob/master/AutoloadPackage.php

Only 56 lines of code to have an autoloader that will install a PEAR2 package. Now if that’s not fun, I don’t know what is.

:-)

Oh, and you should really check out Pyrus, the new PEAR installer. Get it over at the pear2 website (under construction).

Cross-Origin Resource Sharing Demo

September 11th, 2009

The Problem

Many times I’ve been frustrated with the same-origin policy for AJAX requests. I have legitimate content on another domain that I would like to retrieve and display on another page using AJAX.

As an example, department.unl.edu would like to display a list of upcoming events from events.unl.edu/department/.

Historically you would have to serve the content out as JSON, or use a proxy to convert the content to json, and reference the resource using a script tag which gets around the same-origin policy – and for years, this has worked well.

Unfortunately this requires you to either send content out in multiple formats (html + json for these requests), or use html and send all requests for cross-site resources through a proxy script, which adds a translation delay.

Serving the content out in two formats requires more work to maintain, and if the content output is cached, requires more resources to generate. If you use a proxy to translate the content, you have an open proxy unless you lock down what resources the proxy script can retrieve and translate.

CORS is the Answer

The answer is in the Cross-Origin Resource Sharing specification, which is supported in Firefox 3.5+, Safari 4+ and albeit using agent specific methods, supported in Internet Explorer 8+.

The specification allows resources on other domains to be retrieved through AJAX methods, if the resource announces support as a Cross-Origin Resource. This is done through special headers which identify to the user-agent which alternate origin domains can request the resource and what methods are supported.

Demo

For the demo, the html and js is served off of this domain, and the CORS resource is located on ucommbieber.unl.edu.

Click here to go to the demo.

This page demonstrates both GET and POST methods, with custom callbacks, using an api just like the jQuery get and post methods.

The HTML:

<html>
<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script>

<script type="text/javascript" src="cors.js"></script>
<script type="text/javascript">

function testGet()
{
    getCORS('http://ucommbieber.unl.edu/CORS/cors.php', null, function(data){alert(data);});
}
function testPost(form)
{
    postCORS('http://ucommbieber.unl.edu/CORS/cors.php', {name: form.name.value}, function(data){alert(data);});
}
</script>
<body>
<h1>CORS Examples</h1>

<p>Test GET
    This page retrieves content from another server, using CORS<br />
    <a href="#" onclick="testGet(); return false;">Get content from another server</a>
</p>

<p>Test POST:
    <form action="#" onsubmit="testPost(this); return false">

        Name: <input type="text" name="name" />
        <input type="submit" />
    </form>

</p>
<a href="http://saltybeagle.com/2009/09/cross-origin-resource-sharing-demo/">More info</a>
</body>
</html>

The javascript:

/**
 * This is for Cross-site Origin Resource Sharing (CORS) requests.
 *
 * Additionally the script will fail-over to a proxy if you have one set up.
 *
 * @param string   url      the url to retrieve
 * @param mixed    data     data to send along with the get request [optional]
 * @param function callback function to call on successful result [optional]
 * @param string   type     the type of data to be returned [optional]
 */
function getCORS(url, data, callback, type) {
    try {
        // Try using jQuery to get data
        jQuery.get(url, data, callback, type);
    } catch(e) {
        // jQuery get() failed, try IE8 CORS, or use the proxy
        if (jQuery.browser.msie && window.XDomainRequest) {
            // Use Microsoft XDR
            var xdr = new XDomainRequest();
            xdr.open("get", url);
            xdr.onload = function() {
                callback(this.responseText, 'success');
            };
            xdr.send();
        } else {
            try {
                // Ancient browser, use our proxy
                var mycallback = function() {
                    var textstatus = 'error';
                    var data = 'error';
                    if ((this.readyState == 4)
                        && (this.status == '200')) {
                        textstatus = 'success';
                        data = this.responseText;
                    }
                    callback(data, textstatus);
                };
                // proxy_xmlhttp is a separate script you'll have to set up
                request = new proxy_xmlhttp();
                request.open('GET', url, true);
                request.onreadystatechange = mycallback;
                request.send();
            } catch(e) {
                // Could not fetch using the proxy
            }
        }
    }
}

/**
 * This method is for Cross-site Origin Resource Sharing (CORS) POSTs
 *
 * @param string   url      the url to post to
 * @param mixed    data     additional data to send [optional]
 * @param function callback a function to call on success [optional]
 * @param string   type     the type of data to be returned [optional]
 */
function postCORS(url, data, callback, type)
{
    try {
        // Try using jQuery to POST
        jQuery.post(url, data, callback, type);
    } catch(e) {
        // jQuery POST failed
        var params = '';
        for (key in data) {
            params = params+'&'+key+'='+data[key];
        }
        // Try XDR, or use the proxy
        if (jQuery.browser.msie && window.XDomainRequest) {
            // Use XDR
            var xdr = new XDomainRequest();
            xdr.open("post", url);
            xdr.send(params);
            xdr.onload = function() {
                callback(xdr.responseText, 'success');
            };
        } else {
            try {
                // Use the proxy to post the data.
                request = new proxy_xmlhttp();
                request.open('POST', url, true);
                request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                request.send(params);
            } catch(e) {
                // could not post using the proxy
            }
        }
    }
}

The PHP code responding from the server looks like this:

<?php

// Specify domains from which requests are allowed
header('Access-Control-Allow-Origin: *');

// Specify which request methods are allowed
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');

// Additional headers which may be sent along with the CORS request
// The X-Requested-With header allows jQuery requests to go through
header('Access-Control-Allow-Headers: X-Requested-With');

// Set the age to 1 day to improve speed/caching.
header('Access-Control-Max-Age: 86400');

// Exit early so the page isn't fully loaded for options requests
if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') {
    exit();
}

// If raw post data, this could be from IE8 XDomainRequest
// Only use this if you want to populate $_POST in all instances
if (isset($HTTP_RAW_POST_DATA)) {
    
$data explode('&'$HTTP_RAW_POST_DATA);
    foreach (
$data as $val) {
        if (!empty(
$val)) {
            list(
$key$value) = explode('='$val);   
            
$_POST[$key] = urldecode($value);
        }
    }
}

echo 'Hello CORS, this is '
     
$_SERVER['SERVER_NAME'] . PHP_EOL
     
.'You sent a '.$_SERVER['REQUEST_METHOD'] . ' request.' PHP_EOL;

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    echo 
'Your name is ' htmlentities($_POST['name']);
}


Note the special headers that have to be returned to allow the request to complete:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: X-Requested-With

The X-Requested-With allows us to use jQuery’s internal GET method which adds in the additional header.

View all the code on GitHub.

Support for proxy fallback for older browsers is left up to you to code. The proxy requires you to set up a script which translates a page into JSON and handles the response. There are many examples of how to do this.

Lost Stickley Side Table

August 10th, 2008

The rumor is that this was a prototype Stickley table, never put in to production. It has 14 mortise & tenon joints, and the legs angle slightly outward – which makes it a more complex project to build. This was also the first project in which I used hand-cut dovetails. After some reflection, I think this project required every tool in my little workshop to build. From mortising & tenoning jigs, hand saw & chisel for the dovetails, table saw & dado blades, jig saw for rough-cuts, band saw for the smooth curves, thickness planer, biscuit joiner, sanders, drills and screwdrivers – just about everything was used to some degree.

It has taken me quite a few weekends to build, but now that it’s complete I think it will make a nice piece of furniture for our house.
Done finishing!
Lost Stickley Side Table
half blind dovetails on the drawer front

The only question is – what do I build next?

Stickley #602

March 31st, 2008

Recently I discovered the online woodworking community… something which I never even thought of looking for online.

Through the searches and feeds I’ve amassed – I’m finding a lot of inspiration and a ton of projects I’d like to build. The first of which is a reproduction Stickley tabouret.

I’m not finished yet – I just finished staining it tonight and I’ll put topcoat on it in the next few days. But, building it was quite a pleasure, as there’s a lot of good construction and joinery techniques to practice in it.

It’s an excellent little table/stool, with good proportions and a nice size.

Stickley #602

Thanks to TreeFrogFurniture for the inspiration.

New Dog

May 23rd, 2007

Milo

Milo.

Freecycle Piano

February 26th, 2007

First – Thanks to Allen, Allen, Tyson, and Aaron.
Now the news!
A couple weeks ago we got a used piano from freecycle. In case you don’t know… Freecycle is a mailing list where people list things they have to offer for free – the idea is to keep things with some life left in them out of our landfills. The concept is great… larger metropolitan areas will have a list for their area. Anyways, Katie and I had talked about getting a piano for our house, and after a couple weeks one was listed on freecycle.

Piano

The piano is an early 20th century Ivers & Pond mahogany full upright. Some marks on the wood, and missing a couple ivory key tops… but what can you expect for a piano nearly 100 years old.
It is a monster… very big and heavy – but has a good sound to it. We do need to get it tuned some time.

Anyone know where I can get a tuning lever for a piano?