/*
 * (c) 2008 David Harvey. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the 
 *      following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
 *      the following disclaimer in the documentation and/or other materials provided with the distribution.
 *   3. The name of the author may not be used to endorse or promote products derived from this software without 
 *      specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * www.davethehat.com
 */

// Set a function's prototype to be an object - implement
Function.prototype.implement = function(obj) {
	this.prototype = obj;
	return this;
}

// Combine slots from obj into a function's prototype - class mixin
Function.prototype.mixin = function(obj) {
	for (var name in obj) {
		if (!this.prototype[name]) {
			this.prototype[name] = obj[name];
		}
	}
	return this;
}

// Extend - mixin and set base
Function.prototype.extend = function(constructor) {
	this.mixin(constructor.prototype);
	this.prototype.baseinit = constructor.prototype.init;
	return this;
}

// Deep comparison of (non-object) contents of nested arrays
Array.prototype.deepEquals = function(other) {
	if (this === other) return true;
	if (typeof other != typeof this) return false;
	if (other.length != this.length) return false;
	
	for (var i = 0; i < this.length; i++) {
		if (this[i].deepEquals) {
			if (!this[i].deepEquals(other[i])) return false;
		} else if (this[i] != other[i]) {
			return false;
		}
	}
	return true;
}

// Often needed...
Number.prototype.integer = function () {
	return Math[this < 0 ? 'ceiling' : 'floor'](this);
}

// Simple observer pattern implementation
function Notifier() { return this.init(); };
Notifier.implement({
	init: function() { this.observers = {}; return this; },
	addObserver: function (eventType, observer) {
		if (!this.observers[eventType]) {
			this.observers[eventType] = [];
		}
		this.observers[eventType].push(observer);
	},
	notify: function(src,eventType) {
		var observers = this.observers[eventType];
		observers && observers.forEach(function(observer) {
			observer.call(null,src,eventType);
		})
	},	
});

