Pixel Everywhere

There are only 10 types of people in this world. Those who know binary and those who don't.

Storing Values to AS3 using Google Spreadsheets — November 9, 2010

Storing Values to AS3 using Google Spreadsheets

This topic is separated into two parts –storing and retrieving. Retrieving is in another topic. Imagine storing values to a spreadsheet that Flash can retrieve and store them into variables wherein the designer or the project manager no longer has to request for you to change the values. They can change them, themselves. For example, if you have a war game that has a “Bullet Speed” in it, you can just place a column in the spreadsheet and the designers can enter desired values themselves. A self-documenting and a better way to interact with other development teams.

Before we start, there are certain stuffs that you need to setup and download:

1. Firefox and Firebug, which enables us to determine the page header’s request and response values. Once Firebug is installed in Firefox, press F12, go to the “Net” tab and select “Enabled”

2. XAMPP (with cURL enabled) for Windows, which enables us to create a crossdomain policy file that gives us privileges to write into Google’s spreadsheets

3. A Google account to create our spreadsheets

Setting up the spreadsheet

Login to Google and create a spreadsheet. In the empty spreadsheet, search for the phrase “Public on the web”, click “Change”, select “Public on the web” and check “Allow anyone to edit (no sign-in required)”. Save and close the dialog.

Click Form > Create a form. A popup window will appear.

In the Question Title, remove the word “Sample Question 1” and enter the word “BulletSpeed” and click Done.

Remove “Sample Question 2” by clicking its Trash icon.

Click Save.

Before closing the popup window, take note of the formKey value below the window and save it. This value is required when we submit values from Flash to the spreadsheet.

Close the popup window.

By the time the popup window closes, the spreadsheet creates a “Timestamp” and “BulletSpeed” columns in rows A1 and B1, respectively. Timestamp is automatically created by the spreadsheet, which tracks changes in the spread.

Leave the spread like that for now and proceed to setting up the PHP directory and crossdomain proxy file.

Setting up a project directory and crossdomain proxy file using PHP’s cURL

In your XAMPP’s HTDocs directory, create a new directory called WarGame. Inside that directory, create a php file called crossdomain_proxy.php and paste this code:

<?php
	/* Only proxy to URLs matching this */
	$sProxyUrlRegex = '#^http://spreadsheets.google.com/#';
	/**
	* Make a simple status/error page.
	*/
	function showErrorStatus ($iCode=500, $sText='Internal Server Error')
	{
		header("HTTP/1.1 $iCode $sText");
		header("Status: $iCode $sText");
		echo "<html><head><title>Oops</title></head><body><h1>" . $sText . "</h1></body></html>";
	}

	/**
	* Replacement for curl_setopt which only exists in PHP>=5.1.3
	*
	* @param resource $rCh
	* @param array $aCurlOpt
	*/
	function local_curl_setopt_array ($rCh, $aCurlOpt)
	{
		foreach ($aCurlOpt as $sKey=>$sVal) {
			if (!curl_setopt($rCh, $sKey, $sVal)) {
				return false;
			}
		}
		return true;
	}

	/* Set cURL options */
	function init (){
		$aCurlOpt = array (
			CURLOPT_FAILONERROR		=> true, // Fail on non-200 responses
			CURLOPT_POST			=> true,
			CURLOPT_RETURNTRANSFER	=> true,
			CURLOPT_TIMEOUT			=> 60,
			CURLOPT_HEADER			=> false, // Don't include header in response
			CURLOPT_USERAGENT		=> 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)',
			CURLOPT_FOLLOWLOCATION	=> true,
			CURLOPT_MAXREDIRS		=> 5
		);

		/* Check URL is specified */
		if (!isset($_POST['_url']) || empty($_POST['_url'])) {
			showErrorStatus(400, 'No URL specified');
			exit;
		}

		/* Set cURL URL and remove from POST before forwarding */
		$sCurlUrl = $_POST['url'];
		unset($_POST['url']);

		/* Make POST string */
		$aPostData = array();
		foreach ($_POST as $sKey=>$sVal) {
			$sKey = str_replace('_', '.', $sKey); // Revert PHP conversion nonsense
			$aPostData[] = urlencode($sKey) . '=' . urlencode($sVal);
		}

		$sPostData = join('&', $aPostData);

		/* Do cURL stuff */
		if (false == preg_match($sProxyUrlRegex, $sCurlUrl)) {
			showErrorStatus(401, 'Naughty, you can\'t proxy to ' . $sCurlUrl);
		} else if (!$rCh = curl_init($sCurlUrl)) {
			showErrorStatus(NULL, 'Couldn\'t initialize cURL to ' . $sCurlUrl);
		} else if (false == local_curl_setopt_array($rCh, $aCurlOpt)) {
			showErrorStatus(NULL, 'Couldn\'t set cURL options');
		} else if (false == curl_setopt($rCh, CURLOPT_POSTFIELDS, $sPostData)) {
			showErrorStatus(NULL, 'Couldn\'t set POST vars');
		} else if (false == ($sResponse = curl_exec($rCh))) {
			showErrorStatus(NULL, 'Response was false');
		} else {
			/* All good */
			echo $sResponse;
		}
	}
	//
	init();
	exit;
?>

Coding the AS3

function storeVariables():void
{
	var loader:URLLoader = new URLLoader();
	var variables:URLVariables = new URLVariables();
	variables["entry.0.single"] = "hello world";
	var key = "0AlmPB8Zwh3pZdDBjMzY5TzJjZnhTSmFmbmlyVkJXNWc";
	variables["url"] = "http://spreadsheets.google.com/formResponse?key="+key;
	//Call the proxy page passing all the variables above.
	var request:URLRequest = new URLRequest("crossdomain_proxy.php");
	request.data = variables;
	request.method = URLRequestMethod.POST;
	loader.addEventListener(Event.COMPLETE, function(e:Event):void{
		trace(e.target.data);
	});
	loader.load(request);
}

Proving the spreadsheet’s field naming convention

You may be wondering, how did “entry.0.single” get in there? I myself isn’t quite sure as to why Google named it that way. However, to determine how I got this property, I still use Firebug.

Go to the Google spreadsheet…

Tracking the changes by watching the Cache

A simple note whenever you’re trying to modify any of the .php or .as files, always clear the browser’s cache. To prove that the .php file is cached, go to the method “showErrorStatus” in “crossdomain_proxy.php”, try deleting a simple greater-than symbol in the last portion of the echoed string and determine its value in the Firebug’s “Net” tab.

echo “<html><head><title>Oops</title></head><body><h1>$sText</h1></body></html”;

Refresh your browser without clearing the cache and launch Firebug.

Locate “POST crossdomain_proxy.php” and go the the “Response” tab. If the output is not the same on the above, then your Flash page is being cached by the browser.

There you have it! Storing values to the Google spreadsheet using AS3. You may want to proceed in Retrieving Values from Google Spreadsheets using AS3 to continue on the second part of this post.

Download

FlashToGoogleSpreadsheet.zip

No really, what’s the use of AS3 Namespace? —

No really, what’s the use of AS3 Namespace?

One of the purpose of a namespace in AS3 is not only to provide scoping mechanism but also to avoid name conflicts. Assume you’re going to create a StringUtility class that consists of multiple different methods called “trim” developed by your friends namely Abraham and Thomas.

Create a project folder named NamespaceTest.

Inside that directory, create a namespace file called abraham.as like so:

package{
	public namespace abraham = "Abraham Lincoln";
}

Then create a second namespace file called thomas.as like so:

package{
	public namespace thomas = "Thomas Jefferson";
}

Create a class file called StringUtility.as like so:

package{
    public final class StringUtility{
        public function StringUtility(){
           trace(StringUtility.abraham::trim(""));
        }
        //
        abraham static function trim(value:String):String{
            return "a";
        }
        thomas static function trim(value:String):String{
            return "b";
        }
    }
}

Then create a file called NamespaceTest.fla and place this in the Actions Panel:

trace(StringUtility.thomas::trim(""));

A namespace not only allows you to have a multiple function names in a single class but also provides another scoping feature on how to access a method without making method public. To use the trim methods on another projects, just import the namespace itself, which would deliberately make the method look like public. Assuming that you already know name your packages and set your Flash IDE.

SeparateNamespaceTest.fla is on a separate project directory.

import  abraham;
import thomas;
//
trace(StringUtility.::abraham(""));

If you only plan to use your namespaces internally and directly access your multiple methods, declare your namespaces like so inside the class:

package{
    public final class StringUtility{
        public namespace abraham = "Abraham Lincoln";
        public namespace thomas = "Thomas Jefferson";
        //
        public function StringUtility(){
           StringUtility.abraham::trim("");
        }
        //
        abraham static function trim(value:String):String{
            return "a";
        }
        thomas static function trim(value:String):String{
            return "b";
        }
    }
}

I hope that made it clear for you as to what the basic purpose of a namespace in AS3 is.