
// builtins.js
// - provide a few utility methods for any environment

// Supported environments:
//  1. Firefox DOM (plus Firebug, if applicable)
//  2. Firefox Chrome/XUL
//  3. Python-Spidermonkey

// Fundamental functions:
//  * _extend
//  * _iterate
//  * _bind
//  * _load_url => 
//  * _import =>
//  


function _object(o) {
	var f = function(){};
	f.prototype = o;
	return f;
}


function _instance(klass, options) {
	var f = function(){};
	f.prototype = new klass();
	_extend(f.prototype, options);
	return f;
}


// extend() from John Resig:
//   http://ejohn.org/blog/javascript-getters-and-setters/
// Helper method for extending one object with another
function _extend(obj) {
	var si, source, key, value, getter, setter, sources;
	sources = Array.prototype.slice.apply(arguments, [1]);
	
	for (si=0; si<sources.length; si++) {
		source = sources[si];
		for (var key in source) {
			getter = source.__lookupGetter__(key);
			setter = source.__lookupSetter__(key);
			
			if (getter || setter) {
				if (getter)
					obj.__defineGetter__(key, getter);
				if (setter)
					obj.__defineSetter__(key, setter);
			} else {
				obj[key] = source[key];
			}
		}
	}
	
	return obj;
}

//function _extend(a, b) {
//	for ( var i in b ) {
//		var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
//		
//		if ( g || s ) {
//			if ( g )
//				a.__defineGetter__(i, g);
//			if ( s )
//				a.__defineSetter__(i, s);
//		} else
//			a[i] = b[i];
//	}
//	return a;
//}


function _bind(object, method) {
	var bound_args = Array.prototype.slice.apply(arguments, [2]);
	
	if (bound_args.length == 0) {
		return function() {
			return method.apply(object, Array.prototype.slice.apply(arguments));
		}
	} else {
		return function() {
			var new_args = Array.prototype.slice.apply(arguments);
			return method.apply(object,
								bound_args.slice().concat(new_args));
		}
	}
}

//function _bind(o, m) {
//	return function() {
//		return m.apply(o, Array.prototype.slice.apply(arguments));
//	}
//}


function _iterate(o, fn) {
	// _iterate(o, fn) => execute fn for every item in o
	// fn == function(key, value, index) { ... }
	// 
	// 1. o is a function, return _iterate(o(), fn)
	// 2. if o.__iter__ exists, return _iterate(o.__iter__, fn)
	// 3. if o._order exists, iterate o._order to get keys
	// 4. if o is an Array
	// 5. if o is an Object
	var ret=[], k, i;
	fn = fn || function(k,v) { return k; };
	
	if (typeof(o) == "function") {
		return _iterate(o(), fn);
	} else if (typeof(o) == "object" && !!o) {
		if (o.__iter__ !== undefined) {
			return _iterate(o.__iter__, fn);
		} else if (o._order !== undefined) {
			if (typeof(o._order) == "object" && !!o._order) {
				if (o._order.constructor == Array) {
					for (i=0; i<o._order.length; i++) {
						k = o._order[i];
						ret.push(fn(k, o[k], i));
					}
				} else {
					throw ERROR("Don't know how to handle o._order: " + o._order);
				}
			} else {
				throw ERROR("Don't know how to handle o._order: " + o._order);
			}
		} else if (o.constructor == Array) {
			for (k=0; k<o.length; k++) {
				ret.push(fn(k, o[k], k));
			}
		} else {
			i=0;
			for (k in o) {
				ret.push(fn(k, o[k], i));
				i++;
			}
		}
	} else {
		throw ERROR("Cannot iterate object o: " + o);
	}
	return ret;
}

