Created
October 19, 2011 23:52
-
-
Save werelax/1300027 to your computer and use it in GitHub Desktop.
LousyCells + Pseudo-Declarative behaviour proof of concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Some helpers | |
function unique (array) { | |
var a = []; | |
var l = array.length; | |
for(var i=0; i<l; i++) { | |
for(var j=i+1; j<l; j++) { | |
// If array[i] is found later in the array | |
if (array[i] === array[j]) | |
j = ++i; | |
} | |
a.push(array[i]); | |
} | |
return a; | |
} | |
var debounce = (function() { | |
var limit = function(func, wait, debounce) { | |
var timeout; | |
return function() { | |
var context = this, args = arguments; | |
var throttler = function() { | |
timeout = null; | |
func.apply(context, args); | |
}; | |
if (debounce) clearTimeout(timeout); | |
if (debounce || !timeout) timeout = setTimeout(throttler, wait); | |
}; | |
}; | |
return function(func, wait) { return limit(func, wait, true); }; | |
})(); | |
// LousyCell implementation | |
var Cell = (function () { | |
var current_id = 0; | |
function Cell(value) { | |
this.id = current_id++; | |
this.value = value; | |
this.observers = []; | |
}; | |
Cell.prototype = { | |
get: function() { return this.value; }, | |
set: function(value) { | |
this.value = value; | |
this.notify_change(this.id, value); | |
return this.value; | |
}, | |
observe: function(callback) { | |
this.observers.push(callback); | |
this.ovservers = unique(this.observers); | |
}, | |
notify_change: function() { | |
for (var i=0, _len=this.observers.length; i < _len; i++) { | |
var observer = this.observers[i]; | |
if (observer) try { | |
this.observers[i].call(this.value); | |
} catch (e) { } | |
} | |
} | |
}; | |
return Cell; | |
})(); | |
var $C = function(arg1, arg2) { | |
arg1 || (arg1 = ''); | |
var is_array = function(a) { return a.constructor == Array.prototype.constructor; } | |
var attach_observer = function (cell_decorator, fn) { | |
var callback = function() { fn(cell_decorator()); }; | |
var deps = cell_decorator._root_cells; | |
for (var i=0, _len = deps.length; i < _len; i++) { | |
var dep_cell = deps[i]; | |
dep_cell.observe(callback); | |
} | |
}; | |
var bind_cell = function (input, fn) { | |
is_array(input) || (input = [input]); | |
var decorator = function() { | |
var values = input.map(function(c) { return c(); }); | |
return fn ? fn.apply({}, values) : values[0]; | |
} | |
decorator._is_cell = true; | |
decorator._root_cells = input.reduce(function (r, c) { return r.concat(c._root_cells); }, []); | |
decorator._root_cells = unique(decorator._root_cells); | |
decorator._dependencies = input; | |
decorator.listen = function (fn) { return attach_observer(decorator, fn); } | |
return decorator; | |
}; | |
var create_input_cell = function (value, getter_filter) { | |
var cell = new Cell(value); | |
var decorator = function (new_value) { | |
if (new_value !== undefined) { return cell.set(new_value);} | |
else { return getter_filter ? getter_filter(cell.get()) : cell.get(); } | |
} | |
decorator._is_cell = true; | |
decorator._root_cells = [cell]; | |
decorator.connect = function (selector, options) { return connect_to_selector(decorator, selector, options); }; | |
return decorator; | |
}; | |
if (arg1._is_cell || (is_array(arg1) && typeof(arg2) == 'function')) { | |
return bind_cell(arg1, arg2); | |
} else { | |
return create_input_cell(arg1, arg2); | |
}; | |
}; | |
/** Example: | |
var input_cell = $C('Hello'); | |
var cell1 = $C(input_cell); | |
console.log('cell1: ' + cell1()); | |
// "cell1: Hello" | |
var cell2 = $C(input_cell, function(cell) { return cell + ", I said."}); | |
console.log('cell2: ' + cell2()); | |
// "cell2: Hello, I said." | |
var cell3 = $C([cell1, cell2], function(c1, c2) { return c1 + ". " + c2; }); | |
console.log('cell3: ' + cell3()); | |
// "cell3: Hello. Hello, I said." | |
$C(cell3).listen(function(value) { | |
console.log('cell3 changed to: ' + value); | |
}); | |
input_cell('Goodbye'); | |
// "cell3 changed to: Goodbye. Goodbye, I said." | |
/** Another example with multiple input cells | |
var ic1 = $C(10); | |
var ic2 = $C(5); | |
var ic3 = $C(200); | |
var result = $C([ic1, ic2, ic3], function(v1, v2, v3) { return v1 + v2 + v3}); | |
$C(result).listen(function(result) { | |
console.log("Result changed to: " + result); | |
}); | |
console.log("At the start, result is: " + result()); | |
// "At the start, result is: 215" | |
ic1(0); | |
// Result changed to: 205 | |
ic2(1000); | |
// Result changed to: 1200 | |
ic3(-1123); | |
// Result changed to: -123 | |
**/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=utf-8"> | |
<title>Simple_declarative_view_example</title> | |
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.js"></script> | |
<script type="text/javascript" src="lousy_cells.js" charset="utf-8"></script> | |
</head> | |
<body> | |
<script type="text/template" id="test-template"> | |
<h1>{{name}}</h1> | |
<hr/> | |
<label> Type something or put the mouse over: | |
<input id="input1" type="text"/> | |
</label> | |
<div id="result1"></div> | |
<div id="msg1" style="display:none;color:green"> | |
Your mouse is over the input box | |
</div> | |
</script> | |
<div id="container"></div> | |
<script type="text/javascript" charset="utf-8"> | |
// Some shitty templates / view system just for make it work. | |
// Dont look at this code, this is only auxiliar bullshit just for | |
// avoid adding too mucho complexity to the example with external libs | |
function LousyTemplate(id) { | |
var text = $('#'+id).text(); | |
return function() { | |
return LousyTemplate.renderer.call(this, text); | |
}; | |
} | |
LousyTemplate.renderer = function (text) { | |
var data = this; | |
var result = text; | |
var placeholders = text.match(/{{.*?}}/g); | |
var already_replaced = {}; | |
for (i=0,_len=placeholders.length; i<_len; i++) { | |
var placeholder = placeholders[i]; | |
if (placeholder in already_replaced) continue; | |
var prop_name = placeholder.match(/[^{|}]+/); | |
var replacement = data[prop_name]; | |
if (typeof(replacement) == 'function') replacement = replacement.call(this); | |
var rep_exp = new RegExp(placeholder, 'g'); | |
result = result.replace(rep_exp, replacement); | |
already_replaced[placeholder] = true; | |
} | |
return result; | |
}; | |
var T = {test: LousyTemplate('test-template') }; | |
// The code of the declarative behaviour view must be something in this line: | |
function SimpleView(params) { | |
params || (params = {}); | |
this.el = params.el; | |
this.template = params.template; | |
this.behaviour = params.behaviour; | |
} | |
SimpleView.prototype = { | |
render: function() { | |
$(this.el).html(this.template()); | |
this.bind_behaviour(); | |
}, | |
bind_behaviour: function() { | |
for(var i=0, _len1=this.behaviour.length; i<_len1; i++) { | |
var pattern = this.behaviour[i]; | |
var selector = pattern[0]; | |
var event = pattern[1]; | |
var binding = pattern[2]; | |
var cell_or_func = pattern[3]; | |
if (binding == 'value') binding = function() { return $(this).val(); }; | |
if (event == 'observe') { | |
this.connect_output(selector, binding, cell_or_func); | |
} else { | |
this.connect_input(selector, event, binding, cell_or_func); | |
} | |
} | |
}, | |
connect_input: function(selector, event, binding, cell_or_func) { | |
var self = this; | |
$(this.el).find(selector)[event](function() { | |
var value = binding.call(this); | |
cell_or_func.call(self, value); | |
}); | |
}, | |
connect_output: function(selector, binding, cell) { | |
var element = $(this.el).find(selector); | |
$C(cell).listen(function(value) { | |
if (typeof(binding) == 'function') { | |
binding.call(element, value); | |
} else { | |
element[binding](value); | |
} | |
}); | |
} | |
}; | |
// Once the library is ready, the code for using it should be something like this: | |
var SomeView = function(params) { | |
params.template = T['test']; | |
this.data1 = $C(); | |
this.data2 = $C(); | |
this.name = "SIMPLE PSEUDO-DECLARATIVE EXAMPLE"; | |
this.mix = $C([this.data1, this.data2], function (v1, v2) { | |
return "You typed: '" + v1 + (v2?"' and your mouse is over the input":"'"); | |
}); | |
params.behaviour = [ | |
['#input1', 'keyup', 'value', this.data1], | |
['#input1', 'mouseover', function(){return true;}, this.data2], | |
['#input1', 'mouseout', function(){return false;}, this.data2], | |
['#result1', 'observe', function(v){this.html(v);}, this.mix], | |
['#msg1', 'observe', function(v){this.css('display', v?'block':'none')}, this.data2] | |
]; | |
SimpleView.call(this, params); | |
}; | |
SomeView.prototype = new SimpleView(); | |
var v1 = new SomeView({el: $('#container').get(0)}).render(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The live example (as ugly as it is) in: http://jsfiddle.net/werelax/76vvr/