app/assets/javascripts/puffer/right-tabs-src.js
/**
* Tabs widget for RightJS (http://rightjs.org/ui/tabs)
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
var Tabs = RightJS.Tabs = (function(document, parseInt, RightJS) {
/**
* This module defines the basic widgets constructor
* it creates an abstract proxy with the common functionality
* which then we reuse and override in the actual widgets
*
* Copyright (C) 2010-2011 Nikolay Nemshilov
*/
/**
* The tabs init-script
*
* Copyright (C) 2010-2011 Nikolay Nemshilov
*/
var R = RightJS,
$ = RightJS.$,
$$ = RightJS.$$,
$w = RightJS.$w,
$E = RightJS.$E,
Fx = RightJS.Fx,
Object = RightJS.Object,
Browser = RightJS.Browser,
isArray = RightJS.isArray,
isNumber = RightJS.isNumber,
Class = RightJS.Class,
Element = RightJS.Element,
Cookie = RightJS.Cookie;
/**
* The widget units constructor
*
* @param String tag-name or Object methods
* @param Object methods
* @return Widget wrapper
*/
function Widget(tag_name, methods) {
if (!methods) {
methods = tag_name;
tag_name = 'DIV';
}
/**
* An Abstract Widget Unit
*
* Copyright (C) 2010 Nikolay Nemshilov
*/
var AbstractWidget = new RightJS.Class(RightJS.Element.Wrappers[tag_name] || RightJS.Element, {
/**
* The common constructor
*
* @param Object options
* @param String optional tag name
* @return void
*/
initialize: function(key, options) {
this.key = key;
var args = [{'class': 'rui-' + key}];
// those two have different constructors
if (!(this instanceof RightJS.Input || this instanceof RightJS.Form)) {
args.unshift(tag_name);
}
this.$super.apply(this, args);
if (RightJS.isString(options)) {
options = RightJS.$(options);
}
// if the options is another element then
// try to dynamically rewrap it with our widget
if (options instanceof RightJS.Element) {
this._ = options._;
if ('$listeners' in options) {
options.$listeners = options.$listeners;
}
options = {};
}
this.setOptions(options, this);
return (RightJS.Wrapper.Cache[RightJS.$uid(this._)] = this);
},
// protected
/**
* Catches the options
*
* @param Object user-options
* @param Element element with contextual options
* @return void
*/
setOptions: function(options, element) {
element = element || this;
RightJS.Options.setOptions.call(this,
RightJS.Object.merge(options, eval("("+(
element.get('data-'+ this.key) || '{}'
)+")"))
);
return this;
}
});
/**
* Creating the actual widget class
*
*/
var Klass = new RightJS.Class(AbstractWidget, methods);
// creating the widget related shortcuts
RightJS.Observer.createShortcuts(Klass.prototype, Klass.EVENTS || []);
return Klass;
}
/**
* A shared module to create textual spinners
*
* Copyright (C) 2010-2011 Nikolay Nemshilov
*/
var Spinner = new RightJS.Class(RightJS.Element, {
/**
* Constructor
*
* @param Number optional spinner size (4 by default)
* @return void
*/
initialize: function(size) {
this.$super('div', {'class': 'rui-spinner'});
this.dots = [];
for (var i=0; i < (size || 4); i++) {
this.dots.push(new RightJS.Element('div'));
}
this.dots[0].addClass('glowing');
this.insert(this.dots);
RightJS(this.shift).bind(this).periodical(300);
},
/**
* Shifts the spinner elements
*
* @return void
*/
shift: function() {
if (this.visible()) {
var dot = this.dots.pop();
this.dots.unshift(dot);
this.insert(dot, 'top');
}
}
});
/**
* The basic tabs handling engine
*
* Copyright (C) 2009-2011 Nikolay Nemshilov
*/
var Tabs = new Widget('UL', {
extend: {
version: '2.2.0',
EVENTS: $w('select hide load disable enable add remove move'),
Options: {
idPrefix: '', // the tab-body elements id prefix
tabsElement: null, // the tabs list element reference, in case it's situated somewhere else
resizeFx: 'both', // 'slide', 'fade', 'both' or null for no fx
resizeDuration: 400, // the tab panels resize fx duration
scrollTabs: false, // use the tabs list scrolling
scrollDuration: 400, // the tabs scrolling fx duration
selected: null, // the index of the currently opened tab, by default will check url, cookies or set 0
disabled: null, // list of disabled tab indexes
closable: false, // set true if you want a close icon on your tabs
loop: false, // put a delay in ms to make it autostart the slideshow loop
loopPause: true, // make the loop get paused when user hovers the tabs with mouse
url: false, // a common remote tabs url template, should have the %{id} placeholder
cache: false, // marker if the remote tabs should be cached
Xhr: null, // the xhr addtional options
Cookie: null // set the cookie options if you'd like to keep the last selected tab index in cookies
},
// scans and automatically intializes the tabs
rescan: function(scope) {
$(scope || document).find('.rui-tabs,*[data-tabs]').each(function(element) {
element = element instanceof Tabs ? element : new Tabs(element);
});
}
},
/**
* The basic constructor
*
* @param element or id
* @param Object options
*/
initialize: function(element, options) {
this
.$super('tabs', element)
.addClass('rui-tabs');
this.isHarmonica = this._.tagName === 'DL';
this.isCarousel = this.hasClass('rui-tabs-carousel');
this.isSimple = !this.isHarmonica && !this.isCarousel;
this
.findTabs()
.initScrolls()
.findCurrent()
.setStyle('visibility:visible');
if (this.options.disabled) {
this.disable(this.options.disabled);
}
if (this.options.loop) {
this.startLoop();
}
},
/**
* Shows the given tab
*
* @param integer tab index or a Tabs.Tab instance
* @return Tabs this
*/
select: function(tab) {
return this.callTab(tab, 'select');
},
/**
* Disables the given tab
*
* @param integer tab index or a Tabs.Tab instance or a list of them
* @return Tabs this
*/
disable: function(tab) {
return this.callTab(tab, 'disable');
},
/**
* Enables the given tab
*
* @param integer tab index or a Tabs.Tab instance or a list of them
* @return Tabs this
*/
enable: function(tab) {
return this.callTab(tab, 'enable');
},
/**
* Returns the reference to the currently opened tab
*
* @return Tab tab or undefined
*/
current: function() {
return this.tabs.first('current');
},
/**
* Returns the list of enabled tabs
*
* @return Array of enabled tabs
*/
enabled: function() {
return this.tabs.filter('enabled');
},
// protected
// calls the tab (or tabs) method
callTab: function(tabs, method) {
R(isArray(tabs) ? tabs : [tabs]).each(function(tab) {
if (isNumber(tab)) { tab = this.tabs[tab]; }
if (tab && tab instanceof Tab) {
tab[method]();
}
}, this);
return this;
},
// finds and interconnects the tabs
findTabs: function() {
this.tabsList = this.isHarmonica ? this :
$(this.options.tabsElement) || this.first('.rui-tabs-list') ||
(this.first('UL') || $E('UL').insertTo(this)).addClass('rui-tabs-list');
this.tabs = R([]);
this.tabsList.children(this.isHarmonica ? 'dt' : null).map(function(node) {
this.tabs.push(new Tab(node, this));
}, this);
// removing the whitespaces so the didn't screw with the margins
for (var i=0, list = this.tabsList.get('childNodes'); i < list.length; i++) {
if (list[i].nodeType == 3) { this.tabsList._.removeChild(list[i]); }
}
return this;
}
});
/**
* A single tab handling object
*
* Copyright (C) 2009-2011 Nikolay Nemshilov
*/
var Tab = Tabs.Tab = new Class(Element, {
extend: {
autoId: 0
},
/**
* Constructor
*
* @param Element the tab's element
* @param Tabs the main element
* @return void
*/
initialize: function(element, main) {
this.$super(element._);
this.addClass('rui-tabs-tab');
this.main = main;
this.link = this.first('a');
this.id = this.link.get('href').split('#')[1] || Tab.autoId++;
this.panel = new Panel(this.findPanel(), this);
if (this.current()) {
this.select();
}
// adding the 'close' icon onto the tab if needed
if (main.options.closable) {
this.link.insert($E('div', {
'class': 'rui-tabs-tab-close-icon', 'html': '×'
}).onClick(R(this.remove).bind(this)));
}
this.onClick(this._clicked);
},
select: function() {
if (this.enabled()) {
var prev_tab = this.main.current();
if (prev_tab) {
prev_tab.removeClass('rui-tabs-current').fire('hide');
}
this.addClass('rui-tabs-current');
this.main.scrollToTab(this);
this.panel.show();
}
return this.fire('select');
},
disable: function() {
return this.addClass('rui-tabs-disabled').fire('disable');
},
enable: function() {
return this.removeClass('rui-tabs-disabled').fire('enable');
},
disabled: function() {
return !this.enabled();
},
enabled: function() {
return !this.hasClass('rui-tabs-disabled');
},
current: function() {
return this.hasClass('rui-tabs-current');
},
remove: function(event) {
if (event) { event.stop(); }
// switching to the next available sibling
if (this.current()) {
var enabled = this.main.enabled();
var sibling = enabled[enabled.indexOf(this) + 1] || enabled[enabled.indexOf(this)-1];
if (sibling) {
sibling.select();
}
}
// removing the tab out of the list
this.main.tabs.splice(this.main.tabs.indexOf(this), 1);
this.panel.remove();
return this.fire('remove').$super();
},
// protected
// handles the clicks on the tabs
_clicked: function(event) {
event.stop();
return this.select();
},
// searches for a panel for the tab
findPanel: function() {
var main = this.main, panel_id = main.options.idPrefix + this.id, panel;
if (main.isHarmonica) {
var next = this.next();
panel = (next && next._.tagName === 'DD') ? next : $E('DD').insertTo(this, 'after');
} else {
panel = $(panel_id) || $E(main._.tagName === 'UL' ? 'LI' : 'DIV').insertTo(main);
}
return panel.set('id', panel_id);
},
// returns the tab width, used for the scrolling calculations
width: function() {
var next = this.next();
if (next) {
return next.position().x - this.position().x;
} else {
return this.size().x + 1;
}
}
});
/**
* The tab panels behavior logic
*
* Copyright (C) 2009-2011 Nikolay Nemshilov
*/
var Panel = Tabs.Panel = new Class(Element, {
/**
* Basic constructor
*
* @param Element panel-element
* @param Tab the tab object
* @return void
*/
initialize: function(element, tab) {
this.$super(element._);
this.addClass('rui-tabs-panel');
this.tab = tab;
this.id = this.get('id');
},
// shows the panel
show: function() {
return this.resizing(function() {
this.tab.main.find('.rui-tabs-panel').each(function(panel) {
panel[panel === this ? 'addClass' : 'removeClass']('rui-tabs-current');
}, this);
});
},
// updates the panel content
update: function(content) {
// don't use resize if it's some other hidden tab was loaded asynch
if (this.tab.current()) {
this.resizing(function() {
Element.prototype.update.call(this, content||'');
});
} else {
this.$super(content||'');
}
return this;
},
// locks the panel with a spinner locker
lock: function() {
this.insert(this.locker(), 'top');
},
// protected
resizing: function(callback) {
var controller = this.tab.main;
if (controller.__working) { return this.resizing.bind(this, callback).delay(100); }
var options = controller.options;
var prev_panel = controller.first('.rui-tabs-panel.rui-tabs-current');
var this_panel = this;
var swapping = prev_panel !== this_panel;
var loading = this.first('div.rui-tabs-panel-locker');
// sometimes it looses the parent on remote tabs
if (this_panel.parent().hasClass('rui-tabs-resizer')) {
this_panel.insertTo(prev_panel.parent());
}
if (options.resizeFx && RightJS.Fx && prev_panel && (swapping || loading)) {
controller.__working = true;
var unlock = function() { controller.__working = false; };
// calculating the visual effects durations
var fx_name = (options.resizeFx === 'both' && loading) ? 'slide' : options.resizeFx;
var duration = options.resizeDuration; duration = Fx.Durations[duration] || duration;
var resize_duration = fx_name === 'fade' ? 0 : fx_name === 'slide' ? duration : duration / 2;
var fade_duration = duration - resize_duration;
if (fx_name !== 'slide') {
this_panel.setStyle({opacity: 0});
}
// saving the previous sizes
var prev_panel_height = (controller.isHarmonica && swapping) ? 0 : prev_panel.size().y;
// applying the changes
callback.call(this);
// getting the new size
var new_panel_height = this_panel.size().y;
var fx_wrapper = null;
var hide_wrapper = null;
var prev_back = null;
if (fx_name !== 'fade' && prev_panel_height !== new_panel_height) {
// preserving the whole element size so it didn't jump when we are tossing the tabs around
controller._.style.height = controller.size().y + 'px';
// wrapping the element with an overflowed element to visualize the resize
fx_wrapper = $E('div', {
'class': 'rui-tabs-resizer',
'style': 'height: '+ prev_panel_height + 'px'
});
// in case of harmonica nicely hidding the previous panel
if (controller.isHarmonica && swapping) {
prev_panel.addClass('rui-tabs-current');
hide_wrapper = $E('div', {'class': 'rui-tabs-resizer'});
hide_wrapper._.style.height = prev_panel.size().y + 'px';
prev_back = function() {
hide_wrapper.replace(prev_panel.removeClass('rui-tabs-current'));
};
prev_panel.wrap(hide_wrapper);
fx_wrapper._.style.height = '0px';
}
this_panel.wrap(fx_wrapper);
// getting back the auto-size so we could resize it
controller._.style.height = 'auto';
} else {
// removing the resize duration out of the equasion
rezise_duration = 0;
duration = fade_duration;
}
var counter = 0;
var set_back = function() {
if (fx_wrapper) {
if (fx_name == 'both' && !counter) {
return counter ++;
}
fx_wrapper.replace(this_panel);
}
unlock();
};
if (hide_wrapper) {
hide_wrapper.morph({height: '0px'},
{duration: resize_duration, onFinish: prev_back});
}
if (fx_wrapper) {
fx_wrapper.morph({height: new_panel_height + 'px'},
{duration: resize_duration, onFinish: set_back});
}
if (fx_name !== 'slide') {
this_panel.morph.bind(this_panel, {opacity: 1},
{duration: fade_duration, onFinish: set_back}
).delay(resize_duration);
}
if (!fx_wrapper && fx_name === 'slide') {
set_back();
}
} else {
callback.call(this);
}
return this;
},
// builds the locker element
locker: function() {
return this._locker || (this._locker =
$E('div', {'class': 'rui-tabs-panel-locker'}).insert(new Spinner(5))
);
}
});
/**
* Contains the tabs scrolling functionality
*
* NOTE: different types of tabs have different scrolling behavior
* simple tabs just scroll the tabs line without actually picking
* any tab. But the carousel tabs scrolls to the next/previous
* tabs on the list.
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
Tabs.include({
/**
* Shows the next tab
*
* @return Tabs this
*/
next: function() {
return this.pickTab(+1);
},
/**
* Shows the preveious tab
*
* @return Tabs this
*/
prev: function() {
return this.pickTab(-1);
},
/**
* Scrolls the tabs to the left
*
* @return Tabs this
*/
scrollLeft: function() {
if (!this.prevButton.hasClass('rui-tabs-scroller-disabled')) {
this[this.isCarousel ? 'prev' : 'justScroll'](+0.6);
}
return this;
},
/**
* Scrolls the tabs to the right
*
* @return Tabs this
*/
scrollRight: function() {
if (!this.nextButton.hasClass('rui-tabs-scroller-disabled')) {
this[this.isCarousel ? 'next' : 'justScroll'](-0.6);
}
return this;
},
// protected
// overloading the init script to add the scrollbar support
initScrolls: function() {
if ((this.scrollable = (this.options.scrollTabs || this.isCarousel))) {
this.buildScroller();
}
return this;
},
// builds the tabs scroller block
buildScroller: function() {
if (!(
(this.prevButton = this.first('.rui-tabs-scroller-prev')) &&
(this.nextButton = this.first('.rui-tabs-scroller-next'))
)) {
this.prevButton = $E('div', {'class': 'rui-tabs-scroller-prev', 'html': '«'});
this.nextButton = $E('div', {'class': 'rui-tabs-scroller-next', 'html': '»'});
// using a dummy element to insert the scroller in place of the tabs list
$E('div').insertTo(this.tabsList, 'before')
.replace(
$E('div', {'class': 'rui-tabs-scroller'}).insert([
this.prevButton, this.nextButton, this.scroller = $E('div', {
'class': 'rui-tabs-scroller-body'
}).insert(this.tabsList)
])
).remove();
}
this.prevButton.onClick(R(this.scrollLeft).bind(this));
this.nextButton.onClick(R(this.scrollRight).bind(this));
},
// picks the next/prev non-disabled available tab
pickTab: function(pos) {
var current = this.current();
if (current && current.enabled()) {
var enabled_tabs = this.enabled();
var tab = enabled_tabs[enabled_tabs.indexOf(current) + pos];
if (tab) { tab.select(); }
}
},
// scrolls the tabs line to make the tab visible
scrollToTab: function(tab) {
if (this.scroller) {
// calculating the previous tabs widths
var tabs_width = 0;
for (var i=0; i < this.tabs.length; i++) {
tabs_width += this.tabs[i].width();
if (this.tabs[i] === tab) { break; }
}
// calculating the scroll (the carousel tabs should be centralized)
var available_width = this.scroller.size().x;
var scroll = (this.isCarousel ? (available_width/2 + tab.width()/2) : available_width) - tabs_width;
// check if the tab doesn't need to be scrolled
if (!this.isCarousel) {
var current_scroll = parseInt(this.tabsList.getStyle('left') || 0, 10);
if (scroll >= current_scroll && scroll < (current_scroll + available_width - tab.width())) {
scroll = current_scroll;
} else if (current_scroll > -tabs_width && current_scroll <= (tab.width() - tabs_width)) {
scroll = tab.width() - tabs_width;
}
}
this.scrollTo(scroll);
}
},
// just scrolls the scrollable area onto the given number of scrollable area widths
justScroll: function(size) {
if (!this.scroller) { return this; }
var current_scroll = parseInt(this.tabsList.getStyle('left') || 0, 10);
var available_width = this.scroller.size().x;
this.scrollTo(current_scroll + available_width * size);
},
// scrolls the tabs list to the position
scrollTo: function(scroll) {
// checking the constraints
var available_width = this.scroller.size().x;
var overall_width = this.tabs.map('width').sum();
if (scroll < (available_width - overall_width)) {
scroll = available_width - overall_width;
}
if (scroll > 0) { scroll = 0; }
// applying the scroll
this.tabsList.morph({left: scroll+'px'}, {duration: this.options.scrollDuration});
this.checkScrollButtons(overall_width, available_width, scroll);
},
// checks the scroll buttons
checkScrollButtons: function(overall_width, available_width, scroll) {
var has_prev = false, has_next = false;
if (this.isCarousel) {
var enabled = this.enabled();
var current = enabled.first('current');
if (current) {
var index = enabled.indexOf(current);
has_prev = index > 0;
has_next = index < enabled.length - 1;
}
} else {
has_prev = scroll !== 0;
has_next = scroll > (available_width - overall_width);
}
this.prevButton[has_prev ? 'removeClass' : 'addClass']('rui-tabs-scroller-disabled');
this.nextButton[has_next ? 'removeClass' : 'addClass']('rui-tabs-scroller-disabled');
}
});
/**
* This module handles the current tab state saving/restoring processes
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
function get_cookie_indexes() {
return R(RightJS.Cookie ? (Cookie.get('right-tabs-indexes') || '').split(',') : []);
}
function save_tab_in_cookies(options, tabs, event) {
if (RightJS.Cookie) {
var indexes = get_cookie_indexes();
indexes = indexes.without.apply(indexes, tabs.map('id'));
indexes.push(event.target.id);
Cookie.set('right-tabs-indexes', indexes.uniq().join(','), options);
}
}
Tabs.include({
// protected
// searches and activates the current tab
findCurrent: function() {
var enabled = this.enabled(), current = (
this.tabs[this.options.selected] ||
this.tabs[this.urlIndex()] ||
this.tabs[this.cookieIndex()] ||
enabled.first('current') ||
enabled[0]
);
if (current) {
current.select();
}
// initializing the cookies storage if set
if (this.options.Cookie) {
this.onSelect(R(save_tab_in_cookies).curry(this.options.Cookie, this.tabs));
}
return this;
},
// tries to find the current tab index in the url hash
urlIndex: function() {
var index = -1, id = document.location.href.split('#')[1];
if (id) {
for (var i=0; i < this.tabs.length; i++) {
if (this.tabs[i].id == id) {
index = i;
break;
}
}
}
return index;
},
// tries to find the current tab index in the cookies storage
cookieIndex: function() {
var index = -1;
if (this.options.Cookie) {
var indexes = get_cookie_indexes();
for (var i=0; i < this.tabs.length; i++) {
if (indexes.include(this.tabs[i].id)) {
index = i;
break;
}
}
}
return index;
}
});
/**
* This module handles the tabs cration and removing processes
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
Tabs.include({
/**
* Creates a new tab
*
* USAGE:
* With the #add method you have to specify the tab title
* optional content (possibly empty or null) and some options
* The options might have the following keys
*
* * id - the tab/panel id (will use the idPrefix option for the panels)
* * url - a remote tab content address
* * position - an integer position of the tab in the stack
*
* @param String title
* @param mixed content
* @param Object options
* @return Tabs this
*/
add: function(title, content, options) {
options = options || {};
// creating the new tab element
var element = $E(this.isHarmonica ? 'dt' : 'li').insert(
$E('a', {html: title, href: options.url || '#'+(options.id||'')}
)).insertTo(this.tabsList);
// creating the actual tab instance
var tab = new Tab(element, this);
tab.panel.update(content||'');
this.tabs.push(tab);
tab.fire('add');
// moving the tab in place if asked
if ('position' in options) {
this.move(tab, options.position);
}
return this;
},
/**
* Moves the given tab to the given position
*
* NOTE if the position is not within the tabs range then it will do nothing
*
* @param mixed tab index or a tab instance
* @param Integer position
* @return Tabs this
*/
move: function(tab, position) {
tab = this.tabs[tab] || tab;
if (this.tabs[position] && this.tabs[position] !== tab) {
// moving the tab element
this.tabs[position].insert(tab, (position === this.tabs.length-1) ? 'after' : 'before');
// inserting the panel after the tab if it's a harmonica
if (this.isHarmonica) {
tab.insert(tab.panel, 'after');
}
// moving the tab in the registry
this.tabs.splice(this.tabs.indexOf(tab), 1);
this.tabs.splice(position, 0, tab);
tab.fire('move', {index: position});
}
return this;
},
/**
* Removes the given tab
*
* @param integer tab index or a Tabs.Tab instance or a list of them
* @return Tabs this
*/
remove: function(tab) {
return this.callTab(tab, 'remove');
}
});
/**
* This module contains the remote tabs loading logic
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
var old_select = Tab.prototype.select;
Tab.include({
// wrapping the original mehtod, to catch the remote requests
select: function() {
if (this.dogPiling(arguments)) { return this; }
var result = old_select.apply(this, arguments);
var url = R(this.link.get('href'));
var options = this.main.options;
// building the url
if (url.includes('#')) {
url = options.url ? options.url.replace('%{id}', url.split('#')[1]) : null;
}
// if there is an actual url and no ongoing request or a cache, starting the request
if (url && !this.request && !(options.cache || this.cache)) {
this.panel.lock();
try { // basically that's for the development tests, so the IE browsers didn't get screwed on the test page
this.request = new RightJS.Xhr(url, Object.merge({method: 'get'}, options.Xhr))
.onComplete(R(function(response) {
if (this.main.__working) {
return arguments.callee.bind(this, response).delay(100);
}
this.panel.update(response.text);
this.request = null; // removing the request marker so it could be rerun
if (options.cache) {
this.cache = true;
}
this.fire('load');
}).bind(this)
).send();
} catch(e) { if (!Browser.OLD) { throw(e); } }
}
return result;
},
// protected
dogPiling: function(args) {
if (this.main.__working) {
if (this.main.__timeout) {
this.main.__timeout.cancel();
}
this.main.__timeout = R(function(args) {
this.select.apply(this, args);
}).bind(this, args).delay(100);
return true;
}
return (this.main.__timeout = null);
}
});
/**
* This module handles the slide-show loop feature for the Tabs
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
Tabs.include({
/**
* Starts the slideshow loop
*
* @param Number optional delay in ms
* @return Tabs this
*/
startLoop: function(delay) {
if (!delay && !this.options.loop) { return this; }
// attaching the loop pause feature
if (this.options.loopPause) {
this._stopLoop = this._stopLoop || R(this.stopLoop).bind(this, true);
this._startLoop = this._startLoop || R(this.startLoop).bind(this, delay);
this.forgetHovers().on({
mouseover: this._stopLoop,
mouseout: this._startLoop
});
}
if (this.timer) { this.timer.stop(); }
this.timer = R(function() {
var enabled = this.enabled();
var current = this.current();
var next = enabled[enabled.indexOf(current)+1];
this.select(next || enabled.first());
}).bind(this).periodical(this.options.loop || delay);
return this;
},
/**
* Stops the slideshow loop
*
* @return Tabs this
*/
stopLoop: function(event, pause) {
if (this.timer) {
this.timer.stop();
this.timer = null;
}
if (!pause && this._startLoop) {
this.forgetHovers();
}
},
// private
forgetHovers: function() {
return this
.stopObserving('mouseover', this._stopLoop)
.stopObserving('mouseout', this._startLoop);
}
});
/**
* The document level hooks for the tabs-egnine
*
* Copyright (C) 2009-2010 Nikolay Nemshilov
*/
$(document).onReady(function() {
Tabs.rescan();
});
(function() {
var style = document.createElement('style'),
rules = document.createTextNode("div.rui-spinner,div.rui-spinner div{margin:0;padding:0;border:none;background:none;list-style:none;font-weight:normal;float:none;display:inline-block; *display:inline; *zoom:1;border-radius:.12em;-moz-border-radius:.12em;-webkit-border-radius:.12em}div.rui-spinner{text-align:center;white-space:nowrap;background:#EEE;border:1px solid #DDD;height:1.2em;padding:0 .2em}div.rui-spinner div{width:.4em;height:70%;background:#BBB;margin-left:1px}div.rui-spinner div:first-child{margin-left:0}div.rui-spinner div.glowing{background:#777}.rui-tabs,.rui-tabs-list,.rui-tabs-tab,.rui-tabs-panel,.rui-tabs-scroll-left,.rui-tabs-scroll-right,.rui-tabs-scroll-body,.rui-tabs-panel-locker,.rui-tabs-resizer{margin:0;padding:0;background:none;border:none;list-style:none;display:block;width:auto;height:auto}.rui-tabs{display:block;visibility:hidden;border-bottom:1px solid #CCC}.rui-tabs-resizer{overflow:hidden}.rui-tabs-list{display:block;position:relative;padding:0 .5em;border-bottom:1px solid #CCC;white-space:nowrap}.rui-tabs-list .rui-tabs-tab,.rui-tabs-tab *,.rui-tabs-tab *:hover{display:inline-block; *display:inline; *zoom:1;cursor:pointer;text-decoration:none;vertical-align:center}.rui-tabs-list .rui-tabs-tab{vertical-align:bottom;margin-right:.1em}.rui-tabs-tab a{outline:none;position:relative;border:1px solid #CCC;background:#DDD;color:#444;padding:.3em 1em;border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em;border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}.rui-tabs-tab a:hover{border-color:#CCC;background:#EEE}.rui-tabs-list .rui-tabs-current a,.rui-tabs-list .rui-tabs-current a:hover{font-weight:bold;color:#000;background:#FFF;border-bottom:1px solid #FFF;border-top-width:2px;padding-top:.34em;padding-bottom:.34em;top:1px}.rui-tabs-tab a img{border:none;opacity:.6;filter:alpha(opacity=60)}.rui-tabs-tab a:hover img,.rui-tabs-list .rui-tabs-current a img{opacity:1;filter:alpha(opacity=100)}.rui-tabs-disabled a,.rui-tabs-disabled a:hover{background:#EEE;border-color:#DDD;color:#AAA;cursor:default}.rui-tabs-disabled a img,.rui-tabs-disabled a:hover img{opacity:.5;filter:alpha(opacity=50)}.rui-tabs-tab-close-icon{display:inline-block; *display:inline; *zoom:1;margin-right:-0.5em;margin-left:0.5em;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}.rui-tabs-tab-close-icon:hover{opacity:1;filter:alpha(opacity=100);color:#B00;text-shadow:#888 .15em .15em .2em}.rui-tabs-panel{display:none;position:relative;min-height:4em;padding:.5em 0}.rui-tabs-current{display:block}.rui-tabs-scroller{position:relative;padding:0 1.4em}.rui-tabs-scroller-prev,.rui-tabs-scroller-next{width:1.1em;text-align:center;background:#EEE;color:#666;cursor:pointer;border:1px solid #CCC;border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em;position:absolute;bottom:0px;left:0px;padding:0.3em 0;user-select:none;-moz-user-select:none;-webkit-user-select:none}.rui-tabs-scroller-prev:hover,.rui-tabs-scroller-next:hover{color:#000;background:#DDD;border-color:#AAA}.rui-tabs-scroller-prev:active,.rui-tabs-scroller-next:active{background:#eee;border-color:#ccc}.rui-tabs-scroller-next{left:auto;right:0px}.rui-tabs-scroller-disabled,.rui-tabs-scroller-disabled:hover{cursor:default;background:#DDD;border-color:#DDD;color:#AAA}.rui-tabs-scroller-body{overflow:hidden;width:100%;position:relative}.rui-tabs-scroller .rui-tabs-list{padding-left:0;padding-right:0;width:9999em;z-index:10}.rui-tabs-panel-locker{position:absolute;top:0px;left:0px;opacity:0.5;filter:alpha(opacity=50);background:#CCC;width:100%;height:100%;text-align:center}.rui-tabs-panel-locker .rui-spinner{position:absolute;left:44%;top:44%;background:none;border:none;height:2em}.rui-tabs-panel-locker .rui-spinner div{background:#666;width:.65em;margin-left:.15em}.rui-tabs-panel-locker .rui-spinner div.glowing{background:#000}.rui-tabs-carousel .rui-tabs-list{border:none}.rui-tabs-carousel .rui-tabs-tab a,.rui-tabs-carousel .rui-tabs-scroller .rui-tabs-scroller-prev,.rui-tabs-carousel .rui-tabs-scroller .rui-tabs-scroller-next{height:6em;line-height:6em;padding:0;border-bottom:1px solid #ccc;border-radius:.25em;-moz-border-radius:.25em;-webkit-border-radius:.25em}.rui-tabs-carousel .rui-tabs-tab{margin-right:3px}.rui-tabs-carousel .rui-tabs-tab a img{border:1px solid #CCC;vertical-align:middle;margin:.4em;padding:0;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0}.rui-tabs-carousel .rui-tabs-list .rui-tabs-current a{border-width:1px;border-color:#AAA;padding:0;top:auto}.rui-tabs-carousel .rui-tabs-list .rui-tabs-current a img{border-color:#bbb}.rui-tabs-carousel .rui-tabs-panel{text-align:center}dl.rui-tabs{border:none}dt.rui-tabs-tab,dt.rui-tabs-tab a,dt.rui-tabs-tab a:hover{display:block;float:none}dt.rui-tabs-tab a,dt.rui-tabs-tab a:hover{padding:.2em 1em;border:1px solid #ccc;border-radius:.25em;-moz-border-radius:.3em;-webkit-border-radius:.3em}dl.rui-tabs dt.rui-tabs-current a{background:#EEE;border-bottom-left-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}dl.rui-tabs dd.rui-tabs-current+dt.rui-tabs-tab a{border-top-left-radius:0;border-top-right-radius:0;-moz-border-radius-topleft:0;-moz-border-radius-topright:0;-webkit-border-top-left-radius:0;-webkit-border-top-right-radius:0}");
style.type = 'text/css';
if(style.styleSheet) {
style.styleSheet.cssText = rules.nodeValue;
} else {
style.appendChild(rules);
}
document.getElementsByTagName('head')[0].appendChild(style);
})();
return Tabs;
})(document, parseInt, RightJS);