Skip to content

Instantly share code, notes, and snippets.

@kmnk
Created December 11, 2012 08:08
Show Gist options
  • Save kmnk/4256739 to your computer and use it in GitHub Desktop.
Save kmnk/4256739 to your computer and use it in GitHub Desktop.
brook.js by CoffeeScript
var __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
Namespace('brook').define(function(ns) {
var Promise, VERSION, promise;
VERSION = '0.01';
Promise = (function() {
function Promise(next) {
this.next = next || function(next, val) {
return next(val);
};
}
Promise.prototype.concat = function(after) {
var next, _before;
_before = this;
next = function(n, val) {
return _before.subscribe(after.ready(n), val);
};
return new Promise(next);
};
Promise.prototype.bind = function() {
var r, s, _i, _len;
r = this;
for (_i = 0, _len = arguments.length; _i < _len; _i++) {
s = arguments[_i];
s = s instanceof Promise ? s : promise(s);
r = r.concat(s);
}
return r;
};
Promise.prototype.ready = function(n) {
var promise;
promise = this;
return function(val) {
return promise.subscribe(n, val);
};
};
Promise.prototype.run = function(val) {
return this.subscribe(void 0, val);
};
Promise.prototype.subscribe = function(next, val) {
next = next ? next : function() {};
if (!this.errorHandler) return this.next(next, val);
try {
return this.next(next, val);
} catch (e) {
return this.onError(e);
}
};
Promise.prototype.forEach = Promise.subscribe;
Promise.prototype.setErrorHandler = function(promise) {
return this.errorHandler = promise;
};
Promise.prototype.onError = function(e) {
return (this.errorHandler || new Promise()).subscribe(function() {}, e);
};
return Promise;
})();
promise = function(next) {
return new Promise(next);
};
return ns.provide({
promise: promise,
VERSION: VERSION
});
});
Namespace('brook.util').use('brook promise').define(function(ns) {
var EMIT_INTERVAL_MAP, LOCK_MAP, cond, debug, emitInterval, filter, from, lock, mapper, match, now, scatter, stopEmitInterval, takeBy, through, unlock, wait, waitUntil, _arrayWalk;
mapper = function(f) {
return ns.promise(function(next, val) {
return next(f(val));
});
};
through = function(f) {
return ns.promise(function(next, val) {
f(val);
return next(val);
});
};
filter = function(f) {
return ns.promise(function(next, val) {
if (f(val)) return next(val);
});
};
takeBy = function(takeBy) {
var num, queue;
num = 1;
queue = [];
return ns.promise(function(next, val) {
queue.push(val);
if (num++ % takeBy === 0) {
next(queue);
return queue = [];
}
});
};
now = Date.now ? function() {
return Date.now();
} : function() {
return +new Date();
};
_arrayWalk = function(list, func, limit) {
var index, length;
index = 0;
length = list.length;
return (function() {
var startTime;
startTime = now();
while (length > index && limit > (now() - startTime)) {
func(list[index++]);
}
if (length > index) return setTimeout(arguments.callee, 10);
})();
};
scatter = function(limit) {
return ns.promise(function(next, list) {
return _arrayWalk(list, next, limit || 400);
});
};
wait = function(msec) {
var msecFunc;
msecFunc = typeof msec === 'function' ? msec : function() {
return msec;
};
return ns.promise(function(next, val) {
return setTimeout(function() {
return next(val);
}, msecFunc());
});
};
waitUntil = function(f) {
var p;
return p = function(next, val) {
if (f()) return next(val);
return setTimeout(function() {
return p(next, val);
}, 100);
};
};
debug = function(sig) {
sig = sig ? sig : 'debug';
return through(function(val) {
return console.log("" + sig + ":", val);
});
};
cond = function(f, promise) {
return ns.promise(function(next, val) {
if (!f(val)) return next(val);
return promise.subscribe(function(val) {
return next(val);
}, val);
});
};
match = function(dispatchTable, matcher) {
return ns.promise(function(next, val) {
var promise;
if (matcher) promise = dispatchTable[matcher(val)];
if (!promise) {
promise = dispatchTable[val] || dispatchTable.__default__ || ns.promise();
}
return promise.subscribe(function(v) {
return next(v);
}, val);
});
};
LOCK_MAP = {};
unlock = function(name) {
return ns.promise(function(next, val) {
LOCK_MAP[name] = false;
return next(val);
});
};
lock = function(name) {
var tryLock;
tryLock = function(next, val) {
if (!LOCK_MAP[name]) {
LOCK_MAP[name] = true;
return next(val);
}
return setTimeout(function() {
return tryLock(next, val);
}, 100);
};
return ns.promise(tryLock);
};
from = function(value) {
if (value.observe) {
return ns.promise(function(next, val) {
return value.observe(ns.promise(function(n, v) {
return next(v);
}));
});
} else {
return ns.promise(function(next, val) {
return next(value);
});
}
};
EMIT_INTERVAL_MAP = {};
emitInterval = function(msec, name) {
var msecFunc;
msecFunc = typeof msec === 'function' ? msec : function() {
return msec;
};
return ns.promise(function(next, val) {
var id;
id = setInterval(function() {
return next(val);
}, msecFunc());
if (name) return EMIT_INTERVAL_MAP[name] = id;
});
};
stopEmitInterval = function(name) {
return ns.promise(function(next, value) {
clearInterval(EMIT_INTERVAL_MAP[name]);
return next(val);
});
};
return ns.provide({
mapper: mapper,
through: through,
filter: filter,
scatter: scatter,
takeBy: takeBy,
wait: wait,
cond: cond,
match: match,
debug: debug,
lock: lock,
unlock: unlock,
from: from,
waitUntil: waitUntil,
emitInterval: emitInterval,
stopEmitInterval: stopEmitInterval
});
});
Namespace('brook.lambda').define(function(ns) {
var cache, hasArg, lambda, parseExpression;
cache = {};
hasArg = function(expression) {
return expression.indexOf('->' >= 0);
};
parseExpression = function(expression) {
var argsExp, bodyExp, fixed, splitted;
fixed = hasArg(expression) ? expression : "$->" + expression;
splitted = fixed.split('->');
argsExp = splitted.shift();
bodyExp = splitted.join('->');
return {
argumentNames: argsExp.split(','),
body: hasArg(bodyExp) ? lambda(bodyExp).toString() : bodyExp
};
};
lambda = function(expression) {
var func, parsed;
if (cache[expression]) return cache[expression];
parsed = parseExpression(expression);
func = new Function(parsed.argumentNames, "return (" + parsed.body + ");");
cache[expression] = funct;
return func;
};
return ns.provide({
lambda: lambda
});
});
Namespace('brook.channel').use('brook promise').use('brook.util scatter').define(function(ns) {
var Channel, NAMED_CHANNEL, channel, getNamedChannel, indexOf, observeChannel, sendChannel, stopObservingChannel;
indexOf = function(list, value) {
var i, _ref;
for (i = 0, _ref = list.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
if (list[i] === value) return i;
}
return -1;
};
Channel = (function() {
function Channel() {
this.queue = [];
this.promises = [];
}
Channel.prototype.send = function(func) {
var _self;
func = func ? func : function(k) {
return k;
};
_self = this;
return ns.promise(function(next, val) {
_self.sendMessage(func(val));
return next(val);
});
};
Channel.prototype.sendMessage = function(msg) {
var makeRunner, message, runner, scatter, sendError;
scatter = ns.scatter(1000);
sendError = sendChannel('error');
this.queue.push(msg);
makeRunner = function(message) {
return ns.promise(function(next, promise) {
return promise.run(message);
});
};
while (this.queue.length) {
message = this.queue.shift();
runner = makeRunner(message);
runner.setErrorHandler(sendError);
scatter.bind(runner).run(this.promises);
}
};
Channel.prototype.observe = function(promise) {
if (indexOf(this.promises, promise) > -1) return;
return this.promises.push(promise);
};
Channel.prototype.stopObserving = function(promise) {
var index;
index = indexOf(this.promises, promise);
if (index > -1) return this.promises.splice(index, 1);
};
return Channel;
})();
channel = function(name) {
if (name) {
return getNamedChannel(name);
} else {
return new Channel();
}
};
NAMED_CHANNEL = {};
getNamedChannel = function(name) {
if (NAMED_CHANNEL[name]) return NAMED_CHANNEL[name];
NAMED_CHANNEL[name] = new Channel();
return NAMED_CHANNEL[name];
};
observeChannel = function(name, promise) {
return getNamedChannel(name).observe(promise);
};
stopObservingChannel = function(name, promise) {
return getNamedChannel(name).stopObserving(promise);
};
sendChannel = function(name, func) {
var namedChannel;
namedChannel = getNamedChannel(name);
return namedChannel.send(func);
};
return ns.provide({
channel: channel,
sendChannel: sendChannel,
observeChannel: observeChannel,
stopObservingChannel: stopObservingChannel,
createChannel: function() {
return new Channel();
}
});
});
Namespace('brook.model').use('brook promise').use('brook.util *').use('brook.channel *').use('brook.lambda *').define(function(ns) {
var Model, createModel;
Model = (function() {
function Model(obj) {
var prop;
this.methods = {};
this.channels = {};
for (prop in obj) {
if (obj.hasOwnProperty(prop)) this.addMethod(prop, obj[prop]);
}
}
Model.prototype.addMethod = function(method, promise) {
var channel;
if (this.methods[method]) throw "already " + method + " defined";
channel = ns.createChannel();
this.methods[method] = promise.bind(channel.send());
this.channels[method] = channel;
return this;
};
Model.prototype.notify = function(method) {
return ns.promise().bind(this.methods[method]);
};
Model.prototype.method = function(method) {
if (!this.channels[method]) throw 'do not observe undefined method';
return this.channels[method];
};
return Model;
})();
createModel = function(obj) {
return new Model(obj);
};
return ns.provide({
createModel: createModel
});
});
Namespace('brook.dom.compat').define(function(ns) {
var ClassList, check, classList, dataset, getElementsByClassName, hasClassName;
dataset = (function() {
var camelize, proto, wrapper;
wrapper = function(element) {
return element.dataset;
};
if (__indexOf.call(window, 'HTMLElement') >= 0 && HTMLElement.prototype) {
proto = HTMLElement.prototype;
if (proto.dataset) return wrapper;
if (proto.__lookupGetter__ && proto.__lookupGetter__('dataset')) {
return wrapper;
}
}
camelize = function(string) {
return string.replace(/-+(.)?/g, function(match, chr) {
if (chr) {
return chr.toUpperCase();
} else {
return '';
}
});
};
return function(element) {
var attr, sets, _i, _len, _ref;
sets = {};
_ref = element.attributes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
attr = _ref[_i];
if (attr.name.match(/^data-/)) {
sets[camelize(attr.name.replace(/^data-/, ''))] = attr.value;
}
}
return sets;
};
})();
check = function(token) {
if (token === '') throw 'SYNTAX_ERR';
if (!token.indexOf(/\s/ === -1)) throw 'INVALID_CHARACTER_ERR';
};
ClassList = (function() {
function ClassList(element) {
this._element = element;
this._refresh();
}
ClassList.prototype._fake = true;
ClassList.prototype._refresh = function() {
var classes;
classes = (this._element.className || '').split(/\s+/);
if (classes.length && classes[0] === '') classes.shift();
if (classes.length && classes[classes.length - 1] === '') classes.pop();
this._classList = classes;
this.length = classes.length;
return this;
};
ClassList.prototype.item = function(i) {
return this._classList[i] || null;
};
ClassList.prototype.contains = function(token) {
var i, _ref;
check(token);
for (i = 0, _ref = this.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
if (this._classList[i] === token) return true;
}
return false;
};
ClassList.prototype.add = function(token) {
var i, _ref;
check(token);
for (i = 0, _ref = this.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
if (this._classList[i] === token) return;
}
this._classList.push(token);
this.length = this._classList.length;
return this._element.className = this._classList.join(' ');
};
ClassList.prototype.remove = function(token) {
var i, _ref;
check(token);
for (i = 0, _ref = this._classList.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
if (this._classList[i] === token) {
this._classList.splice(i, 1);
this._element.className = this._classList.join(' ');
}
}
return this.length = this._classList.length;
};
ClassList.prototype.toggle = function(token) {
var i, _ref;
check(token);
for (i = 0, _ref = this.length; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
if (this._classList[i] === token) {
this.remove(token);
return false;
}
}
this.add(token);
return true;
};
return ClassList;
})();
classList = function(element) {
return new ClassList(element);
};
hasClassName = function(element, className) {
var classSyntax;
classSyntax = element.className;
if (!(classSyntax && className)) return false;
return new RegExp("(^|\\s)" + className + "(\\s|$)").test(classSyntax);
};
getElementsByClassName = function(className) {
var allElements, element, ret, _i, _len;
if (document.getElementsByClassName) {
return document.getElementsByClassName(className);
}
allElements = document.getElementsByTagName('*');
ret = [];
for (_i = 0, _len = allElements.length; _i < _len; _i++) {
element = allElements[_i];
if (hasClassName(element, className)) ret.push(element);
}
return ret;
};
return ns.provide({
getElementsByClassName: getElementsByClassName,
hasClassName: hasClassName,
dataset: dataset,
classList: classList
});
});
Namespace('brook.dom.gateway').define(function(ns) {
return ns.provide({});
});
Namespace('brook.widget').use('brook promise').use('brook.channel *').use('brook.util *').use('brook.dom.compat *').define(function(ns) {
var TARGET_CLASS_NAME, applyNamespace, classList, dataset, elementsByClassName, errorChannel, mapByNamespace, mapToPairs, registerElements, removeClassName, updater, widgetChannel;
TARGET_CLASS_NAME = 'widget';
classList = ns.classList;
dataset = ns.dataset;
widgetChannel = ns.channel('widget');
errorChannel = ns.channel('error');
removeClassName = function(className, element) {
return classList(element).remove(className);
};
elementsByClassName = ns.promise(function(n, v) {
v = v || TARGET_CLASS_NAME;
return n([v, Array.prototype.slice.call(ns.getElementsByClassName(v))]);
});
mapByNamespace = ns.promise(function(n, val) {
var data, map, targetClassName, widget, widgetElements, widgetNamespace, _i, _len;
targetClassName = val[0];
widgetElements = val[1];
map = {};
for (_i = 0, _len = widgetElements.length; _i < _len; _i++) {
widget = widgetElements[_i];
removeClassName(targetClassName || TARGET_CLASS_NAME, widget);
data = dataset(widget);
widgetNamespace = data.widgetNamespace;
if (!widgetNamespace) continue;
if (!map[widgetNamespace]) map[widgetNamespace] = [];
map[widgetNamespace].push([widget, data]);
}
return n(map);
});
mapToPairs = ns.promise(function(n, map) {
var namespace, pairs;
pairs = [];
for (namespace in map) {
if (map.hasOwnProperty(namespace)) pairs.push([namespace, map[namespace]]);
}
return n(pairs);
});
applyNamespace = ns.promise(function(n, pair) {
return Namespace.use([pair[0], '*'].join(' ')).apply(function(ns) {
return n([ns, pair[1]]);
});
});
registerElements = ns.promise(function(n, v) {
var elements, widget, widgets, _i, _j, _len, _len2, _ns;
_ns = v[0];
widgets = v[1];
try {
if (_ns.registerElement) {
for (_i = 0, _len = widgets.length; _i < _len; _i++) {
widget = widgets[_i];
_ns.registerElement.apply(null, widget);
}
} else if (_ns.registerElements) {
elements = [];
for (_j = 0, _len2 = widgets.length; _j < _len2; _j++) {
widget = widgets[_j];
elements.push(widget[0]);
}
_ns.registerElements(elements);
} else {
throw "registerElement or registerElements not defined in " + _ns.CURRENT_NAMESPACE;
}
} catch (e) {
errorChannel.sendMessage(e);
}
});
updater = ns.promise().bind(ns.lock('class-seek'), elementsByClassName, mapByNamespace, mapToPairs, ns.unlock('class-seek'), ns.scatter(), applyNamespace, registerElements);
widgetChannel.observe(updater);
return ns.provide({
bindAllWidget: widgetChannel.send()
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment