NEO = {
	Pop: function(src) {
		var width = arguments[1] || 600;	
		var height = arguments[2] || 400;	
		
		var o = $H({
			width: width,
			height: height,
			left: Math.floor((screen.width-width)/2),
			top: Math.floor((screen.height-height)/2),
			resizable: 'yes',
			scrollbars: 'yes'
		});
		
		var options = o.collect(function(pair) {
			return pair.join('=');
		}).join(',');
		
		openPopWindow(src, options, 'neopop');		
	},
	
	bookmark: function(title,url) {
		if (window.sidebar) // firefox
			window.sidebar.addPanel(title, url, "");
		else if(window.opera && window.print){ // opera
			var elem = document.createElement('a');
			elem.setAttribute('href',url);
			elem.setAttribute('title',title);
			elem.setAttribute('rel','sidebar');
			elem.click();
		} 
		else if(document.all)// ie
			window.external.AddFavorite(url, title);
		else
			alert('ERROR: Unsupported browser.');
	}
}

NEO.Errors = Class.create({}, {
	initialize: function(errors) {
		this.options = Object.extend({
			prefix: '',
			focusFirst: false
	    }, arguments[1] || { });
		
		this.focused = false;
		
		$H(errors).each(this.apply.bind(this));		
	},
	
	apply: function(pair) {
		if (typeof pair.value == 'object') {
			var o = Object.clone(this.options);
			o.prefix+= pair.key + '_';
			return new NEO.Errors(pair.value, o);
		}
		
		var name 	= this.options.prefix + pair.key;
		var el 		= $(name);
		var l		= $$('label[for="' + name +'"]').first();
		 
		if (el)
			el.addClassName('error');
		
		if (l)
			l.addClassName('error')
		
		if (this.options.focusFirst && !this.focused && (l || el)) {
			if (el.focus) el.focus();
			if (l.scrollTo) l.scrollTo();
			this.focused = true;
		}

		var errors = $(this.options.prefix + 'errors');
		if (!errors) errors = $('errors');
		if (errors) errors.insert('<p class="error">' + pair.value + '</p>');
	}
});

NEO.Block = Class.create({}, {
	initialize: function(el) {
		this.options = Object.extend({
			expanded: 			false,
			expandedSelector: 	'.show_expanded',
			collapsedSelector: 	'.show_collapsed',
			handleSelector: 	'.box_control',
			txtExpand:			_MESSAGES.EXPAND,
			txtCollapse:		_MESSAGES.COLLAPSE		
	    }, arguments[1] || {});
		
		this.el 		= $(el);
		this.handle		= this.el.down(this.options.handleSelector); 

		this.handle.observe('click', this.toggle.bindAsEventListener(this))
		this.display();
		this.el.show();
	},
	
	toggle: function(e) {
		e.stop();
		this.options.expanded = !this.options.expanded;
		this.display();
	},
	
	expand: function() {
		this.options.expanded = true;
		this.display();
	},
	
	collapse: function() {
		this.options.expanded = false;
		this.display();
	},
	
	display: function() {
		this.el.select(this.options.expandedSelector).each(this.options.expanded ? Element.show : Element.hide);
		this.el.select(this.options.collapsedSelector).each(this.options.expanded ? Element.hide : Element.show);
		this.handle.update(this.options.expanded ? this.options.txtCollapse : this.options.txtExpand);
	} 
});

// based on http://code.google.com/p/js-uri/
NEO.URI = Class.create({}, {
	initialize: function(str) {
		if (!str) str = "";
	    // Based on the regex in RFC2396 Appendix B.
	    var parser = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
	    var result = str.match(parser);
	    this.scheme    = result[1] || null;
	    this.authority = result[2] || null;
	    this.path      = result[3] || null;
	    this.query     = result[4] || null;
	    this.fragment  = result[5] || null;
		
		this.options = Object.extend({
			queryElements: {}
	    }, arguments[1] || { });	
	},
	
	addQueryElement: function(name, el) {
		this.options.queryElements[name] = el;
	},
	
	getQuery: function() {
		var query = this.query || "";
		var params = $H();
		
		$H(this.options.queryElements).each(function(pair) {
			params.set(pair.key, $F(pair.value));
		});
		
		return query + '&' + params.toQueryString();
	},
	
	toString: function() {
		var str = "";
		var query = this.getQuery();
		
	    this.scheme 	&& (str += this.scheme + ":");
	    this.authority	&& (str += "//" + this.authority);
	    this.path		&& (str += this.path);
	    query			&& (str += "?" + query);
	    this.fragment	&& (str += "#" + this.fragment);
	    return str;
	}
});

NEO.Ajax = {};

NEO.Ajax.Select = Class.create({}, {
	initialize: function(name, url) {
		this.input			= $(name);
		this.choices 		= $(name + '_choices');
		this.loader 		= $(name + '_loader');
		this.url			= new NEO.URI(url);
		
		this.parentValues 	= $H();
		
		this.options = Object.extend({
			belongsTo: {},
			deferLoad: false,
			allowOther: false,
			otherKey: 'OTHER',
			otherValue: _MESSAGES.SELECT_OTHER
	    }, arguments[2] || { });
		
		var o = this;
		
		$H(this.options.belongsTo).each(function(pair) {
			o.url.addQueryElement(pair.key, pair.value);
			
			$(pair.value).observe('change', o.checkParent.bindAsEventListener(o, pair.key));
			$(pair.value).observe('neo:change', o.checkParent.bindAsEventListener(o, pair.key));
			$(pair.value).observe('neo:load', o.checkParent.bindAsEventListener(o, pair.key));
		});

		this.choices.observe('change', this.choicesListener.bindAsEventListener(this));
		
		this.checkChoice();
		
		if (!this.options.deferLoad)
			this.load();
	},
	
	load: function(){
		this.update()
	},
	
	checkParent: function(e, key) {
		var value 	= $F(e.element());
		var old 	= this.parentValues.get(key);
		
		if (old != value) {
			if (old != undefined && this.options.allowOther)
				this.input.value = '';
			
			this.parentValues.set(key, value);
			this.load();
		}
	},
	
	update: function() {
		this.choices.hide();
		this.loader.show();
		var o = this;		
		new Ajax.Request(this.url.toString(), { onSuccess: o.build.bindAsEventListener(o) });
	},
	
	build: function(t) {
		var o = this.choices;
		o.update('');
		
		var rs = t.responseText.evalJSON();
		
		if (rs.length != undefined)
			rs.each(function(val) { o.options.add(new Option(val, val)); });
		else
			$H(rs).each(function(pair) { o.options.add(new Option(pair.value, pair.key)); });
			
		if (this.options.allowOther)
			o.options.add(new Option(this.options.otherValue, this.options.otherKey));
		
		this.checkChoice();
		
		this.loader.hide();
		this.choices.show();		
		this.choices.fire('neo:load');
	},
	
	checkChoice: function() {
		var value 	= $F(this.input);
		var old		= $F(this.choices);
		
		this.choices.value = value;
		
		if ($F(this.choices) != value) {
			if (value && this.options.allowOther)
				this.choices.value = this.options.otherKey;
			else if (this.choices.options.length > 0) {
				this.choices.selectedIndex = 0;
				this.input.value = $F(this.choices);
			} 				
		}
		
		if (this.options.allowOther && $F(this.choices) == this.options.otherKey) {
			if ($F(this.input) == this.options.otherKey)
				this.input.value = '';
				
			this.input.show();
		}
		else
			this.input.hide();
			
		if (old != $F(this.choices))
			this.choices.fire('neo:change');	
	},
	
	choicesListener: function() {
		this.input.value = $F(this.choices);
		this.checkChoice();
	}
});

NEO.AutoClosingBox = Class.create({}, {
	initialize: function (el) {
		this.el 			= $(el);
		this.box_clicked 	= false;
		
		this.el.observe('click', 	this.onBoxClick.bind(this));
		document.observe('click', 	this.onDocumentClick.bind(this));
	},
	
	show: function() {
		this.box_clicked = true;
		this.el.show();
	},
	
	hide: function() {
		this.el.hide();
	},
	
	onBoxClick: function(e) {
		this.box_clicked = true;
	},
	
	onDocumentClick: function(e) {
		if (!this.el.visible()) return;

		if (!this.box_clicked)
			this.el.hide();
		
		this.box_clicked = false;
	}
});

NEO.ScrollingBox = Class.create({}, {
	initialize: function (el, url) {
		this.el 	= $(el);
		this.url	= url;
		this.step	= 0;
		
		this.el.down('.control_prev').observe('click', this.scroll.bindAsEventListener(this, -1));		
		this.el.down('.control_next').observe('click', this.scroll.bindAsEventListener(this, 1));
	},
	
	scroll: function(e, d) {
		e.stop();
		this.step+= d;
		var o = this;

		new Ajax.Request(this.url, {
			parameters: { scroller_step: this.step },
			onComplete: function(t) {
				o.el.down('.items').update(t.responseText);
			}
		})
	}
});
