Component.extend(function(element, name, options) {
this.tick = element.data('start') || options.start;
this.inc = 1;
}, {
"sig/start": function() {
this.interval = setInterval(this.ticking.bind(this), 1000);
},
"sig/render": function() {
this.html('Elapsed: ' + this.tick + '');
},
"hub/flip": function() {
this.inc = -this.inc;
},
"sig/stop": function() {
clearInterval(this.interval);
},
"ticking": function() {
this.tick += this.inc;
this.signal('render');
}
});
<special>[/<type>](arguments...)
when the same special appears on the hierarchy of a component, sequential calls will guarantee the extended specials are always called ahead of super ones
var Parent = Component.extend({
"sig/start": function(){
console.log("super");
}});
Parent.create({
"sig/start": function(){
console.log("me");
}}).signal("start");
// "me", "super"
Component.extend({
"sig/render": function() {
this.html('Elapsed: ' + this.tick + '');
},
"hub/flip": function() {
this.inc = -this.inc;
}
});
Component.extend({
"sig/start": function() {
this.on("sig/render", function onRender() {
this.html('Elapsed: ' + this.tick + '');
});
this.on("hub/flip", function onFlip() {
this.inc = -this.inc;
});
}
});
return a promise in handler will block the emitting, and
value that resolves the promise will return to emit
Component.extend({
"sig/start": function() {
return this.emit("login", "troopjs", "******")
.spread(function(login_time) {
console.log("user logged in:", login_time);
});
},
"on/login": function onLogin(username, password) {
return $.ajax({
url: "/auth",
data: {user: username, passwd: password}
}).done(function() {
return +Date.now();
});
}
});
this.emit({
type: 'login',
runner: function runHandlers(event, handlers, args) {
var CALLBACK = "callback", CONTEXT = "context";
var results = [], resultsCount = 0, count = 0;
var next = function(result, skip) {
var candidate;
// Store result if no skip
if (skip !== true) {
results[resultsCount++] = result;
}
// 1. run handlers one after the other;
// 2. pass each handler the original argument;
// 3. call each handler in scope of "context" property;
// 4. collect all return values from handlers;
return (candidate = handlers[count++])
? when(candidate[CALLBACK].apply(candidate[CONTEXT], args), next)
: when.resolve(results);
};
return next(args, true);
}
});
hub subscribers are running with the pipeline runner
prior one may block and influence the next
// from one component
one.publish("log", "info", "some message").spread(function (type, msg) {
// type and message are echo here.
});
// Adding timestamp.
another.subscribe("log", function (type, msg) {
return [type, msg, Date.now()];
});
// Print the log message.
someother.subscribe("log", function (type, msg, timestamp) {
console[type](msg + ' at ' + Date(timestamp));
});
Component.extend({
"displayName": "login-component",
"hub/login": function onLogin(username, password) {
return $.ajax({
url: "/auth",
data: {user: username, passwd: password}
}).done(function() {
return +Date.now();
});
}
});
Component.extend({
"displayName": "user-component",
"sig/start": function onLogin() {
return this.publish("login", "troopjs", "******")
.spread(function(login_time) {
console.log("user logged in:", login_time);
});
}
});
var Foo = Factory(function Constructor() {
// constructing
}, {
"something": function () {
// regular method
},
"something(special)": function () {
// a special method
}
});
var Bar = Foo.extend({
"something(special)": function(){
// override parent special
},
"some/other/special": function () {
// a special method too
}
});
// instantiation
Bar();
new Bar();
Bar.create();
function inherit(Parent, Child) {
var proto;
if(Object.create){
proto = Object.create(Parent.prototype);
}
else {
var Surrogate = function(){};
Surrogate.prototype = Parent.prototype;
proto = new Surrogate;
}
Child.prototype = proto;
}
object composition, flattened/merged properties
function compose(Parent, Child) {
var proto = {};
Object.getOwnPropertyNames(Parent.prototype).forEach(function (prop) {
proto[prop] = Parent.prototype[prop];
});
Child.prototype = proto;
}
a bit more performant
a bit more secure
describe behaviour of element
<ul data-weave="app/widget/dropdown">
<li>troopjs<li>
<li>backbone<li>
<li>angular<li>
</ul>
alter behaviour with least cost
<ul data-weave="app/widget/sidenav">
<li>troopjs<li>
<li>backbone<li>
<li>angular<li>
</ul>
<form class="contact"
data-weave="app/widget/form/contact, app/validation/form">
<fieldset>
<input name="name" placeholder="fullname name"/>
<input data-weave="app/validation/phonenumber" name="phone" placeholder="mobile number"/>
</fieldset>
...
</form>
passing properties
<ul class="choices" data-weave="app/widget/navigation('horizontal')">
...
</ul>
Widget.extend(function($element, name, direction){
if(direction === "horizontal")
$element.style("float", "left");
});
<ul class="choices" data-weave="app/widget/navigation">...</ul>
weave.call($('.foo'));
// OR
$('.foo').weave();
<ul class="choices" data-weave="" data-woven="app/widget/navigation@1">...</ul>
unweave.call($('.foo'));
// OR
$('.foo').unweave();
<ul class="choices" data-weave="app/widget/navigation" data-woven="">...</ul>
$("[data-woven]").woven().then(function(woven){
// iterate over all woven elements.
woven.forEach(function(widgets){
// iterate over the widgets per element.
widgets.forEach(function(widget){
console.log(widget.$element.get(0), require.toUrl(widget.displayName));
});
});
});
$.event.special.destroy = {
"noBubble": true,
"trigger": function () { return false; },
"remove": function onDestroyRemove(handleObj) {
var me = this;
handleObj.handler.call(me, $.Event(handleObj.type));
}
};
Widget.extend({
"dom/destroy": function() {
if (this.phase !== "finalize") {
unweave.call(this[$ELEMENT]);
}
}
});
node + browser