/* PolyListbox, version 1.0.0.0
 * Copyright (c) 2008-2009 Mario Smeritschnig and Polymorph OG (http://www.polymorph.at)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * If you like this script, and in case you want to support us, we would appreciate
 * it if you put a link to http://www.polymorph.at on your website.
 *
 * For details, see our blog at: http://blog.polymorph.at/  
 */

var PolyListbox = Class.create({
	initialize : function(c,s) {
		var t=this, fid, name;
		t.initialized = false;
		c.hide();
		
		// set default value for css path
		if(!s.css_path) {
			// find out path of poly_listbox.css
			$$("link").each(function(e) {
				if(e.href.toLowerCase().endsWith("poly_listbox.css")) s.css_path = e.href;
			});
		}

		// set default values for onchange and ondefault events
		t.onchange = s.onchange ? s.onchange : null;
		t.ondefault = s.ondefault ? s.ondefault : null;
		
		// set default value for prototype_path
		if(!s.prototype_path) {
			// find out path of prototype.js
			$$("script").each(function(e) {
				if(e.src.toLowerCase().endsWith("prototype.js")) s.prototype_path = e.src;
			});
		}
		
		// compose id for the listbox
		t.id = 'poly_listbox_'+s.idx;

		t.height = parseInt(c.getStyle('height'));
		
		t.container = c;
		
		var st = "width:"+c.getStyle('width')+"; height:"+c.getStyle('height')+";";

		t.iframe = new Element('iframe', { id: t.id, name: t.id, border: '0', frameBorder: '0', frameSpacing: '0', marginheight: '0', marginwidth: '0', style: st, 'class': 'poly_listbox' } );
		c.insert({ after : t.iframe });
		t.i_doc = $(t.getDocument());
		
		name = c.readAttribute("name");
		if(name==null) name = c.id;

		t.input = new Element('input', { id: t.id, name: name, type: 'hidden', value: '' } );
		c.insert({ after : t.input });
		
		var scr = '<script type="text/javascript" language="JavaScript" src="'+s.prototype_path+'"><\/script>';
		var html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html class=\"poly_listbox_html\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"><title>PolyListbox</title><link rel=\"stylesheet\" type=\"text/css\" href=\""+s.css_path+"\" />"+scr+"</head><body class=\"poly_listbox_body\">"+c.innerHTML+"</body></html>";
		t.i_doc.open();
		t.i_doc.write(html);
		t.i_doc.close();

		// wait until prototype is loaded
		t.i_doc.ld = function() {
			var jsloaded = false;
			try {
				Element.observe(t.i_doc.body, "dom:loaded", function() {});
				jsloaded = true;
			} catch (e) {
				setTimeout(t.i_doc.ld, 100);
			}
			if(jsloaded) t.initialize_iframe();
		}
		t.i_doc.ld();
		
		t.selectedIndex = -1;
		

	},
	
	initialize_iframe : function() {
		var t=this, sel=-1, nodes = t.i_doc.body.childNodes, c = nodes.length,i,idx;
		t.items = t.i_doc.body.childElements();

		// remove all non div elements (ie6 has a script tag at the end)
		t.items.each(function(e) {
			if(e.tagName.toUpperCase() != "DIV") t.items = t.items.without(e);
		});

		c = t.items.length;
			
		for(idx=0;idx<c;idx++) {
			i = t.items[idx];
			i.selected = false;
			i.style.cursor = "default";
			i.observe("mouseover", function() { if(this.selected) this.addClassName = 'poly_listbox_item poly_listbox_item_selected'; else this.className = 'poly_listbox_item poly_listbox_item_over'; });
			i.observe("mouseout", function() { if(this.selected) this.className = 'poly_listbox_item poly_listbox_item_selected'; else this.className = 'poly_listbox_item'; });
			i.observe("mousedown", t.select.bindAsEventListener(t, idx));
			if(i.hasClassName("selected")) sel = idx;
		}
		
		if(sel != -1) t.select(null, sel, true);

		Event.observe(t.i_doc, "keydown", t.keydown.bindAsEventListener(t));
		t.initialized = true;
	},

	
	select : function(e, sel_idx) {
		var t=this,vpo,a=arguments,value;

		// prevent text selection
		if(e != null) Event.stop(e);
		// but with Event.stop(e) the the frame doesn't get the focus either, so the frame needs to get focused manually (for key events)
		t.iframe.contentWindow.focus();
		if(sel_idx < 0) sel_idx = 0;
		else if(sel_idx >= t.items.length) sel_idx = t.items.length -1;
		
		// if already selected
		if(t.selectedIndex == sel_idx) return;

		t.selectedIndex = sel_idx;
		var l=t.items.length, i, idx;
		for(idx=0; idx<l; idx++) {
			i = t.items[idx];
			i.selected = false;
			i.className = 'poly_listbox_item';
			if(idx==sel_idx) {
				i.selected = true;
				i.className = 'poly_listbox_item poly_listbox_item_selected';
				// scroll if necessary
				vpo = i.viewportOffset().top;
				var so = document.viewport.getScrollOffsets();
				if(vpo < 0) i.scrollIntoView(true);
				else if(vpo+i.getHeight() > t.height) i.scrollIntoView(false); 
				window.scrollTo(so.left, so.top);
			}
		}

		// set the <input type="hidden" /> value, so the listbox work with forms
		value = t.get_value();
		t.input.value = value;

		// trigger ondefault when default value gets selected, else trigger onchange
		if(a.length > 2 && a[2] == true && t.ondefault == null) {}
		else if(a.length > 2 && a[2] == true && t.ondefault != null) t.ondefault({ id: t.id, idx: sel_idx, value: value});
		else if(t.onchange != null) t.onchange({ id: t.id, idx: sel_idx, value: t.get_value()});

	},
	
	get_value : function() {
		var t=this,a=arguments,i;
		if(a.length > 0) i = a[0];
		else i = t.selectedIndex;
		if(i == -1) return null;
		if(!t.items || !t.items[i]) return null;
		return t.items[i].readAttribute("value");
	},
	
	keydown : function(e) {
		var t=this, key = e.which || e.keyCode;

		// prevent scrolling with arrow keys
		Event.stop(e);
		if(key == 40 || key == 39) t.select(null, t.selectedIndex+1);
		else if(key == 38 || key == 37) t.select(null, t.selectedIndex-1);
		else {
			var needle = String.fromCharCode(key).toUpperCase(), h, found_before=-1, found=-1, re = /(<([^>]+)>)/ig, re1 = /^(\W+)/;
			t.items.each(function(i, idx) {
				h = i.innerHTML.replace(re, "").replace(re1, "").substr(0, 1).toUpperCase();
				if(found==-1) {
					if(idx > t.selectedIndex && h == needle) found = idx;
					else if(found_before == -1 && h == needle) found_before = idx;
				}
			});
			if(found != -1) t.select(null, found);
			else if(found_before != -1) t.select(null, found_before);
		}
	},
	
	getDocument : function() {
		var t=this;
		return t.iframe.contentDocument || // W3
		(
			(t.iframe.contentWindow)&&(t.iframe.contentWindow.document)
		) ||  // IE
		(
			(t.iframe.name)&&(document.frames[t.iframe.name])&&
			(document.frames[t.iframe.name].document)
		) || null;
	}
});

// Listbox Manager
var poly_listbox = {
	
	initialize : function() {
		var t=this;
		t.listboxes = new Object();
		t.settings = new Array();
		document.observe("dom:loaded", t.init_loaded.bindAsEventListener(t));
	},
	
	init : function(s) {
		var t=this;
		t.settings.push(s);
	},
	
	init_loaded : function() {
		var t=this, global_settings = {}, settings;

		t.containers = Element.select("document.body", 'div.poly_listbox');
		
		// find out global settings (without id)
		t.settings.each(function(s) {
			if(!s.id) {
				global_settings = s;
				t.settings.without(s);
			}
		});

		var l = t.containers.length, c;
		for(var idx=0; idx<l; idx++) {
			c = t.containers[idx];
			if(!c.id) c.id = "poly_"+idx;
			
			// find out settings for this id
			settings = global_settings;
			t.settings.each(function(s) {
				if(s.id == c.id) {
					// merge settings with global settings
					if(s.css_path) settings.css_path = s.css_path;
					if(s.prototype_path) settings.prototype_path = s.prototype_path;
					if(s.onchange) settings.onchange = s.onchange;
					if(s.ondefault) settings.ondefault = s.ondefault;
					t.settings.without(s);
				}
			});
		
			settings.idx = idx;
			t.listboxes[c.id] = new PolyListbox(c, settings);
		}
	},
	
	get : function(id) {
		var t=this;
		return t.listboxes[id] ? t.listboxes[id] : null;
	}
}

poly_listbox.initialize();
