GZoom inducted into Google; becomes DragZoom
My GZoom drag-to-zooom control has been incorporated into Google's GMaps Utility Library. Pamela Fox from Google has been working on it with me for some time, and has done a fantastic job with both the code and the documentation. GZoom is now called DragZoom, and it is available directly off of Google's servers. No need to download your own copy, you can source it from Google just like the GMaps API itself.
Pamela also put together a great example page demonstrating the different instantiation options. Other links for the project:
- Packed source: http://gmaps-utility-library.googlecode.com/svn/trunk/dragzoom/1.0/src/dragzoom_packed.js
- Unpacked source: http://gmaps-utility-library.googlecode.com/svn/trunk/dragzoom/1.0/src/dragzoom_packed.js
- DragZoom project Root
- Documentation: examples and reference
Continue reading "GZoom inducted into Google; becomes DragZoom"»
GZoom enhancements
I added a few things to GZoom today. What's new:
- GZoom now works on maps that move or are resized on the page
- new "Sticky" mode -- can stay in zoom mode for multiple zooms
- more flexible zoom button, can use images, arbitrary text, etc.
- tested and confirmed to work with GMaps new Marker Manager
- you can specify optional callback functions for the following events: buttonClick,dragStart,dragging,dragEnd
See the GZoom original post, the new example, or read on for details of what's updated. Or just grab the new code:
gzoom.js -- right-click --> save asgzoom_uncompressed.js -- right-click --> save as
Read on much more detail . . .
Continue reading "GZoom enhancements"»
An Experiment with Visual and Semantic Relationships
I am very interested in online collaboration tools. This experiment is an early offshoot of some tools and techniques I am developing:
| A visual 'map' as a collaborative workspace. In this case, the goal is to build a visual representation of the Web 2.0 space, including entities (people, concepts, companies) and relationships among those entities. |
|
Feel free to participate! Just three things you need to do:
Try it out at http://web20.earthcode.com |
Continue reading "An Experiment with Visual and Semantic Relationships"»
GZoom uncompressed Source released
Continue reading "GZoom uncompressed Source released"»
jQuery plugin goodness
HighlightFade by Blair Mitchelmore is a very thorough jQuery implementation of the "yellow fade" technique. Options include the type of fade (linear, sinusoidal, exponential), the duration, and a function to call on completion. The plugin weighs in at 3.5K (uncompressed, so it could be made smaller), and should handle all your yellow-fading needs.
tablesorter by Christian Bach is browser-side table sorter (click on a column heading to sort the table accordingly). It's fast (in the demo, sorts 400 rows in about .3 seconds), and it auto-detects the column type to provide the appropriate kind of sorting. For example, it automatically recognizes date vs. text vs. URL contents, and sorts accordingly. For URLs, it disregards the http/https/ftp/file prefix for sorting purposes, which is more likely to give you the sort you actually want. You can also define your own auto-detects, which usually involves 5-10 lines of code. The plugin is about 10.5K uncompressed.
Did you know there are lots more jQuery plugins listed on the wiki at http://proj.jquery.com/plugins? Note that the URL to the wiki will change when jQuery goes 1.0.
Continue reading "jQuery plugin goodness"»
GZoom drag-to-zoom Google Maps control
Update #1: I've posted the uncompressed source code here: gzoom_uncompressed.js . The code is released under an MIT-style license. Enjoy, and let me know if you make enhancements to the code that others might find useful -- I'll try to roll useful enhancements back into the core.
Update #2: version 0.2 released 11/21/06. What's new:
- handles maps that move or are resized on the page
- more flexible activation button, can use images, arbitrary text, etc.
- tested and confirmed to work with GMaps new Marker Manager
- you can specify optional callback functions for the following events: buttonClick,dragStart,dragging,dragEnd
- Here's a new example demonstrating some of the new options and the callbacks.
Update #3: GZoom has been incorporated into Google's own Google Maps Utility Library, and renamed DragZoom. From now on, you should use the code from Google's repository. Get DragZoom here.
Continue reading "GZoom drag-to-zoom Google Maps control"»
Lightbox for Modal Dialogs Redux
Since I last posted on Lightbox for modal dialogs in May, some additional relevant projects have sprung up. This is a roundup of more Lightbox-type modal dialog options. As you go about choosing the perfect script for your needs, Here are some questions to keep in mind:
- What's your content? Do you need to just display images? Or arbitrary HTML? Many scripts support both, and some are tailored around displaying images very easily.
- Is your content remote? You may need to retrieve your content from the server, or the content may already be rendered on the page in a hidden div. I actually find the second case the more common one -- the dialog content is already loaded on the page, and I just want to reveal it in a modal window.
- What are your bandwidth requirements? These scripts vary quite a bit in size. If you're building an internal application you can get away with a bulkier script, but otherwise you might want to look for one of the leaner ones.
- How important is look-and-feel? Some of these scripts offer a slick and customizable look, and others are more spartan. I appreciate the nice looking options, but usually end up using the scripts with less flourish. Your needs may be different.
- What JS Library are you using? Prototype? Moo.fx? Dojo? jQuery? Your choice may narrow the field for modal scripts, since you typically will not include another whole JS library just for the modal dialog functionality.
Continue reading "Lightbox for Modal Dialogs Redux"»
Unobtrusive tabs, Drag & Drop, and more
You may know from my previous posts that I've been moving away from Prototype/Scriptaculous and toward jQuery for JS/AJAX programming, primarily for jQuery's powerful selectors, code-shrinking chaining model, and light weight.
jQuery also has an active development community, working off of a plugin architecture which allows incremental inclusion of only the modules you need. The community is coming out with some awesome plugins, which will further shrink the functional gap between jQuery and Prototype/Scriptaculous. Below are three exceptional ones from the jQuery mailing list:
|
Draggable, sortable lists
All the major functions are here: dragging, sorting, target outlines, drop zone highlighting, drag-time ghosting. |
|
Selectables
Define a rectangle by clicking and dragging, and select the elements which fall within the rectangle -- just like selecting icons on your desktop or a group of photos in Picassa. |
|
Accessible, unobtrusive tabs
Render a nice tabbed structure with one line of Javascript. The markup on which the plugin operates is very natural -- the tabs are rendered from a list of anchor tags, exactly how you would (for example) markup a table of contents. |
Continue reading "Unobtrusive tabs, Drag & Drop, and more"»
Lightbox for modal dialogs
Continue reading "Lightbox for modal dialogs"»
Six key JavaScript techniques
These are six techniques I have found to be invaluable in my JavaScript/AJAX development. If you are using library like Prototype.js, these techniques will help you better understand what's going on behind the scenes. On the other hand, if you don't want to use one of the pre-baked JS libraries (for example because you are building a very lightweight page, and don't want to incur Prototype's 50K download), then these techniques provide a very lightweight toolkit to make your JS coding more efficient.
Continue reading "Six key JavaScript techniques"»
The Scriptaculous/Prototype Conundrum
Now, you need to decide if you will leverage the Prototype and Scriptaculous libraries (P/S hereafter) to take some of the AJAX weight of your back. If you are programming with Ruby on Rails (and you probably are, because that's the way you roll), the decision seems almost made for you: all those nifty helpers for AJAX updaters, drag and drops with callbacks, etc. etc. Rails makes it so easy, why wouldn't you go with the P/S one-two punch?
Continue reading "The Scriptaculous/Prototype Conundrum"»
Yahoo Gets It
Continue reading "Yahoo Gets It"»
JavaScript: Namespaces
- so your functions/objects don't conflict with other peoples code. If you're writing code you intend to distribute, or if you are utilizing anyone else's code, you want to ensure none of the functions have the same name. Javascript will happily replace your setCookie() function with someone else's setCookie() function without so much as mentioning it to you. Problems like this are hard to track down. Namespacing your code is the solution.
- to organize your functions/objects into groupings that make sense. If you were programming in Java, you wouldn't put all your classes in the same package, right?
How to namespace
In Javascript, you can namespace functions by making them properties of an object. Here's an example, which namespaces an augment() function I will utilize later:
var EC = new Object(); // EC is the first-level namespace, short for EarthCode
EC.F = new Object(); // F is the second-level namespace, short for foundation.
// now define the augment function itself
EC.F.augment = function (oSelf, oOther) {
if (oSelf == null) {
oSelf = {};
}
for (var i = 1; i < arguments.length; i++) {
var o = arguments[i];
if (typeof(o) != 'undefined' && o != null) {
for (var j in o) {
oSelf[j] = o[j];
}
}
}
return oSelf;
}
Now, in real usage, you will be placing multiple functions in a namespace. Here's the syntax I use:
// first, declare the two namespaces if they do not already exist
if (EC == null || typeof(EC) != "object") { var EC = new Object();}
if (EC.F == null || typeof(EC.F) != "object") { EC.F = new Object();}
// all the functions in the EC.F namespace will go in this block
EC.F = {
augment: function (oSelf, oOther) {
// contents of the augment function
},
Observer: function () {
// contents of the Observer class
},
thirdFunction: function () {
// function contents
}
} // end of the EC.F namespace
Tips on namespace names
- Make 'em short. You're going to be typing the "full path" to the function a lot in your code, and if it's long and a pain to type, you'll revert back to declaring everything in the root namespace.
- Keep the whole path short. In Java it's common practice to name your packages "com.mycompany.etc.etc.," but this is unnecessary in Javascript. Again, you will be typing the full path a lot, so you want to keep it brief and utilitarian. Most of my namespaces are two packages deep, i.e, "EC.F.Augment()".
- Plan for future growth. For example, if you're creating a pop-up notifier class, do you anticipate making other kinds of notifiers? Is it worthwhile placing it in a "notifier" package?
Continue reading "JavaScript: Namespaces"»
Javascript: Event broadcasting/listening
A key advantage of OO programming is reuse. The more broadly useful an object is, the more likely it is you will need to "wire it in" to your page (or other objects) in unanticipated ways. But, if you want the object to trigger event X when Y happens, how do you do it without changing the actual methods in your object?
To connect up your object up to other objects without violating its integrity, you need event broadcasting/listening.
To make this concrete, imagine a simple object representing a tri-state checkbox. HTML checkboxes are two-state (either checked or unchecked), and you need one with three states (checked, unchecked, and grayed out). You will program this as an image which responds to onClick events by cycling the image source through a series of three graphics. So far so good?
What do you want to invoke when the checkbox object changes state? If you're programming it in a non-reusable way, you can hard-code the action directly into the onclick event: if (state==checked) do X; if (state==grayed_out) do y; etc. If you're programming it as a reusable component, you need a more flexible solution.
First, in psuedo-code, for the onClick event:
// PSUEDO CODE!
method onClick() {
if (state == unchecked) {
update graphic src to "checked";
broadcast "checked" event;
} else if (state == checked) {
update graphic src to "grayed_out";
broadcast "grayed_out" event;
} else if (state == grayed_out) {
update graphic src to "unchecked";
broadcast "unchecked" event;
}
}
Now, your page just needs a way to listen for the events, and you're set -- you can have anything on the page react to an event in the checkbox instance, without changing code inside the checkbox class itself.
How to do it
We're going to create an Observer object, which takes care of both event management (adding and removing events which can be broadcast) and notification (registering functions to be called in the case of a specific event).
/*
Event handler, intended to be used in composition with other objects
*/
function Observer () {
this.add=add;
this.remove=remove;
this.notify=notify;
this.oObservers = new Object();
function add (sEvent, fObserver) {
// must be one of the event types already defined in observers
if (this.oObservers[sEvent] != null) {
this.oObservers[sEvent].add(fObserver);
} else {
alert("attempted to add an observer for an event which doesn't exist. "+
"Event Name is "+sEvent);
}
}
function remove (sEvent, fObserver) {
// must be one of the event types already defined in observers
if (this.oObservers[sEvent] != null) {
this.oObservers[sEvent].remove(fObserver);
} else {
alert("attempted to remove an observer for an event which doesn't exist. "+
"Event Name is "+sEvent);
}
}
function notify(sEvent,oArgs) {
if (this.oObservers[sEvent] != null) {
var aTemp = this.oObservers[sEvent];
for(var i = 0; i < aTemp.length; i++) {
aTemp[i](oArgs);
}
} else {
alert("attempted to invoke an event which doesn't exist. "+
"Event Name is "+sEvent);
}
}
//initialization. If there are any arguments passed,
// add them and the associated arrays
if (arguments.length >0) {
for (i = 0; i< arguments.length; i ++) {
this.oObservers[arguments[i]] = new Array();
}
}
}
Now, the tri-state checkbox can hold a reference to an instance of Observer, register the necessary events, and broadcast as appropriate. For this class, there is only one event (clicked), but you could have as many events as you want -- just separate them in a comma-delimited list.
// in the constructor for the checkbox class:
this.observers = new Observer();
this.observers.oObservers = {
"clicked": new Array()
};
. . . .
// the "click" event for the checkbox
// Pseudo-code except for the Observer.notify() part
onClick() {
if (state == unchecked) {
update graphic src to "checked";
this.observers.notify("clicked",{"sNewState":"checked"});
} else if (state == checked) {
update graphic src to "grayed_out";
this.observers.notify("clicked",{"sNewState":"grayed_out"});
} else if (state == grayed_out) {
update graphic src to "unchecked";
this.observers.notify("clicked",{"sNewState":"unchecked"});
}
}
This code broadcasts the "clicked" event for each of the three states the checkbox can be in. The first argument to the observer.notify() method is the event name -- recall that for this example, only the "clicked event is defined.
The second argument to the observer.notify() method is a set of key-value pairs, allowing you to pass whatever information you want to the listener. The listener can use these values or discard them, but it has to know the key to access them. In this case we pass only one key (sNewState), but we could pass as many as we like in a comma-delimited list inside the {}'s.
Attaching listeners to events
So far, we've prepared an object for event broadcasting. All that's left to do set up listeners. Attaching listeners to the events the object broadcasts will let us utilize the class in unforeseen ways, without changing the internals of the class.
// assuming you have an instance of checkbox called oCheckBox:
oCheckBox.observers.add("clicked",
function(oArgs) {
alert("You clicked it! New state is: "+oArgs.sNewState);
});
The "function(oArgs)" is the event handler. We associated it to an instance of the CheckBox class by calling oCheckbox.observers.add(). We're specifying the "clicked" event in the first argument to the observers.add() method.
oArgs is the object we passed in the observers.notify() method. Recall that we specified just one key, sNewState. sNewState is used in the event handler alert().
Of course, in actual usage the object would be more complex, and it would broadcast more events. Handlers tend to stay relatively simply, however -- their main purpose is "wiring together" different objects, so an event in one triggers an action in another which is appropriate for the current page.
A final note: you can add as many event handlers as you like for any event on any object! Just call [object].observers.add() as many times as needed -- the handlers will be executed in the order you add them!
Event broadcasting/listening can add a huge about of flexibility to your Javascript OO programming, and allow you to utilize classes in diverse environments without changing their internals.
Continue reading "Javascript: Event broadcasting/listening"»
Javascript: Object Orientation
An OO approach is a key part of your Javascript/Ajax programming arsenal. There are multiple ways to program Javascript in an object-oriented way. The Prototype library adds some helper constructs to facilitate object orientation, as do many other Javascript libraries. Regardless of which framework you use (or if you choose to use none at all), an object-oriented approach can make your Ajax programming easier.
Here, I'm demonstrating a basic syntax for OO; you can use this syntax literally, or use it to enhance your understanding of the various framework-based OO approaches available out here.
Defining the Class
In Javascript, classes are defined as functions. The "new" keyword together with the function gives you new instances of the class. Here's an example:
// a Person class
function Person (sName, nAge) {
this.sName = sName;
this.nAge = nAge;
}
// add a method to the class by adding a function to its prototype
Person.prototype.tellMe = function() {
return ("My Name is "+this.sName+". I am "+this.nAge+" years old");
}
That's all there is too it. You have now defined a class (Person), with one method (tellMe).
Using the Object
To use the Person class we just defined:
var oPerson = new Person("Bob", 28);
alert (oPerson.tellMe());
. . . will give us:
Where's the Constructor?
In this example, all the code inside function Person() is the constructor -- this code gets executed each time you use the function to create a new instance of the class, and sets the members of the instance (sName and nAge) to the values passed to the function.There's lots more to OO Javascript programming
You can model inheritance and more with Javascript. A good place to learn more is webreference's Object-Oriented Programming with JavaScript series.
Continue reading "Javascript: Object Orientation"»
Javascript: Adding functionality to built-in classes
String.prototype.trim = function () {
var sNew = this.replace(/^\s*/, ""); // strip whitespace from the beginning of the string
sNew = sNew.replace(/\s*$/, ""); // strip whitespace from the end of the string
return sNew; // return the mutated string
}
You can call the method on any instance of string:
var sString = "a value from an HTML form . . . "; sString = sString.trim();
What you need to know: Prototypes and the "this" keyword
Everything in Javascript has a prototype. Any functions you add to the prototype are automatically available to each and every instance of the class. Accessing the prototype is as easy as specifying [class].prototype. So in the example above, we added a function (trim) to string's prototype: string.prototype.trim=function(){...};
The other key is the "this" keyword. Whenever you are coding a "prototype" function, you need access to the particular instance. The "this" keyword lets you do it. At runtime, the "this" keyword resolves to the actual instance of the object, in this case a String.
Note that this example does not mutate the string on which you called the method. Rather, it returns a new string with whitespace removed. The original instance is left unchanged. If you were to do the following:
var sString = " a value from an HTML form . . . "; sString.trim();
. . . sString would not change. Calling the trim() method would produce a new string, which would then be thrown away because it isn't being assigned to anything.
Continue reading "Javascript: Adding functionality to built-in classes"»
What's in *your* Javascript try-catch blocks?
And try-catch blocks.
Which brings us to the question, what's in your try-catch blocks? What are you doing when you catch an error?. In some cases you want to take some specific action (redirect the user to another page, show a clear user-visible error, tell the user there's no point going on, etc.), but in other cases there's nothing you can really do for the user -- you just want to record the error so you can debug the problem as quickly as possible. If this was Java, PHP, Ruby, C#, or whatever, you would log an error and that would be that.
With Javascript, you can use JSLog, and log an error in your catch block. The code looks like this:
try {
var foo = somethingRisky();
} catch (e) {
jslog.error("Something risky failed! foo="+foo+"; error is "+e.message);
}
Which in turn would give you a JSLog display of:

This works great during development, when you probably have JSLog enabled, and are using it for debugging output anyway. As your code matures into production, you can leave the jslog.error() call there, even as you disable JSLog. If the page is behaving unexpectedly, you can temporarily expose JSLog output (either by switching the config_enabled setting back to true, or by including &enablejslog in the url; see here for details) and see the problem as it was logged in the catch block.
Basically, the problem is that output from the catch block needs someplace to go. JSLog gives it someplace to go, where it can be easily retrieved and inspected, in either a development or production environment. Read more about JSLog here.
Continue reading "What's in *your* Javascript try-catch blocks?"»




