if(VVF === undefined) {
	var VVF = {};
}

(function(namespace) {
	namespace = namespace || window;
	
	// Extend one class with another class
	// and/or add members (override if one already exists)
	namespace.extend = function(sb, sp, members) {
		function addToPrototype(o) {
			for(var m in o) {
				sb.prototype[m] = o[m];
			}
		}
		
		if(typeof sp == 'object') {
			addToPrototype(sp);
		} else {
			var F = function() {};
			F.prototype = sp.prototype;
			sb.prototype = new F();
			sb.prototype.constructor = sb;
			sb.superclass = sp.prototype;
			if(sp.prototype.constructor == Object.prototype.constructor) {
				sp.prototype.constructor = sp;
			}
			
			if(typeof members == 'object') {
				addToPrototype(members);
			}
		}
	};
	
	// Observable class
	namespace.Observable = function() {
		// TODO add the possibility to have some listeners for load event
		this.listeners = {};
	};
	namespace.Observable.prototype = {
		addEvents: function(events, onload) {
			for(var i = 0, len = events.length; i < len; i++) {
				this.listeners[events[i]] = [];
			}
			if(onload) {
				var o = (typeof onload == 'object') ? onload : {fn: onload, scope: this};
				this.listeners['load'] = [o];
			}
		},
		
		fireEvent: function(eventName) {
			var ls = this.listeners[eventName], len, li, i = 0, args = [{type: eventName, target: this}];
			if(typeof eventName == 'string' && ls && (len = ls.length)) {
				if(arguments.length > 1) {
					args = args.concat(Array.prototype.slice.call(arguments, 1));
				}
				for(; i < len; i++) {
					li = ls[i];
					li.fn.apply(li.scope, args);
				}
			}
		},
		
		addListener: function(eventName, fn, scope) {
			if(!this.listeners[eventName]) {
				this.listeners[eventName] = [];
			}
			scope = scope || this;
			if(this.findListener(eventName, fn, scope) == -1) {
				this.listeners[eventName].push({fn: fn, scope: scope});
			}
			return this;
		},
		
		removeListener: function(eventName, fn, scope) {
			if(this.listeners[eventName]) {
				scope = scope || this;
				var i;
				if((i = this.findListener(eventName, fn, scope)) > -1) {
					this.listeners[eventName].splice(i, 1);
				}
			}
			return this;
		},
		
		purgeListeners: function(eventName) {
			if(typeof eventName == 'string') {
				this.listeners[eventName] = [];
			} else {
				this.listeners = {};
			}
			return this;
		},
		
		findListener: function(eventName, fn, scope){
			scope = scope || this;
			var ls = this.listeners[eventName], len = ls.length, l, i = 0;
			for(; i < len; i++) {
				l = ls[i];
				if(l.fn == fn && l.scope == scope) {
					return i;
				}
			}
			return -1;
		}
	};
})(VVF);