Native.implement([Element, Window, Document, Events], {
  addOneEvent: function(type, fn) {
        return this.addEvent(type, function() {
            this.removeEvent(type, arguments.callee);
            return fn.apply(this, arguments);
        });
    }
});


String.implement({
	parseQueryString: function(){
		var vars = this.split(/[&;]/), res = {};
		if (vars.length) vars.each(function(val){
			var index = val.indexOf('='),
				keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g),
				value = decodeURIComponent(val.substr(index + 1)),
				obj = res;
			keys.each(function(key, i){
				var current = obj[key];
				if(i < keys.length - 1)
					obj = obj[key] = current || {};
				else if($type(current) == 'array')
					current.push(value);
				else
					obj[key] = $defined(current) ? [current, value] : value;
			});
		});
		return res;
	}
});


/* toJSON:
 - use: var query = element.toJSON();
 - outputs an object like: {"param1": "value1", "param2": "value2"}
---------- */

Element.implement({
    toJSON: function(){
        var queryObject = {};
        this.getElements('input, select, textarea', true).each(function(el){
            if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
            var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
                return opt.value;
            }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
            $splat(value).each(function(val){
                if (typeof val != 'undefined') {
                    if (typeof queryObject[el.name] != 'undefined') {
                        if ($type(queryObject[el.name]) == 'array') {
                            queryObject[el.name].push(val);
                        } else {
                            queryObject[el.name] = [queryObject[el.name],val];
                        }
                    } else {
                        queryObject[el.name] = val;
                    }
                }
            });
        });
        return queryObject;
    }
});



var CNB = {};


CNB.log = function(){
    if (window.console && console.log){
        try {
            console.log.apply(console, arguments);
        } catch(e) {
            console.log(Array.slice(arguments));
        }
    }
};


CNB.htmlToElement = function(html, container) {
    if ($type(html) == 'string') {
        html = new Element('div', {
            'html': html
        });
        
        if (container === false) {
            html = html.getChildren();
        }
    }
    return html;
};


CNB.scrollTo = function(id) {
    var section = $(id);
    if(section !== null) {
        var scroll = new Fx.Scroll(window).toElement(section);
    }
};


CNB.getCookieHost = function() {
    var hostMatch = location.hostname.match("([^\.]*?)\.(com|net|org|co\.uk|com\.au|co\.cn)$");
    if($chk(hostMatch)) {
        return '.' + hostMatch[0];
    } else {
        return location.hostname;
    }
};


CNB.hasCookiesEnabled = function() {
    Cookie.write('cookies-enabled', 'enabled');

    if(Cookie.read('cookies-enabled') != null) {
        Cookie.dispose('cookies-enabled');
        return true;
    } else {
        return false;
    }
};



CNB.User = new Hash({
    //'userName' : null,
    //'email': null,
    //'rememberMe': null,
    'regId': null,
    'isLoggedIn': false,
    'geoDomain': null
});


CNB.Overlay = new Class({
    Implements: [Options, Events],
    options: {
        //onContentReady: $empty,
        //onSetContent: $empty,
        //onOpen: $empty,
        //onOpenComplete: $empty,
        //onClose: $empty,
        //onRemove: $empty,
        id: 'overlay',
        containerClass: 'contain-overlay-12', //class name of container
        retainContent: false,
        //NOTE: use only one of content or contentId to fill overlay
        content: null, // element(s) or html string
        contentId: null //id of element that has the content
    },

    initialize: function(options){
        this.setOptions(options);

        this.shell = new Element('div', {
            'id': this.options.id,
            'class': 'overlay-shell',
            'styles': {
                'display': 'none'
            }
        });

        this.overlay = new Element('div', {
            'class': 'overlay'
        }).inject(this.shell);

        this.interior = new Element('div', {
            'class': 'overlay-int'
        }).inject(this.overlay);

        var closeButton = new Element('a', {
            'class': 'overlay-close',
            'text': 'close',
            'events': {
                'click': this.close.bind(this)
            }
        }).inject(this.interior);

        this.contentContainer = new Element('div', {
            'class': 'overlay-content clear'
        }).inject(this.interior);

        this.containerClass = '';
        this.setContainerClass();

        this.addEvent('contentReady', function(content) {
            content.getElements('.close').each(function(el) {
                el.addEvent('click', this.close.bind(this));
            }.bind(this));
            
            var placeholders = content.getElements('*[placeholder]');
            if (placeholders.length > 0) {
                var placeholder = new CNB.Placeholder(placeholders);
            }
        }.bind(this));

        this.addEvent('open', function() {
            window.addEvent('keydown', this.keyEvents.bind(this));
        }.bind(this));

        this.addEvent('close', function() {
            window.removeEvent('keydown', this.keyEvents);
        }.bind(this));

        window.addEvent('domready', function() {            
            this.shell.inject($(document.body));
        }.bind(this));
    },

    open: function(options) {
        if ($chk(options)) this.setOptions(options);

        //need to make sure dom is ready
        window.addEvent('domready', function() {
            this.fireEvent('open');
            this.setContainerClass();
            this.setContent();
            this.position();
            this.shell.setStyle('display', 'block');
            
            this.fireEvent('openComplete');
        }.bind(this));

        return this;
    },

    close: function() {
        this.fireEvent('close');
        this.shell.setStyle('display', 'none');
    },

    remove: function() {
        this.fireEvent('remove');
        this.shell.dispose();
    },

    notify: function(message, options) {        
        if(!$chk(message)) {
            CNB.log('No notification received for overlay.');
            return false;
        }
        
        var notification = new Element('div', {'class': 'notify', 'html': message});
        
        this.addOneEvent('openComplete', function() {
            this.closeDelay = this.close.delay(2500, this);
            this.addOneEvent('close', function() { $clear(this.closeDelay); }.bind(this));
        }.bind(this));
        
        this.open($merge(options, {'content': notification, 'containerClass': 'contain-overlay-12'}));
    },

    setContent: function(options) {
        if ($chk(options)) this.setOptions(options);

        this.fireEvent('setContent');

        if ($chk(this.options.content)) {
            this._setHtml(this.options.content);
        } else if($chk(this.options.contentId)){
            this._getHtml(this.options.contentId);
        }

        this.setOptions({
            'content': null,
            'contentId': null
        });

    },

    position: function() {
        var offset = Math.floor(window.getSize().y / 6);
        var scroll = window.getScroll().y;
        this.overlay.setStyle('margin-top', offset + scroll);
        this.shell.setStyle('height', window.getScrollSize().y);
    },

	setContainerClass: function() {
        if ($chk(this.containerClass)) this.overlay.removeClass(this.containerClass);
        this.overlay.addClass(this.options.containerClass);
        this.containerClass = this.options.containerClass;
    },

    keyEvents: function(event){
        if (event.key == 'esc') this.close();
    },

    _setHtml: function(content){
        if (this.contentContainer.getChildren().length>0) this.contentContainer.empty();
                
        if(!$chk(content)) {
            CNB.log('No content received for overlay.');
            return false;
        }

        if ($type(content) == 'string') {
            content = new Element('div', {'html': content});
        }
        
        content.inject(this.contentContainer);
        this.fireEvent('contentReady', content);
    },

    _getHtml: function(id){
        var content = $(id).get('html');
        this._setHtml(content);
    }
});



CNB.Overlay.Async = new Class({
    Extends: CNB.Overlay,
    options: {
        url: '',
        data: ''
    },

    initialize: function(options) {
        this.parent(options);

        this.loader = new CNB.Loading(this.contentContainer);

        this.addEvent('setContent', this.makeRequest.bind(this));
    },
        
    makeRequest: function() {
        if (!$chk(this.options.url)) return false;
     
        this.loader.add();

        var request = new Request.HTML({
            url: this.options.url,
            data: this.options.data,
            onSuccess: function(tree, els, html, js) {
                this._setHtml(html);
            }.bind(this),
            onComplete:  this.loader.remove.bind(this.loader),
            onFailure: function(xhr) {
                CNB.log('request - failure');
                this._setHtml('<div class="notify">Oops. Something went wrong.</div>');
            }.bind(this)
        }).get();

        this.setOptions({'url': '', 'data': ''});
    }
});


CNB.Overlay.load = function(options) {
    var overlay;
    if ($chk(options.url)) {
        overlay = new CNB.Overlay.Async(options).open();
        return false;
    } else if(options.contentId != null || options.content != null) {
        overlay = new CNB.Overlay(options).open();
        return false;
    }
    overlay = new CNB.Overlay(options).notify('Sorry. That content could not be loaded.');
};


CNB.Loading = new Class({
    Implements: Options,
    options: {
        'className': 'loading',
        'opacity': '0',
        'float': 'none' //This is due to a moo bug - can be removed with 2.0
    },

    initialize: function(container, options) {
        this.setOptions(options);

        this.contentContainer = $(container);

        this.loadingContainer = new Element('div', {
            'class': 'loading-container',
            'styles': {'float': this.options.float, 'display': this.contentContainer.getStyle('display')}
        }).wraps(this.contentContainer);
    },

    add: function() {
        this.loadingContainer.addClass(this.options.className);
        this.contentContainer.setStyle('opacity', this.options.opacity);
    },

    remove: function() {
        this.loadingContainer.removeClass(this.options.className);
        this.contentContainer.setStyle('opacity', '1');
    }
});


CNB.Selector = new Class({
    Implements: [Options, Events],
    options: {
        //onOpen: $empty,
        //onClose: $empty,
        cookieName: null, //cookie user
        startSeq: 0,
        hiddenClass: 'hide'
    },

    initialize: function(tabs, contents, options){
        this.setOptions(options);
        
        this.tabs = $splat(tabs);
        this.contents = $splat(contents);
        
        this.seq = this.options.startSeq;
        if (this.options.cookieName != null) {
            var prevSeq = Cookie.read(this.options.cookieName);
            if (parseInt(prevSeq) < this.tabs.length) this.seq = prevSeq;
        }
    },

    load: function() {
        if ((this.tabs.length == 0) || (this.contents.length == 0)) return false;
                        
        this.tabs.each(function(el, i) {
            if (!el.hasClass('external')) {
                el.addEvent('click', function(e){
                    e.stop();
                    this.open(i);
                }.bind(this));
                this.close(i);
            }
        }, this);
        
        this.open(this.seq);
        this.addEvent('open', function() {
            this.close(this.seq);
        }.bind(this));
        
        return this;
    },

    open: function(i){
        this.fireEvent('open', i);
        this.tabs[i].addClass('on');
        this.contents[i].removeClass(this.options.hiddenClass);
        this.seq = i;
        if (this.options.cookieName != null) {
            Cookie.write(this.options.cookieName, i, {
                'domain': CNB.getCookieHost(),
                'duration': 30,
                'path': location.pathname
            });
        }
    },

    close: function(i){
        this.fireEvent('close', i);

        this.tabs[i].removeClass('on');
        this.contents[i].addClass(this.options.hiddenClass);
    }
});



CNB.Pop = new Class({
    Extends: CNB.Selector,
    options: {
        hiddenClass: 'pop-hidden',
        position: {
            x: 'left',
            y: 'bottom'
        },
        offset: {
            x: 0,
            y: 0
        }
    },

    initialize: function(tabs,contents,options){
        this.parent(tabs, contents, options);

        this.addEvent('open', function(i){
            this.contents[i].setStyle('visibility', 'visible');
            this.position(i);
        }.bind(this));           

        this.addEvent('close', function(i) {
            this.contents[i].setStyle('visibility', 'hidden');
        }.bind(this));
    },

    load: function() {

        this.tabs.each(function(tab, i) {
            this.close(i);
            this.contents[i].setStyles({'position': 'absolute'});

            [tab, this.contents[i]].each(function(el) {
                el.addEvent('mouseenter', this.handleMouseEnter.pass(i, this));
                el.addEvent('mouseleave', this.handleMouseLeave.pass(i, this));
            }, this);
        }, this);

        return this;
    },
    
    handleMouseEnter: function(i) {
        $clear(this.closeDelay);
        if(i != this.seq) this.close(this.seq);
        this.openDelay = this.open.delay(100, this, i);    
    },
    
    handleMouseLeave: function(i) {
        $clear(this.openDelay);
        this.closeDelay = this.close.delay(250, this, i);    
    },

    position: function(i) {
        this.contents[i].position({
            relativeTo: this.tabs[i],
            position: this.options.position,
            offset: this.options.offset
        });
    }
});



CNB.Rotocop = new Class({
    Implements: [Options,Events],
    options: {
        //onNext: $empty,
        //onPrevious: $empty,
        //onLoad: $empty,
        itemsPerPage: 3, //number of items in window
        showPrevNext: true,
        prevNextClass: '',
        controlsClass: '',
        showPage: false, // show nav that shows which pane you are on
        slide: true, // show slide fx
        startPage: 1, // which page to start on
        timer: null // time in miliseconds
    },

    initialize: function(container,options){
        this.setOptions(options);		
        this.container = $(container);
        this.contents = this.container.getChildren();
        
        this.itemCount = this.contents.length;
        this.pageCount = Math.ceil(this.itemCount / this.options.itemsPerPage);
        this.page = this.options.startPage;
        
        this.width = this.container.getSize().x;

        this.cover = new Element('div', {
            'styles': {
                'width': this.width,
                'overflow':'hidden',
                'position': 'relative'
            }
        });

        this.controls = new Element('div', {
            'class': 'roto-controls ' + this.options.controlsClass
        });
    },

    load: function() {
        if (this.pageCount == 1) return false;

        this.fireEvent('load', this.contents);

        this.container.setStyle('width', '10000px');

        this.cover.wraps(this.container);
        
        this.controls.inject(this.cover, 'after');

        if (this.options.showPrevNext) this.buildPrevNextNav();
        if (this.options.showPage) this.buildPageNav();
        if (this.page != 1) this.rotate(this.page);
        if ($chk(this.options.timer)) this.setTimer();

        return this;
    },

    setTimer: function(){
        var timer = this.next.periodical(this.options.timer,this);

        // clear timer when a button is clicked
        var btns = [];
        if (this.options.showPrevNext) btns.extend([this.prevBtn, this.nextBtn]); 
        if (this.options.showPage) btns.extend(this.pageNav); 

        btns.each(function(el){ 
            el.addEvent('click', function(){
                $clear(timer);
            });
        });
    },

    rotate: function(page){
        var firstInPage = this.contents[this.options.itemsPerPage * (page - 1)];

        var shift = firstInPage.getPosition(this.container).x;

        if (this.options.slide == true) {
            this.container.tween('margin-left', '-'+shift+'px');
        } else {
            this.container.setStyle('margin-left', '-'+shift+'px');
        }

        if(this.options.showPage) {
            this.pageNav[this.page - 1].removeClass('on');
            this.pageNav[page - 1].addClass('on');
        }

        if(this.options.showPrevNext) {
            [this.prevBtn, this.nextBtn].each(function(el) {
                el.removeClass('disabled');
            });
			document.getElementById('provectorvenstreknap').style.display=''
			document.getElementById('provectorhojreknap').style.display=''
            switch (page) {
                case this.pageCount: 
                    this.nextBtn.addClass('disabled');
					//alert(page + ' ' + this.pageCount)
					document.getElementById('provectorhojreknap').style.display='none'
					document.getElementById('provectorvenstreknap').style.display=''
					//Skjuler højre knap
                    break;
                case 1:
                    this.prevBtn.addClass('disabled');
					document.getElementById('provectorhojreknap').style.display=''
					document.getElementById('provectorvenstreknap').style.display='none'
					
                    break;
            }
        }

        this.page = page;
    },

    buildPrevNextNav: function(){
        var prevNext = new Element('div', {
            'class': 'prev-next ' + this.options.prevNextClass
        });

        this.prevBtn = new Element('a', {
            'class': 'btn prev disabled',
            'html': '<img src=3design/prevnow.png style="cursor:pointer;display:none" id="provectorvenstreknap" onmouseover="this.src=\'/3design/prevnow.png\'" onmouseout="this.src=\'/3design/prevnow.png\'">',
            'events': {
                'click': this.prev.bind(this)
            }
        }).inject(prevNext,'bottom');

        this.nextBtn = new Element('a', {
            'class': 'btn next',
            'html': '<img src="/3design/nextnow.png" style="cursor:pointer;" id="provectorhojreknap" onmouseover="this.src=\'/3design/nextnow.png\'" onmouseout="this.src=\'/3design/nextnow.png\'">',
            'events': {
                'click': this.next.bind(this)
            }
        }).inject(prevNext,'bottom');

        prevNext.inject(this.controls, 'bottom');
    },

    buildPageNav: function(){
        this.pageNav = [];
        var container = new Element('ul', {'class': 'roto-pagination clear'});
        for (i=0;i<this.pageCount;i++) {
            this.pageNav[i] = new Element('li', {'html': '<a>'+ (i + 1) +'</a>'}).inject(container,'bottom');
        }
        this.pageNav[0].addClass('on');
        this.pageNav.each(function(el, i){ 
            el.addEvent('click', this.rotate.pass(i + 1, this));
        }, this);
        
        container.inject(this.controls,'bottom');
    },

    prev: function(){
        var prevPage = (this.page == 1) ? this.pageCount : this.page - 1;
        this.rotate(prevPage);
        this.fireEvent('prev');
    },

    next: function(){
        var nextPage = (this.page == this.pageCount) ? 1 : this.page + 1;
        this.rotate(nextPage); 
        this.fireEvent('next');
    }
});



CNB.TagsMore = new Class({
    Implements: Options,
    options: {
        visibleCount: 2,
        textMore: 'more&nbsp;+',
        textLess: 'less&nbsp;+'
    },
    
    initialize: function(containers, options) {
        this.setOptions(options);

        $splat(containers).each(function(el) {
            var tags = el.get('html').clean().split(',');
            if (tags.length > this.options.visibleCount) this.build(el, tags);
        }, this);
    },
    
    build: function(container, tags) {
        var moreTags = tags.splice(this.options.visibleCount, tags.length - this.options.visibleCount);
        
        
        var moreCont = new Element('span', {
            'class': 'tags-more',
            'html': moreTags.join(', ')
        });
        
        var moreBtn = new Element('a', {
            'class': 'more',
            'html': this.options.textMore
        });
        moreBtn.addEvent('click', this.toggle.pass([moreCont, moreBtn], this));
        
        var hold = new Element('div', {
            'html': tags.join(', ') + ', '
        }).adopt(moreCont).appendText(' ').adopt(moreBtn);
        
        container.empty().adopt(hold);
        this.hide(moreCont, moreBtn);
    },
    
    toggle: function(moreCont, moreBtn) {
        if (moreCont.retrieve('isVisible')) {
            this.hide(moreCont, moreBtn);
        } else {
            this.show(moreCont, moreBtn);
        }
    },
    
    show: function(moreCont, moreBtn) {
        moreCont.store('isVisible', true).setStyle('display', 'inline');
        moreBtn.set('html', this.options.textLess);
    },
    
    hide: function(moreCont, moreBtn) {
        moreCont.store('isVisible', false).setStyle('display', 'none');
        moreBtn.set('html', this.options.textMore);
    }
});



CNB.Expander = new Class({
    Implements: [Options, Events],
    options: {
//      onShow: $empty,
//      onHide: $empty,
        content: null,
        action: 'click',
        hidden: 'true', //hide at start
        btn: null,
        btnClass: '',
        btnText: {
            show: 'Show',
            hide: 'Hide'
        }
    },

    initialize: function(container, options){
        this.setOptions(options);

        this.container = $(container);

        if ($chk(this.options.btn)) {
            this.btn = $(this.options.btn);
        } else {
            var btnCont = new Element('div', {
                'class': 'more ' + this.options.btnClass
            });
            
            this.btn = new Element('a', {
                'text': this.options.btnText.show
            }).inject(btnCont);
            
            btnCont.inject(this.container, 'after');
        }

        this.slide = new Fx.Slide(this.container, {
            'mode': 'vertical'
        }).hide();

        this.visible = false;

        this.btn.addEvent(this.options.action, this.toggle.bind(this));

        if ($chk(this.options.btnText.show)) {
            this.addEvents({
                'show': this.setText.bind(this,this.options.btnText.hide),
                'hide': this.setText.bind(this,this.options.btnText.show)
           });
        }
    },

    toggle: function(){
        if (this.visible) {
        this.hide();
        } else {
            this.show();
        }
    },

    setText: function(t){
        var text = ($chk(t)) ? t : (this.visible) ? this.options.btnText.hide : this.options.btnText.show;
        this.btn.set('html', text);
    },

    show: function() {
        this.visible = true;
        this.slide.slideIn();
        this.fireEvent('show', [this.container, this.btn]);
    },

    hide: function(){
        this.visible = false;
        this.slide.slideOut();
        this.fireEvent('hide', [this.container, this.btn]);
    }
});




CNB.Placeholder = new Class({
    initialize: function(inputs){
        var placeholders = $splat(inputs);
        if(placeholders.length > 0) {
            this.set(placeholders);
        }
    },

    addPlaceholder: function(el) {
        var placeholder = el.getProperty('placeholder');
        if(!$chk(el.value) || (el.value == placeholder)) {
            el.addClass('placeholder');
            el.value = placeholder;
        }
    },

    removePlaceholder: function(el) {
        if (el.hasClass('placeholder')) {
            el.value = '';
            el.removeClass('placeholder');
        }	
    },

    set: function(inputs) {			
        inputs.each(function(el){
            try {
                el.store('placeholder', this);
                el.addEvents({
                    'focus': this.removePlaceholder.pass(el),
                    'blur': this.addPlaceholder.pass(el)
                });
                this.addPlaceholder(el);
            } catch(e) {
                CNB.log('placeholder error:', e);
            }
        }, this);
    }
});




CNB.Validator = new Class({
    Implements: [Options, Events],
    options: {
        onValidateSuccess: function(event, form) {

        },
        onValidateFailure: function(event, form) {
            event.stop();
        },
        validateOnBlur: true,
        scrollToErrors: false
    },

    initialize: function(form, options){
        this.setOptions(options);

        this.form = $(form);

        this.rules = {
            'required': {
                'type': 'field',
                'msg': 'This field is required',
                'test': function(el) {
                    if (el.type == 'select-one' || el.type == 'select') {
                        return (el.selectedIndex >= 0 && el.options[el.selectedIndex].value != '');
                    } else {
                        return ((el.get('value') != null) && (el.get('value').length != 0));
                    }              
                }
            },
            'one-required': {
                'type': 'group',
                'msg': 'One of these fields is required',
                'test': function(el) {
                    var parent = el.getParent('.validate-group') || el.getParent();
                    
                    return parent.getElements('input').some(function(el){
                        if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
                     //   return el.get('checked');
                    });
                }
            },
            'validate-minlength': {
                'type': 'field',
                'msg': function(el) {
                    return 'This field must be at least '+el.getProperty('minlength')+' characters';
                },
                'test': function(el) {
                    return (el.get('value').length >= el.getProperty('minlength'));
                }
            },
            'validate-maxlength': {
                'type': 'field',
                'msg': function(el) {
                    return 'This field must be no more than least '+el.getProperty('maxlength')+' characters';
                },
                'test': function(el) {
                    return (el.get('value').length <= el.getProperty('maxlength'));
                }
            },
            'validate-email': {
                'type': 'field',
                'msg': 'Please enter a valid email.',
                'test': function(el) {
                    return (el.get('value').test(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i));
                }
            },
            'validate-match': {
                'type': 'field',
                'msg': function(el) {
                    return 'This field must match the '+$(el.getProperty('match')).getProperty('name')+' field';
                },
                'test': function(el) {
                    return ($(el.getProperty('match')).get('value') == el.get('value'));
                }
            },
            'validate-specialchars': {
                'type': 'field',
                'msg': 'Special characters are not allowed in this field',
                'test': function(el) {
                    return !(el.get('value').test(/[%;]/));
                }
            },
            'validate-zipdcode-us': {
                'type': 'field',
                'msg': 'Invalid Postal Code',
                'test': function(el) {
                    return el.get('value').test(/^[0-9]+$/);
                }
            }
        };

        this.errors = new Hash();
        this.errorsGlobal = [];
        
        this.form.store('validator', this);

        this.form.addEvent('submit', this.validateFields.bind(this));

        if(this.options.validateOnBlur) {
            this.getFields(true).each(function(el) {
                el.addEvent('blur', this.removeError.pass(el, this));
                el.addEvent('blur', this.testField.bindWithEvent(this, el));
            }, this);
        }
    },
    
    getRule: function(name) {
        if (typeof this.rules[name] != 'undefined') {
            return this.rules[name];
        } else {
            return false;
        }
    },
    
    addRules: function(rule) {
        $each(rules, function(v, k) {
            this.rules[k] = v;
        }, this);
    },
    
    getFields: function(excludeToggles) {
        return this.form.getElements('input, select, textarea').filter(function(el){
            if (el.disabled || el.type == 'submit' || el.type  == 'reset' && el.type == 'file') {
                return false;
            }
            if(excludeToggles) {
                if (el.type == 'checkbox' || el.type  == 'radio') {
                    return false;
                }
            }
            return true;
        }, this);
    },
    
    testField: function(event, el) {
        el.className.split(' ').each(function(cl){
            var rule = this.getRule(cl);

            if (rule !== false) {
                if (event.type == 'blur' && rule.type == 'group') return;
                                
                if (!rule.test(el)) {
                    var msg = (typeof rule.msg == 'function') ? rule.msg(el) : rule.msg;
                    this.addError(el, msg, rule.type);
                }
            }
        }, this);
    },
    
    validateFields: function(event) {
        this.errors.each(this.removeError, this);

        this.errorsGlobal.each(function(el) {
            el.destroy();
        });
        
        this.getFields().each(function(el) {
            this.testField(event, el);
        }, this);

        if(this.errors.getLength() == 0) {
            this.fireEvent('validateSuccess', [event, this.form]);
        } else {
            this.fireEvent('validateFailure', [event, this.form]);

            if(this.options.scrollToErrors) {
                this.scrollToErrors();
            }
        }
    },
    
    scrollToErrors: function() {
        var i = 0;
        this.errors.each(function(el, name){
            if (i == 0) {
                var scroll = new Fx.Scroll(window).toElement(el.getParent());
            }
            i++;
        }, this);
    },
    
    removeError: function(el) {
        var error = el.retrieve('error');
        if(error == null) return;
        
        error.container.destroy();
        el.removeClass('error-input').eliminate('error');
        
        this.errors.erase(el.getProperty('name'));
    },
    
    addError: function(el, msg, type) {
        //only show one error at a time
        if(el.retrieve('error') != null) return;
        var cont;
    
        if (type == 'group') {
            var parent = el.getParent('.validate-group') || el.getParent();
            cont = new Element('div', {
                'class': 'error-lvl',
                'html': msg
            }).inject(parent, 'top');
        } else {        
            el.addClass('error-input');
    
            cont = new Element('div', {
                'class': 'error',
                'html': msg
            }).inject(el, 'after');
            
            //el.addOneEvent('blur', this.removeFieldError.pass(el, this));
        } 

        el.store('error', {'type': 'group', 'container': cont});
        this.errors.set(el.getProperty('name'), el);
    },
    
    addGlobalError: function(msg) {
        var cont = new Element('div', {
            'class': 'error-lvl',
            'html': msg
        }).inject(this.form, 'top');
        
        this.errorsGlobal.push(cont);
    }
    
});



window.addEvent('domready', function() {
    var placeholders = new CNB.Placeholder($$('input[placeholder]'));
    
    //auto scroll to
    $(document.body).addEvent('click:relay(a)', function(e, el) {
        var href = el.getProperty('href');
        if (href===null) return false;
        if (href.trim().indexOf('#') === 0) {
            e.stop();
            CNB.scrollTo(href.substr(1));
        }
    });
});
