/**
* Collapsible, jQuery Plugin
*
* This plugin enables the management of
* collapsibles on the page with cookie support.
*
* Copyright (c) 2010 John Snyder (snyderplace.com)
* @license http://www.snyderplace.com/collapsible/license.txt New BSD
* @version 1.1
*/
(function($) {
$.fn.collapsible = function(cmd, arg) {
//address command requests
if (typeof cmd == 'string') {
return $.fn.collapsible.dispatcher[cmd](this, arg);
}

//return the command dispatcher
return $.fn.collapsible.dispatcher['_create'](this, cmd);
};

//create the command dispatcher
$.fn.collapsible.dispatcher = {
//initialized with options
_create : function(obj, arg) {
createCollapsible(obj, arg);
},
//toggle the element's display
toggle: function(obj) {
toggle(obj, loadOpts(obj));
return obj;
},
//show the element
open: function(obj) {
open(obj, loadOpts(obj));
return obj;
},
//hide the element
close: function(obj) {
close(obj, loadOpts(obj));
return obj;
},
//check if the element is closed
collapsed: function(obj) {
return collapsed(obj, loadOpts(obj));
}
};

//create the initial collapsible
function createCollapsible(obj, options) {
//build main options before element iteration
var opts = $.extend({}, $.fn.collapsible.defaults, options);

//store any opened default values to set cookie later
var opened = new Array();

//iterate each matched object, bind, and open/close
obj.each(function() {
var $this = $(this);
saveOpts($this, opts);

//bind it to the event
if (opts.bind == 'mouseenter') {
$this.bind('mouseenter', function(e) {
e.preventDefault();
toggle($this, opts);
});
}

//bind it to the event
if (opts.bind == 'mouseover') {
$this.bind('mouseover',function(e) {
e.preventDefault();
toggle($this, opts);
});
}

//bind it to the event
if (opts.bind == 'click') {
$this.bind('click', function(e) {
e.preventDefault();
toggle($this, opts);
});
}

//bind it to the event
if (opts.bind == 'dblclick') {
$this.bind('dblclick', function(e) {
e.preventDefault();
toggle($this, opts);
});
}

//initialize the collapsibles
//get the id for this element
id = $this.attr('id');

//if not using cookies, open defauls
if (!useCookies(opts)) {
//is this collapsible in the default open array?
dOpenIndex = inDefaultOpen(id, opts);

//close it if not defaulted to open
if (dOpenIndex === false) {
$this.addClass(opts.cssClose);
$this.next().hide();
} else { //its a default open, open it
$this.addClass(opts.cssOpen);
$this.next().show();
opened.push(id);
}
} else { //can use cookies, use them now
//has a cookie been set, this overrides default open
if (issetCookie(opts)) {
cookieIndex = inCookie(id, opts);
if (cookieIndex === false) {
$this.addClass(opts.cssClose);
$this.next().hide();
} else {
$this.addClass(opts.cssOpen);
$this.next().show();
opened.push(id);
}
} else { //a cookie hasn't been set open defaults, add them to opened array
dOpenIndex = inDefaultOpen(id, opts);
if (dOpenIndex === false) {
$this.addClass(opts.cssClose);
$this.next().hide();
} else {
$this.addClass(opts.cssOpen);
$this.next().show();
opened.push(id);
}
}
}
});

//now that the loop is done, set the cookie
if (opened.length > 0 && useCookies(opts)) {
setCookie(opened.toString(), opts);
} else { //there are none open, set cookie
setCookie('', opts);
}

return obj;
};

//load opts from object
function loadOpts($this) {
return $this.data('collapsible-opts');
}

//save opts into object
function saveOpts($this, opts) {
return $this.data('collapsible-opts', opts);
}

//returns true if object is opened
function collapsed($this, opts) {
return $this.hasClass(opts.cssClose);
}

//hides a collapsible
function close($this, opts) {
//give the proper class to the linked element
$this.addClass(opts.cssClose).removeClass(opts.cssOpen);

//close the element
opts.animateOpen($this, opts);

//do cookies if plugin available
if (useCookies(opts)) {
// split the cookieOpen string by ","
id = $this.attr('id');
unsetCookieId(id, opts);
}
}

//opens a collapsible
function open($this, opts) {
//give the proper class to the linked element
$this.removeClass(opts.cssClose).addClass(opts.cssOpen);

//open the element
opts.animateClose($this, opts);

//do cookies if plugin available
if (useCookies(opts)) {
// split the cookieOpen string by ","
id = $this.attr('id');
appendCookie(id, opts);
}
}

//toggle a collapsible on an event
function toggle($this, opts) {
if (collapsed($this, opts)) {
//open a closed element
open($this, opts);
} else {
//close an open element
close($this, opts);
}

return false;
}

//use cookies?
function useCookies(opts) {
//return false if cookie plugin not present or if a cookie name is not provided
if (!$.cookie || opts.cookieName == '') {
return false;
}

//we can use cookies
return true;
}

//append a collapsible to the cookie
function appendCookie(value, opts) {
//check if cookie plugin available and cookiename is set
if (!useCookies(opts)) {
return false;
}

//does a cookie already exist
if (!issetCookie(opts)) {
//no lets set one
setCookie(value, opts);
return true;
}

//cookie already exists, is this collapsible already set?
if (inCookie(value, opts)) { //yes, quit here
return true;
}

//get the cookie
cookie = $.cookie(opts.cookieName);

//unescape it
cookie = unescape(cookie);

//turn it into an array
cookieArray = cookie.split(',');

//add it to list
cookieArray.push(value);

//save it
setCookie(cookieArray.toString(), opts);

return true;
}

//unset a collapsible from the cookie
function unsetCookieId(value, opts)
{
//check if cookie plugin available and cookiename is set
if (!useCookies(opts)) {
return false;
}

//if its not there we don't need to remove from it
if (!issetCookie(opts)) { //quit here, don't have a cookie
return true;
}

//we have a cookie, is this collapsible in it
cookieIndex = inCookie(value, opts);
if (cookieIndex === false) { //not in the cookie quit here
return true;
}

//still here get the cookie
cookie = $.cookie(opts.cookieName);

//unescape it
cookie = unescape(cookie);

//turn it into an array
cookieArray = cookie.split(',');

//lets pop it out of the array
cookieArray.splice(cookieIndex, 1);

setCookie(cookieArray.toString(), opts);
}

//set a cookie
function setCookie(value, opts)
{
//can use the cookie plugin
if (!useCookies(opts)) { //no, quit here
return false;
}

//cookie plugin is available, lets set the cookie
$.cookie(opts.cookieName, value, opts.cookieOptions);
}

//check if a collapsible is in the cookie
function inCookie(value, opts)
{
//can use the cookie plugin
if (!useCookies(opts)) {
return false;
}

//if its not there we don't need to remove from it
if (!issetCookie(opts)) { //quit here, don't have a cookie
return false;
}

//unescape it
cookie = unescape($.cookie(opts.cookieName));

//turn it into an array
cookieArray = cookie.split(',');

//get the index of the collapsible if in the cookie array
cookieIndex = $.inArray(value, cookieArray);

//is this value in the cookie arrray
if (cookieIndex == -1) { //no, quit here
return false;
}

return cookieIndex;
}

//check if a cookie is set
function issetCookie(opts)
{
//can we use the cookie plugin
if (!useCookies(opts)) { //no, quit here
return false;
}

//is the cookie set
if ($.cookie(opts.cookieName) == null) { //no, quit here
return false;
}

return true;
}

//check if a collapsible is in the list of collapsibles to be opened by default
function inDefaultOpen(id, opts)
{
//get the array of open collapsibles
defaultOpen = getDefaultOpen(opts);

//is it in the default open array
index = $.inArray(id, defaultOpen);
if (index == -1) { //nope, quit here
return false;
}

return index;
}

//get the default open collapsibles and return array
function getDefaultOpen(opts)
{
//initialize an empty array
defaultOpen = new Array();

//if there is a list, lets split it into an array
if (opts.defaultOpen != '') {
defaultOpen = opts.defaultOpen.split(',');
}

return defaultOpen;
}

// settings
$.fn.collapsible.defaults = {
cssClose: 'collapse-close', //class you want to assign to a closed collapsible header
cssOpen: 'collapse-open', //class you want to assign an opened collapsible header
cookieName: 'collapsible', //name of the cookie you want to set for this collapsible
cookieOptions: { //cookie options, see cookie plugin for details
path: '/',
expires: 7,
domain: '',
secure: ''
},
defaultOpen: '', //comma seperated list of header ids that you want opened by default
speed: 'slow', //speed of the slide effect
bind: 'click', //event to bind to, supports click, dblclick, mouseover and mouseenter
animateOpen: function (elem, opts) { //replace the standard slideUp with custom function
elem.next().slideUp(opts.speed);
},
animateClose: function (elem, opts) { //replace the standard slideDown with custom function
elem.next().slideDown(opts.speed);
}
};
})(jQuery);
