/*
* @Class: Blinds
*
* @Description: A class which can turn a set of elements into overlapping 'blinds' which open and close with animation
*
* @Usage:
*
* var s3 = new Blinds(collection contentElements,element containerElement, capElement, options hash);
*
*
*
* blinds - makes the div contents into blinds
* 89 - height of content by default (omitting this parameter makes the blinds
* automatically-sized by content height of each blind. to avoid this, make
* sure to explicitly specify the height in case you want statically-sized heights)
* auto - make height automatically detected
* click - make blinds click-activated instead of mouseover-activated
* closeable - make blinds able to close
* closed - make all the sections of the blinds closed initially
*
* options hash details:
*
* {
* 'current_id':'test123', the DOM id of the link or heading which is the "current" item
* 'current_item_class':'someclassname' the CSS classname to apply to an element which is "current"
* }
*
* @Methods: no public methods
*
*/
var Blinds = (!detectBrowser.modernBrowser())?function(){}:Class.create();
Blinds.prototype = (!detectBrowser.modernBrowser())?{}:{
initialize: function(elements,containerElement,capElement,options,id){ //extra id param
if(!elements){
return false;
}
if(!containerElement){
return false;
}
this.contentHeight = 80;
var classNames = $A(containerElement.classNames());
this.containerElement = containerElement;
// Some elements look bizarre during initial load until javascript gets to them.
// For those elements, we apply a class called "prejs" so that once the code is
// running it can remove that CSS class. This way, a "pre-rendered" state can be
// seen by the user while the Javascript is still loading, and unsettled layout
// can be avoided.
if(this.containerElement.className.indexOf("prejs_")>-1){
for(var i=0;i-1){
this.containerElement.removeClassName(classNames[i]);
}
}
}
this.contentHeight = parseInt(classNames.find(function(cname){ return parseInt(cname)>0; }));
this.eventType = (classNames.include("click")?"click":"mouseover");
this.autoHeight = classNames.include("auto") || (typeof(this.contentHeight)!='number');
this.isAnimating = false;
this.nextEffect = null;
this.initiallyClosed = classNames.include("closed");
this.closeable = this.initiallyClosed || classNames.include("closeable");
if(options!=null && typeof(options)=='object' && typeof(options['current_id'])=='string' && options['current_id'].strip()!=""){
var currentItemClass = "current_item";
if(typeof(options['current_item_class'])=='string'){
options['current_item_class'] = currentItemClass;
}
var currentItem = $(options['current_id'].strip());
if(currentItem){
currentItem.addClassName(currentItemClass);
// climb up to .content
// prev() yields header
// give header current style too
// if this is NOT the content node, we have to climb up to it
if(currentItem.hasClassName("content")!=true){
var p = currentItem;
var done = false;
while(!done && p!=document.body){
p = p.up();
if(p.hasClassName("content")){
p.addClassName(currentItemClass);
this.contentElementToOpen = p;
if(p.previous()){
p.previous().addClassName(currentItemClass);
}
done = true;
}
}
} else {
this.contentElementToOpen = currentItem;
}
}
}
if(this.contentElementToOpen){
this.previousElement = this.contentElementToOpen;
} else {
this.previousElement = elements[0];
}
this.elements = elements;
this.capElement = capElement;
this.currentlyHoveredElement = -1; // index of currently hovered element. Used to track if we have "committed" to hovering over a blind
this.measuredSizes = [];
this.transition = (classNames.include("click")?Effect.Transitions.EaseTo:Effect.Transitions.EaseFromTo);
this.makeSliders();
// id check
if (id != null) {
this.activateBlind (id);
}
},
/*eTouch - Parag*/
activateBlind: function (id) {
var element = null;
var tokens = id.split ('_');
this.elements.each (function(item){
if (item.getAttribute ("blindsindex") == $(tokens[0]).parentNode.parentNode.getAttribute ("blindsindex")) {
element = item;
}
})
if (element != null) {
var func = this.makeSlideFunc (this.elements, element,
$(id).parentNode.parentNode.getAttribute ("blindsindex"));
func ();
}
$(id).setStyle ('font-weight: bold');
},
/*eTouch -Parag*/
// iterate through the content blocks and make assign event listeners which trigger the sliding animation
makeSliders: function(elements){
this.elements.each(function(item,i){
item = $(item);
item.setAttribute("blindsindex",i);
if(this.contentElementToOpen){
if(this.contentElementToOpen==item){
// found the "current" item
item.setAttribute("blindstatus","open");
} else {
item.setAttribute("blindstatus","closed");
}
} else {
/* this is added,not to open the first item of left nav if nothing is opened. */
//item.setAttribute("blindstatus",((i==0)?((this.initiallyClosed==true)?"closed":"open"):("closed")));
if(this.eventType == 'click') {
item.setAttribute("blindstatus","closed");
} else {
item.setAttribute("blindstatus",((i==0)?((this.initiallyClosed==true)?"closed":"open"):("closed")));
}
/* End of changes for,not to open the first item of left nav if nothing is opened. */
}
// if a particular element is to be opened because it's "current", we make it open
// and make all others closed
if(this.contentElementToOpen){
if(this.contentElementToOpen==item && this.initiallyClosed==false){
item.previous().addClassName('active_header');
item.addClassName('active_content');
item.next().addClassName('active_footer');
} else {
item.previous().addClassName('inactive_header');
item.addClassName('inactive_content');
item.next().addClassName('inactive_footer');
}
} else {
// If no particular element is to be open, set the 0th element as open (unless initiallyClosed is true)
if(i==0 && this.initiallyClosed==false) {
/* this is added,not to open the first item of left nav if nothing is opened. */
if(this.eventType == 'click') {
item.previous().addClassName('inactive_header');
item.addClassName('inactive_content');
item.next().addClassName('inactive_footer');
} else {
item.previous().addClassName('active_header');
item.addClassName('active_content');
item.next().addClassName('active_footer');
}
/* End of changes for,not to open the first item of left nav if nothing is opened. */
} else {
item.previous().addClassName('inactive_header');
item.addClassName('inactive_content');
item.next().addClassName('inactive_footer');
}
}
// measure size of the content area
if(this.autoHeight) {
this.measuredSizes[i] = item.getHeight();
}
if(this.initiallyClosed==true){
item.setStyle({'height':'0px'});
} else {
if(this.contentElementToOpen){
if(this.contentElementToOpen==item){
item.setStyle({'height':(this.autoHeight?this.measuredSizes[i]:this.contentHeight)+'px'});
} else {
item.setStyle({'height':'0px'});
}
} else {
//item.setStyle({'height':((i==0)?(this.autoHeight?this.measuredSizes[i]:this.contentHeight):(0))+'px'});
/* this is added,not to open the first item of left nav if nothing is opened. */
if(this.eventType == 'click') {
item.setStyle({'height':'0px'});
} else {
item.setStyle({'height':((i==0)?(this.autoHeight?this.measuredSizes[i]:this.contentHeight):(0))+'px'});
}
/* End of changes for,not to open the first item of left nav if nothing is opened. */
}
}
// slightly different classes get applied to the last item in the blinds so they can be styled as a cap or base.
if(i==this.elements.length - 1){
item.previous().addClassName('last_header');
item.addClassName("last_content");
item.next().addClassName('last_footer');
}
item.previous().isHeader=true;
item.isContent=true;
// observe the header, content, and footer of each blind panel with mouse events (click or hover depending on config)
[item.previous(),item,item.next()].each(function(subItem){
var slideFunc = this.makeSlideFunc(this.elements,item,i);
// Allow the keyboard focus to actually activate this blind section
subItem.getElementsBySelector("a").each(function(focusableItem){
if(focusableItem.hasClassName('deadmouse')){
// we require some placeholder / dummy links that don't go anywhere
// kill all links that have class "deadmouse"
// both of these events fire when a click happens so we don't want the side to happen twice
var focusHasFired = false;
Event.observe(focusableItem,'click',function(ev){
if(focusHasFired){
focusHasFired = false;
} else {
slideFunc();
}
ev.stop();
focusHasFired = false;
return false;
}.bind(this));
Event.observe(focusableItem,'focus',function(ev){
focusHasFired = true;
slideFunc();
ev.stop();
return false;
}.bind(this));
} else {
if(subItem.isHeader==true){
// all links inside of headers can be focus-triggers-open
Event.observe(focusableItem,'focus',slideFunc);
} else if (subItem.isContent==true){
// for content we have to proxy slideFunc through a check to see if
// the item is currently open before triggering slideFunc
Event.observe(focusableItem,'focus',function(){
if(subItem.getAttribute("blindstatus")!="open"){
// if this item is already opening, we need to tell slideFunc not to queue this attempt to
// open after the current open. Thus, we pass in the current item plus a flag to tell it
// to check that we aren't trying to double-open a given content area
if(this.isAnimating!=true){
slideFunc(true,subItem);
}
}
}.bind(this));
}
}
}.bind(this));
if(subItem) {
Event.observe(subItem,this.eventType,slideFunc);
}
if(subItem && this.eventType=='mouseover') {
Event.observe(subItem,'mouseout',this.makeMouseOutFunc(this.elements,item,i));
}
}.bind(this));
Event.observe(item.previous(),"mouseover",function(){item.previous().addClassName('header_hover')});
Event.observe(item.previous(),"mouseout",function(){item.previous().removeClassName('header_hover')});
}.bind(this));
this.setCapClasses(this.elements[0]);
this.setFooterBlendingClasses(this.elements[0]);
},
// check for a queued effect. If one is present cancel the current one and start the new one
checkForNextEffect: function(effect){
this.isAnimating = false;
if(typeof(this.nextEffect)=='function'){
var f = this.nextEffect;
this.nextEffect = null;
f();
}
},
// if there is a cap (element immediately above the top header), style it according to whether the header is active
setCapClasses: function(currentElement){
if(this.capElement) {
// is the capping element active ?
if(parseInt(currentElement.getAttribute("blindsindex"))==0){
this.capElement.removeClassName("cap_inactive");
this.capElement.addClassName("cap_active");
} else {
this.capElement.removeClassName("cap_active");
this.capElement.addClassName("cap_inactive");
}
}
},
// a footer can either be above an inactive header, or above an active header, or not above any header at all.
// Append appropriate CSS classes
setFooterBlendingClasses: function(currentElement,activeIndex){
if(typeof(activeIndex)!='number') activeIndex = parseInt(currentElement.getAttribute("blindsindex"));
for(var i=0;i