summaryrefslogtreecommitdiff
path: root/framework/Web/Javascripts/extended
diff options
context:
space:
mode:
Diffstat (limited to 'framework/Web/Javascripts/extended')
-rw-r--r--framework/Web/Javascripts/extended/array.js465
-rw-r--r--framework/Web/Javascripts/extended/base.js24
-rw-r--r--framework/Web/Javascripts/extended/dom.js6
-rw-r--r--framework/Web/Javascripts/extended/event.js24
-rw-r--r--framework/Web/Javascripts/extended/functional.js171
-rw-r--r--framework/Web/Javascripts/extended/string.js37
-rw-r--r--framework/Web/Javascripts/extended/util.js90
7 files changed, 817 insertions, 0 deletions
diff --git a/framework/Web/Javascripts/extended/array.js b/framework/Web/Javascripts/extended/array.js
new file mode 100644
index 00000000..2aeb9084
--- /dev/null
+++ b/framework/Web/Javascripts/extended/array.js
@@ -0,0 +1,465 @@
+/**
+ARRAY EXTENSIONS
+by Caio Chassot (http://v2studio.com/k/code/)
+*/
+
+//function v2studio_com_code()
+//{
+
+
+/**
+ * Searches Array for <b>value</b>.
+ * returns the index of the first item
+ * which matches <b>value</b>, or -1 if not found.
+ * searching starts at index 0, or at <b>start</b>, if specified.
+ *
+ * Here are the rules for an item to match <b>value</b>
+ * if strict is false or not specified (default):
+ * if <b>value</b> is a:
+ * # <b>function</b> -> <b>value(item)</b> must be true
+ * # <b>RegExp</b> -> <b>value.test(item)</b> must be true
+ * # anything else -> <b>item == value</b> must be true
+ * @param value the value (function, regexp) to search
+ * @param start where to start the search
+ * @param strict use strict comparison (===) for everything
+ */
+Array.prototype.indexOf = function(value, start, strict) {
+ start = start || 0;
+ for (var i=start; i<this.length; i++) {
+ var item = this[i];
+ if (strict ? item === value :
+ isRegexp(value) ? value.test(item) :
+ isFunction(value) ? value(item) :
+ item == value)
+ return i;
+ }
+ return -1;
+}
+
+/**
+ * searches Array for <b>value</b> returns the first matched item, or null if not found
+ * Parameters work the same as indexOf
+ * @see #indexOf
+ */
+Array.prototype.find = function(value, start, strict) {
+ var i = this.indexOf(value, start, strict);
+ if (i != -1) return this[i];
+ return null
+}
+
+
+
+/* A.contains(value [, strict])
+/**
+ * aliases: has, include
+ * returns true if <b>value</b> is found in Array, otherwise false;
+ * relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
+ * @see #indexOf
+ */
+Array.prototype.contains = function(value,strict) {
+ return this.indexOf(value,0,strict) !== -1;
+}
+
+
+Array.prototype.has = Array.prototype.contains;
+
+Array.prototype.include = Array.prototype.contains;
+
+
+/**
+ * counts occurences of <b>value</b> in Array
+ * relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
+ * @see #indexOf
+ */
+Array.prototype.count = function(value, strict) {
+ var pos, start = 0, count = 0;
+ while ((pos = this.indexOf(value, start, strict)) !== -1) {
+ start = pos + 1;
+ count++;
+ }
+ return count;
+}
+
+
+/**
+ * if <b>all</b> is false or not provied:
+ * removes first occurence of <b>value</b> from Array
+ * if <b>all</b> is provided and true:
+ * removes all occurences of <b>value</b> from Array
+ * returns the array
+ * relies on indexOf, see its doc for details on <b>value</b> and <b>strict</b>
+ * @see #indexOf
+ */
+Array.prototype.remove = function(value,all,strict) {
+ while (this.contains(value,strict)) {
+ this.splice(this.indexOf(value,0,strict),1);
+ if (!all) break
+ }
+ return this;
+}
+
+
+
+/* A.merge(a [, a]*)
+ Append the contents of provided arrays into the current
+ takes: one or more arrays
+ returns: current array (modified)
+*/
+Array.prototype.merge = function() {
+ var a = [];
+ for (var i=0; i<arguments.length; i++)
+ for (var j=0; j<arguments[i].length; j++)
+ a.push(arguments[i][j]);
+ for (var i=0; i<a.length; i++) this.push(a[i]);
+ return this
+}
+
+
+
+/* A.min()
+ returns the smallest item in array by comparing them with >
+*/
+Array.prototype.min = function() {
+ if (!this.length) return;
+ var n = this[0];
+ for (var i=1; i<this.length; i++) if (n>this[i]) n=this[i];
+ return n;
+}
+
+
+
+/* A.min()
+ returns the graetest item in array by comparing them with <
+*/
+Array.prototype.max = function() {
+ if (!this.length) return;
+ var n = this[0];
+ for (var i=1; i<this.length; i++) if (n<this[i]) n=this[i];
+ return n;
+}
+
+
+
+/* A.first()
+ returns first element of Array
+*/
+Array.prototype.first = function() { return this[0] }
+
+
+
+/* A.last()
+ returns last element of Array
+*/
+Array.prototype.last = function() { return this[this.length-1] }
+
+
+
+/* A.sjoin()
+ Shorthand for A.join(' ')
+*/
+Array.prototype.sjoin = function() { return this.join(' ') }
+
+
+
+/* A.njoin()
+ Shorthand for A.join('\n')
+*/
+Array.prototype.njoin = function() { return this.join('\n') }
+
+
+
+/* A.cjoin()
+ Shorthand for A.join(', ')
+*/
+Array.prototype.cjoin = function() { return this.join(', ') }
+
+
+
+/* A.equals(a [, strict])
+ true if all elements of array are equal to all elements of `a` in the same
+ order. if strict is specified and true, all elements must be equal and of
+ the same type.
+*/
+Array.prototype.equals = function(a, strict){
+ if (this==a) return true;
+ if (a.length != this.length) return false;
+ return this.map(function(item,idx){
+ return strict? item === a[idx] : item == a[idx]
+ }).all();
+}
+
+
+
+/* A.all([fn])
+ Returns true if fn returns true for all elements in array
+ if fn is not specified, returns true if all elements in array evaluate to
+ true
+*/
+Array.prototype.all = function(fn) {
+ return filter(this, fn).length == this.length;
+}
+
+
+
+/* A.any([fn])
+ Returns true if fn returns true for any elements in array
+ if fn is not specified, returns true if at least one element in array
+ evaluates to true
+*/
+Array.prototype.any = function(fn) {
+ return filter(this, fn).length > 0;
+}
+
+
+
+/* A.each(fn)
+ method form of each function
+*/
+Array.prototype.each = function(fn) { return each(this, fn) }
+
+
+
+/* A.map([fn])
+ method form of map function
+*/
+Array.prototype.map = function(fn) { return map(this, fn) }
+
+
+
+/* A.filter([fn])
+ method form of filter function
+*/
+Array.prototype.filter = function(fn) { return filter(this, fn) }
+
+
+Array.prototype.select = Array.prototype.filter
+
+
+/* A.reduce([initial,] fn)
+ method form of filter function
+*/
+Array.prototype.reduce = function() {
+ var args = map(arguments);
+ fn = args.pop();
+ d = args.pop();
+ return reduce(this, d, fn);
+}
+
+
+Array.prototype.inject = Array.prototype.reduce
+
+
+
+/* A.reject(fn)
+ deletes items in A *in place* for which fn(item) is true
+ returns a
+*/
+Array.prototype.reject = function(fn) {
+ if (typeof(fn)=='string') fn = __strfn('item,idx,list', fn);
+ var self = this;
+ var itemsToRemove = [];
+ fn = fn || function(v) {return v};
+ map(self, function(item,idx,list) { if (fn(item,idx,list)) itemsToRemove.push(idx) } );
+ itemsToRemove.reverse().each(function(idx) { self.splice(idx,1) });
+ return self;
+}
+
+
+
+/* __strfn(args, fn)
+ this is used internally by each, map, combine, filter and reduce to accept
+ strings as functions.
+
+ takes:
+ `args` -> a string of comma separated names of the function arguments
+ `fn` -> the function body
+
+ if `fn` does not contain a return statement, a return keyword will be added
+ before the last statement. the last statement is determined by removing the
+ trailing semicolon (';') (if it exists) and then searching for the last
+ semicolon, hence, caveats may apply (i.e. if the last statement has a
+ string or regex containing the ';' character things will go wrong)
+*/
+function __strfn(args, fn) {
+ function quote(s) { return '"' + s.replace(/"/g,'\\"') + '"' }
+ if (!/\breturn\b/.test(fn)) {
+ fn = fn.replace(/;\s*$/, '');
+ fn = fn.insert(fn.lastIndexOf(';')+1, ' return ');
+ }
+ return eval('new Function('
+ + map(args.split(/\s*,\s*/), quote).join()
+ + ','
+ + quote(fn)
+ + ')'
+ );
+}
+
+
+
+/* each(list, fn)
+ traverses `list`, applying `fn` to each item of `list`
+ takes:
+ `list` -> anything that can be indexed and has a `length` property.
+ usually an array.
+ `fn` -> either a function, or a string containing a function body,
+ in which case the name of the paremeters passed to it will be
+ 'item', 'idx' and 'list'.
+ se doc for `__strfn` for peculiarities about passing strings
+ for `fn`
+
+ `each` provides a safe way for traversing only an array's indexed items,
+ ignoring its other properties. (as opposed to how for-in works)
+*/
+function each(list, fn) {
+ if (typeof(fn)=='string') return each(list, __strfn('item,idx,list', fn));
+ for (var i=0; i < list.length; i++) fn(list[i], i, list);
+}
+
+
+/* map(list [, fn])
+ traverses `list`, applying `fn` to each item of `list`, returning an array
+ of values returned by `fn`
+
+ parameters work the same as for `each`, same `__strfn` caveats apply
+
+ if `fn` is not provided, the list item is returned itself. this is an easy
+ way to transform fake arrays (e.g. the arguments object of a function or
+ nodeList objects) into real javascript arrays.
+ e.g.: args = map(arguments)
+
+ If you don't care about map's return value, you should use `each`
+
+ this is a simplified version of python's map. parameter order is different,
+ only a single list (array) is accepted, and the parameters passed to [fn]
+ are different:
+ [fn] takes the current item, then, optionally, the current index and a
+ reference to the list (so that [fn] can modify list)
+ see `combine` if you want to pass multiple lists
+*/
+function map(list, fn) {
+ if (typeof(fn)=='string') return map(list, __strfn('item,idx,list', fn));
+
+ var result = [];
+ fn = fn || function(v) {return v};
+ for (var i=0; i < list.length; i++) result.push(fn(list[i], i, list));
+ return result;
+}
+
+
+/* combine(list [, list]* [, fn])
+
+ takes:
+ `list`s -> one or more lists (see `each` for definition of a list)
+ `fn` -> Similar s `each` or `map`, a function or a string containing
+ a function body.
+ if a string is used, the name of parameters passed to the
+ created function will be the lowercase alphabet letters, in
+ order: a,b,c...
+ same `__strfn` caveats apply
+
+ combine will traverse all lists concurrently, passing each row if items as
+ parameters to `fn`
+ if `fn` is not provided, a function that returns a list containing each
+ item in the row is used.
+ if a list is smaller than the other, `null` is used in place of its missing
+ items
+
+ returns:
+ an array of the values returned by calling `fn` for each row of items
+*/
+function combine() {
+ var args = map(arguments);
+ var lists = map(args.slice(0,-1),'map(item)');
+ var fn = args.last();
+ var toplen = map(lists, "item.length").max();
+ var vals = [];
+
+ if (!fn) fn = function(){return map(arguments)};
+ if (typeof fn == 'string') {
+ if (lists.length > 26) throw 'string functions can take at most 26 lists';
+ var a = 'a'.charCodeAt(0);
+ fn = __strfn(map(range(a, a+lists.length),'String.fromCharCode(item)').join(','), fn);
+ }
+
+ map(lists, function(li) {
+ while (li.length < toplen) li.push(null);
+ map(li, function(item,ix){
+ if (ix < vals.length) vals[ix].push(item);
+ else vals.push([item]);
+ });
+ });
+
+ return map(vals, function(val) { return fn.apply(fn, val) });
+}
+
+
+
+/* filter(list [, fn])
+ returns an array of items in `list` for which `fn(item)` is true
+
+ parameters work the same as for `each`, same `__strfn` caveats apply
+
+ if `fn` is not specified the items are evaluated themselves, that is,
+ filter will return an array of the items in `list` which evaluate to true
+
+ this is a similar to python's filter, but parameter order is inverted
+*/
+function filter(list, fn) {
+ if (typeof(fn)=='string') return filter(list, __strfn('item,idx,list', fn));
+
+ var result = [];
+ fn = fn || function(v) {return v};
+ map(list, function(item,idx,list) { if (fn(item,idx,list)) result.push(item) } );
+ return result;
+}
+
+
+
+/* reduce(list [, initial], fn)
+ similar to python's reduce. paremeter onder inverted...
+
+ TODO: document this properly
+
+ takes:
+ `list` -> see doc for `each` to learn more about it
+ `inirial -> TODO: doc`
+ `fn` -> similar to `each` too, but in the case where it's a string,
+ the name of the paremeters passed to it will be 'a' and 'b'
+ same `__strfn` caveats apply
+
+*/
+function reduce(list, initial, fn) {
+ if (undef(fn)) {
+ fn = initial;
+ initial = window.undefined; // explicit `window` object so browsers that do not have an `undefined` keyword will evaluate to the (hopefully) undefined parameter `undefined` of `window`
+ }
+ if (typeof(fn)=='string') return reduce(list, initial, __strfn('a,b', fn));
+ if (isdef(initial)) list.splice(0,0,initial);
+ if (list.length===0) return false;
+ if (list.length===1) return list[0];
+ var result = list[0];
+ var i = 1;
+ while(i<list.length) result = fn(result,list[i++]);
+ return result;
+}
+
+/* range(start, stop, step)
+ identical to python's range.
+ range(stop)
+ range(start,stop)
+ range(start,stop,step)
+
+ Return a list containing an arithmetic progression of integers.
+ range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
+ When step is given, it specifies the increment (or decrement).
+ For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
+ [from python's range's docstring]
+*/
+function range(start,stop,step) {
+ if (isUndefined(stop)) return range(0,start,step);
+ if (isUndefined(step)) step = 1;
+ var ss = (step/Math.abs(step)); // step sign
+ var r = [];
+ for (i=start; i*ss<stop*ss; i=i+step) r.push(i);
+ return r;
+} \ No newline at end of file
diff --git a/framework/Web/Javascripts/extended/base.js b/framework/Web/Javascripts/extended/base.js
new file mode 100644
index 00000000..145b003f
--- /dev/null
+++ b/framework/Web/Javascripts/extended/base.js
@@ -0,0 +1,24 @@
+
+/**
+ * true if o is an Element Node or document or window. The last two because it's used for onload events
+ if you specify strict as true, return false for document or window
+ */
+function isElement(o, strict) {
+ return o && isObject(o) && ((!strict && (o==window || o==document)) || o.nodeType == 1)
+}
+
+/**
+ * get element
+ @ @param element or element id string
+ @ returns element
+ */
+function $(n,d) {
+ if(isElement(n)) return n;
+ if(isString(n)==false) return null;
+ var p,i,x;
+ if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
+ d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
+ if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
+ for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=DOM.find(n,d.layers[i].document);
+ if(!x && d.getElementById) x=d.getElementById(n); return x;
+} \ No newline at end of file
diff --git a/framework/Web/Javascripts/extended/dom.js b/framework/Web/Javascripts/extended/dom.js
new file mode 100644
index 00000000..4c664de7
--- /dev/null
+++ b/framework/Web/Javascripts/extended/dom.js
@@ -0,0 +1,6 @@
+Object.extend(Element, {
+ condClassName : function (element, className, cond)
+ {
+ (cond?Element.addClassName:Element.removeClassName)(element,className);
+ }
+}); \ No newline at end of file
diff --git a/framework/Web/Javascripts/extended/event.js b/framework/Web/Javascripts/extended/event.js
new file mode 100644
index 00000000..17a5432c
--- /dev/null
+++ b/framework/Web/Javascripts/extended/event.js
@@ -0,0 +1,24 @@
+Object.extend(Event, {
+ OnLoad : function (fn) {
+ // opera onload is in document, not window
+ var w = document.addEventListener && !window.addEventListener ? document : window;
+ Event.__observe(w,'load',fn);
+ },
+ observe: function(elements, name, observer, useCapture) {
+ if(isElement(elements))
+ return this.__observe(elements, name, observer, useCapture);
+ for(var i=0; i<elements.length; i++)
+ this.__observe(elements[i], name, observer, useCapture);
+ },
+ __observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ ((navigator.appVersion.indexOf('AppleWebKit') > 0)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ }
+}); \ No newline at end of file
diff --git a/framework/Web/Javascripts/extended/functional.js b/framework/Web/Javascripts/extended/functional.js
new file mode 100644
index 00000000..2ff0f4a3
--- /dev/null
+++ b/framework/Web/Javascripts/extended/functional.js
@@ -0,0 +1,171 @@
+/**
+FUNCTIONAL
+by Caio Chassot (http://v2studio.com/k/code/)
+*/
+
+/**
+ * this is used internally by each, map, combine, filter and reduce to accept
+ * strings as functions.
+ *
+ * if <b>fn</b> does not contain a return statement, a return keyword will be added
+ * before the last statement. the last statement is determined by removing the
+ * trailing semicolon (';') (if it exists) and then searching for the last
+ * semicolon, hence, caveats may apply (i.e. if the last statement has a
+ * string or regex containing the ';' character things will go wrong)
+ * @param args a string of comma separated names of the function arguments
+ * @param fn the function body
+ */
+function __strfn(args, fn) {
+ /**
+ * Internal function. Do not call it directly.
+ */
+ function quote(s) { return '"' + s.replace(/"/g,'\\"') + '"' }
+ if (!/\breturn\b/.test(fn)) {
+ fn = fn.replace(/;\s*$/, '');
+ fn = fn.insert(fn.lastIndexOf(';')+1, ' return ');
+ }
+ return eval('new Function('
+ + map(args.split(/\s*,\s*/), quote).join()
+ + ','
+ + quote(fn)
+ + ')'
+ );
+}
+
+
+/**
+ * traverses <b>list</b>, applying <b>fn</b> to each item of <b>list</b>.
+ * see doc for <b>__strfn</b> for peculiarities about passing strings for <b>fn</b>
+ *
+ * <b>each</b> provides a safe way for traversing only an array's indexed items,
+ * ignoring its other properties. (as opposed to how for-in works)
+ * @param list anything that can be indexed and has a <b>length</b> property. usually an array.
+ * @param fn either a function, or a string containing a function body,
+ * in which case the name of the paremeters passed to it will be
+ * 'item', 'idx' and 'list'.
+ * @see #__strfn
+ */
+function each(list, fn) {
+ if (typeof(fn)=='string') return each(list, __strfn('item,idx,list', fn));
+ for (var i=0; i < list.length; i++) fn(list[i], i, list);
+}
+
+
+/**
+ * traverses <b>list</b>, applying <b>fn</b> to each item of <b>list</b>, returning an array
+ of values returned by <b>fn</b>
+
+ parameters work the same as for <b>each</b>, same <b>__strfn</b> caveats apply
+
+ if <b>fn</b> is not provided, the list item is returned itself. this is an easy
+ way to transform fake arrays (e.g. the arguments object of a function or
+ nodeList objects) into real javascript arrays.
+ e.g.: args = map(arguments)
+
+ If you don't care about map's return value, you should use <b>each</b>
+
+ this is a simplified version of python's map. parameter order is different,
+ only a single list (array) is accepted, and the parameters passed to [fn]
+ are different:
+ [fn] takes the current item, then, optionally, the current index and a
+ reference to the list (so that [fn] can modify list)
+ see <b>combine</b> if you want to pass multiple lists
+ */
+function map(list, fn) {
+ if (typeof(fn)=='string') return map(list, __strfn('item,idx,list', fn));
+
+ var result = [];
+ fn = fn || function(v) {return v};
+ for (var i=0; i < list.length; i++) result.push(fn(list[i], i, list));
+ return result;
+}
+
+
+/**
+ * combine will traverse all lists concurrently, passing each row if items as
+ parameters to <b>fn</b>
+ if <b>fn</b> is not provided, a function that returns a list containing each
+ item in the row is used.
+ if a list is smaller than the other, <b>null</b> is used in place of its missing
+ items
+ * @param list one or more lists (see <b>each</b> for definition of a list)
+ * @param fn Similar s <b>each</b> or <b>map</b>, a function or a string containing
+ a function body.
+ if a string is used, the name of parameters passed to the
+ created function will be the lowercase alphabet letters, in
+ order: a,b,c...
+ same <b>__strfn</b> caveats apply
+ * @see #each
+ * @see #__strfn
+ * @return an array of the values returned by calling <b>fn</b> for each row of items
+ *//*
+function combine() {
+ var args = map(arguments);
+ var lists = map(args.slice(0,-1),'map(item)');
+ var fn = args.last();
+ var toplen = map(lists, "item.length").max();
+ var vals = [];
+
+ if (!fn) fn = function(){return map(arguments)};
+ if (typeof fn == 'string') {
+ if (lists.length > 26) throw 'string functions can take at most 26 lists';
+ var a = 'a'.charCodeAt(0);
+ fn = __strfn(map(range(a, a+lists.length),'String.fromCharCode(item)').join(','), fn);
+ }
+
+ map(lists, function(li) {
+ while (li.length < toplen) li.push(null);
+ map(li, function(item,ix){
+ if (ix < vals.length) vals[ix].push(item);
+ else vals.push([item]);
+ });
+ });
+
+ return map(vals, function(val) { return fn.apply(fn, val) });
+}
+
+/**
+ * returns an array of items in <b>list</b> for which <b>fn(item)</b> is true
+
+ parameters work the same as for <b>each</b>, same <b>__strfn</b> caveats apply
+
+ if <b>fn</b> is not specified the items are evaluated themselves, that is,
+ filter will return an array of the items in <b>list</b> which evaluate to true
+
+ this is a similar to python's filter, but parameter order is inverted
+ *//*
+function filter(list, fn) {
+ if (typeof(fn)=='string') return filter(list, __strfn('item,idx,list', fn));
+
+ var result = [];
+ fn = fn || function(v) {return v};
+ map(list, function(item,idx,list) { if (fn(item,idx,list)) result.push(item) } );
+ return result;
+}
+
+/**
+ * similar to python's reduce. paremeter order inverted...
+ * @param list see doc for <b>each</b> to learn more about it
+ * @param initial TODO
+ * @param fn similar to <b>each</b> too, but in the case where it's a string,
+ the name of the paremeters passed to it will be 'a' and 'b'
+ same <b>__strfn</b> caveats apply
+ *//*
+function reduce(list, initial, fn) {
+ if (undef(fn)) {
+ fn = initial;
+ // explicit <b>window</b> object so browsers that do not have an <b>undefined</b>
+ //keyword will evaluate to the (hopefully) undefined parameter
+ //<b>undefined</b> of <b>window</b>
+ initial = window.undefined;
+ }
+ if (typeof(fn)=='string') return reduce(list, initial, __strfn('a,b', fn));
+ if (isdef(initial)) list.splice(0,0,initial);
+ if (list.length===0) return false;
+ if (list.length===1) return list[0];
+ var result = list[0];
+ var i = 1;
+ while(i<list.length) result = fn(result,list[i++]);
+ return result;
+}*/
+
diff --git a/framework/Web/Javascripts/extended/string.js b/framework/Web/Javascripts/extended/string.js
new file mode 100644
index 00000000..f79072fb
--- /dev/null
+++ b/framework/Web/Javascripts/extended/string.js
@@ -0,0 +1,37 @@
+Object.extend(String.prototype, {
+ pad : function(side, len, chr) {
+ if (!chr) chr = ' ';
+ var s = this;
+ var left = side.toLowerCase()=='left';
+ while (s.length<len) s = left? chr + s : s + chr;
+ return s;
+ },
+
+ padLeft : function(len, chr) {
+ return this.pad('left',len,chr);
+ },
+
+ padRight : function(len, chr) {
+ return this.pad('right',len,chr);
+ },
+
+ zerofill : function(len) {
+ var s = this;
+ var ix = /^[+-]/.test(s) ? 1 : 0;
+ while (s.length<len) s = s.insert(ix, '0');
+ return s;
+ },
+
+ trim : function() {
+ return this.replace(/^\s+|\s+$/g,'');
+ },
+
+ trimLeft : function() {
+ return this.replace(/^\s+/,'');
+ },
+
+ trimRight : function() {
+ return this.replace(/\s+$/,'');
+ }
+
+});
diff --git a/framework/Web/Javascripts/extended/util.js b/framework/Web/Javascripts/extended/util.js
new file mode 100644
index 00000000..1e1fad69
--- /dev/null
+++ b/framework/Web/Javascripts/extended/util.js
@@ -0,0 +1,90 @@
+/**
+ * Test if it is an object and has no constructors.
+ */
+function isAlien(a) { return isObject(a) && typeof a.constructor != 'function' }
+
+/**
+ * isArray?
+ */
+function isArray(a) { return isObject(a) && a.constructor == Array }
+
+/**
+ * isBoolean?
+ */
+function isBoolean(a) { return typeof a == 'boolean' }
+
+/**
+ * isFunction?
+ */
+function isFunction(a) { return typeof a == 'function' }
+
+/**
+ * isNull?
+ */
+function isNull(a) { return typeof a == 'object' && !a }
+
+/**
+ * isNumber?
+ */
+function isNumber(a) { return typeof a == 'number' && isFinite(a) }
+
+/**
+ * isObject?
+ */
+function isObject(a) { return (a && typeof a == 'object') || isFunction(a) }
+
+/**
+ * isRegexp?
+ * we would prefer to use instanceof, but IE/mac is crippled and will choke at it
+ */
+function isRegexp(a) { return a && a.constructor == RegExp }
+
+/**
+ * isString?
+ */
+function isString(a) { return typeof a == 'string' }
+
+/**
+ * isUndefined?
+ */
+function isUndefined(a) { return typeof a == 'undefined' }
+
+/**
+ * isEmpty?
+ */
+function isEmpty(o) {
+ var i, v;
+ if (isObject(o)) {
+ for (i in o) {
+ v = o[i];
+ if (isUndefined(v) && isFunction(v)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * alias for isUndefined
+ */
+function undef(v) { return isUndefined(v) }
+
+/**
+ * alias for !isUndefined
+ */
+function isdef(v) { return !isUndefined(v) }
+
+/**
+ * true if o is an Element Node or document or window. The last two because it's used for onload events
+ if you specify strict as true, return false for document or window
+ */
+function isElement(o, strict) {
+ return o && isObject(o) && ((!strict && (o==window || o==document)) || o.nodeType == 1)
+}
+
+/**
+ * true if o is an Array or a NodeList, (NodeList in Opera returns a type of function)
+ */
+function isList(o) { return o && isObject(o) && (isArray(o) || o.item) }
+