// class ZList
function ZList(element, options) {
    this.element = $(element);
    if (this.element.id) ZList._byId[this.element.id] = this;

    if (this.element.tagName == 'TABLE') {
	this.container = this.element.down();
	this.itemType = 'tr';
    }
    else if (this.element.tagName == 'UL') {
	this.container = this.element;
	this.itemType = 'li';
    }
    else {
	this.container = this.element;
	this.itemType = 'div';
    }
	     
    this.afterUpdate = options.afterUpdate;
    this.template = options.template;
    this.eventHandlers = options.eventHandlers || [];
    var eventTypes = [];
    for (var i = 0; i <  this.eventHandlers.length; i++) {
	eventTypes.push(this.eventHandlers[i].type);
    }
    this.eventTypes = eventTypes.uniq();

    this.dataMap = options.dataMap || {};
    if (options.dataSet) this.dataSet = Object.extend({ _key: true }, options.dataSet);

    this.keyName = options.keyName;
    this.autoId = 1;

    this.items = {};
    
     if (options.initialItems) {
         var cn = this.container.childNodes;
         var ci = 0;
         var fi = options.firstElementIndex || 0;
         while (cn[ci] && cn[ci].nodeType != 1) { ci++; }
         while (fi > 0) {
             fi--;
             ci++;
             while (cn[ci] && cn[ci].nodeType != 1) { ci++; }
         }
         for (var i = 0; i < options.initialItems.length; i++) {
             this.addItem(options.initialItems[i], cn[ci++]);
             while (cn[ci] && cn[ci].nodeType != 1) { ci++; }
         }
     }
}

ZList._byId = {};
ZList.byId = function(id) {
    return ZList._byId[id];
};

ZList.prototype = {
    mapData: function(data) {
	var tmplVars = {};
	for (var key in this.dataSet || data) {
	    tmplVars[key] = (this.dataMap[key] || function(d, k) { return d[k]; })(data, key) || this.dataSet[key] || '';
	}
	return tmplVars;
    },

    addItem: function(data, element) {
	var key = this.keyName ? data[this.keyName] : this.autoId++;
	if (this.items[key]) {
	    this.items[key].data = data;
	    if (element) {
		this.container.replaceChild(element, this.items[key].element);
		this.items[key].element = element;
		this.items[key].observed = false;
		if (this.afterUpdate) this.afterUpdate(null, this, key);
	    }
	    else {
		this.updateItem(key);
	    }
	}
	else {
	    this.items[key] = { visible: true };
	    this.items[key].data = data;
	    if (element) {
		this.items[key].element = element;
		if (this.afterUpdate) this.afterUpdate(null, this, key);
	    } 
	    else {
		this.items[key].element = document.createElement(this.itemType);
		this.updateItem(key);
	    }
	    if (this.items[key].element.parentNode != this.container) this.container.appendChild(this.items[key].element);
	}
	this.observeItem(key);
	return key;
    },
    
    removeItem: function(key) {
	if (this.items[key]) {
	    var data = this.items[key].data;
	    Element.remove(this.items[key].element);
	    delete(this.items[key]);
	    return data;
	}
	else {
	    return null;
	}
    },

    invalidateItem: function(key) {
	if (this.items[key]) {
	    this.updateItem(key);
	    this.observeItem(key);
	}
    },

	
    invalidateItems: function() {
	for (var key in this.items) {
	    this.invalidateItem(key);
	}
    },

    observeItem: function(key) {
	if (!this.items[key].observed) {
	    Event.observe(this.items[key].element, 'click', this.dispatchEvent.bindAsEventListener(this, key));
	    this.items[key].observed = true;
	}
    },

    updateItem: function(key) {	
	if (this.template) { 
	    this.items[key].element.innerHTML = this.template.evaluate(this.mapData(Object.extend({ _key: key }, this.items[key].data)));
	    if (this.afterUpdate) this.afterUpdate(null, this, key);
	}
    },

    getItemData: function(key) {
	if (this.items[key]) {
	    return this.items[key].data;
	}
	else {
	    return null;
	}
    },

    getItemElement: function(key) {
	if (this.items[key]) {
	    return $(this.items[key].element);
	}
	else {
	    return null;
	}
    },

    enableItem: function(key) {
	if (this.items[key]) {
	    this.items[key].disabled = false;
	    Element.removeClassName(this.items[key].element, 'disabled');
	}
    },

    disableItem: function(key) {
	if (this.items[key]) {
	    this.items[key].disabled = true;
	    Element.addClassName(this.items[key].element, 'disabled');
	}
    },

    itemDisabled: function(key) {
	if (this.items[key]) {
	    this.items[key].disabled;
	}
	else {
	    return false;
	}
    },

    setFilter: function(filter) {
	for (var key in this.items) {
	    if (!filter || filter(this.items[key].data, this.items[key].element)) {
		this.items[key].visible = true;
		Element.show(this.items[key].element);
	    }
	    else {
		this.items[key].visible = false;
		Element.hide(this.items[key].element);
	    }
	}
    },

    dispatchEvent: function(event, key) {
	if (this.items[key] && !this.items[key].disabled) {
	    for (var i = 0; i < this.eventHandlers.length; i++) {
		if (this.eventHandlers[i].type == event.type) {
		    var parts = this.eventHandlers[i].selector.strip().split(/\s+/);
		    var element = Event.element(event);
		    while (parts.length != 0 && element != this.items[key].element) {
			if (parts[parts.length - 1].strip() == '') {
			    parts.pop();
			    continue;
			}
			if (Element.match(element, parts[parts.length - 1])) parts.pop();
			element = element.parentNode;
		    }
		    if (parts.length == 0) {
			this.eventHandlers[i].handler(event, this, key);
			i = this.eventHandlers.length;
		    }
		}
	    }
	}
    },

    getItemsData: function() {
	var itemsData = [];
	for (var key in this.items) {
	    itemsData.push(this.items[key].data);
	}
	return itemsData;
    },

    getItemsKeys: function() {
	var itemsKeys = [];
	for (var key in this.items) {
	    itemsKeys.push(key);
	}
	return itemsKeys;
    },
    
    getCount: function() {
    return $H(this.items).size();
    },
    

    getFilteredItemsData: function(filter) {
	var itemsData = [];
	for (var key in this.items) {
	    if (this.items[key].visible && (!filter || filter(this.items[key].data))) itemsData.push(this.items[key].data);
	}
	return itemsData;
    }
};

ZList.Utils = {
    escape: function(d, k) {
	return String.interpret(d[k]).escapeHTML().replace(/\"/g, '&quot;');
    }
};

