/* * @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