JavaScript Events & Messages, or JEM, is an augmented JavaScript with two new capabilities: event coordination and message passing. These new capabilities closely tie together jQuery and PageBus to make it easier to create Rich Internet Applications (RIA).
JEM focuses on the these three areas:
JEM achieves this by introducing two new simple constructs: the when keyword for event/message coordination and @id for message querying and sending.
There are two ways to add JEM to your Deki pages:
<script type="text/jem"> code blockThe syntax for the first method is simple:
ctor="when(condition) action;"
Conditions allow you to define the event or message you want to handle. When a condition is met, one or more actions will be executed. In JEM, conditions are either the receipt of a PageBus message (with optional tests for data in the message) or a jQuery event (e.g. click) on a DOM object.
Actions define what happens when a condition is met. Actions allow you to produce new messages and manipulate the DOM.
JEM provides shorthand notations for sending messages and accessing DOM elements. The notations make these common operations much shorter and more readable.
@msg() | Send an empty message on channel @msg. |
@msg(data) | Send data on channel @msg. |
@"msg"() | Send an empty message on channel "msg". |
@"msg"(data) | Send data on channel "msg". |
#id.fn() | Select DOM element by id and invoke function fn() on it. |
Type a value in the field and it will be echoed. The first example uses the "ctor" attribute. The second uses a JEM code block.
you typed:
you typed:
Here is the code for the example above:
<p>
<!-- using "ctor" attribute -->
<input type="text" ctor="when($this.keypress) @changed($this.val());" />
<span style="font-size:11px;padding-left:10px">you typed:
<span class="codeword" ctor="when(@changed) $this.text(@changed);"> </span>
</span>
</p>
<p>
<!-- using JEM code block -->
<input type="text" id="my_input" />
<span style="font-size:11px;padding-left:10px">you typed:
<span class="codeword" id="echo"> </span>
</span>
<script type="text/jem">
when(#my_input.keypress) @changed2(Deki.$(this).val());
when(@changed2) #echo.text(@changed2);
</script>
</p>
Conditions are used to define event and message handling logic. This section outlines the different types of conditions that JEM supports including examples of each.
You can use any JEM message as a condition. The following table shows the syntax for message-based conditions. We use the message @msg in all examples and the action $this.show().
| ctor="when(@msg) $this.show()" | Trigger whenever a message is sent. |
| ctor="when(@msg.name == 'Fred') $this.show()" | Trigger when a message is sent with a name field that matches a value. |
| ctor="when(@msg.name != 'Fred') $this.show()" | Trigger when a message is sent with a name field that does not match a value. |
| ctor="when(/^a/.test(@msg.name)) $this.show()" | Trigger when a message is sent with a name field that begins with a value. |
| ctor="when(/a$/.test(@msg.name)) $this.show()" | Trigger when a message is sent with a name field that ends with a value. |
| ctor="when(/a/.test(@msg.name)) $this.show()" | Trigger when a message is sent with a name field that contains a value. |
| ctor="when(@msg.count < 100) $this.show()" | Trigger when a message is sent with a count field that is less than a value. |
| ctor="when(@msg.count <= 100) $this.show()" | Trigger when a message is sent with a count field that is less than or equal to a value. |
| ctor="when(@msg.count > 100) $this.show()" | Trigger when a message is sent with a count field that is greater than a value. |
| ctor="when(@msg.count >= 100) $this.show()" | Trigger when a message is sent with a count field that is greater than or equal to a value. |
| ctor="when(!@msg.name) $this.show()" | Trigger when a message is sent without a name field. |
| ctor="when(@msg.count == 1 && @msg.name == 'Fred') $this.show()" | Trigger when all conditions on a message are met. |
| ctor="when(@msg.count > 1 && #my_input.val() == 'one') $this.show()" | Trigger when the conditions on a message are met and the application is in the right state. |
The following example shows how a message can be sent and how actions can be triggered based on the values in the message. Change the values in the inputboxes and click the send message button to trigger different actions.
Here's the code for the example above. Note that this example requires the jQuery script to be installed for the highlight effect.
<pre class="script">jquery.ui()</pre>
<script type="text/javascript">
function animate($this) { $this.effect('highlight', {}, 'fast'); }
</script>
<div style="margin-left: 15px;">
<div style="margin-bottom: 10px;">Name:
<input type="text" value="Fred" id="name" />
Count:
<input type="text" value="100" id="count" />
<input type="button" ctor="when($this.click) @message({ name: #name.val(), count: #count.val() });" value="send message" />
</div>
<div>
<div ctor="when(@message) animate($this);">Message Only</div>
<div ctor="when(@message.name == 'Fred') animate($this);">Name == Fred</div>
<div ctor="when(@message.name != 'Fred') animate($this);">Name != Fred</div>
</div>
<div>
<div ctor="when(/^F/.test(@message.name)) animate($this);">Name starts with F</div>
<div ctor="when(/d$/.test(@message.name)) animate($this);">Name ends with d</div>
<div ctor="when(@message.name == 'Fred' && @message.count == 100) animate($this);">Name == Fred, count == 100</div>
</div>
<div>
<div ctor="when(@message.count <= 100 && /^F/.test(@message.name)) animate($this);">Count <= 100, Name starts with F</div>
<div ctor="when(@message.count <= 50) animate($this);">Count <= 50</div>
<div ctor="when(@message && Deki.$.browser.safari) animate($this);">Your browser is Safari</div>
</div>
</div>
JEM makes it easy to subscribe to jQuery events on DOM elements. The table below shows the syntax that can be used for DOM-based conditions. The complete list of available events can be found on the jQuery events page.
You can capture events for the current DOM element by using the $this prefix. To capture events for arbitrary DOM elements, prefix them with #id, where "id" is the id of the DOM element.
You can retrieve additional information about the event using the $event variable. The $event variable is a jQuery Event object.
| blur | Triggered when an element is blur-ed (i.e., loses focus). Usage: ctor="when($this.blur) $this.hide();" |
| change | Triggered when an element's value changes. Usage: ctor="when($this.change) $this.hide();" |
| click | Triggered when an element is clicked either by a user or by the click action. Usage: ctor="when($this.click) $this.hide();" |
| dblclick | Triggered when an element is double-clicked. Usage: ctor="when($this.dblclick) $this.hide();" |
| error | Triggered when an element received an error event. Usage: ctor="when($this.error) $this.hide();" |
| focus | Triggered when an element receives focus. Usage: ctor="when($this.focus) $this.hide();" |
| keydown | Triggered when a key is pressed down on an element. Usage: ctor="when($this.keydown) $this.hide();" |
| keypress | Triggered when a key is pressed or repeated on an element. Usage: ctor="when($this.keypress) $this.hide();" |
| keyup | Triggered when a key is released on an element. Usage: ctor="when($this.keyup) $this.hide();" |
| load | Triggered when en element receives a load event. Usage: ctor="when($this.load) $this.hide();" |
| mousedown | Triggered when an element receives a mousedown event. Usage: ctor="when($this.mousedown) $this.hide();" |
| mouseover | Triggered when an element receives a mouseover event. Usage: ctor="when($this.mouseover) $this.hide();" |
| mouseout | Triggered when an element receives a mouseout event. Usage: ctor="when($this.mouseout) $this.hide();" |
| mouseup | Triggered when an element receives a mouseup event. Usage: ctor="when($this.mouseup) $this.hide();" |
| resize | Triggered when an element is resized. Usage: ctor="when($this.resize) $this.hide();" |
| scroll | Triggered when an element is scrolled. Usage: ctor="when($this.scroll) $this.hide();" |
| select | Triggered when text of an element is selected. Usage: ctor="when($this.select) $this.hide();" |
| submit | Triggered when a form is submitted. Usage: ctor="when($this.submit) $this.hide();" |
| unload | Triggered when an element receives the unload event. Usage: ctor="when($this.unload) $this.hide();" |
The following example shows how actions can be triggered on the kind of event. Click, double click, or type in the input box to see which actions get triggered.
Here's the code for the example above. Note that this example requires the jQuery script to be installed for the highilght effect.
<pre class="script">jquery.ui()</pre>
<script type="text/javascript">
function animate($this) { $this.effect('highlight', {}, 'fast'); }
</script>
<div style="margin-left: 15px;">
<input type="text" id="bt" />
</div>
<div>
<div ctor="when(#bt.click) animate($this);">click</div>
<div ctor="when(#bt.dblclick) animate($this);">dblclick</div>
<div ctor="when(#bt.change) animate($this);">change</div>
</div>
<div>
<div ctor="when(#bt.mouseover) animate($this);">mouseover</div>
<div ctor="when(#bt.mouseout) animate($this);">mouseout</div>
<div ctor="when(#bt.mousedown) animate($this);">mousedown</div>
</div>
<div>
<div ctor="when(#bt.focus) animate($this);">focus</div>
<div ctor="when(#bt.blur) animate($this);">blur</div>
<div ctor="when(#bt.keypress) animate($this);">keypress</div>
</div>
The $event variable contains details about the value and status of key events. You can interrogate the $event variable to add additional constraints to when an event should be handled.
| altKey | Set to true if the ALT key is down. |
| charCode | Character code for keypress events, otherwise 0. |
| ctrlKey | Set to true if the CTRL key is down. |
| keyCode | Keyboard scan code for keydown/keyup events, otherwise 0. |
| metaKey | Set to true if the META key is down. |
| shiftKey | Set to true if the SHIFT key is down. |
| which | Character code for keypress events. Keyboard code for keydown/keyup events. |
The following example shows how events and event constraints can be combined in the when condition to trigger an action only if specific conditions are met. The first input box will hide the text only if it has the focus and the escape key is pressed. The second input box will only echo letters that are typed in, not digits.
Here's the code for the example above:
<!-- EXAMPLE 1 -->
<div>
<input type="text" id="box1" value="focus and press esc" />
<span style="padding: 10px; margin-left: 10px;"
ctor="when(#box1.keyup && $event.which==27) $this.hide('fast');"
>I'm visible</span>
</div>
<!-- EXAMPLE 2 -->
<div>
<input type="text" id="box2"
ctor="when(
$this.keypress && (
$event.which == 32 ||
($event.which >= 65 && $event.which <= 90) ||
($event.which >= 97 && $event.which <= 122)
)
) @key($event)" />
<div ctor="when(@key) $this.append(Deki.$('<span />')).children(':last').append(document.createTextNode(String.fromCharCode(@key.which)))">Add text - </div>
<div ctor="when(#box.keydown) $this.text('KEYDOWN Key Code: ' + $event.keyCode + ' Char Code: ' + $event.charCode + ' which: ' + $event.which)" />
<div ctor="when(#box.keypress) $this.text('KEYPRESS Key Code: ' + $event.keyCode + ' Char Code: ' + $event.charCode + ' which: ' + $event.which)" />
</div>
If you have more than one condition that should use the same action, you can specify multiple conditions within one expression. Here's the syntax:
<div ctor="when($this.click || #other.click || @msg1 || @msg) action;">
The following example shows how an action can be triggered by different events. Click on the button or the area itself to extend the area.
Here's the code for the example above:
<div>
<input type="button" value="click me" id="b1" />
<input type="button" value="reset" id="b2" />
<div style="width: 200px; text-align: center; background-color: rgb(200, 200, 200);"
ctor="when($this.click || #b1.click) $this.width(500);
when(#b2.click) $this.width(200);"
>click me</div>
</div>
JEM features many client-side actions that make it easier to build interactive web user interfaces. You can send messages, set the value of elements, affect layout, trigger effects, and much more.
An action can be any JEM message. The following table shows the syntax that can be used for message-based actions. We will use the message @msg in all of the examples and the condition $this.click.
| ctor="when($this.click) @msg()" | Send message without a payload. |
| ctor="when($this.click) @msg({ city: 'Atlanta', state: 'GA' })" | Send message with a constant payload. |
| ctor="when($this.click) @msg({ city: #city_input.val() })" | Send message with a computed payload. |
The contents of elements can be modifid using jQuery manipulation functions. By default, $this refers to the element to which to ctor attribute belongs to. Other elements can be selected using #id or jQuery selectors. The following table shows the most common manipulation functions.
| $this.html(value) | Set the html contents of the current element. |
| $this.text(value) | Set the text contents of the current element. |
| $this.append(value) | Append value inside of the current element. |
| $this.prepend(value) | Prepend value inside of the current element. |
| $this.after(value) | Add value after the current element. |
| $this.before(value) | Add value before the current element. |
| $this.wrap(value) | Wrap value around the current element. |
| $this.wrapInner(value) | Wrap value around contents of the current element. |
| $this.empty() | Remove all contents of the current element. |
The styling of elements can be modified using jQuery CSS functions. By default, $this refers to the element to which to ctor attribute belongs to. Other elements can be selected using #id or jQuery selectors. The following table shows the most common CSS functions.
| $this.css(name) | Get the value of a CSS property. |
| $this.css({name: value}) | Set the value of a CSS property. |
| $this.css(values) | Set CSS properties for each key-value pair in the passed in map. |
| $this.offset() | Get the offset of the current element. The returned value contains two keys: left and top. |
| $this.position() | Get the position of the current element. The returned value contains two keys: left and top. |
| $this.scrollTop() | Get the scroll top offset of the current element. |
| $this.scrollTop(value) | Set the scroll top offset. |
| $this.scrollLeft() | Get the scroll left offset of the current element. |
| $this.scrollLeft(value) | Set the scroll left offset. |
| $this.height() | Get the pixel height of the current element. |
| $this.height(value) | Set the CSS height. |
| $this.width() | Get the pixel width of the current element. |
| $this.width(value) | Set the CSS width. |
| $this.innerHeight() | Get the inner height (w/o border, with padding) of the current element. |
| $this.innerWidth() | Get the inner width (w/o border, with padding) of the current element. |
The status of elements can be adjusted using jQuery effect and jQuery event functions. By default, $this refers to the element to which to ctor attribute belongs to. Other elements can be selected using #id or jQuery selectors. The following table shows the most common effect and event functions.
| $this.show(speed) | Reveal current element. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.hide(speed) | Hide current element. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.toggle(speed) | Toggle visibility of current element. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.slideDown(speed) | Reveal current element by adjusting its height. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.slideUp(speed) | Hide current element by adjusting its height. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.slideToggle(speed) | Toggle visibility of current element by adjusting its height. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.fadeIn(speed) | Fade in current element by adjusting the opacity. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.fadeOut(speed) | Fade out current element by adjusting the opacity. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.fadeTo(speed, opacity) | Fade current element to given opacity. Effect is immediate unless speed is specified (one of "slow", "normal", "fast"). |
| $this.blur() | Unfocus (blur) the current element and trigger the event. |
| $this.change() | Trigger the change event. |
| $this.click() | Trigger the click event. |
| $this.dblclick() | Trigger the double click event. |
| $this.error() | Trigger the error event. |
| $this.focus() | Focus the current element and trigger the event. |
| $this.keydown() | Trigger the key down event. |
| $this.keypress() | Trigger the key press event. |
| $this.keyup() | Trigger the key up event. |
| $this.select() | Trigger the select event. |
| $this.submit() | Trigger the submit event. |
DekiScript expressions can be evaluated inline of JEM code using the double curly braces notation ({{ }}). A DekiScript expression is evaluated server-side and its outcome is injected into JEM code. Conversion from DekiScript values to JavaScript values is done by using json.emit implicitly.
The following example shows how an DekiScript expression can be executed server-side and its results be embedded into JEM code. DekiScript values are automatically converted to equivalent JavaScript values using the DekiScript JSON conversion rules.
Here's the code for the example above:
<input type="button" ctor="when($this.click) alert({{user.anonymous}} ? 'Hi stranger!' : 'Hi ' + {{user.name}} + '!');" value="click me" />
This section is for advanced users who want to understand in detail how JEM works. It's not necessary to read this section to use JEM, but it can be useful when investigating unexpected behaviors or simply wanting to use it to its fullest extend.
ctor attributeThe code in the ctor attribute is executed once the DOM is fully loaded. This is accomplished by using the jQuery ready() function. The context of the code is selected using the id attribute. If the DOM element does not have an id attribute, an id attribute is automatically generated. Finally, $this is initialized to the jQuery object for the current DOM element.
Before:
<div id="DOM-ID" ctor="CODE" />
After:
Deki.$(function() {
Deki.$("#DOM-ID").each(function() {
var $this = Deki.$(this);
CODE;
});
});
when statementThe when statement is parsed into two components: the condition and the action. The action part is left untouched, except for converting the shorthand notations. The condition part is decomposed into three parts: events on the current DOM element (only available if the when statement occurs inside a ctor attribute), events for arbitrary DOM elements, and messages.
The condition and action statements are captured as a function, making it easy to refer to the same code from multiple locations.
For events, the condition is updated to check for the event type. The event information is placed in the local $event variable. For messages, the message is read from the channel once and stored inside a $channels variable. This ensure that the same value is seen for all references to the message.
The defined function is then bound exactly once to each DOM element for the listed events and only the listed events. The defined function is also bound to each listed message channel.
Before:
when($this.click || #ITEM.dlblick || #ITEM.change || @msg.field) { ACTION; }
After:
var _my5BlNY2 = function($event) {
$event = $event||{};
var $channels = { "msg" : Deki.query("msg_xyz")||{} };
if(($event.type == "click") || ($event.type == "dblclick") || ($event.type == "change") || $channel["msg"].field) {
ACTION;
}
};
$this.bind("click", _my5BlNY2);
Deki.$("#ITEM").bind("dblclick change", _my5BlNY2);
Deki.subscribe("msg_xyz", this, _my5BlNY2);
The following table shows the correspondence between the shorthand notation and the produced JavaScript code.
| JEM | Plain JavaScript |
@msg() | Deki.publish("msg_xyz") |
@msg(data) | Deki.publish("msg_xyz", data) |
@"msg"() | Deki.publish("msg") |
@"msg"(data) | Deki.publish("msg", data) |
#id.fn() | Deki.$("#id").fn() |
For questions and discussions, please visit DekiScript forums.
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by