﻿// ---------------------------------------------------------------------------
// MousePosition
// ---------------------------------------------------------------------------

var MousePosition = Class.create();

MousePosition.prototype = {

    initialize: function()
    {
        this.mouseX = 0;
        this.mouseY = 0;
        
        if (document.layers)
	        document.captureEvents(Event.MOUSEMOVE);
	    
	    var self = this;
        document.onmousemove = function(e){self.updatePosition(e);};
    },
    
    GetX: function()
    {
        var offset = 0;
        
        if (document.all)
        {
            if(typeof window.pageXOffset != 'undefined')
            {
                offset = window.pageXOffset;
            }
            else if(typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat')
            {
                offset = document.documentElement.scrollLeft;
            }
            else if(typeof document.body != 'undefined')
            {
                offset = document.body.scrollLeft;
            }
        }
        
        return this.mouseX + offset;
    },
    
    GetY: function()
    {
        var offset = 0;
        
        if (document.all)
        {
            if(typeof window.pageYOffset != 'undefined')
            {
                offset = window.pageYOffset;
            }
            else if(typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat')
            {
                offset = document.documentElement.scrollTop;
            }
            else if(typeof document.body != 'undefined')
            {
                offset = document.body.scrollTop;
            }
        }
        
        return this.mouseY + offset;
    },
    
    updatePosition: function(e)
	{
        if (document.all)
        {
            this.mouseX = event.clientX;
            this.mouseY = event.clientY;
        }
        else
        {  
            this.mouseX = e.pageX;
            this.mouseY = e.pageY;
        }
    }
}

var mouseposition = new MousePosition();

// ---------------------------------------------------------------------------
// GuiHelper
// ---------------------------------------------------------------------------

var GuiHelper = {
    
    isKeyPressed: function(e, keycodelist)
    {
        var _self = this;
        
        var pressed = false;
        keycodelist.each(function(keycode){ keycode = parseInt(keycode); if(_self.getKeyCode(e) == keycode) pressed = true; });
        return pressed;
    },
    
    isKeyMatch: function(e, expr)
    {
        return this.getKeyChar(e).match(expr) != null;
    },
    
    getKeyCode: function(e)
    {
        if(e.which)
            return e.which;
        else if(e.keyCode)
            return e.keyCode;
    },
    
    getKeyChar: function(e)
    {
        return String.fromCharCode(this.getKeyCode(e));
    }

}

// ---------------------------------------------------------------------------
// SlideShow
// ---------------------------------------------------------------------------

var SlideShow = Class.create();

SlideShow.prototype = {

    initialize: function(storageobjid)
    {
        this.changeinterval = 5000;
        this.currentIndex = 0;
        this.fadeinopacity = 0;
        this.fadeoutopacity = 100;
        this.fadingactive = false;
        this.slideshowinterval = null;
        this.playButtonID = null;
        this.stopButtonID = null;
        this.items = new Array();
        this.itemSwitchEventHandlerList = new Array();
        
        this.currentItemIDStorageObj = this.createCurrentItemIDStorageObj(storageobjid);
    },
    
    getCurrentItem: function()
    {
        return this.items[this.currentIndex];
    },
    
    setCurrentItem: function(item)
    {
        this.currentItemIDStorageObj.value = item.itemID;
        this.currentIndex = this.items.indexOf(item);
    },
    
    getNextItem: function()
    {
        index = this.items.indexOf(this.getCurrentItem());
        	
        if(index >= this.items.length-1)
            index = 0;
        else
            index++;
        
        return this.items[index];		
    },
    
    getPreviousItem: function()
    {
        index = this.items.indexOf(this.getCurrentItem());
        	
        if(index <= 0)
            index = this.items.length-1;
        else
            index--;
        
        return this.items[index];		
    },
    
    nextItem: function(autoswitch)
    {
        if(this.actionsAvailable())
        {
            if(!autoswitch)
                this.stopSlideShow();
                
            nextitem = this.getNextItem();
            this.switchToItem(nextitem);
        }
    },
    
    previousItem: function(autoswitch)
    {
        if(this.actionsAvailable())
        {
            if(!autoswitch)
                this.stopSlideShow();
                
            previousitem = this.getPreviousItem();
            this.switchToItem(previousitem);
        }
    },
    
    setItem: function(itemid)
    {
        for (var i=0; i<this.items.length; i++) 
        {
            if(this.items[i].itemID == itemid)
            {
                if(this.actionsAvailable())
                {
                    this.switchToItem(this.items[i]);
                    break;
                }
            }
        }
    },
    
    startSlideShow: function()
    {
        if(this.actionsAvailable())
        {
            if(this.playButtonID != null)
                $(this.playButtonID).style.visibility = 'hidden';
                
            if(this.stopButtonID != null)
                $(this.stopButtonID).style.visibility = 'visible';
                
            var self = this;
            this.slideshowinterval = window.setInterval(function(){self.nextItem(true);}, this.changeinterval);
        }
    },
    
    stopSlideShow: function()
    {
        if(!this.fadingactive)
        {
            if(this.playButtonID != null)
                $(this.playButtonID).style.visibility = 'visible';
                
            if(this.stopButtonID != null)
                $(this.stopButtonID).style.visibility = 'hidden';
            
            window.clearInterval(this.slideshowinterval);
        }
    },
    
    addItem: function(itemid, controlid)
    {
        this.items.push(new SlideShowItem(itemid, controlid));
    },
    
    switchToItem: function(newitem)
    {
        this.onItemSwitch();
        this.invokeItemSwitchEvent(this.getCurrentItem(), newitem);
        this.fadingactive = true;
        this.fadeIn(newitem);
        this.fadeOut(this.getCurrentItem());
        this.setCurrentItem(newitem);
    },
    
    fadeIn: function(item)
    {
        item.displayControl.style.visibility = 'visible';
    
        if(this.fadingAvailable())
        {
            if(this.fadeinopacity == 100)
                this.fadeinopacity = 0;
        	        
            this.fadeinopacity += 5;
            
            this.setAlpha(item, this.fadeinopacity);
        	    
            if(this.fadeinopacity < 100)
            {
                var self = this;
                window.setTimeout(function(){self.fadeIn(item);}, 30);
            }
            else
            {
                this.fadingactive = false;
            }
        }
    },
    
    fadeOut: function(item)
    {
        if(this.fadingAvailable())
        {
            if(this.fadeoutopacity == 0)
                this.fadeoutopacity = 100;
        	        
            this.fadeoutopacity -= 5;
        	    
            this.setAlpha(item, this.fadeoutopacity);
                
            if(this.fadeoutopacity > 0)
            {
                var self = this;
                window.setTimeout(function(){self.fadeOut(item);}, 30);
            }
            else
            {
                item.displayControl.style.visibility = 'hidden';
                this.fadingactive = false;
            }
        }
        else
        {
            item.displayControl.style.visibility = 'hidden';
            this.fadingactive = false;
        }
    },
    
    setAlpha: function(item, alpha)
    {
        item.displayControl.style.opacity = alpha / 100; 
        item.displayControl.style.filter = 'alpha(opacity=' + alpha + ')';
    },
    
    createCurrentItemIDStorageObj: function(storageobjid)
    {
        document.write("<input type='hidden' id='" + storageobjid + "' name='" + storageobjid + "'>");
        return $(storageobjid);
    },
    
    actionsAvailable: function()
    {
        if((this.items.length > 1) && (!this.fadingactive))
            return true;
        
        return false;
    },
    
    fadingAvailable: function()
    {
        var ua = navigator.userAgent.toLowerCase();
        return ((ua.indexOf("netscape/") == -1) && (ua.indexOf("opera") == -1))
    },
    
    addItemSwitchEventHandler: function(eventhandler)
    {
        this.itemSwitchEventHandlerList.push(eventhandler);
    },
    
    invokeItemSwitchEvent: function(olditem, newitem)
    {
        for (var i=0; i<this.itemSwitchEventHandlerList.length; i++) 
        {
            var handler = this.itemSwitchEventHandlerList[i];
            handler.invoke(olditem, newitem);
        }
    },
    
    onItemSwitch: function()
    {
    }
}

var SlideShowItem = Class.create();

SlideShowItem.prototype = {

    initialize: function(itemid, controlid)
    {
        this.itemID = itemid;
        this.displayControl = $(controlid);
    }
}

var ItemSwitchEventHandler = Class.create({

    initialize: function(delegatfunction)
    {
        this.delegatfunction = delegatfunction;
    },
    
    invoke: function(olditem, newitem)
    {
        this.delegatfunction(olditem, newitem);
    }

});

function InitializeSlideShow(slideshow)
{
    if(slideshow.items.length > 0)
    {
        slideshow.setCurrentItem(slideshow.items[0]);
        slideshow.items[0].displayControl.style.visibility = 'visible';
        slideshow.startSlideShow();
    }
}

function ShowReportingPanel(panelid, slideshow)
{
    if(!slideshow.fadingactive)
    {
        slideshow.stopSlideShow();
        $(panelid).style.display = "";
    }	
}

function HideReportingPanel(panelid)
{
    $(panelid).style.display = "none";
}
	
function DeleteCurrentItem(slideshow)
{
    if(!slideshow.fadingactive)
    {
        slideshow.stopSlideShow();
        return confirm("Mchten Sie diesen Inhalt wirklich löschen?");
    }
    
    return false;
}

// ---------------------------------------------------------------------------
// DescriptionPanel
// ---------------------------------------------------------------------------

function ShowDescriptionPanel(elementid)
{
    MoveToMousePosition(elementid);
    ChangeElementVisibility(elementid, "visible");
}

function HideDescriptionPanel(elementid)
{
    ChangeElementVisibility(elementid, "hidden");
}

function MoveToMousePosition(elementid)
{	
	var element = $(elementid);
	element.style.left = mouseposition.GetX() + 12 + "px";
	element.style.top = mouseposition.GetY() + 10 + "px";
}

function ChangeElementVisibility(elementid, visibility)
{
	if(document.layers){
		document[elementid].visibility=visibility;
	}
		
	if(document.all){
		document.all[elementid].style.visibility=visibility;
	}
		
	if(document.getElementById){
		document.getElementById(elementid).style.visibility=visibility;
	}
}

// ---------------------------------------------------------------------------
// ReorderList
// ---------------------------------------------------------------------------

var ReorderList = Class.create();

ReorderList.prototype = {

    initialize: function(element)
    {
        this.baseElement = $(element);
        
        var self = this;
        
        this.options = Object.extend(
        {
            isSortable:         true,
            defaultClass:       null,
            alternativeClass:   null,
            servicemethod:      null,
            scroll:             window,
            tag:                'li',
            onUpdate:           function(){ self.update(); self.alternateList(); }
        },
        arguments[1]);
        
        this.alternateList();
        
        if(this.options.isSortable)
        {
            Sortable.create(this.baseElement, this.options);
            this.sequence = Sortable.sequence(this.baseElement);
        }
    },
    
    update: function()
    {
        var upperitems = null;
        var loweritems = null;
        
        newsequence = Sortable.sequence(this.baseElement);
        
        for (var i=0; i<newsequence.length; i++) 
        {
            if(newsequence[i] != this.sequence[i])
            {
                if(upperitems == null)
                    upperitems = [this.sequence[i],newsequence[i]];
                
                loweritems = [this.sequence[i],newsequence[i]];
            }
        }
        
        if(loweritems[0] == upperitems[1])
        {
            //aufwärts
            this.options.onreorder(loweritems[0], upperitems[0]);
            
        }
        else
        {
            //abwärts
            this.options.onreorder(upperitems[0], loweritems[0]);
        }
        
        this.sequence = newsequence;
    },
    
    alternateList: function()
    {
        var currentindex = 1;
        var currentnode = this.baseElement.firstChild;
        
        while (currentnode != null) 
        {
            if(currentnode.tagName && currentnode.tagName.toLowerCase() == this.options.tag.toLowerCase())
            {
                if(currentindex % 2 == 0)
                {
                    currentnode.className = this.options.alternativeClass;
                }
                else
                {
                    currentnode.className = this.options.defaultClass;
                }
                
                currentindex++;
            }
            
            currentnode = currentnode.nextSibling;
        }
    },
    
    moveNext: function(index)
    {
        this.moveItem(index, 1);
    },
    
    movePrevious: function(index)
    {
        this.moveItem(index, -1);
    },
    
    moveItem: function(index, direction)
    {
        var sequence = Sortable.sequence(this.baseElement);
        
        for (var i=0; i<sequence.length; i++) 
        {
            var n = i + direction;
            
            if(sequence[i] == index && n >= 0 && n <= sequence.length)
            {
                var temp = sequence[n];
                sequence[n] = index;
                sequence[i] = temp;
                break;
            }
        }
        
        Sortable.setSequence(this.baseElement, sequence);
        this.alternateList();
        this.update();
    }
    
}

// ---------------------------------------------------------------------------
// TextCutter
// ---------------------------------------------------------------------------

var TextCutter = Class.create()

TextCutter.prototype = {

    initialize: function(containerid, cutlength)
    {
        this.container = $(containerid);
        
        this.content = this.container.innerHTML;
        this.preview = this.cut(this.content, cutlength);
        
        this.showPreview();
    },
    
    showPreview: function()
    {
        this.container.innerHTML = this.preview;
        
        if(this.content.length > this.preview.length)
        {
            this.container.innerHTML += " ... ";
            
            var self = this;
            var btn = new Element('a', {href: '#'}).update('mehr');
            btn.observe('click', function(event){self.showContent()});
            this.container.appendChild(btn);
        }
    },
    
    showContent: function()
    {
        this.container.innerHTML = this.content + "<br />";
        
        var self = this;
        var btn = new Element('a', {href: '#'}).update('zurück');
        btn.observe('click', function(event){self.showPreview()});
        this.container.appendChild(btn);
    },
    
    cut: function(text, length)
    {
        var cutout = text.substring(0, Math.min(text.length, length))
        var a = cutout.lastIndexOf(' ')
        if(a > -1)
            cutout = cutout.substring(0, a);
              
        return cutout;
    }

}

// ---------------------------------------------------------------------------
// UrlUtility
// ---------------------------------------------------------------------------

var UrlUtility = Class.create();

UrlUtility.prototype = {

    initialize: function(url)
    {
        this.url = url;
    },
    
    getUrlParam: function(index)
    {
        if(this.url != null)
        {
            var aurl = this.url.split('/');
            var aparams = aurl[aurl.length-1].split('.');
            
            if(aparams.length > 2)
            {
                aparams.shift();
                aparams.pop();
                
                for(var i=0; i<=aparams.length; i++)
                {
                    if(i == index)
                        return aparams[i]
                }
            }
        }
        
        return null
    },
    
    setUrlParam: function(index, value)
    {
        if(this.url != null)
        {
            var aurl = this.url.split('/');
            var aparams = aurl[aurl.length-1].split('.');
            
            if(aparams.length > 2)
            {
                var name = aparams[0];
                var suffix = aparams[aparams.length-1];
                aparams.shift();
                aparams.pop();
                
                for(var i=0; i<=index; i++)
                {
                    if(aparams.length-1 < i)
                        aparams.push(0);
                        
                    if(i==index)
                        aparams[i] = value;
                }
                
                this.url = name + "." + aparams.join('.') + "." + suffix;
            }
        }
        
        return this.url;
    },

    setUrlParamList: function(list)
    {
        var self = this;
        $H(list).each(function(item){self.setUrlParam(item.key, item.value)});
        return this.url;
    },
    
    setQueryStringParam: function(key, value)
    {
        if(this.url != null)
        {
            key = key.toString().strip().toLowerCase();
            value =  value.toString().strip().toLowerCase();
            
            var newitem = key + "=" + value;
            
            var aquerystring = new Array();
            
            var aurl = this.url.split('?');
            
            if(aurl.length > 1)
            {
                var aparams = aurl[aurl.length-1].split('&');
                
                if(aparams.length > 0)
                {
                    for(var i=0; i<aparams.length; i++)
                    {
                        var currentitem = aparams[i].toLowerCase();
                        if(currentitem.split('=')[0] != key)
                            aquerystring.push(currentitem);
                    }
                }
            }
            
            aquerystring.push(newitem);
            this.url = aurl[0] + "?" + aquerystring.join('&');
        }
        
        return this.url;
    },
    
    setQueryStringParamList: function(list)
    {
        var self = this;
        $H(list).each(function(item){self.setQueryStringParam(item.key, item.value)});
        return this.url;
    }
    
}

// ---------------------------------------------------------------------------
// HiddenParamParser
// ---------------------------------------------------------------------------

var HiddenParamParser = Class.create();
        
HiddenParamParser.prototype = {

    initialize: function()
    {
        var expr =  /\$(\w+)\:(\w+)/gi;
        var anchorlist = document.body.getElementsByTagName('a');
        
        for(var i=0; i<= anchorlist.length; i++)
        {
            var anchor = $(anchorlist[i]);
            
            if(anchor != null)
            {
                var cname = anchor.className;
                if(cname != null)
                {
                    if(cname.indexOf('$') > -1)
                    {
                        var url = anchor.readAttribute('href');
                        if(url != null)
                        {
                            var params = new Hash();
                            var match;
                            
                            while(match = expr.exec(cname))
                            {  
                                params.set(RegExp.$1, RegExp.$2);
                            }
                            
                            var util = new UrlUtility(url);
                            util.setQueryStringParamList(params);
                            anchor.writeAttribute('href', util.url);
                        }
                    }
                }
            }
        }
    }
    
}

document.observe("dom:loaded", function(){new HiddenParamParser()}, false);

// ---------------------------------------------------------------------------
// Calendar
// ---------------------------------------------------------------------------

var Calendar = Class.create({
            
    initialize: function(containerid, startdate)
    {
        this.container = $(containerid);
        
        this.visibleDate = new Date(startdate.getFullYear(), startdate.getMonth(), 1);
        this.selectedDate = this.cloneDate(startdate);
        
        this.options = Object.extend(
        {
            freeSelection: true,
            selectableDates: null
        },
        arguments[2]);
        
        this.days = new Hash({ 1: 'Mo', 2: 'Di', 3: 'Mi', 4: 'Do', 5: 'Fr', 6: 'Sa', 0: 'So' });
        this.month = new Hash({ 0: 'Januar', 1: 'Februar', 2: 'März', 3: 'April', 4: 'Mai', 5: 'Juni', 6: 'Juli', 7: 'August', 8: 'September', 9: 'Oktober', 10: 'November', 11: 'Dezember'});
        this.rows = 6;
        this.dateToRender = null;
        
        this.dateSelectionChanged = function(newdate){}
        
        this.render();
    },
    
    render: function()
    {
        this.dateToRender = this.cloneDate(this.visibleDate);
        
        var table = new Element('table', {'class': 'calendar'});
        
        this.renderTableHead(table);
        this.renderTableBody(table);
        
        this.container.innerHTML = '';
        this.container.appendChild(table);
    },
    
    renderTableHead: function(table)
    {
        var thead = new Element('thead');
        
        this.renderNavigation(thead);
        this.renderDescription(thead);
        
        table.appendChild(thead);
    },
    
    renderNavigation: function(thead)
    {
        var _self = this;
    
        var row = new Element('tr');
        
        row.appendChild(this.createNavigationItem('&lt&lt', function(){_self.onClick_PreviousYear()}).wrap(new Element('th')));
        row.appendChild(this.createNavigationItem('&lt', function(){_self.onClick_PreviousMonth()}).wrap(new Element('th')));
        row.appendChild(new Element('th', {'style': 'text-align:center;', 'colspan': _self.days.size() - 4}).update('<h3>' + _self.month.get(_self.visibleDate.getMonth()) + '&nbsp;' + _self.visibleDate.getFullYear() + '</h3>'));
        row.appendChild(this.createNavigationItem('&gt', function(){_self.onClick_NextMonth()}).wrap(new Element('th')));
        row.appendChild(this.createNavigationItem('&gt&gt', function(){_self.onClick_NextYear()}).wrap(new Element('th')));
        
        thead.appendChild(row);
    },
    
    renderDescription: function(thead)
    {
        var row = new Element('tr');
        
        this.days.each(function(item)
        {
            row.appendChild(new Element('th', { 'class': 'description' }).update(item.value));
        });
        
        thead.appendChild(row);
    },
    
    renderTableBody: function(table)
    {
        var _self = this;
        
        var body = new Element('tbody');
        
        (this.rows).times(function(r)
        {
            var row = new Element('tr');
            
            _self.days.each(function(day)
            {
                var td = new Element('td');
                
                if(_self.dateToRender.getMonth() == _self.visibleDate.getMonth() && _self.dateToRender.getDay() == day.key)
                {
                    if(_self.isEqualDate(_self.dateToRender, _self.selectedDate))
                    {
                        td.addClassName('selected');
                    }
                    
                    td.appendChild(_self.getCalendarDayElement());
                    _self.dateToRender.setDate(_self.dateToRender.getDate() + 1);
                }
                else
                {
                    td.update('&nbsp;');
                }
                
                row.appendChild(td);
            });
            
            body.appendChild(row);
        });
        
        table.appendChild(body);
    },
    
    createNavigationItem: function(text, onclick)
    {
        var _self = this;
        
        var item = new Element('a', { 'href': 'javascript:void(0);'});
        Event.observe(item, 'click', onclick);
        item.update(text);
        return item;
    },
    
    getCalendarDayElement: function()
    {
        var _self = this;
        
        var date = this.cloneDate(this.dateToRender);
        var selectable = false;
        
        if(this.options.selectableDates != null)
        {
            for(var i=0; i<this.options.selectableDates.length; i++)
            {
                if(this.isEqualDate(this.options.selectableDates[i], this.dateToRender))
                {
                    selectable = true;
                    break;
                }
            }
        }
        
        var element;
        
        if((this.options.freeSelection || selectable) && (!this.isEqualDate(date, this.selectedDate)))
        {
            element = new Element('a', { 'href': 'javascript:void(0);'});
            Event.observe(element, 'click', function(){_self.onClick_DateSelection(date);});
        }
        else
        {
            element = new Element('span');
        }
        
        element.update(date.getDate());
        return element;
    },
    
    isEqualDate: function(firstdate, seconddate)
    {
        return firstdate.getFullYear() == seconddate.getFullYear() && firstdate.getMonth() == seconddate.getMonth() && firstdate.getDate() == seconddate.getDate()
    },
    
    cloneDate: function(date)
    {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate());
    },
    
    onClick_NextYear: function()
    {
        this.visibleDate.setFullYear(this.visibleDate.getFullYear() + 1);
        this.render();
    },
    
    onClick_PreviousYear: function()
    {
        this.visibleDate.setFullYear(this.visibleDate.getFullYear() - 1);
        this.render();
    },
    
    onClick_NextMonth: function()
    {
        this.visibleDate.setMonth(this.visibleDate.getMonth() + 1);
        this.render();
    },
    
    onClick_PreviousMonth: function()
    {
        this.visibleDate.setMonth(this.visibleDate.getMonth() - 1);
        this.render();
    },
    
    onClick_DateSelection: function(date)
    {
        this.selectedDate = this.cloneDate(date);
        this.render();
        this.dateSelectionChanged(date);
    }
    
});

// ---------------------------------------------------------------------------
// LinkMaskUtility
// ---------------------------------------------------------------------------

var LinkMaskUtility = {

    charlist: '1234567890´`qwertzuiopü+#äölkjhgfdsayxcvbnm,.-*_:;|!\\§$%&/()=?QWERTZUIOPÜÄÖLKJHGFDSAYXCVBNM@\n\t\r\b\f',
    
    encryptUrl: function(value)
    {
        res = '';
        for(i=0; i<value.length; i++)
        {
            a = value.substr(i,1);
            b = LinkMaskUtility.charlist.indexOf(a) + 99;
            while(b > LinkMaskUtility.charlist.length){b = b-LinkMaskUtility.charlist.length;}
            res += LinkMaskUtility.charlist.substr(b,1);
        }
        
        return(encodeURIComponent(res));
    },
    
    decryptUrl: function(value)
    {
        value = decodeURIComponent(value);
        res = '';
        for(i=0; i<value.length; i++)
        {
            a = value.substr(i,1);
            b = LinkMaskUtility.charlist.indexOf(a) - 99;
            while(b < 0){b = b+LinkMaskUtility.charlist.length;}
            res += LinkMaskUtility.charlist.substr(b,1);
        }
        
        return(res);
    },
    
    follow: function(urlcrypt)
    {
        document.location.href = LinkMaskUtility.decryptUrl(urlcrypt);
    },
    
    open: function(urlcrypt)
    {
        window.open(LinkMaskUtility.decryptUrl(urlcrypt));
    }

};

// ---------------------------------------------------------------------------
// AutoComplete
// ---------------------------------------------------------------------------

var AutoCompleteProvider = Class.create({
    
    initialize: function(servicemethod)
    {
        this.serviceMethod = servicemethod;
        
        this.consumers = new Array();
        this.results = new Hash();
    },
    
    request: function(input, count, callback)
    {
        var _self = this;
        
        var cacheresult = this.results.get(input);
        if(cacheresult == null)
        {
            this.serviceMethod(input, count, function(result){ _self.results.set(input, result); callback(result); }, function(error){ /*alert(error.get_message());*/ });
        }
        else
        {
            callback(cacheresult);
        }
    },
    
    bind: function(targetid, oncomplete, options)
    {
        this.consumers.push(new AutoCompleteConsumer(targetid, this, oncomplete, options));
    }

});

var AutoCompleteConsumer = Class.create({
    
    initialize: function(targetid, source, oncomplete)
    {
        this.element = $(targetid);
        this.source = source;
        this.onComplete = oncomplete;
        
        this.refreshExecutor = null;
        this.lastrequest = null;
        this.requestinprocess = false;
        
        this.options = Object.extend(
        {
            minimumPrefixLength: 3,
            completionSetCount: 10,
            completionInterval: 0.75
        }, arguments[3]);
        
        this.element.writeAttribute('autocomplete', 'off');
        
        this.wrapper = new Element('div', { 'class':'acomplete' });
        this.element.insert({after:this.wrapper});
        
        this.initEventHandler();
    },
    
    initEventHandler: function()
    {
        var _self = this;
        this.element.observe('focus', function(){ _self.activate(); });
        this.element.observe('blur', function(){ _self.deactivate(); });
        this.element.observe('keydown', function(event){ if(GuiHelper.isKeyPressed(event,[13])) _self.deactivate(); });
    },
    
    activate: function()
    {
        if(this.refreshExecutor == null)
        {
            var _self = this;
            this.refreshExecutor = new PeriodicalExecuter(function(){ _self.refresh(); }, this.options.completionInterval);
        }
    },
    
    deactivate: function()
    {
        var _self = this;
        
        this.refreshExecutor.stop(); 
        this.refreshExecutor = null;
        
        window.setTimeout(function(){  _self.hide(); }, 300);
    },
    
    refresh: function()
    {
        var _self = this;
        
        var value = this.element.value;
        
        if(value.length >= this.options.minimumPrefixLength)
        {
            if(this.lastrequest != value)
            {
                if(!this.requestinprocess)
                {
                    this.requestinprocess = true;
                    this.source.request(value, this.options.completionSetCount, function(result){ _self.requestinprocess = false; _self.render(result) });
                    
                }
            }
        }
        else
        {
           this.hide(); 
        }
        
        this.lastrequest = value;
    },
    
    render: function(result)
    {
        var _self = this;
        
        var list = new Element('ul');
        
        for(var i = 0; i < result.length; i++)
        {
            var link = new Element('a', {'href':'javascript:void(0);'});
            link.update(result[i]);
            link.observe('click', function(event){ _self.selectTerm(event.element().innerHTML); });
            
            list.appendChild(link.wrap(new Element('li')));
        }
        
        this.clear();
        this.wrapper.appendChild(list);
        
        if(result.length > 0)
            this.show();
        else
            this.hide();
        
    },
    
    selectTerm: function(searchterm)
    {
        this.element.value = searchterm;
        
        if(this.onComplete != null)
            this.onComplete(this.element);
    },
    
    clear: function()
    {
        if(this.wrapper.hasChildNodes())
        {
            this.wrapper.innerHTML = '';
        }
    },
    
    hide: function()
    {
        this.wrapper.hide();
    },
    
    show: function()
    {
        this.wrapper.show();
    }
    
});

// ---------------------------------------------------------------------------
// WatermarkExtender
// ---------------------------------------------------------------------------

var WatermarkExtender = Class.create({

    initialize: function(targetid, text)
    {
        var _self = this;
        
        this.element = $(targetid);
        this.text = text;
        this.wmark = this.createWmark();
        
        this.wmark.observe('focus', function(){ this.nextSibling.focus(); this.hide(); });
        this.element.observe('focus', function(){ this.previousSibling.hide(); });
        this.element.observe('blur', function(){ if(_self.getcprop(this).length==0){ this.previousSibling.writeAttribute('style', this.readAttribute('style')); this.previousSibling.show(); } });
        
        var wrapper = new Element('span');
        Element.wrap(this.element, wrapper);
        wrapper.insert({ top:this.wmark });
    },
    
    createWmark: function()
    {
        var wmark = this.element.clone();
        wmark.name += "WMExtender";
        wmark.id += "WMExtender";
        wmark.writeAttribute('tabindex', null);
        this.setcprop(wmark, this.text);
        
        wmark.className = 'wmark' + ((wmark.className.length > 0) ? ' ' + wmark.className : '');
        
        if(this.element.value.length > 0)
            wmark.hide();
        else
            wmark.show();
            
        return wmark;
    },
    
    getcprop: function(element)
    {
        
        return element.value;
    },
    
    setcprop: function(element, value)
    {
        
        element.value = value;
    }

});

// ---------------------------------------------------------------------------
// TextBoxFilter
// ---------------------------------------------------------------------------

var TextBoxFilter = Class.create({
    
    initialize: function(targetid, expr)
    {
        var _self = this;
        
        this.element = $(targetid);
        this.expr = expr;
        
        this.element.observe('keypress', function(e){
        
            if (!GuiHelper.isKeyPressed(e, [8, 37, 38, 39, 40, 116]))
            { 
                if(GuiHelper.isKeyMatch(e, _self.expr)) 
                    e.stop(); 
            }
            
        });
        
        this.element.observe('keyup', function(){ this.value = this.value.replace(_self.expr, '') });
    }

});

var NumericFilter = Class.create(TextBoxFilter, {

    initialize: function($super, targetid)
    {
        $super(targetid, /[^\d]/ig);
    }

});
