//Create the sense Namespace
var sense = {};

sense.Base = Class.create({});


// Create the ui Namespace
sense.ui = {};

sense.ui.Base = Class.create(sense.Base,{
	// Properties
	element: null,
	options: null,

	// Methods
	initialize: function($super, element, options, defaults) {
		// Call Parent Constructor
		$super();

		// Intialise properties
		this.element = element;
		this.options = {};

		// Initialise properties with any defaults passed into the constructor
		this.setOptions(defaults);
		// Over-ride properties with any options passed into the constructor
		this.setOptions(options);
		// Over-ride properties with any options specified via the "_options" attribute
		this.setOptions(this.parseElementOptions());
	},

	setOptions: function(options) {
		Object.extend(this.options, options);
	},
	
	parseElementOptions: function() {
		if (!this.element || !this.element.nodeType) return;

		// Extract any key-value option pairs from the _options="..." value
		// Options should be in the format "key1=val1;key2=val2;key3=val3"
		var optString = this.element.readAttribute('_options');
		if (!optString) return;

		var opts = {};
		var optPairs = optString.split(';');
		optPairs.each(function(pair){
			var pair = pair.split('=');
			var key = pair[0].strip();
			var val = unescape(pair[1].strip());
	
			// Try to do a bit of simple conversion so
			// everything isn't just a string by default
			if (val === "true") val = true;
			if (val === "false") val = false;
			if (val === Number(val).toString()) {
				val = Number(val);
			}

			opts[key] = val;
		});

		return opts;
	},
	
	px: function(n) {
		// Try and make sure we don't use any non-integer values for CSS
		// pixel measurements since they can cause slight visual oddities
		return (typeof n == 'string') ? n : Math.round(n || 0) + 'px';
	}
});


//Create the sense Namespace
sense.ui.widget = {};

sense.ui.widget.Base = Class.create(sense.ui.Base, {});


// Made in a singleton-ish style. Defines the class and
// immediately creates the one-and-only instance of it.

sense.ui.widget.Manager = new (Class.create(sense.Base, {
	//Properties
	registry: new Hash(),
	debug: false,

	//Methods
	initialize: function($super, element, options) {
		//Call Parent Constructor
		$super();

		document.observe('dom:loaded', this.initializeWidgets.bind(this));
		document.observe('dom:updated', this.initializeWidgets.bind(this));
	},

	register: function(widgetType){
		if (typeof widgetType == 'string') {
			widgetType = { constructor:widgetType };
		}

		if (!widgetType.selector) widgetType.selector = '.js' + widgetType.constructor;
		if (!widgetType.options) widgetType.options = {};
		
		widgetType.instances = [];

		this.registry.set(widgetType.constructor, widgetType);
	},

	initializeWidgets: function() {
		this.registry.values().each(function(widgetType){

			// If the widget definition .js file has not been loaded, just return.
			if (typeof sense.ui.widget[widgetType.constructor] == 'undefined') return;
			if (this.debug) console.log(widgetType);

			// Iterate through each DOM element matching the selector and pass it into the constructor.
			$$(widgetType.selector).each(function(el){

				// Make sure that we only initialise a widget once.
				if (typeof el[widgetType.constructor] != 'undefined') return;
				if (this.debug) console.log(el);

				if (widgetType.options) {
					var instance = new sense.ui.widget[widgetType.constructor](el, widgetType.options);
				} else {
					var instance = new sense.ui.widget[widgetType.constructor](el);
				}

				el[widgetType.constructor] = instance;
				widgetType.instances.push(instance);

			}.bind(this));

		}.bind(this));
	},
	
	getInstances: function(widgetType) {
		var obj = this.registry.get(widgetType);
		return (obj) ? obj.instances : false;
	}
}))();

// A short-hand reference for ease of use in the console
var uim = sense.ui.widget.Manager;



// ----------------------------------------------------------------------------
// Use a stub object to prevent errors in IE from using console.log() etc.
// ----------------------------------------------------------------------------

(function(){
	// Prevent stray debugging calls from erroring when firebug isn't present
	if (!('console' in window) || !('firebug' in console)) {
		var names = ['log','debug','info','warn','error','assert','dir','dirxml','group','groupEnd','time','timeEnd','count','trace','profile','profileEnd'];
		window.console = window.console || {};
		for (var i=0; i<names.length; ++i) {
			window.console[names[i]] = window.console[names[i]] || function(){};
		}
	}
})();


// ----------------------------------------------------------------------------
// Extensions to the prototype framework (inspired by jQuery 1.2.3)
// Allows for showing and hiding of elements that have been hidden via an 
// external CSS declaration, rather than an inline "display:none;"
// ----------------------------------------------------------------------------

sense.elementMethods = {
	visible: function(element) {
		return $(element).getStyle('display') != 'none';
	},
	show: function(element){
		$(element).setStyle({ display: $(element).oldblock || '' });
		if ($(element).getStyle('display') == 'none') {
			var newElement = document.createElement($(element).tagName);
			$$('body').first().insert({bottom: newElement});
			$(element).setStyle({ display: $(newElement).getStyle('display')});
			$(newElement).remove();
			if ($(element).getStyle('display') == 'none') $(element).setStyle({ display: 'block'});
		}
		return element;
	},
	hide: function(element){
		$(element).oldblock = $(element).oldblock || $(element).getStyle("display");
		$(element).setStyle({display: 'none'});
		return element;
	}
};

// ----------------------------------------------------------------------------
// A few core UI classes applied directly to Element so we can call them as 
// short-hand methods on the object, i.e. myElement.tween({ ...options... });
//
// (We also provide for these classes being instantiated in a widget-y fashion)
// ----------------------------------------------------------------------------

$A(['Tween','Dragger','Dropper','Sortable']).each(function(klassName){
	var methodName = klassName.toLowerCase();
	sense.elementMethods[methodName] = function(element, options) {
		if (typeof sense.ui[klassName] != 'undefined') {
			return element[klassName] = new sense.ui[klassName](element, options);
		} else {
			return element;
		}
	}

	sense.ui.widget[klassName] = sense.ui[klassName];
	sense.ui.widget.Manager.register(klassName);
});

Element.addMethods(sense.elementMethods);

// ----------------------------------------------------------------------------
// Instantly hide elements with a class of 'jsHide' (for JS-fallback content)
// ----------------------------------------------------------------------------

(function(){
	// See: http://yuiblog.com/blog/2007/06/07/style/
	function addCss(cssCode) {
		var styleElement = document.createElement("style");
		styleElement.type = "text/css";
		if (styleElement.styleSheet) {
			styleElement.styleSheet.cssText = cssCode;
		} else {
			styleElement.appendChild(document.createTextNode(cssCode));
		}
		document.getElementsByTagName("head")[0].appendChild(styleElement);
	}
	addCss(".jsHide { display:none; }");
})();
