/** * tools.tabs 1.0.4 - Tabs done right. * * Copyright (c) 2009 Tero Piirainen * http://flowplayer.org/tools/tabs.html * * Dual licensed under MIT and GPL 2+ licenses * http://www.opensource.org/licenses * * Launch : November 2008 * Date: ${date} * Revision: ${revision} */ (function($) { // static constructs $.tools = $.tools || {}; $.tools.tabs = { version: '1.0.4', conf: { tabs: 'a', current: 'current', onBeforeClick: null, onClick: null, effect: 'default', initialIndex: 0, event: 'click', api:false, rotate: false }, addEffect: function(name, fn) { effects[name] = fn; } }; var effects = { // simple "toggle" effect 'default': function(i, done) { this.getPanes().hide().eq(i).show(); done.call(); }, /* configuration: - fadeOutSpeed (positive value does "crossfading") - fadeInSpeed */ fade: function(i, done) { var conf = this.getConf(), speed = conf.fadeOutSpeed, panes = this.getPanes(); if (speed) { panes.fadeOut(speed); } else { panes.hide(); } panes.eq(i).fadeIn(conf.fadeInSpeed, done); }, // for basic accordions slide: function(i, done) { this.getPanes().hide(); this.getPanes().eq(i).slideDown(400, done); }, // simple AJAX effect ajax: function(i, done) { this.getPanes().eq(0).load(this.getTabs().eq(i).attr("href"), done); } }; var w; // this is how you add effects $.tools.tabs.addEffect("horizontal", function(i, done) { // store original width of a pane into memory if (!w) { w = this.getPanes().eq(0).width(); } // set current pane's width to zero this.getCurrentPane().animate({width: 0}, function() { $(this).hide(); }); // grow opened pane to it's original width this.getPanes().eq(i).animate({width: w}, function() { $(this).show(); done.call(); }); }); function Tabs(tabs, panes, conf) { var self = this, $self = $(this), current; // bind all callbacks from configuration $.each(conf, function(name, fn) { if ($.isFunction(fn)) { $self.bind(name, fn); } }); // public methods $.extend(this, { click: function(i, e) { var pane = self.getCurrentPane(); var tab = tabs.eq(i); if (typeof i == 'string' && i.replace("#", "")) { tab = tabs.filter("[href*=" + i.replace("#", "") + "]"); i = Math.max(tabs.index(tab), 0); } if (conf.rotate) { var last = tabs.length -1; if (i < 0) { return self.click(last, e); } if (i > last) { return self.click(0, e); } } if (!tab.length) { if (current >= 0) { return self; } i = conf.initialIndex; tab = tabs.eq(i); } // current tab is being clicked if (i === current) { return self; } // possibility to cancel click action e = e || $.Event(); e.type = "onBeforeClick"; $self.trigger(e, [i]); if (e.isDefaultPrevented()) { return; } // call the effect effects[conf.effect].call(self, i, function() { // onClick callback e.type = "onClick"; $self.trigger(e, [i]); }); // onStart e.type = "onStart"; $self.trigger(e, [i]); if (e.isDefaultPrevented()) { return; } // default behaviour current = i; tabs.removeClass(conf.current); tab.addClass(conf.current); return self; }, getConf: function() { return conf; }, getTabs: function() { return tabs; }, getPanes: function() { return panes; }, getCurrentPane: function() { return panes.eq(current); }, getCurrentTab: function() { return tabs.eq(current); }, getIndex: function() { return current; }, next: function() { return self.click(current + 1); }, prev: function() { return self.click(current - 1); }, bind: function(name, fn) { $self.bind(name, fn); return self; }, onBeforeClick: function(fn) { return this.bind("onBeforeClick", fn); }, onClick: function(fn) { return this.bind("onClick", fn); }, unbind: function(name) { $self.unbind(name); return self; } }); // setup click actions for each tab tabs.each(function(i) { $(this).bind(conf.event, function(e) { self.click(i, e); return false; }); }); // if no pane is visible --> click on the first tab if (location.hash) { self.click(location.hash); } else { if (conf.initialIndex === 0 || conf.initialIndex > 0) { self.click(conf.initialIndex); } } // cross tab anchor link panes.find("a[href^=#]").click(function(e) { self.click($(this).attr("href"), e); }); } // jQuery plugin implementation $.fn.tabs = function(query, conf) { // return existing instance var el = this.eq(typeof conf == 'number' ? conf : 0).data("tabs"); if (el) { return el; } if ($.isFunction(conf)) { conf = {onBeforeClick: conf}; } // setup options var globals = $.extend({}, $.tools.tabs.conf), len = this.length; conf = $.extend(globals, conf); // install tabs for each items in jQuery this.each(function(i) { var root = $(this); // find tabs var els = root.find(conf.tabs); if (!els.length) { els = root.children(); } // find panes var panes = query.jquery ? query : root.children(query); if (!panes.length) { panes = len == 1 ? $(query) : root.parent().find(query); } el = new Tabs(els, panes, conf); root.data("tabs", el); }); return conf.api ? el: this; }; }) (jQuery);