Today I gave a talk at dojo.connect about debugging dojo applications. The talk actually covered a few thing that weren’t necessarily “debugging” related, but still had some good tips. Besure to check out the slides!
Speaking at dojo.connect Online Conference

The first dojo.connect online conference is coming up this week February 10-12, 2010. I’ll be giving a talk about Debugging Dojo Applications. I’ll be covering the following:
- Common mistakes and how to avoid them
- Web browser debug tools
- Built-in Dojo debugging tools
- Methods for finding and fixing syntax issues or malformed data
- Writing tests to trap specific bugs
There is still room left, so head over to http://www.widespreadconferences.com/ and sign up today!
I saw a really neat lightweight lightbox a while ago and decided to make a version for Dojo. I’ve dubbed this version the LightboxNano because it is under 2K over the wire after a build and being gzip’d.
The LightboxNano is not a dijit._Widget. It is designed to be very lightweight and only requires Dojo core and dojo.fx. The LightboxNano was coded for Dojo 1.2, but should work with past versions with little or no modifications.
Features
For being so small, it packs a handful of features:
- Automatically resizes large images to fit in browser window.
- Preloads the larger image to make loading faster.
- Keyboard accessible.
- Instantly makes your website awesome.
Example
The LightboxNano is really easy to use. Here’s an example:
<script src="/path/to/dojo.js" type="text/javascript"></script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dojox.image.LightboxNano");
</script>
<a dojoType="dojox.image.LightboxNano" href="/path/to/large/image.jpg">
<img src="/path/to/small/image.jpg">
</a>
To see it in action, click the image below:
Styling
When the LightboxNano is created, it adds two <div> tags inside the anchor tag: one for the enlarge icon and one for the loading icon. For example, you can define styles like this:
a:hover .dojoxEnlarge {
display: block !important;
}
.dojoxEnlarge {
background: url(images/enlarge.png) no-repeat 0 0;
top: -5px;
left: -5px;
margin: 0 !important;
width: 16px;
height: 16px;
}
.dojoxLoading {
background: #333 url(images/loading-dark.gif) no-repeat center center;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border: 2px solid #000;
height: 24px;
opacity: 0.8;
filter: alpha(opacity=80);
padding: 6px;
width: 24px;
}
Usage
You can declaratively create the LightboxNano using a dojoType on an <a> or <img> tag. Or you can choose to programatically create the LightboxNano:
<script type="text/javascript">
dojo.addOnLoad(function(){
new LightboxNano({
href: "/path/to/large/image.jpg"
}, "myLink");
});
</script>
<a id="myLink" href="/path/to/large/image.jpg">
<img src="/path/to/small/image.jpg">
</a>
If you want to access it for some reason, you’ll need to use its jsId.
Parameters
href – String
URL to the large image to show in the lightbox.
duration – int
The delay in milliseconds of the LighboxNano open and close animation.
preloadDelay – int
The delay in milliseconds after the LightboxNano is created before preloading the larger image.
File Sizes
The LightboxNano is pretty small, but it relies on several other core Dojo components. If you do a custom Dojo build containing the LightboxNano and dojo.parser, the dojo.js is 96KB. If you enable HTTP compression on the web server, you can get that down to a nice 32KB.
Limitations
- The LightboxNano does not reposition itself when the browser window is resize or scrolled.
- In IE6, drop down select lists bleed through the LighboxNano layer. This can be fixed with an iframe shim. This was skipped to keep the size down and because we all want IE6 to just go away.
Future Improvements
There’s always things that can be done to improve the LightboxNano. Here’s a couple ideas and you’re welcome to make suggestions in the comments.
- Add support for displaying a caption.
- Add support for
dojox.embedto play Flash or Quicktime movies.
Try it Today!
You can download the LightboxNano files here: LightboxNano.tar.gz [594KB]. Soon, the LightboxNano will be in dojox, so you won’t need to install it separately.
You can view the original test page here: http://download.dojotoolkit.org/release-1.3.1/dojo-release-1.3.1/dojox/image/tests/test_LightboxNano.html.
If you end up using the LightboxNano, feel free to link to your site so everyone can see your new awesome website!
CB1, INC. Site Refresh and Kimura Framework Site Launched
Over 2 months in the making and the CB1, INC. website refresh is finally launched. Whoo! The site has been upgraded to Drupal 6, the interface has been cleaned up a bit and lots old content has been removed. Now that the Dojo Toolkit Module is hosted over at Google Code, I removed the documentation.
I’ve also set up a dedicated site for my side project, the Kimura Framework, and it is awesome. Kimura is a PHP powered framework with several out of the box features for things such as content management, blogs, forums, and more. It is still heavily under development and I’ve got a bit of cleanup on the existing code. For more information, visit http://www.kimuraframework.org.
I still have hopes for building a Comet server. I’ve written some great code for it, but I never got around to dumping into a public repo. That will be my goal for next year.
Virtual Hosts and Wildcard SSL Certificates with Apache 2.2
It is possible with Apache to host multiple websites with a single static IP address. However, you can only have one SSL certificate per static IP. This post describes setting up Apache with multiple secure virtual hosts and a single self-signed wildcard certificate. To learn about creating the certificate, check out Creating Self-Signed Certs on Apache 2.2.
Here’s a table of our example hosts:
| Domain | Force SSL? | Trusted? | Valid Domain? |
|---|---|---|---|
| www.site-a.com | No | No | Yes |
| secure.site-a.com | Yes | No | Yes |
| test.site-a.com | No | No | Yes |
| www.site-b.com | No | No | No |
| secure.site-b.com | Yes | No | No |
In order for the certificate to be trusted, you need to obtain the certificate from a trusted certificate authority. Since we are using self-signed certificates, they are not trusted and we will see some warning messages. The data will still be encrypted.
For this to work, we are going to use name-based virtual hosts.
The name and location of Apache’s configuration files vary based on which platform you use. This post assumes Ubuntu in which case the configuration files in the /etc/apache2 directory.
For starters, we need to tell Apache which ports to listen on by editing the file /etc/apache2/ports.conf.
Listen 80
<IfModule mod_ssl.c>
Listen 443
</IfModule>
Next we need to add our virtual hosts. They are kept in the /etc/apache2/sites-available directory. For organization purposes, separate your sites into separate config files, then run a2ensite for each site to generate a symbolic link in the /etc/apache2/sites-enabled directory.
Here is the configuration for the sites:
NameVirtualHost 192.168.1.200:80 NameVirtualHost 192.168.1.200:443 # http://site-a.com # https://site-a.com -- Throws warning because cert is for *.site-a.com... see bottom # http://www.site-a.com # https://www.site-a.com <VirtualHost 192.168.1.200:80 192.168.1.200:443> ServerName site-a.com ServerAlias www.site-a.com DocumentRoot /path/to/www.site-a # Note: SSL settings only need to be defined once! SSLEngine On SSLCertificateFile /path/to/thecert.crt SSLCertificateKeyFile /path/to/thecert.key </VirtualHost> # Not SSL, redirect to https://secure.site-a.com <VirtualHost 192.168.1.200:80> ServerName secure.site-a.com Redirect / https://secure.site-a.com/ </VirtualHost> # https://secure.site-a.com <VirtualHost 192.168.1.200:443> ServerName secure.site-a.com DocumentRoot /path/to/secure.site-a </VirtualHost> # http://test.site-a.com # https://test.site-a.com <VirtualHost 192.168.1.200:80 192.168.1.200:443> ServerName test.site-a.com DocumentRoot /path/to/test.site-a </VirtualHost> # http://www.site-b.com # https://www.site-b.com -- Throws warning because cert is for *.site-a.com <VirtualHost 192.168.1.200:80 192.168.1.200:443> ServerName www.site-b.com DocumentRoot /path/to/secure.site-b </VirtualHost> # Not SSL, redirect to https://secure.site-b.com <VirtualHost *:80> ServerName secure.site-b.com Redirect / https://secure.site-b.com/ </VirtualHost> # https://secure.site-b.com -- Throws warning because cert is for *.site-a.com <VirtualHost 192.168.1.200:443> ServerName secure.site-b.com DocumentRoot /path/to/secure.site-b </VirtualHost>
Despite having a wildcard certificate for *.site-a.com, you will get an invalid domain message when you don’t supply the subdomain (i.e. http://site-a.com). The only way I know of to solve this is with 2 static IPs and 2 certs where one cert is for site-a.com and the other is for *.site-a.com.
I’m using 192.168.1.200 for the server’s IP address behind the firewall. You could try using * instead of 192.168.1.200 in the <VirtualHost> blocks, but I haven’t tested this. Leave a comment if you happen to test this.
Assuming the stars have aligned, you should have several secured virtual hosts!
Integrating Google AJAX Search API on Your Website
I recently had to integrate search capabilities into a website I worked on. I chose to use the Google AJAX Search API. I needed the form field display to be very customized and clean. I do leverage the Dojo Javascript Toolkit to help with the boilerplate DOM and event stuff.

The first thing you need to do is sign up for a new key:
Once you get the key, you’re ready to start coding your own custom search form.
Begin by adding the the following to your <head> tag:
<script src="http://www.google.com/jsapi?key=<KEY GOES HERE>" type="text/javascript"></script>
Since my example uses Dojo, you need to include Dojo for my code to work. If you don’t already use Dojo, you can simply include it by adding the following to the <head>:
<script src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js" type="text/javascript"></script>
Next we need to add our code that performs the search. First tell the Google API to load the search API:
<script type="text/javascript">
google.load("search", "1", {"nocss":true,"nooldnames":true});
Next we need to initialize our page when the page loads:
dojo.addOnLoad(function(){
var results = dojo.byId("results");
var cursor = dojo.byId("cursor");
var ws = new google.search.WebSearch();
ws.setNoHtmlGeneration();
Be sure to set the domain you want to search. The LARGE_RESULTSET will return the maximum number of results Google allows, which is usually 8 results.
ws.setSiteRestriction("example.com");
ws.setResultSetSize(google.search.Search.LARGE_RESULTSET);
Next we need to tell the WebSearch object the function we want called after the results are returned. The 3rd argument to setSearchCompleteCallback() is an array with a reference to our WebSearch instance. This array gets passed to our callback function when the callback is fired.
ws.setSearchCompleteCallback(null, function(w){
if(!w.results){return;}
results.innerHTML = "";
cursor.innerHTML = "";
var d = dojo.doc.createElement("div");
results.appendChild(d);
for(var i=0,len=w.results.length; i<len; i++){
var r = w.results[i];
Our WebSearch object has a createResultHtml() function that creates an the DOM nodes of our result, but it adds a bunch of crap and forces the result to open in a new window. So, to fix that, we use Dojo to go clean up the nodes before adding them to the page.
w.createResultHtml(r);
if(r.html){
var n = r.html.cloneNode(true);
dojo.query(".gs-visibleUrl", n).forEach(function(p){
p.parentNode.removeChild(p);
});
dojo.query(".gs-watermark", n).forEach(function(p){
p.parentNode.removeChild(p);
});
dojo.query("a", n).forEach(function(p){
if(p.getAttribute("target")){
p.removeAttribute("target");
}
});
results.appendChild(n);
}
}
if(w.cursor){
var cn = dojo.doc.createElement("div");
cn.className = "gsc-cursor";
var label = dojo.doc.createElement("div");
label.innerHTML = "Pages:";
label.className = "label";
cn.appendChild(label);
for(var i=0, len=w.cursor.pages.length; i<len; i++){
var pn = dojo.doc.createElement("div");
pn.className = "gsc-cursor-page";
if(i == w.cursor.currentPageIndex){
pn.className += " gsc-cursor-current-page";
}
pn.innerHTML = w.cursor.pages[i].label;
pn.idx = i;
dojo.connect(pn, "onclick", function(evt){
ws.gotoPage(evt.target.idx);
});
cn.appendChild(pn);
}
cursor.appendChild(cn);
}
}, [ws]);
If you want to execute a search when the page loads, then you can fire execute() with the criteria.
ws.execute("");
Finally we need to wire up the search button and form. We don’t care about the event details, we just want to execute the search for the current value in the search criteria field.
function query(evt){
dojo.stopEvent(evt);
ws.execute(dojo.trim(dojo.byId("searchCriteria").value));
}
dojo.connect(dojo.byId("searchButton"), "onclick", query);
dojo.connect(dojo.byId("searchForm"), "onsubmit", query);
});
</script>
In the body of your page, add the following markup to define the form:
<div>
<form id="searchForm">
<input type="text" autocomplete="off" id="searchCriteria" value=""/>
<a href="#" id="searchButton"><span>Search</span></a>
</form>
</div>
<div id="results"></div>
<div id="cursor"></div>
For clarity, I removed all styling. One thing you’ll notice is the “Search” button is an <a> tag instead of an <input> tag so that it could be more easily be styled.
Aside from our text field and search button, there are some CSS styles you can tweak for the search results and pagination:
<style type="text/css">
.gs-result{padding:6px 0;}
.gsc-cursor{display:inline;padding:10px 0;}
.gsc-cursor .label{color:#000;display:inline;font-weight:bold;margin-right:8px;}
.gsc-cursor-page{cursor:pointer;color:#00F;display:inline;margin-right:8px;
text-decoration:underline;}
.gsc-cursor-current-page{color:#000;font-weight:bold;text-decoration:none;}
</style>
That’s all there is to it. Load the page up and do a search!

Dojo Toolkit Module Moved to Google Code
Tonight I moved the Dojo Toolkit Module to Google Code! The new project URL is:
Why? I simply don’t have the time and have lost the desire to continue work on this module. Who’s going to maintain the module from now on? No idea. This project needs someone who can take the reins and bring this module into the present.
If you are interested, contact me and let me know what your thoughts are for the future of the Dojo Toolkit Module.
Here’s my presentation I gave June 9, 2008, at the Twin Cities MySQL and PHP User Group about my highly available cluster using DRBD and Heartbeat.
I added a few slides and cleaned things up a bit. The presentation went well and we had a lot of good questions.
The MySQL and PHP User Group will be taking some time off over the summer. There will be another meetup mid-summer to come up with some ideas for future meetings.
Installing DRBD 8.2.5 on Ubuntu 8.04 Hardy Heron
DRBD is a block device driver for Linux that allows you to mirror a partition between two servers.
I had a single application server, but whenever a server failure occurred, my websites, Subversion repository, and e-mail would go down. In order to be highly available, I added a second server in the event of a failure.
| app1.cb1inc.com | app2.cb1inc.com |
|---|---|
| AMD Opteron 180 2.4GHz dual-core 4GB RAM DDR 2 x 250GB SATA 7,200 hard drives (Software RAID 1) 2 x Gigabit network cards |
AMD Sempron 2800+ 3GB RAM DDR 250GB IDE 7,200 hard drive 2 x Gigabit network cards |
I don’t have a ton of load, so the second server doesn’t have to be super powerful.
Each machine has 2 network cards: one for public and one for private traffic. The public interfaces connect to my main switch on the 192.168.0.X network. The private interfaces are connected via a crossover cable on the 10.26.210.X network.
Once you have installed Ubuntu 8.04 Server, you need to install some build tools. I don’t know specifically which build packages you need, so I just install a bunch of them and it should work.
apt-get install build-essential binutils cpp gcc autoconf automake1.9 libtool autotools-dev g++ make flex
In theory, that should get all of the development tools installed that you’ll need.
Next we need to get the entire kernel source code. Just the kernel headers won’t cut it. We need to compile the kernel so that it builds some of the scripts needed to compile the DRBD driver. We’ll also install the ncurses library so that menuconfig works.
apt-get install libncurses5-dev linux-source-2.6.24
Then extract the kernel source:
cd /usr/src tar -xvf linux-source-2.6.24.tar.bz2 cd /usr/src/linux-source-2.6.24
Next, lets clean up any unneeded files (which there shouldn’t be any the first time):
make mrproper
Before you can build the kernel, you need to copy your existing kernel build configuration into the kernel source directory:
cp /boot/config-2.6.24-16-server /usr/src/linux-source-2.6.24/.config
Now we run the menuconfig which will read in our kernel build configuration and build some version files. As soon as the GUI appears, just exit immediately. You don’t have to change any of the settings.
make menuconfig
Finally we need to prepare the kernel and compile it. This will take quite some time.
make prepare make
Now that we have the kernel source compiled and ready to go, let’s get the DRBD source.
cd /root wget http://oss.linbit.com/drbd/8.2/drbd-8.2.5.tar.gz tar -xvf drbd-8.2.5.tar.gz cd /root/drbd-8.2.5
We need to build the DRBD driver and specify the path to the kernel source, then install the driver in the /lib path:
make KDIR=/usr/src/linux-source-2.6.24 make install
Once the driver is compiled, we need to move/copy it to the appropriate lib directory:
mv /lib/modules/2.6.24.3/kernel/drivers/block/drbd.ko
/lib/modules/2.6.24-16-server/kernel/drivers/block
Next we need to start the driver and tell Linux to load it the next time it boots:
modprobe drbd echo 'drbd' >> /etc/modules update-rc.d drbd defaults
Now that everything is installed, verify the driver is loaded:
lsmod | grep drbd
It might be a good idea to reboot and make sure it loads.
At this point, you should set up the /etc/drbd.conf, which mine looks like this:
global {
usage-count no;
}
common {
protocol C;
syncer {
rate 30M;
al-extents 1801;
}
startup {
wfc-timeout 0;
degr-wfc-timeout 15;
}
disk {
on-io-error detach;
# fencing resource-and-stonith;
}
net {
sndbuf-size 512k;
timeout 60; # 6 seconds (unit = 0.1 seconds)
connect-int 10; # 10 seconds (unit = 1 second)
ping-int 10; # 10 seconds (unit = 1 second)
ping-timeout 5; # 500 ms (unit = 0.1 seconds)
max-buffers 8000;
max-epoch-size 8000;
cram-hmac-alg "sha1";
shared-secret "secret";
}
}
resource r0 {
on app1 {
disk /dev/md2;
address 10.26.210.10:7788;
device /dev/drbd0;
meta-disk internal;
}
on app2 {
disk /dev/sda3;
address 10.26.210.11:7788;
device /dev/drbd0;
meta-disk internal;
}
}
Notice the disk is different for each machine. The first machine is a software raid (md) while the second is a single drive (sda). Your setup will most likely be /dev/sdaX if you are using SATA or a RAID card, but it could be /dev/hdaX. You can use cfdisk to quickly check your partitions.
With the configuration set, you need to restart/reload DRBD, create the meta disk, and bring the drive up:
/etc/init.d/drbd restart drbdadm create-md r0 drbdadm up r0
At this point you can view the DRBD status:
chris@app1:~$ cat /proc/drbd
version: 8.0.11 (api:86/proto:86)
GIT-hash: b3fe2bdfd3b9f7c2f923186883eb9e2a0d3a5b1b build by phil@mescal, 2008-02-12
0: cs:Connected st:Primary/Secondary ds:UpToDate/UpToDate C r---
ns:221871972 nr:7160 dw:3856764 dr:227396211 al:763 bm:17504 lo:0 pe:0 ua:0 ap:0
resync: used:0/31 hits:13841287 misses:13628 starving:0 dirty:0 changed:13628
act_log: used:0/1801 hits:961638 misses:790 starving:0 dirty:27 changed:763
chris@app1:~$ /etc/init.d/drbd status
drbd driver loaded OK; device status:
version: 8.0.11 (api:86/proto:86)
GIT-hash: b3fe2bdfd3b9f7c2f923186883eb9e2a0d3a5b1b build by phil@mescal, 2008-02-12
m:res cs st ds p mounted fstype
0:r0 Connected Primary/Secondary UpToDate/UpToDate C /mnt/data ext3
You’re now ready to mount /dev/drbd0 and put data on it. There’s still a couple other things you need to do if you plan on using Heartbeat for failover monitoring.
Download the Driver
If you are running an amd64 architecture, you can download the already compiled driver that was built with the steps above. Just to be clear, this was compiled for Ubuntu 8.04 with the 2.6.24-16-server kernel.
Just put the file in the drivers folder and don’t forget to set the proper owner.
wget http://www.cb1inc.com/files/drbd.ko chown root:root drbd.ko mv drbd.ko /lib/modules/2.6.24-16-server/kernel/drivers/block
I hope this is of some help and good luck!
Here’s my presentation I gave May 12, 2008, at the Twin Cities MySQL and PHP User Group about my experience at the 2008 MySQL Conference and Expo.
Thanks to all of those that came. I had a great time!


