This repository is private.
All pages are served over SSL and all pushing and pulling is done over SSH.
No one may fork, clone, or view it unless they are added as a member.
Every repository with this icon (
) is private.
Every repository with this icon (
This repository is public.
Anyone may fork, clone, or view it.
Every repository with this icon (
) is public.
Every repository with this icon (
Nick Kallen (author)
Sun Mar 09 18:32:58 -0700 2008
commit 51842de47f71869898e0dc58617caea4b1073f28
tree 536413e24540b072ca3f890dfbd5cd0249e2d96e
parent d79e63f1ef66264971e92bd9115ec5e17d0be672
tree 536413e24540b072ca3f890dfbd5cd0249e2d96e
parent d79e63f1ef66264971e92bd9115ec5e17d0be672
effen /
| name | age | message | |
|---|---|---|---|
| |
.DS_Store | Fri Feb 22 00:11:55 -0800 2008 | [Nick Kallen] |
| |
README | Sun Feb 24 15:06:03 -0800 2008 | [Nick Kallen] |
| |
fn_spec.html | Sun Mar 09 18:32:58 -0700 2008 | [Nick Kallen] |
| |
jquery-1.2.3.js | Fri Feb 22 00:11:55 -0800 2008 | [Nick Kallen] |
| |
jquery.fn.js | Sun Mar 09 18:32:58 -0700 2008 | [Nick Kallen] |
| |
jspec.js | Sat Feb 23 13:36:24 -0800 2008 | [Nick Kallen] |
README
# Effen #
*Effen* is a jQuery plugin that supports Concrete Javascript. Concrete Javascript is a pattern in which the state and
behavior of your domain are attached directly to DOM elements. This differs from MVC, where domain behavior is isolated
from the view. Concrete Javascript is draws inspiration from Self's Morphic UI framework.
## Use ##
To attach behaviors (methods) to DOM nodes, pass a hash to the `fn` function:
$('div').fn({
method1: function(...) {...},
method2: function(...) {...}
})
To invoke a method, pass a string naming the method and any arguments to the `fn` function:
$('div').fn('method1', some, arguments, here);
That's all there is to it. But there are some subtle details to bear in mind:
* While `$('div').fn({...})` will bind the behavior to all divs, calling `$('div').fn('foo')` will only invoke `foo` on
the first `div`. This may be changed in future releases.
* Binding is order-dependent. So, for example, to provide specialized behavior:
$('div').fn({ foo: function() {} });
$('div.special').fn({ foo: function() {} });
The latter takes precedence because it appears last--NOT because the selector is more specific. This may be changed in
future releases.
## An in-depth example of Concrete Programming ##
Suppose we have the following HTML code, representing a Script that has many Scenes. A Scene has a Video description and
many lines of Dialog.
<div class="script" url="/scripts/1">
<ol class="scenes" url="/scripts/1/scenes">
<li class="scene" url="/scripts/1/scenes/1">
<textarea name="video">Interior of Pivotal Labs, Day.</textarea>
<ol class="dialogs" url="/scripts/1/scenes/1/dialogs">
<li class="dialog" url="/scripts/1/scenes/1/dialogs/1">
<input type="text" name="character" value="Nick" />
<input type="text" name="text" value="I like Concrete Programming" />
</li>
<li class="dialog" url="/scripts/1/scenes/1/dialogs/2">
<input type="text" name="character" value="Nathan" />
<input type="text" name="text" value="Me too" />
</li>
</ol>
</li>
</ol>
</div>
### Let's start with some simple CRUD operations. To *create* a scene, we POST to the url: ###
$('.scenes').fn({
create: function() {
$.post($(this).attr('url'), ...);
}
});
We'd like to also create a scene any time someone hits <Ctrl+Enter> within the video textarea.
$('.scene textarea.video').bind('keypress[ctrl+enter]', function() {
...
});
In order to invoke the `create` function on the scene containing the textarea, we'll need a reference from the textarea
to the scene:
$('.scene').each(function(i, scene) {
$(scene).find('*').fn('scene', $(this));
});
Now, binding <Ctrl+Enter> on the textarea to invoke create is as simple as:
$('.scenes textarea.video').bind('keypress[ctrl+enter]', function() {
$(this).fn('scenes').fn('create');
});
### Let's perform a Delete now ###
We'd also like to bind the <Backspace> key from within the Dialog character to delete Dialog elements if both the
character and text are blank. In other words:
$('.dialog input[@name=character]').bind('keypress[backspace]', function() {
$(this).fn('dialog').fn('delete');
});
$('.dialog').fn({
delete: function() {
if ($(this).fn('isBlank'))
$.delete($(this).attr('url'), $(this).remove);
},
isBlank: function() {
return $(this).find('input[@name=character]').val() == "" &&
$(this).find('input[@name=text]').val() == "";
},
});
Using the technique shown in the previous section, the character input is given a reference to the dialog (omitted
here).
### Polymorphism in Concrete Javascript ###
We'd like to add a similar delete feature to Scenes. When the user hits <Backspace> while in the Video textarea it
should delete the Scene--but only if the Video is blank and so are *each* of the Dialog elements within the Scene.
First, let's define `isBlank` on Scene:
$('.scene').fn({
isBlank: function() {
return $(this).fn('video').fn('isBlank') &&
$.all($(this).find('.dialog'), function(dialog) {
return $(dialog).fn('isBlank')
})
}
});
At this point, we may notice that the definition of delete is the same for Scene as for Dialog. Thus,
$('.scene, .dialog').fn({
delete: function() {
if ($(this).fn('isBlank'))
$.delete($(this).attr('url'), $(this).remove);
}
});
So now we have achieved a kind of polymorphism: Both Scenes and Dialogs respond to `isBlank`, though each have differing
implementations. Each has a different `url` attribute and each (given that they are DOM objects) can be `removed`. Given
that they conform to the same interface, we can implement a generic `delete` method for both of them.



