/* @Perfect World Entertainment Global Library 
 * @version 2.0
 * @description Global Libary that controls functionality and allows rapid develoment across multiple sites
 */

/* @namespace PWE 
 * @requires document
 * @description PWE global namespace
 * */

var PWE = {

	config: {
	
		updateOnAjax: false,
		ajaxCacheOn: false	

	},

	classes: {},
	page: null,
	UI: {},
	mediator: null,
	storage: null,
	singletonEnforcer: null,
	ajaxCache: null,
	ajax: null,

	bootstrapper: function(){

		PWE.mediator.broadcast('initialize');
		PWE.mediator.broadcast('cleanup');

		delete PWE.bootstrapper;
	
	}

};

/* @class SingletonEnforcer
 * @requires document, PWE, Utils
 * @description allows object registration to force singletons
 * @accessibility read-only
 */

PWE.classes.SingletonEnforcer = function(){

    if(PWE.singletonEnforcer) return PWE.singletonEnforcer;

    var singletons = [];

    function register(name, obj){

        singletons[name] = obj;

    }

    function get(name){

        if(singletons[name]){

            return singletons[name];

        }else{

            return false;

        }

    }

    var objInterface = {register: register, get: get};
    Utils.model.freeze(objInterface);

    return objInterface;

}

PWE.singletonEnforcer = new PWE.classes.SingletonEnforcer();

/* @class Mediator
 * @requires document, PWE, SingletonEnforcer, Utils
 * @description Mediator class to allow different components to broadcast commands to one another as well as issue mass commands to components system-wide.
 * @accessibility read-only
 */

PWE.classes.Mediator = function(){

    if(PWE.singletonEnforcer.get('mediator')) return PWE.singletonEnforcer.get('mediator');

    var components = {};

    var broadcast = function(fn, args){

        a = args || {};

        if(!fn) return;

        for(var n in components){

            if(typeof components[n][fn] == 'function'){

                components[n][fn](args);

            }

        }

    }

    var broadcastTo = function(name, fn, args){

        a = args || {};

        if(!fn) return;

        if(typeof components[name][fn] == Function){

            components[name][fn](args);

        }else{

            debug(name, fn);

        }
    }

    var addComponent = function(name, component, replace){

        if(name in components && replace) removeComponent(name);

		if(!replace && isRegistered(name)) return;

        components[name] = component;

    }

    var removeComponent = function(name){

        if(name in components) delete components[name];

    }

    var isRegistered = function(name){

        var result;

        components[name] ? result = true : result = false;

        return result;

    }

    var debug = function(name, fn){

        if(console) console.warn('Function "'+fn+'" not found on component "' + name + '"');

    }

    var objInterface =  {

        broadcast: broadcast,
        broadcastTo: broadcastTo,
        subscribe: addComponent,
        unsubscribe: removeComponent,
        isSubscribed: isRegistered
    };

    PWE.singletonEnforcer.register('mediator', objInterface);
    Utils.model.freeze(objInterface);

    return objInterface;

}

PWE.mediator = new PWE.classes.Mediator();

/* @class Storage
 * @requires document, PWE, Utils
 * @description Utilize local storage for persistant data  
 * @accessibility read-only 
 */

PWE.classes.Storage = function(){

	var storage;

	localStorage ? storage = localStorage : storage = {};

	this.store = function(key, value){

		try{

			storage[key] = value;

		}catch(e){

			for(n in storage){

				if(storage.removeItem) storage.removeItem(n);
				
				try{
			
					storage[key] = value;
					break;

				}catch(e){

					continue;

				}

			}

		}

	}

	this.get = function(key){

		if(storage[key]) return storage[key];

	}

	this.flush = function(){

		if(storage.removeItem){

			for(n in storage) storage.removeItem(n);

		}

	}

	this.remove = function(key){

		if(storage[n] && storage.removeItem) storage.removeItem(n);

	}
   
	Utils.model.freeze(this);

}

PWE.storage = new PWE.classes.Storage();

/* @class AjaxCache extends Storage
 * @requires document, PWE, SingletonEnforcer, Utils
 * @description caches ajax results with expiration timestamps in local storage
 * @accessibility read-only
 */

PWE.classes.AjaxCache = function(){

	var ac = this;

	if(PWE.singletonEnforcer.get('ajaxcache')) return PWE.singletonEnforcer.get('ajaxcache');

	this.check = function(url, params){
			
		if(!url) return false;
		
		if(ac.get(url) && JSON){

			var d = new Date(), now = d.getTime(), rq = JSON.parse(ac.get(url));

			if(now > rq.expiration || params != rq.params){

				return false;

			}else{
				
				return rq.data;
			}
			
		}

	}	

	this.cache = function(url, params, data, expiration){
		
		if(!url || !data || !expiration) return false;

		var d = new Date(), now = d.getTime(), exp = now + (60000 * expiration);

		var rq = {

			expiration: exp,
			params: params,
			data: data

		}

		if(JSON) ac.store(url, JSON.stringify(rq));

	}

	PWE.singletonEnforcer.register('ajaxcache', this);
	Utils.model.freeze(this);

}

Utils.model.extend(PWE.classes.AjaxCache, PWE.classes.Storage);

PWE.ajaxCache = new PWE.classes.AjaxCache();

/* @class Ajax
 * @requires document, PWE, jQuery, Mediator
 * @description handles global Ajax events using jQuery and depending on configuration will update on ajax calls and work with the ajax cache
 * @accessibility read/write
 */

PWE.classes.AjaxHandler = function(){

	$.ajaxSetup({

		beforeSend: function(xhr, options){
		
			if(PWE.config.ajaxCacheOn){
            		
				if(!options.data) options.data = '';
            	
				if(PWE.ajaxCache.check(options.url, options.data)){
        			        	
					options.success(PWE.ajaxCache.check(options.url, options.data));
					xhr.abort();
				}

			}

		},

		expiration: 0

	});

	$(document).ajaxSuccess(function(e, xhr, options){

		if(PWE.config.ajaxCacheOn){

			if(!options.data) options.data = '';
			PWE.ajaxCache.cache(options.url, options.data, xhr.responseText, options.expiration);

		}	

	});

	$(document).ajaxComplete(function(){

		if(PWE.config.updateOnAjax) PWE.mediator.broadcast('update');

	});

}

PWE.ajax = new PWE.classes.AjaxHandler();

/* @class Page
 * @requires document, PWE, SingletonEnforcer, Mediator
 * @description contains information about the current page and a queueing system
 * @accessibility read/write
 */

PWE.classes.Page = function(){

	if(PWE.singletonEnforcer.get('page')) return PWE.singletonEnforcer.get('page');

	var page = this, onLoadQueue = [], initQueue = [], delQueue = [];

	var getURLComponents = function(){

		this.config.MODULE = this.config.LOCATION.replace('http://'+this.config.HOST, '');

		var urlComponents = this.config.MODULE.split('/');

		if(urlComponents[1]) this.config.MODULE = urlComponents[1];
		if(urlComponents[2]) this.config.SECTION = urlComponents[2];
		if(urlComponents[3]) this.config.SUB_SECTION = urlComponents[3].substr(0, urlComponents[3].indexOf('?'));

		if(this.config.MODULE == '') this.config.MODULE = 'home';

		var getArr = this.config.QUERY_STRING.split('&');

		for(var i = 0; i<getArr.length; i++){

			var tempArr = getArr[i].split('=');

			if(tempArr[0] != '') this.config.GET[tempArr[0]] = tempArr[1];

		}
	}

	var checkForFlash = function(){

		try{

			var fl = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
			return true;

		}catch(e){

			if(navigator.mimeTypes['application/x-shockwave-flash']) return true;

		}

		return false;

	}

	this.config = {
	
		LOCATION: location.href, 
		HOST: location.host, 
		MODULE: null, 
		SECTION: null, 
		SUB_SECTION: null, 
		QUERY_STRING: decodeURI(location.search).replace('?', ''), 
		GET: []

	};

	this.doc = document;
	this.win = window;
	this.userLoggedIn = false;
	this.language = 'en';
	this.browser = /CoreClient|MSIE [\d|\d\d]|Mozilla|Opera|WebKit/.exec(navigator.userAgent).toString();
	this.flashInstalled = checkForFlash();

	this.initialize = function(){
			
		for(var i=0; i<onLoadQueue.length; i++) onLoadQueue[i]();
		
		if(delQueue.length > 0){
					
			for(i=0; i<delQueue.length; i++) delete delQueue[i];
			
		}

	};


	this.queue = function(fn, type){
		
			onLoadQueue.push(fn);
	};

	this.deleteQueue = function(obj){

			delQueue.push(obj);

	};

	this.login = function(elem){
		
		elem.action = 'https://account.perfectworld.com/login';
		elem.submit();

	};

	this.checkEnter = function(e, elem){
	
		if (!e) var e = Page.win.event
		if (e && e.keyCode == 13) page.login(elem);
        
	};

	this.removeFocus = function(){
   
		var a = page.doc.getElementsByTagName("a");
           
		for(var i = 0; i < a.length; i++) a[i].onfocus = function(){this.blur();};
       
	}


	this.cleanup = function(){
	
		delete page.initialize;
		delete page.queue;
		delete page.deleteQueue;
		delete page.cleanup;

	}	

	this.init = function(){};

	getURLComponents.call(this);

	PWE.mediator.subscribe('page', this);
	PWE.singletonEnforcer.register('page', this);
}

PWE.page = Page = new PWE.classes.Page();

/* @class Tabs
 * @requires document, jQuery, PWE, Mediator, SingletonEnforcer
 * @description creates tab functionality using mark up conventions
 * @accessibility read/write
 */ 

PWE.classes.Tabs = function(){

	if(PWE.singletonEnforcer.get('tabs')) return PWE.singletonEnforcer.get('tabs');
		
	function switchTab(){

		if($(this).hasClass('tab-content')) return;

		var parent = $(this).parent().parent(), container = $('#'+this.id+'-tab');

		$('.selected').removeClass('selected');

		parent.find('.tab-content').addClass('hidden');
		
		$(this).addClass('selected');

	}

	this.initialize = function(){	
		
		if(!$('.tab').length && !PWE.config.updateOnAjax){

			delete PWE.UI.tabs;
			PWE.mediator.unsubscribe('tabs');
			return;
			
		}
		
		$('.tab').bind('click', switchTab);
	
	}

	this.update = function(){

		$('.tab').unbind('click', switchTab).bind('click', switchTab);

	}

	this.cleanup = function(){

		delete initialize;
		delete cleanup;

	}

	PWE.mediator.subscribe('tabs', this);
	PWE.singletonEnforcer.register('tabs', this);
}

PWE.UI.tabs = new PWE.classes.Tabs();

/* @class LightBox
 * @requires document, jQuery, SingletonEnforcer, Page, Mediator, Utils
 * @description creates lightbox functionality using markup conventions
 * @accessibility read/write
*/

PWE.classes.LightBox = function(){

	if(PWE.singletonEnforcer.get('lightbox')) return PWE.singletonEnforcer.get('lightbox');
	
	var lb = this, isActive = false, to = null, sc = null;

	function reposition(){
	
		if(!isActive) return;

		$('.lb-visible').each(function(){

			$(this).animate({

				left: Utils.UI.calculateCenterX($(this)),
				top: Utils.UI.calculateCenterY($(this))

			}, 300).dequeue();

		})

		$('#lbbg').width(Utils.UI.calculateScreenWidth()).height(Utils.UI.calculateScreenHeight());

	}

	function set(){

		clearTimeout(to);
		to = setTimeout(reposition, 100);
	}

	function setScroll(){

		if(!isActive) return;

		clearTimeout(sc);
		sc = setTimeout(reposition, 100);
	}

	function testEscape(e){

		if(!isActive) return;
		if(e.keyCode == 27) lb.hide();
	}

	this.initialize = function(){
		
		if(!$('.lb').length && !PWE.config.updateOnAjax){

			delete PWE.UI.lb;
			PWE.mediator.unsubscribe('lightbox');
			return;
		}

		$('.lb').bind('click', lb.show);
		$('#lbbg').bind('click', lb.hide);

		PWE.page.doc.all ? $(Page.doc).bind('keydown', testEscape) : $(Page.win).bind('keydown', testEscape);
		PWE.page.doc.all ? $(Page.win).bind('resize', set) : $(Page.win).bind('resize', reposition);
		$(PWE.page.win).bind('scroll', setScroll);
	}

	this.update = function(){

		$('.lb').unbind('click', lb.show).bind('click', lb.show);
		$('#lbbg').unbind('click', lb.hide).bind('click', lb.hide);

		PWE.page.doc.all ? $(PWE.page.doc).unbind('keydown', testEscape).bind('keydown', testEscape) : $(PWE.page.win).unbind('keydown', testEscape).bind('keydown', testEscape);
		PWE.page.doc.all ? $(PWE.page.win).unbind('resize', set).bind('resize', set) : $(PWE.page.win).unbind('resize', reposition).bind('resize', reposition);
		$(PWE.page.win).unbind('scroll', setScroll).bind('scroll', setScroll);
	}

	this.show = function(element){
			
		isActive = true;
		var LBox;        

		if(!element.id) element = null;

		element ? LBox = $('#'+element.id + '-lb') : LBox = $('#'+this.id+'-lb');
		
		if(!LBox.length){

			if(element){

				LBox = $('#'+$(element).attr('target'));
				if($(element).attr('fn')) eval($(element).attr('fn') + '()');

			}else{

				LBox = $('#'+$(this).attr('target'));
				if($(this).attr('fn')) eval($(this).attr('fn') + '()');

			}

			if(!LBox.length) return;

		}

		$('#lbbg').width(Utils.UI.calculateScreenWidth()).height(Utils.UI.calculateScreenHeight()).fadeIn(300);

		LBox.css('left', Utils.UI.calculateCenterX(LBox)).css('top', Utils.UI.calculateCenterY(LBox)).fadeIn(300).addClass('lb-visible');
		LBox.find('.close').bind('click', lb.hide);

		LBox.find('input, a').each(function(){

			if(this.nodeName == 'INPUT'){

				$(this).focus();
				return;
			}

		});
	
	}

	this.hide = function(){

		$('.lb-visible').fadeOut(300).removeClass('lb-visible');
		$('#lbbg').fadeOut(300);

		isActive = false;
	}

	this.cleanup = function(){

		delete lb.initialize;
		delete lb.cleanup;
	}

	this.init = this.initialize; //backwards compatibility

	PWE.mediator.subscribe('lightbox', this);
	PWE.singletonEnforcer.register('lightbox', this);

};

PWE.UI.lb = LB = new PWE.classes.LightBox();

/* @ class DropDown
 * @ requires document, jQuery, SingletonEnforcer, Mediator, Page, ScrollBar
 * @ description Creates custom dropdowns with numerous accessibility features
 * @ accessibility read-only
 */

PWE.classes.DropDown = function(){

	if(PWE.singletonEnforcer.get('dropdown')) return PWE.singletonEnforcer.get('dropdown');

	var listIndex = [], scrollbars = [], storedChars = null, chars = '', dispError = false, mozillaScrollReference = null, dd = this;

	var bindEvents = function(){

		$('.dd').find('a').bind('click', dd.toggleDD).bind('keydown', cycleValuesInit).click(function(){ return false;});
		$('.dd').find('li').bind('click', setValue);		
	}

	var rebindEvents = function(){

		$('.dd').find('a').unbind('click', dd.toggleDD).bind('click', dd.toggleDD).unbind('keydown', cycleValuesInit).bind('keydown', cycleValuesInit).click(function(){ return false;});
		$('.dd').find('li').unbind('click', setValue).bind('click', setValue);
	}

	var createScrollbars = function(t){ //called at a later date if dispError is true, creates the scrollbar, this usually gets called due to ajax calls

		t = t.parent();
                
		var w = t.width(), h1 = t.find('div.outer-dd').height(), h2 = 0, lis = t.find('ul').eq(0).find('li');
                
		t.width(w);

		for(var i = 0; i<lis.length; i++) h2 += Math.abs(lis.eq(i).outerHeight());

		t.find('ul').eq(0).height(h2);

		if(h2 > h1){

			scrollbars.push(new PWE.classes.ScrollBar(t));
			t.find('.scrollbar').show();

		}else{

			t.find('.scrollbar').hide();

		}

	}

	var checkBodyClick = function(e){

		if($(e.target).hasClass('dd')) return;

		var par = $(e.target), ddClicked = false, i = 0;

		while(true){

			if(i > 7) break;
			par = par.parent();
			i++;
			if(par.hasClass('dd')){

				ddClicked = true;
				break;
			}
		}

		if(!ddClicked){

		if($.browser.mozilla){
			
			try{
				PWE.page.win.removeEventListener('DOMMouseScroll', mozillaScrollReference.onWheel, false);
			}catch(e){}

			}else{

				PWE.page.doc.onmousewheel = null;

			}

			$('div.outer-dd').hide();
			$('body').unbind('click', checkBodyClick);

		}
	}

	var setValue = function(){

		var t = $(this), list = t.parent().parent().parent().parent(), val = t.attr('val'), target = list.attr('target');
        
		$('#'+target + '-input').val(val);
		list.children().eq(0).text(t.text());
		$('li.selectedOption').removeClass('selectedOption');
		t.addClass('selectedOption');
		t.parent().parent().parent().hide();
	}

	var cycleValuesInit = function(e){

		e.preventDefault();

		var t = $(this);
		
		if(!$(this).parent().find('.outer-dd').is(':visible')) dd.toggleDD(t);
	
		if(e.which == 9){ //if tab is hit

			t.parent().find('div.outer-dd').hide();
			return;

		}else{

			var listIndexKey = t.parent().attr('id');

			if(listIndex[listIndexKey] == null){ //if the array key for the index does not exist create it

				listIndex[listIndexKey] = 0;
				cycleValues(e.which, -1, $(this).parent().find('ul').eq(0), $(this), listIndexKey);

			}else{

				cycleValues(e.which, listIndex[listIndexKey], $(this).parent().find('ul').eq(0), $(this), listIndexKey);

			}

		}

	}

	var cycleValues = function(key, index, list, srcElement, listIndexKey){

		var par = srcElement.parent();
		
		switch(key){

			case 38: //up
				index--;
				listIndex[listIndexKey]--;
				break;

			case 40: //down
				index++;
				listIndex[listIndexKey]++;
				break;

			case 13: //enter
				dd.toggleDD($(srcElement).parent().find('a').eq(0));
				return;

			default:
				searchValues(key, index, list, srcElement, listIndexKey);
				return;
		}

		if(index < 0){

			index = listIndex[listIndexKey] = list.find('li').length - 1;

		}else if(index >= list.find('li').length){

			index = listIndex[listIndexKey] = 0;
		}

		par.find('li').removeClass('selectedOption');
		par.find('li').eq(index).addClass('selectedOption');
		srcElement.text(srcElement.parent().find('li').eq(index).text());
		$('#'+par.attr('target')+'-input').val(srcElement.parent().find('li').eq(index).attr('val'));
		
		var sbObj = getMatchingScrollbar(srcElement);

		if(!sbObj) return;

		var liHeight = srcElement.parent().find('li').eq(0).outerHeight()
		var scHeight = sbObj.scroller.height();
		var posY = index * liHeight;

		sbObj.innerContWrap.scrollTop(posY);

		if(posY > scHeight && parseInt(sbObj.scroller.css('top')) < (sbObj.outerHeight-scHeight)){

			if(posY/sbObj.ratio > sbObj.outerHeight - scHeight){

				sbObj.scroller.css('top', sbObj.outerHeight - scHeight);

			}else{

				sbObj.scroller.css('top', posY/sbObj.ratio);

			}

		}else if(parseInt(sbObj.scroller.css('top')) >= (sbObj.outerHeight-scHeight) && index != 0){

			if(key == 38){

				if(posY/sbObj.ratio < sbObj.outerHeight - scHeight){

					sbObj.scroller.css('top', posY/sbObj.ratio);

				}else{

					sbObj.scroller.css('top', sbObj.outerHeight - scHeight);

				}

			}else{

				sbObj.scroller.css('top', sbObj.outerHeight - scHeight);

			}

		}else if(index == 0){

			sbObj.scroller.css('top', 0);

		}else{

			sbObj.scroller.css('top', 0);

		}

	}

	var getMatchingScrollbar = function(el){

		var id = el.parent().attr('id');
		var sb = null;

		for(var i = 0; i<scrollbars.length; i++){

			if(scrollbars[i].outerCont.parent().attr('id') == id){

				sb = scrollbars[i];
				break;
			};

		}

		return sb;

	}

	var deleteStoredCode = function(){

		chars = '';

	}

	var searchValues = function(key, index, list, srcElement, listIndexKey){
		
		var items = $.makeArray(srcElement.parent().find('li')), text = [];
	
		for(var i = 0; i<items.length; i++) text.push(items[i].innerHTML.toLowerCase());

		text.sort();

		chars += String.fromCharCode(key).toLowerCase();
		
		clearTimeout(storedChars);
		storedChars = setTimeout(deleteStoredCode, 300);
	
		if(key >47 && key<91){ //a - z
	
			var r1 = 0, r2 = 0;

			for(var i=0; i<text.length; i++){

				var l = '';

				for(t = 0; t<chars.length; t++) l += text[i][t].toLowerCase();
                
				if(l <= chars){
                    
					r1 = i;

				}else if( l > chars){

					break;

				}

			}

			var selectedText = text[r1];
		
			var selectedValue = '';

			srcElement.parent().find('li').each(function(){

				if($(this).text() == selectedText) selectedValue = $(this).attr('val');

			});
			
			for(i = 0; i<items.length; i++){

				listIndex[listIndexKey] = i;
				if($(items).eq(i).text() == selectedText) break;

			}

			srcElement.text(selectedText);
			$('#'+srcElement.parent().attr('target')+'-input').val(selectedValue);
		}
	}

	this.initialize = function(){

		if($('.dd').length || PWE.config.updateOnAjax){

			bindEvents();

			$('.dd').find('div.outer-dd').each(function(){

				var t = $(this); w = t.parent().outerWidth(), h1 = t.height(), h2 = t.find('ul').eq(0).height();
                
				t.width(w);
                
				if(h2 > h1 && h1 != 0){ //if the inner list is taller than the outer add a scrollbar
                    
					scrollbars.push(new PWE.classes.ScrollBar(t)); //creates scrollbars

				}else{

					if(h2 == 0) dispError = true; //the UL is hidden, will calculate later

					t.find('.scrollbar').hide();

				}

				t.hide().css('left', 'auto');

			});			

		}else{

			delete PWE.UI.dd;
			PWE.mediator.unsubscribe('dropdown');
			return;
		}

	}

	this.update = function(){

		rebindEvents();

		scrollbars = [];

		$('.dd').find('div.outer-dd').each(function(){

			var t = $(this); w = t.parent().outerWidth(), h1 = t.height(), h2 = t.find('ul').eq(0).height();

			t.width(w);

			if(h2 > h1 && h1 != 0){ //if the inner list is taller than the outer add a scrollbar

				scrollbars.push(new PWE.classes.ScrollBar(t)); //creates scrollbars

			}else{

				if(h2 == 0) dispError = true; //the UL is hidden, will calculate later

				t.find('.scrollbar').hide();

			}

			t.hide().css('left', 'auto');

		});

	}

	this.toggleDD = function(e){

		if(e.preventDefault){

			var t = $(this);
			e.preventDefault();			

		}else{

			var t = $(e);

		}

		if(t.parent().hasClass('disabled') || t.hasClass('disabled')) return;

		var cont = t.parent().find('div.outer-dd').eq(0), sb = getMatchingScrollbar(cont);

		if(cont.is(":visible")){

			$('body').unbind('click', checkBodyClick); //if you click outside the dropdown it hides it
			cont.hide();

			if($.browser.mozilla){

				try{ PWE.page.win.removeEventListener('DOMMouseScroll', sb.onWheel, false)}catch(e){};

			}else{

				PWE.page.doc.onmousewheel = null;

			}

		}else{

			if($('div.outer-dd').is(":visible")) $('div.outer-dd').hide(); //hides other dropdowns
			cont.show();
			$('body').bind('click', checkBodyClick);
			if(dispError) createScrollbars(t); //if the UL is hidden

			if(sb){

				if($.browser.mozilla){

					PWE.page.win.addEventListener('DOMMouseScroll', sb.onWheel, false);
					mozillaScrollReference = sb;

				}else{

					PWE.page.doc.onmousewheel = sb.onWheel;

				}

			}
		}
	}

	this.init = this.initialize; //backward compatibility

	PWE.mediator.subscribe('dropdown', this);
	PWE.singletonEnforcer.register('dropdown', this);
	Utils.model.freeze(this);

}

PWE.UI.dd = DD = new PWE.classes.DropDown();

/* @class ScrollBar
 * @requires document, Page, jQuery
 * @param el - > div.outer-dd
 * 
 * @description
 *
 * This is an object representation of the scrollbar and controls it's functionality
 * each scrollbar has it's own ScrollBar object
 */

PWE.classes.ScrollBar = function(el){
	
	var sb = this;
	var isIE = false;
	var cursorY = null;
	var maxTop = 0;
		
	this.outerCont = el.find('div.outer-dd');
	this.innerContWrap = this.outerCont.find('div.inner-dd');
	this.innerCont = this.innerContWrap.find('ul').eq(0);
	this.bar = el.find('.scrollbar');
	this.scroller = this.bar.find('div').eq(0);
	this.outerHeight = this.outerCont.height();
	this.outerWidth = this.outerCont.width();
	this.innerHeight = this.innerCont.height();
	this.ratio = this.innerHeight/this.outerHeight; 
	this.curScrollTop = 0;
	
	var initialize = function(){

		this.scroller.bind('mousedown', this.scrollInit);
		this.bar.bind('click', this.scrollTo);
		
		this.outerCont.scrollTop(0);
		this.bar.css('margin-left', this.outerWidth - this.bar.width()).height(this.outerHeight); //positions scrollbar
		this.scroller.height(this.outerHeight/this.ratio).css('left', 0); //resizes scroller
		this.scroller.attr('draggable', 'false'); //so when you mousedown it doesn't treat the element as draggable
		maxTop = this.outerHeight - this.scroller.height(); //the maximum the scroller can scroll down
	
		if(document.all) isIE = true;

	}
	
	this.scrollInit = function(e){
		
		if(isIE && $.browser.version == '7.0' || isIE && $.browser.version == '8.0'){
			$(PWE.page.doc).bind('mousemove', sb.scroll);
			$(PWE.page.doc).bind('mouseup', sb.stopScroll);
		}else{
			
			$(PWE.page.win).bind('mousemove', sb.scroll);
			$(PWE.page.win).bind('mouseup', sb.stopScroll);
		}
		
		if(isNaN(parseInt(sb.scroller.css('top')))) sb.scroller.css('top', 0); 

		cursorY = e.clientY - parseInt(sb.scroller.css('top')); //records the original mousedown	
		return false;
	}
	
	this.scroll = function(e){
		
		var y = e.clientY - cursorY;	
	
		if(y < 2) y = 0;

		if(y > maxTop) y = maxTop;
		
		var r = y * sb.ratio;

		sb.scroller.css('top', y);
			
		sb.innerContWrap.scrollTop(r);

		sb.curScrollTop = r;
	}

	this.stopScroll = function(e){

		if($.browser.msie && $.browser.version == '7.0' || $.browser.msie && $.browser.version == '8.0'){
			
			$(PWE.page.doc).unbind('mousemove', sb.scroll);
			$(PWE.page.doc).unbind('mouseup', sb.stopScroll);

		}else{

			$(PWE.page.win).unbind('mousemove', sb.scroll);
			$(PWE.page.win).unbind('mouseup', sb.stopScroll);

		}

	}
	
	this.scrollTo = function(e){
		
		if($(e.target).attr('class') != 'scrollbar') return;
		
		var Y = 0, regx = /Chrome|Safari/;

		PWE.page.doc.all || regx.test(navigator.userAgent) ? Y = e.offsetY : Y = e.layerY;
	  	
		var targetY = Y - sb.scroller.height()/2;
		var maxY = sb.outerHeight - sb.scroller.height();
	
		if(targetY < 0) targetY = 0;
		if(targetY > maxY) targetY = maxY; 	
			
		sb.scroller.css('top', targetY);
		sb.innerContWrap.scrollTop(targetY * sb.ratio);
		sb.curScrollTop = targetY * sb.ratio;
	}
	
	this.onWheel = function(e){
		
		if(!e) e = window.event;

		if(e.preventDefault) e.preventDefault();
		
		var sPos = sb.scroller.position().top;
		var sPos2 = sPos + sb.scroller.height();
	
		if(e.wheelDelta < 0 || e.detail > 0){
			
			if(sPos2 <= sb.outerHeight){
	
				if(sPos + 4 >= sb.outerHeight){

					sb.scroller.css('top', sb.outerHeight);
					sb.innerContWrap.scrollTop(sb.outerHeight * sb.ratio);
					sb.curScrollTop = sb.outerHeight * sb.ratio;
				
				}else{

					sb.scroller.css('top', sPos + 4);
					sb.innerContWrap.scrollTop((sPos + 4) * sb.ratio);
					sb.curScrollTop = (sPos + 4) * sb.ratio;
				}
			}			

		}else{

			if(sPos >= 0){

				if(sPos - 4 < 0){

					sb.scroller.css('top', 0);
					sb.innerContWrap.scrollTop(0);			
					sb.curScrollTop = 0;
				}else{

					sb.scroller.css('top', sPos - 4);
					sb.innerContWrap.scrollTop((sPos - 4) * sb.ratio);
					sb.curScrollTop = (sPos - 4) * sb.ratio;
				}
			}	

		}	

		return false;

	}

	initialize.call(this);
	Utils.model.seal(this);
}

/*  @class RequireLogin
 *	@requires document, Page, jQuery
 *  @description forces lightbox for login required pages
 */

PWE.classes.RequireLogin = function(){

	if(PWE.singletonEnforcer.get('requirelogin')) return PWE.singletonEnforcer.get('requirelogin');

	var rl = this;

	var proxyLBOpen = function(e){

		PWE.UI.lb.show(PWE.page.doc.getElementById('login'));
		e.preventDefault();
	}

	this.initialize = function(){

		var els = $('.check-login');

		if(els.length > 0 && !PWE.page.userLoggedIn || PWE.config.updateOnAjax){

			els.addClass('require-login');

			$('.require-login').bind('click', proxyLBOpen);

		}else{

			delete PWE.UI.requireLogin;
			PWE.mediator.unsubscribe('requiredlogin');
			return;

		}
	}

	this.update = function(){

		$('.check-login').addClass('require-login');

		$('.require-login').unbind('click', proxyLBOpen).bind('click', proxyLBOpen);
	}

	this.init = this.initialize; //backwards compatibility

	PWE.mediator.subscribe('requiredlogin', this);
	PWE.singletonEnforcer.register('requirelogin', this);
	Utils.model.freeze(this);
}

PWE.UI.requireLogin = RequireLogin = new PWE.classes.RequireLogin();

/* @class LangCheck
 * @requires document, Page, jQuery
 * @description Shows the languages selection lightbox for sites that are marked enabled 
 */

PWE.classes.LangCheck = function(){

	if(PWE.singletonEnforcer.get('langcheck')) return PWE.singletonEnforcer.get('langcheck');

	var lc = this;
	var content = null;
	var timeout = null;

	var bindEvents = function(){

		$('.radio, .langFlag').bind('click', chooseLang);
		$('#lbbg').bind('click', saveLang);
		$('#langsave').bind('click', saveLang);
		content.find('.close').bind('click', saveLang);
		$(PWE.page.win).bind('resize', resizeInit);
		$(PWE.page.win).bind('scroll', resizeInit);
	}

	var positionLB = function(){

		$('#lbbg').fadeIn(350).width(Utils.UI.calculateScreenWidth()).height(Utils.UI.calculateScreenHeight());
		content.css('left', Utils.UI.calculateCenterX(content)).css('top', Utils.UI.calculateCenterY(content)).fadeIn(350);
	}

	var chooseLang = function(){

		$('.radio').removeClass('selected').attr('selected', 'false');

		var radio = null;

		$(this).hasClass('radio') ? radio = $(this) : radio = $(this).prev('.radio');

		radio.addClass('selected').attr('selected', 'true');
	}

	var resizeInit = function(){

		clearTimeout(LangCheck.timeout);

		setTimeout(resize, 20);
	}

	var resize = function(){

		content.animate({

			left: Utils.UI.calculateCenterX(content),
			top: Utils.UI.calculateCenterY(content)

		}, 300).dequeue();	

		$('#lbbg').width(Utils.UI.calculateScreenWidth()).height(Utils.UI.calculateScreenHeight());
	}

	var saveLang = function(){

		var redirectLang = 'en';

		$('.radio').each(function(){

			if($(this).hasClass('selected')) redirectLang = $(this).attr('lang');

		});

		$.ajax({

			url: '/home/langcheck',
			type: 'post',
			data: 'lang=',
			success: function(data){

				if(redirectLang == 'en'){ 

					content.fadeOut(350);
					$('#lbbg').fadeOut(500).unbind('click', chooseNone);					
	
				}else{ 

					redirect(redirectLang);

				}

			}
		});

		$(PWE.page.win).unbind('resize', resizeInit);
		$(PWE.page.win).unbind('scroll', resizeInit);
	}

	var redirect = function(redirectLang){

		var loc = Page.config.LOCATION;		
		
		switch(redirectLang){

			case 'de':
					
					loc = loc.replace('www', '');
					loc = loc.replace('perfectworld', 'de.perfectworld');
					loc = loc.replace('.com', '.eu');

					break;

				case 'fr':

					loc = loc.replace('www', '');
					loc = loc.replace('perfectworld', 'fr.perfectworld');
					loc = loc.replace('.com', '.eu');

					break;

		}			

		location.replace(loc);	

	}

	this.init = function(){
		
		content = $('#langpref');

		bindEvents();
		positionLB();
	}

	this.initialize = function(){

		if(!content){

			delete PWE.UI.langCheck;
			delete LangCheck;
			PWE.mediator.unsubscribe('langcheck');
			return;

		}

	}

	this.cleanup = function(){

		delete lc.initialize;
		delete lc.cleanup;

		Utils.model.freeze(lc);
	}

	PWE.mediator.subscribe('langcheck', this);
	PWE.singletonEnforcer.register('langcheck', this);
}

PWE.UI.langCheck = LangCheck = new PWE.classes.LangCheck();

/* @class RecentActivity
 * @requires document, Page, jQuery
 * @description 
 * fetches activities, splits and queues results, rotates them marquee-style 
 * Used with our recent activity module.
 */

PWE.classes.RecentActivity = function(){

	if(PWE.singletonEnforcer.get('recentactivity')) return PWE.singletonEnforcer.get('recentactivity');

	var self = this;
	var numActivities = 0;    
	var queuedActivities = [];
	var curIndex = 0;
	var ulContainer = null;
	var interval = null;
	var retrievedActivities = false;

	var getActivities = function(){

		if(retrievedActivities) return;
		
		$.ajax({

			url: '/home/recent_activity',
			type: 'post',
			data: null,
			expiration: 5,
			success: function(data){
                
				if(data.indexOf('<html') != -1 || data.indexOf('Fatal Error') != -1){

					$('.bg-recent').removeClass('ra-loading').append('<h3> Error loading recent activity. </h3>');
					return;
				}

				formatData(data);
				retrievedActivities = true;
			},

			error: function(xhr, err){

				$('.bg-recent').removeClass('ra-loading').append('<h3> Error loading recent activity. </h3>');

			}

		});

	}

	var formatData = function(data){
			
		queuedActivities = data.split('</li>').slice(0,-1);
	
		numActivities = queuedActivities.length;

		for(var i = 0; i<numActivities; i++) queuedActivities[i] += '</li>';

		initContainer();

		$('.bg-recent').removeClass('ra-loading');

	}

	var initContainer = function(){

		for(var i = 0; i<4; i ++){

			ulContainer.append(queuedActivities[i]);
			curIndex++;
		}

		if(numActivities < 4) return;

		interval = setInterval(rotate, 3000);

	}

	var addActivity = function(){

		ulContainer.append(queuedActivities[curIndex]);

	}

	var removeActivity = function(){

		ulContainer.find('li').eq(0).remove();

	}

	var rotate = function(){

		curIndex == numActivities - 1 ? curIndex = 0 : curIndex++;

		addActivity();

		ulContainer.find('li').eq(0).animate({

			marginTop: -58

		}, 350, removeActivity);
	}
	
	this.initialize = function(){
	
		if(!$('.bg-recent').length && !PWE.config.updateOnAjax){

			delete PWE.UI.recentActivity;
			PWE.mediator.unsubscribe('recentactivity');
			return;

		}

		ulContainer = $('.bg-recent').find('ul').eq(0);	
		getActivities();
	}

	this.cleanup = function(){

		delete self.initialize;
		delete self.cleanup;
		Utils.model.freeze(self);

	}

	PWE.singletonEnforcer.register('recentactivity', this);
	PWE.mediator.subscribe('recentactivity', this);
}

PWE.UI.recentActivity = new PWE.classes.RecentActivity();

/* @class OffersPU
 * @requires document, Page, Utils
 * @description Controls the zen offer popup
 */

PWE.classes.OffersPU = function(){

	if(PWE.singletonEnforcer.get('offerspu')) return PWE.singletonEnforcer.get('offerspu');

	var bg = null, popup = null, btns = {cont: null, canc: null}, offers = this;

	var setElements = function(){

		bg = $('#offerslbbg');
		popup = $('#offers-popup');
		btns.cont = $('#btn-continue');
		btns.canc = $('#btn-cancel');
	}

	var bindEvents = function(){

		btns.cont.bind('click', viewOffer);
		btns.canc.bind('click', cancelOffer);
	}

	var callLightBox = function(){
			
		bg.hide().width(Utils.UI.calculateScreenWidth()).height(Utils.UI.calculateScreenHeight()).fadeIn(300);
		popup.hide().css('left', Utils.UI.calculateCenterX(popup)).css('top', Utils.UI.calculateCenterY(popup)).fadeIn(300);
	}

	var viewOffer = function(){

		$.getJSON('http://www.perfectworld.com/home/continuepopup?ajax=1&callback=?', null, function(data){});
		location.href = 'http://offers.perfectworld.com';
	}

	var cancelOffer = function(){

		$.getJSON('http://www.perfectworld.com/home/cancelpopup?ajax=1&callback=?', null, function(data){});

		bg.fadeOut(300);
		popup.fadeOut(300);
	}

	this.trigger = false;

	this.initialize = function(){
	
		if(!offers.trigger){
			
			delete OffersPU;
			delete PWE.UI.offersPU;
			Utils.model.seal(offers);
			return;
		}
			
		setElements();
		bindEvents();
		callLightBox();
		Utils.model.seal(offers);
	}

	this.init = function(){};

	PWE.singletonEnforcer.register('offerspu', this);
	PWE.mediator.subscribe('offerspu', this);
}

PWE.UI.offersPU = OffersPU = new PWE.classes.OffersPU();

//------------- end global.js objects ----------------------------//

$(PWE.page.doc).ready(PWE.bootstrapper);


