﻿
/**
  * MSML.Accordion 
  * 
  * @description
  * Microsoft Live Messenger Portal Accordion funtionalities.
  * - Vertical accordion, one open element
  * - "1-2-3", horizontal accordion used to describe step one to three. 
  *   In fact that's no real accordion. Just looks similar.
  * 
  * @copyright       Neue Digitale / Razorfish 
  * @author          martin.krause@neue-digitale.de
  * @version         1.0
  *
  * @revision        $Revision$
  * @lastmodified    $Date$
  *
  * @jslint          2009-01-28
  *
  */


MSLM.accordion = {
	
	__version: 1.0, // class version 
	__class: 'MSLM.accordion', // class name
	 
	// set default options
	_oDefaults: {
		sSelectorVertical : '.accordion_vertical dt',
		sSelectorOneTwoThree : '.one-two-three ',
		oCSSPos: { // precalculated values sliding elements to @TODO: implement .one-two
			one: ['-738px','0px'],
			two: ['-685px','51px']
		}
	},
	
	// caches the last slided 1-2-3-type, 'casue ew need to reset this on an vertical accordion animation in internet explorers
	_oLast123:{
		sAnimationId: null,
		$elOne: null,
		$elTwo: null,
		$elOneTwo: null
	},
	
	_oAnimations : {}, // number of running animations
	// store state and action in relation to animation id in case of multiple 1-2-3 elements on one page 
	_oState: {},  //'none', // possible values : none, one, one-two, two, three
	_oAction: {}, //'', // possible values: one, two, three, key: animationId
	 
	/**
	 * Setup
	 * @param {String} [sTitle], title
	 * @return {Void}
	 */
	_setUp: function (oOptions) {
		var _scope = MSLM.accordion;
		// merge options and defaults
		_scope._oOpt = jQuery.extend({},_scope._oDefaults, oOptions);
		_scope._oDefaults = null;

		// add events to links that close the current active and opens the first possible vertical accordion item
		jQuery('.jGoto_accordionitem_1')
			.unbind('click.MSLMAccordion')
			.bind('click.MSLMAccordion',{'sType':'vertical-goto', 'iShow': 1 },MSLM.accordion.click);

		// add evetns to all vertical accordion items
		jQuery(_scope._oOpt.sSelectorVertical)
			.unbind('click.MSLMAccordion')
			.bind('click.MSLMAccordion',{'sType':'vertical'},MSLM.accordion.click);
		
		// add events to all 1-2-3-accordion sensitive areas (<h3>), passing neccesary options
		jQuery(_scope._oOpt.sSelectorOneTwoThree+' .one h3')
			.unbind('click.MSLMAccordion')
			.bind('click.MSLMAccordion',{'sType':'one-two-tree', 'sAction' : 'one'},MSLM.accordion.click);
		jQuery(_scope._oOpt.sSelectorOneTwoThree+' .two h3')
			.unbind('click.MSLMAccordion')
			.bind('click.MSLMAccordion',{'sType':'one-two-tree', 'sAction' : 'two'},MSLM.accordion.click);
		jQuery(_scope._oOpt.sSelectorOneTwoThree+' .three h3')
			.unbind('click.MSLMAccordion')
			.bind('click.MSLMAccordion',{'sType':'one-two-tree', 'sAction' : 'three'},MSLM.accordion.click);
			
	},
	
	/**
	 * onClick Callback
	 * @param {Event} event
	 * @return {Boolean}
	 */
	click:function (event) {
		
		var _$elParent = null;
		
		// get element
		var _$element = jQuery(this);
				
		// switch action
		switch(event.data.sType) {
		
			case 'vertical':
				// get wrapper, necessary for multiple accordions on one page
				_$elParent = _$element.parents('dl').eq(0);
				MSLM.accordion._doClickVertical(_$element,_$elParent);
				window.trackingFire(_$element.attr('id').split('_cl')[1])
			break;
		
			case 'vertical-goto':
				// get wrapper, necessary for multiple accordions on one page
				_$elParent = _$element.parents('dl').eq(0);
				MSLM.accordion._doClickVertical(_$element,_$elParent,{iShow: event.data.iShow});
			break;
		
			case 'one-two-tree':
				// get wrapper, necessary for multiple accordions on one page
				_$elParent = _$element.parents('div.one-two-three').eq(0);
				MSLM.accordion._doClickOneTwoThree(_$element,_$elParent,event.data.sAction);
				window.trackingFire(_$elParent.attr('id').split('_cl')[1]+_$element.attr('id').split('_cl')[1])
			break;
		}
	
		return false;
	},
	
	/**
	 * Handles click type: vertical accordion
	 * @param {jQueryElement} $element
	 * @param {jQueryElement} $elementParent, used for narrowing scope
	 * @param {Options} additional options
	 * @private
	 * @return {Void}
	 */
	_doClickVertical: function($element,_$elParent,oOpt) {
		// exit on active element of running animation
		var _sAnimationId = _$elParent.WLMidentify().attr('id');
		if ( $element.next().is(':visible') || MSLM.DOM.getUnique(_sAnimationId) ) {
			return;
		}
		// set Running flag
		MSLM.DOM.setUnique(_sAnimationId);
		MSLM.accordion._oAnimations[_sAnimationId] = 1 ;
		
		// handle ie, breaks on overflow: hidden / absolute positioned elements during slideToggle
		if (jQuery.browser.msie) {
			
			var _scope = MSLM.accordion;
			_scope.reset123ElementPositionsStart();
			
			jQuery('#'+_sAnimationId + ' .one-two-three').addClass('fixIe');
			
			// reset position
//			_$elParent
//				.find('.one')
//				.css('left', _scope._oOpt.oCSSPos['one'][1]);
//			_$elParent
//				.find('.two')
//				.css('left', _scope._oOpt.oCSSPos['two'][1]);
		}
		// hide all visible elements being part of this accordion
		_$elParent
			.find('dd:visible')
			.slideUp();
		
		// goto: going to open another element?
		if (oOpt && oOpt.iShow) {
			_$elParent
				.find('dd')
				.eq(oOpt.iShow)
				.slideDown(
					function(){
						// pass animation id for removing flag
						MSLM.accordion.onComplete(_sAnimationId);
						jQuery('#'+_sAnimationId+ ' .one-two-three').removeClass('fixIe');
					}
				);
			return false;
		} 
		// default aka normal behaviour
		else {
		// show current by sliding the next sibling 
		$element
			.next()
			.slideDown(
				function(){
					// pass animation id for removing flag
					MSLM.accordion.onComplete(_sAnimationId);
					// remove fixie stuff
					jQuery('#'+_sAnimationId+ ' .one-two-three').removeClass('fixIe');
				}
			);
		}

		
	},

	/**
	 * Handles click type: "1-2-3"
	 * @param {jQueryElement} $element
	 * @private
	 * @return {Bool}
	 */
	_doClickOneTwoThree: function ($element,_$elParent,sAction) {
		
		// force id 
		var _sAnimationId = _$elParent.WLMidentify().attr('id');
		// exit on active element of running animation
		if ( MSLM.DOM.getUnique(_sAnimationId) ) {
			return false;
		}
		// set Running flag
		MSLM.DOM.setUnique(_sAnimationId);
		var _scope = MSLM.accordion;
		
		// cache current action, key: animationId
		_scope._oAction[_sAnimationId] = sAction;
		
		// set default value if necessary
		if ( typeof(_scope._oState[_sAnimationId]) == 'undefined') {_scope._oState[_sAnimationId] = 'none'; }
		
		// cache elements 
		var _$elOne = _$elParent.find('.one');
		var _$elTwo = _$elParent.find('.two');
		var _$elOneTwo = _$elParent.find('.one-two');
		
		// store this animation properties for resetting reasons
		if (jQuery.browser.msie) {
			_scope._oLast123 = {
				sAnimationId: _sAnimationId,
				$elOne: _$elOne,
				$elTwo: _$elTwo,
				$elOneTwo: _$elOneTwo
			};
		}
		
		
		// clicked on element "one"
		if (sAction === 'one') {
			
			// if none or just one element is on the left side, toggle element 'one'
			if (_scope._oState[_sAnimationId] === 'none' || _scope._oState[_sAnimationId] === 'one' ) {
				// toggle element "one"
				_scope._animateOneTwoThree(_$elOne, 'one', _sAnimationId);
				// toggle state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'one') ? 'none' : 'one';
				return true;
			}

			// if .one-two have been animated, reset position, animte .one-two to slide both elements 
			if (_scope._oState[_sAnimationId] === 'one-two') {
				if (_$elOne.left <= 0) {
                    _scope._doReset123ElementPositions(_sAnimationId,_$elOneTwo,_$elOne,_$elTwo);
//					_$elOne.css('left', '0px');
//					_$elTwo.css('left', '53px');
//					_$elOneTwo.css('left', '-738px');
				}
				// toggle element "one"
				_scope._animateOneTwoThree(_$elOneTwo, 'one', _sAnimationId);
				// set state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'one-two') ? 'none' : 'one-two';
				return true;
			}

			// if the last animation used element "two", remove marker classes to get the right images during the animation
			if (_scope._oState[_sAnimationId] === 'two') {
				jQuery('#'+_sAnimationId)
					.removeClass('state-one')
					.removeClass('state-two')
					.removeClass('state-one-two');
				// if the element "one" is on the left hand side, reset positions for sliding element "one-two"
				if (_$elOne.position().left <= 0) {
                    _scope._doReset123ElementPositions(_sAnimationId,_$elOneTwo,_$elOne,_$elTwo);
//					_$elOne.css('left', '0px');
//					_$elTwo.css('left', '53px');
//					_$elOneTwo.css('left', '-738px');
				}
				// toggle element "one-two"
				_scope._animateOneTwoThree(_$elOneTwo, 'one', _sAnimationId);
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'two') ? 'none' : 'two';
				return true;
			}

 		}

		// clicked on element "two"
		if (sAction === 'two') {
 
			// no element on the left hand side, animate element one (showing element two) by sliding "one"
			if (_scope._oState[_sAnimationId] === 'none') {
				// toggle "one-two"
				_scope._animateOneTwoThree(_$elOne, 'one', _sAnimationId);
				// toggle state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'one') ? 'none' : 'one';
				return true;
			}

			// element "one" is on the left hand side, slide element "two"
 			if (_scope._oState[_sAnimationId] === 'one') {
				// toggle element two
				_scope._animateOneTwoThree(_$elTwo, 'two', _sAnimationId);
				// toggle state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'one') ? 'two' : 'one';
				return true;
			}
 
 			// if the last animation slided the "one-two" wrapper, 
			if ( _scope._oState[_sAnimationId] === 'one-two') {
			 	
				// if the "one-two" wrapper's slided to the left 
				if (_$elOneTwo.position().left <  0) {
					jQuery(MSLM.accordion._oOpt.sSelectorOneTwoThree).addClass('animation');
					// reset position
					_$elOne.css('left', '-738px');
					_$elTwo.css('left', '-685px');
					_$elOneTwo.css('left', '0px');
					// slide element two
					_scope._animateOneTwoThree(_$elTwo, 'two', _sAnimationId);
					// toggle state
					_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'one-two') ? 'one' : 'one-two';
					return true;

				}
				// else slide the element "two"
				_scope._animateOneTwoThree(_$elTwo, 'two', _sAnimationId);
				// set state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'two') ? 'one-two' : 'two';
				return true;
			}
			// the last slide occured on element "two"
			 if ( _scope._oState[_sAnimationId] === 'two') {
				// set classes to force animation grafix
		 		jQuery('#'+_sAnimationId)
					.addClass('state-one')
					.removeClass('state-two')
					.removeClass('state-one-two');
				// toggle element two
				_scope._animateOneTwoThree(_$elTwo, 'two', _sAnimationId);
				// toggle state
				_scope._oState[_sAnimationId] = (_scope._oState[_sAnimationId] === 'two') ? 'one' : 'two';
				return true;
			}
		}
		
		// clicked on element three and "one-two" ain't left
		if (sAction === 'three' && _scope._oState[_sAnimationId] != 'one-two' ) {
			
			// if "one" is left, and "two" ain't, slide "two"
			if ( _$elOne.position().left < 0 && _$elTwo.position().left >= 0) {
				// toggle two
				_scope._animateOneTwoThree(_$elTwo, 'two', _sAnimationId);
				// set state
				_scope._oState[_sAnimationId] = 'two';
				return true;
			}
			
			// if none ist left, slide "one-two" for combined sliding both elements
			if (_$elOne.position().left === 0 && _$elTwo.position().left >= 0) {
				// toggle "one-two"
				_scope._animateOneTwoThree(_$elOneTwo, 'one', _sAnimationId);
				// set state
				_scope._oState[_sAnimationId] = 'one-two';
				return true;
			}
			

		}
		
		// failed sliding, remove running flag 
		MSLM.DOM.unsetUnique(_sAnimationId);
		// exit
		return false;
		
	},
	
	/**
	 * Toggle element for animation "1-2-3"
	 * @param {jQuery-Element} $element to toggle
	 * @param {String} sAction marker
	 * @param {string} sAnimationId animation id aka running flag
	 */
	_animateOneTwoThree: function ($element,sAction,sAnimationId) {
		var _scope = MSLM.accordion;
		// get left from stored positions
		var _sLeft = (parseInt($element.position().left) >= -0) ? _scope._oOpt.oCSSPos[sAction][0] : _scope._oOpt.oCSSPos[sAction][1];
		// force position in case of clicked "two" && last animation was "one-two"
		if (_scope._oAction === 'two' && _scope._oState[sAnimationId] === 'one-two') {
//		if (_scope._oAction === 'two' && _scope._oState[_sAnimationId] === 'one-two') {
			_sLeft = _scope._oOpt.oCSSPos['two'][1];
		}
		// set animation counter 
		MSLM.accordion._oAnimations[sAnimationId] = (typeof MSLM.accordion._oAnimations[sAnimationId] !== 'undefined') ? MSLM.accordion._oAnimations[sAnimationId]+1 : 0;
 		// run animation
		$element
			// force animation image 
			.addClass('animation')
			.animate(
				{
					left: _sLeft
				}
				,function(){
					MSLM.accordion.onComplete(sAnimationId,'one-two-three');

				}
			);
	},

 	/**
	 * Resets 1-2-3 elements. Start positions are necessary due to ie-sliding bugs
	 * @return {void}
	 */
	reset123ElementPositionsStart:  function() {
		// cache 
		var _scope = MSLM.accordion;
		var _oCached = _scope._oLast123;
		
		// reset position if possible
 		if (_oCached.sAnimationId) {
			_scope._doReset123ElementPositions(_oCached.sAnimationId,_oCached.$elOneTwo,_oCached.$elOne,_oCached.$elTwo,
				{	sOne: _scope._oOpt.oCSSPos['one'][1], 
					sTwo:_scope._oOpt.oCSSPos['two'][1],
					sOneTwo:'0px'
				}
			);
			// reset properties
			_scope._oState[_oCached.sAnimationId] = 'none';
			MSLM.accordion._oLast123 = {
				sAnimationId: null,
				$elOne: null,
				$elTwo: null,
				$elOneTwo: null
			};
		} 
//		else {
//			alert('nuttin')
//		}
	},
	
	/**
	 * internal 1-2-3 reset
	 * @param {jQuery-Element} $elOneTwo
     * @param {jQuery-Element} $elOne
	 * @param {jQuery-Element} $elTwo
	 * @param {Object} [oPositions] one, two
	 * @return {void}
	 */
	_doReset123ElementPositions:  function(sAnimationId,$elOneTwo,$elOne,$elTwo,oPositions) {
		var _scope = MSLM.accordion;
		if (oPositions && oPositions.sOne && oPositions.sTwo && oPositions.sOneTwo) {
			$elOne.css('left', oPositions.sOne);
			$elTwo.css('left', oPositions.sTwo);
			$elOneTwo.css('left', oPositions.sOneTwo);
		}
		// default 
		else {
			$elOne.css('left', _scope._oOpt.oCSSPos['one'][1] );
			$elTwo.css('left', _scope._oOpt.oCSSPos['two'][1] );
			$elOneTwo.css('left', '-738px');
		}
		jQuery('#'+sAnimationId)
			.removeClass('state-one')
			.removeClass('state-two')
			.removeClass('state-one-two');
	},
	
	/**
	 * Animation onComplete callback
	 * @param {String} sAnimationId aka running flag
	 * @param {String} sType void || one-two-three
	 * @return {void}
	 */
	onComplete : function (sAnimationId,sType) {

		var sState = '';
		
		// construct selector
		var _sSelector = ['#',sAnimationId].join('');
		jQuery(_sSelector)
			// remove active classes
			.find('.active')
				.removeClass('active')
			.end()
			// add class .active on all visible elments and their previous siblings
			.find('dd:visible')
				.addClass('active')
				.prev()
				.addClass('active')
			.end();
	
		// decrease animation counter
		MSLM.accordion._oAnimations[sAnimationId]--;
		if (MSLM.accordion._oAnimations[sAnimationId] <= 0) {
			// type : one-two-three
			if (sType === 'one-two-three') {
				jQuery('#'+sAnimationId)
					// clear states
					.removeClass('state-one')
					.removeClass('state-two')
					.removeClass('state-one-two')
					// add current state class
					.addClass('state-'+MSLM.accordion._oState[sAnimationId]);
				// remove animation classes
				jQuery(MSLM.accordion._oOpt.sSelectorOneTwoThree +' .animation')
					.removeClass('animation');
				jQuery(MSLM.accordion._oOpt.sSelectorOneTwoThree)
					.removeClass('animation');
			}
			// remove running flag
			MSLM.DOM.unsetUnique(sAnimationId);
		}
	},
	
	 /**
	 * Constructor
	 * @param {Object} [oOptions]
	 * @constructor
	 */
	initialize: function (oOptions) {
		// fake singleton
		if (this._oOpt) {
			return this;
		}
		// setup 
		this._setUp(oOptions);
		// fake singleton
		return this;
	}
};

// initialize onDOMReady
jQuery(function() {
	MSLM.accordion.initialize();
});
