There is a lot of stuff I miss in javascript. I made an small lib for myself, which I want to share.
First I added some OO patterns:
Decorator,
Proxy,
Observer,
Observable,
Strategy,
Chain,
Chainable exceptionhandlers
I added a logger, which plays nicely with firebug. And some functional stuff like:
foldr,
foldl
A function, which returns a function, which allow partial function application:
pfunc
And a function, which gives any object an fluent interface (useful for loggers):
fluentize
And also added the function merge to the Array object. Here is the small lib:
There are probably some bugs and I haven't documentation, but on request I can add some examples.
/* Some functions I miss in javascript */
/* Haskell style partial function application */
/*
Transforms a function of type:
( (a, b) -> c)
to
a -> b -> c
so if we have a function
f(x,y,z) -> x - y + z
we can do this now:
f(1,y,z) = g(y,z) = 1 - y + z
or this:
f(1,2,z) = g(z) = 1 - 2 + z
or this
f(x,y,z) = f(x,y,z);
use this class as
function min(a,b,c){
return a - b + c;
}
var pmin = pfunc(min);
var t = pmin(1)(2)(3);
print(t);
*/
function pfunc(func, i, acc){
if(!i){ i = 0; acc = new Array(); }
if(i == func.arity) return func.apply(func, acc);
return function(){
acc.merge(arguments);
return pfunc(func, i + arguments.length, acc);
}
}
/* Makes an object fluent */
function fluentize(obj){
var newobj = {};
for(key in obj){
var val = obj[key];
var valn;
if(typeof(val) == 'function'){
newobj[key] = function(){
val.apply(obj, this.arguments);
return obj;
}
} else {
newobj[key] = val;
}
}
return newobj;
}
/* Foldr and fold */
function foldr(list, func, acc){
var list = list.reverse();
if(list.length == 0) return acc;
for(var i = 0; i < list.length; i++){
var val = list[i];
if(typeof(val) != 'string' && typeof(val) != 'boolean' && typeof(val) != 'number') continue;
acc = func(val,acc);
}
return acc;
}
function foldl(list, func, acc){
if(list.length == 0) return acc;
for(var i = 0; i < list.length; i++){
var val = list[i];
if(typeof(val) != 'string' && typeof(val) != 'boolean' && typeof(val) != 'number') continue;
acc = func(acc, val);
}
return acc;
}
/* Very important, array merge: */
Array.prototype.merge = function(arr){
for(var i = 0; i < arr.length; i++){
this.push(arr[i]);
}
}
/* Voor ieder een hel
Voor de enkeling een hemel
maak je keuze snel
*/
/*
Function which can ensure an element exists.
callback should be return true or false
*/
function ensure(callback, cps){
function _ensurethis(){
if(callback()){
cps();
return;
}
ensure(callback, cps);
}
window.setTimeout(_ensurethis, 10);
}
/* Some OO patterns, which are very useful: */
/* Extends an object with other objects
* @function extendedObject = Extend(obj, obj1, obj2, obj3 .. objn)
*/
function Extend(){
var obj = arguments[0];
for(var i = 1; i < arguments.length; i++){
for(key in arguments[i]){
obj[key] = arguments[i][key];
}
}
return obj;
}
/* Couple of methods, which let us forward calls to an inner object,
very useful to build decorators, proxies etcetera */
function ForwardCall(){
}
/* Forward calls to an inner object */
ForwardCall.prototype.forwardCall = function(func) {
this[func] = function(){
return this.innerCall(func, arguments);
}
}
ForwardCall.prototype.innerCall = function (func, args){
return this.obj[func].apply(this.obj, args);
}
/* Decorator object */
Decorator.prototype = ForwardCall.prototype;
function Decorator(obj){
this.obj = obj;
for(key in this.obj){
var val = this.obj[key];
if(typeof(val) == 'function'){
this.forwardCall(key);
}
}
}
/* Proxy object */
Proxy.prototype = ForwardCall.prototype;
function Proxy(obj){
this.obj = obj;
for(key in obj){
var val = this.obj[key];
if(typeof(val) == 'function'){
this[key] = function(){
var args = this.before(key, arguments);
ret = this.innerCall(key, args);
return this.after(key, ret);
}
}
}
this.before = function(func, args){
return args;
};
this.after = function (func, ret){
return ret;
}
}
/* Observable */
function Observable(){
}
Observable.prototype.addObserver = function(observer){
if(typeof(this.observers) == 'undefined'){
this.observers = new Array();
}
this.observers.push(observer);
}
Observable.prototype.updateObservers = function (message){
for(observerid in this.observers){
var observer = this.observers[observerid];
observer.receiveMessage(message);
}
}
/* Observer */
function Observer(){
}
Observer.prototype.receiveMessage = function (message){
}
/* Implementation of the strategy pattern */
function Strategy(){
this.strategies = new Array();
this.createStrategy = function (func, test){
return {func: func, test: test};
};
this.addStrategy = function (func, test){
this.strategies.push(this.createStrategy(func, test));
return this.strategies.length;
};
this.setDefaultStrategy = function (func){
this.defaultStrategy = func;
};
this.removeStrategy = function (id){
this.strategies[id] = null;
};
this.run = function() {
var args = arguments;
for(key in this.strategies){
if(this.strategies[key] != null){
var strategy = this.strategies[key];
if(strategy['test'].apply(this, args)){
return strategy['func'].apply(this, args);
}
}
}
return this.defaultStrategy.apply(this, args);
}
}
function Chain(){
}
function Chain(test, func){
this.func = func;
this.test = test;
this.next = null;
this.run = function(){
if( this.test.apply(this, arguments) ){
return this.func.apply(this, arguments);
}
if(this.next != null){
return this.next.run.apply(this.next, arguments);
}
return null;
}
this.addToChain = function(test,func){
if(this.next == null){
this.next = new Chain(test,func);
return;
}
this.next.addToChain(test,func);
}
}
ExceptionHandler.prototype = ForwardCall.prototype;
function ExceptionHandler (obj){
this.obj = obj;
this.strategy = new Strategy;
this.strategy.setDefaultStrategy(function(ex,key){
print ("Uncaught Exception calling: " + key);
print(ex);
return;
});
this.addFunc = function(key){
this[key] = function(){
try {
this.innerCall(key, arguments);
} catch (e){
this.strategy.run(e, key);
}
}
}
for(key in this.obj){
var val = this.obj[key];
if(typeof(val) == 'function'){
this.addFunc(key);
return this.next.run.apply(this.next, arguments);
}
return null;
}
this.addToChain = function(test,func){
if(this.next == null){
this.next = new Chain(test,func);
return;
}
this.next.addToChain(test,func);
}
}
ExceptionHandler.prototype = ForwardCall.prototype;
function ExceptionHandler (obj){
this.obj = obj;
this.strategy = new Strategy;
this.strategy.setDefaultStrategy(function(ex,key){
print ("Uncaught Exception calling: " + key);
print(ex);
return;
});
this.addFunc = function(key){
this[key] = function(){
try {
this.innerCall(key, arguments);
} catch (e){
this.strategy.run(e, key);
}
}
}
for(key in this.obj){
var val = this.obj[key];
if(typeof(val) == 'function'){
this.addFunc(key);
this.addFunc(key);
}
}
this.addHandler = function(test, func){
this.strategy.addStrategy(test, func);
}
}
/* Inspection function */
function Inspect(obj, depth, i, indent){
if(!depth){
depth = 5;
}
if(!i){
i = 0;
indent = '';
}
for(key in obj){
var type = typeof(obj[key]);
print (indent + type + ": " + key);
if((type == 'object' || type == 'function' ) && i < depth){
Inspect(obj[key], depth, i + 1, indent + " ");
}
}
}
/* We need to define a print function, to work with all the classes above:
*/
function print(str){
Logger.getInstance().startGroup('print').log('from: ' + arguments.callee.caller.toString()).log(str).endGroup();
}
/* HTML generating functions */
/* Simple positioning function */
function putHTML(pos){
return function(data){
Logger.getInstance().startGroup('putHTML');
Logger.getInstance().log('position data at: ' + pos);
$(pos).html(data);
Logger.getInstance().endGroup();
}
}
/* Javascript logger, logs to firebug, this an singleton, on non development site, we have to run Logger.getInstance().setSilence(),
then it shuts up */
function Logger(){
this.silence = false;
}
Logger.getInstance = function(){
if(!Logger.instance){
Logger.instance = /*fluentize(*/new Logger();//);
}
return Logger.instance;
}
Logger.prototype.setSilence = function(silence){
if(!silence){
silence = true;
}
this.silence = silence;
}
Logger.prototype.startGroup = function(name){
if(this.silence) return;
console.group(name);
return this;
}
Logger.prototype.endGroup = function(){
if(this.silence) return;
console.groupEnd();
return this;
}
Logger.prototype.log = function (logline, errorlevel){
/* formatted mode */
if(!console || this.silence){
return;
}
if(typeof(logline) == 'object'){
this.multiline(logline, errorlevel);
}
if(errorlevel == 'error'){
console.error(logline);
} else
if(errorlevel == 'warn'){
console.warn(logline);
} else {
console.info(logline);
}
return this;
}
Logger.prototype.multiline = function(loglines, errorlevel){
console.group('Object inspector:');
console.dir(loglines);
console.groupEnd();
return this;
}

Geen opmerkingen:
Een reactie posten