February 7, 2009

inspiration

if you're involved in open source at all and haven't seen this, you should - especially if you're just hanging around the edges and not comfortable with diving right in. this was a great inspiration to me to get involved.

February 1, 2009

dereferencing object properties in dojox.grid.DataGrid

i'm working on a demo for Persevere and the interface is basically editable tables that represent the data that is persisted by Persevere. the editable tables are the dojox.grid.DataGrid and the demo is a primitive calendar application.

Persevere supports lazy loading and so if you have an Event object that has a calendar property to indicate which Calendar the event belongs in, that calendar is stored as a reference in the event. similarly, if the calendar has an owner that is a User object, then the owner property will reference a User. these objects that are stored as references have to be loaded before they can be referenced.

in my grid that displays the list of events, i want my columns to show:
  • Calendar - the name of the calendar that the event belongs to
  • Date - the date of the event
  • Event Name - the name/title of the event
  • Description - some extra details about the event


the way i have my Event class, this would mean that the ideal layout for my grid would be something like this:
var layout = [
{width: "20%", name: "Calendar", field: "calendar.name"},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];

unfortunately, the grid (or the store - depending on where you want to post the blame) doesn't support drilling down into the item by using "calendar.name" and so the first iteration of our solution is to use a custom get function.
var getCalendar = function(row, item){
if(!item){
return this.defaultValue;
}
var store = this.grid.store;
var isItem = store && store.isItem(item, true);
if(isItem){
var calendar = store.getValue(item, "calendar");
var name = store.getValue(calendar, "name");
return name;
}
return this.defaultValue;
}
var layout = [
{width: "20%", name: "Calendar", get: getCalendar},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];

this is fine for this one column but what about if this grid had more columns that i wanted to do this to and then what if i had more grids with more columns like this... that's a lot of repeating and we don't like repeating. so, the final iteration of our solution would ultimately be a subclass of dojox.grid.DataGrid that would allow us to use field names like "calendar.name"

i haven't taken this all the way through to a subclass yet but i have gone as far as overriding the default get method in the grid for each instance that i use this in.

WARNING: what i'm about to show here is only valid for reading and extra work probably needs to be done if you want to be able to edit these fields and have them write back to the store properly. if i have the need for it then i'll work on the code for writing back to the store and at that point i feel it would be worth a complete subclass and possibly a request to a dojo committer to include this as part of the grid.

var layout = [
{width: "20%", name: "Calendar", field: "calendar.name"},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];
var grid = new dojox.grid.DataGrid({
structure: layout,
region: 'center',
store: this.store,
get: function(rowIndex, inItem){
var grid = this.grid;
var dereference = function(item, field){
var props = field.concat().split('.');
var val = item;
dojo.forEach(props, function(prop){
val = grid.store.getValue(val, prop);
});
return val;
}
return (!inItem ? this.defaultValue : (!this.field ? this.value : dereference(inItem, this.field)));
}
}, dojo.doc.createElement('div')).placeAt(this);


this code is taken from a custom widget that is a subclass of dijit.layout.BorderContainer that i'm using so take that into consideration when looking at lines like the last line of code that places the grid at this.

something worth noting is that inside the get function, the context is a cell.

to conclude, i'm certainly looking for any improvements to this and help with writing back to the store properly would be appreciated too. ultimately, i would like this functionality to be included in the grid.

January 15, 2009

dojo.data.ItemFileWriteStore

when starting to use the dojo data API, the first store that most people learn to use is the dojo.data.ItemFileReadStore (read only) and it's counterpart dojo.data.ItemFileWriteStore (read & write). out of the box, dojo.data.ItemFileWriteStore (IFWS) doesn't actually have a mechanism to write anything anywhere. everything is in place to write, but the functions that actually do the writing are essentially a no-op and so you have to write your own. this is something that is not clearly obvious when you start to use IFWS and so it needs to be clearly stated. also, here is a simple example of writing to your server URL. NOTE: IFWS is not a very efficient store since ALL the data is written each time the store is saved.

the complete example is available here

here's the javascript:

dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.form.Button");

var fillStore = function(){
dojo.xhrGet({
url: "samfuqua.json",
handleAs: "json",
load: function(data, ioArgs){
dijit.byId('table').setStore(new dojo.data.ItemFileWriteStore({
data: data
}));
dijit.byId('table').store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){
console.log(newFileContentString);
dojo.xhrPost({
url: "yourURL",
postData: dojo.toJson(dojo.fromJson(newFileContentString)), // this unprettifies the string
error: saveFailedCallback,
load: saveCompleteCallback
});
}
}
});
}

dojo.addOnLoad(fillStore);


and here's the html:

<body class="tundra">
<button dojoType="dijit.form.Button" onclick="dijit.byId('table').store.revert();">Revert</button>
<button dojoType="dijit.form.Button" onclick="dijit.byId('table').store.save();">Save</button>
<table id="table" dojoType="dojox.grid.DataGrid">
<thead>
<tr>
<th field="genre">Genre</th>
<th field="author" editable=true>Author</th>
<th field="title">Title</th>
<th field="price">Price</th>
</tr>
</thead>
</table>
</body>


the part to focus in on is this section here:

dijit.byId('table').store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){
console.log(newFileContentString);
dojo.xhrPost({
url: "yourURL",
postData: dojo.toJson(dojo.fromJson(newFileContentString)), // this unprettifies the string
error: saveFailedCallback,
load: saveCompleteCallback
});
}


for IFWS to actually do something, you have to override either the _saveEverything or the _saveCustom functions. in this case, we are overriding _saveEverything. when IFWS calls the _saveEverything function it will pass a saveCompleteCallback, saveFailedCallback and a newFileContentString. conveniently, these are basically what we would need to have to make an xhrPost to our server :)

the xhrPost is fairly straightforward, you provide a url, some data, an error callback and a load (success) callback. the only manipulation we've had to do here is "unprettify" the newFileContentString. when newFileContentString is passed in, it is a "prettified" json string - ie it has tabs, and newlines, etc. to format the string so that it looks "pretty" when it's printed. this is nice if you're writing the text directly to a file but this can cause problems if your server needs to understand the data so to unprettify it, i've converted the json to an object and then back to json again without prettifying it.

this is just a quick look at IFWS and for a more complete look at it, take a look at the docs at dojocampus

November 12, 2008

multiple versions of dojo

so, you've inherited a project and it uses an old version of dojo and you really don't have the time to get up to speed with the changes that have happened in dojo since that release and your project ain't broke so you don't really want to fix it but somebody wants a new feature or noticed a small defect and even if this scenario doesn't completely match your circumstances... for some reason you have a need to be running 2 versions of dojo at the same time. if this is the case then keep reading...

first of all, for the attention impaired, here's a link to what we're working towards http://archive.dojotoolkit.org/nightly/dojotoolkit/dojo/tests/_base/_loader/scope/scope04.html. this page shows dojo 0.4.3 from the aol cdn working beside the latest nightly build of dojo.

i recently helped someone who had a project that used dojo 0.4.3 and according to them, on FF3 the initial dialog markup was being displayed as the page was loading even though there was inline styling that was supposed to keep them hidden. i never confirmed this for myself but that is what they were telling me. in this case, they didn't want to migrate their whole project but they just wanted to fix their dialogs. so, it seemed that the path of least resistance was to run 2 versions of dojo side-by-side and leave most of the code alone and just migrate the dialogs.

the book of dojo already has a section about multiple versions of dojo in a page but it doesn't hurt to have another perspective on it. so let's dig into this. the basic idea is leave the old stuff alone and bring in the new stuff in a way that the old stuff is unaware of.

i'll start at the top of the file and work down explaining each part - css, javascript and html.

css
this file links to 3 different style sheets and the first one and the third one are related to the nightly build version of dojo and the 2nd one is specifically related to the dijit tests.

<link rel="stylesheet" type="text/css" href="../../../../resources/dojo.css">
<link rel="stylesheet" type="text/css" href="../../../../../dijit/tests/css/dijitTests.css">
<link rel="stylesheet" type="text/css" href="../../../../../dijit/themes/tundra/tundra.css">

for the guy i was helping, he didn't want any changes to the styling he already had so the only css he had to add was:

<link rel="stylesheet" type="text/css" href="dojoroot/dijit/themes/dijit.css">

this file has the essential styles for the dialog (and other widgets) to function properly.

javascript
since the ability to use multiple versions of dojo was only introduced in 1.1, if we are using a version that is pre 1.1 then we want to load the old version of dojo first. in theory, if both versions you want to use are 1.1 or later then it may be possible to load either version first. the important thing is the one which is loaded without using custom namespaces will be the one that gets the "dojo" namespace (and "dijit" and "dojox" if that version used them). i would recommend that if both versions are 1.1 or later then it would be a good practice to give each one it's own custom namespace so that there is no confusion about which version of dojo you are referring to.

loading the old version of dojo
you shouldn't need to change anything here if you're working with a pre-existing project. if you don't have djConfig defined then there should be no need to define it at this point.

<script type="text/javascript">
//djConfig for 0.4.3 setup.
djConfig = {
isDebug: true
};
</script>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.4.3/dojo.js"></script>

loading the new version of dojo
now that the old version has used djConfig and loaded itself, it will be safe for us to make changes to djConfig and load in our 2nd version of dojo - making sure to use a custom namespace for this version. the custom namespace is achieved through djConfig.scopeMap. this tells dojo how to map it's namespaces. in the example below "dojo" maps to "dojo10", "dijit" maps to "dijit10" and "dojox" maps to "dojox10". it is worthwhile noting that djConfig.baseUrl should point to the directory containing the dojo.js file - this is not quite obvious from this example.

<script type="text/javascript">
//Need scope map defined in a script block. It will not work as part of the
//djConfig attribute on the script that loads Dojo.
//Also, just adding properties instead of redefining djConfig, since that
//will wipe out djConfig values set up by the 0.4.3 dojo.
djConfig.parseOnLoad = true;
djConfig.baseUrl = "../../../../";
djConfig.scopeMap = [
["dojo", "dojo10"],
["dijit", "dijit10"],
["dojox", "dojox10"]
];
</script>
<script type="text/javascript" src="../../../../dojo.js"></script>


putting this to use
after getting the groundwork laid, now we can do something with this. to use the old version, we just keep doing things like they were being done before. calling dojo.require or using a dojoType tag or calling dojo.byId or dojo.addOnLoad will all use the 0.4.3 version of dojo since the 0.4.3 version took the "dojo" namespace. to use the new version, we call dojo10.require or use dojo10Type tags, etc. this is because we mapped the newer version of dojo to use the "dojo10", "dijit10" and "dojox10" namespaces. refer to the book of dojo for limitations to this feature.

<script type="text/javascript">
dojo.require("dojo.widget.DropdownDatePicker");
dojo10.require("dijit._Calendar");
dojo10.require("dojo.date.locale");
dojo10.require("dojo.parser"); // scan page for widgets

dojo.addOnLoad(function(){
dojo.byId("output043").innerHTML = dojo.version.toString();
});
dojo10.addOnLoad(function(){
dojo.byId("output10").innerHTML = dojo10.version.toString();
});

function myHandler(id,newValue){
console.debug("onChange for id = " + id + ", value: " + newValue);
}

function foobar(){
dojo.byId("typeOut").innerHTML = (typeof dojo.addClass);
}
setTimeout(foobar, 2000);
</script>


html
finally, we get to the html. the most important thing to notice here is the tags - "dojoType" vs "dojo10Type". you will also notice that class="tundra" was put on the parent of the dijit._Calendar so that the tundra theme would be applied to that widget.

<h1>Multiversion Dojo: 0.4.3 and 1.0</h1>

<p><b>NOTE: This test only works with a built version of Dojo</b></p>

<p>This page loads Dojo 0.4.3 and Dojo 1.0.</p>

<p>Dojo 0.4.3 version: <span id="output043"></span></p>

<p>Dojo 1.0 version: <span id="output10"></span></p>

<p><b>dojo.addClass should be undefined:</b> <span id="typeOut"></span></p>

<p>
<input dojoType="dropdowndatepicker" value="2006-10-31"
containerToggle="wipe" containerToggleDuration="300" >
</p>

<p class="tundra">
<input id="calendar1" dojo10Type="dijit._Calendar" onChange="myHandler(this.id,arguments[0])">
</p>

this was a problem i helped someone solve recently and thought it was worth blogging. feel free to leave comments - good or bad.

October 16, 2008

alternative syntax highlighting

an alternative way to do syntax highlighting is using the dojo toolkit. dojo toolkit is a javascript library.

add this code to your template in the same place described in the previous post.


<link href='http://o.aolcdn.com/dojo/1.2/dojox/highlight/resources/highlight.css' rel='stylesheet'/>
<link href='http://o.aolcdn.com/dojo/1.2/dojox/highlight/resources/pygments/default.css' rel='stylesheet'/>
<script djConfig='parseOnLoad: true' src='http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js'/>
<script>
dojo.require("dojox.highlight");
dojo.require("dojox.highlight.languages._all");
dojo.require("dojox.highlight.languages.pygments._www");
dojo.addOnLoad(function() {
dojo.query("code").forEach(dojox.highlight.init);
});
</script>

put all your code inside <pre> and <code> blocks and use a <code class="lang"> to define the language for the code block. valid values for lang are:
  • cpp
  • css
  • delphi
  • django
  • html
  • javascript
  • python
  • sql
  • xml
unfortunately, since php is not supported, i probably won't end up using this option. also, feel free to suggest any other options that i might like better than the 2 i've found so far.

October 9, 2008

first post - syntax highlighting

there are probably hundreds of posts about this already but here's another one for your pleasure...

to get syntax highlighting in your blogger, click on the 'copy to clipboard' link below and then go to Layout->Edit HTML and paste the code right before the </body> tag at the end of the template:


<link href='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Styles/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>

<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shCore.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushCSharp.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushPhp.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushJScript.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushJava.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushVb.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushSql.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushXml.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushDelphi.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushPython.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushRuby.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushCss.js'/>
<script class='javascript' src='http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/shBrushCpp.js'/>
<script class='javascript'>
//<![CDATA[
dp.SyntaxHighlighter.BloggerMode();

dp.SyntaxHighlighter.ClipboardSwf = 'http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/clipboard.swf';
dp.SyntaxHighlighter.HighlightAll('code');
//]]>
</script>


some posts that i read suggested that you needed to download the SyntaxHighlighter library and host it somewhere but since you can link to the SyntaxHighlighter files directly in the googlecode svn, then you don't need to get your own copy of the files hosted somewhere. i've linked to version 1.5.1 but if you were really daring, you might even link to the trunk!

a little bit of background for this blog
i've been working with the dojo toolkit, zend framework and doctrine for a few months now and just wanted to share some of the things i've learned and also use this blog as a placeholder that i can refer back to also.

a little bit of background about me
i'm an australian who lives in the usa. i moved here in 2000 and i married my wife in august 2007. my wife is pregnant and we have a baby due in january 2009. i went to university from 1993 to 1998 and graduated with a bachelor of electrical engineering and a bachelor of information technology. since moving to america in 2000, i have been touring with bands doing sound. at the moment, i work for tobymac and on the side i'm working on web development. i hope to have a fancy looking web app up and running soon...

stay tuned for further details.