Archive for the ‘dojo’ Category.

A Dojo Plugin Pattern

I created a new widget last night based on a couple small functions in base plugd: dojo.twit ... It is a very simple widget with a minimal 2k overhead and illustrates just how truly "plug-able" and flexible Dojo is.

For the horribly impatient: checkout the twitter feed on the sidebar (another degradable example of Dojo) or download the full built twit.js (requires Dojo 1.3)

There are three existing patterns in Dojo that are loosely related:

  • Acting upon a node - eg: dojo.style(node, {}), dojo.anim(node, {})
  • Acting upon a list of nodes - eg: dojo.query(".nodes").style({})
  • Converting a node into a "widget" - eg: new my.Widget({}, node)

The first two go hand-in-hand. Anything you can do to one node you should be able to repeat across a list of nodes in the same way with only a marginal cost for the iteration. The functions to act upon a node follow a very simple API:

 
some.func = function(/* String|DomNode */node, /* Object? */props){
	// summary: do something to node.
	// node: Can be a DOMNode reference, or string ID or a DOMNode to use
	// props: Object-hash of properties and parameters for this function call
	node = dojo.byId(node);
	// now do something to node.
}
 

The functions that act upon a list of nodes are simply mixed into dojo.NodeList:

 
// new in Dojo 1.3.0:
dojo.NodeList.func = dojo.NodeList._adaptAsForEach(some.func);	
 
// old Dojo 1.0 - 1.2.x way:
dojo.extend(dojo.NodeList, {
	func: function(props){
		// summary: run `some.func` for each of the nodes in this list
		return this.forEach(function(n){
			some.func(n, props);
		});
	}
});
 

To use the some.func function either way:

 
// one node
some.func("someId", { arg1: "a" });
// all with class="bar"
dojo.query(".bar").func({ arg1: "a" });
 

Note the above use of a new Dojo 1.3 API _adaptAsForEach ... These are private API's put in place to allow little to no duplication between Dojo's own following of this pattern. The direct forEach method will always work as shown, as they are public APIs, though _adaptAsForEach may change. (I'm taking the gamble they will remain in place in some form for quite some time, and enjoy the savings they provide)

By following this pattern, Dojo is able to provide incredibly fast low-level APIs, then map them directly into the Selector engine (aka: dojo.query) for bulk operations.

The last item ("Widgets") actually has two use cases: programatic and declarative. The programatic v. declarative argument has been going on for a long time, and many people still cry standards-foul when referring to Dojo -- this is entirely unnecessary. The declarative way is the optional method of the "plain old JavaScript" way of instantiating some class[like] object:

 
// programatic:
new dijit.TitlePane({ title:"The Title" }, "nodeOrId");
 
<!-- declarative -->
<div dojoType="dijit.TitlePane" id="someId" title="The Title"></div>
 

The pattern here is simple. The dojoType attribute maps to some object/function (in the example: dijit.TitlePane). The dojo.parser is the only thing in Dojo that understands the dojoType attribute. The Parser's sole job is to:

  • find nodes with a dojoType attribute. eg: dojoType="Thinger"
  • read the attributes on the found nodes, and convert them to an object.
  • call new Thinger(thatObject, foundNode) for each of the nodes

The attributes which are read by the parser aren't entirely arbitrary. For performance reasons, the attributes must be defined in the class you plan to instantiate in order for the parser to find them:

 
dojo.declare("Thinger", null, {
	exampleAttrib:"default-value"
	constructor: function(args, node){
		// generic ctor function
		dojo.mixin(this, args);
		this.node = dojo.byId(node);
	}
})
 

The above "Thinger" example will only attempt to locate an "exampleAttrib" parameter on a passed node:

 
<div dojoType="Thinger" exampleAttrib="overridden" ignoredAttrib="useless"></div>
 

So the widget pattern is nearly identical to the Dojo pattern (only reversed). Again together:

 
	var args = { prop:"b" };
 
	// functions go node, args
	my.func("someNode", args);
	dojo.query("#someNode").func(args);
 
	// widgets go args, node
	new Thinger(args, "someNode");	
<p dojoType="Thinger" prop="b">
 

The reasoning behind the parameter order difference is simple: with widgets the node is optional and with functions the node is explicit. If you are programatically creating a Thinger, you can omit the node (at least in the case of Dijit) as one will be created for you. You must place the node in the DOM manually in this case.

 
	new dijit.TitlePane({ title:"Title", content:"
 
The inner content
 
" }).placeAt(dojo.body());
 

The parser simply passes the node with the dojoType, and no new nodes are created.

All of this explanation is leading into my twit.js code, albeit an incredibly shortened version of the final file. The above use cases exist in Dojo, and are more or less a constant across the toolkit. I wanted to make my little tweeter plugin work for all of them, without any duplication.

First things first: define the dojo.twit() and dojo.NodeList functions as skeletons. We're making this a full module, so we must include the dojo.provide() call.

 
dojo.provide("plugd.twit");
 
dojo.require("dojo.string"); // required for template substitution
dojo.require("plugd.script"); // simple/tiny dojo.io.script replacement
 
(function(d){
 
	// the defaults we can override
	var defaults = {
		"user":"phiggins",
		"count":"7",
		"template":"
<p" + ">${text}</" + "p>"
	}
 
	// the main function
	d.twit = function(node, args){
		node = d.byId(node);
		// mix in the defaults with the args
		var opts = d.mixin({}, defaults, args);
 
		/* the rest of the twit code */
	}
 
	// 1.3+ only. use this.forEach method for 1.0 - 1.2
	d.NodeList.prototype.twit = d.NodeList._adaptAsForEach(d.twit);
 
})(dojo);
 

This, so far, allows us the two functional examples following the Dojo pattern. We need to fill in some code in the core dojo.twit() function to have this complete, but the keys are there:

  • follows the same API pattern of accepting a string ID or domNode
  • accepts an object hash of properties to mix over the defaults
  • and works transparently with dojo.query

Before we move into making this work as a typical widget would, I should point on special thing out. The "template" member in the defaults is using dojo.string.substitute for basic variable replacement. When we fetch the list of tweets for the opts.user, we will apply this template to each data item returned. No extra work on my part was needed:

 
	d.twit = function(node, args){
		node = d.byId(node);
		// mix in the defaults with the args
		var opts = d.mixin({}, defaults, args);
 
		/* the rest of the twit code: in pseudo-code */
		fetch(twitterUrl, function(data){
			dojo.forEach(data, function(tweet){
				// append a new DOM to this node based on the passed template:
				dojo.place(dojo.string.substitute(opts.template, tweet), node)
			})
		})
	}
 

The substitute function simply mixes the tweet data into opts.template string, eg: ${text} becomes whatever tweet.text (the tweet content) is. By passing this result directly to dojo.place we are creating the new dom (this is also new in 1.3 -- not place(), but place() acting as a dom-creation API), and appending it to the node we targeted.

This is awesome until we go to implement the Class-based / dojo.parser version. The template:"" option needs to be an HTML snippet, and it would be incredibly ugly to support a pattern like:

 
<ul dojoType="dojo.Twitter" template="&lt;li&gt;${text}&lt;/li&gt;"></ul>
 

That makes me cringe, and I'm ok with the general use of a dojoType. I needed a better solution. I decided it would be simple enough to use the content of the widget node as the template. For example:

 
<ul dojoType="dojo.Twitter" user="phiggins">
<li>${text}</li>
</ul>
 

To do this we'll need to read the innerHTML at the time of instantiation, then empty the node (to clear out the template):

 
	d.Twitter = function(args, node){
		node = d.byId(node);
		d.twit(node, d.mixin(args, { template: n.innerHTML }));
		d.empty(node);
	}
	// mix in the defaults into the .prototype, making parser recognize them:
	d.extend(d.Twitter, defaults);
 

Now, officially, if dojo.twit does what is is supposed to (and it does), all the following API's are available with almost no repetition. Pick your style:

 
// functions:
dojo.twit("nodeId", { user:"phiggins", template:"
 
${text}
 
" })
dojo.query("ul.foo").twit({ template:"
<li>${text}</li>
 
" });
 
// plain class
new dojo.Twitter({ user:"foo" }, "nodeId")
// and the infamous dojoType
<ul dojoType="dojo.Twitter" user="phiggins">
<li>${text}</li>
</ul>
 

You can see this in action in the sidebar. Entirely degradable. If no JS, no "Twitter" label is shown. Two lines in my global.js trigger the rest:

 
dojo.addOnLoad(function(){
	dojo.style("tweets", "display", "block"); // show the block, js is enabled
	dojo.query("#twitter").twit({ template:"
<li>
 
${text}
</li>
 
 "}); // make it a list
});
 

I love JavaScript, and Dojo 1.3 makes JavaScript that much more fun. Even if you don't agree with the library, and functionality deemed important and common enough for the base dojo.js -- there are years of professional-grade coding living in there, available to learn from and grow upon.

Changing APIs, or: why JavaScript rocks.

Dojo has an exceptionally fast and performant "custom event" system that allows a developer to communicate ambiguously between components with little overhead, and [much like Dojo itself] in a "use as needed" manner: dojo.publish and dojo.subscribe, both provided in base Dojo (dojo.js)

The API's are basic: dojo.subscribe some function to a topic, and dojo.publish some data on that topic. Bonus points for being able to unsubscribe at will:

 
	var h = dojo.subscribe("/dojo/rocks", function(data){
		console.log(data); // "A message";
	});
	dojo.publish("/dojo/rocks", ["A message!"]);
	dojo.unsubscribe(h);
 

The most immediate issue seen is having to pass an array to the publish() call. Dion pointed this out to me shortly after I reviewed the newly-ported-to-Dojo Bespin code base. The "array as the first parameter" bit is defined by the API docs and expected, though in many cases entirely unnecessary. The reason makes absolute sense: dojo.publish converts the passed array into ordered arguments:

 
	dojo.subscribe("/dojo/kicks", function(a, b, c, d){
		console.log(a,b,c,d); // one, 2, three, 4
	});
	dojo.publish("/dojo/kicks", ["one", 2, "three", 4]);
 

Without getting into the details, the reason is the subscribed function is called with the array passed to .apply:

 
	// define dojo.publish:
	dojo.publish = function(topic, args){
		var f = dojo._topics[topic];
		if(f){
			f.apply(f, args||[]);
		}
	}
 

This triggers an empty function bound to the the topic name, and the magic of dojo.connect calls any attached functions, passing the same arguments to each. Because .apply() only accepts an array an empty array is passed in the event the passed args parameter is 'falsey'.

... which makes perfect sense until one of two things happen (which are arguably the most common):

 
	// I want to pass one, single, parameter:
	dojo.publish("topic:foo", "bar");
	// I want to pass a list of parameters in order:
	dojo.publish("topic:foo", "a", "b", "c");
 

The second example makes less sense until you understand that dojo.connect executes all attached functions with the same parameters as the called function:

 
	var obj = {
		bar: function(a,b,c){
			// ... anything.
		}
	}
 
	// this won't work out of the box:
	dojo.connect(obj, "bar", dojo.partial(dojo.publish, "foo"));
	dojo.subscribe("foo", function(a,b,c,d){
		console.log(a,b,c,d); // one, two, three, undefined
	});
 
	obj.bar("one", "two", "three");
 

When obj.bar() is called, the dojo.partial call executes "dojo.publish('foo', ...)", where '...' is equal to any arguments passed to the original obj.bar() call. In the above case, "one", "two", "three" is passed to the bound connected function. The connected function is a curried version of dojo.publish, pushing to the topic "foo".

I wanted to allow for either publish() syntax as mentioned, and not break backwards compatibility. I attempted to do this with:

 
	// _this_ is why JavaScript is cool:
	var old = dojo.publish;
	dojo.publish = function(topic, args){
		return old(topic, dojo.isArray(args) ? args : [args]);
	}
 

But this has the unfortunate (non-back-compat) side effects of a) potentially passing a literal undefined to the subscribed functions 2) is an additional call to some [possibly] expensive functions, and c) generally breaks down for anyone expecting publish() to work as they've been explained previously, and 4) doesn't cover the case of an ambiguous length of arguments.

A quick solution to a) would be:

 
	return old(topic, dojo.isArray(args) ? args : [ args || {} ]);
 

... but you must still check the passed argument before dot-accessing any of it's members.

My solution was a new API, "pub", which simply wraps "dojo.publish" with some fun JavaScript foo:

 
	dojo.pub = function(){
		// summary: wrap `dojo.publish` to allow any number of arguments
		var a = dojo._toArray(arguments);
		return dojo.publish(a.shift(), a);
	}
 

Thought I won't take credit for it, I may have just just invented the most compelling use of an inline .shift() call, ever. Briefly: dojo._toArray, while private, will convert any enumerable object to a real array. Our function "pub" takes any number of arguments, converts them to an array (a), then returns the value from calling dojo.publish() ... what we pass to publish() is the fun:

a.shift() will return the first element of the array (a). This return value is passed to the first argument of the dojo.publish call. The shift() call also modifies the array (a) to be one element shorter, so our next reference to (a) will the reduced list. We're passing this reduced array to the second.

Following this pattern in your own functions is easy, even without Dojo. dojo._toArray does more, but in these examples all we've done is convert 'arguments' to an array. We can likely do this faster (and without Dojo):

 
	var aps = Array.prototype.slice;
	// simple version of `dojo._toArray`
	var toArray(obj){
		return aps.call(obj, 0);
	}
 

`dojo.pub` is implemented in plugd, which will have a new version to coincide with Dojo 1.3.0 to be released shortly. Hopefully the dojo.pub API will take over the existing dojo.publish API in Dojo 2.0, where we get to fix these kinds of things.

update Mar 7 2009: I mistakenly transposed the function as .slice(). It should have been .shift() in this blog. Thanks Wolfram for catching that! PlugD was using .shift() and the unit tests pass.

[more] JavaScript tips

A few weeks ago, Thomas Fuchs (author of script.aculo.us) posted a few "JavaScript tips" that sparked my interest ...

The first, a post on Credit Card validation, and the importance of client-side validation because of potential costs and overhead associated with invalid CC requests to a provider. It immediately reminded me of dojox.validate, Dojo's own in-house validation extension. I was certain there were existing functions to do all the things mentioned in the blog, and it turns out the dojox.validate.creditCard module I ported from Dojo 0.4 is still valid and kicking. It provides methods for:

  • Determinging CC type by #
  • Determinging CC number by type
  • Simple validation of the CCV #
  • Determining validity by length and LUHN of CC #

... and a few other methods. I recently updated it to expose the map of CC types to the regular expressions that match them so users could extend the validation to check their own custom Gift Card / Gift Certificate numbers. For instance, creating a custom type 'my' that starts with 7, made of only numbers, and is 15 characters long:

 
dojo.require("dojox.validate.creditCard");
dojo.mixin(dojox.validate._cardInfo, {
	"my":"7[0-9]{14}"
});
 

The dojox.validate package also has out of the box email, url, and number validation, as well as locale-specific validation for edge cases like validating a Canadian Social Health number. This new extension point for CC's will be available in Dojo 1.3, as it was a private variable previously. I also cleaned up the inline documentation and unit tests for the project, though the modules have been very stable for a long time now it seems.

I setup an incredibly crude test on my Dojo sandbox using the Google CDN and Dojo 1.2.3. It simply prevents a form from submitting if an invalid credit card is detected, styling the input red. There is no endpoint, so no CC#-stealing is taking place ... I promise.

The other JavaScript Tips post was providing a Prototype-based DRY hint. It was a great tip. The example was a "page turner", using the same function for two actions, and currying nextPage and prevPage functions around that. The technique is sound. It made me think of dojo.hitch's lesser-used cousin dojo.partial. To summarize:

 
var turnPage = function(direction){
      // do something if direction is -1 or 1
}
var nextPage = dojo.partial(turnPage, 1);
var prevPage = dojo.partial(turnPage, -1);
 
// goto next page.
nextPage(); // calls turnPage(1);
 
// setup the nav:
dojo.query("a.nextPageLink").onclick(nextPage);
dojo.query("a.prevPageLink").onclick(prevPage);
 

This is all well and good. With dojo.partial I was able to simulate Thomas's example without issue -- but it got me thinking. I noticed in the blog example the 'curry' method was being called directly on the function. eg:

 
var turnPage(dir){ /* doit */ },
    nextPage = turnPage.curry(1),
    prevPage = turnPage.curry(-1);
 

Which means it exists on the native prototype ... Though it is only an opinion, one typically should avoid extending native objects (in this case Function) with methods ... I would go so far as to say it is a bad practice overall. That fact, however, didn't squelch my curiosity as to how this was implemented, so I set out to to write a curry implementation in plain JavaScript that worked like dojo.partial except transparently on Function.prototype.

Initially, I tried using dojo.partial direcly, but couldn't quite get the arg-shifting and scopes right ... I turned to my friend and colleague Eugene Lazutkin to help me write a function that behaved similarly to dojo.partial in that the 'hitched'/'curried' function would be called with the passed args first, as well as any arguments passed to the returned function. eg:

 
var foo = function(a,b,c){ /* code */ };
var bar = dojo.partial(foo, "one");
bar(); // foo("one")
bar("baz"); // foo("one","baz");
 

So we came up with a little function to do just this. Just a few lines of code, actually:

 
// a generic simple currying function. (thanks uhop!)
var curryFunc = function(){
	var me = this, a = Array.prototype.concat.apply([], arguments);
	return function(){
		return me.apply(this, a.concat.apply(a, arguments));
	}
};
 

So we have this function which refers to 'this', generates an Array (via .concat magic) from the passed 'arguments', and returns a function that will call (.apply) the original function (me) with those arguments mixed in with any others. We can see this function working without extending any native objects by just referencing it on an instance:

 
// setup the test:
var bar = function(a, b, c){ console.log('bar', a, b, c); }
bar.partial = curryFunc;
 
// curry bar
var foo = bar.partial(1, 2);
foo(); // echos 'bar', 1, 2, undefined
foo(3); // echos 'bar', 1, 2, 3
 

But adding the 'partial' function to every function we define seems like a lot of extra work. While I'm entirely happy calling dojo.partial(someFunction, "curried","args"), this experiment was to implement this function for all Functions. Easy enough:

 
// don't do this, please.
Function.prototype.curry = function(){
	var me = this, a = Array.prototype.concat.apply([], arguments);
	return function(){
		return me.apply(this, a.concat.apply(a, arguments));
	}
};
 

Which allows you (without using Dojo or Prototype for that matter) to write code like:

 
// create the function and the curried function:
var foo = function(a,b){ console.log(a,b); }
var bar = foo.curry("first");
 
// test:
bar(); // "first", undefined
bar("baz"); // "first", "baz"
 
// create a curried function from a curried function:
var baz = bar.curry("second");
baz(); // "first", "second"
 

Though, upon seeing my test page, Alex immediately scolded me for touching Function.prototype at all, and suggested I set ENUMMERABLE off so it doesn't show up in for( in ) loops. I didn't get that far. I like using dojo.partial, and don't recommend doing this at all.

The article made me think about how Dojo recently re-factored dojo.trim to defer to a native String.trim() function if available ... It seems a great idea, but I was interested to see if anyone had provided String.prototype with this function themselves, like:

 
// don't do this, please. Give all strings a .trim() method.
String.prototype.trim = function(){ return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }
 
// safer, recommended. Make a function to trim a string.
my.trim = function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }
 

Google says quite a few in fact, or at least a lot of people talk about it. So Dojo will defer to their implementation in edge cases (if it exists prior to dojo.js being on a page.) The API for trim() is simple and fairly universal so it shouldn't be too much an issue, but illustrates the complications extending native objects introduces. I'm not sure my Function.prototype.curry() method is 100% API compatible with prototypejs's implementation, so in creating it I am also creating potential interoperability issues. Fairly certain I am not. Short of looking at the code, .curry is to dojo.partial as .bind is to dojo.hitch, so the implementations should behave well together, but I'd like to avoid it none the less.

Back In Business

What an incredibly busy week! I'm finally getting settled in from a painful week of sunshine, technology and drinking in beautiful southern Florida, still suffering slightly from the rapid drop in ambient temperature I experienced exiting the plane in Knoxville [several hours late, no less]. But, I'm here, and back in business -- it's good to be home.

A big thanks to SpringSource for forcing me talk about Dojo at the annual SpringOne America conference. I've been there all week, and the experience was entirely positive: got to meet a whole host of SpringPeople, talk to countless java developers, attend several quality talks, and ran into a number of Dojo contributors both known and anonymous (always nice to have a face to a name). Bonus points for the late-afternoon-beach-parties, endless supply of coffee, and incredible locale selection. I delivered [what I felt to be] a well-received talk about the philosophies and structure behind Dojo from base to util to dojoc/, covered a lot of great material and just had an all-around great experience. Here's to hoping they like my paper for the upcoming SpringOne Europe tour (which ironically is in the same hotel I stayed in during my last visit to Amsterdam, this time three years ago) . Salud.

My slides from the presentation are available on SlideShare.

So a week of hard work on the beaches of Florida also yielded several other awesome events: A night of good Cuban food, drinks and debauchery with another of my oldest friends (which isn't so nice anymore, as we're 'rounding to 30 now', and old is becoming an increasingly sensitive word) was only the beginning:

Start to Finish: A motion to adopt John Resig's newest creation Sizzle as a top-level project to the Dojo Foundation was recently proposed to the mailing list, and passed unanimously 21-nil. The Dojo Toolkit was considering adopting the 'idea of a unified querySelectorAll engine', and suggested the idea to John to ensure "all the things the Dojo Foundation is trying to achieve" were in place -- enabling the Dojo Toolkit to use this new code (as well as anyone else wishing to adopt it). There seem to be a lot of mixed emotions about the overall implications of this -- and I'd like to briefly throw in my take:

There is only one est. Be it tallest, smallest, smartest, or fastest. Until which time we can defer to a fastest native implementation for CSS selectors, we should collaborate on a unified codebase, out in the open, and available to all. This is all just JavaScript, we can offload library-specific redundancy by utilizing js's dynamic nature and create some portable underlying thing (which coincidentally Dojo already did to a degree) -- we are all fighting the same battle, Moore is on our side, and we should shift our performance-related optimizations at what it is we as toolkits choose to do with the nodes given to us by whatever selector the spec/browser provides ...

I respect their decision not to adopt the code-base, though am glad there are now two query engines available with which we might start this period of cooperation I long for. I love the fact they are even able to make such a choice. I welcome John to the "Project Lead Council" (I think is what it's called ... ) of the Dojo Foundation, and look forward to working more closely with yet another incredibly talented coder.

Dojo 1.2.3 Released - We've already quietly released 1.2.1 and 1.2.2, both with minor (though critical stability fixes) changes, but had one large issue needing attention before commiting to pushing the release to the various CDNs. I pushed a small announcement, and the source is available in the usual location. The 1.2 release was a strong one, and I might have secretly tricked Adam Peller into cutting a 1.2.2 a day early so that we would have a 1.2.3 (which has a nice ring to it, as far as version numbers go).

Zend Framework 1.7.1 was released, which I note because I failed to mention the release of 1.7 a few weeks back, which included an update to using Dojo 1.2 and several other fixes. The release of Dojo 1.2.3 should be a transparent update for Zend FrameWork 1.7 / Dojo 1.2 users. Congrats to all the Zend developers! I've installed Git, and have been playing with Matthew O'Phinney's pastebin app in the hopes of putting together a good example of best Dojo practices within Zend Framework ...

dojo.beer() happened in Munich over the weekend. This is the second of the type put on by the Uxebu folk, and it was reportedly a great success. From what was only a handful of German Dojo Developers grew exponentially it seems -- I even had the pleasure of attending virtually via iChat alongside Dylan, though hopefully I'll be able to attend physically sometime soon ... Thanks to Mayflower for hosting the event, and Wolfram, Tobias, and Nikolai for organizing everything! I love seeing this kind of community-driven meet up happen, be it formal or informal.

That in mind -- I'd love to start something similar regionally: A SouthEast-based DDD, just to get together for the face-to-face and have a dojo.beer(), talk shop and otherwise interact. Tennessee alone houses several Dojo developers I know personally, and I think it feasible to coordinate something somewhere reasonably close: Atlanta, GA? Charlotte, NC? -- In the immediate future we may be doing a small Nashville, TN dojo.beer() -- so if you are interested drop me a line and we'll see what we can put together and pick a good central locale.

Dojo: The Missing APIs

In my last article I created a set of small, useful utility functions on top of the Base Dojo APIs for personal consumption. Patterns I'd been following and using in Dojo for some time that Dojo didn't have "official" functions for. Well, I've taken it "a little too far", and created a (if I do say so myself) rather cool mini-open-source-project called plugd.

I've put up a minified version of the most recent "stable" version, and submitted all the code to subversion. Before gzip, the plugin is 2.5k (1.1k after), so I've broken my 1k threshold -- but I think the extra 100 bytes are worth it. So what's new? Where did the bytes comes from!?

Michael Carter of Orbited fame stumbled into #dojo recently asking if Dojo had a simple "create some Dom from text" function like jQuery:

 
$("
<div class='bar'></div>
 
").appendTo("#baz");
 

(note: wordpress doesn't like div tags in code blocks? what's that about?)

And unfortunately, we don't. Dojo's functional approach to things would say: "But dojo.query is a dom selection function, not a dom creation function", which is entirely reasonable. In order to accomplish the above, you'd have to inject magic [bloat?] into dojo.query functionality to determine if the selector was in fact some DOM, and branch internally to return a NodeList from the created single element. This doesn't seem ideal: it changes the meaning of dojo.query. You can already use query this way by passing a node to dojo.query (and dojo.create from the plugd plugin):

 
var n = dojo.create('div');
dojo.query(n).appendTo("#foo");
// or, because people seem to like single lines:
dojo.query(dojo.create('div')).appendTo("#foo")
 

Which is, to me, acceptable in most cases. It does not, however, solve the "complex markup" use case described initially. It would be really convenient if I were able to make calls like:

 
var n = dojo.create("
<div class='bar'>
<p class='baz'>Hi, <span>Dojo</span>
</div>
 
");
dojo.place(n, dojo.body(), "first");
 

So that's where to 100 bytes came from. I added some simple detection to the dojo.create function to either create "just a node from a tag", or a full structure of DOM nodes (with the caveat that it is only allowed a single top-level dom node, such as the div in the example). It could likely be more robust, and work around some cross-browser compatibility issues but I'm aiming for size here and can generally remember to not create invalid markup this way. (Note, there is already a much more robust Dom setter utility in the dojo.html plugin already in the Dojo Toolkit, this syntax is just an extremely lightweight version of that)

Truthfully though, that should not have taken 100 bytes -- there's more. I added other stuff, and rearranged some others:

  • deprecated { bling:true }. bling is a silly word. the djConfig parameter is now simply conflict. eg: djConfig = { conflict:true } will create $, and $().ready() API's, mapping $ to dojo.query and providing the .fn stub for your own plugins.
  • added dojo.toggle(node), matching the API for the $().toggle() method already in place
  • exposed the .val() function in NodeList - it seemed useful
  • added a private _stash method developers can use for advanced chaining, and a public .end() function to get out of it.

NodeList._stash and .end are nice little utilities, too: Sometimes after making a selection of domNodes you want to return a subset of those nodes, or children relative to those nodes, etc, and returning a new NodeList from the last call makes the most sense (so you can continue chaining, except different nodes this time). Even more often, you would like to break out of that list, and back into the original list.

For example, the .create() method returns a "stashed NodeList" because it makes the most sense to return the newly created node(s). wrap() uses the same logic, though provides an optional 'out' by not returning a stashed list by default. By using _stash in the plugin, and utilizing end() we can write Dojo code that looks like:

 
$(document).ready(function(){
  $("a[href^=http://]")
      .addClass("external")
      .wrap("div", true)
           // now we're "in" the new list
           .addClass("aroundExternalLink")
      .end()
      // we're back to the <a>'s, but they have a
<div> around them now
      .onclick(function(e){ ... })
});
 

I've also added DOH unit tests for all base.min.js functions, and added some other experimental plugins like a very simple block overlay based on an earlier article, and a cross-browser position:fixed implementation that is incomplete, but has potential. None of the experimental code has very good in-line documentation though, so use with caution (if at all). base.js is mostly documentation, if you feel like reading. If you are interested in contributing to this mini-project, let me know ... Ohloh is complaining it only has one developer, and I'd love to see a whole collection of these "unofficial plugins" out in the wild.