/*!
 * jQuery UI AriaTabs (31.01.11)
 * http://github.com/fnagel/jQuery-Accessible-RIA
 *
 * Copyright (c) 2009 Felix Nagel for Namics (Deustchland) GmbH
 * Copyright (c) 2010-2011 Felix Nagel
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 *
 * Depends: jQuery UI
 *   		jQuery UI Tabs
 * Optional: jQuery Address Plugin
 */ 
/* 
 USAGE:::::::::::::
* Take a look in the html file or the (german) pdf file delivered with this example
* Simply add the js file uner the regular ui.tabs.js script tag
* Supports all options, methods and callbacks of the original widget
* sortable tabs are accessable but the sortable functionality as it is provided by the ui.sortable widget doesnt support ARIA 

 * Options	
jqAddress			You need to add the add the jQuery Address file, please see demo file!
	enable			enable browser history support
	title
		enable		enable title change
		split		set delimiter string
 
*/
(function($) {
    $.fn.extend($.ui.tabs.prototype, {

        // when widget is initiated
        _create: function() {
            var self = this, options = this.options;
            // add jQuery address default options
            if ($.address) {
                var jqAddressDefOpt = {
                    enable: true,
                    title: {
                        enable: true,
                        split: ' | '
                    }
                };
                if (!$.isEmptyObject(options.jqAddress)) $.extend(true, jqAddressDefOpt, options.jqAddress);
                else options.jqAddress = {};
                $.extend(true, options.jqAddress, jqAddressDefOpt);
            }

            // add jQuery Address stuff
            if ($.address && options.jqAddress.enable) var anchorId = "#" + $.address.value().replace("/", '');

            // fire original function
            self._tabify(true);

            // accessibility: needed to prevent blur() when enter key is pushed to enable forms mode in screenreader
            // needs to be fixed in tabs widget in line 333
            this.anchors.bind(options.event + '.tabs-accessibility', function() { this.focus(); });


            // ARIA
            // self.element.attr("role", "application");
            self.list.attr("role", "tablist");
            for (x = 0; x < self.anchors.length; x++) {
                // add jQuery Address stuff | get proper tab by anchor
                if ($.address && options.jqAddress.enable && anchorId != "#" && $(self.anchors[x]).attr("href") == anchorId) self.select(x);
                // init aria atrributes for each panel and anchor
                self._ariaInit(x);
            }

            // keyboard
            self.list.keydown(function(event) {
                var ret = true;
                if (event.keyCode == $.ui.keyCode.RIGHT) {
                    self.select(options.selected + 1);
                    ret = false;
                }

                if (event.keyCode == $.ui.keyCode.DOWN) {
                    self.select(options.selected + 1);
                    ret = false;
                }

                if (event.keyCode == $.ui.keyCode.UP) {
                    self.select(options.selected - 1);
                    ret = false;
                }

                if (event.keyCode == $.ui.keyCode.LEFT) {
                    self.select(options.selected - 1);
                    ret = false;
                }

                if (event.keyCode == $.ui.keyCode.END) {
                    self.select(self.anchors.length - 1);
                    ret = false;
                }

                if (event.keyCode == $.ui.keyCode.HOME) {
                    ret = false;
                    self.select(self.select(0));
                }

                //				switch (event.keyCode) {
                //					case $.ui.keyCode.RIGHT:
                //						self.select(options.selected+1);
                //						break;
                //					case $.ui.keyCode.DOWN:
                //						self.select(options.selected+1);
                //						// FIXME issues with NVDA: down key is needed for reading content
                //						// return false;
                //						ret = true;
                //						break;
                //					case $.ui.keyCode.UP:
                //						self.select(options.selected-1);
                //						break;
                //					case $.ui.keyCode.LEFT:
                //						self.select(options.selected-1);
                //						break;
                //					case $.ui.keyCode.END:
                //						self.select(self.anchors.length-1);
                //						break;
                //					case $.ui.keyCode.HOME: 
                //						self.select(0);
                //						break;				
                //				}
                return ret;
            });

            // add jQuery address stuff
            if ($.address && this.options.jqAddress.enable) {
                $.address.externalChange(function(event) {
                    // Select the proper tab
                    var anchorId = "#" + event.value.replace("/", '');
                    var x = 0;
                    while (x < self.anchors.length) {
                        if ($(self.anchors[x]).attr("href") == anchorId) {
                            self.select(x);
                            return;
                        }
                        x++;
                    }
                });
            }
            self.initiated = true;
        },

        _original_load: $.ui.tabs.prototype.load,
        // called whenever a tab is selected but if option collapsible is set | fired once at init for the chosen tab
        load: function(index) {

            // add jQuery Address stuff
            // workaround: only set values when user interacts aka not on init
            // ToDO use this.initiated to check for init
            if ($.address && this.options.jqAddress.enable) {
                if ($(this.anchors[0]).attr("aria-selected") !== undefined) {
                    if (this.options.forceFirst === 0 && index !== 0) {
                        // if there is no anchor to keep, prevent double entry
                        if ($.address.value() == "") $.address.history(false);
                        $.address.value($(this.anchors[0]).attr("href").replace(/^#/, ''));
                        $.address.history(true);
                        this.options.forceFirst = false;
                    }
                    if (this.options.jqAddress.title.enable) $.address.title($.address.title().split(this.options.jqAddress.title.split)[0] + this.options.jqAddress.title.split + $(this.anchors[index]).text());
                    $.address.value($(this.anchors[index]).attr("href").replace(/^#/, ''));
                } else {
                    this.options.forceFirst = index;
                }
            }

            // hide all unselected
            for (x = 0; x < this.anchors.length; x++) {
                // anchors
                this._ariaSet(x, false);
                // remove ARIA live settings
                if ($.data(this.anchors[x], 'href.tabs')) {
                    $(this.panels[x])
						.removeAttr("aria-live")
						.removeAttr("aria-busy");
                }
            }
            // is remote? set ARIA states 
            if ($.data(this.anchors[index], 'href.tabs')) {
                $(this.panels[index])
					.attr("aria-live", "polite")
					.attr("aria-busy", "true");
            }
            // fire original function
            this._original_load(index);

            // is remote? end ARIA busy
            if ($.data(this.anchors[index], 'href.tabs')) {
                $(this.panels[index])
					.attr("aria-busy", "false");
                // TODO jQuery Address: title is wrong when using Ajax Tab
            }
            // set state for the activated tab
            this._ariaSet(index, true);
        },

        // sets aria states for single tab and its panel
        _ariaSet: function(index, state) {
            var tabindex = (state) ? 0 : -1;
            var anchor = $(this.anchors[index]);
            // set ARIA state for loaded tab			
            anchor.attr("tabindex", tabindex)
				.attr("aria-selected", state);
            // set focus and remove focus CSS class
            if (state) {
                //if (!$.browser.msie && this.initiated) anchor.focus();
            } else {
                // needed to remove CSS class set by original widget
                anchor.closest("li").removeClass("ui-state-focus");
            }
            // set ARIA state for loaded tab
            $(this.panels[index])
				.attr("aria-hidden", !state)
				.attr("aria-expanded", state);
            // accessibility: needed to prevent blur() because IE loses focus when using keyboard control
            // this needs rto be fixed in jQuery UI Tabs in line 402
            if ($.browser.msie && this.initiated) this.options.timeout = window.setTimeout(function() { /* anchor.focus(); */}, 100);
            // update virtual Buffer
            if (state) this._updateVirtualBuffer();
        },

        // sets all attributes when plugin is called or if tab is added
        _ariaInit: function(index) {
            var self = this;
            // get widget generated ID of the panel
            var panelId = $(this.panels[index]).attr("id");
            // ARIA anchors and li's
            $(this.anchors[index])
				.attr("aria-controls", panelId)
				.attr("id", panelId + "-tab")
            // set role to the li not the a because of NVDA tabindex issue
			.parent().attr("role", "tab");
            // ARIA panels aka content wrapper
            $(this.panels[index])
				.attr("role", "tabpanel")
            // add tabpanel to the tabindex
				.attr("tabindex", 0)
				.attr("aria-labelledby", panelId + "-tab");
            // if collapsible, set event to toggle ARIA state
            if (this.options.collapsible) {
                $(this.anchors[index]).bind(this.options.event, function(event) {
                    // get class to negate it to set states correctly when panel is collapsed
                    self._ariaSet(index, !$(self.panels[index]).hasClass("ui-tabs-hide"));
                });
            }
        },

        _original_add: $.ui.tabs.prototype.add,
        // called when a tab is added
        add: function(url, label, index) {
            // fire original function
            this._original_add(url, label, index);
            // ARIA			
            this.element
				.attr("aria-live", "polite")
				.attr("aria-relevant", "additions");

            // if no index is defined tab should be added at the end of the tab list
            if (index) {
                this._ariaInit(index);
                this._ariaSet(index, false);
            } else {
                this._ariaInit(this.anchors.length - 1);
                this._ariaSet(this.anchors.length - 1, false);
            }
        },

        _original_remove: $.ui.tabs.prototype.remove,
        // called when a tab is removed
        remove: function(index) {
            // fire original function
            this._original_remove(index);
            // ARIA
            this.element
				.attr("aria-live", "polite")
				.attr("aria-relevant", "removals");
        },

        _original_destroy: $.ui.tabs.prototype.destroy,
        // removes all the setted attributes
        destroy: function() {
            var self = this, options = this.options;
            // remove ARIA attribute
            // wrapper element
            self.element
				.removeAttr("role")
				.removeAttr("aria-live")
				.removeAttr("aria-relevant");
            // ul element
            self.list.removeAttr("role");
            for (x = 0; x < self.anchors.length; x++) {
                // tabs
                $(self.anchors[x])
					.removeAttr("aria-selected")
					.removeAttr("aria-controls")
					.removeAttr("role")
					.removeAttr("id")
					.removeAttr("tabindex")
                // remove presentation role of the li element
				.parent().removeAttr("role");
                // tab panels
                $(self.panels[x])
					.removeAttr("aria-hidden")
					.removeAttr("aria-expanded")
					.removeAttr("aria-labelledby")
					.removeAttr("aria-live")
					.removeAttr("aria-busy")
					.removeAttr("aria-relevant")
					.removeAttr("role");
            }
            // remove virtual buffer form
            $("body>form #virtualBufferForm").parent().remove();
            // fire original function
            this._original_destroy();
        },

        // updates virtual buffer | for older screenreader
        _updateVirtualBuffer: function() {
            var form = $("body>form #virtualBufferForm");
            if (form.length) {
                if (form.val() == "1") form.val("0"); else form.val("1");
                if (form.hasClass("ui-accessibility-odd")) form.addClass("ui-accessibility-even").removeClass("ui-accessibility-odd");
                else form.addClass("ui-accessibility-odd").removeClass("ui-accessibility-even");
            } else {
                $("body").append('<form><input id="virtualBufferForm" type="hidden" value="1" /></form>');
            }
        }
    });
})(jQuery); 



