I'm shooting for at least one post a month and I'm on a two month roll. This month's entry is about a feature not available to all. Those of you on IE, Firefox and Opera can move along. Those with Safari and Chrome can hang around. Unfortunately, this cool feature doesn't work with Safari on the iphone, at least not v2.0.
One of the new cool things that comes with HTML5 support is canvas. Using canvas you can write graphics directly to the browser. The chart image was made using canvas and a little chart api I wrote a while back in .net. I converted the api to javascript. I only moved over the bar chart though there are a few other charts still in .net. I'll move them over when I get a chance. I'm thinking of changing up holeytwit to track and cache the top searches over time and use the chart canvas api to chart out the results.
To see iCanChart and canvas in action click on the image or this link.
To get started with canvas, you need to create a ..... you guessed it a ..... canvas tag.
<body onload="draw()">
<canvas id="canvas1" width="500" height="300"></canvas>
</body>
<script type="text/javascript" src="../script/iCanChart.js"></script>
<script type="text/javascript">
function draw(){
var bc = new BarChart;
var dataPoints = new Array();
// build out the data points
for(var i=0;i<10;i ){
dataPoints[i] = new Object();
dataPoints[i].Label = 'day ' (i 1);
dataPoints[i].Value = Math.random()*100;
}
// create the chart
bc.initBarChart('canvas1',300,500,dataPoints,'Hits per Day','hits');
}</script>
function canvasElement(){
return document.getElementById(this.canvasId).getContext('2d');
}
function drawLine(_color,_width,_x1,_y1,_x2,_y2){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.strokeStyle = _color;
ctx.lineWidth = _width;
ctx.moveTo(_x1,_y1);
ctx.lineTo(_x2,_y2);
ctx.stroke();
ctx.closePath();
}
function drawRectangle(_color,_x,_y,_height,_width){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.fillStyle = _color;
ctx.fillRect(_x,_y,_width,_height);
ctx.fill();
ctx.closePath();
}
function drawText(_color,_align,_font,_text,_x,_y){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.font = _font;
ctx.textAlign = _align;
ctx.fillStyle = _color;
ctx.fillText(_text, _x, _y);
ctx.fill();
ctx.closePath();
}
Sorry, I don't have a decent name for this yet.
My wife picked up a new learning math cartridge for my son's DS last week. I'm not sure why we thought we could trick my son into thinking that a DS cartridge that teaches math would be cooler than the upteenmillion Pokemon games he owns. Predictably, he only played it for the first 10 minutes right after he cracked it open. Once he discovered that there was no shooting or spells or attacks involved, it was never seen again. We got the game mainly to build up his speed in answering questions. He gets stressed out when we time him, so we thought it would be better if the DS took the heat.
But all is not lost. One of the games on this cartridge was a grid that you had to complete. The little Japanese scientist in the game says that all the Japanese kids are doing it. It's the latest fad. The game goes like this...You choose the operation and how many cells you want to fill out and go. It's timed and you can compete with up to 15 other people (As we all know 15 is the magical size limit for all Japanese classrooms and of course all Japanese students are issued a gameboy as soon as they enter preschool). I wanted to impress the Japanese with my skills, so this weekend I created a copy of the grid game matrix thing. It's not quite complete, but is good enough to show.
The limitations are:
1. Only multiplication
2. Not working on Safari or iPod (yet...more below)
2a. Only tested on IE, so Patrick it probably won't work on firefox
3. Timer not yet available - Next, should be pretty easy
4. 15 school children cannot play together (interesting, might add and make it 42 so American classmates can play)
5. Always 100 cells6. No score or fireworks when you finish, works like flashcards
To play click mathMatrix or click on the picture.
Update: Safari now works. I reworked the game to store the values of the display in an array of numbers. Originally we were trying to manipulate the display cell values. It works on the iPod as well. I'm going to rework the keypad to make the numbers easier to hit on the ipod.
Updated 3/9:fixed the innerText problem with Firefox and added something at the end
Take a look at the clock that tells time in a not very friendly way. Here's a link to the clock. A description of the code is below.
You can view the page source to see all of the style and script. It should be pretty easy to reduce the size of the blocks and put this in the corner of a page. It might look like one of those stupid animated gifs from the 90's.
The time is 20:11 in the clock to the right.
It's been almost a month since I last made a post. Sorry but I've been busy, mainly with work. I can only spend a few hours a week working on this. I'll make up for it with two posts in one day. The first post is to talk a little more about the asynchronous library I originally created for this blog. It's called SimpleAsynch. I't's been enhanced a bit and I'm now using it on a new application that I'll talk about a later. (Sorry Patrick, I haven't been writing games where things blow up. I promise to do that next.)

1.Construct a request and submit it to the application server through a post action using XMLHttpRequest.
2.On the application server treat the request as any standard post request, parse the parameter list and perform an action, database update, reach out to a web service, etc.
3.When we're ready to return a response, use the asynchronous library to construct a XML document. In the document is a list of action node. Each action node is made up of an action (duh), the action recipient which could be an element id or JavaScript function, and finally a value to either store in the element or pass to a callback.
4.The response is then returned to the browser where the asynchronous library loops through and executes all of the actions.

input - populate an input field, if it doesn't exist create a hidden input field and store the value
div - replace the innerHTML of the div with the value supplied
options - replace all options in a select with the comma delimented option list stored in the value
select - set the selected option in a select to the value supplied
tab - if we use the tabber.js library, we can use this action to activate the tab named in the value parameter
delayCallback - we can use this to execute a callback function on the client, I can't remember why I needed this, I had a reason
callback - use the callback to execute yes a callback, the callback function could build another asynchronous request, we actually do this on the admin piece for the blog and on the project application that I'll talk about later
alert - the alert action will display an alert with the name and value concatenated, useful for reporting errors or for debugging
Now the first version of SimpleAsynch did not construct a XML response, but returned a pipe delimented set of actions, ids, and values. While the delimented response was initially easier to construct and prove out, it was a pain to manage and debug once more complex actions were performed. The project application uses the XML version, while the blog and Salesforce applications use the old delimited version.
The current library accommodates the most important of all browsers, IE6. There are no other browsers. That's wrong. It actually supports all browsers that I have access to IE6, IE7, Firefox 3 on Windows and Linux, Safari 3 on Mac and iPod. I've not downloaded Safari 4 yet nor I have I tried it on Chrome.
Now for some sample code...
The event handler on the browser constructs the request using SimpleAsynch.
function updateProject(){
var request = ""
request = addRequestFunction(request,"updateProject",false);
request = addRequestParameter(request,"projectId",false);
request = addRequestParameter(request,"projectName",false)
request = addRequestParameter(request,"projectStatus",false)
request = addRequestParameter(request,"projectDescription",false)
request = addRequestParameter(request,"projectScheduledStart",false)
request = addRequestParameter(request,"projectScheduledStop",false)
request = addRequestParameter(request,"projectActualStart",false)
request = addRequestParameter(request,"projectActualStop",false)
request = addRequestParameterAndValue(request,"projectAccessList",getAccessList(),false)
request = addCallback(request,"clearProjectForm",true);
postRequest("/updateProject",request);
}
There are a few functions that can be used to add parameters, addRequestFunction, addRequestParameter, addRequestParameterAndValue, and addCallback. The addRequestParameter function will locate the element by the id passed and will populate the value of that element in the parameter list. The addRequestParameterAndValue will let you add parameters to the request for elements that may not exist on the page. The addCallback function will be returned back to the client by the application server in the action list. Finally, postRequest will send the request off to the server.
I won't pull up all of the SimpleAsynch.js code here. You can get it from the site.
On the server you would process the request and build a response. Here's an example where we're trying to populate the screen with the details of a requirement. This example shows the construction of the xml using the SimpleAsynch ResponseXml class. ResponseXml uses a django template to construct the XML for the response.
class LoadRequirement(webapp.RequestHandler):
def post(self):
requirementId = self.request.get('requirementId')
requirements_query = db.GqlQuery("SELECT * FROM Requirement where requirementId = :1",
int(requirementId))
requirements = requirements_query.fetch(1)
rx = ResponseXml()
for requirement in requirements:
rx.addAction("input","requirementIdHidden",str(requirement.requirementId))
rx.addAction("input","requirementNumber",str(requirement.requirementNumber))
rx.addAction("input","listRequirementNumber",str(requirement.requirementNumber))
rx.addAction("input","requirementSubNumber",str(requirement.subNumber))
rx.addAction("input","requirementVersion",str(requirement.version))
rx.addAction("input","requirementMaxVersionHidden",str(requirement.maxVersion))
rx.addAction("input","requirementSummary",requirement.summary)
rx.addAction("input","requirementDescription",requirement.description)
rx.addAction("input","requirementComments",requirement.comments)
rx.addAction("input","requirementStatus",requirement.status)
labelString = ""
for label in requirement.labels:
labelString = label " "
rx.addAction("input","requirementLabels",labelString)
rx.addAction("input","requirementAuthor",str(requirement.requirementAuthor))
rx.addAction("input","requirementEntered",requirement.entered.strftime("%m/%d/%y %H:%M"))
rx.addAction("callback",self.request.get("callback"),"")
if rx.getLength()==0:
rx.addAction("alert","Requirement was not found.","")
self.response.out.write(rx.getXml())