Skip to content

Instantly share code, notes, and snippets.

@onestepcreative
Created April 3, 2014 00:38
Show Gist options
  • Save onestepcreative/9946174 to your computer and use it in GitHub Desktop.
Save onestepcreative/9946174 to your computer and use it in GitHub Desktop.
/*
Author: Josh McDonald
Twitter: @onestepcreative
Website: developerstoolbox.net
Version: 2.0.1
This plugin allows for you to define your
tabs, and your tab content by simple selectors.
The script then assigns a matching index to each
tab and content selector, for easy cookie storage
later. So tab one and tabContent one will both
be assigned data-index="1", thus being able
to link up each tab to its corresponding
tab content.
This plugin requires jQuery Cookies to
take advantage of active tab saving.
Download Cookies: https://github.com/carhartl/jquery-cookie
*/
;(function($, window, document, undefined) {
'use strict';
CrystalCore.libs.tably = {
name : 'tably',
version : '2.0.1',
init : function(scope, settings) {
// kick off the plugin
new Tably(scope, settings);
// handle parameters passed
}
};
var Tably = function(scope, settings) {
var self = this;
var scope = $(scope);
// Default Tably Options
self.options = {
// Define tablys scope
scoped : scope,
// Tab settings
trigger : '.tab-trigger',
panel : '.tab-panel',
active : 'active',
effect : 'fade',
start : 3,
// Tably's Loader Settings
loader : 'stretch',
speed : 'medium',
// Cookie settings
save : true,
name : 'active_tab',
path : '/',
time : 7,
// Editable Ajaxify options for Tably
ajaxify : {
// default ajax options
data : null,
type : 'GET',
dataType : 'html',
// callbacks to handle data
before : null,
success : null,
failure : null,
// local storage options
cached : false,
key : settings.url,
duration : 3,
// data cleaning options
clean : {
links : true,
images : true
}
}
},
$.extend(true, self.options, settings);
self.init = function() {
self.loader.build();
self.build.init()
self.events.bind();
},
self.build = {
init: function() {
self.build.tabs();
self.build.panels();
self.build.setActive();
},
tabs: function() {
// Find all tabs within plugin scope
var tabs = self.get.tabs;
// Setup index for looping over tabs
var indx = 0;
// Loop through tabs and create markup
$(tabs).each(function(indx) {
// Cache single tab to variable
var tab = $(this);
// Add data-index to tab element
tab.attr('data-index', indx + 1);
});
// Determine + show the active
//self.markup.activate();
},
panels: function() {
// Find all panels within plugin scope
var panels = self.get.panels;
// Setup index for looping over panels
var indx = 0;
// Loop through panels and create markup
$(panels).each(function(indx) {
// Cache single tab to variable
var panel = $(this);
// Add data-index to panel element
panel.attr('data-index', indx + 1);
// If fade effect is on, add fade classes
if(self.options.effect === 'fade') {
panel.addClass('fade out');
}
});
},
setActive: function() {
// A var to store active index info
var activeIndex;
// If cookies are enabled and a cookie value already exists
if(self.options.save && self.cookies.get(self.options.name)) {
// Set value of active from the cookie value stored
activeIndex = self.cookies.get(self.options.name);
// If a start position was defined in the plugin settings
} else if(self.options.start && self.options.start > 0) {
// Set value of active based on 'start' setting
activeIndex = self.options.start;
// If no overrides exist, just show the first tab + panel by default
} else {
// If no condition match from above, show first tab + panel by default
activeIndex = 1;
}
// Set new active tab based on the activeIndex generated from above
var tab = scope.find(self.options.trigger + '[data-index="' + activeIndex + '"]');
// The matching panel of clicked tab
var panel = self.get.match(tab);
// Check to see if tab is to use Ajaxify
var ajaxify = self.AjaxifyTab(tab);
// Add active class to the activeIndex tab
tab.addClass(self.options.active);
// Add active class to tab's matching panel
panel.addClass(self.options.active);
// If this is an ajax tab
if(ajaxify && !self.get.match(tab).attr('data-ajaxified')) {
ajaxify.init();
}
},
},
self.get = {
tabs: (function() {
// Returns jQuery array of all tabs within scope
return (scope.find(self.options.trigger));
}()),
panels: (function() {
// Returns jQuery array of all panels within scope
return (scope.find(self.options.panel));
}()),
indx: function(elem) {
// Gets the data-indx value of passed in tab
return elem.attr('data-index');
},
match: function(tab) {
// Returns the corresponding tab panel, for tab that is passed in
return scope.find(self.options.panel + '[data-index="' + tab.attr('data-index') + '"]');
},
},
self.loader = {
init: function() {
// Exit function if loader setting is turned off
if(self.options.loader === null) { return; }
// Make sure loader setting is a 'string'
if(self.options.loader === 'string') {
self.loader.build();
}
},
build: function() {
// This function creates the html markup for the loading indicator
// The parent container for the loader
var markup = '<div class="tably-loader loader hide">';
var i = 1;
// Add six children to parent, so we have something to animate
for(i; i < 6; i++) { markup += '<div class="c' + i + '"></div>'; }
// Close the parent container
markup += '</div>';
// Turn markup into jQuery object
var loader = $(markup);
// Add effect & speed classes to loader
self.loader.effects(loader);
// Add loader to the top of the scope container
scope.prepend(loader);
},
effects: function(loader) {
// Assigns classes based on effect & speed
// Get animation effect from plugin settings
var effect = self.options.loader;
// Get animation speed from plugin settings
var speed = self.options.speed;
// Add loader effect class
switch(effect) {
case 'stretch':
loader.addClass('stretch');
break;
case 'worm' :
loader.addClass('worm');
break;
case 'bar' :
loader.addClass('bar');
break;
default :
loader.addClass('bar');
break;
}
// Add speed effect class
switch(speed) {
case 'slow':
loader.addClass('slow');
break;
case 'medium' :
loader.addClass('medium');
break;
case 'fast' :
loader.addClass('fast');
break;
default :
loader.addClass('medium');
break;
}
},
show: function() {
// Assign loader to a var for perf
var loader = scope.find('.tably-loader');
// Fadeout loader and add 'hide' class
loader.fadeIn(500).removeClass('hide');
},
hide: function() {
// Assign loader to a var for perf
var loader = scope.find('.tably-loader');
// Fadeout loader and add 'hide' class
loader.fadeOut(250).addClass('hide');
},
},
self.cookies = {
opts: { expires: self.options.time, path: self.options.path },
get: function(name) {
return $.cookie(name);
},
set: function(name, value) {
// If cookies are enabled
if(!self.options.save) { return; }
// If the cookie already exists with value
if(self.cookies.get(name)) {
// Update the value of the cookie
self.cookies.update(name, value);
} else {
// If no cookie exists, create a new one
self.cookies.create(name, value, self.cookies.opts);
}
},
create: function(name, value) {
// Create new cookie from clicked tab
$.cookie(name, value, self.cookies.opts);
},
update: function(name, value) {
// Update cookie value
$.cookie(name, value);
},
destroy: function(name, opts) {
$.removeCookie(name, opts);
}
},
self.events = {
bind: function() {
// Add a new event listener to all tabs
//self.get.tabs.on('click', self.events.open);
scope.on('click.ajaxify', '.tab-trigger', self.events.open);
},
open: function(e) {
// If clicked tab is already active, exit function
if($(this).hasClass(self.options.active)) { return; }
// Prevent default action
e.preventDefault();
// Get the tab that was active before click
var active = scope.find(self.options.trigger + '.' + self.options.active);
// The tab that was clicked
var tab = $(this);
// The matching panel of clicked tab
var panel = self.get.match(tab);
// Get the data indx for the clicked tab
var indx = self.get.indx(tab);
// Checks if tab has Ajaxify content
var ajaxify = self.AjaxifyTab(tab);
// Close the current active tab
self.events.close(active);
// Update cookie value if it exists, or create cookie if it doesn't
self.cookies.set(self.options.name, self.get.indx(tab));
// If an ajax tab, and tab doesn't have it's content
if(ajaxify && !self.get.match(tab).attr('data-ajaxified')) {
ajaxify.init();
}
// Add active class to tab just clicked
tab.addClass(self.options.active);
// If fade option is set in scope settings
if(self.options.effect === 'fade') {
// Remove the 'fade out' class
panel.removeClass('out')
}
// Add active class to tabs matching panel
panel.addClass(self.options.active);
// Trigger the open event for active tab
scope.trigger('open.tably.tab_' + indx);
//scope.trigger('open.tably.tab', ['tab_' + indx]);
},
close: function(tab) {
var indx = self.get.indx(tab);
var panel = self.get.match(tab);
// Remove active class the tab passed in
tab.removeClass(self.options.active);
// Remove active class passed tab's matching panel
panel.removeClass(self.options.active);
// If fade option is set in scope settings
if(self.options.effect === 'fade') {
// Add the 'fade out' class
panel.addClass('out');
}
// Trigger the close event for tab passed in
scope.trigger('close.tably.tab_' + indx);
//scope.trigger('close.tably.tab', ['tab_' + indx]);
},
listeners: function() {
// This function sets up event listeners on the
// window for various plugins and functionality,
// and fires callbacks when the events are detected
// If ajaxify returns an empty response, hide loader
$(window).on('ajaxify.responseError', self.callbacks.responseError);
// When ajax is done, and is on the page, remove loader
$(window).on('ajaxify.contentParsed', self.loader.hide);
}
},
self.callbacks = {
responseError: function(event, data) {
dev.error('ajaxify error', data);
// Build alert message from error data received
var alert = $('<span class="ajaxify-error">' + data.msg + '</span>');
// Find the active tab panel that made the request
var panel = scope.find(self.options.panel + '.' + self.options.active);
// Show alert in active panel
panel.append(alert);
// Add the ajax 'flag'
panel.attr('data-ajaxified', 'true');
},
},
// ===========================================================================
// ===== Function only called when a tab is using Ajaxify for dynamic content
// ===========================================================================
self.AjaxifyTab = function(tab) {
/*
This function, using the 'Reveal Module Pattern' defines the
functionality for tabs loading ajax content. If conditions are
met in the 'self.events.select' function, this function returns
a nicely packaged object consisting of all ajaxify settings that
are needed in order to work in the Tably implementation.
@param tab - (string) the tab that was clicked on
*/
var init = function() {
/*
In order for this init function to run, which
will make an ajax request using the Ajaxify Plugin, it
must first meet two conditions in the 'self.events.selector'
function. The 'self.events.selector' is an event callback
that is fired when a tab is clicked,
1. The tab clicked must have the url store in a 'data-ajaxify-url' attr
2. The tab's matching content panel cannot have a 'data-ajaxified' attr
*/
// Indicate that something is loading to the user
self.loader.show();
// Initialize Ajaxify Plugin to make ajax request
$(document).crystalcore('ajaxify', {
// Dynamically set from tab markup
scoped : this.scoped,
url : this.url,
elem : this.elem,
parsed : this.parsed,
// Explicitly set to work best with Tably
loader : null,
speed : null,
replace : false,
effect : self.options.effect,
// Default ajax options
data : self.options.ajaxify.data,
type : self.options.ajaxify.type,
dataType : self.options.ajaxify.dataType,
// callbacks to handle data
before : self.options.ajaxify.before,
success : self.options.ajaxify.success,
failure : self.options.ajaxify.failure,
// local storage options
cached : self.options.ajaxify.cached,
key : self.options.ajaxify.key,
duration : self.options.ajaxify.duration,
// data cleaning options
clean : {
links : self.options.ajaxify.links,
images : self.options.ajaxify.images
}
});
}
// If the tab is setup with a ajax url
if(tab.attr('data-ajaxify-url')) {
// Check if content has already been loaded to this tab
var parsedFlag = self.get.match(tab).attr('data-ajaxified') || false;
// Get the ajaxify url from data attr
var requestUrl = tab.attr('data-ajaxify-url');
// The class for ajax data to be injected in to
var appendClass = tab.attr('data-ajaxify-class');
// Return an object with required settings for ajax
return {
flag : parsedFlag,
scoped : scope,
url : requestUrl,
elem : appendClass,
init : init
};
}
return false;
}
// Kick off init func
self.init();
// Global event listeners
self.events.listeners();
};
})(jQuery, this, this.document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment