/**
 * @package     jkefel
 * @version     1.3.0
 *
 * @author      jproven.com
 * @link        http://www.jproven.com
 * @copyright   Copyright (C) 2011 jproven.com. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
*/
var jkefel = new Class({
        Implements: [Options],
        options: {
                kefelId: '',
                cookieName: '',
                ui: 'tabs',
                trigger: 'click',
                persistTab: true,
                persistMode: 'global',
                currentHash: '',
                refreshTab: false,
                allClosed: '__all_closed__',
                selectors: {
                        'contents': {'tabs': 'div.current > dd', 'sliders': 'div.jpane-slider'},
                        'open': {'tabs': 'open', 'sliders': 'jpane-toggler-down'}
                },
                tracker: false,
                isInitial: true,
                containerKefel: null
        },

        initialize: function(options, wires, containerKefel) {
                this.setOptions(options);
                
                window.addEvent('domready', function(){
                        this.cleanCookies();
                        this.initiateTabs(wires);
                        this.options.containerKefel = eval(containerKefel);
                }.bind(this));
        },

        cleanCookies: function() {
                var vals = this._getCookie(), hasChanged = false;
                
                for (var kefelId in vals) {
                        if (!this.isPaneUI(kefelId)) {
                                hasChanged = true;
                                delete vals[kefelId];
                        }

                        if (kefelId == this.options.kefelId) {
                                if (vals[kefelId] == this.options.allClosed) {
                                        if (this.options.ui != 'sliders') {
                                                hasChanged = true;
                                                delete vals[kefelId];
                                        }
                                }
                        }
                }

                if (hasChanged) {
                        this._setCookie(vals);
                }
        },

        setCookie: function(tab) {
                var vals = this._getCookie();

                tab = this.getTab(tab);

                vals[this.options.kefelId] =
                        this.options.ui == 'sliders' && !this.isOpen(tab) ?
                        this.options.allClosed :
                        tab.getProperty('id');

                if (this.options.tracker) {
                        document.location.hash = '#'+vals[this.options.kefelId];
                }

                this._setCookie(vals);
        },

        getCookie: function() {
                var vals = this._getCookie();

                return vals[this.options.kefelId];
        },

        __getCookie: function(name) {
                var res = MooTools.version.substr(2, 1) == 1 ? Cookie.get(name) : Cookie.read(name);
                
                if ($type(res) == 'string') {
                        res = MooTools.version.substr(2, 1) == 1 ? Json.evaluate(res) : JSON.decode(res);
                }
                
                return res;
        },

        __setCookie: function(name, value) {
                if ($type(value) != 'string') {
                        value = MooTools.version.substr(2, 1) == 1 ? Json.toString(value) : JSON.encode(value);
                }
                
                return MooTools.version.substr(2, 1) == 1 ? Cookie.set(name, value) : Cookie.write(name, value);
        },

        _setCookie: function(cookies) {
                if (this.options.persistMode == 'global') {
                        this.__setCookie(this.options.cookieName, cookies, {'path': '/'});
                } else {
                        this.__setCookie(this.options.cookieName, cookies);
                }
        },

        _getCookie: function() {
                var vals = this.__getCookie(this.options.cookieName);
                
                if (!vals) {
                        vals = {};
                }

                return vals;
        },

        isTab: function(tab) {
                return this.getTabs().contains($(tab));
        },

        isFirstTab: function(tab) {
                var tabs = this.getTabs();

                return $(tab) == tabs[0];
        },

        isPane: function(pane) {
                if (!$(pane)) return false;

                return $(pane).getProperty('id') == this.options.kefelId;
        },

        isPaneUI: function(pane) {
                pane = $(pane);
                
                return pane && (
                        (pane.tagName.toLowerCase() == 'dl' && pane.hasClass('tabs')) ||
                        (pane.tagName.toLowerCase() == 'div' && pane.hasClass('pane-sliders'))
                );
        },

        getTab: function(tab) {
                tab = $(tab);
                return tab && tab.tagName.toLowerCase() == 'span' ? tab.getParent() : tab;
        },

        getTabs: function() {
                var tabs;

                if (this.options.ui == 'sliders') {
                        tabs = [];

                        $(this.options.kefelId).getChildren('div.panel').each(function(c) {
                               c.getChildren('h3.title').each (function(ic) {
                                       if (ic.tagName.toLowerCase() == 'h3') {
                                               tabs.push(ic);
                                       }
                               }.bind(this))
                        }.bind(this));
                } else if (this.options.ui == 'tabs') {
                        tabs = $(this.options.kefelId).getChildren('dt');
                }

                return tabs;
        },
        
        getContentFromTab: function(tab) {
                var content;

                tab = this.getTab(tab);
                
                if (tab) {
                        if (this.options.ui == 'tabs') {
                                var tabs = this.getTabs(), i = tabs.indexOf(tab);
                                
                                if (i != -1) {
                                        content = tab.getParent().getNext().getElements('dd')[i];
                                }
                        } else if (this.options.ui == 'sliders') {
                                content = tab.getNext();
                        }
                }

                return content;
        },

        getTabFromContent: function(content) {
                var tab;

                content = $(content);

                if (this.options.ui == 'tabs') {
                        var
                                contents = content.getParent().getChildren(),
                                i = contents.indexOf(content);
                                
                        if (i != -1) {
                                var tabs = content.getParent().getPrevious().getChildren();
                                tab = tabs[i];
                        }
                } else if (this.options.ui == 'sliders') {
                        tab = content.getPrevious();
                }

                return tab;
        },

        getTabFromPosition: function(position) {
                var tabs = this.getTabs();
                return tabs[position];
        },

        getPosition: function(tab) {
                var tabs = this.getTabs();
                var position = tabs.indexOf(tab);
                
                return position != -1 && position;
        },

        findParent: function(el, selector) {
                el = $(el);
                var prt = el.getParent(), notFound = true, fromStart = prt.getElements(selector);
                while (prt && (notFound = (prt.getElements(selector).length == fromStart.length))) {
                        el = prt;
                        prt = el.getParent();
                        
                }
                if (!notFound) {
                        return el;
                }
        },        
        
        getContainerTab: function(selector) {
                var c = this.options.containerKefel, notContains = true, result;
                
                if (!selector && c) {
                        selector = '#'+this.options.kefelId;
                }
                
                if (selector && c) {
                        while ((notContains = !this._contains(c, selector)) && c.options.containerKefel) {
                                c = c.options.containerKefel;
                        }
                }
                
                if (c && !notContains) {
                       if (selector) {
                               var tabs = c.getTabs(), contents = c.options.ui == 'tabs'? tabs[0].getParent().getNext().getChildren() : tabs;
                               for (var i = 0, n = contents.length; i < n; i++) {
                                       if (contents[i].getElements(selector).length > 0) {
                                               result = [c, tabs[i]];
                                               break;
                                       }
                               }
                       } 
                }
                
                return result;
        },
        
        _contains: function(kefel, selector) {
                if (!selector || !kefel) return false;
                var cont = $(kefel.options.kefelId);
                if (kefel.options.ui == 'tabs') cont = cont.getParent();
                return cont.getElements(selector).length > 0;
        },

        isOpen: function(tab) {
                tab = this.getTab(tab);
                return tab && tab.hasClass(this.options.selectors.open[this.options.ui]);
        },

        openTab: function(tab) {
                tab = this.getTab(tab);
                
                if (!tab || (this.isOpen(tab) && !this.options.isInitial)) return;

                var opt;

                if (this.options.ui == 'sliders') {
                        opt = [tab, tab.getNext()];
                } else if (this.options.ui == 'tabs') {
                        opt = tab;
                }

                this.options.isInitial = false;
                
                return tab.fireEvent('click', opt);
        },

        goToTab: function(tab) {
                if (this.isTab(tab)) {
                        return this.openTab(tab);
                } else if (this.isPane(tab)) {
                        var c = this.getCookie();

                        if (c == this.options.allClosed) {
                                return false;
                        }
                        
                        if (c) {
                                return this.goToTab(c);
                        } else if (this.options.ui == 'tabs') {
                                var tabs = this.getTabs(tab);
                                
                                return this.goToTab(tabs[0]);
                        }
                } else if (tab == null) {
                        tab = document.location.hash;

                        if (tab) {
                                tab = tab.replace(/^\#/, '');
                                var ctab;
                                
                                if (this.isTab(tab)) {
                                        var t = this.goToTab(tab);
                                        ctab = this.getContainerTab('#'+$(t).getProperty('id'));
                                        t = t & ctab[0].goToTab(ctab[1]);
                                        return t;
                                } else {
                                        ctab = this.getContainerTab('a[href=#'+tab+']');
                                        
                                        if (ctab) {
                                                return ctab[0].goToTab(ctab[1]) && this.goToTab(tab);
                                        } else {
                                                ctab = this.getContainerTab('#'+tab);
                                                
                                                if (ctab) {
                                                        return ctab[0].goToTab(ctab[1]) && this.goToTab(tab);
                                                }
                                        }
                                }
                        }

                        return this.goToTab(this.options.kefelId);
                }
                
                return false;
        },

        initiateTabs: function(wires) {
                this.wireTabs(wires);

		var tabs = this.getTabs();
		
		tabs.each(function(tab){
			if (this.options.trigger != 'click') {
				tab.addEvent(this.options.trigger, function(t) {
					t = this.fromEvent(t);
					this.goToTab(t);
				}.bind(this));
			}
			
			if (this.options.persistTab) {
				tab.addEvent(this.options.trigger, function(t) {
					t = this.fromEvent(t);
					
					if (t === false) return;
					
					t = this.getTab(t);
					
					this.setCookie(t);
				}.bind(this));
			}
		}.bind(this));

                window.addEvent("load", function() {
                        if (this.options.isInitial) {
                                if (this.options.ui == 'tabs' && this.options.initial) {
                                        var tab = this.getTabFromPosition(this.options.initial);
                                        this.goToTab(tab);
                                } else {
                                        this.goToTab();
                                }
                        }
                }.bind(this));
                
                this.fixInternalTabNavigation();
        },

        fromEvent: function(e) {
                if (!e) return false;

                if (typeof e == 'object' && (e.target != undefined || e.srcElement != undefined)) {
                       e = e.target ? e.target : e.srcElement;
                }

                return e;
        },

        wireTabs: function(wires) {
                var id;
                
                for (var i = 0; i < wires.length; i++) {
                        id = wires[i].id;
                        
                        if (this.isTab(id)) {
                                if (wires[i].href) {
                                        $(id).setProperty('href', wires[i].href);
                                }
                                
                                if (wires[i].onsuccess) {
                                        $(id).setProperty('onsuccess', wires[i].onsuccess);
                                }

                                if (wires[i].onsuccessp) {
                                        $(id).setProperty('onsuccessp', wires[i].onsuccessp);
                                }

                                if (wires[i].freq) {
                                        $(id).setProperty('freq', wires[i].freq);
                                }

                                $(id).setProperty('refresh', 
                                        wires[i]['refresh'] == undefined ?
                                                this.options.refreshTab :
                                                wires[i].refresh
                                );
                                        
                                $(id).addEvent(this.options.trigger, function(e) {
                                        var tab = this.fromEvent(e);

                                        if (tab === false) return;

                                        tab = this.getTab(tab);

                                        var refresh = tab.getProperty('refresh') == 'true';
                                        var freq = tab.getProperty('freq') == 'true';
                                        var loaded = tab.getProperty('loaded') == 'true';
                                        
                                        if (!refresh && loaded) {
                                                return;
                                        }
                                        
                                        if (freq && loaded) {
                                                return;
                                        }
                                        
                                        var el = this.getContentFromTab(tab);
                                        
                                        if (!tab.getProperty('href')) {
                                                if (tab.getProperty('onsuccess')) {
                                                        eval(tab.getProperty('onsuccess')+'(el)')
                                                }
                                                
                                                if (tab.getProperty('onsuccessp')) {
                                                        eval(tab.getProperty('onsuccessp'))
                                                }

                                                tab.setProperty('loaded', true);

                                                return;
                                        }

                                        if (el) {
                                                el = el.getElement('.ajax_container');
                                        } else {
                                                return;
                                        }

                                        this._load(tab, el, refresh);

                                        if (freq) {
                                                freq = parseInt(freq) * 1000;
                                                
                                                (function() {
                                                        var _el = this._periodicalRecord[this.getPosition(tab)];
                                                        this._load(tab, _el, refresh);
                                                }.bind(this)).periodical(freq);
                                        }
                               }.bind(this));
                       }
                }
        },

        _periodicalRecord: {},

        _load: function(tab, el, refresh) {
                new Ajax(tab.getProperty('href'), {
                        method: 'get',
                        noCache: refresh,
                        onSuccess: function (response) {
                                el = this.updateHTML(response, el);

                                this._periodicalRecord[this.getPosition(tab)] = el;

                                if (tab.getProperty('onsuccess')) {
                                        eval(tab.getProperty('onsuccess')+'(el)')
                                }

                                if (tab.getProperty('onsuccessp')) {
                                        eval(tab.getProperty('onsuccessp'))
                                }

                                tab.setProperty('loaded', true);
                        }.bind(this)
                }).request();
        },

        // take care of case when page hash changes (only time interval check available)
        // not even ff 3.6 seem to be firing hashchange event as it should
        fixInternalTabNavigation: function() {
                (function() {
                        var currentHash = document.location.hash;

                        if (this.options.currentHash != currentHash) {
                                this.options.currentHash = currentHash;
                                this.goToTab();
                        }
                }.bind(this)).periodical(50);
        },

        updateHTML: function (html, update) {
                // extract and replace javascript
                var jsCode = '';

                html = html.replace(/<script\s+(?![^>]*type=['"]?moo)(?:[^>]*?(?:src=(['"]?)([^>]*?)\1[^>]*)?)*>([\s\S]*?)<\/script>/gi,
                function(ignore, delim, src, code){
                        if (src) {
                                // sync download as various embedded code might expect the js files to be loaded
                                this.downloadScripts(src, 'js', true);
                        } else {
                                code = code.trim();

                                if (code != '') jsCode += code;
                        }

                        return "";
                }.bind(this));

                // extract and replace css
                html = html.replace(/<link\s+(?![^>]*type=['"]?moo)(?:[^>]*?(?:href=(['"]?)([^>]*?)\1[^>]*)?)*\/>/gi,
                function(ignore, delim, src){
                        if (src) {
                                this.downloadScripts(src, 'css');
                        }

                        return "";
                }.bind(this));


                html = html.replace(/<style[^>]*>(?:\s*@import\s*(?:url\()?[\'\"]?([^\'\"\()]+)[\'\"]?\)?;\s*)+<\/style>/gim,
                function(ignore, src){
                        if (src) {
                                this.downloadScripts(src, 'css');
                        }

                        return "";
                }.bind(this));

                html = html.trim();

                // Need to be called before appending script as script might require
                // presence of elements in html
                update = this.setHTML(update, html);

                this.appendToBody(jsCode, 'js');

                return update;
        },

        /**
         * annoying IE issues with set('html', html) and set('text', text) necessitates the
         * following 2 implementations of setHTML and appendToBody
         */

        /**
         * setHTML since setting is done by replacing given element don't forget to reset it
         */
        setHTML: function(el, html) {
                var replacement = $(el).clone(), id = $(el).getProperty('id');
                
                replacement.innerHTML = html;

                var parent = el.parentNode;

                if (!parent) return;
                
                parent.replaceChild(replacement, el);

                if (id) replacement.setProperty('id', id);

                return replacement;
        },

        replaces: function(el){
		el = document.id(el, true);
		el.parentNode.replaceChild(this, el);
		return this;
	},

        appendToBody: function(code, type) {
                if (code != '') {
                        var appendNode = $(document.body), el = appendNode.ownerDocument.createElement(type == 'js' ?  'script' : 'style');
                        el.type = type == 'js' ?  "text/javascript" : "text/css";
                        appendNode.appendChild(el);
                        el.text = code;
                }
        },

        downloadScripts: function(srcs, type, isSync, evt) {
                if (typeof srcs == 'string') srcs = [srcs];

                var counter = 0;

                if (!isSync) isSync = false;

                for (var i = 0; i < srcs.length; i++) {
                        if (this.isScriptDownloaded(srcs[i], type)) continue;

                        new Ajax(srcs[i], {
                                method: 'get',
                                async: !isSync,
                                onComplete: function(code) {
                                        this.appendToBody(code, type);

                                        counter++;

                                        if (srcs.length == counter) {
                                                if (evt) this.fireEvent(evt);
                                        }
                                }.bind(this)
                        }).request();
                }
        },

        isScriptDownloaded: function(src, type) {
                var scripts = $(document).head.getChildren(type == 'css' ? 'link' : 'script');

                for (var i = 0; i < scripts.length; i++) {
                        if (scripts[i].getProperty(type == 'css' ? 'href' : 'src') == src) {
                                return true;
                        }
                }

                return false;
        }
});

if (MooTools.version.substr(2, 1) == 1) {
        jkefel.implement(new Options);
}

