1936 lines
52 KiB
JavaScript
1936 lines
52 KiB
JavaScript
/**
|
|
** ==============================
|
|
** O O O OOOO
|
|
** O O O O O O
|
|
** O O O O O O
|
|
** OOOO OOOO O OOO OOOO
|
|
** O O O O O O O
|
|
** O O O O O O O
|
|
** OOOO OOOO O O OOOO
|
|
** ==============================
|
|
** Dr. Stefan Bosse http://www.bsslab.de
|
|
**
|
|
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
|
|
** BY THE AUTHOR(S).
|
|
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
|
|
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
|
|
** OUTSIDE OF THE SOFTWARE SYSTEM.
|
|
**
|
|
** $AUTHORS: Stefan Bosse
|
|
** $INITIAL: (C) 2006-2021 bLAB
|
|
** $CREATED: 30-3-15 by sbosse.
|
|
** $VERSION: 1.24.1
|
|
**
|
|
** $INFO:
|
|
**
|
|
** JavaScript-OCaML Compatibility Module
|
|
**
|
|
** $ENDOFINFO
|
|
*/
|
|
var Io = Require('com/io');
|
|
var Path = Require('com/path');
|
|
var Sprintf = Require('com/sprintf');
|
|
|
|
/*******************************
|
|
** Some global special "values"
|
|
********************************/
|
|
|
|
/** A matching template pattern matching any value
|
|
*
|
|
* @type {undefined}
|
|
*/
|
|
global.any = undefined;
|
|
/** A matching template pattern matching any value
|
|
*
|
|
* @type {undefined}
|
|
*/
|
|
global._ = undefined;
|
|
|
|
/**
|
|
*
|
|
* @type {null}
|
|
*/
|
|
global.none = null;
|
|
/**
|
|
*
|
|
* @type {null}
|
|
*/
|
|
global.empty = null;
|
|
|
|
global.NL = '\n';
|
|
|
|
global.int = function (v) {return v|0};
|
|
global.div = function (a,b) {return a/b|0};
|
|
|
|
if (!Object.prototype.forEach) {
|
|
Object.defineProperties(Object.prototype, {
|
|
'forEach': {
|
|
value: function (callback) {
|
|
if (this == null) {
|
|
throw new TypeError('Not an object');
|
|
}
|
|
var obj = this;
|
|
for (var key in obj) {
|
|
if (obj.hasOwnProperty(key)) {
|
|
callback.call(obj, obj[key], key, obj);
|
|
}
|
|
}
|
|
},
|
|
writable: true
|
|
}
|
|
});
|
|
}
|
|
/** Just transfer parent prototypes to child
|
|
*
|
|
*/
|
|
function inherit(child,parent) {
|
|
for(var p in parent.prototype) {
|
|
if (p == '__proto__') continue;
|
|
child.prototype[p]=parent.prototype[p];
|
|
}
|
|
}
|
|
|
|
/** Portable class inheritance and instanceOf polyfill
|
|
*
|
|
*/
|
|
// SomeObject.prototype.__proto__=SomeObject2.prototype;
|
|
// Child class inherits prototype from parent using __proto__
|
|
function inheritPrototype(child,parent) {
|
|
var __proto__=child.__proto__;
|
|
child.prototype.__proto__=parent.prototype;
|
|
if (!__proto__) for(var p in parent.prototype) {
|
|
if (p == '__proto__') continue;
|
|
child.prototype[p]=parent.prototype[p];
|
|
}
|
|
}
|
|
// Polyfill fir o instanceof c with inheritance check (checking __proto__)
|
|
function instanceOf(obj,cla) {
|
|
var p=obj.__proto__;
|
|
if (obj instanceof cla) return true;
|
|
while (p) {
|
|
if (p === cla.prototype) return true;
|
|
p=p.__proto__
|
|
}
|
|
return false;
|
|
}
|
|
// Polyfill for __defineGetter__ / __defineSetter__
|
|
function defineGetter(cla,prop,fun) {
|
|
Object.defineProperty(cla.prototype,prop,{
|
|
configurable:true,
|
|
get:fun
|
|
});
|
|
}
|
|
function defineSetter(cla,prop,fun) {
|
|
Object.defineProperty(cla.prototype,prop,{
|
|
configurable:true,
|
|
set:fun
|
|
});
|
|
|
|
}
|
|
|
|
Object.addProperty = function (obj,name,fun) {
|
|
if (obj.prototype[name]) return;
|
|
obj.prototype[name]=fun;
|
|
Object.defineProperty(obj.prototype, name, {enumerable: false});
|
|
};
|
|
|
|
Object.updateProperty = function (obj,name,fun) {
|
|
obj.prototype[name]=fun;
|
|
Object.defineProperty(obj.prototype, name, {enumerable: false});
|
|
};
|
|
|
|
Object.addProperty(Array,'contains',function (el) { return this.indexOf(el)!=-1 });
|
|
Object.addProperty(Array,'last',function () { return this[this.length-1] });
|
|
|
|
global.inherit = inherit;
|
|
global.inheritPrototype = inheritPrototype;
|
|
global.instanceOf = instanceOf;
|
|
global.defineGetter = defineGetter;
|
|
global.defineSetter = defineSetter;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
var assert = function(condmsg) {
|
|
if (condmsg != true) {
|
|
Io.out('** Assertion failed: '+condmsg+' **');
|
|
Io.stacktrace();
|
|
throw Error(condmsg);
|
|
}
|
|
};
|
|
global.assert=assert;
|
|
|
|
function forof(obj,f) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = obj[Symbol.iterator](), _step;
|
|
!(_iteratorNormalCompletion = (_step = _iterator.next()).done);
|
|
_iteratorNormalCompletion = true) {
|
|
element = _step.value;
|
|
|
|
f(element);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
global.forof=forof;
|
|
|
|
/** OBJ
|
|
*
|
|
*/
|
|
var obj = {
|
|
/** Compact an object:
|
|
* [{a:b},[c:d},..] -> {a:b,c:d,..}
|
|
* {a:[b]} -> {a:b}
|
|
*
|
|
*/
|
|
compact: function (o) {
|
|
var a;
|
|
if (obj.isArray(o)) {
|
|
if (o.length==1 && obj.isObject(o[0])) return obj.compact(o[0]);
|
|
else return o;
|
|
} else if (obj.isObject(o)) for (a in o) {
|
|
var elem=o[a];
|
|
o[a]=obj.compact(elem);
|
|
}
|
|
return o;
|
|
},
|
|
copy: function (o) {
|
|
if (o === null || typeof o !== 'object') {
|
|
return o;
|
|
}
|
|
|
|
var temp = (o instanceof Array) ? [] : {};
|
|
for (var key in o) {
|
|
temp[key] = obj.copy(o[key]);
|
|
}
|
|
|
|
return temp;
|
|
},
|
|
equal: function (o1,o2) {
|
|
if (!o1 || !o2) return false;
|
|
for(var i in o1) if (o1[i]!=o2[i]) return false;
|
|
for(var i in o2) if (o1[i]!=o2[i]) return false;
|
|
return true;
|
|
},
|
|
extend: function (o1,o2) {
|
|
for(var i in o2) o1[i]=o2[i];
|
|
return o1;
|
|
},
|
|
find: function(obj,fun) {
|
|
var p;
|
|
for(p in obj) {
|
|
if (fun(obj[p],p)) return obj[p];
|
|
}
|
|
},
|
|
|
|
hasProperty: function (o,p) {
|
|
return o[p]!=undefined || (p in o);
|
|
},
|
|
head:function(o) {
|
|
for (var p in o) return p;
|
|
return undefined;
|
|
},
|
|
// transfer src attributes to dst recusively (no object overwrite)
|
|
inherit: function (dst,src) {
|
|
for(var i in src) {
|
|
if (typeof dst[i] == 'object' && typeof src[i] == 'object')
|
|
inherit(dst[i],src[i]);
|
|
else if (typeof dst[i] == 'undefined')
|
|
dst[i]=src[i];
|
|
}
|
|
return dst;
|
|
},
|
|
isArray:function (o) {
|
|
if (o==_ || o ==null) return false;
|
|
else return typeof o == "array" || (typeof o == "object" && o.constructor === Array);
|
|
},
|
|
isMatrix:function (o) {
|
|
if (o==_ || o ==null) return false;
|
|
else return obj.isArray(o) &&
|
|
obj.isArray(o[0]);
|
|
},
|
|
isEmpty: function (o) {
|
|
for(var prop in o) {
|
|
if (o[prop]!=undefined) return false;
|
|
}
|
|
return true;
|
|
},
|
|
isError: function (o) {
|
|
return o instanceof Error
|
|
},
|
|
isFunction: function (o) {
|
|
return typeof o == "function";
|
|
},
|
|
isObj:function (o) {
|
|
return typeof o == "object";
|
|
},
|
|
isObject:function (o) {
|
|
return typeof o == "object";
|
|
},
|
|
isRegex: function (o) {
|
|
return o instanceof RegExp;
|
|
},
|
|
isString: function (o) {
|
|
return typeof o == "string" || (typeof o == "object" && o.constructor === String);
|
|
},
|
|
isNumber: function (o) {
|
|
return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
|
|
},
|
|
|
|
|
|
iter: function(obj,fun) {
|
|
var p;
|
|
for(p in obj) {
|
|
fun(obj[p],p)
|
|
}
|
|
}
|
|
};
|
|
|
|
/** ARRAY
|
|
*
|
|
*/
|
|
var array = {
|
|
/** Evaluate a function returning a boolean value for each member of the array and
|
|
* compute the boolean conjunction.
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
*/
|
|
and: function(array,fun) {
|
|
var res=true;
|
|
var i=0;
|
|
var len=array.length;
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
res=res&&fun(element,i)
|
|
}
|
|
return res;
|
|
},
|
|
/** Append one element at the end of the array.
|
|
*
|
|
* @param {* []} array
|
|
* @param {*} element
|
|
* @returns {* []}
|
|
*/
|
|
append : function(array,element) {
|
|
array.push(element);
|
|
return array;
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
*/
|
|
call: function(array,args) {
|
|
var i=0;
|
|
var len=array.length;
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
element()
|
|
}
|
|
},
|
|
/** Check for an elenment in the array by using a check function.
|
|
*
|
|
* @param array
|
|
* @param fun
|
|
* @returns {boolean}
|
|
*/
|
|
check: function(array,fun) {
|
|
var i,exist;
|
|
exist=false;
|
|
loop: for(i in array) {
|
|
var element=array[i];
|
|
if (fun(element,i)) {
|
|
exist=true;
|
|
break loop;
|
|
}
|
|
}
|
|
return exist;
|
|
},
|
|
/** Append array2 at the end of array inplace. The extended array is returned.
|
|
* Source array (1) will be modified.
|
|
*
|
|
* @param {*[]} array
|
|
* @param {*[]} array2
|
|
* @returns {*[]}
|
|
*/
|
|
concat : function(array,array2) {
|
|
for(var i in array2) {
|
|
array.push(array2[i]);
|
|
}
|
|
return array;
|
|
},
|
|
/** Create the conjunction set of two arrays
|
|
*
|
|
*/
|
|
conjunction :function (set1,set2,fun) {
|
|
return array.union(set1,set2,fun);
|
|
},
|
|
/**
|
|
*
|
|
* @param {*[]} array
|
|
* @param {number|string|*|*[]} elements
|
|
* @param {function} [fun] Optional equality test function
|
|
* @returns {boolean}
|
|
*/
|
|
contains : function(array,elements,fun) {
|
|
var i = array.length;
|
|
if (!fun) fun=function(o1,o2) {return o1===o2};
|
|
if (obj.isArray(elements)) {
|
|
while (i--) {
|
|
var j = elements.length;
|
|
while (j--) {
|
|
if (fun(array[i],elements[j])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else while (i--) {
|
|
if (fun(array[i],elements)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
/** Return a fresh copy of the source array or copy src array to dst.
|
|
*
|
|
* @param array
|
|
* @returns {Array.<T>|string|Blob|ArrayBuffer}
|
|
*/
|
|
copy: function(src,dst) {
|
|
var i;
|
|
if (dst) {
|
|
for(i in src) dst[i]=src[i];
|
|
} else return src.slice();
|
|
},
|
|
/** Create a new array with initial element values.
|
|
*
|
|
* @param length
|
|
* @param init
|
|
* @returns {Array}
|
|
*/
|
|
create : function(length,init) {
|
|
var arr = [], i = length;
|
|
while (i--) {
|
|
arr[i] = init;
|
|
}
|
|
return arr;
|
|
},
|
|
/** Create a matrix (array of array) with initial element values.
|
|
*
|
|
*/
|
|
create_matrix : function(rows,cols,init) {
|
|
var m = [];
|
|
var r = [];
|
|
var i,j;
|
|
for (i = 0; i < rows; i++) {
|
|
r=[];
|
|
for(j=0;j<cols;j++) r.push(init);
|
|
m.push(r);
|
|
}
|
|
return m;
|
|
},
|
|
/** Create the (inclusive) disjunction set of two arrays.
|
|
* Source arrays will not be modified.
|
|
*
|
|
*/
|
|
disjunction :function (set1,set2,fun) {
|
|
return array.merge(set1,set2);
|
|
},
|
|
/**
|
|
*
|
|
* @param array
|
|
* @returns {boolean}
|
|
*/
|
|
empty : function (array) {
|
|
return (array==undefined ||
|
|
array.length==0)
|
|
},
|
|
|
|
/** Test for equality
|
|
*/
|
|
equal: function (a1,a2) {
|
|
if (a1.length != a2.length) return false;
|
|
for(var i in a1) if (a1[i]!=a2[i]) return false;
|
|
return true;
|
|
},
|
|
|
|
/** Create the (exclusive) disjunction set of two arrays.
|
|
* Source arrays will not be modified.
|
|
*
|
|
*/
|
|
exclusive :function (set1,set2,fun) {
|
|
var i,j,found,res = [];
|
|
for (i in set1) {
|
|
found=false;
|
|
loop1: for (j in set2) {
|
|
if (fun != undefined && fun(set1[i],set2[j])) {found=true; break loop1;}
|
|
else if (fun == undefined && set1[i]==set2[j]) {found=true; break loop1;};
|
|
}
|
|
if (!found) res.push(set1[i]);
|
|
}
|
|
for (i in set2) {
|
|
found=false;
|
|
loop2: for (j in set1) {
|
|
if (fun != undefined && fun(set2[i],set1[j])) {found=true; break loop2;}
|
|
else if (fun == undefined && set2[i]==set1[j]) {found=true; break loop2;};
|
|
}
|
|
if (!found) res.push(set2[i]);
|
|
}
|
|
return res;
|
|
},
|
|
/** Find an element in an array and return it (or none);
|
|
*
|
|
* @param array
|
|
* @param fun
|
|
* @returns {undefined|*}
|
|
*/
|
|
find: function(array,fun) {
|
|
var i;
|
|
for(i in array) {
|
|
if (fun(array[i],i)) return array[i];
|
|
}
|
|
return none;
|
|
},
|
|
/** Search and map an element of an array using a test&map function.
|
|
*
|
|
* @param array
|
|
* @param {function(*,number):*} fun
|
|
* @returns {undefined|*}
|
|
*/
|
|
findmap: function(array,fun) {
|
|
var i,found;
|
|
for(i in array) {
|
|
found=fun(array[i],i);
|
|
if (found) return found;
|
|
}
|
|
return none;
|
|
},
|
|
/** Filter out elements using a test function.
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number):boolean} fun
|
|
* @returns {* []}
|
|
*/
|
|
filter: function(array,fun) {
|
|
if (array.filter) return array.filter(fun);
|
|
else {
|
|
var res=[],
|
|
len=array.length,
|
|
element,i;
|
|
for(i=0;i<len;i++) {
|
|
element=array[i];
|
|
if (fun(element,i)) res.push(element);
|
|
}
|
|
return res;
|
|
}
|
|
},
|
|
/** Filter out and map elements using a test&map function.
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number):*|undefined} fun
|
|
* @returns {* []}
|
|
*/
|
|
filtermap: function(array,fun) {
|
|
var res=[],
|
|
len=array.length,
|
|
element,mapped,i;
|
|
for(i=0;i<len;i++) {
|
|
element=array[i];
|
|
mapped=fun(element,i);
|
|
if (mapped!=undefined) res.push(mapped);
|
|
}
|
|
return res;
|
|
},
|
|
/** Flattens an array consting of arrays (and elements)
|
|
*
|
|
* @param array
|
|
* @returns {Array}
|
|
*/
|
|
flatten: function (array) {
|
|
var res=[];
|
|
var len=array.length;
|
|
var i;
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
if (!obj.isArray(element)) res.push(element);
|
|
else {
|
|
var j;
|
|
var len2=element.length;
|
|
for(j=0;j<len2;j++) {
|
|
var element2=element[j];
|
|
res.push(element2);
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
|
|
},
|
|
/**
|
|
*
|
|
* @param array
|
|
* @returns {*}
|
|
*/
|
|
head : function(array) {
|
|
return array[0];
|
|
},
|
|
/**
|
|
*
|
|
* @param length
|
|
* @param fun
|
|
* @returns {Array}
|
|
*/
|
|
init : function(length,fun) {
|
|
var arr = [], i = length;
|
|
while (i--) {
|
|
arr[i] = fun(i);
|
|
}
|
|
return arr;
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
*/
|
|
iter: function(array,fun) {
|
|
/*
|
|
var i=0;
|
|
var len=array.length;
|
|
for(i=0;i<len;i++) {
|
|
fun(array[i],i)
|
|
}
|
|
*/
|
|
array.forEach(fun);
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array1
|
|
* @param {* []} array2
|
|
* @param {function(*,*,number)} fun
|
|
*/
|
|
iter2: function(array1,array2,fun) {
|
|
var i=0;
|
|
assert((array1.length == array2.length)||('Array.iter2: arrays of different lengths'));
|
|
/*
|
|
var len=array1.length;
|
|
for(i=0;i<len;i++) {
|
|
fun(array1[i],array2[i],i)
|
|
}
|
|
*/
|
|
array1.forEach(function (e1,i) { fun(e1,array2[i],i) });
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun Returning a true value leaves iteration loop
|
|
*/
|
|
iter_break: function(array,fun) {
|
|
var i=0;
|
|
var len=array.length;
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
if (fun(element,i)) return;
|
|
}
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
*/
|
|
iter_rev: function(array,fun) {
|
|
var i;
|
|
var len=array.length;
|
|
for(i=len-1;i>=0;i--) {
|
|
fun(array[i],i)
|
|
}
|
|
},
|
|
/** Return last element of array.
|
|
*
|
|
*/
|
|
last : function(array) {
|
|
var len=array.length;
|
|
if (len==0) return none;
|
|
else return array[len-1];
|
|
},
|
|
|
|
length : function(array) {
|
|
return array.length;
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array1
|
|
* @param {* []} array2
|
|
* @param {function(*,*,number)} fun
|
|
* @returns {* []}
|
|
*/
|
|
map2: function(array1,array2,fun) {
|
|
var i=0;
|
|
assert((array1.length == array2.length)||('Array.map2: arrays of different lengths'));
|
|
var len=array1.length;
|
|
var res=[];
|
|
for(i=0;i<len;i++) {
|
|
res.push(fun(array1[i],array2[i],i));
|
|
}
|
|
return res;
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
* @returns {* []}
|
|
*/
|
|
map: function(array,fun) {
|
|
var i=0;
|
|
var len=array.length;
|
|
var res=[];
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
res.push(fun(element,i));
|
|
}
|
|
return res;
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {Function} fun_hdtl - function(hd,tl)
|
|
* @param {Function} [fun_empty] - function()
|
|
*/
|
|
match: function(array,fun_hdtl,fun_empty) {
|
|
if (array.length == 0) {
|
|
if (fun_empty) fun_empty();
|
|
} else if (array.length>1) {
|
|
var hd = this.head(array);
|
|
var tl = this.tail(array);
|
|
fun_hdtl(hd,tl);
|
|
} else fun_hdtl(this.head(array),[]);
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {Function} fun_hd1hd2 - function(hd1,hd2)
|
|
* @param {Function} [fun_hdtl] - function(hd,tl)
|
|
* @param {Function} [fun_empty] - function()
|
|
*/
|
|
match2: function(array,fun_hd1hd2,fun_hdtl,fun_empty) {
|
|
if (array.length == 0 && fun_empty)
|
|
fun_empty();
|
|
else if (array.length == 2) {
|
|
var hd1 = this.head(array);
|
|
var hd2 = this.second(array);
|
|
fun_hd1hd2(hd1,hd2);
|
|
}
|
|
else if (array.length>1 && fun_hdtl) {
|
|
var hd = this.head(array);
|
|
var tl = this.tail(array);
|
|
fun_hdtl(hd,tl);
|
|
} else if (fun_hdtl) fun_hdtl(this.head(array),[]);
|
|
},
|
|
/** Return the maximum element of an array applying
|
|
* an optional mapping function.
|
|
*
|
|
* @param {* []} array
|
|
* @param [fun]
|
|
* @returns {number|undefined}
|
|
*/
|
|
max : function (array,fun) {
|
|
var res,max,num;
|
|
for(var i in array) {
|
|
if (fun) num=fun(array[i]); else num=array[i];
|
|
if (max==undefined) { max=num; res=array[i] }
|
|
else if (num > max) { max=num; res=array[i] }
|
|
}
|
|
return res;
|
|
},
|
|
/** Return the minimum element of an array applying
|
|
* an optional mapping function.
|
|
*
|
|
* @param {* []} array
|
|
* @param [fun]
|
|
* @returns {number|undefined}
|
|
*/
|
|
min : function (array,fun) {
|
|
var res,min,num;
|
|
for(var i in array) {
|
|
if (fun) num=fun(array[i]); else num=array[i];
|
|
if (min==undefined) { min=num; res=array[i] }
|
|
else if (num < min) { min=num; res=array[i] }
|
|
}
|
|
return res;
|
|
},
|
|
/** Check for an element in the array.
|
|
*
|
|
* @param {(number|string|boolean) []} array
|
|
* @param {number|string|boolean} element
|
|
* @returns {boolean}
|
|
*/
|
|
member: function(array,element) {
|
|
var i,exist;
|
|
var len=array.length;
|
|
exist=false;
|
|
loop: for(i=0;i<len;i++) {
|
|
var _element=array[i];
|
|
if (_element==element) {
|
|
exist=true;
|
|
break loop;
|
|
}
|
|
}
|
|
return exist;
|
|
},
|
|
/** Merge all arrays and return a new array.
|
|
*
|
|
* @param {Array} array1
|
|
* @param {Array} array2
|
|
* @param {Array} [array3]
|
|
* @param {Array} [array4]
|
|
* @returns {Array}
|
|
*/
|
|
merge: function(array1,array2,array3,array4) {
|
|
var arraynew=array1.slice();
|
|
arraynew=arraynew.concat(array2);
|
|
if (array3!=undefined) arraynew=arraynew.concat(array3);
|
|
if (array4!=undefined) arraynew=arraynew.concat(array4);
|
|
return arraynew;
|
|
},
|
|
/** Return the next element from array after val (next element after last is first!)
|
|
* @param {Array} array
|
|
* @param {number|string} val
|
|
* @returns {number|string}
|
|
*/
|
|
next: function(array,val) {
|
|
var i;
|
|
var len=array.length;
|
|
if (obj.isString(val))
|
|
for(i=0;i<len;i++) {
|
|
if (string.equal(array[i],val)) {
|
|
if (i==len-1) return array[0];
|
|
else return array[i+1];
|
|
}
|
|
}
|
|
else
|
|
for(i=0;i<len;i++) {
|
|
if (array[i]==val) {
|
|
if (i==len-1) return array[0];
|
|
else return array[i+1];
|
|
}
|
|
}
|
|
|
|
return none;
|
|
},
|
|
/** Evaluate a function returning a boolean value for each member of the array and
|
|
* compute the boolean disjunction.
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,number)} fun
|
|
*/
|
|
or: function(array,fun) {
|
|
var res=false;
|
|
var i=0;
|
|
var len=array.length;
|
|
for(i=0;i<len;i++) {
|
|
var element=array[i];
|
|
res=res||fun(element,i)
|
|
}
|
|
return res;
|
|
},
|
|
|
|
/**
|
|
* Gets the property value of `key` from all elements in `collection`.
|
|
*
|
|
* var users = [
|
|
* { 'user': 'barney', 'age': 36 },
|
|
* { 'user': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* pluck(users, 'user');
|
|
* // => ['barney', 'fred']
|
|
*/
|
|
pluck: function(collection, key) {
|
|
return collection.map(function(object) {
|
|
return object == null ? undefined : object[key];
|
|
});
|
|
},
|
|
/*
|
|
** Push/pop head elements (Stack behaviour)
|
|
*/
|
|
/** Remove and return top element of array.
|
|
*
|
|
* @param array
|
|
* @returns {*}
|
|
*/
|
|
pop : function(array) {
|
|
var element=array[0];
|
|
array.shift();
|
|
return element;
|
|
},
|
|
print: function(array) {
|
|
var i;
|
|
var len=array.length;
|
|
var str='[';
|
|
for(i=0;i<len;i++) {
|
|
var cell=array[i];
|
|
str=str+cell;
|
|
}
|
|
return str+']';
|
|
},
|
|
/** Add new element at top of array.
|
|
*
|
|
* @param array
|
|
* @param element
|
|
*/
|
|
push : function(array,element) {
|
|
array.unshift(element);
|
|
},
|
|
/** Create an ordered array of numbers {a,a+1,..b}
|
|
*
|
|
* @param a
|
|
* @param b
|
|
* @returns {Array}
|
|
*/
|
|
range : function(a,b) {
|
|
var i;
|
|
var array=[];
|
|
for(i=a;i<=b;i++) array.push(i);
|
|
return array;
|
|
},
|
|
/** Remove elements from an array.
|
|
* [1,2,3,4,5,6] (begin=2,end=4) => [1,2,6]
|
|
* @param {* []} array
|
|
* @returns {* []}
|
|
*/
|
|
remove: function(array,begin,end) {
|
|
var i,a;
|
|
if (end==undefined) end=begin+1;
|
|
if (begin<0 || end >= array.length) return [];
|
|
a=array.slice(0,begin);
|
|
for(i=end;i<array.length;i++) a.push(array[i]);
|
|
return a;
|
|
},
|
|
|
|
second : function(array) {
|
|
return array[1];
|
|
},
|
|
/**
|
|
*
|
|
* @param {* []} array
|
|
* @param {function(*,*):number} fun (1:a gt. b by the ordering criterion,-1: a lt. b, 0: a eq. b)
|
|
* @returns {* []}
|
|
*/
|
|
sort: function(array,fun) {
|
|
var array2=array.slice();
|
|
array2.sort(fun);
|
|
return array2;
|
|
},
|
|
/** Split an array at position 'pos', i.e., remove 'len' (1) elements starting at
|
|
* position 'pos'.
|
|
* ==> use remove!!! split should return two arrays!!
|
|
*
|
|
* @param array
|
|
* @param pos
|
|
* @param [len]
|
|
* @param element
|
|
*/
|
|
split: function(array,pos,len) {
|
|
if (pos==0) return array.slice((len||1));
|
|
else {
|
|
var a1=array.slice(0,pos);
|
|
var a2=array.slice(pos+(len||1));
|
|
return a1.concat(a2);
|
|
}
|
|
},
|
|
/** Return the sum number of an array applying
|
|
* an optional mapping function.
|
|
*
|
|
* @param {* []} array
|
|
* @param [fun]
|
|
* @returns {number|undefined}
|
|
*/
|
|
sum : function (array,fun) {
|
|
var res=0;
|
|
for(var i in array) {
|
|
var num=0;
|
|
if (fun) num=fun(array[i]); else num=array[i];
|
|
if (!obj.isNumber(num)) return undefined;
|
|
res += num;
|
|
}
|
|
return res;
|
|
},
|
|
/** Return a new array w/o the head element (or optional
|
|
* w/o the first top elements).
|
|
*
|
|
*/
|
|
tail : function(array,top) {
|
|
var array2=array.slice();
|
|
array2.shift();
|
|
if (top) for(;top>1;top--) array2.shift();
|
|
return array2;
|
|
},
|
|
/** Return union of two sets (== conjunction set)
|
|
*
|
|
* @param {* []} set1
|
|
* @param {* []} set2
|
|
* @param {function} [fun] Equality test
|
|
* @returns {* []}
|
|
*/
|
|
union : function(set1,set2,fun) {
|
|
var i,j,res = [];
|
|
for (i in set1) {
|
|
for (j in set2) {
|
|
if (fun != undefined && fun(set1[i],set2[j])) res.push(set1[i]);
|
|
else if (fun == undefined && set1[i]==set2[j]) res.push(set1[i]);
|
|
}
|
|
}
|
|
return res;
|
|
},
|
|
|
|
/**
|
|
* Creates a duplicate-free version of an array
|
|
*/
|
|
unique: function(array) {
|
|
var length = array ? array.length : 0;
|
|
function baseUniq(array) {
|
|
var index = -1,
|
|
length = array.length,
|
|
seen,
|
|
result = [];
|
|
|
|
seen = result;
|
|
outer:
|
|
while (++index < length) {
|
|
var value = array[index];
|
|
var seenIndex = seen.length;
|
|
while (seenIndex--) {
|
|
if (seen[seenIndex] === value) {
|
|
continue outer;
|
|
}
|
|
}
|
|
result.push(value);
|
|
}
|
|
return result;
|
|
}
|
|
if (!length) {
|
|
return [];
|
|
}
|
|
return baseUniq(array);
|
|
},
|
|
|
|
/**
|
|
* Creates an array excluding all provided values
|
|
* without([1, 2, 1, 3], 1, 2);
|
|
* // => [3]
|
|
*/
|
|
without: function () {
|
|
var array,
|
|
values=[];
|
|
for(var i in arguments) {
|
|
if (i==0) array=arguments[0];
|
|
else values.push(arguments[i]);
|
|
}
|
|
return array.filter(function (e) {
|
|
return values.indexOf(e) == -1;
|
|
});
|
|
},
|
|
/** Test for zero elements {0, '', false, undefined, ..}
|
|
*/
|
|
zero: function (array) {
|
|
for(var i in array) if (!!array[i]) return false;
|
|
return true;
|
|
},
|
|
};
|
|
|
|
/** STRING
|
|
*
|
|
*/
|
|
var string = {
|
|
/** Is pattern conatined in template?
|
|
*
|
|
*/
|
|
contains: function (template,pattern) {
|
|
return template.indexOf(pattern)>-1;
|
|
},
|
|
copy: function(src) {
|
|
var i;
|
|
var dst='';
|
|
for(i=0;i<src.length;i++) dst=dst+src.charAt(i);
|
|
return dst;
|
|
},
|
|
/**
|
|
*
|
|
* @param {number} size
|
|
* @returns {string} filled with spaces
|
|
*/
|
|
create: function(size)
|
|
{
|
|
var i;
|
|
var s='';
|
|
var init=' ';
|
|
for(i=0;i<size;i++) s=s+init;
|
|
return s;
|
|
},
|
|
endsWith : function (str,tail) {
|
|
return str.indexOf(tail)==(str.length-tail.length);
|
|
},
|
|
empty: function (str) {
|
|
return this.equal(str,'');
|
|
},
|
|
equal: function(str1,str2) {
|
|
var i;
|
|
var eq=true;
|
|
if (str1.length != str2.length) return false;
|
|
for(i=0;i<str1.length;i++) { if (string.get(str1,i)!=string.get(str2,i)) eq=false;}
|
|
return eq;
|
|
},
|
|
find: function (search,str) {
|
|
return str.indexOf(search);
|
|
},
|
|
format_hex: function (n,len) {
|
|
// format a hexadecimal number with 'len' figures.
|
|
switch (len) {
|
|
case 2: return (((n>>4) & 0xf).toString(16))+
|
|
((n&0xf).toString(16));
|
|
case 4: return (((n>>12) & 0xf).toString(16)+
|
|
((n>>8) & 0xf).toString(16)+
|
|
((n>>4) & 0xf).toString(16)+
|
|
(n&0xf).toString(16));
|
|
case 6: return (((n>>20) & 0xf).toString(16)+
|
|
((n>>16) & 0xf).toString(16)+
|
|
((n>>12) & 0xf).toString(16)+
|
|
((n>>8) & 0xf).toString(16)+
|
|
((n>>4) & 0xf).toString(16)+
|
|
(n&0xf).toString(16));
|
|
case 8: return (((n>>28) & 0xf).toString(16)+
|
|
((n>>24) & 0xf).toString(16)+
|
|
((n>>20) & 0xf).toString(16)+
|
|
((n>>16) & 0xf).toString(16)+
|
|
((n>>12) & 0xf).toString(16)+
|
|
((n>>8) & 0xf).toString(16)+
|
|
((n>>4) & 0xf).toString(16)+
|
|
(n&0xf).toString(16));
|
|
default: return 'format_hex??';
|
|
}
|
|
},
|
|
/**
|
|
*
|
|
* @param {string} str
|
|
* @param {number} index
|
|
* @returns {string}
|
|
*/
|
|
get: function (str,index) {
|
|
assert((str != undefined && index < str.length && index >= 0)||('string.get ('+str.length+')'));
|
|
return str.charAt(index);
|
|
},
|
|
isBoolean: function (str) {
|
|
return (str=='true' || str=='false')
|
|
},
|
|
isNumeric: function (str) {
|
|
return !isNaN(parseFloat(str)) && isFinite(str);
|
|
},
|
|
isText: function (s) {
|
|
var is_text=true;
|
|
string.iter(s,function (ch,i) {
|
|
string.match(ch,[
|
|
['a','z',function () {}],
|
|
['A','Z',function () {}],
|
|
['0','9',function () {if (i==0) is_text=false;}],
|
|
function () {is_text=false;}
|
|
]);
|
|
});
|
|
return is_text;
|
|
},
|
|
/**
|
|
*
|
|
* @param {string} str
|
|
* @param {function(string,number)} fun
|
|
*/
|
|
iter: function(str,fun) {
|
|
var i;
|
|
var len=str.length;
|
|
for (i = 0; i < len; i++) {
|
|
var c = str.charAt(i);
|
|
fun(c,i);
|
|
}
|
|
},
|
|
/**
|
|
*
|
|
* @param str
|
|
* @returns {*}
|
|
*/
|
|
length: function(str) {
|
|
if (str!=undefined) return str.length;
|
|
else return 0;
|
|
},
|
|
/**
|
|
*
|
|
* @param str
|
|
* @returns {string}
|
|
*/
|
|
lowercase : function (str) {
|
|
return str.toLowerCase();
|
|
},
|
|
/**
|
|
*
|
|
* @param {number} size
|
|
* @param {string} init
|
|
* @returns {string}
|
|
*/
|
|
make: function(size,init)
|
|
{
|
|
var i;
|
|
var s='';
|
|
for(i=0;i<size;i++) s=s+init;
|
|
return s;
|
|
},
|
|
/** Map a string with a set of (test,reuslt) transformation rules.
|
|
*
|
|
* @param {string} str
|
|
* @param {* [] []} case - ([string,string] | fun) []
|
|
*/
|
|
map: function(str,mapping) {
|
|
var i;
|
|
var map;
|
|
for(i in mapping) {
|
|
map=mapping[i];
|
|
if (obj.isFunction(map)) return map(str);
|
|
else if (this.equal(str,map[0])) return map[1];
|
|
}
|
|
},
|
|
/** Match a string with different patterns and apply a matching function.
|
|
*
|
|
* @param {string} str
|
|
* @param {* [] []} cases - ([string,fun] | [string [<case1>,<case2>,..],fun] | [<range1>:string,<range2>:string,fun] | fun) []
|
|
*/
|
|
match: function(str,cases) {
|
|
var i,j;
|
|
var cas,cex,cv;
|
|
for(i in cases) {
|
|
cas=cases[i];
|
|
if (obj.isArray(cas)) {
|
|
switch (cas.length) {
|
|
case 2:
|
|
// Multi-value-case
|
|
cex=cas[0];
|
|
if (!obj.isArray(cex)) {
|
|
if (this.equal(str,cex)) {
|
|
cas[1]();
|
|
return;
|
|
}
|
|
} else {
|
|
for(j in cex) {
|
|
cv=cex[j];
|
|
if (this.equal(str,cv)) {
|
|
cas[1]();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
// Character range check
|
|
try {
|
|
j=pervasives.int_of_char(str);
|
|
if (j>= pervasives.int_of_char(cas[0]) && j<=pervasives.int_of_char(cas[1])) {
|
|
cas[2](str);
|
|
return;
|
|
}
|
|
} catch(e) {
|
|
return
|
|
};
|
|
break;
|
|
case 1:
|
|
cas[0](str); // Default case - obsolete
|
|
return;
|
|
default:
|
|
throw 'String.match #args';
|
|
}
|
|
} else if (obj.isFunction(cas)) {
|
|
// Default case
|
|
cas(str);
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
/** Pad a string on the left (pre-str.length) if pre>0,
|
|
* right (post-str.length) if post>0, or centered (pre>0&post>0).
|
|
*
|
|
*/
|
|
|
|
pad: function (str,pre,post,char) {
|
|
var len = str.length;
|
|
if (pre>0 && post==0) return string.make(len-pre,char||' ')+str;
|
|
else if (post>0 && pre==0) return str+string.make(post-len,char||' ');
|
|
else return string.make(len-pre/2,char||' ')+str+string.make(len-post/2,char||' ');
|
|
},
|
|
/**
|
|
*
|
|
* @param str
|
|
* @param pos
|
|
* @param len
|
|
* @returns {Number}
|
|
*/
|
|
parse_hex: function (str,pos,len) {
|
|
// parse a hexadecimal number in string 'str' starting at position 'pos' with 'len' figures.
|
|
return parseInt(this.sub(str,pos,len),16);
|
|
},
|
|
/** Return the sub-string after a point in the source string ('.' or optional point string).
|
|
* If there is no splitting point, the original string is returned.
|
|
*
|
|
* @param str
|
|
* @param [point]
|
|
* @returns {string}
|
|
*/
|
|
postfix: function (str,point) {
|
|
var n = str.indexOf(point||'.');
|
|
if (n <= 0) return str;
|
|
else return str.substr(n+1);
|
|
},
|
|
/** Return the sub-string before a point in the source string ('.' or optional point string)
|
|
* If there is no splitting point, the original string is returned.
|
|
*
|
|
* @param str
|
|
* @param [point]
|
|
* @returns {string}
|
|
*/
|
|
prefix: function (str,point) {
|
|
var n = str.indexOf(point||'.');
|
|
if (n <= 0) return str;
|
|
else return str.substr(0,n);
|
|
},
|
|
replace_first: function (pat,repl,str) {
|
|
return str.replace(pat,repl);
|
|
},
|
|
replace_all: function (pat,repl,str) {
|
|
return str.replace('/'+pat+'/g',repl);
|
|
},
|
|
/**
|
|
*
|
|
* @param str
|
|
* @param index
|
|
* @param char
|
|
* @returns {string}
|
|
*/
|
|
set: function (str,index,char) {
|
|
assert((str != undefined && index < str.length && index >= 0)||'string.get');
|
|
return str.substr(0, index) + char + str.substr(index+1)
|
|
},
|
|
/**
|
|
*
|
|
* @param delim
|
|
* @param str
|
|
* @returns {*|Array}
|
|
*/
|
|
split: function (delim,str) {
|
|
return str.split(delim);
|
|
},
|
|
startsWith : function (str,head) {
|
|
return !str.indexOf(head);
|
|
},
|
|
/** Return a sub-string.
|
|
*
|
|
* @param str
|
|
* @param off
|
|
* @param [len] If not give, return a sub-string from off to end
|
|
* @returns {string}
|
|
*/
|
|
sub: function (str,off,len) {
|
|
if (len)
|
|
return str.substr(off,len);
|
|
else
|
|
return str.substr(off);
|
|
},
|
|
/** Remove leading and trailing characters from string
|
|
*
|
|
* @param str
|
|
* @param {number} pref number of head characters to remove
|
|
* @param {number} post number of tail characters to remove
|
|
* @returns {*}
|
|
*/
|
|
trim: function (str,pref,post) {
|
|
if (str.length==0 ||
|
|
pref>str.length ||
|
|
post>str.length ||
|
|
pref < 0 || post < 0 ||
|
|
(pref==0 && post==0)
|
|
) return str;
|
|
return str.substr(pref,str.length-pref-post);
|
|
},
|
|
/** Return a string with all characters converted to uppercase letters.
|
|
*
|
|
* @param str
|
|
* @returns {string}
|
|
*/
|
|
uppercase : function (str) {
|
|
return str.toUpperCase();
|
|
},
|
|
/** Return a string with first character converted to uppercase letter.
|
|
*
|
|
* @param str
|
|
* @returns {string}
|
|
*/
|
|
Uppercase : function (str) {
|
|
var len = str.length;
|
|
if (len > 1) {
|
|
var head = str.substr(0,1);
|
|
var tail = str.substr(1,len-1);
|
|
return head.toUpperCase()+tail.toLowerCase()
|
|
} if (len==1) return str.toUpperCase();
|
|
else return '';
|
|
}
|
|
};
|
|
|
|
/** RANDOM
|
|
*
|
|
*/
|
|
var rnd = Math.random;
|
|
/* Antti Syk\E4ri's algorithm adapted from Wikipedia MWC
|
|
** Returns a random generator function [0.0,1.0| with seed initialization
|
|
*/
|
|
var seeder = function(s) {
|
|
var m_w = s;
|
|
var m_z = 987654321;
|
|
var mask = 0xffffffff;
|
|
|
|
return function() {
|
|
m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
|
|
m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
|
|
|
|
var result = ((m_z << 16) + m_w) & mask;
|
|
result /= 4294967296;
|
|
|
|
return result + 0.5;
|
|
}
|
|
}
|
|
|
|
var random = {
|
|
float: function(max) {
|
|
return rnd()*max
|
|
},
|
|
int: function(max) {
|
|
return Math.floor(rnd()*max+0)
|
|
},
|
|
// integer
|
|
interval: function(min,max) {
|
|
return Math.round(min+rnd()*(max-min))
|
|
},
|
|
// float
|
|
range: function(min,max) {
|
|
return min+rnd()*(max-min)
|
|
},
|
|
seed: function (s) {
|
|
// Create a new initialized random generator
|
|
rnd=seeder(s);
|
|
}
|
|
};
|
|
|
|
/** PRINTF
|
|
*
|
|
*/
|
|
var printf = {
|
|
/** Trim string(s).
|
|
*
|
|
* @param str
|
|
* @param indent
|
|
* @param [width]
|
|
* @param {string} [tab]
|
|
* @returns {string}
|
|
*/
|
|
align: function (str,indent,width,tab) {
|
|
var lines = string.split('\n',str);
|
|
var form = '';
|
|
var sp = printf.spaces(indent);
|
|
var spbreak = sp;
|
|
|
|
array.iter(lines,function(line){
|
|
var rest;
|
|
function breakit(spbreak,str) {
|
|
if (width < (str.length + spbreak.length)) {
|
|
return spbreak+string.sub(str,0,width-spbreak.length)+'\n'+
|
|
breakit(spbreak,string.sub(str,width-spbreak.length,str.length-width+spbreak.length));
|
|
} else return spbreak+str+'\n';
|
|
}
|
|
if (width && width < (line.length + indent)) {
|
|
if (tab) {
|
|
var pos = string.find(tab,line);
|
|
if (pos > 0 && pos < width) spbreak=printf.spaces(pos+indent+1);
|
|
else spbreak=sp;
|
|
}
|
|
form=form+sp+string.sub(line,0,width-indent)+'\n';
|
|
rest=string.sub(line,width-indent,line.length-width+indent);
|
|
form=form+breakit(spbreak,rest);
|
|
}
|
|
else
|
|
form=form+sp+line+'\n';
|
|
});
|
|
return form;
|
|
},
|
|
/** Format a list of array elements using the (optional) mapping
|
|
* function <fun> and the separator <sep> (optional, too, default is ',').
|
|
*
|
|
*/
|
|
list: function (array,fun,sep) {
|
|
var i, str='';
|
|
if (sep==undefined) sep=',';
|
|
if (fun==undefined) fun=function (s) {return s;};
|
|
if (!obj.isArray(array)) array=[array];
|
|
for (i in array) {
|
|
if (str==='') str=fun(array[i]);
|
|
else str=str+sep+fun(array[i]);
|
|
}
|
|
return str;
|
|
},
|
|
/**
|
|
*
|
|
* @param n
|
|
* @returns {string}
|
|
*/
|
|
spaces: function (n){
|
|
return string.make(n,' ');
|
|
},
|
|
/** Formatted printer (simplified)
|
|
*
|
|
* @param {* []} args (['%format',arg]|string) [] format=%s,%d,%f,%c,%x,%#d,%#s,..
|
|
* @returns {string}
|
|
*/
|
|
sprintf2: function(args) {
|
|
var str='';
|
|
array.iter(args,function(fmtarg) {
|
|
var len, n,fs;
|
|
if (obj.isArray(fmtarg)) {
|
|
if (fmtarg.length==2) {
|
|
var fmt=fmtarg[0];
|
|
var arg=fmtarg[1];
|
|
var fc='';
|
|
var fn=0;
|
|
string.iter(fmt,function(c) {
|
|
if (c=='s' || c=='d' || c=='f' || c=='x') {
|
|
fc=c;
|
|
} else if (c!='%') {
|
|
fn=fn*10;
|
|
n=parseInt(c);
|
|
if (!isNaN(n)) fn=fn+n;
|
|
}
|
|
});
|
|
if (fc=='s' && obj.isString(arg)) {
|
|
str=str+arg;
|
|
if (fn!=0) {
|
|
len=arg.length;
|
|
if (len<fn) str=str+string.create(fn-len);
|
|
}
|
|
} else if (fc=='d' && obj.isNumber(arg)) {
|
|
fs = pervasives.string_of_int(arg);
|
|
if (fn!=0) {
|
|
len = fs.length;
|
|
if (len < fn) {
|
|
str=str+string.create(fn-len);
|
|
}
|
|
}
|
|
str=str+fs;
|
|
} else if (fc=='x' && obj.isNumber(arg)) {
|
|
fs = string.format_hex(arg,fn||8);
|
|
str=str+fs;
|
|
}
|
|
}
|
|
} else if (obj.isString(fmtarg)) {
|
|
str = str + fmtarg;
|
|
}
|
|
});
|
|
return str;
|
|
},
|
|
sprintf:Sprintf.sprintf
|
|
};
|
|
|
|
/** FILENAME
|
|
*
|
|
*/
|
|
var filename = {
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {string}
|
|
*/
|
|
basename : function (path) {
|
|
return Path.basename(path);
|
|
},
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {string}
|
|
*/
|
|
dirname : function (path) {
|
|
return Path.dirname(path);
|
|
},
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {string}
|
|
*/
|
|
extname : function (path) {
|
|
return Path.extname(path)
|
|
},
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {boolean}
|
|
*/
|
|
is_relative: function(path) {
|
|
return !(path.length > 0 && path[0] == '/');
|
|
},
|
|
/**
|
|
*
|
|
* @param pathl
|
|
* @param absolute
|
|
* @returns {string}
|
|
*/
|
|
join: function (pathl,absolute) {
|
|
var path=(absolute?'/':'');
|
|
array.iter(pathl,function (name,index) {
|
|
if (index>0) {
|
|
path=path+'/'+name;
|
|
}
|
|
else {
|
|
path=path+name;
|
|
}
|
|
});
|
|
return path;
|
|
},
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {string}
|
|
*/
|
|
normalize : function (path) {
|
|
return Path.normalize(path)
|
|
},
|
|
/**
|
|
*
|
|
* @param path
|
|
* @returns {*}
|
|
*/
|
|
path_absolute: function (path) {
|
|
if (this.is_relative(path)) {
|
|
var workdir = Io.workdir();
|
|
return this.path_normalize(workdir + '/' + path);
|
|
} else return this.path_normalize(path);
|
|
},
|
|
/** Duplicate of Path.normalize!?
|
|
*
|
|
* @param path
|
|
* @returns {string}
|
|
*/
|
|
path_normalize: function (path) {
|
|
var i;
|
|
if (string.equal(path, '')) path = '/';
|
|
var relpath = !(string.get(path, 0) == '/');
|
|
var pathlist = path.split('/');
|
|
var pathlist2 = pathlist.filter(function (s) {
|
|
return (!string.equal(s, '') && !string.equal(s, '.'))
|
|
});
|
|
var pathlist3 = [];
|
|
array.iter(pathlist2, function (pe) {
|
|
if (!string.equal(pe, '..')) {
|
|
array.push(pathlist3, pe)
|
|
} else {
|
|
if (pathlist3.length == 0) return '';
|
|
else
|
|
pathlist3 = array.tail(pathlist3);
|
|
}
|
|
});
|
|
var path2 = '';
|
|
i = 0;
|
|
array.iter(pathlist3, function (pe) {
|
|
var sep;
|
|
if (i == 0) sep = ''; else sep = '/';
|
|
path2 = pe + sep + path2;
|
|
i++;
|
|
});
|
|
if (relpath) return path2; else return '/' + path2;
|
|
},
|
|
removeext: function (path) {
|
|
return path.substr(0, path.lastIndexOf('.'));
|
|
}
|
|
};
|
|
|
|
/** PERVASIVES
|
|
*
|
|
*
|
|
*/
|
|
var pervasives = {
|
|
assert:assert,
|
|
char_of_int: function (i) {return String.fromCharCode(i)},
|
|
div: function(a,b) {return a/b|0;},
|
|
failwith: function(msg) {Io.err(msg);},
|
|
float_of_string: function(s) {var num=parseFloat(s); if (isNaN(num)) throw 'NaN'; else return num;},
|
|
int_of_char: function(c) {return c.charCodeAt()},
|
|
int_of_float: function(f) {return f|0;},
|
|
int_of_string: function(s) {
|
|
var num=parseInt(s); if (isNaN(num)) throw 'NaN'; else return num;
|
|
},
|
|
|
|
/** Try to find a value in a search list and return a mapping value.
|
|
*
|
|
* @param {*} value
|
|
* @param {* []} mapping [testval,mapval] []
|
|
* @returns {*}
|
|
*/
|
|
map: function(value,mapping) {
|
|
function eq(v1,v2) {
|
|
if (v1==v2) return true;
|
|
if (obj.isString(v1) && obj.isString(v2)) return string.equal(v1,v2);
|
|
return false;
|
|
}
|
|
if (!array.empty(mapping)) {
|
|
var hd=array.head(mapping);
|
|
var tl=array.tail(mapping);
|
|
if (eq(hd[0],value)) return hd[1];
|
|
else return pervasives.map(value,tl);
|
|
} else return undefined;
|
|
},
|
|
/** Apply a matcher function to a list of cases with case handler functions.
|
|
* A case is matched if the matcher function returns a value/object.
|
|
*
|
|
* The result of the matcher function is passed as an argument ot the case handler function.
|
|
* The return value of the case handler fucntion is finally returned by this match function
|
|
* or undefined if there was no matching case.
|
|
*
|
|
* @param {function(*,*):*} matcher function(expr,pat)
|
|
* @param {*} expr
|
|
* @param {*[]} cases (pattern,handler function | handler function) []
|
|
* @returns {*|undefined}
|
|
*/
|
|
match: function (matcher,expr,cases) {
|
|
var ret = undefined;
|
|
array.iter_break(cases, function (match) {
|
|
var quit, succ, pat, fun;
|
|
|
|
if (match.length == 2) {
|
|
/*
|
|
** Pattern, Function
|
|
*/
|
|
pat = match[0];
|
|
fun = match[1];
|
|
succ = matcher(expr, pat);
|
|
if (succ) ret = fun(succ);
|
|
quit = succ!=undefined;
|
|
} else if (match.length == 1) {
|
|
/*
|
|
** Default case, Function
|
|
*/
|
|
fun = match[0];
|
|
ret = fun();
|
|
quit= true;
|
|
}
|
|
return quit;
|
|
});
|
|
return ret;
|
|
},
|
|
mtime: function () {var time = new Date(); return time.getTime();},
|
|
min: function(a,b) { return (a<b)?a:b},
|
|
max: function(a,b) { return (a>b)?a:b},
|
|
string_of_float: function(f) {return f.toString()},
|
|
string_of_int: function(i) {return i.toString()},
|
|
string_of_int64: function(i) {return i.toString()},
|
|
time: function () {var time = new Date(); return (time.getTime()/1000)|0;}
|
|
};
|
|
|
|
/** BIT
|
|
*
|
|
*/
|
|
var bit = {
|
|
get: function (v,b) {return (v >> b) && 1;},
|
|
isSet: function (v,b) {return ((v >> b) && 1)==1;},
|
|
set: function (v,b) {return v & (1 << b);}
|
|
};
|
|
|
|
/** ARGS
|
|
*
|
|
*/
|
|
var args = {
|
|
/** Parse process or command line arguments (array argv). The first offset [1] arguments are
|
|
** ignored. The numarg pattern '*' consumes all remaining arguments.
|
|
*
|
|
* @param {string []} argv
|
|
* @param {*[]} map [<argname>,<numargs:0..3|'*'>,<handler(up to 3 arguments|[])>]|[<defhandler(val)>] []
|
|
* @param {number} [offset]
|
|
*/
|
|
parse: function(argv,map,offset) {
|
|
var shift=undefined,
|
|
in_shift=0,
|
|
shift_args=[],
|
|
names,
|
|
mapfun,
|
|
numarg,
|
|
len=argv.length;
|
|
|
|
if (offset==undefined) offset=1;
|
|
|
|
argv.forEach(function (val, index) {
|
|
var last=index==(len-1);
|
|
if(index>=offset) {
|
|
if (in_shift==0) {
|
|
array.check(map,function (onemap) {
|
|
assert(onemap!=undefined||'map');
|
|
if (onemap.length==3) {
|
|
names = onemap[0];
|
|
numarg = onemap[1];
|
|
mapfun = onemap[2];
|
|
if (!obj.isArray(names)) names=[names];
|
|
var found = array.find(names,function (name) {
|
|
if (string.equal(val, name)) return name; else _;
|
|
});
|
|
if (found) {
|
|
if (numarg==0) mapfun(found);
|
|
else {
|
|
in_shift=numarg;
|
|
shift_args=[];
|
|
shift=mapfun;
|
|
}
|
|
return true;
|
|
}
|
|
} else if (obj.isFunction(onemap)) {
|
|
onemap(val);
|
|
return true;
|
|
} else if (onemap.length==1) {
|
|
mapfun = onemap[0];
|
|
mapfun(val);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
} else {
|
|
shift_args.push(val);
|
|
if (in_shift!='*') in_shift--;
|
|
if (in_shift==0 && shift!=undefined) {
|
|
numarg=shift_args.length;
|
|
switch (numarg) {
|
|
case 0: shift(val);break;
|
|
case 1: shift(shift_args[0],val); break;
|
|
case 2: shift(shift_args[0],shift_args[1],val); break;
|
|
case 3: shift(shift_args[0],shift_args[1],shift_args[2],val); break;
|
|
default: break;
|
|
}
|
|
shift=undefined;
|
|
} else if (in_shift=='*' && last) shift(shift_args);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
/** HASHTBL
|
|
*
|
|
*/
|
|
var hashtbl = {
|
|
add: function(hash,key,data) {
|
|
hash[key]=data;
|
|
},
|
|
create: function(initial) {
|
|
return [];
|
|
},
|
|
empty: function(hash) {
|
|
for (var key in hash) return false;
|
|
return true;
|
|
},
|
|
find: function(hash,key) {
|
|
return hash[key];
|
|
},
|
|
invalidate: function(hash,key) {
|
|
hash[key]=undefined;
|
|
},
|
|
iter: function(hash,fun) {
|
|
for (var key in hash) {
|
|
if (hash[key]!=undefined) fun(key,hash[key]);
|
|
}
|
|
},
|
|
mem: function(hash,key) {
|
|
return hash[key] != undefined;
|
|
},
|
|
remove: function(hash,key) {
|
|
// TODO: check, its wrong!
|
|
if (!hash.hasOwnProperty(key))
|
|
return;
|
|
if (isNaN(parseInt(key)) || !(hash instanceof Array))
|
|
delete hash[key];
|
|
else
|
|
hash.splice(key, 1)
|
|
}
|
|
};
|
|
|
|
var types = [];
|
|
/**
|
|
*
|
|
* @param name
|
|
* @returns {number}
|
|
*/
|
|
function register_type(name) {
|
|
var typoff = 1000+types.length*1000;
|
|
if (array.member(types,name)) throw('[COMP] register_type: type '+name+' exists already.');
|
|
types.push(name);
|
|
return typoff;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @typedef {{v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tuple
|
|
*/
|
|
/**
|
|
*
|
|
* @typedef {{t:number, v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tagged_tuple
|
|
*/
|
|
|
|
module.exports = {
|
|
args:args,
|
|
assert: assert,
|
|
array:array,
|
|
bit:bit,
|
|
copy:obj.copy,
|
|
div:pervasives.div,
|
|
filename:filename,
|
|
hashtbl:hashtbl,
|
|
isNodeJS: function () {
|
|
return (typeof global !== "undefined" &&
|
|
{}.toString.call(global) == '[object global]');
|
|
},
|
|
obj:obj,
|
|
pervasives:pervasives,
|
|
printf:printf,
|
|
random:random,
|
|
string:string,
|
|
isArray: obj.isArray,
|
|
isString: obj.isString,
|
|
isNumber: obj.isNumber,
|
|
|
|
register_type:register_type,
|
|
/**
|
|
*
|
|
* @param tag
|
|
* @param [val1]
|
|
* @param [val2]
|
|
* @param [val3]
|
|
* @returns {(tagged_tuple)}
|
|
*/
|
|
Tuple: function (tag,val1,val2,val3) {
|
|
if(val3) return {t:tag,v1:val1,v2:val2,v3:val3};
|
|
else if (val2) return {t:tag,v1:val1,v2:val2};
|
|
else if (val1) return {t:tag,v1:val1};
|
|
else return {t:tag};
|
|
}
|
|
};
|