From ccf76e430b7703db028966a845a966f50956f490 Mon Sep 17 00:00:00 2001 From: xue <> Date: Mon, 5 Dec 2005 01:00:16 +0000 Subject: --- framework/Web/Javascripts/extended/functional.js | 171 +++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 framework/Web/Javascripts/extended/functional.js (limited to 'framework/Web/Javascripts/extended/functional.js') 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 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) + * @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 list, applying fn to each item of list. + * see 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) + * @param list anything that can be indexed and has a length 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 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 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 + * @param list one or more lists (see each for definition of a list) + * @param 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 + * @see #each + * @see #__strfn + * @return 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) }); +} + +/** + * 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; +} + +/** + * similar to python's reduce. paremeter order inverted... + * @param list see doc for each to learn more about it + * @param initial TODO + * @param 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; + // explicit window object so browsers that do not have an undefined + //keyword will evaluate to the (hopefully) undefined parameter + //undefined of window + 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