"use strict";
(function ($) {
$.fn.tabSlideOut = function (callerSettings) {
/**
* @param node Element to get the height of.
* @return string e.g. '123px'
*/
function heightAsString(node) {
return parseInt(node.outerHeight() + 1, 10) + 'px';
}
/**
* @param node Element to get the width of.
* @return string e.g. '123px'
*/
function widthAsString(node) {
return parseInt(node.outerWidth() + 1, 10) + 'px';
}
/*
* Get the width of the given border, in pixels.
*
* @param node element
* @param string edge
* @returns int
*/
function borderWidth(element, edge) {
return parseInt(element.css('border-' + edge + '-width'), 10);
}
/**
* Return the desired height of the panel to maintain both offsets.
*/
function calculatePanelSize() {
var available = $(window).height();
if (edge === 'top' || edge === 'bottom') {
available = $(window).width();
}
return available - parseInt(settings.otherOffset) - parseInt(settings.offset);
}
var panel = this;
/**
* True if the tab is open.
*
* @returns boolean
*/
function isOpen() {
return panel.hasClass('ui-slideouttab-open');
}
if (typeof callerSettings == 'string')
{
// param is a string, use command mode
switch (callerSettings)
{
case 'open':
this.trigger('open');
return this;
case 'close':
this.trigger('close');
return this;
case 'isOpen':
return isOpen();
case 'toggle':
this.trigger('toggle');
return this;
case 'bounce':
this.trigger('bounce');
return this;
default:
throw "Invalid tabSlideOut command";
}
} else
{
// param is an object, it's initialisation mode
var settings = $.extend({
tabLocation: 'left', // left, right, top or bottom
tabHandle: '.handle', // JQuery selector for the tab, can use any JQuery selector
action: 'click', // action which will open the panel, e.g. 'hover'
hoverTimeout: 5000, // ms to keep tab open after no longer hovered - only if action = 'hover'
offset: '200px', // panel dist from top or left (bottom or right if offsetReverse is true)
offsetReverse: false, // if true, panel is offset from right or bottom of window instead of left or top
otherOffset: null, // if set, panel size is also set to maintain this dist from bottom or right of view port (top or left if offsetReverse)
handleOffset: null, // e.g. '10px'. If null, detects panel border to align handle nicely on edge
handleOffsetReverse: false, // if true, handle is offset from right or bottom of panel instead of left or top
bounceDistance: '50px', // how far bounce event will move everything
bounceTimes: 4, // how many bounces when 'bounce' is called
bounceSpeed: 300, // time to animate bounces
tabImage: null, // optional image to show in the tab
tabImageHeight: null, // optional IE8 and lower only, else autodetected size
tabImageWidth: null, // optional IE8 and lower only, else autodetected size
onLoadSlideOut: false, // slide out after DOM load
clickScreenToClose: true, // close tab when somewhere outside the tab is clicked
clickScreenToCloseFilters: ['.ui-slideouttab-panel'], // if click target or parents match any of these, click won't close this tab
onOpen: function () {}, // handler called after opening
onClose: function () {}, // handler called after closing
onSlide: function () {}, // handler called after opening or closing
onBeforeOpen: function () {
return true;
}, // handler called before opening, return false to cancel
onBeforeClose: function () {
return true;
}, // handler called before closing, return false to cancel
onBeforeSlide: function () {
return true;
} // handler called before opening or closing, return false to cancel
}, callerSettings || {});
var edge = settings.tabLocation;
var handle = settings.tabHandle = $(settings.tabHandle, panel);
panel.addClass('ui-slideouttab-panel')
.addClass('ui-slideouttab-' + edge);
if (settings.offsetReverse)
panel.addClass('ui-slideouttab-panel-reverse');
handle.addClass('ui-slideouttab-handle'); // need this to find it later
if (settings.handleOffsetReverse)
handle.addClass('ui-slideouttab-handle-reverse');
settings.toggleButton = $(settings.toggleButton);
// apply an image to the tab if one is defined
if (settings.tabImage !== null) {
var imageHeight = 0;
var imageWidth = 0;
if (settings.tabImageHeight !== null && settings.tabImageWidth !== null) {
imageHeight = settings.tabImageHeight;
imageWidth = settings.tabImageWidth;
} else {
var img = new Image();
img.src = settings.tabImage;
imageHeight = img.naturalHeight;
imageWidth = img.naturalWidth;
}
handle.addClass('ui-slideouttab-handle-image');
handle.css({
'background': 'url(' + settings.tabImage + ') no-repeat',
'width': imageWidth,
'height': imageHeight
});
}
// determine whether panel and handle are positioned from top, bottom, left, or right
if (edge === 'top' || edge === 'bottom') {
settings.panelOffsetFrom =
settings.offsetReverse ? 'right' : 'left';
settings.handleOffsetFrom =
settings.handleOffsetReverse ? 'right' : 'left';
} else {
settings.panelOffsetFrom =
settings.offsetReverse ? 'bottom' : 'top';
settings.handleOffsetFrom =
settings.handleOffsetReverse ? 'bottom' : 'top';
}
/* autodetect the correct offset for the handle using appropriate panel border*/
if (settings.handleOffset === null) {
settings.handleOffset = '-' + borderWidth(panel, settings.handleOffsetFrom) + 'px';
}
if (edge === 'top' || edge === 'bottom') {
/* set left or right edges */
panel.css(settings.panelOffsetFrom, settings.offset);
handle.css(settings.handleOffsetFrom, settings.handleOffset);
// possibly drive the panel size
if (settings.otherOffset !== null) {
panel.css('width', calculatePanelSize() + 'px');
// install resize handler
$(window).resize(function () {
panel.css('width', calculatePanelSize() + 'px');
});
}
if (edge === 'top') {
handle.css({'bottom': '-' + heightAsString(handle)});
} else {
handle.css({'top': '-' + heightAsString(handle)});
}
} else {
/* set top or bottom edge */
panel.css(settings.panelOffsetFrom, settings.offset);
handle.css(settings.handleOffsetFrom, settings.handleOffset);
// possibly drive the panel size
if (settings.otherOffset !== null) {
panel.css('height', calculatePanelSize() + 'px');
// install resize handler
$(window).resize(function () {
panel.css('height', calculatePanelSize() + 'px');
});
}
if (edge === 'left') {
handle.css({'right': '0'});
} else {
handle.css({'left': '0'});
}
}
handle.on('click', function (event) {
event.preventDefault();
});
settings.toggleButton.on('click', function (event) {
event.preventDefault();
});
// now everything is set up, add the class which enables CSS tab animation
panel.addClass('ui-slideouttab-ready');
var close = function () {
if (settings.onBeforeSlide() && settings.onBeforeClose()) {
panel.removeClass('ui-slideouttab-open').trigger('slideouttabclose');
settings.onSlide();
settings.onClose();
}
};
var open = function () {
if (settings.onBeforeSlide() && settings.onBeforeOpen()) {
panel.addClass('ui-slideouttab-open').trigger('slideouttabopen');
settings.onSlide();
settings.onOpen();
}
};
var toggle = function () {
if (isOpen()) {
close();
} else {
open();
}
};
// animate the tab in and out when 'bounced'
var moveIn = [];
moveIn[edge] = '-=' + settings.bounceDistance;
var moveOut = [];
moveOut[edge] = '+=' + settings.bounceDistance;
var bounceIn = function () {
var temp = panel;
for (var i = 0; i < settings.bounceTimes; i++)
{
temp = temp.animate(moveIn, settings.bounceSpeed)
.animate(moveOut, settings.bounceSpeed);
}
panel.trigger('slideouttabbounce');
};
var bounceOut = function () {
var temp = panel;
for (var i = 0; i < settings.bounceTimes; i++)
{
temp = temp.animate(moveOut, settings.bounceSpeed)
.animate(moveIn, settings.bounceSpeed);
}
panel.trigger('slideouttabbounce');
};
// handle clicks in rest of document to close tabs if they're open
if (settings.clickScreenToClose) {
// install a click handler to close tab if anywhere outside the tab is clicked,
// that isn't filtered out by the configured filters
$(document).on('click', function (event) {
// first check the tab is open and the click isn't inside it
if (isOpen() && !panel[0].contains(event.target)) {
// something other than this panel was clicked
var clicked = $(event.target);
// check to see if any filters return true
for (var i = 0; i < settings.clickScreenToCloseFilters.length; i++) {
var filter = settings.clickScreenToCloseFilters[i];
if (typeof filter === 'string') {
// checked clicked element itself, and all parents
if (clicked.is(filter) || clicked.parents().is(filter)) {
return; // don't close the tab
}
} else if (typeof filter === 'function') {
// call custom filter
if (filter.call(panel, event))
return; // don't close the tab
}
}
// we haven't returned true from any filter, so close the tab
close();
}
});
}
;
//choose which type of action to bind
if (settings.action === 'click') {
handle.on('click', function (event) {
toggle();
});
} else if (settings.action === 'hover') {
var timer = null;
panel.hover(
function () {
if (!isOpen()) {
open();
}
timer = null; // eliminate the timer, ensure we don't close now
},
function () {
if (isOpen() && timer === null) {
timer = setTimeout(function () {
if (timer)
close();
timer = null;
}, settings.hoverTimeout);
}
});
handle.on('click', function (event) {
if (isOpen()) {
close();
}
});
}
if (settings.onLoadSlideOut) {
open();
setTimeout(open, 500);
}
// custom event handlers -------
panel.on('open', function (event) {
if (!isOpen()) {
open();
}
});
panel.on('close', function (event) {
if (isOpen()) {
close();
}
});
panel.on('toggle', function (event) {
toggle();
});
panel.on('bounce', function (event) {
if (isOpen()) {
bounceIn();
} else {
bounceOut();
}
});
}
return this;
};
})(jQuery);