var log4javascript;(function(){function ff(){return function(){}}function copy(obj,props){for(var i in props){obj[i]=props[i]}}var f=ff();var Logger=ff();copy(Logger.prototype,{addAppender:f,removeAppender:f,removeAllAppenders:f,log:f,setLevel:f,getLevel:f,trace:f,debug:f,info:f,warn:f,error:f,fatal:f});var getLogger=function(){return new Logger()};log4javascript={isStub:true,version:"dummy",logLog:{setQuietMode:f,setAlertAllErrors:f,debug:f,warn:f,error:f},addErrorListener:f,removeErrorListener:f,setEnabled:f,setShowStackTraces:f,isEnabled:f,evalInScope:f,getLogger:getLogger,getDefaultLogger:getLogger,getNullLogger:getLogger,Level:ff(),LoggingEvent:ff(),Layout:ff(),Appender:ff()};log4javascript.LoggingEvent.prototype={getThrowableStrRep:f};log4javascript.Level.prototype={toString:f,equals:f,isGreaterOrEqual:f};var level=new log4javascript.Level();copy(log4javascript.Level,{ALL:level,TRACE:level,DEBUG:level,INFO:level,WARN:level,ERROR:level,FATAL:level,OFF:level});log4javascript.Layout.prototype={defaults:{},format:f,ignoresThrowable:f,getContentType:f,allowBatching:f,getDataValues:f,setKeys:f,setCustomField:f,hasCustomFields:f};log4javascript.SimpleLayout=ff();log4javascript.SimpleLayout.prototype=new log4javascript.Layout();log4javascript.XmlLayout=ff();log4javascript.XmlLayout.prototype=new log4javascript.Layout();log4javascript.XmlLayout.prototype.escapeCdata=f;log4javascript.JsonLayout=ff();log4javascript.JsonLayout.prototype=new log4javascript.Layout();copy(log4javascript.JsonLayout.prototype,{setReadable:f,isReadable:f});log4javascript.HttpPostDataLayout=ff();log4javascript.HttpPostDataLayout.prototype=new log4javascript.Layout();log4javascript.PatternLayout=ff();log4javascript.PatternLayout.prototype=new log4javascript.Layout();log4javascript.NullLayout=ff();log4javascript.NullLayout.prototype=new log4javascript.Layout();log4javascript.Appender=ff();log4javascript.Appender.prototype={layout:new log4javascript.PatternLayout(),threshold:log4javascript.Level.ALL,doAppend:f,append:f,setLayout:f,getLayout:f,setThreshold:f,getThreshold:f,toString:f};log4javascript.AlertAppender=ff();log4javascript.AlertAppender.prototype=new log4javascript.Appender();log4javascript.ArrayAppender=ff();log4javascript.ArrayAppender.prototype=new log4javascript.Appender();log4javascript.AjaxAppender=ff();log4javascript.AjaxAppender.prototype=new log4javascript.Appender();copy(log4javascript.AjaxAppender.prototype,{isTimed:f,setTimed:f,getTimerInterval:f,setTimerInterval:f,isWaitForResponse:f,setWaitForResponse:f,getBatchSize:f,setBatchSize:f,setRequestSuccessCallback:f,setFailCallback:f,sendAll:f,defaults:{requestSuccessCallback:null,failCallback:null}});function ConsoleAppender(){}ConsoleAppender.prototype=new log4javascript.Appender();copy(ConsoleAppender.prototype,{create:f,isNewestMessageAtTop:f,setNewestMessageAtTop:f,isScrollToLatestMessage:f,setScrollToLatestMessage:f,getWidth:f,setWidth:f,getHeight:f,setHeight:f,getMaxMessages:f,setMaxMessages:f});log4javascript.InPageAppender=ff();log4javascript.InPageAppender.prototype=new ConsoleAppender();copy(log4javascript.InPageAppender.prototype,{isInitiallyMinimized:f,setInitiallyMinimized:f,hide:f,show:f,isVisible:f,close:f,defaults:{layout:new log4javascript.PatternLayout(),maxMessages:null}});log4javascript.InlineAppender=log4javascript.InPageAppender;log4javascript.PopUpAppender=ff();log4javascript.PopUpAppender.prototype=new ConsoleAppender();copy(log4javascript.PopUpAppender.prototype,{isUseOldPopUp:f,setUseOldPopUp:f,isComplainAboutPopUpBlocking:f,setComplainAboutPopUpBlocking:f,isFocusPopUp:f,setFocusPopUp:f,isReopenWhenClosed:f,setReopenWhenClosed:f,close:f,defaults:{layout:new log4javascript.PatternLayout(),maxMessages:null}});log4javascript.BrowserConsoleAppender=ff();log4javascript.BrowserConsoleAppender.prototype=new log4javascript.Appender()})();var log4javascript_dummy=log4javascript;
/* OnlineOpinion (S3tS,1424b) */
/* This product and other products of OpinionLab, Inc. are protected by U.S. Patent No. 6606581, 6421724, 6785717 B1 and other patents pending. */
var timePass = "";
var custom_var,_sp='%3A\\/\\/',_rp='%3A//',_poE=0.0, _poX=0.0,_sH=screen.height,_d=document,_w=window,_ht=escape(_w.location.href),_hr=_d.referrer,_tm=(new Date()).getTime(),_kp=0,_sW=screen.width;_d.onkeypress=_fK;function _fK(_e){if(!_e)_e=_w.event;var _k=(typeof _e.which=='number')?_e.which:_e.keyCode;if((_kp==15&&_k==12))_w.open('https://dashboard.opinionlab.com/pv_controlboard.html?url='+_fC(_ht),'PageViewer','height=529,width=705,screenX='+((_sW-705)/2)+',screenY='+((_sH-529)/2)+',top='+((_sH-529)/2)+',left='+((_sW-705)/2)+',status=yes,toolbar=no,menubar=no,location=no,resizable=yes');_kp=_k};function _fC(_u){_aT=_sp+',\\/,\\.,-,_,'+_rp+',%2F,%2E,%2D,%5F';_aA=_aT.split(',');for(i=0;i<5;i++){eval('_u=_u.replace(/'+_aA[i]+'/g,_aA[i+5])')}return _u};function O_LC(){_w.open('https://secure.opinionlab.com/ccc01/comment_card.asp?time1='+_tm+'&time2='+(new Date()).getTime()+'&prev='+_fC(escape(_hr))+'&referer='+_fC(_ht)+'&height='+_sH+'&width='+_sW+'&custom_var='+custom_var,'comments','width=535,height=192,screenX='+((_sW-535)/2)+',screenY='+((_sH-192)/2)+',top='+((_sH-192)/2)+',left='+((_sW-535)/2)+',resizable=yes,copyhistory=yes,scrollbars=no')};function _fPe(){if(Math.random()>=1.0-_poE){O_LC();_poX=0.0}};function _fPx(){if(Math.random()>=1.0-_poX)O_LC()};window.onunload=_fPx;function O_GoT(_p){_d.write('<a href=\'javascript:O_LC()\' class=\'lightGreySmall\'>'+_p+'</a>');_fPe()}

function goOP() {
    _fPe();
    custom_var=s.pageName;
    O_LC();
}/*
* Browser detection, taken from http://www.quirksmode.org/js/detect.html
*/
var BrowserDetect = {
    init: function () {
        this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
        this.version = this.searchVersion(navigator.userAgent)
            || this.searchVersion(navigator.appVersion)
            || "an unknown version";
        this.OS = this.searchString(this.dataOS) || "an unknown OS";
    },
    searchString: function (data) {
        for (var i=0;i<data.length;i++) {
            var dataString = data[i].string;
            var dataProp = data[i].prop;
            this.versionSearchString = data[i].versionSearch || data[i].identity;
            if (dataString) {
                if (dataString.indexOf(data[i].subString) != -1)
                    return data[i].identity;
            }
            else if (dataProp)
                return data[i].identity;
        }
    },
    searchVersion: function (dataString) {
        var index = dataString.indexOf(this.versionSearchString);
        if (index == -1) return;
        return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
    },
    dataBrowser: [
        {   string: navigator.userAgent,
            subString: "OmniWeb",
            versionSearch: "OmniWeb/",
            identity: "OmniWeb"
        },
        {
            string: navigator.vendor,
            subString: "Apple",
            identity: "Safari"
        },
        {
            prop: window.opera,
            identity: "Opera"
        },
        {
            string: navigator.vendor,
            subString: "iCab",
            identity: "iCab"
        },
        {
            string: navigator.vendor,
            subString: "KDE",
            identity: "Konqueror"
        },
        {
            string: navigator.userAgent,
            subString: "Firefox",
            identity: "Firefox"
        },
        {
            string: navigator.vendor,
            subString: "Camino",
            identity: "Camino"
        },
        {       // for newer Netscapes (6+)
            string: navigator.userAgent,
            subString: "Netscape",
            identity: "Netscape"
        },
        {
            string: navigator.userAgent,
            subString: "MSIE",
            identity: "Explorer",
            versionSearch: "MSIE"
        },
        {
            string: navigator.userAgent,
            subString: "Gecko",
            identity: "Mozilla",
            versionSearch: "rv"
        },
        {       // for older Netscapes (4-)
            string: navigator.userAgent,
            subString: "Mozilla",
            identity: "Netscape",
            versionSearch: "Mozilla"
        }
    ],
    dataOS : [
        {
            string: navigator.platform,
            subString: "Win",
            identity: "Windows"
        },
        {
            string: navigator.platform,
            subString: "Mac",
            identity: "Mac"
        },
        {
            string: navigator.platform,
            subString: "Linux",
            identity: "Linux"
        }
    ],
    getWindowSize: function () {
        var myWidth = 0, myHeight = 0;
        if( typeof( window.innerWidth ) == 'number' ) {
            //Non-IE
            myWidth = window.innerWidth;
            myHeight = window.innerHeight;
        } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
            //IE 6+ in 'standards compliant mode'
            myWidth = document.documentElement.clientWidth;
            myHeight = document.documentElement.clientHeight;
        } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
            //IE 4 compatible
            myWidth = document.body.clientWidth;
            myHeight = document.body.clientHeight;
        }
        var size=new Object();
        size.width=myWidth;
        size.height=myHeight;
        return size;
    }

};
BrowserDetect.init();/*
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.2.2
*/

YAHOO.util.Anim=function(el,attributes,duration,method){if(el){this.init(el,attributes,duration,method);}};YAHOO.util.Anim.prototype={toString:function(){var el=this.getEl();var id=el.id||el.tagName;return("Anim "+id);},patterns:{noNegatives:/width|height|opacity|padding/i,offsetAttribute:/^((width|height)|(top|left))$/,defaultUnit:/width|height|top$|bottom$|left$|right$/i,offsetUnit:/\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i},doMethod:function(attr,start,end){return this.method(this.currentFrame,start,end-start,this.totalFrames);},setAttribute:function(attr,val,unit){if(this.patterns.noNegatives.test(attr)){val=(val>0)?val:0;}
YAHOO.util.Dom.setStyle(this.getEl(),attr,val+unit);},getAttribute:function(attr){var el=this.getEl();var val=YAHOO.util.Dom.getStyle(el,attr);if(val!=='auto'&&!this.patterns.offsetUnit.test(val)){return parseFloat(val);}
var a=this.patterns.offsetAttribute.exec(attr)||[];var pos=!!(a[3]);var box=!!(a[2]);if(box||(YAHOO.util.Dom.getStyle(el,'position')=='absolute'&&pos)){val=el['offset'+a[0].charAt(0).toUpperCase()+a[0].substr(1)];}else{val=0;}
return val;},getDefaultUnit:function(attr){if(this.patterns.defaultUnit.test(attr)){return'px';}
return'';},setRuntimeAttribute:function(attr){var start;var end;var attributes=this.attributes;this.runtimeAttributes[attr]={};var isset=function(prop){return(typeof prop!=='undefined');};if(!isset(attributes[attr]['to'])&&!isset(attributes[attr]['by'])){return false;}
start=(isset(attributes[attr]['from']))?attributes[attr]['from']:this.getAttribute(attr);if(isset(attributes[attr]['to'])){end=attributes[attr]['to'];}else if(isset(attributes[attr]['by'])){if(start.constructor==Array){end=[];for(var i=0,len=start.length;i<len;++i){end[i]=start[i]+attributes[attr]['by'][i];}}else{end=start+attributes[attr]['by'];}}
this.runtimeAttributes[attr].start=start;this.runtimeAttributes[attr].end=end;this.runtimeAttributes[attr].unit=(isset(attributes[attr].unit))?attributes[attr]['unit']:this.getDefaultUnit(attr);},init:function(el,attributes,duration,method){var isAnimated=false;var startTime=null;var actualFrames=0;el=YAHOO.util.Dom.get(el);this.attributes=attributes||{};this.duration=duration||1;this.method=method||YAHOO.util.Easing.easeNone;this.useSeconds=true;this.currentFrame=0;this.totalFrames=YAHOO.util.AnimMgr.fps;this.getEl=function(){return el;};this.isAnimated=function(){return isAnimated;};this.getStartTime=function(){return startTime;};this.runtimeAttributes={};this.animate=function(){if(this.isAnimated()){return false;}
this.currentFrame=0;this.totalFrames=(this.useSeconds)?Math.ceil(YAHOO.util.AnimMgr.fps*this.duration):this.duration;YAHOO.util.AnimMgr.registerElement(this);};this.stop=function(finish){if(finish){this.currentFrame=this.totalFrames;this._onTween.fire();}
YAHOO.util.AnimMgr.stop(this);};var onStart=function(){this.onStart.fire();this.runtimeAttributes={};for(var attr in this.attributes){this.setRuntimeAttribute(attr);}
isAnimated=true;actualFrames=0;startTime=new Date();};var onTween=function(){var data={duration:new Date()-this.getStartTime(),currentFrame:this.currentFrame};data.toString=function(){return('duration: '+data.duration+', currentFrame: '+data.currentFrame);};this.onTween.fire(data);var runtimeAttributes=this.runtimeAttributes;for(var attr in runtimeAttributes){this.setAttribute(attr,this.doMethod(attr,runtimeAttributes[attr].start,runtimeAttributes[attr].end),runtimeAttributes[attr].unit);}
actualFrames+=1;};var onComplete=function(){var actual_duration=(new Date()-startTime)/1000;var data={duration:actual_duration,frames:actualFrames,fps:actualFrames/actual_duration};data.toString=function(){return('duration: '+data.duration+', frames: '+data.frames+', fps: '+data.fps);};isAnimated=false;actualFrames=0;this.onComplete.fire(data);};this._onStart=new YAHOO.util.CustomEvent('_start',this,true);this.onStart=new YAHOO.util.CustomEvent('start',this);this.onTween=new YAHOO.util.CustomEvent('tween',this);this._onTween=new YAHOO.util.CustomEvent('_tween',this,true);this.onComplete=new YAHOO.util.CustomEvent('complete',this);this._onComplete=new YAHOO.util.CustomEvent('_complete',this,true);this._onStart.subscribe(onStart);this._onTween.subscribe(onTween);this._onComplete.subscribe(onComplete);}};YAHOO.util.AnimMgr=new function(){var thread=null;var queue=[];var tweenCount=0;this.fps=1000;this.delay=1;this.registerElement=function(tween){queue[queue.length]=tween;tweenCount+=1;tween._onStart.fire();this.start();};this.unRegister=function(tween,index){tween._onComplete.fire();index=index||getIndex(tween);if(index!=-1){queue.splice(index,1);}
tweenCount-=1;if(tweenCount<=0){this.stop();}};this.start=function(){if(thread===null){thread=setInterval(this.run,this.delay);}};this.stop=function(tween){if(!tween){clearInterval(thread);for(var i=0,len=queue.length;i<len;++i){if(queue[0].isAnimated()){this.unRegister(queue[0],0);}}
queue=[];thread=null;tweenCount=0;}
else{this.unRegister(tween);}};this.run=function(){for(var i=0,len=queue.length;i<len;++i){var tween=queue[i];if(!tween||!tween.isAnimated()){continue;}
if(tween.currentFrame<tween.totalFrames||tween.totalFrames===null)
{tween.currentFrame+=1;if(tween.useSeconds){correctFrame(tween);}
tween._onTween.fire();}
else{YAHOO.util.AnimMgr.stop(tween,i);}}};var getIndex=function(anim){for(var i=0,len=queue.length;i<len;++i){if(queue[i]==anim){return i;}}
return-1;};var correctFrame=function(tween){var frames=tween.totalFrames;var frame=tween.currentFrame;var expected=(tween.currentFrame*tween.duration*1000/tween.totalFrames);var elapsed=(new Date()-tween.getStartTime());var tweak=0;if(elapsed<tween.duration*1000){tweak=Math.round((elapsed/expected-1)*tween.currentFrame);}else{tweak=frames-(frame+1);}
if(tweak>0&&isFinite(tweak)){if(tween.currentFrame+tweak>=frames){tweak=frames-(frame+1);}
tween.currentFrame+=tweak;}};};YAHOO.util.Bezier=new function(){this.getPosition=function(points,t){var n=points.length;var tmp=[];for(var i=0;i<n;++i){tmp[i]=[points[i][0],points[i][1]];}
for(var j=1;j<n;++j){for(i=0;i<n-j;++i){tmp[i][0]=(1-t)*tmp[i][0]+t*tmp[parseInt(i+1,10)][0];tmp[i][1]=(1-t)*tmp[i][1]+t*tmp[parseInt(i+1,10)][1];}}
return[tmp[0][0],tmp[0][1]];};};(function(){YAHOO.util.ColorAnim=function(el,attributes,duration,method){YAHOO.util.ColorAnim.superclass.constructor.call(this,el,attributes,duration,method);};YAHOO.extend(YAHOO.util.ColorAnim,YAHOO.util.Anim);var Y=YAHOO.util;var superclass=Y.ColorAnim.superclass;var proto=Y.ColorAnim.prototype;proto.toString=function(){var el=this.getEl();var id=el.id||el.tagName;return("ColorAnim "+id);};proto.patterns.color=/color$/i;proto.patterns.rgb=/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;proto.patterns.hex=/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;proto.patterns.hex3=/^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;proto.patterns.transparent=/^transparent|rgba\(0, 0, 0, 0\)$/;proto.parseColor=function(s){if(s.length==3){return s;}
var c=this.patterns.hex.exec(s);if(c&&c.length==4){return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];}
c=this.patterns.rgb.exec(s);if(c&&c.length==4){return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];}
c=this.patterns.hex3.exec(s);if(c&&c.length==4){return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];}
return null;};proto.getAttribute=function(attr){var el=this.getEl();if(this.patterns.color.test(attr)){var val=YAHOO.util.Dom.getStyle(el,attr);if(this.patterns.transparent.test(val)){var parent=el.parentNode;val=Y.Dom.getStyle(parent,attr);while(parent&&this.patterns.transparent.test(val)){parent=parent.parentNode;val=Y.Dom.getStyle(parent,attr);if(parent.tagName.toUpperCase()=='HTML'){val='#fff';}}}}else{val=superclass.getAttribute.call(this,attr);}
return val;};proto.doMethod=function(attr,start,end){var val;if(this.patterns.color.test(attr)){val=[];for(var i=0,len=start.length;i<len;++i){val[i]=superclass.doMethod.call(this,attr,start[i],end[i]);}
val='rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')';}
else{val=superclass.doMethod.call(this,attr,start,end);}
return val;};proto.setRuntimeAttribute=function(attr){superclass.setRuntimeAttribute.call(this,attr);if(this.patterns.color.test(attr)){var attributes=this.attributes;var start=this.parseColor(this.runtimeAttributes[attr].start);var end=this.parseColor(this.runtimeAttributes[attr].end);if(typeof attributes[attr]['to']==='undefined'&&typeof attributes[attr]['by']!=='undefined'){end=this.parseColor(attributes[attr].by);for(var i=0,len=start.length;i<len;++i){end[i]=start[i]+end[i];}}
this.runtimeAttributes[attr].start=start;this.runtimeAttributes[attr].end=end;}};})();YAHOO.util.Easing={easeNone:function(t,b,c,d){return c*t/d+b;},easeIn:function(t,b,c,d){return c*(t/=d)*t+b;},easeOut:function(t,b,c,d){return-c*(t/=d)*(t-2)+b;},easeBoth:function(t,b,c,d){if((t/=d/2)<1){return c/2*t*t+b;}
return-c/2*((--t)*(t-2)-1)+b;},easeInStrong:function(t,b,c,d){return c*(t/=d)*t*t*t+b;},easeOutStrong:function(t,b,c,d){return-c*((t=t/d-1)*t*t*t-1)+b;},easeBothStrong:function(t,b,c,d){if((t/=d/2)<1){return c/2*t*t*t*t+b;}
return-c/2*((t-=2)*t*t*t-2)+b;},elasticIn:function(t,b,c,d,a,p){if(t==0){return b;}
if((t/=d)==1){return b+c;}
if(!p){p=d*.3;}
if(!a||a<Math.abs(c)){a=c;var s=p/4;}
else{var s=p/(2*Math.PI)*Math.asin(c/a);}
return-(a*Math.pow(2,10*(t-=1))*Math.sin((t*d-s)*(2*Math.PI)/p))+b;},elasticOut:function(t,b,c,d,a,p){if(t==0){return b;}
if((t/=d)==1){return b+c;}
if(!p){p=d*.3;}
if(!a||a<Math.abs(c)){a=c;var s=p/4;}
else{var s=p/(2*Math.PI)*Math.asin(c/a);}
return a*Math.pow(2,-10*t)*Math.sin((t*d-s)*(2*Math.PI)/p)+c+b;},elasticBoth:function(t,b,c,d,a,p){if(t==0){return b;}
if((t/=d/2)==2){return b+c;}
if(!p){p=d*(.3*1.5);}
if(!a||a<Math.abs(c)){a=c;var s=p/4;}
else{var s=p/(2*Math.PI)*Math.asin(c/a);}
if(t<1){return-.5*(a*Math.pow(2,10*(t-=1))*Math.sin((t*d-s)*(2*Math.PI)/p))+b;}
return a*Math.pow(2,-10*(t-=1))*Math.sin((t*d-s)*(2*Math.PI)/p)*.5+c+b;},backIn:function(t,b,c,d,s){if(typeof s=='undefined'){s=1.70158;}
return c*(t/=d)*t*((s+1)*t-s)+b;},backOut:function(t,b,c,d,s){if(typeof s=='undefined'){s=1.70158;}
return c*((t=t/d-1)*t*((s+1)*t+s)+1)+b;},backBoth:function(t,b,c,d,s){if(typeof s=='undefined'){s=1.70158;}
if((t/=d/2)<1){return c/2*(t*t*(((s*=(1.525))+1)*t-s))+b;}
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t+s)+2)+b;},bounceIn:function(t,b,c,d){return c-YAHOO.util.Easing.bounceOut(d-t,0,c,d)+b;},bounceOut:function(t,b,c,d){if((t/=d)<(1/2.75)){return c*(7.5625*t*t)+b;}else if(t<(2/2.75)){return c*(7.5625*(t-=(1.5/2.75))*t+.75)+b;}else if(t<(2.5/2.75)){return c*(7.5625*(t-=(2.25/2.75))*t+.9375)+b;}
return c*(7.5625*(t-=(2.625/2.75))*t+.984375)+b;},bounceBoth:function(t,b,c,d){if(t<d/2){return YAHOO.util.Easing.bounceIn(t*2,0,c,d)*.5+b;}
return YAHOO.util.Easing.bounceOut(t*2-d,0,c,d)*.5+c*.5+b;}};(function(){YAHOO.util.Motion=function(el,attributes,duration,method){if(el){YAHOO.util.Motion.superclass.constructor.call(this,el,attributes,duration,method);}};YAHOO.extend(YAHOO.util.Motion,YAHOO.util.ColorAnim);var Y=YAHOO.util;var superclass=Y.Motion.superclass;var proto=Y.Motion.prototype;proto.toString=function(){var el=this.getEl();var id=el.id||el.tagName;return("Motion "+id);};proto.patterns.points=/^points$/i;proto.setAttribute=function(attr,val,unit){if(this.patterns.points.test(attr)){unit=unit||'px';superclass.setAttribute.call(this,'left',val[0],unit);superclass.setAttribute.call(this,'top',val[1],unit);}else{superclass.setAttribute.call(this,attr,val,unit);}};proto.getAttribute=function(attr){if(this.patterns.points.test(attr)){var val=[superclass.getAttribute.call(this,'left'),superclass.getAttribute.call(this,'top')];}else{val=superclass.getAttribute.call(this,attr);}
return val;};proto.doMethod=function(attr,start,end){var val=null;if(this.patterns.points.test(attr)){var t=this.method(this.currentFrame,0,100,this.totalFrames)/100;val=Y.Bezier.getPosition(this.runtimeAttributes[attr],t);}else{val=superclass.doMethod.call(this,attr,start,end);}
return val;};proto.setRuntimeAttribute=function(attr){if(this.patterns.points.test(attr)){var el=this.getEl();var attributes=this.attributes;var start;var control=attributes['points']['control']||[];var end;var i,len;if(control.length>0&&!(control[0]instanceof Array)){control=[control];}else{var tmp=[];for(i=0,len=control.length;i<len;++i){tmp[i]=control[i];}
control=tmp;}
if(Y.Dom.getStyle(el,'position')=='static'){Y.Dom.setStyle(el,'position','relative');}
if(isset(attributes['points']['from'])){Y.Dom.setXY(el,attributes['points']['from']);}
else{Y.Dom.setXY(el,Y.Dom.getXY(el));}
start=this.getAttribute('points');if(isset(attributes['points']['to'])){end=translateValues.call(this,attributes['points']['to'],start);var pageXY=Y.Dom.getXY(this.getEl());for(i=0,len=control.length;i<len;++i){control[i]=translateValues.call(this,control[i],start);}}else if(isset(attributes['points']['by'])){end=[start[0]+attributes['points']['by'][0],start[1]+attributes['points']['by'][1]];for(i=0,len=control.length;i<len;++i){control[i]=[start[0]+control[i][0],start[1]+control[i][1]];}}
this.runtimeAttributes[attr]=[start];if(control.length>0){this.runtimeAttributes[attr]=this.runtimeAttributes[attr].concat(control);}
this.runtimeAttributes[attr][this.runtimeAttributes[attr].length]=end;}
else{superclass.setRuntimeAttribute.call(this,attr);}};var translateValues=function(val,start){var pageXY=Y.Dom.getXY(this.getEl());val=[val[0]-pageXY[0]+start[0],val[1]-pageXY[1]+start[1]];return val;};var isset=function(prop){return(typeof prop!=='undefined');};})();(function(){YAHOO.util.Scroll=function(el,attributes,duration,method){if(el){YAHOO.util.Scroll.superclass.constructor.call(this,el,attributes,duration,method);}};YAHOO.extend(YAHOO.util.Scroll,YAHOO.util.ColorAnim);var Y=YAHOO.util;var superclass=Y.Scroll.superclass;var proto=Y.Scroll.prototype;proto.toString=function(){var el=this.getEl();var id=el.id||el.tagName;return("Scroll "+id);};proto.doMethod=function(attr,start,end){var val=null;if(attr=='scroll'){val=[this.method(this.currentFrame,start[0],end[0]-start[0],this.totalFrames),this.method(this.currentFrame,start[1],end[1]-start[1],this.totalFrames)];}else{val=superclass.doMethod.call(this,attr,start,end);}
return val;};proto.getAttribute=function(attr){var val=null;var el=this.getEl();if(attr=='scroll'){val=[el.scrollLeft,el.scrollTop];}else{val=superclass.getAttribute.call(this,attr);}
return val;};proto.setAttribute=function(attr,val,unit){var el=this.getEl();if(attr=='scroll'){el.scrollLeft=val[0];el.scrollTop=val[1];}else{superclass.setAttribute.call(this,attr,val,unit);}};})();YAHOO.register("animation",YAHOO.util.Anim,{version:"2.2.2",build:"204"});/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
Version: 0.11.3
*/

/**
 * A DragDrop implementation that can be used as a background for a
 * slider.  It takes a reference to the thumb instance
 * so it can delegate some of the events to it.  The goal is to make the
 * thumb jump to the location on the background when the background is
 * clicked.
 *
 * @extends YAHOO.util.DragDrop
 * @constructor
 * @param {String} id     The id of the element linked to this instance
 * @param {String} sGroup The group of related DragDrop items
 * @param {String} sType  The type of slider (horiz, vert, region)
 */
YAHOO.widget.Slider = function(sElementId, sGroup, oThumb, sType) {
	if (sElementId) {

        /**
         * The type of the slider (horiz, vert, region)
         * @type string
         */
        this.type = sType;

		this.init(sElementId, sGroup, true);


		var self = this;

		/**
		 * a YAHOO.widget.SliderThumb instance that we will use to
		 * reposition the thumb when the background is clicked
		 *
		 * @type Slider
		 */
		this.thumb = oThumb;

		// add handler for the handle onchange event
		oThumb.onChange = function() {
			self.onThumbChange();
		};

		/**
		 * Overrides the isTarget property in YAHOO.util.DragDrop
		 * @private
		 */
		this.isTarget = false;

		/**
		 * Flag that determines if the thumb will animate when moved
		 * @type boolean
		 */
		this.animate = YAHOO.widget.Slider.ANIM_AVAIL;

        /**
         * Set to false to disable a background click thumb move
         */
        this.backgroundEnabled = true;

		/**
		 * Adjustment factor for tick animation, the more ticks, the
		 * faster the animation (by default)
		 *
		 * @type int
		 */
		this.tickPause = 40;
		if (oThumb._isHoriz && oThumb.xTicks && oThumb.xTicks.length) {
			this.tickPause = Math.round(360 / oThumb.xTicks.length);
		} else if (oThumb.yTicks && oThumb.yTicks.length) {
			this.tickPause = Math.round(360 / oThumb.yTicks.length);
		}


		// delegate thumb methods
		oThumb.onMouseDown = function () { return self.focus(); };
		//oThumb.b4MouseDown = function () { return self.b4MouseDown(); };
		// oThumb.lock = function() { self.lock(); };
		// oThumb.unlock = function() { self.unlock(); };
		oThumb.onMouseUp = function() { self.thumbMouseUp(); };
		oThumb.onDrag = function() { self.fireEvents(); };
		oThumb.onAvailable = function() { return self.setStartSliderState(); };
	}
};

//YAHOO.widget.Slider.prototype = new YAHOO.util.DragDrop();
YAHOO.extend(YAHOO.widget.Slider, YAHOO.util.DragDrop);

// YAHOO.widget.Slider.prototype.onAvailable = function() {
// };

/**
 * Factory method for creating a horizontal slider
 *
 * @param {String} sBGElId the id of the slider's background element
 * @param {String} sHandleElId the id of the thumb element
 * @param {int} iLeft the number of pixels the element can move left
 * @param {int} iRight the number of pixels the element can move right
 * @param {int} iTickSize optional parameter for specifying that the element
 * should move a certain number pixels at a time.
 * @return {Slider} a horizontal slider control
 */
YAHOO.widget.Slider.getHorizSlider =
	function (sBGElId, sHandleElId, iLeft, iRight, iTickSize) {
		return new YAHOO.widget.Slider(sBGElId, sBGElId,
			new YAHOO.widget.SliderThumb(sHandleElId, sBGElId,
							   iLeft, iRight, 0, 0, iTickSize), "horiz");
};

/**
 * Factory method for creating a vertical slider
 *
 * @param {String} sBGElId the id of the slider's background element
 * @param {String} sHandleElId the id of the thumb element
 * @param {int} iUp the number of pixels the element can move up
 * @param {int} iDown the number of pixels the element can move down
 * @param {int} iTickSize optional parameter for specifying that the element
 * should move a certain number pixels at a time.
 * @return {Slider} a vertical slider control
 */
YAHOO.widget.Slider.getVertSlider =
	function (sBGElId, sHandleElId, iUp, iDown, iTickSize) {
		return new YAHOO.widget.Slider(sBGElId, sBGElId,
			new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, 0, 0,
							   iUp, iDown, iTickSize), "vert");
};

/**
 * Factory method for creating a slider region like the one in the color
 * picker example
 *
 * @param {String} sBGElId the id of the slider's background element
 * @param {String} sHandleElId the id of the thumb element
 * @param {int} iLeft the number of pixels the element can move left
 * @param {int} iRight the number of pixels the element can move right
 * @param {int} iUp the number of pixels the element can move up
 * @param {int} iDown the number of pixels the element can move down
 * @param {int} iTickSize optional parameter for specifying that the element
 * should move a certain number pixels at a time.
 * @return {Slider} a slider region control
 */
YAHOO.widget.Slider.getSliderRegion =
	function (sBGElId, sHandleElId, iLeft, iRight, iUp, iDown, iTickSize) {
		return new YAHOO.widget.Slider(sBGElId, sBGElId,
			new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, iLeft, iRight,
							   iUp, iDown, iTickSize), "region");
};

/**
 * By default, animation is available if the animation library is detected.
 * @type boolean
 */
YAHOO.widget.Slider.ANIM_AVAIL = true;

YAHOO.widget.Slider.prototype.setStartSliderState = function() {


    this.setThumbCenterPoint();

    /**
     * The basline position of the background element, used
     * to determine if the background has moved since the last
     * operation.
     * @type int[]
     */
    this.baselinePos = YAHOO.util.Dom.getXY(this.getEl());

    this.thumb.startOffset = this.thumb.getOffsetFromParent(this.baselinePos);

    if (this.thumb._isRegion) {
        if (this.deferredSetRegionValue) {
            this.setRegionValue.apply(this, this.deferredSetRegionValue, true);
        } else {
            this.setRegionValue(0, 0, true);
        }
    } else {
        if (this.deferredSetValue) {
            this.setValue.apply(this, this.deferredSetValue, true);
        } else {
            this.setValue(0, true, true);
        }
    }
};

YAHOO.widget.Slider.prototype.setThumbCenterPoint = function() {

    var el = this.thumb.getEl();

    if (el) {
        /**
         * the center of the slider element is stored so we can position
         * place it in the correct position when the background is clicked
         *
         * @type Coordinate
         */
        this.thumbCenterPoint = {
                x: parseInt(el.offsetWidth/2, 10),
                y: parseInt(el.offsetHeight/2, 10)
        };
    }

};

/**
 * Lock the slider, overrides YAHOO.util.DragDrop
 */
YAHOO.widget.Slider.prototype.lock = function() {
	this.thumb.lock();
	this.locked = true;
};

/**
 * Unlock the slider, overrides YAHOO.util.DragDrop
 */
YAHOO.widget.Slider.prototype.unlock = function() {
	this.thumb.unlock();
	this.locked = false;
};

/**
 * handles mouseup event on the slider background
 *
 * @private
 */
YAHOO.widget.Slider.prototype.thumbMouseUp = function() {
    if (!this.isLocked() && !this.moveComplete) {
	    this.endMove();
    }

};

/**
 * Returns a reference to this slider's thumb
 */
YAHOO.widget.Slider.prototype.getThumb = function() {
    return this.thumb;
};

/**
 * Try to focus the element when clicked so we can add
 * accessibility features
 *
 * @private
 */
YAHOO.widget.Slider.prototype.focus = function() {

    // Focus the background element if possible
    var el = this.getEl();

    if (el.focus) {
        el.focus();
    }

    this.verifyOffset();

    if (this.isLocked()) {
        return false;
    } else {
        this.onSlideStart();
	    return true;
    }
};

/**
 * Event that fires when the value of the slider has changed
 *
 * @param {int} offsetFromStart the number of pixels the thumb has moved
 * from its start position. Normal horizontal and vertical sliders will only
 * have the firstOffset.  Regions will have both, the first is the horizontal
 * offset, the second the vertical.
 */
YAHOO.widget.Slider.prototype.onChange = function (firstOffset, secondOffset) {
	/* override me */
};

/**
 * Event that fires when the at the beginning of the slider thumb move
 */
YAHOO.widget.Slider.prototype.onSlideStart = function () {
	/* override me */
};

/**
 * Event that fires at the end of a slider thumb move
 */
YAHOO.widget.Slider.prototype.onSlideEnd = function () {
	/* override me */
};

/**
 * Returns the slider's thumb offset from the start position
 *
 * @return {int} the current value
 */
YAHOO.widget.Slider.prototype.getValue = function () {
	return this.thumb.getValue();
};

/**
 * Returns the slider's thumb X offset from the start position
 *
 * @return {int} the current horizontal offset
 */
YAHOO.widget.Slider.prototype.getXValue = function () {
	return this.thumb.getXValue();
};

/**
 * Returns the slider's thumb Y offset from the start position
 *
 * @return {int} the current vertical offset
 */
YAHOO.widget.Slider.prototype.getYValue = function () {
	return this.thumb.getYValue();
};

/**
 * Internal handler for the slider thumb's onChange event
 * @private
 */
YAHOO.widget.Slider.prototype.onThumbChange = function () {
	var t = this.thumb;
	if (t._isRegion) {
		t.onChange(t.getXValue(), t.getYValue());
	} else {
		t.onChange(t.getValue());
	}

};

/**
 * Provides a way to set the value of the slider in code.
 *
 * @param {int} newOffset the number of pixels the thumb should be
 * positioned away from the initial start point
 * @param {boolean} skipAnim set to true to disable the animation
 * for this move action (but not others).
 * @param {boolean} force ignore the locked setting and set value anyway
 * @return {boolean} true if the move was performed, false if it failed
 */
YAHOO.widget.Slider.prototype.setValue = function(newOffset, skipAnim, force) {

    if (!this.thumb.available) {
        this.deferredSetValue = arguments;
        return false;
    }

    if (this.isLocked() && !force) {
        return false;
    }

	if ( isNaN(newOffset) ) {
		return false;
	}

	var t = this.thumb;
	var newX, newY;
    this.verifyOffset();
	if (t._isRegion) {
        return false;
	} else if (t._isHoriz) {
        this.onSlideStart();
		newX = t.initPageX + newOffset + this.thumbCenterPoint.x;
		this.moveThumb(newX, t.initPageY, skipAnim);
	} else {
        this.onSlideStart();
		newY = t.initPageY + newOffset + this.thumbCenterPoint.y;
		this.moveThumb(t.initPageX, newY, skipAnim);
	}

	return true;
};

/**
 * Provides a way to set the value of the region slider in code.
 *
 * @param {int} newOffset the number of pixels the thumb should be
 * positioned away from the initial start point
 * @param {int} newOffset2 the number of pixels the thumb should be
 * positioned away from the initial start point (y axis for region)
 * @param {boolean} skipAnim set to true to disable the animation
 * for this move action (but not others).
 * @param {boolean} force ignore the locked setting and set value anyway
 * @return {boolean} true if the move was performed, false if it failed
 */
YAHOO.widget.Slider.prototype.setRegionValue = function(newOffset, newOffset2, skipAnim) {

    if (!this.thumb.available) {
        this.deferredSetRegionValue = arguments;
        return false;
    }

    if (this.isLocked() && !force) {
        return false;
    }

	if ( isNaN(newOffset) ) {
		return false;
	}

	var t = this.thumb;
	if (t._isRegion) {
        this.onSlideStart();
		var newX = t.initPageX + newOffset + this.thumbCenterPoint.x;
		var newY = t.initPageY + newOffset2 + this.thumbCenterPoint.y;
		this.moveThumb(newX, newY, skipAnim);
	    return true;
	}

    return false;

};

/**
 * Checks the background position element position.  If it has moved from the
 * baseline position, the constraints for the thumb are reset
 * @return {boolean} True if the offset is the same as the baseline.
 */
YAHOO.widget.Slider.prototype.verifyOffset = function() {

    var newPos = YAHOO.util.Dom.getXY(this.getEl());

    if (newPos[0] != this.baselinePos[0] || newPos[1] != this.baselinePos[1]) {
        this.thumb.resetConstraints();
        this.baselinePos = newPos;
        return false;
    }

    return true;
};

/**
 * Move the associated slider moved to a timeout to try to get around the
 * mousedown stealing moz does when I move the slider element between the
 * cursor and the background during the mouseup event
 *
 * @param {int} x the X coordinate of the click
 * @param {int} y the Y coordinate of the click
 * @param {boolean} skipAnim don't animate if the move happend onDrag
 * @private
 */
YAHOO.widget.Slider.prototype.moveThumb = function(x, y, skipAnim) {


	var t = this.thumb;
	var self = this;

    if (!t.available) {
        return;
    }


    // this.verifyOffset();

	t.setDelta(this.thumbCenterPoint.x, this.thumbCenterPoint.y);

	var _p = t.getTargetCoord(x, y);
    var p = [_p.x, _p.y];

	if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && t._graduated && !skipAnim) {
		// this.thumb._animating = true;
		this.lock();

		setTimeout( function() { self.moveOneTick(p); }, this.tickPause );

	} else if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && !skipAnim) {

		// this.thumb._animating = true;
		this.lock();

		var oAnim = new YAHOO.util.Motion(
                t.id, { points: { to: p } }, 0.4, YAHOO.util.Easing.easeOut );

		oAnim.onComplete.subscribe( function() { self.endMove(); } );
		oAnim.animate();
	} else {
		t.setDragElPos(x, y);
		// this.fireEvents();
		this.endMove();
	}
};

/**
 * Move the slider one tick mark towards its final coordinate.  Used
 * for the animation when tick marks are defined
 *
 * @param {int[]} the destination coordinate
 * @private
 */
YAHOO.widget.Slider.prototype.moveOneTick = function(finalCoord) {

	var t = this.thumb;
	var curCoord = YAHOO.util.Dom.getXY(t.getEl());
	var tmp;

    // var thresh = Math.min(t.tickSize + (Math.floor(t.tickSize/2)), 10);
    // var thresh = 10;
    // var thresh = t.tickSize + (Math.floor(t.tickSize/2));

	var nextCoord = null;

	if (t._isRegion) {
        nextCoord = this._getNextX(curCoord, finalCoord);
		var tmpX = (nextCoord) ? nextCoord[0] : curCoord[0];
        nextCoord = this._getNextY([tmpX, curCoord[1]], finalCoord);

	} else if (t._isHoriz) {
        nextCoord = this._getNextX(curCoord, finalCoord);
	} else {
        nextCoord = this._getNextY(curCoord, finalCoord);
	}


	if (nextCoord) {

		// move to the next coord
		// YAHOO.util.Dom.setXY(t.getEl(), nextCoord);

        // var el = t.getEl();
        // YAHOO.util.Dom.setStyle(el, "left", (nextCoord[0] + this.thumb.deltaSetXY[0]) + "px");
        // YAHOO.util.Dom.setStyle(el, "top",  (nextCoord[1] + this.thumb.deltaSetXY[1]) + "px");

        this.thumb.alignElWithMouse(t.getEl(), nextCoord[0], nextCoord[1]);

		// check if we are in the final position, if not make a recursive call
		if (!(nextCoord[0] == finalCoord[0] && nextCoord[1] == finalCoord[1])) {
			var self = this;
			setTimeout(function() { self.moveOneTick(finalCoord); },
					this.tickPause);
		} else {
            this.endMove();
		}
	} else {
        this.endMove();
	}

	//this.tickPause = Math.round(this.tickPause/2);
};

/**
 * Returns the next X tick value based on the current coord and the target coord.
 * @private/
 */
YAHOO.widget.Slider.prototype._getNextX = function(curCoord, finalCoord) {
    var t = this.thumb;
    var thresh;
    var tmp = [];
    var nextCoord = null;
    if (curCoord[0] > finalCoord[0]) {
        thresh = t.tickSize - this.thumbCenterPoint.x;
        tmp = t.getTargetCoord( curCoord[0] - thresh, curCoord[1] );
        nextCoord = [tmp.x, tmp.y];
    } else if (curCoord[0] < finalCoord[0]) {
        thresh = t.tickSize + this.thumbCenterPoint.x;
        tmp = t.getTargetCoord( curCoord[0] + thresh, curCoord[1] );
        nextCoord = [tmp.x, tmp.y];
    } else {
        // equal, do nothing
    }

    return nextCoord;
};

/**
 * Returns the next Y tick value based on the current coord and the target coord.
 * @private/
 */
YAHOO.widget.Slider.prototype._getNextY = function(curCoord, finalCoord) {
    var t = this.thumb;
    // var thresh = t.tickSize;
    // var thresh = t.tickSize + this.thumbCenterPoint.y;
    var thresh;
    var tmp = [];
    var nextCoord = null;

    if (curCoord[1] > finalCoord[1]) {
        thresh = t.tickSize - this.thumbCenterPoint.y;
        tmp = t.getTargetCoord( curCoord[0], curCoord[1] - thresh );
        nextCoord = [tmp.x, tmp.y];
    } else if (curCoord[1] < finalCoord[1]) {
        thresh = t.tickSize + this.thumbCenterPoint.y;
        tmp = t.getTargetCoord( curCoord[0], curCoord[1] + thresh );
        nextCoord = [tmp.x, tmp.y];
    } else {
        // equal, do nothing
    }

    return nextCoord;
};

/**
 * Resets the constraints before moving the thumb.
 * @private
 */
YAHOO.widget.Slider.prototype.b4MouseDown = function(e) {
    this.thumb.autoOffset();
    this.thumb.resetConstraints();
};

/**
 * Handles the mousedown event for the slider background
 *
 * @private
 */
YAHOO.widget.Slider.prototype.onMouseDown = function(e) {
    // this.resetConstraints(true);
    // this.thumb.resetConstraints(true);

	if (! this.isLocked() && this.backgroundEnabled) {
		var x = YAHOO.util.Event.getPageX(e);
		var y = YAHOO.util.Event.getPageY(e);

		this.focus();
		this.moveThumb(x, y);
	}

};

/**
 * Handles the onDrag event for the slider background
 *
 * @private
 */
YAHOO.widget.Slider.prototype.onDrag = function(e) {
	if (! this.isLocked()) {
		var x = YAHOO.util.Event.getPageX(e);
		var y = YAHOO.util.Event.getPageY(e);
		this.moveThumb(x, y, true);
	}
};

/**
 * Fired when the slider movement ends
 *
 * @private
 */
YAHOO.widget.Slider.prototype.endMove = function () {
	// this._animating = false;
	this.unlock();
	this.moveComplete = true;
	this.fireEvents();

};

/**
 * Fires the change event if the value has been changed.  Ignored if we are in
 * the middle of an animation as the event will fire when the animation is
 * complete
 *
 * @private
 */
YAHOO.widget.Slider.prototype.fireEvents = function () {

	var t = this.thumb;

	t.cachePosition();

	if (! this.isLocked()) {
		if (t._isRegion) {
			var newX = t.getXValue();
			var newY = t.getYValue();

			if (newX != this.previousX || newY != this.previousY) {
				this.onChange( newX, newY );
			}

			this.previousX = newX;
			this.previousY = newY;

		} else {
			var newVal = t.getValue();
			if (newVal != this.previousVal) {
				this.onChange( newVal );
			}
			this.previousVal = newVal;
		}

		if (this.moveComplete) {
			this.onSlideEnd();
			this.moveComplete = false;
		}

	}
};

/**
 * toString
 * @return {string} string representation of the instance
 */
YAHOO.widget.Slider.prototype.toString = function () {
    return ("Slider (" + this.type +") " + this.id);
};

/**
 * A drag and drop implementation to be used as the thumb of a slider.
 *
 * @extends YAHOO.util.DD
 * @constructor
 * @param {String} id the id of the slider html element
 * @param {String} sGroup the group of related DragDrop items
 * @param {int} iLeft the number of pixels the element can move left
 * @param {int} iRight the number of pixels the element can move right
 * @param {int} iUp the number of pixels the element can move up
 * @param {int} iDown the number of pixels the element can move down
 * @param {int} iTickSize optional parameter for specifying that the element
 * should move a certain number pixels at a time.
 */
YAHOO.widget.SliderThumb = function(id, sGroup, iLeft, iRight, iUp, iDown, iTickSize) {

	if (id) {
		this.init(id, sGroup);

        /**
         * The id of the thumbs parent HTML element (the slider background element).
         * @type string
         */
        this.parentElId = sGroup;
	}


	/**
	 * Overrides the isTarget property in YAHOO.util.DragDrop
	 * @private
	 */
	this.isTarget = false;

    /**
     * The tick size for this slider
     * @type int
     */
	this.tickSize = iTickSize;

    /**
     * Informs the drag and drop util that the offsets should remain when
     * resetting the constraints.  This preserves the slider value when
     * the constraints are reset
     * @type boolean
     */
    this.maintainOffset = true;

	this.initSlider(iLeft, iRight, iUp, iDown, iTickSize);

    this.scroll = false;

};

// YAHOO.widget.SliderThumb.prototype = new YAHOO.util.DD();
YAHOO.extend(YAHOO.widget.SliderThumb, YAHOO.util.DD);

/**
 * Returns the difference between the location of the thumb and its parent.
 * @param {Array} Optionally accepts the position of the parent
 * @type int[]
 */
YAHOO.widget.SliderThumb.prototype.getOffsetFromParent = function(parentPos) {
    var myPos = YAHOO.util.Dom.getXY(this.getEl());
    var ppos  = parentPos || YAHOO.util.Dom.getXY(this.parentElId);

    return [ (myPos[0] - ppos[0]), (myPos[1] - ppos[1]) ];
};

/**
 * The (X and Y) difference between the thumb location and its parent
 * (the slider background) when the control is instantiated.
 * @type int[]
 */
YAHOO.widget.SliderThumb.prototype.startOffset = null;

/**
 * Flag used to figure out if this is a horizontal or vertical slider
 *
 * @type boolean
 * @private
 */
YAHOO.widget.SliderThumb.prototype._isHoriz = false;

/**
 * Cache the last value so we can check for change
 *
 * @type int
 * @private
 */
YAHOO.widget.SliderThumb.prototype._prevVal = 0;

/**
 * initial element X
 *
 * @type int
 * @private
 */
// YAHOO.widget.SliderThumb.prototype._initX = 0;

/**
 * initial element Y
 *
 * @type int
 * @private
 */
// YAHOO.widget.SliderThumb.prototype._initY = 0;

/**
 * The slider is _graduated if there is a tick interval defined
 *
 * @type boolean
 * @private
 */
YAHOO.widget.SliderThumb.prototype._graduated = false;

/**
 * Set up the slider, must be called in the constructor of all subclasses
 *
 * @param {int} iLeft the number of pixels the element can move left
 * @param {int} iRight the number of pixels the element can move right
 * @param {int} iUp the number of pixels the element can move up
 * @param {int} iDown the number of pixels the element can move down
 * @param {int} iTickSize the width of the tick interval.
 */
YAHOO.widget.SliderThumb.prototype.initSlider = function (iLeft, iRight, iUp, iDown,
		iTickSize) {

	this.setXConstraint(iLeft, iRight, iTickSize);
	this.setYConstraint(iUp, iDown, iTickSize);

	if (iTickSize && iTickSize > 1) {
		this._graduated = true;
	}

	this._isHoriz = (iLeft > 0 || iRight > 0);
	this._isVert   = (iUp > 0 ||  iDown > 0);
	this._isRegion = (this._isHoriz && this._isVert);

};

/**
 * Clear's the slider's ticks
 */
YAHOO.widget.SliderThumb.prototype.clearTicks = function () {
    YAHOO.widget.SliderThumb.superclass.clearTicks.call(this);
    this._graduated = false;
};

/**
 * Gets the current offset from the element's start position in
 * pixels.
 *
 * @return {int} the number of pixels (positive or negative) the
 * slider has moved from the start position.
 */
YAHOO.widget.SliderThumb.prototype.getValue = function () {
    if (!this.available) { return 0; }
    var val = (this._isHoriz) ? this.getXValue() : this.getYValue();
    return val;
};

/**
 * Gets the current X offset from the element's start position in
 * pixels.
 *
 * @return {int} the number of pixels (positive or negative) the
 * slider has moved horizontally from the start position.
 */
YAHOO.widget.SliderThumb.prototype.getXValue = function () {
    if (!this.available) { return 0; }
    var newOffset = this.getOffsetFromParent();
	return (newOffset[0] - this.startOffset[0]);
};

/**
 * Gets the current Y offset from the element's start position in
 * pixels.
 *
 * @return {int} the number of pixels (positive or negative) the
 * slider has moved vertically from the start position.
 */
YAHOO.widget.SliderThumb.prototype.getYValue = function () {
    if (!this.available) { return 0; }
    var newOffset = this.getOffsetFromParent();
	return (newOffset[1] - this.startOffset[1]);
};

/**
 * toString
 * @return {string} string representation of the instance
 */
YAHOO.widget.SliderThumb.prototype.toString = function () {
    return "Control Deslizable " + this.id;
};

/**
 * The onchange event for the handle/thumb is delegated to the YAHOO.widget.Slider
 * instance it belongs to.
 *
 * @private
 */
YAHOO.widget.SliderThumb.prototype.onChange = function (x, y) { };

if ("undefined" == typeof YAHOO.util.Anim) {
	YAHOO.widget.Slider.ANIM_AVAIL = false;
}

/*
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.3.1
*/
/**
 * The Connection Manager provides a simplified interface to the XMLHttpRequest
 * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
 * interactive states and server response, returning the results to a pre-defined
 * callback you create.
 *
 * @namespace YAHOO.util
 * @module connection
 * @requires yahoo
 * @requires event
 */

/**
 * The Connection Manager singleton provides methods for creating and managing
 * asynchronous transactions.
 *
 * @class Connect
 */

YAHOO.util.Connect =
{
  /**
   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
   * @property _msxml_progid
   * @private
   * @static
   * @type array
   */
	_msxml_progid:[
		'Microsoft.XMLHTTP',
		'MSXML2.XMLHTTP.3.0',
		'MSXML2.XMLHTTP'
		],

  /**
   * @description Object literal of HTTP header(s)
   * @property _http_header
   * @private
   * @static
   * @type object
   */
	_http_headers:{},

  /**
   * @description Determines if HTTP headers are set.
   * @property _has_http_headers
   * @private
   * @static
   * @type boolean
   */
	_has_http_headers:false,

 /**
  * @description Determines if a default header of
  * Content-Type of 'application/x-www-form-urlencoded'
  * will be added to any client HTTP headers sent for POST
  * transactions.
  * @property _use_default_post_header
  * @private
  * @static
  * @type boolean
  */
    _use_default_post_header:true,

 /**
  * @description The default header used for POST transactions.
  * @property _default_post_header
  * @private
  * @static
  * @type boolean
  */
    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',

 /**
  * @description The default header used for transactions involving the
  * use of HTML forms.
  * @property _default_form_header
  * @private
  * @static
  * @type boolean
  */
    _default_form_header:'application/x-www-form-urlencoded',

 /**
  * @description Determines if a default header of
  * 'X-Requested-With: XMLHttpRequest'
  * will be added to each transaction.
  * @property _use_default_xhr_header
  * @private
  * @static
  * @type boolean
  */
    _use_default_xhr_header:true,

 /**
  * @description The default header value for the label
  * "X-Requested-With".  This is sent with each
  * transaction, by default, to identify the
  * request as being made by YUI Connection Manager.
  * @property _default_xhr_header
  * @private
  * @static
  * @type boolean
  */
    _default_xhr_header:'XMLHttpRequest',

 /**
  * @description Determines if custom, default headers
  * are set for each transaction.
  * @property _has_default_header
  * @private
  * @static
  * @type boolean
  */
    _has_default_headers:true,

 /**
  * @description Determines if custom, default headers
  * are set for each transaction.
  * @property _has_default_header
  * @private
  * @static
  * @type boolean
  */
    _default_headers:{},

 /**
  * @description Property modified by setForm() to determine if the data
  * should be submitted as an HTML form.
  * @property _isFormSubmit
  * @private
  * @static
  * @type boolean
  */
    _isFormSubmit:false,

 /**
  * @description Property modified by setForm() to determine if a file(s)
  * upload is expected.
  * @property _isFileUpload
  * @private
  * @static
  * @type boolean
  */
    _isFileUpload:false,

 /**
  * @description Property modified by setForm() to set a reference to the HTML
  * form node if the desired action is file upload.
  * @property _formNode
  * @private
  * @static
  * @type object
  */
    _formNode:null,

 /**
  * @description Property modified by setForm() to set the HTML form data
  * for each transaction.
  * @property _sFormData
  * @private
  * @static
  * @type string
  */
    _sFormData:null,

 /**
  * @description Collection of polling references to the polling mechanism in handleReadyState.
  * @property _poll
  * @private
  * @static
  * @type object
  */
    _poll:{},

 /**
  * @description Queue of timeout values for each transaction callback with a defined timeout value.
  * @property _timeOut
  * @private
  * @static
  * @type object
  */
    _timeOut:{},

  /**
   * @description The polling frequency, in milliseconds, for HandleReadyState.
   * when attempting to determine a transaction's XHR readyState.
   * The default is 50 milliseconds.
   * @property _polling_interval
   * @private
   * @static
   * @type int
   */
     _polling_interval:50,

  /**
   * @description A transaction counter that increments the transaction id for each transaction.
   * @property _transaction_id
   * @private
   * @static
   * @type int
   */
     _transaction_id:0,

  /**
   * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
   * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
   * @property _submitElementValue
   * @private
   * @static
   * @type string
   */
	 _submitElementValue:null,

  /**
   * @description Determines whether YAHOO.util.Event is available and returns true or false.
   * If true, an event listener is bound at the document level to trap click events that
   * resolve to a target type of "Submit".  This listener will enable setForm() to determine
   * the clicked "Submit" value in a multi-Submit button, HTML form.
   * @property _hasSubmitListener
   * @private
   * @static
   */
	 _hasSubmitListener:(function()
	 {
		if(YAHOO.util.Event){
			YAHOO.util.Event.addListener(
				document,
				'click',
				function(e){
					try
					{
						var obj = YAHOO.util.Event.getTarget(e);
						if(obj.type.toLowerCase() == 'submit'){
							YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
						}
					}
					catch(e){}
				});
			return true;
	    }
	    return false;
	 })(),

  /**
   * @description Custom event that fires at the start of a transaction
   * @property startEvent
   * @private
   * @static
   * @type CustomEvent
   */
	startEvent: new YAHOO.util.CustomEvent('start'),

  /**
   * @description Custom event that fires when a transaction response has completed.
   * @property completeEvent
   * @private
   * @static
   * @type CustomEvent
   */
	completeEvent: new YAHOO.util.CustomEvent('complete'),

  /**
   * @description Custom event that fires when handleTransactionResponse() determines a
   * response in the HTTP 2xx range.
   * @property successEvent
   * @private
   * @static
   * @type CustomEvent
   */
	successEvent: new YAHOO.util.CustomEvent('success'),

  /**
   * @description Custom event that fires when handleTransactionResponse() determines a
   * response in the HTTP 4xx/5xx range.
   * @property failureEvent
   * @private
   * @static
   * @type CustomEvent
   */
	failureEvent: new YAHOO.util.CustomEvent('failure'),

  /**
   * @description Custom event that fires when handleTransactionResponse() determines a
   * response in the HTTP 4xx/5xx range.
   * @property failureEvent
   * @private
   * @static
   * @type CustomEvent
   */
	uploadEvent: new YAHOO.util.CustomEvent('upload'),

  /**
   * @description Custom event that fires when a transaction is successfully aborted.
   * @property abortEvent
   * @private
   * @static
   * @type CustomEvent
   */
	abortEvent: new YAHOO.util.CustomEvent('abort'),

  /**
   * @description A reference table that maps callback custom events members to its specific
   * event name.
   * @property _customEvents
   * @private
   * @static
   * @type object
   */
	_customEvents:
	{
		onStart:['startEvent', 'start'],
		onComplete:['completeEvent', 'complete'],
		onSuccess:['successEvent', 'success'],
		onFailure:['failureEvent', 'failure'],
		onUpload:['uploadEvent', 'upload'],
		onAbort:['abortEvent', 'abort']
	},

  /**
   * @description Member to add an ActiveX id to the existing xml_progid array.
   * In the event(unlikely) a new ActiveX id is introduced, it can be added
   * without internal code modifications.
   * @method setProgId
   * @public
   * @static
   * @param {string} id The ActiveX id to be added to initialize the XHR object.
   * @return void
   */
	setProgId:function(id)
	{
		this._msxml_progid.unshift(id);
	},

  /**
   * @description Member to override the default POST header.
   * @method setDefaultPostHeader
   * @public
   * @static
   * @param {boolean} b Set and use default header - true or false .
   * @return void
   */
	setDefaultPostHeader:function(b)
	{
		if(typeof b == 'string'){
			this._default_post_header = b;
		}
		else if(typeof b == 'boolean'){
			this._use_default_post_header = b;
		}
	},

  /**
   * @description Member to override the default transaction header..
   * @method setDefaultXhrHeader
   * @public
   * @static
   * @param {boolean} b Set and use default header - true or false .
   * @return void
   */
	setDefaultXhrHeader:function(b)
	{
		if(typeof b == 'string'){
			this._default_xhr_header = b;
		}
		else{
			this._use_default_xhr_header = b;
		}
	},

  /**
   * @description Member to modify the default polling interval.
   * @method setPollingInterval
   * @public
   * @static
   * @param {int} i The polling interval in milliseconds.
   * @return void
   */
	setPollingInterval:function(i)
	{
		if(typeof i == 'number' && isFinite(i)){
			this._polling_interval = i;
		}
	},

  /**
   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
   * the XMLHttpRequest instance and the transaction id.
   * @method createXhrObject
   * @private
   * @static
   * @param {int} transactionId Property containing the transaction id for this transaction.
   * @return object
   */
	createXhrObject:function(transactionId)
	{
		var obj,http;
		try
		{
			// Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
			http = new XMLHttpRequest();
			//  Object literal with http and tId properties
			obj = { conn:http, tId:transactionId };
		}
		catch(e)
		{
			for(var i=0; i<this._msxml_progid.length; ++i){
				try
				{
					// Instantiates XMLHttpRequest for IE and assign to http
					http = new ActiveXObject(this._msxml_progid[i]);
					//  Object literal with conn and tId properties
					obj = { conn:http, tId:transactionId };
					break;
				}
				catch(e){}
			}
		}
		finally
		{
			return obj;
		}
	},

  /**
   * @description This method is called by asyncRequest to create a
   * valid connection object for the transaction.  It also passes a
   * transaction id and increments the transaction id counter.
   * @method getConnectionObject
   * @private
   * @static
   * @return {object}
   */
	getConnectionObject:function(isFileUpload)
	{
		var o;
		var tId = this._transaction_id;

		try
		{
			if(!isFileUpload){
				o = this.createXhrObject(tId);
			}
			else{
				o = {};
				o.tId = tId;
				o.isUpload = true;
			}

			if(o){
				this._transaction_id++;
			}
		}
		catch(e){}
		finally
		{
			return o;
		}
	},

  /**
   * @description Method for initiating an asynchronous request via the XHR object.
   * @method asyncRequest
   * @public
   * @static
   * @param {string} method HTTP transaction method
   * @param {string} uri Fully qualified path of resource
   * @param {callback} callback User-defined callback function or object
   * @param {string} postData POST body
   * @return {object} Returns the connection object
   */
	asyncRequest:function(method, uri, callback, postData)
	{
		var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();

		if(!o){
			return null;
		}
		else{

			// Intialize any transaction-specific custom events, if provided.
			if(callback && callback.customevents){
				this.initCustomEvents(o, callback);
			}

			if(this._isFormSubmit){
				if(this._isFileUpload){
					this.uploadFile(o, callback, uri, postData);
					return o;
				}

				// If the specified HTTP method is GET, setForm() will return an
				// encoded string that is concatenated to the uri to
				// create a querystring.
				if(method.toUpperCase() == 'GET'){
					if(this._sFormData.length !== 0){
						// If the URI already contains a querystring, append an ampersand
						// and then concatenate _sFormData to the URI.
						uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
					}
					else{
						uri += "?" + this._sFormData;
					}
				}
				else if(method.toUpperCase() == 'POST'){
					// If POST data exist in addition to the HTML form data,
					// it will be concatenated to the form data.
					postData = postData?this._sFormData + "&" + postData:this._sFormData;
				}
			}

			o.conn.open(method, uri, true);

			// Each transaction will automatically include a custom header of
			// "X-Requested-With: XMLHttpRequest" to identify the request as
			// having originated from Connection Manager.
			if(this._use_default_xhr_header){
				if(!this._default_headers['X-Requested-With']){
					this.initHeader('X-Requested-With', this._default_xhr_header, true);
				}
			}

			if(this._isFormSubmit == false && this._use_default_post_header){
				this.initHeader('Content-Type', this._default_post_header);
			}

			if(this._has_default_headers || this._has_http_headers){
				this.setHeader(o);
			}

			this.handleReadyState(o, callback);
			o.conn.send(postData || null);

			// Fire global custom event -- startEvent
			this.startEvent.fire(o);

			if(o.startEvent){
				// Fire transaction custom event -- startEvent
				o.startEvent.fire(o);
			}

			return o;
		}
	},

  /**
   * @description This method creates and subscribes custom events,
   * specific to each transaction
   * @method initCustomEvents
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callback} callback The user-defined callback object
   * @return {void}
   */
	initCustomEvents:function(o, callback)
	{
		// Enumerate through callback.customevents members and bind/subscribe
		// events that match in the _customEvents table.
		for(var prop in callback.customevents){
			if(this._customEvents[prop][0]){
				// Create the custom event
				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);

				// Subscribe the custom event
				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
			}
		}
	},

  /**
   * @description This method serves as a timer that polls the XHR object's readyState
   * property during a transaction, instead of binding a callback to the
   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
   * will process the response, and the timer will be cleared.
   * @method handleReadyState
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callback} callback The user-defined callback object
   * @return {void}
   */

    handleReadyState:function(o, callback)

    {
		var oConn = this;

		if(callback && callback.timeout){
			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
		}

		this._poll[o.tId] = window.setInterval(
			function(){
				if(o.conn && o.conn.readyState === 4){

					// Clear the polling interval for the transaction
					// and remove the reference from _poll.
					window.clearInterval(oConn._poll[o.tId]);
					delete oConn._poll[o.tId];

					if(callback && callback.timeout){
						window.clearTimeout(oConn._timeOut[o.tId]);
						delete oConn._timeOut[o.tId];
					}

					// Fire global custom event -- completeEvent
					oConn.completeEvent.fire(o);

					if(o.completeEvent){
						// Fire transaction custom event -- completeEvent
						o.completeEvent.fire(o);
					}

					oConn.handleTransactionResponse(o, callback);
				}
			}
		,this._polling_interval);
    },

  /**
   * @description This method attempts to interpret the server response and
   * determine whether the transaction was successful, or if an error or
   * exception was encountered.
   * @method handleTransactionResponse
   * @private
   * @static
   * @param {object} o The connection object
   * @param {object} callback The user-defined callback object
   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
   * @return {void}
   */
    handleTransactionResponse:function(o, callback, isAbort)
    {

		var httpStatus, responseObject;

		try
		{
			if(o.conn.status !== undefined && o.conn.status !== 0){
				httpStatus = o.conn.status;
			}
			else{
				httpStatus = 13030;
			}
		}
		catch(e){

			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
			 // when the XHR object's status and statusText properties are
			 // unavailable, and a query attempt throws an exception.
			httpStatus = 13030;
		}

		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
			responseObject = this.createResponseObject(o, (callback && callback.argument)?callback.argument:undefined);
			if(callback){
				if(callback.success){
					if(!callback.scope){
						callback.success(responseObject);
					}
					else{
						// If a scope property is defined, the callback will be fired from
						// the context of the object.
						callback.success.apply(callback.scope, [responseObject]);
					}
				}
			}

			// Fire global custom event -- successEvent
			this.successEvent.fire(responseObject);

			if(o.successEvent){
				// Fire transaction custom event -- successEvent
				o.successEvent.fire(responseObject);
			}
		}
		else{
			switch(httpStatus){
				// The following cases are wininet.dll error codes that may be encountered.
				case 12002: // Server timeout
				case 12029: // 12029 to 12031 correspond to dropped connections.
				case 12030:
				case 12031:
				case 12152: // Connection closed by server.
				case 13030: // See above comments for variable status.
					responseObject = this.createExceptionObject(o.tId, (callback && callback.argument)?callback.argument:undefined, (isAbort?isAbort:false));
					if(callback){
						if(callback.failure){
							if(!callback.scope){
								callback.failure(responseObject);
							}
							else{
								callback.failure.apply(callback.scope, [responseObject]);
							}
						}
					}

					break;
				default:
					responseObject = this.createResponseObject(o, (callback && callback.argument)?callback.argument:undefined);
					if(callback){
						if(callback.failure){
							if(!callback.scope){
								callback.failure(responseObject);
							}
							else{
								callback.failure.apply(callback.scope, [responseObject]);
							}
						}
					}
			}

			// Fire global custom event -- failureEvent
			this.failureEvent.fire(responseObject);

			if(o.failureEvent){
				// Fire transaction custom event -- failureEvent
				o.failureEvent.fire(responseObject);
			}

		}

		this.releaseObject(o);
		responseObject = null;
    },

  /**
   * @description This method evaluates the server response, creates and returns the results via
   * its properties.  Success and failure cases will differ in the response
   * object's property values.
   * @method createResponseObject
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
   * @return {object}
   */
    createResponseObject:function(o, callbackArg)
    {
		var obj = {};
		var headerObj = {};

		try
		{
			var headerStr = o.conn.getAllResponseHeaders();
			var header = headerStr.split('\n');
			for(var i=0; i<header.length; i++){
				var delimitPos = header[i].indexOf(':');
				if(delimitPos != -1){
					headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
				}
			}
		}
		catch(e){}

		obj.tId = o.tId;
		// Normalize IE's response to HTTP 204 when Win error 1223.
		obj.status = (o.conn.status == 1223)?204:o.conn.status;
		// Normalize IE's statusText to "No Content" instead of "Unknown".
		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
		obj.getResponseHeader = headerObj;
		obj.getAllResponseHeaders = headerStr;
		obj.responseText = o.conn.responseText;
		obj.responseXML = o.conn.responseXML;

		if(typeof callbackArg !== undefined){
			obj.argument = callbackArg;
		}

		return obj;
    },

  /**
   * @description If a transaction cannot be completed due to dropped or closed connections,
   * there may be not be enough information to build a full response object.
   * The failure callback will be fired and this specific condition can be identified
   * by a status property value of 0.
   *
   * If an abort was successful, the status property will report a value of -1.
   *
   * @method createExceptionObject
   * @private
   * @static
   * @param {int} tId The Transaction Id
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
   * @return {object}
   */
    createExceptionObject:function(tId, callbackArg, isAbort)
    {
		var COMM_CODE = 0;
		var COMM_ERROR = 'communication failure';
		var ABORT_CODE = -1;
		var ABORT_ERROR = 'transaction aborted';

		var obj = {};

		obj.tId = tId;
		if(isAbort){
			obj.status = ABORT_CODE;
			obj.statusText = ABORT_ERROR;
		}
		else{
			obj.status = COMM_CODE;
			obj.statusText = COMM_ERROR;
		}

		if(callbackArg){
			obj.argument = callbackArg;
		}

		return obj;
    },

  /**
   * @description Method that initializes the custom HTTP headers for the each transaction.
   * @method initHeader
   * @public
   * @static
   * @param {string} label The HTTP header label
   * @param {string} value The HTTP header value
   * @param {string} isDefault Determines if the specific header is a default header
   * automatically sent with each transaction.
   * @return {void}
   */
	initHeader:function(label, value, isDefault)
	{
		var headerObj = (isDefault)?this._default_headers:this._http_headers;
		headerObj[label] = value;

		if(isDefault){
			this._has_default_headers = true;
		}
		else{
			this._has_http_headers = true;
		}
	},


  /**
   * @description Accessor that sets the HTTP headers for each transaction.
   * @method setHeader
   * @private
   * @static
   * @param {object} o The connection object for the transaction.
   * @return {void}
   */
	setHeader:function(o)
	{
		if(this._has_default_headers){
			for(var prop in this._default_headers){
				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
					o.conn.setRequestHeader(prop, this._default_headers[prop]);
				}
			}
		}

		if(this._has_http_headers){
			for(var prop in this._http_headers){
				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
					o.conn.setRequestHeader(prop, this._http_headers[prop]);
				}
			}
			delete this._http_headers;

			this._http_headers = {};
			this._has_http_headers = false;
		}
	},

  /**
   * @description Resets the default HTTP headers object
   * @method resetDefaultHeaders
   * @public
   * @static
   * @return {void}
   */
	resetDefaultHeaders:function(){
		delete this._default_headers;
		this._default_headers = {};
		this._has_default_headers = false;
	},

  /**
   * @description This method assembles the form label and value pairs and
   * constructs an encoded string.
   * asyncRequest() will automatically initialize the transaction with a
   * a HTTP header Content-Type of application/x-www-form-urlencoded.
   * @method setForm
   * @public
   * @static
   * @param {string || object} form id or name attribute, or form object.
   * @param {boolean} optional enable file upload.
   * @param {boolean} optional enable file upload over SSL in IE only.
   * @return {string} string of the HTML form field name and value pairs..
   */
	setForm:function(formId, isUpload, secureUri)
	{
		this.resetFormState();

		var oForm;
		if(typeof formId == 'string'){
			// Determine if the argument is a form id or a form name.
			// Note form name usage is deprecated by supported
			// here for legacy reasons.
			oForm = (document.getElementById(formId) || document.forms[formId]);
		}
		else if(typeof formId == 'object'){
			// Treat argument as an HTML form object.
			oForm = formId;
		}
		else{
			return;
		}

		// If the isUpload argument is true, setForm will call createFrame to initialize
		// an iframe as the form target.
		//
		// The argument secureURI is also required by IE in SSL environments
		// where the secureURI string is a fully qualified HTTP path, used to set the source
		// of the iframe, to a stub resource in the same domain.
		if(isUpload){

			// Create iframe in preparation for file upload.
			var io = this.createFrame(secureUri?secureUri:null);
			// Set form reference and file upload properties to true.
			this._isFormSubmit = true;
			this._isFileUpload = true;
			this._formNode = oForm;

			return;

		}

		var oElement, oName, oValue, oDisabled;
		var hasSubmit = false;

		// Iterate over the form elements collection to construct the
		// label-value pairs.
		for (var i=0; i<oForm.elements.length; i++){
			oElement = oForm.elements[i];
			oDisabled = oForm.elements[i].disabled;
			oName = oForm.elements[i].name;
			oValue = oForm.elements[i].value;

			// Do not submit fields that are disabled or
			// do not have a name attribute value.
			if(!oDisabled && oName)
			{
				switch(oElement.type)
				{
					case 'select-one':
					case 'select-multiple':
						for(var j=0; j<oElement.options.length; j++){
							if(oElement.options[j].selected){
								if(window.ActiveXObject){
									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
								}
								else{
									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
								}
							}
						}
						break;
					case 'radio':
					case 'checkbox':
						if(oElement.checked){
							this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
						}
						break;
					case 'file':
						// stub case as XMLHttpRequest will only send the file path as a string.
					case undefined:
						// stub case for fieldset element which returns undefined.
					case 'reset':
						// stub case for input type reset button.
					case 'button':
						// stub case for input type button elements.
						break;
					case 'submit':
						if(hasSubmit === false){
							if(this._hasSubmitListener && this._submitElementValue){
								this._sFormData += this._submitElementValue + '&';
							}
							else{
								this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
							}

							hasSubmit = true;
						}
						break;
					default:
						this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
				}
			}
		}

		this._isFormSubmit = true;
		this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);


		this.initHeader('Content-Type', this._default_form_header);

		return this._sFormData;
	},

  /**
   * @description Resets HTML form properties when an HTML form or HTML form
   * with file upload transaction is sent.
   * @method resetFormState
   * @private
   * @static
   * @return {void}
   */
	resetFormState:function(){
		this._isFormSubmit = false;
		this._isFileUpload = false;
		this._formNode = null;
		this._sFormData = "";
	},

  /**
   * @description Creates an iframe to be used for form file uploads.  It is remove from the
   * document upon completion of the upload transaction.
   * @method createFrame
   * @private
   * @static
   * @param {string} optional qualified path of iframe resource for SSL in IE.
   * @return {void}
   */
	createFrame:function(secureUri){

		// IE does not allow the setting of id and name attributes as object
		// properties via createElement().  A different iframe creation
		// pattern is required for IE.
		var frameId = 'yuiIO' + this._transaction_id;
		var io;
		if(window.ActiveXObject){
			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');

			// IE will throw a security exception in an SSL environment if the
			// iframe source is undefined.
			if(typeof secureUri == 'boolean'){
				io.src = 'javascript:false';
			}
			else if(typeof secureURI == 'string'){
				// Deprecated
				io.src = secureUri;
			}
		}
		else{
			io = document.createElement('iframe');
			io.id = frameId;
			io.name = frameId;
		}

		io.style.position = 'absolute';
		io.style.top = '-1000px';
		io.style.left = '-1000px';

		document.body.appendChild(io);
	},

  /**
   * @description Parses the POST data and creates hidden form elements
   * for each key-value, and appends them to the HTML form object.
   * @method appendPostData
   * @private
   * @static
   * @param {string} postData The HTTP POST data
   * @return {array} formElements Collection of hidden fields.
   */
	appendPostData:function(postData)
	{
		var formElements = [];
		var postMessage = postData.split('&');
		for(var i=0; i < postMessage.length; i++){
			var delimitPos = postMessage[i].indexOf('=');
			if(delimitPos != -1){
				formElements[i] = document.createElement('input');
				formElements[i].type = 'hidden';
				formElements[i].name = postMessage[i].substring(0,delimitPos);
				formElements[i].value = postMessage[i].substring(delimitPos+1);
				this._formNode.appendChild(formElements[i]);
			}
		}

		return formElements;
	},

  /**
   * @description Uploads HTML form, inclusive of files/attachments, using the
   * iframe created in createFrame to facilitate the transaction.
   * @method uploadFile
   * @private
   * @static
   * @param {int} id The transaction id.
   * @param {object} callback User-defined callback object.
   * @param {string} uri Fully qualified path of resource.
   * @param {string} postData POST data to be submitted in addition to HTML form.
   * @return {void}
   */
	uploadFile:function(o, callback, uri, postData){

		// Each iframe has an id prefix of "yuiIO" followed
		// by the unique transaction id.
		var frameId = 'yuiIO' + o.tId;
		var uploadEncoding = 'multipart/form-data';
		var io = document.getElementById(frameId);
		var oConn = this;

		// Track original HTML form attribute values.
		var rawFormAttributes =
		{
			action:this._formNode.getAttribute('action'),
			method:this._formNode.getAttribute('method'),
			target:this._formNode.getAttribute('target')
		};

		// Initialize the HTML form properties in case they are
		// not defined in the HTML form.
		this._formNode.setAttribute('action', uri);
		this._formNode.setAttribute('method', 'POST');
		this._formNode.setAttribute('target', frameId);

		if(this._formNode.encoding){
			// IE does not respect property enctype for HTML forms.
			// Instead it uses the property - "encoding".
			this._formNode.setAttribute('encoding', uploadEncoding);
		}
		else{
			this._formNode.setAttribute('enctype', uploadEncoding);
		}

		if(postData){
			var oElements = this.appendPostData(postData);
		}

		// Start file upload.
		this._formNode.submit();

		// Fire global custom event -- startEvent
		this.startEvent.fire(o);

		if(o.startEvent){
			// Fire transaction custom event -- startEvent
			o.startEvent.fire(o);
		}

		// Start polling if a callback is present and the timeout
		// property has been defined.
		if(callback && callback.timeout){
			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
		}

		// Remove HTML elements created by appendPostData
		if(oElements && oElements.length > 0){
			for(var i=0; i < oElements.length; i++){
				this._formNode.removeChild(oElements[i]);
			}
		}

		// Restore HTML form attributes to their original
		// values prior to file upload.
		for(var prop in rawFormAttributes){
			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
				if(rawFormAttributes[prop]){
					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
				}
				else{
					this._formNode.removeAttribute(prop);
				}
			}
		}

		// Reset HTML form state properties.
		this.resetFormState();

		// Create the upload callback handler that fires when the iframe
		// receives the load event.  Subsequently, the event handler is detached
		// and the iframe removed from the document.
		var uploadCallback = function()
		{
			if(callback && callback.timeout){
				window.clearTimeout(oConn._timeOut[o.tId]);
				delete oConn._timeOut[o.tId];
			}

			// Fire global custom event -- completeEvent
			oConn.completeEvent.fire(o);

			if(o.completeEvent){
				// Fire transaction custom event -- completeEvent
				o.completeEvent.fire(o);
			}

			var obj = {};
			obj.tId = o.tId;
			obj.argument = callback.argument;

			try
			{
				// responseText and responseXML will be populated with the same data from the iframe.
				// Since the HTTP headers cannot be read from the iframe
				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
			}
			catch(e){}

			if(callback && callback.upload){
				if(!callback.scope){
					callback.upload(obj);
				}
				else{
					callback.upload.apply(callback.scope, [obj]);
				}
			}

			// Fire global custom event -- uploadEvent
			oConn.uploadEvent.fire(obj);

			if(o.uploadEvent){
				// Fire transaction custom event -- uploadEvent
				o.uploadEvent.fire(obj);
			}

			YAHOO.util.Event.removeListener(io, "load", uploadCallback);

			setTimeout(
				function(){
					document.body.removeChild(io);
					oConn.releaseObject(o);
				}, 100);
		};

		// Bind the onload handler to the iframe to detect the file upload response.
		YAHOO.util.Event.addListener(io, "load", uploadCallback);
	},

  /**
   * @description Method to terminate a transaction, if it has not reached readyState 4.
   * @method abort
   * @public
   * @static
   * @param {object} o The connection object returned by asyncRequest.
   * @param {object} callback  User-defined callback object.
   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
   * @return {boolean}
   */
	abort:function(o, callback, isTimeout)
	{
		var abortStatus;

		if(o.conn){
			if(this.isCallInProgress(o)){
				// Issue abort request
				o.conn.abort();

				window.clearInterval(this._poll[o.tId]);
				delete this._poll[o.tId];

				if(isTimeout){
					window.clearTimeout(this._timeOut[o.tId]);
					delete this._timeOut[o.tId];
				}

				abortStatus = true;
			}
		}
		else if(o.isUpload === true){
			var frameId = 'yuiIO' + o.tId;
			var io = document.getElementById(frameId);

			if(io){
				// Remove the event listener from the iframe.
				YAHOO.util.Event.removeListener(io, "load", uploadCallback);
				// Destroy the iframe facilitating the transaction.
				document.body.removeChild(io);

				if(isTimeout){
					window.clearTimeout(this._timeOut[o.tId]);
					delete this._timeOut[o.tId];
				}

				abortStatus = true;
			}
		}
		else{
			abortStatus = false;
		}

		if(abortStatus === true){
			// Fire global custom event -- abortEvent
			this.abortEvent.fire(o);

			if(o.abortEvent){
				// Fire transaction custom event -- abortEvent
				o.abortEvent.fire(o);
			}

			this.handleTransactionResponse(o, callback, true);
		}

		return abortStatus;
	},

  /**
   * @description Determines if the transaction is still being processed.
   * @method isCallInProgress
   * @public
   * @static
   * @param {object} o The connection object returned by asyncRequest
   * @return {boolean}
   */
	isCallInProgress:function(o)
	{
		// if the XHR object assigned to the transaction has not been dereferenced,
		// then check its readyState status.  Otherwise, return false.
		if(o && o.conn){
			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
		}
		else if(o && o.isUpload === true){
			var frameId = 'yuiIO' + o.tId;
			return document.getElementById(frameId)?true:false;
		}
		else{
			return false;
		}
	},

  /**
   * @description Dereference the XHR instance and the connection object after the transaction is completed.
   * @method releaseObject
   * @private
   * @static
   * @param {object} o The connection object
   * @return {void}
   */
	releaseObject:function(o)
	{
		//dereference the XHR instance.
		if(o.conn){
			o.conn = null;
		}
		//dereference the connection object.
		o = null;
	}
};

YAHOO.register("connection", YAHOO.util.Connect, {version: "2.3.1", build: "540"});

/*
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.7.0
*/
/**
 * The Browser History Manager provides the ability to use the back/forward
 * navigation buttons in a DHTML application. It also allows a DHTML
 * application to be bookmarked in a specific state.
 *
 * This library requires the following static markup:
 *
 * &lt;iframe id="yui-history-iframe" src="path-to-real-asset-in-same-domain"&gt;&lt;/iframe&gt;
 * &lt;input id="yui-history-field" type="hidden"&gt;
 *
 * @module history
 * @requires yahoo,event
 * @namespace YAHOO.util
 * @title Browser History Manager
 */

/**
 * The History class provides the ability to use the back/forward navigation
 * buttons in a DHTML application. It also allows a DHTML application to
 * be bookmarked in a specific state.
 *
 * @class History
 * @constructor
 */
YAHOO.util.History = (function () {

    /**
     * Our hidden IFrame used to store the browsing history.
     *
     * @property _histFrame
     * @type HTMLIFrameElement
     * @default null
     * @private
     */
    var _histFrame = null;

    /**
     * INPUT field (with type="hidden" or type="text") or TEXTAREA.
     * This field keeps the value of the initial state, current state
     * the list of all states across pages within a single browser session.
     *
     * @property _stateField
     * @type HTMLInputElement|HTMLTextAreaElement
     * @default null
     * @private
     */
    var _stateField = null;

    /**
     * Flag used to tell whether YAHOO.util.History.initialize has been called.
     *
     * @property _initialized
     * @type boolean
     * @default false
     * @private
     */
    var _initialized = false;

    /**
     * List of registered modules.
     *
     * @property _modules
     * @type array
     * @default []
     * @private
     */
    var _modules = [];

    /**
     * List of fully qualified states. This is used only by Safari.
     *
     * @property _fqstates
     * @type array
     * @default []
     * @private
     */
    var _fqstates = [];

    /**
     * location.hash is a bit buggy on Opera. I have seen instances where
     * navigating the history using the back/forward buttons, and hence
     * changing the URL, would not change location.hash. That's ok, the
     * implementation of an equivalent is trivial.
     *
     * @method _getHash
     * @return {string} The hash portion of the document's location
     * @private
     */
    function _getHash() {
        var i, href;
        href = top.location.href;
        i = href.indexOf("#");
        return i >= 0 ? href.substr(i + 1) : null;
    }

    /**
     * Stores all the registered modules' initial state and current state.
     * On Safari, we also store all the fully qualified states visited by
     * the application within a single browser session. The storage takes
     * place in the form field specified during initialization.
     *
     * @method _storeStates
     * @private
     */
    function _storeStates() {

        var moduleName, moduleObj, initialStates = [], currentStates = [];

        for (moduleName in _modules) {
            if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
                moduleObj = _modules[moduleName];
                initialStates.push(moduleName + "=" + moduleObj.initialState);
                currentStates.push(moduleName + "=" + moduleObj.currentState);
            }
        }

        _stateField.value = initialStates.join("&") + "|" + currentStates.join("&");

        if (YAHOO.env.ua.webkit) {
            _stateField.value += "|" + _fqstates.join(",");
        }
    }

    /**
     * Sets the new currentState attribute of all modules depending on the new
     * fully qualified state. Also notifies the modules which current state has
     * changed.
     *
     * @method _handleFQStateChange
     * @param {string} fqstate Fully qualified state
     * @private
     */
    function _handleFQStateChange(fqstate) {

        var i, len, moduleName, moduleObj, modules, states, tokens, currentState;

        if (!fqstate) {
            // Notifies all modules
            for (moduleName in _modules) {
                if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
                    moduleObj = _modules[moduleName];
                    moduleObj.currentState = moduleObj.initialState;
                    moduleObj.onStateChange(unescape(moduleObj.currentState));
                }
            }
            return;
        }

        modules = [];
        states = fqstate.split("&");
        for (i = 0, len = states.length; i < len; i++) {
            tokens = states[i].split("=");
            if (tokens.length === 2) {
                moduleName = tokens[0];
                currentState = tokens[1];
                modules[moduleName] = currentState;
            }
        }

        for (moduleName in _modules) {
            if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
                moduleObj = _modules[moduleName];
                currentState = modules[moduleName];
                if (!currentState || moduleObj.currentState !== currentState) {
                    moduleObj.currentState = currentState || moduleObj.initialState;
                    moduleObj.onStateChange(unescape(moduleObj.currentState));
                }
            }
        }
    }

    /**
     * Update the IFrame with our new state.
     *
     * @method _updateIFrame
     * @private
     * @return {boolean} true if successful. false otherwise.
     */
    function _updateIFrame (fqstate) {

        var html, doc;

        html = '<html><body><div id="state">' + fqstate + '</div></body></html>';

        try {
            doc = _histFrame.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * Periodically checks whether our internal IFrame is ready to be used.
     *
     * @method _checkIframeLoaded
     * @private
     */
    function _checkIframeLoaded() {

        var doc, elem, fqstate, hash;

        if (!_histFrame.contentWindow || !_histFrame.contentWindow.document) {
            // Check again in 10 msec...
            setTimeout(_checkIframeLoaded, 10);
            return;
        }

        // Start the thread that will have the responsibility to
        // periodically check whether a navigate operation has been
        // requested on the main window. This will happen when
        // YAHOO.util.History.navigate has been called or after
        // the user has hit the back/forward button.

        doc = _histFrame.contentWindow.document;
        elem = doc.getElementById("state");
        // We must use innerText, and not innerHTML because our string contains
        // the "&" character (which would end up being escaped as "&amp;") and
        // the string comparison would fail...
        fqstate = elem ? elem.innerText : null;

        hash = _getHash();

        setInterval(function () {

            var newfqstate, states, moduleName, moduleObj, newHash, historyLength;

            doc = _histFrame.contentWindow.document;
            elem = doc.getElementById("state");
            // See my comment above about using innerText instead of innerHTML...
            newfqstate = elem ? elem.innerText : null;

            newHash = _getHash();

            if (newfqstate !== fqstate) {

                fqstate = newfqstate;
                _handleFQStateChange(fqstate);

                if (!fqstate) {
                    states = [];
                    for (moduleName in _modules) {
                        if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
                            moduleObj = _modules[moduleName];
                            states.push(moduleName + "=" + moduleObj.initialState);
                        }
                    }
                    newHash = states.join("&");
                } else {
                    newHash = fqstate;
                }

                // Allow the state to be bookmarked by setting the top window's
                // URL fragment identifier. Note that here, we are on IE, and
                // IE does not touch the browser history when setting the hash
                // (unlike all the other browsers). I used to write:
                //     top.location.replace( "#" + hash );
                // but this had a side effect when the page was not the top frame.
                top.location.hash = newHash;
                hash = newHash;

                _storeStates();

            } else if (newHash !== hash) {

                // The hash has changed. The user might have clicked on a link,
                // or modified the URL directly, or opened the same application
                // bookmarked in a specific state using a bookmark. However, we
                // know the hash change was not caused by a hit on the back or
                // forward buttons, or by a call to navigate() (because it would
                // have been handled above) We must handle these cases, which is
                // why we also need to keep track of hash changes on IE!

                // Note that IE6 has some major issues with this kind of user
                // interaction (the history stack gets completely messed up)
                // but it seems to work fine on IE7.

                hash = newHash;

                // Now, store a new history entry. The following will cause the
                // code above to execute, doing all the dirty work for us...
                _updateIFrame(newHash);
            }

        }, 50);

        _initialized = true;
        YAHOO.util.History.onLoadEvent.fire();
    }

    /**
     * Finish up the initialization of the Browser History Manager.
     *
     * @method _initialize
     * @private
     */
    function _initialize() {

        var i, len, parts, tokens, moduleName, moduleObj, initialStates, initialState, currentStates, currentState, counter, hash;

        // Decode the content of our storage field...
        parts = _stateField.value.split("|");

        if (parts.length > 1) {

            initialStates = parts[0].split("&");
            for (i = 0, len = initialStates.length; i < len; i++) {
                tokens = initialStates[i].split("=");
                if (tokens.length === 2) {
                    moduleName = tokens[0];
                    initialState = tokens[1];
                    moduleObj = _modules[moduleName];
                    if (moduleObj) {
                        moduleObj.initialState = initialState;
                    }
                }
            }

            currentStates = parts[1].split("&");
            for (i = 0, len = currentStates.length; i < len; i++) {
                tokens = currentStates[i].split("=");
                if (tokens.length >= 2) {
                    moduleName = tokens[0];
                    currentState = tokens[1];
                    moduleObj = _modules[moduleName];
                    if (moduleObj) {
                        moduleObj.currentState = currentState;
                    }
                }
            }
        }

        if (parts.length > 2) {
            _fqstates = parts[2].split(",");
        }

        if (YAHOO.env.ua.ie) {

            if (typeof document.documentMode === "undefined" || document.documentMode < 8) {

                // IE < 8 or IE8 in quirks mode or IE7 standards mode
                _checkIframeLoaded();

            } else {

                // IE8 in IE8 standards mode
                YAHOO.util.Event.on(top, "hashchange",
                    function () {
                        var hash = _getHash();
                        _handleFQStateChange(hash);
                        _storeStates();
                    });

                _initialized = true;
                YAHOO.util.History.onLoadEvent.fire();

            }

        } else {

            // Start the thread that will have the responsibility to
            // periodically check whether a navigate operation has been
            // requested on the main window. This will happen when
            // YAHOO.util.History.navigate has been called or after
            // the user has hit the back/forward button.

            // On Safari 1.x and 2.0, the only way to catch a back/forward
            // operation is to watch history.length... We basically exploit
            // what I consider to be a bug (history.length is not supposed
            // to change when going back/forward in the history...) This is
            // why, in the following thread, we first compare the hash,
            // because the hash thing will be fixed in the next major
            // version of Safari. So even if they fix the history.length
            // bug, all this will still work!
            counter = history.length;

            // On Gecko and Opera, we just need to watch the hash...
            hash = _getHash();

            setInterval(function () {

                var state, newHash, newCounter;

                newHash = _getHash();
                newCounter = history.length;
                if (newHash !== hash) {
                    hash = newHash;
                    counter = newCounter;
                    _handleFQStateChange(hash);
                    _storeStates();
                } else if (newCounter !== counter && YAHOO.env.ua.webkit) {
                    hash = newHash;
                    counter = newCounter;
                    state = _fqstates[counter - 1];
                    _handleFQStateChange(state);
                    _storeStates();
                }

            }, 50);

            _initialized = true;
            YAHOO.util.History.onLoadEvent.fire();
        }
    }

    return {

        /**
         * Fired when the Browser History Manager is ready. If you subscribe to
         * this event after the Browser History Manager has been initialized,
         * it will not fire. Therefore, it is recommended to use the onReady
         * method instead.
         *
         * @event onLoadEvent
         * @see onReady
         */
        onLoadEvent: new YAHOO.util.CustomEvent("onLoad"),

        /**
         * Executes the supplied callback when the Browser History Manager is
         * ready. This will execute immediately if called after the Browser
         * History Manager onLoad event has fired.
         *
         * @method onReady
         * @param {function} fn what to execute when the Browser History Manager is ready.
         * @param {object} obj an optional object to be passed back as a parameter to fn.
         * @param {boolean|object} override If true, the obj passed in becomes fn's execution scope.
         * @see onLoadEvent
         */
        onReady: function (fn, obj, override) {

            if (_initialized) {

                setTimeout(function () {
                    var ctx = window;
                    if (override) {
                        if (override === true) {
                            ctx = obj;
                        } else {
                            ctx = override;
                        }
                    }
                    fn.call(ctx, "onLoad", [], obj);
                }, 0);

            } else {

                YAHOO.util.History.onLoadEvent.subscribe(fn, obj, override);

            }
        },

        /**
         * Registers a new module.
         *
         * @method register
         * @param {string} module Non-empty string uniquely identifying the
         *     module you wish to register.
         * @param {string} initialState The initial state of the specified
         *     module corresponding to its earliest history entry.
         * @param {function} onStateChange Callback called when the
         *     state of the specified module has changed.
         * @param {object} obj An arbitrary object that will be passed as a
         *     parameter to the handler.
         * @param {boolean} override If true, the obj passed in becomes the
         *     execution scope of the listener.
         */
        register: function (module, initialState, onStateChange, obj, override) {

            var scope, wrappedFn;

            if (typeof module !== "string" || YAHOO.lang.trim(module) === "" ||
                typeof initialState !== "string" ||
                typeof onStateChange !== "function") {
                throw new Error("Missing or invalid argument");
            }

            if (_modules[module]) {
                // Here, we used to throw an exception. However, users have
                // complained about this behavior, so we now just return.
                return;
            }

            // Note: A module CANNOT be registered after calling
            // YAHOO.util.History.initialize. Indeed, we set the initial state
            // of each registered module in YAHOO.util.History.initialize.
            // If you could register a module after initializing the Browser
            // History Manager, you would not read the correct state using
            // YAHOO.util.History.getCurrentState when coming back to the
            // page using the back button.
            if (_initialized) {
                throw new Error("All modules must be registered before calling YAHOO.util.History.initialize");
            }

            // Make sure the strings passed in do not contain our separators "," and "|"
            module = escape(module);
            initialState = escape(initialState);

            // If the user chooses to override the scope, we use the
            // custom object passed in as the execution scope.
            scope = null;
            if (override === true) {
                scope = obj;
            } else {
                scope = override;
            }

            wrappedFn = function (state) {
                return onStateChange.call(scope, state, obj);
            };

            _modules[module] = {
                name: module,
                initialState: initialState,
                currentState: initialState,
                onStateChange: wrappedFn
            };
        },

        /**
         * Initializes the Browser History Manager. Call this method
         * from a script block located right after the opening body tag.
         *
         * @method initialize
         * @param {string|HTML Element} stateField <input type="hidden"> used
         *     to store application states. Must be in the static markup.
         * @param {string|HTML Element} histFrame IFrame used to store
         *     the history (only required on Internet Explorer)
         * @public
         */
        initialize: function (stateField, histFrame) {

            if (_initialized) {
                // The browser history manager has already been initialized.
                return;
            }

            if (YAHOO.env.ua.opera && typeof history.navigationMode !== "undefined") {
                // Disable Opera's fast back/forward navigation mode and puts
                // it in compatible mode. This makes anchor-based history
                // navigation work after the page has been navigated away
                // from and re-activated, at the cost of slowing down
                // back/forward navigation to and from that page.
                history.navigationMode = "compatible";
            }

            if (typeof stateField === "string") {
                stateField = document.getElementById(stateField);
            }

            if (!stateField ||
                stateField.tagName.toUpperCase() !== "TEXTAREA" &&
                (stateField.tagName.toUpperCase() !== "INPUT" ||
                 stateField.type !== "hidden" &&
                 stateField.type !== "text")) {
                throw new Error("Missing or invalid argument");
            }

            _stateField = stateField;

            // IE < 8 or IE8 in quirks mode or IE7 standards mode
            if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) {

                if (typeof histFrame === "string") {
                    histFrame = document.getElementById(histFrame);
                }

                if (!histFrame || histFrame.tagName.toUpperCase() !== "IFRAME") {
                    throw new Error("Missing or invalid argument");
                }

                _histFrame = histFrame;
            }

            // Note that the event utility MUST be included inline in the page.
            // If it gets loaded later (which you may want to do to improve the
            // loading speed of your site), the onDOMReady event never fires,
            // and the history library never gets fully initialized.
            YAHOO.util.Event.onDOMReady(_initialize);
        },

        /**
         * Call this method when you want to store a new entry in the browser's history.
         *
         * @method navigate
         * @param {string} module Non-empty string representing your module.
         * @param {string} state String representing the new state of the specified module.
         * @return {boolean} Indicates whether the new state was successfully added to the history.
         * @public
         */
        navigate: function (module, state) {

            var states;

            if (typeof module !== "string" || typeof state !== "string") {
                throw new Error("Missing or invalid argument");
            }

            states = {};
            states[module] = state;

            return YAHOO.util.History.multiNavigate(states);
        },

        /**
         * Call this method when you want to store a new entry in the browser's history.
         *
         * @method multiNavigate
         * @param {object} states Associative array of module-state pairs to set simultaneously.
         * @return {boolean} Indicates whether the new state was successfully added to the history.
         * @public
         */
        multiNavigate: function (states) {

            var currentStates, moduleName, moduleObj, currentState, fqstate;

            if (typeof states !== "object") {
                throw new Error("Missing or invalid argument");
            }

            if (!_initialized) {
                throw new Error("The Browser History Manager is not initialized");
            }

            for (moduleName in states) {
                if (!_modules[moduleName]) {
                    throw new Error("The following module has not been registered: " + moduleName);
                }
            }

            // Generate our new full state string mod1=xxx&mod2=yyy
            currentStates = [];

            for (moduleName in _modules) {
                if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
                    moduleObj = _modules[moduleName];
                    if (YAHOO.lang.hasOwnProperty(states, moduleName)) {
                        currentState = states[unescape(moduleName)];
                    } else {
                        currentState = unescape(moduleObj.currentState);
                    }

                    // Make sure the strings passed in do not contain our separators "," and "|"
                    moduleName = escape(moduleName);
                    currentState = escape(currentState);

                    currentStates.push(moduleName + "=" + currentState);
                }
            }

            fqstate = currentStates.join("&");

            if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) {

                return _updateIFrame(fqstate);

            } else {

                // Known bug: On Safari 1.x and 2.0, if you have tab browsing
                // enabled, Safari will show an endless loading icon in the
                // tab. This has apparently been fixed in recent WebKit builds.
                // One work around found by Dav Glass is to submit a form that
                // points to the same document. This indeed works on Safari 1.x
                // and 2.0 but creates bigger problems on WebKit. So for now,
                // we'll consider this an acceptable bug, and hope that Apple
                // comes out with their next version of Safari very soon.
                top.location.hash = fqstate;
                if (YAHOO.env.ua.webkit) {
                    // The following two lines are only useful for Safari 1.x
                    // and 2.0. Recent nightly builds of WebKit do not require
                    // that, but unfortunately, it is not easy to differentiate
                    // between the two. Once Safari 2.0 departs the A-grade
                    // list, we can remove the following two lines...
                    _fqstates[history.length] = fqstate;
                    _storeStates();
                }

                return true;

            }
        },

        /**
         * Returns the current state of the specified module.
         *
         * @method getCurrentState
         * @param {string} module Non-empty string representing your module.
         * @return {string} The current state of the specified module.
         * @public
         */
        getCurrentState: function (module) {

            var moduleObj;

            if (typeof module !== "string") {
                throw new Error("Missing or invalid argument");
            }

            if (!_initialized) {
                throw new Error("The Browser History Manager is not initialized");
            }

            moduleObj = _modules[module];
            if (!moduleObj) {
                throw new Error("No such registered module: " + module);
            }

            return unescape(moduleObj.currentState);
        },

        /**
         * Returns the state of a module according to the URL fragment
         * identifier. This method is useful to initialize your modules
         * if your application was bookmarked from a particular state.
         *
         * @method getBookmarkedState
         * @param {string} module Non-empty string representing your module.
         * @return {string} The bookmarked state of the specified module.
         * @public
         */
        getBookmarkedState: function (module) {

            var i, len, idx, hash, states, tokens, moduleName;

            if (typeof module !== "string") {
                throw new Error("Missing or invalid argument");
            }

            // Use location.href instead of location.hash which is already
            // URL-decoded, which creates problems if the state value
            // contained special characters...
            idx = top.location.href.indexOf("#");
            if (idx >= 0) {
                hash = top.location.href.substr(idx + 1);
                states = hash.split("&");
                for (i = 0, len = states.length; i < len; i++) {
                    tokens = states[i].split("=");
                    if (tokens.length === 2) {
                        moduleName = tokens[0];
                        if (moduleName === module) {
                            return unescape(tokens[1]);
                        }
                    }
                }
            }

            return null;
        },

        /**
         * Returns the value of the specified query string parameter.
         * This method is not used internally by the Browser History Manager.
         * However, it is provided here as a helper since many applications
         * using the Browser History Manager will want to read the value of
         * url parameters to initialize themselves.
         *
         * @method getQueryStringParameter
         * @param {string} paramName Name of the parameter we want to look up.
         * @param {string} queryString Optional URL to look at. If not specified,
         *     this method uses the URL in the address bar.
         * @return {string} The value of the specified parameter, or null.
         * @public
         */
        getQueryStringParameter: function (paramName, url) {

            var i, len, idx, queryString, params, tokens;

            url = url || top.location.href;

            idx = url.indexOf("?");
            queryString = idx >= 0 ? url.substr(idx + 1) : url;

            // Remove the hash if any
            idx = queryString.lastIndexOf("#");
            queryString = idx >= 0 ? queryString.substr(0, idx) : queryString;

            params = queryString.split("&");

            for (i = 0, len = params.length; i < len; i++) {
                tokens = params[i].split("=");
                if (tokens.length >= 2) {
                    if (tokens[0] === paramName) {
                        return unescape(tokens[1]);
                    }
                }
            }

            return null;
        }

    };

})();
YAHOO.register("history", YAHOO.util.History, {version: "2.7.0", build: "1799"});
function embedFlashObject(oeTags){
    document.write(oeTags);
}

/**
 * This method opens a popup window and fills it with the content provided to by the 'functionToGenerateHTML' function of 'subData' object
 * @param {Object} wWidth width of the popup window
 * @param {Object} wHeight height of the poput window
 * @param {Object} title title of the popup window
 * @param {Object} subData the object which requested the popup
 * @param {Object} functionToGenerateHTML the function in the the object 'subData' that will supply the content that goes into this popup
 */
function openPopup(wWidth, wHeight, title, subData, functionToGenerateHTML){
    var htmlContentStr = subData[functionToGenerateHTML](subData);
    // configure window options
    var options = "menubar=0,toolbar=0,location=0,directories=0,status=0,scrollbars=1,resizable=1,width=" + wWidth + ",height=" + wHeight;

    // open window and write HTML
    var win = window.open("", "", options);
    win.document.open();
    win.document.write(htmlContentStr);
    win.document.close();
    win.focus();
}
/**
 * This method opens a popup window with the given URL and given width and height.
 * @param {Object} URL url of the popup window
 * @param {Object} wWidth width of the popup window
 * @param {Object} wHeight height of the poput window
 */
function openPopupLink(url, wWidth, hHeight)
{
    var options = "menubar=0,toolbar=0,location=0,directories=0,status=0,scrollbars=1,resizable=1,width=" + wWidth +",height=" + hHeight;
    window.open(url, "_blank", options);
}

/**
 * Attaches a the baseDiv to the display-div in the doument and sets the
 * baseDiv innerHTML property to the html passed.
 * The method firs searches for the baseDiv under child elements of the
 * 'display-div'. If found, the htmlStr is set as inner HTML property of the
 * baseDiv. If baseDiv is not found, a new div is created and attached as the
 * baseDiv to the 'display-div'.
 * @params baseDiv The widget base div to be attached to the 'display-div'
 * @params htmlStr detempletized html string to be set as inner html of the baseDiv
 * @params widgetId id of the caller widget
 **/
function attachBaseDivToDisplayDiv(basediv, htmlStr, widgetId){
    log4javascript.getDefaultLogger().debug("attaching basediv for  -> " + widgetId + " " + basediv);

    var __displayDiv = document.getElementById("display-div");

    if (null != __displayDiv) {
        var childDivs = __displayDiv.getElementsByTagName("div");

        var isBaseDivPresent = false;
        //for( var i in childDivs ) {
        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (basediv == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Basediv already present. Quitting ... ");
                    childDivs[i].innerHTML = htmlStr;
                    isBaseDivPresent = true;
                    break;
                }

            }
            catch (e) {
            }
        }

        if (!isBaseDivPresent) {
            log4javascript.getDefaultLogger().debug("No " + basediv + " div in the DOM. Creating it dynamically.");
            var __baseDiv = document.createElement("div");
            __baseDiv.setAttribute("id", basediv);
            __displayDiv.appendChild(__baseDiv);
            __baseDiv.innerHTML = htmlStr;
        }
    }
    else {
        log4javascript.getDefaultLogger().debug("No displayDiv div in the DOM. Attaching " + basediv + " to body.");
        document.getElementById(basediv).innerHTML = wtk.loader.deParametrizeTemplate(htmlStr, widgetId);

    }
}


getChildDivDOM = function(parentDivId, divId){
    var _parentDiv = document.getElementById(parentDivId);
    var div = null;

    if (null != _parentDiv) {
        log4javascript.getDefaultLogger().debug("Parent Div present : "+parentDivId);
        var childDivs = _parentDiv.getElementsByTagName("div");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }

        childDivs = _parentDiv.getElementsByTagName("tr");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }

        childDivs = _parentDiv.getElementsByTagName("table");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }

        childDivs = _parentDiv.getElementsByTagName("td");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }

        childDivs = _parentDiv.getElementsByTagName("span");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }

        childDivs = _parentDiv.getElementsByTagName("img");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }
        
        childDivs = _parentDiv.getElementsByTagName("center");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            }
            catch (e) {
            }
        }
        
        childDivs = _parentDiv.getElementsByTagName("ul");

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (divId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : "+childDivs[i].getAttribute("id"));
                    div = childDivs[i];
                    break;
                }
            } 
            catch (e) {
            }
        }
    }else{
        log4javascript.getDefaultLogger().debug("Parent Div NOT present : "+parentDivId);
        div = YAHOO.util.Dom.get(divId);
    }
    return div;
}

/**
 * This function returns the HTML element inside the parent div.
 * @param {Object} parentDivId The parent div ID
 * @param {Object} elementId The element ID
 * @param {Object} elementTagName The tag Name of the element, example: img
 */
getChildElement = function(parentDivId, elementId, elementTagName){
    var _parentDiv = document.getElementById(parentDivId);

    if (null != _parentDiv) {
        log4javascript.getDefaultLogger().debug("Parent Div present : " + parentDivId);
        var childDivs = _parentDiv.getElementsByTagName(elementTagName);

        for (var i = 0; i < childDivs.length; i++) {
            try {
                if (elementId == childDivs[i].getAttribute("id")) {
                    log4javascript.getDefaultLogger().debug("Child Div present : " + childDivs[i].getAttribute("id"));
                    return childDivs[i];
                }
            }
            catch (e) {
            }
        }
    }
    return null ;
}

BPTrim = function (str) {
    var str = str.replace(/^\s\s*/, ''),
        ws = /\s/,
        i = str.length;
    while (ws.test(str.charAt(--i)));
    return str.slice(0, i + 1);
}
/**
 * returns false if the value is undefined.
 * @param {Object} value
 */
isDefined = function( value ){
    if( typeof value == 'undefined' ){
            return false;
        }else{
           return true;
        }
}

/**
 * The following date time functions are taken from
 * Author: Matt Kruse <matt@mattkruse.com>
 * WWW: http://www.mattkruse.com/
 */
var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
function LZ(x) {return(x<0||x>9?"":"0")+x}

// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
formatDate = function (date,format) {
    format=format+"";
    var result="";
    var i_format=0;
    var c="";
    var token="";
    var y=date.getYear()+"";
    var M=date.getMonth()+1;
    var d=date.getDate();
    var E=date.getDay();
    var H=date.getHours();
    var m=date.getMinutes();
    var s=date.getSeconds();
    var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
    // Convert real date parts into formatted versions
    var value=new Object();
    if (y.length < 4) {y=""+(y-0+1900);}
    value["y"]=""+y;
    value["yyyy"]=y;
    value["yy"]=y.substring(2,4);
    value["M"]=M;
    value["MM"]=LZ(M);
    value["MMM"]=MONTH_NAMES[M-1];
    value["NNN"]=MONTH_NAMES[M+11];
    value["d"]=d;
    value["dd"]=LZ(d);
    value["E"]=DAY_NAMES[E+7];
    value["EE"]=DAY_NAMES[E];
    value["H"]=H;
    value["HH"]=LZ(H);
    if (H==0){value["h"]=12;}
    else if (H>12){value["h"]=H-12;}
    else {value["h"]=H;}
    value["hh"]=LZ(value["h"]);
    if (H>11){value["K"]=H-12;} else {value["K"]=H;}
    value["k"]=H+1;
    value["KK"]=LZ(value["K"]);
    value["kk"]=LZ(value["k"]);
    if (H > 11) { value["a"]="PM"; }
    else { value["a"]="AM"; }
    value["m"]=m;
    value["mm"]=LZ(m);
    value["s"]=s;
    value["ss"]=LZ(s);
    while (i_format < format.length) {
        c=format.charAt(i_format);
        token="";
        while ((format.charAt(i_format)==c) && (i_format < format.length)) {
            token += format.charAt(i_format++);
            }
        if (value[token] != null) { result=result + value[token]; }
        else { result=result + token; }
        }
    return result;
    }
//END of date functions taken from www.mattkruse.com
isIE6Browser = function() {
    var msie = navigator.appVersion.split('MSIE');
    if (typeof msie != "undefined" && msie != null) {
        if (parseInt(msie[1]) <= 6) {
            return true;
        }
    }
    return false;
}

isIE8Browser = function() {
    var msie = navigator.appVersion.split('MSIE');
    if (typeof msie != "undefined" && msie != null) {
        if(parseInt(msie[1]) > 7)
            return true;
    }
    return false;    
}

getFirstValue = function(valueMap)
{  
    if (typeof (valueMap) != "string") {
        if ( typeof(valueMap) == 'object' ) {
            if (valueMap.length)
                return valueMap[0];
            else
                return valueMap;
        }
    } 
    else { 
        return valueMap;
    }
}

/**
 * This function returns an array, if the passed value is NOT an array.
 * Returns the same object otherwise
 * @param {Object} value
 */
getValues = function(value) {
    if("undefined" == typeof(value.length)) {
        var array = new Array( ) ;
        array[0] = value ;
        return array ;
    }
    return value ;
}
/**
 * This class does all the loading and initialization of the widgets. It is also responsible
 * for loading page state information.
 *
 * @author janil
 */

/**
 * This function represents the com.forddirect.presentation.wtk.Loader class.
 */
getPackageForName( "com.forddirect.presentation.wtk" ).Loader = function( parentInstance )
{
    //The js, css, html classpath variables
    this.jsFileClassPath = "" ;
    this.cssFileClassPath = "" ;
    this.htmlFileClassPath = "" ;
    this.flashFileClassPath = "" ;

    this.parentInstance = parentInstance ;

    //these flags are needed to check whether the flash controller is already loaded or not
    this.flashControllerLoadEvent = new YAHOO.util.CustomEvent( "FlashControllerLoaded" ) ;

    //this widgets list stores the list of widgets for the reference of loading widgets from mainFlashAppInitialized
    this.widgetList = "";

    //this stores the group id of widgets for the reference of loading widgets from mainFlashAppInitialized
    this.groupId = "";

    /**
     * This function initializes the Loader object and returns the loadInit id.
     * @param {String} jsFileClassPath Classpath for the *.js files
     * @param {String} cssFileClassPath Classpath for the *.css files
     * @param {String} htmlFileClassPath Classpath for the *.html files
     * @param {String} flashFileClassPath Classpath for the *.swf files
     * @return An event which signals the successful initialization of the Loader module.
     * The calling function must subscribe to the event to perform logic on the Loader.
     */
    this.init = function( jsFileClassPath, cssFileClassPath, htmlFileClassPath, flashFileClassPath )
    {
        //log4javascript.getDefaultLogger( ).debug( "Loader::INIT" ) ;
        this.jsFileClassPath = jsFileClassPath ;
        this.cssFileClassPath = cssFileClassPath ;
        this.htmlFileClassPath = htmlFileClassPath ;
        this.flashFileClassPath = flashFileClassPath ;

        //This internal function is called when the BaseWidget *file* is loaded. The event
        //internalSuccessEvents(widgetId) is fired to signal the successful loading.
        var _onBaseWidgetDownloaded = function( event, firedData, subscribedData ) {
            //log4javascript.getDefaultLogger( ).info( "Loader::Loader class initialized" ) ;
            subscribedData['loaderInitEvent'].fire( ) ;
        } ;

        var successEvent = new YAHOO.util.CustomEvent( "successEvent", this ) ;
        var failureEvent = new YAHOO.util.CustomEvent( "failureEvent", this ) ;
        var loaderInitEvent = new YAHOO.util.CustomEvent( "loaderInit", this.parentInstance ) ;

        successEvent.subscribe( _onBaseWidgetDownloaded, {'loaderInitEvent':loaderInitEvent} ) ;

        var baseWidgetObject = {
                "widgetClass" : "com.forddirect.presentation.widgets.basewidget.BaseWidget",
                "initParams" : "param=1",
                "type" : "js",
                "widgetId" : "basewidget",
                "widgetLocation" : __baseClassPath+"static/com/forddirect/presentation/widgets/basewidget/BaseWidget.js"
            } ;

        this.load( baseWidgetObject, successEvent, failureEvent ) ;
        return loaderInitEvent ;
    }


    /**
     * This function downloads the list of components.
     * @param {Object} components List of the components to be loaded Each component should have
     * the following attributes -
     * 1) widgetClass: The class of the component. for example,
     *                                  com.forddirect.presentation.widgets.config.Config
     * 2) type: whether js, css, swf, html, widget
     * 3) baseDiv: The div in which the component is to be loaded
     * 4) parentWidgetId: [Only for HTML]The id of the widget to which the HTML template is to be
     *                                   applied.
     * 5) initParams: {Object} The parameters to be sent when loading the class
     * 6) widgetId: The unique ID of each component
     * This functions calls the load function on individual event and creates a group event for
     * the group. This groupEvent is fired when all the components are successfully loaded.
     *
     * @return widgetsDownloadedEvent The event which would be fired on successful downloading of
     * of the group of components.
     */
    this.loadComponents = function( components , shouldInit , clubWidgets )
    {
        log4javascript.getDefaultLogger( ).debug( "Loader::LoadComponents" ) ;

        var widgetsDownloadedEvent = new YAHOO.util.CustomEvent( "widgetsDownloadedEvent", this.parentInstance );
        var successEvent = "" ;
        var failureEvent = "" ;
        if ("undefined" == typeof(clubWidgets)  || null == clubWidgets || clubWidgets != true) {
            for (var widgetName in components) {
                for (var c in components[widgetName]) {
                    components[widgetName][c].isLoaded = false;
                    successEvent = new YAHOO.util.CustomEvent(components[widgetName][c].widgetId, this);
                    failureEvent = new YAHOO.util.CustomEvent(components[widgetName][c].widgetId, this);
                    successEvent.subscribe(this._onLoadComplete, {
                        'widgetsDownloadedEvent': widgetsDownloadedEvent,
                        'components': components
                    });
                    this.load(components[widgetName][c], successEvent, failureEvent);
                }
            }
        }
        else {
            this.loadClubbedWidgets(components, widgetsDownloadedEvent);
        }
        return widgetsDownloadedEvent ;
    }

    this.loadClubbedWidgets = function(components , widgetsDownloadedEvent) {
        var location = __baseClassPath + "localization/?";
        
        var widgetsParam = "";
        for( widgetName in components ){
            for (c in components[widgetName]) {
                log4javascript.getDefaultLogger().debug("Component Loading = " + components[widgetName][c].widgetName);
                components[widgetName][c].isLoaded = false;
            }
            if(widgetsParam != "") {
                widgetsParam = widgetsParam + "|";
            }
            widgetsParam = widgetsParam + widgetName;
        }
        
        var context = this ;
        var successEvent = new YAHOO.util.CustomEvent( widgetsParam, this ) ;
        var failureEvent = new YAHOO.util.CustomEvent( widgetsParam, this ) ;
        successEvent.subscribe( this.onLoadWidgetsComplete, {'widgetsDownloadedEvent': widgetsDownloadedEvent, 'components':components} ) 
        var callback = {
            success: function(o) { context._onClubbedWidgetFileDownloaded( eval('('+o.responseText+')'), successEvent, components ) ; },
            failure: function(o) { log4javascript.getDefaultLogger( ).debug(o) ; failureEvent.fire( o ) ; }
        } ;

        location = location + "widgets=" + widgetsParam;
        //Read the urlParamMap (defined in the [App layer] index.jsp) and adding all the parameters
        //to the localization URL query string
        for(var key in urlParamMap)
        {
            location = location + "&" + key + "=" + urlParamMap[key];
        }
        location = location + "&clubWidgets=true";
        log4javascript.getDefaultLogger( ).debug("Component Loading Location = " + location);
        YAHOO.util.Connect.asyncRequest( "GET", location, callback ) ;
    }

    this.onLoadWidgetsComplete = function(event, firedData , subscribedData) {
        log4javascript.getDefaultLogger( ).debug("widgetsDownloadedEvent firing");
        subscribedData['widgetsDownloadedEvent'].fire( ) ;
    }

    this._onClubbedWidgetFileDownloaded = function(responseObject, firedData, components) {
        for (var widgetName in responseObject) {
            log4javascript.getDefaultLogger( ).debug("Component Response = " + widgetName);
            if(commUtils.isDefinedAndNotNull(components[widgetName])) {
                log4javascript.getDefaultLogger( ).debug("Component Response = " + widgetName + " Assigning Values");
                for (var comp in components[widgetName]) {
                    component = components[widgetName][comp];
                    component["type"] = responseObject[widgetName]["type"];
                    component["js"] = responseObject[widgetName]["js"];
                    
                    if ("swf" == component["type"]) {
                        log4javascript.getDefaultLogger().debug("Component Response = " + widgetName + " Adding SWF Component");
                        component["swf"] = (typeof responseObject[widgetName]["swf"] == "undefined") ? responseObject[widgetName]["swf"] : responseObject[widgetName]["swf"][component.widgetName];
                    }
                    else {
                        log4javascript.getDefaultLogger().debug("Component Response = " + widgetName + " Adding CSS Component");
                        component["css"] = (typeof responseObject[widgetName]["css"] == "undefined") ? responseObject[widgetName]["css"] : responseObject[widgetName]["css"][component.widgetName];
                        if (typeof responseObject[widgetName]["html"] != "undefined" && responseObject[widgetName]["html"] != null) {
                            var temp = this.getCount(responseObject[widgetName]["html"]);
                            component["html"] = (temp > 1) ? responseObject[widgetName]["html"] : responseObject[widgetName]["html"][component.widgetName];
                        }
                        else {
                            component["html"] = responseObject[widgetName]["html"];
                        }
                        log4javascript.getDefaultLogger().debug("Component Response = " + widgetName + " Adding HTML Component");
                    }
                }
            }
        }
        firedData.fire();
    }

    /**
     * This function listens to the successEvent of each component, which signals the successful
     * downloading of the component.
     * @param {Object} event SuccessEvent signaling the download of the component
     * @param {Object} firedData {}
     * @param {Object} subscribedData {'widgetsDownloadedEvent', 'components'}
     */
    this._onLoadComplete = function( event, firedData, subscribedData )
    {
        //log4javascript.getDefaultLogger( ).info( "Loader::" + firedData[0] + " downloaded" ) ;
        var components = subscribedData['components'] ;
        var _flag = false ;
        for(var widgetName in components ) {
            for (comp in components[widgetName]) {
                //Check if the widget which fired the successEvent is same as the current component
                if (firedData[0] == components[widgetName][comp].widgetId) {
                    components[widgetName][comp].isLoaded = true;
                }
                //Check if the current component is loaded
                if (!components[widgetName][comp].isLoaded) {
                    _flag = true;
                }
            }
        }
        //Check if there is AT LEAST one component still NOT downloaded
        if( _flag )
            return ;
        log4javascript.getDefaultLogger( ).info( "Loader::_onLoadComplete All components loaded" ) ;
        subscribedData['widgetsDownloadedEvent'].fire( ) ;
    }

    /**
     * This function loads a single component
     * @param {Object} component This object has all the attributes necessary for loading the
     *                           component.
     * @param {Event} successEvent Event to be fired on successful download
     * @param {Event} failureEvent Event to be fired on failed download
     * Each component should have the following attributes -
     * 1) widgetClass: The class of the component.
     *                 for example, com.forddirect.presentation.widgets.config.Config
     * 2) type: whether js, css, swf, html, widget
     * 3) baseDiv: The div in which the component is to be loaded
     * 4) parentWidgetId: [Only for HTML]The id of the widget to which the HTML template is to be
     *                                   applied.
     * 5) initParams: {Object} The parameters to be sent when loading the class
     * 6) widgetId: The unique ID of each component
     * 7) widgetLocation: [For HTML, CSS, JS]The filePath of the component
     *
     * @return widgetId
     */
    this.load = function(component, successEvent, failureEvent)
    {
        //log4javascript.getDefaultLogger( ).debug( "Loader::Loading " + component.widgetId + "/" + component.type ) ;

        //A pointer to the loader instance
        var context = this ;

        if( "widget" == component.type ) {
            //The JSON response from the localization service is loaded and processed.
            return this.loadWidget( component, successEvent, failureEvent ) ;
        }
        else if( "html" == component.type ) {
            //the HTML file is downloaded and attached to the basediv after de-parametrizing
            var callback = {
                success : function(o) { context._onHtmlFileDownloaded( o.responseText, successEvent, component.baseDivId, component.widgetId, context ) },
                failure : function(o) { }
            } ;
            YAHOO.util.Connect.asyncRequest("GET", component.widgetLocation, callback, component.initParams);
            return component.widgetId;
        }
        //For CSS/JS create and attach the tags to the document head.
        //Set the flag required for the IE script/link tags
        var ieFlag = this.getIeFlag( component.type ) ;
        //Create the tag for JS/CSS
        var componentTag = this.getComponentTag( this, component.widgetLocation, component.type, component.widgetId ) ;
        //Attach the JS/CSS tag
        this.attachComponent(successEvent, component.widgetId, componentTag, ieFlag);
            
        return component.widgetId;
    }


   
    /**
     * This function attaches the tag to the HEAD of the DOM. On successful attachment, the event
     * is fired.
     * @param {Object} event The successEvent to be fired when the tag is attached
     * @param {Object} widgetId The widgetId associated with the JS/CSS
     * @param {Object} tag The tag to be attached
     * @param {Object} ieFlag The flag to determine the IE attachment
     */
    this.attachComponent = function( event, widgetId, tag, ieFlag )
    {
        //log4javascript.getDefaultLogger( ).debug( "Loader::Adding " + widgetId + " tag to the DOM" ) ;
        //In Firefox, when a script is loaded, browser calls the onload function
        tag.onload = function( ) {
            event.fire( widgetId ) ;
        } ;
        //In IE, when a script is loaded, browser calls the onreadystatechange function. The ready states
        //of the script should be checked everytime. when the script is loaded for the first time, it's readystate
        //is 'loaded'. But when it is cached and loaded again, it calls the function for 2 readystates: 'loaded'
        //and 'complete'. Hence the ieFlag is used so that the logic in executed only once.
        tag.onreadystatechange = function( ) {
            if( "loaded" == tag.readyState || "complete" == tag.readyState && ieFlag ) {
                ieFlag = false ;
                event.fire( widgetId ) ;
            }
        } ;
        document.getElementsByTagName( "head" ).item( 0 ).appendChild( tag ) ;
    }

    /**
     * This function returns the component tag for the particular file and type
     *
     * @param {Object} context The scope of the function (this)
     * @param {String} filePath The file path to be added to the head tag
     * @param {String} type The type of the file to be added to the head tag
     * @return ComponentTag
     */
    this.getComponentTag = function( context, filePath, type , id )
    {
        if( "js" == type )
            return context._getJavascriptComponent( filePath, id ) ;
        else if( "css" == type )
            return context._getCssComponent( filePath, id ) ;
        else if( "alt css" == type )
            return context._getRelCssComponent( filePath, id ) ;
        return null ;
    }

    /**
     * This function sets the IE Flag to false for the css files.
     *
     * @param {String} type The type of the file (css/js/html)
     * @return Boolean
     */
    this.getIeFlag = function( type )
    {
        if( "css" == type || "alt css" == type)
            return false ;
        return true ;
    }

    /**
     * This function returns the classpath for the type specified
     *
     * @param {Object} context The scope of the function(this)
     * @param {String} type The type of the class
     * @return Classpath
     */
    this.getClassPath = function( context, type, componentLocation )
    {
        if( componentLocation != null )
            return componentLocation ;
        else if( "js" == type )
            return context.jsFileClassPath ;
        else if( "css" == type || "alt css" == type )
            return context.cssFileClassPath ;
        else if( "html" == type )
            return context.htmlFileClassPath ;
        else if( "swf" == type )
            return context.flashFileClassPath ;
        else
            return null ;
    }

    /**
     * This function copies the contents of one div to another. It accepts parameters as divId
     * of the div to be copied and parentDiv, to which content needs to be copied.
     * @param divId Id of the div, whose contents are to be copied
     * @param parentDiv id of the div to which contents need to be copied
     * @return success true if successful; false otherwise
     */
    this.copyDiv = function( currentDivID, currentAncestorDivID, targetDivID, targetAncestorDivID )
    {
        //TODO: update documentation
        log4javascript.getDefaultLogger( ).debug("Copying div  " +currentAncestorDivID+"."+ currentDivID + " to target div " +targetAncestorDivID+"."+ targetDivID);

        if(currentDivID !== targetDivID){//ideal scenario
            var contentDivNode = YAHOO.util.Dom.get(currentDivID);
            var targetDivNode = YAHOO.util.Dom.get(targetDivID);
            targetDivNode.innerHTML = contentDivNode.innerHTML;
            return true;
        }
        else if( null != currentAncestorDivID && null != targetAncestorDivID)
        {
            var currentDivNode = YAHOO.util.Dom.get(currentAncestorDivID);
            //search the specified div in parent div
            var srcDivElements = currentDivNode.getElementsByTagName("div");
            var sourceDivNode = null;
            for(i=0;i< srcDivElements.length;i++) {
                //check the div id
                if(srcDivElements[i].getAttribute("id") == currentDivID) {
                    //copy the contents of the div
                    sourceDivNode = srcDivElements[i];
                    break;
                }
            }
            if( null == sourceDivNode ){
                return false;
            }
            var targetAncestorDivNode = YAHOO.util.Dom.get(targetAncestorDivID);

            var targetDivElements = targetAncestorDivNode.getElementsByTagName("div");
            for(i=0; i<targetDivElements.length; i++) {
                //check the div id
                if(targetDivElements[i].getAttribute("id") == targetDivID) {
                    //copy the contents of the div
                    targetDivElements[i].innerHTML = sourceDivNode.innerHTML;
                    return true;
                }
            }
        }

        //div not found in parent div; return false
        return false;
    }

    /**
     * This function accepts widgetId and removes css link of that widget
     * @param widgetId
     * @return success flag
     */
    this.removeCSS = function ( widgetId )
    {
        var cssNode = document.getElementById(widgetId + "_css");
        if(cssNode != null) {
            //delete css from the document
            document.getElementsByTagName( "head" ).item( 0 ).removeChild( cssNode ) ;
            return true;
        } else {
            log4javascript.getDefaultLogger( ).error( "css link for " + widgetId + "_css not found") ;
            return false;
        }
    }

    /**
     * This function accepts widgetId and removes js script of that widget
     * @param widgetId
     * @return success flag
     */
    this.removeJS = function ( widgetId )
    {
        var jsNode = document.getElementById(widgetId + "_js");
        if(jsNode != null) {
            //delete css from the document
            document.getElementsByTagName( "head" ).item( 0 ).removeChild( jsNode ) ;
            return true;
        } else {
            log4javascript.getDefaultLogger( ).error( "js script for " + widgetId + "_js not found") ;
            return false;
        }
    }

    //This internal function is used to get a <script> tag for the js filePath
    this._getJavascriptComponent = function( filePath, id )
    {
        var scriptTag = document.createElement( "script" ) ;
        scriptTag.setAttribute( "type", "text/javascript" ) ;
        scriptTag.setAttribute( "src", filePath ) ;
        scriptTag.setAttribute( "id", id + "_js") ;
        return scriptTag ;
    }

    //This internal function is used to get a <link> tag for the css filePath
    this._getCssComponent = function( filePath, id )
    {
        var linkTag = document.createElement( "link" ) ;
        linkTag.setAttribute( "rel", "stylesheet") ;
        linkTag.setAttribute( "type", "text/css") ;
        linkTag.setAttribute( "href", filePath ) ;
        linkTag.setAttribute( "id", id ) ;
        return linkTag ;
    }

    //This internal function is used to get a <link> tag for the css filePath
    this._getRelCssComponent = function( filePath, id )
    {
        var linkTag = document.createElement( "link" ) ;
        linkTag.setAttribute( "rel", "alternate stylesheet") ;
        linkTag.setAttribute( "type", "text/css") ;
        linkTag.setAttribute( "href", filePath ) ;
        linkTag.setAttribute( "id", id ) ;
        linkTag.setAttribute( "title", id ) ;
        return linkTag ;
    }

    /**
     * This function unloads a widget. It basically unloads the js, css and html contents of the specified javascript widget
     * TODO: when should the js instance be made null??
     * @param divId
     * @param widgetId
     */
    this.unload = function (divId,widgetId)
    {
        log4javascript.getDefaultLogger( ).debug( "unloading widget " + widgetId) ;

        //get node for specified css file
        //var cssNode = document.getElementsByTagName( "head" ).item( 0 ).getElementById(widgetId + "_css");
        var cssNode = document.getElementById(widgetId + "_css");
        if(cssNode != null) {
            log4javascript.getDefaultLogger( ).debug( "found cssNode") ;
            //delete css from the document
            document.getElementsByTagName( "head" ).item( 0 ).removeChild( cssNode ) ;
        }
        
        if(null != divId){
            var __displayDiv = document.getElementById("display-div");
            //TODO: To be removed once page-level HTMLs are removed
            if (null != __displayDiv) {
                var childDivs = __displayDiv.getElementsByTagName("div");
                
                var isBaseDivPresent = false;
                //for( var i in childDivs ) {
                for (var i = 0; i < childDivs.length; i++) {
                    try {
                        if (divId == childDivs[i].getAttribute("id")) {
                            //log4javascript.getDefaultLogger().debug("Basediv already present. Quitting ... ");
                            childDivs[i].innerHTML = ""; 
                            break;
                        }
                    } 
                    catch (e) {
                    }
                }
            }
            //remove the html from the div
            //YAHOO.util.Dom.get(divId).innerHTML = ""; //ToDo --> should be done before css is removed.
            /*var divToDel = YAHOO.util.Dom.get(divId);
            var ancestorDiv = YAHOO.util.Dom.getAncestorByTagName(divToDel, 'div');
            ancestorDiv.removeChild(divToDel);*/
        }

        //get node for specified css file
        //var jsNode = document.getElementsByTagName( "head" ).item( 0 ).getElementById(widgetId + "_js");
        var jsNode = document.getElementById(widgetId + "_js");
        if(jsNode != null)
        {
            log4javascript.getDefaultLogger( ).debug( "found jsNode") ;
            //delete css from the document
            document.getElementsByTagName( "head" ).item( 0 ).removeChild( jsNode ) ; //ToDo --> head/baseDiv
        }
        /*//return the reference to the node specified in jsClass
        return (this.getDocumentNode("js", jsPath));*/
    }

    /**
     * This function returns the document node script/link depending upon the type of the file and file path.
     * It searched is document/header for the specified node and returns it
     * @param type Type of the file (js/css)
     * @param filePath path of the file
     * @return node reference to the specified file if exists; null otherwise
     */
    this.getDocumentNode = function( type, filePath )
    {
        var i;
        var typeVal;
        var pathVal;
        var linkNodes;

        //get the nodes of the type script/link depending upon the type js/css
        if("js" == type)
        {
            linkNodes = document.getElementsByTagName( "head" ).item( 0 ).getElementsByTagName("script");
        } else if("css" == type || "alt css" == type) {
            linkNodes = document.getElementsByTagName( "head" ).item( 0 ).getElementsByTagName("link");
        }

        if(linkNodes == null)
        {
            return null;
        }

        //loop through all the nodes to find the node having specified path
        var length = linkNodes.length;
        for(i=0;i<length;i++)
        {
            var linkNode = linkNodes[i];

            //check the type of the node
            typeVal = linkNode.getAttribute("type");
            if("js" == type) {
                if(typeVal == "text/javascript") {
                    pathVal = linkNode.getAttribute("src");
                    if(pathVal == filePath) {
                        return linkNode;
                    }
                }
            } else if("css" == type || "alt css" == type) {
                if(typeVal == "text/css") {
                    pathVal = linkNode.getAttribute("href");
                    if(pathVal == filePath) {
                        return linkNode;
                    }
                }
            }
        }

        //specified node not found; return null
        return null;
    }

   /**
    *
    */
    this.setActiveStyleSheet = function(title)
    {
        var i, a, main;
        for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
             if(a.getAttribute("rel").indexOf("alternate stylesheet") != -1
                && a.getAttribute("title")) {
                   a.disabled = true;
                   if(a.getAttribute("title") == title) a.disabled = false;
               }
         }
   }

   /**
    * This function downloads the widget JSON response from the Localization service and signals
    * the completion by the successEvent
    *
    * @param {Object} component The widget component
    * @param {Object} successEvent The event to be fired on success
    * @param {Object} failureEvent The event to be fired on failure
    *
    * This function
    */
    this.loadWidget = function( component, successEvent, failureEvent )
    {
        //log4javascript.getDefaultLogger( ).debug( "Loader::Loading widget " + component.widgetName ) ;
        var context = this ;
        var callback = {
            success: function(o) { context._onWidgetFileDownloaded( eval('('+o.responseText+')'), successEvent, component ) ; },
            failure: function(o) { log4javascript.getDefaultLogger( ).debug(o) ; failureEvent.fire( o ) ; }
        } ;
        var location = __baseClassPath + "localization/?widgetName=" + component.widgetName ;
        //Read the urlParamMap (defined in the [App layer] index.jsp) and adding all the parameters
        //to the localization URL query string
        for(var key in urlParamMap)
        {
            location = location + "&" + key + "=" + urlParamMap[key];
        }
        //log4javascript.getDefaultLogger( ).debug( "Loader ::: Adding Custom Localization Keys"  + component.localizationParams) ;
        if (typeof component.localizationParams != "undefined" && component.localizationParams != null) {
            for (var i = 0; i < component.localizationParams.length; i++) {
                //log4javascript.getDefaultLogger().debug("Loader ::: Adding Custom Localization Keys with " + component.localizationParams[i].name + " = " + component.localizationParams[i].value);
                location = location + "&" + component.localizationParams[i].name + "=" + component.localizationParams[i].value;
            }
        }
        //log4javascript.getDefaultLogger( ).debug( "Loader ::: Added Custom Localization Keys") ;
        //log4javascript.getDefaultLogger( ).debug( "Localization URL query for " + component.widgetId + " = " + location ) ;
        YAHOO.util.Connect.asyncRequest( "GET", location, callback ) ;
        return component.widgetId ;
    }

    /**
     * This function is called on the successful downloading of the JSON response of a widget.
     * The JSON object params are as follows -
     * {
     *     "type" : javascript/flash
     *     "js" : A name-value map of widgetId-JS code
     *     "css" : A name-value map of widgetId-CSS code
     *     "html" : A name-value map of widgetId-HTML code
     * }
     * @param {Object} responseObject The response JSON object
     * @param {Object} successEvent The event to be fired on success
     * @param {Object} component The event to be fired on failure
     */
    this._onWidgetFileDownloaded = function( responseObject, successEvent, component )
    {
        //log4javascript.getDefaultLogger( ).info( "Loader::Downloaded JSON object for " + component["widgetId"] ) ;

        component["type"] = responseObject["type"] ;
        component["js"] = responseObject["js"] ;

        if ("swf" == component["type"])
        {
            component["swf"] = (typeof responseObject["swf"] == "undefined") ? responseObject["swf"] : responseObject["swf"][component.widgetName] ;
        }
        else {
            component["css"] = (typeof responseObject["css"] == "undefined") ? responseObject["css"] : responseObject["css"][component.widgetName] ;
            if( typeof responseObject["html"] != "undefined" && responseObject["html"] != null )
            {
                var temp = this.getCount( responseObject["html"] ) ;
                component["html"] = (temp > 1)?responseObject["html"]:responseObject["html"][component.widgetName] ;
            }
            else{
                component["html"] = responseObject["html"] ;
            }
        }
        successEvent.fire( component["widgetId"] ) ;
    }

    this.getCount = function( responseObject ){

        var tempI = 0 ;
        for( var i in responseObject ) {
            tempI ++ ;
        }
        return tempI ;

    }
    /**
     * This function listens to the success event signaling the successful downloading of the HTML
     * template for the widget.
     *
     * @param {Object} responseText The HTML response file
     * @param {Object} successEvent The event to be fired on success
     * @param {Object} baseDivId The ID of the DIV to which the template is to be attached
     * @param {Object} widgetId The ID of the widget to which the template belongs to
     * @param {Object} context The required context
     */
    this._onHtmlFileDownloaded = function( responseText, successEvent, baseDivId, widgetId, context )
    {
        //log4javascript.getDefaultLogger( ).info( "Loader::Downloaded template for " + widgetId );

        YAHOO.util.Dom.get( baseDivId ).innerHTML = context.deParametrizeTemplate( responseText, widgetId ) ;

        successEvent.fire( widgetId ) ;
    }

    /**
     * This function de-parametrizes the text by removing all the '&MYSELF;' with the parent
     * widget references.
     *
     * @param {Object} templateText The text to be de-parametrized
     * @param {Object} parentWidgetId The ID of the parent widget
     */
    this.deParametrizeTemplate = function( templateText, parentWidgetId )
    {
        //log4javascript.getDefaultLogger( ).debug( "Loader::De-parametrizing template for " + parentWidgetId ) ;

        while (templateText.indexOf("&MYSELF;") != -1){
            templateText = templateText.replace("&MYSELF;", 'wtk.widgetInstances["' + parentWidgetId + '"]' ) ;
        }

        return templateText ;
    }

}
// END OF CLASS DEFINITION


/**
 * This function returns the package object for the name that has been passed. This method should be
 * called before adding a class to the package to ensure that the package exists; if not, the method
 * instantiates the package.
 * @param {String} packageName
 */
function getPackageForName( packageName )
{
    var arr = packageName.split('.');
    //log4javascript.getDefaultLogger().debug( packageName ) ;
    var obj = window;
    for(var i=0; i<arr.length; i++)
    {
      if(typeof obj[arr[i]] == 'undefined')
        obj[arr[i]] = {};
      obj = obj[arr[i]];
    }
    return obj;
}

/**
 * This function represents the com.forddirect.presentation.wtk.CommonUtils class.
 */
getPackageForName("com.forddirect.presentation.wtk").CommonUtils = function(){

    /**
     * Fixes PNG 8-bit opacity for IE6 and lower
     * @deprecated use PNGLoader instead
     * @param none
     */
    this.fixPngOpacity = function(){

        log4javascript.getDefaultLogger().debug("** " + wtk.path);
        this.fixPNGImg(YAHOO.util.Dom.get("preferencesPunchLine"));
        this.fixPNGImg(YAHOO.util.Dom.get("preferencesPart1Image"));
        this.fixPNGImg(YAHOO.util.Dom.get("preferencesPart2Image"));
        this.fixPNGBg(YAHOO.util.Dom.get("preferencesSelectorBg"), wtk.path, 'http://www2.showroom.fordvehicles.com/images/bg.png');


    }


    /**
     * Swithes background on selected part.
     * IE6 PNG 8-bit alpha opacity fix.
     * @deprecated use PNGLoader instead
     * @param none
     */
    this.switchPNGBg = function(selectedDivId){

        var parts = YAHOO.util.Dom.getElementsByClassName("preferences-part-bg");
        if (selectedDivId == "preferencesPart1") {
            this.fixPNGBg(parts[0], wtk.path, 'http://www2.showroom.fordvehicles.com/images/bg_selected.png');
            parts[1].style.filter = '';
            parts[1].style.backgroundColor = '#ffffff';
        }
        else {
            this.fixPNGBg(parts[1], wtk.path, 'http://www2.showroom.fordvehicles.com/images/bg_selected.png');
            parts[0].style.filter = '';
            parts[0].style.backgroundColor = '#ffffff';
        }

    }

    /**
     * Fixes IMG element PNG 8-bit alpha opacity in IE6
     * @deprecated use PNGLoader instead
     * @param {Object} img
     */
    this.fixPNGImg = function(img){

        var imgName = img.src.toUpperCase();
        if (imgName.substring(imgName.length - 3, imgName.length) == "PNG") {
            var temp = "id='" + img.id + "' ";
            var imgID = (img.id) ? temp : "";
            temp = "class='" + img.className + "' ";
            var imgClass = (img.className) ? temp : "";
            temp = "title='" + img.title + "' ";
            var _temp = "title='" + img.alt + "' ";
            var imgTitle = (img.title) ? temp : _temp;
            var imgStyle = "display:inline-block;" + img.style.cssText;
            if (img.align == "left")
                imgStyle = "float:left;" + imgStyle;
            if (img.align == "right")
                imgStyle = "float:right;" + imgStyle;
            if (img.parentElement.href)
                imgStyle = "cursor:hand;" + imgStyle;
            var strNewHTML = "<span " + imgID + imgClass + imgTitle +
            " style=\"" +
            "width:" +
            img.offsetWidth +
            "px; height:" +
            img.offsetHeight +
            "px;" +
            imgStyle +
            ";" +
            "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader" +
            "(src=\'" +
            img.src +
            "\', sizingMethod='scale');\"></span>";
            img.outerHTML = strNewHTML;
        }
    }

    /**
     * Fixes background with PNG 8-bit alpha opacity in IE6
     * @deprecated use PNGLoader instead
     * @param {Object} el
     * @param {Object} basePath
     * @param {Object} imagePath
     */
    this.fixPNGBg = function(el, basePath, imagePath){

        el.style.backgroundImage = "";
        el.style.background = "";
        var widgetPath = 'static/com/forddirect/presentation/widgets/ui/preferences/';
        log4javascript.getDefaultLogger().debug("--->" + basePath + " " + widgetPath + " " + imagePath + " " + wtk.path);
        var __temp = ';filter:progid:DXImageTransform.Microsoft.AlphaImageLoader' + '(src=\'' + basePath + widgetPath + imagePath + '\', sizingMethod=\'scale\');';
        el.style.cssText += __temp;

    }

    /**
     * Constructor for a custom locale object.
     *
     * @param {String} decimalPoint    String containing the symbol used as the decimal delimiter.
     * @param {String} thousandSep     String containing the symbol used as a separator for groups
     *                                 of digits to the left of the decimal delimiter.
     *
     * @param {Number} fracDigits      Number representing the number of fractional digits to be written
     *                                 to the right of the decimal delimiter.
     */
    this.locale = function(decimalPoint, thousandSep, fracDigits){
        this.decimalPoint = new String(decimalPoint);
        this.thousandSep = new String(thousandSep);
        this.fracDigits = fracDigits;
    }

    /**
     * Returns number num rounded to the fracDigits'th decimal.
     *
     * @param {Number} num
     * @param {Number} fracDigits
     */
    this.roundFloat = function(num, fracDigits){
        var factor = Math.pow(10, fracDigits);
        return (Math.round(num * factor) / factor);
    }

    /**
     * Returns a string representing num formatted with lc.decimalPoint as decimal separator.
     *
     * @param {Number} num
     * @param {Object} lc
     */
    this.toLcString = function(num, lc){
        var str = new String(num);
        var aParts = str.split(".");
        return (aParts.join(lc.decimalPoint));
    }

    /**
     * Returns a string representing num rounded to lc.fracDigits'th decimal
     * and formatted with lc.decimalPoint as decimal separator.
     *
     * @param {Number} num
     * @param {Object} lc
     */
    this.formatNum = function(num, lc){
        //Use the default locale, if NO locale provided
        if ("undefined" == typeof(lc) || null == lc || "" == lc) {
            lc = g_numLocale;
        }
        var sNum = new String(commUtils.roundFloat(num, lc.fracDigits));
        var aParts = sNum.split(".");
        var intPart = aParts[0];
        var t = [];
        var n = ((new String(intPart.length / 3)).split('.'))[0];


        intPart = intPart.split('');
        for (var i = 0; i < n; i++) {
            t.push((intPart.splice(intPart.length - 3, 3)).join(''));
        }
        if (intPart.length > 0) {
            t.push(intPart.join(''));
        }
        t.reverse();
        intPart = t.join(lc.thousandSep);

        aParts[0] = intPart;
        sNum = aParts.join('.');

        if (lc.fracDigits > 0) {
            if (sNum.indexOf(".") < 0) {
                sNum = sNum + ".";
                while (sNum.length < 1 + sNum.indexOf(".") + lc.fracDigits) {
                    sNum = sNum + "0";
                }
            }
        }
        return (commUtils.toLcString(sNum, lc));
    }

    /**
     * Returns the numerical value of the number contained in str
     * and formatted according to lc.thousandSep and lc.decimalPoint properties.
     *
     * @param {String} str
     * @param {Object} lc
     */
    this.parseLcNum = function(str, lc){
        var sNum = new String(str);
        var aParts = sNum.split(lc.thousandSep);
        sNum = aParts.join("");
        aParts = sNum.split(lc.decimalPoint);
        return (parseFloat(aParts.join(".")));
    }

    /**
     * Returns the URL to a stylized text string containing the requested text
     * and formatted according to the Themes defined in the servlet.
     * NOTE: It is the caller's responsibility to ensure the image is 'fixed' for IE6.
     * @param {String} text
     * @param {String} substyle
     * @param {int} height
     * @param {int} width
     */
    this.getURLforTextImage = function(text, substyle, height, width){
        // TODO: Get the theme from something like wtk.loader.deParametrizeTemplate( "theme", widgetId ) ;
        // (or should each widget need to query for the skin/theme being used?  that seems odd.)
        var skin = urlParamMap["Skin"].toLowerCase();
        log4javascript.getDefaultLogger().debug( "CommUtils: skin = " + skin );
        return __baseClassPath + "text/img.png?theme=" + skin + "." + substyle +
        (height > 0 ? ("&height=" + height) : "") +
        (width > 0 ? ("&width=" + width) : "") +
        "&text=" +
        encodeURI(text).replace("+", "%2B");
    }

    /**
     * Returns an HTML IMG tag &lt;IMG SRC=...&gt; containing the requested text.
     * NOTE: It is the caller's responsibility to ensure the image is 'fixed' for IE6.
     * @param {String} text
     * @param {String} substyle
     * @param {int} height
     * @param {int} width
     */
    this.getHTMLImgForText = function(text, substyle, height, width){
        log4javascript.getDefaultLogger().debug("CommonUtils::getHTMLImgForText(" + text + ",...)");
        return "<IMG SRC=\"" + this.getURLforTextImage(text, substyle, height, width) + "\" " +
        (height > 0 ? (" HEIGHT=\"" + height + "\" ") : "") +
        (width > 0 ? (" WIDTH=\"" + width + "\" ") : "") +
        " ALT=\"" +
        text.replace("\"", "&quot;") +
        "\"/>";
    }

    this.isIE6 = (parseInt(navigator.appVersion.split('MSIE')[1]) <= 6);

    // id property is required
    this.getImgObjectForText = function(text, substyle, height, width){
        log4javascript.getDefaultLogger().debug("CommonUtils::getImgObjectForText(" + text + ",...)");
        var ret = new Image();
        var theurl = this.getURLforTextImage(text, substyle, height, width);
        ret.src = theurl;
        ret.alt = text;
        if (height > 0) {
            ret.height = height;
        }
        if (width > 0) {
            ret.width = width;
        }
        if (this.isIE6) {
            log4javascript.getDefaultLogger().debug("CommonUtils::getImgObjectForText fixing image for IE6");
            // trying to fix - there appears to be some kind of race condition on this one though.
            PNGLoader.attachPNG(ret, theurl, {
                isBackground: false,
                overrideDimensions: false,
                sizingMethod : "crop"
            });
        }
        return ret;
    }

    /**
     * Returns true if the object is defined and NOT null, else false
     * @param {Object} value to be checked
     */
    this.isDefinedAndNotNull = function(value){
        if (typeof value != "undefined" && null != value && 'null' != value) {
            return true;
        }
        else {
            return false;
        }
    }
	
    /**
     * Formats the value passed for display
     * @param value 
     * @param prefix
     * @param suffix
     * @return If the value argument represents a number, then the prefix and suffix are added to it and returned. Otherwise, the
     *         value field is returned as it is.
     */
    this.formatValue = function( /*String or Number*/value, /*String*/prefix, /*String*/suffix ){
        // If it is not an integer, return as it is
        var intVal = parseInt( value );
        if (value != intVal) {
            return value;
        }
        
        // Check if value is not available
        if (intVal < 0) {
            return "NA";
        }
        
        // Get the comma-separated format
        var strVal = new String( intVal );
        var resultStr = "";
        while (strVal.length > 3) {
            var substr = strVal.substr( strVal.length - 3 );
            strVal = strVal.substr( 0, strVal.length - 3 );
            resultStr = "," + substr + resultStr;
        }
        resultStr = strVal + resultStr;
        
        return prefix + resultStr + suffix;
    }

}

var commUtils = new com.forddirect.presentation.wtk.CommonUtils();
//Defining a global locale for the formatted number. This is the default locale used,
//if NO locale is specifically passed to the function
var g_numLocale = new commUtils.locale('.', ',', 0);
/**
 * This file contains the constants to be used in BP2 application widgets.
 */
var Constants = {
    "VehicleName": {
        "Default": "BP2_Pages_VehicleName_Regular",
        "Expanded": "BP2_Pages_VehicleName_Expanded",
        "PowerBar": "BP2_Pages_VehicleName_PowerBar",
        "PrintPage": "BP2_Pages_VehicleName_PrintPage",
        "PreconfigTitle": "BP2_Pages_VehicleName_PreconfigTitle",
        "Short": "BP2_Pages_VehicleName_Short",
        "Compare": "BP2_Pages_VehicleName_Compare",
        "FlipTitle": "BP2_Pages_VehicleName_FlipTitle"
    },
    
    "Config": {
        "ExteriorBodyColorAttribute": "EXTERIOR_BODY_COLOR",
        "InteriorBodyColorAttribute": "INTERIOR_BODY_COLOR",
        "DEFINER_PAGES": 0,
        "CONFIG_PAGES": 1,
        "PRECONFIG_MISC_PAGES": 2,
        "MISC_PAGES": 3,
        "PRECONFIG_SPLIT_PAGE": 4,
        "PRECONFIG_CONFIG_PAGE": 5
    },
    
    "PartState": {
        "SELECTED": "SELECTED",
        "INCLUDED": "INCLUDED",
        "STANDARD": "STANDARD_PRODUCT",
        "SELECTABLE": "SELECTABLE",
        "EXCLUDED": "EXCLUDED",
        "PRODUCT_EXCLUDED": "PRODUCT_EXCLUDED"
    },
    
    "Inventory": {
        "VehicleImagesSmallAttribute": "BP2_VehicleImages_Inventory_Small",
        "ComingSoonTitleAttribute": "BP2_Copy_Inventory_ComingSoonTitle",
        "ComingSoonSorryTextAttribute": "BP2_Copy_Inventory_ComingSoonSorryText",
        "ComingSoonDealerCanHelpAttribute": "BP2_Copy_Inventory_ComingSoonDealerCanHelp",
        "EmptyEnabledAttribute": "InventoryEmptyEnabled",
        "ComingSoonEnabledAttribute": "InventoryComingSoonEnabled",
        "VehicleNameAttribute": "BP2_Pages_Inventory_Vehicle_Name",
        "EmptyMessageAttribute": "InventoryEmptyMessage",
        "TransmissionsAttribute": "Transmissions",
        "SpecialPackageAttribute": "Special Package",
        "EngineAttribute": "Engine",
        "DefinersClass": "BP2_Pages_Inventory_DefinersClass",
        "SelectedDefiners": "BP2_Pages_Inventory_SelectedDefiners"
    },
    
    "Client": {
        "SpeedHighAttribute": "SPEED_HIGH",
        "SpeedLowAttribute": "SPEED_LOW",
        "BWHighAttribute": "BW_HIGH",
        "BWLowAttribute": "BW_LOW",
        "HighAttribute": "HIGH",
        "MediumAttribute": "MEDIUM",
        "LowAttribute": "LOW"
    },
    
    "Pricing": {
        "PriceMSRPAttribute": "PRICE_MSRP",
        "PriceNetMSRPAttribute": "PRICE_NET_MSRP",
        "PriceInvoiceAttribute": "PRICE_INVOICE",
        "AZ_PLAN_LABEL": "A/Z-Plan",
        "X_PLAN_LABEL": "X-Plan",
        "AZ_PLAN_STR": "AZ",
        "X_PLAN_STR": "X"
    },
    
    "Leads": {
        "OVMTCAttributeAttribute": "OVMTC",
        "ReferrerAttribute": "REFERRER",
        "ReferrerUrlAttribute": "REFERRER_URL",
        "ReferingSiteAttribute": "REFERRING_SITE",
        "CampaignIdAttribute": "CAMPAIGN_ID",
        "ReferredOnDateAttribute": "REFERRED_ON_DATE",
        "LeadSourceLinkshareAttribute": "LEAD_SOURCE_LINKSHARE",
        "LeadSourceMyFolderAttribute": "LEAD_SOURCE_MY_FOLDER",
        "LeadSourceFordVehiclesAttribute": "LEAD_SOURCE_FORDVEHICLES",
        "LeadSourceFordComAttribute": "LEAD_SOURCE_FORD_COM",
        "LeadSourceSearchEngineAttribute": "LEAD_SOURCE_SEARCHENGINE",
        "LeadSourceDealerDirectAttribute": "LEAD_SOURCE_DEALERDIRECT",
        "LeadSourceAXZPlanAttribute": "LEAD_SOURCE_AXZPLAN",
        "LeadSourceUnknownAttribute": "LEAD_SOURCE_UNKNOWN",
        "LeadSourceMarketingPartnerAttribute": "LEAD_SOURCE_MARKETINGPARTNER",
        "LeadSourceDealerConnectionAttribute": "LEAD_SOURCE_DEALERCONNECTION",
        "LeadSourceDealerConnectionDirectUrlAttribute": "LEAD_SOURCE_DEALERCONNECTION_DIRECT_URL",
        "LeadSourceFordVehiclesPkgAttribute": "LEAD_SOURCE_FORDVEHICLES_PKG",
        "LinkshareAttribute": "LINKSHARE",
        "FordDotComAttribute": "FORD_DOT_COM",
        "FordVehiclesAttribute": "FORD_VEHICLES",
        "SearchEngineSiteAttribute": "SEARCH_ENGINE_SITE",
        "UnknwonMarketingAffiliateAttribute": "UNKNOWN_MARKETING_AFFILIATE",
        "FordDirectAttribute": "FORDDIRECT",
        "DCSubsourceFordAttribute": "DC_SUBSOURCE_FORD",
        "UnknownSourceAttribute": "UNKNOWN_SOURCE",
        "SkipDealerQuoteType": "FDAF"
    },
    
    "PreConfig": {
        "ChooseYourPathPreConfigButtonTextAttribute": "BP2_Copy_Pages_ChooseYourPath_PreConfigButtonText",
        "ChooseYourPathPreConfigAssetAttribute": "BP2_Pages_ChooseYourPath_PreConfigAsset",
        "ChooseYourPathPreConfigDescTextAttribute": "BP2_Copy_Pages_ChooseYourPath_PreConfigDescText",
        "ChooseYourPathBuildButtonTextAttribute": "BP2_Copy_Pages_ChooseYourPath_BuildButtonText",
        "ChooseYourPathBuildAssetAttribute": "BP2_Pages_ChooseYourPath_BuildAsset",
        "ChooseYourPathBuildDescTextAttribute": "BP2_Copy_Pages_ChooseYourPath_BuildDescText",
        "PreConfigSelectSubTitleTextAttribute": "BP2_Copy_Pages_PreConfigSelect_SubTitleText",
        "PreConfigSelectPriceTextAttribute": "BP2_Copy_Pages_PreConfigSelect_PriceText",
        "PreConfigSelectNextStepsAttribute": "BP2_Copy_Pages_PreConfigSelect_NextSteps",
        "PreConfigSelectBuildDescTextAttribute": "BP2_Copy_Pages_PreConfigSelect_BuildDescText",
        "PreConfigSelectTitleTextAttribute": "BP2_Copy_Pages_PreConfigSelect_TitleText",
        "PreConfigSelectSearchInventoryAttribute": "BP2_Copy_Pages_PreConfigSelect_SearchInventory",
        "PreConfigSelectBuildButtonTextAttribute": "BP2_Copy_Pages_PreConfigSelect_BuildButtonText",
        "PreConfigSelectWhyBuyCategoriesAttribute": "BP2_Copy_Pages_PreConfigSelect_WhyBuyCategories",
        "PreConfigDetailsTotalInvoiceAttribute": "BP2_Copy_Pages_PreConfigDetails_TotalInvoice",
        "PreConfigDetailsDetailsAttribute": "BP2_Copy_Pages_PreConfigDetails_Details",
        "PreConfigDetailsDestDelAttribute": "BP2_Copy_Pages_PreConfigDetails_DestDel",
        "PreConfigDetailsBasePriceAttribute": "BP2_Copy_Pages_PreConfigDetails_BasePrice",
        "PreConfigDetailsExteriorAttribute": "BP2_Copy_Pages_PreConfigDetails_Exterior",
        "PreConfigDetailsTotalOptionsAttribute": "BP2_Copy_Pages_PreConfigDetails_TotalOptions",
        "PreConfigDetailsSeeWhatsStandardAttribute": "BP2_Copy_Pages_PreConfigDetails_SeeWhatsStandard",
        "PreConfigDetailsMSRPAttribute": "BP2_Copy_Pages_PreConfigDetails_MSRP",
        "PreConfigDetailsSelectYourColorAttribute": "BP2_Copy_Pages_PreConfigDetails_SelectYourColor",
        "PreConfigDetailsPrintSummaryAttribute": "BP2_Copy_Pages_PreConfigDetails_PrintSummary",
        "PreConfigDetailsInteriorAttribute": "BP2_Copy_Pages_PreConfigDetails_Interior",
        "PreConfigDetailsSearchInventoryAttribute": "BP2_Copy_Pages_PreConfigDetails_SearchInventory",
        "PreConfigDetailsExterior_AssetAttribute": "BP2_Pages_PreConfigDetails_Exterior_Asset",
        "PreConfigDetailsInterior_AssetAttribute": "BP2_Pages_PreConfigDetails_Interior_Asset"
    },
    
    "quoteForm_GIP": {
        "SUBMIT_BUTTON_THEME": "QUOTE_MEDIUM_ARROW",
        "SUBMIT_BUTTON_TEXT": "PRECIO EN INTERNET",
        "SUBMIT_BUTTON_WIDTH": 155
    },
    
    "quoteForm": {
        "SUBMIT_BUTTON_THEME": "LARGE",
        "SUBMIT_BUTTON_TEXT": "ENVIAR",
        "SUBMIT_BUTTON_WIDTH": 132
    },
    
    "Common": {
        "DISABLED": "DISABLED",
        "ERROR": "ERROR",
        "UPDATED": "UPDATED",
        "UPDATING": "UPDATING",
        "SELECTED": "SELECTED",
        "INCLUDED": "INCLUDED",
        "COMPLETED": "COMPLETED",
        "TPOLevelAttribute": "TPOLevel",
        "ModelGroupAttribute": "ModelGroup",
        "BodyStyleAttribute": "BodyStyle",
        "ExteriorPartClassPaintTypeAttribute": "Paint Type",
        "InteriorPartClassTrimColourAttribute": "Interior Trim Colour",
        "MultimapURL": "BP2_Settings_MultiMapURL"
    },
    "Compare": {
        "VehicleImagesCompare": "BP2_VehicleImages_Compare",
        "ComparePartClassAttribute": "BP2_Pages_Compare_PartClasses"
    },
    "Disclaimer": {
        "FDShowroom": [{
            "text": "El Pago en Efectivo del Cliente, la Bonificaci\363n en Efectivo de Ford Credit y otros incentivos pueden variar seg\372n el lugar de residencia del cliente. Contacta a tu Concesionario Ford para obtener todos los detalles sobre la elegibilidad del programa. Bonificaci\363n en Efectivo de Ford Credit compatible con Oferta de Financiamiento de Ford Credit."
        }, {
            "text": "Los c\341lculos del Pago por arrendamiento son s\363lo estimaciones y se basan en el c\341lculo del millaje anual determinado por el concesionario. Se calcula un cargo por cada millaje recorrido que exceda este l\355mite. Consulta a tu concesionario para obtener m\341s detalles."
        }, {
            "text": "El Precio Base Total Minorista Sugerido por el Fabricante y Opciones (MSRP Total) es el precio que el fabricante recomienda para un veh\355culo que incluye todas las opciones seleccionadas. El MSRP Base Total y Opciones puede no incluir cargos por destino y gastos de despacho. El MSRP Base Total no incluye impuestos a las ventas o de otra \355ndole, etiquetas, tarifas de registro, tarifas gubernamentales, certificados de control de contaminaci\363n, cargos por pruebas de emisi\363n de gases, cargos por financiaci\363n ni cargos por elaboraci\363n de documentos del concesionario. El precio y la disponibilidad var\355an, entre otros, seg\372n la Regi\363n, las Opciones, el Concesionario, la Marca, el Modelo y los Costos. Estos datos se brindan \372nicamente con fines informativos y s\363lo en tu concesionario local podr\341s obtener los precios reales."
        }, {
            "text": "Las im\341genes que se muestran no representan necesariamente las opciones configurables seleccionadas o disponibles en el veh\355culo."
        }],
        "FVShowroom": [{
            "text": "Precio Inicial Minorista Sugerido por el Fabricante, sin incluir cargo por destino/entrega, impuestos, t\355tulo y tarifas de registro. Equipo opcional no incluido."
        }, {
            "text": "Estimaci\363n de mpg en ciudad y en carretera seg\372n la EPA, basada en la configuraci\363n de motor/transmisi\363n."
        }],
        "FuelDisclaimer": "Debido a la amplia variedad de carga y condiciones operativas del veh\355culo entre las distintas aplicaciones de los clientes, las camionetas con un peso bruto de m\341s de 8,500 libras no se incluyen en el sistema de calificaci\363n de consumo de combustible de la EPA."
    },
    "ContextMap" : {
        "Page ID:M0.1" : ["#1", "#2","#12","#13"], 
        "Page ID:M0.2" : ["#3", "#4", "#5", "#6", "#2","#12","#13"],
        "Page ID:M1.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M2.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M3.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M4.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M5.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M6.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:M7.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:F1.0" : ["#1", "#2", "#3"],
        "Page ID:F2.1" : ["#1", "#2", "#3"],
        "Page ID:F2.2" : ["#1", "#2", "#3"],
        "Page ID:F2.3" : ["#1", "#2", "#3"],
        "Page ID:F3.1" : ["#1", "#2", "#3"],
        "Page ID:F3.2" : ["#1", "#2", "#3"],
        "Page ID:F3.3" : ["#1", "#2", "#3"],
        "Page ID:F4.1" : ["#1", "#2", "#3", "#8"],
        "Page ID:F4.2" : ["#1", "#2", "#3", "#9", "#11"],
        "Page ID:F4.3" : ["#1", "#2", "#3", "#8"],
        "Page ID:F5.1" : ["#1", "#2", "#3"],
        "Page ID:F5.2" : ["#1", "#2", "#3"],
        "Page ID:F6.0" : ["#1", "#2", "#3"],
        "Page ID:F7.1A": ["#1", "#2", "#3", "#7"],
        "Page ID:F7.1B": ["#1", "#2", "#3", "#7"],
        "Page ID:F7.2" : ["#1", "#2", "#3", "#7"],
        "Page ID:F7.3" : ["#1", "#2", "#3", "#7"],
        "Page ID:F7.4" : ["#1", "#2", "#3", "#7"],
        "Page ID:F7.5" : ["#1", "#2", "#3", "#7"],
        "Page ID:F7.6" : ["#1", "#2", "#3", "#7"],
        "Page ID:F7.7" : ["#1", "#2", "#3", "#7"],
        "Page ID:F8.1" : ["#1", "#2", "#3"],
        "Page ID:F8.1.1":["#1", "#2", "#3"],
        "Page ID:F8.2" : ["#1", "#2", "#3"],
        "Page ID:F8.2.1":["#1", "#2", "#3"],
        "Page ID:F9.1" : ["#1", "#2", "#3"],
        "Page ID:F9.2" : ["#1", "#2", "#3"],
        "Page ID:F9.3" : ["#1", "#2", "#3"],
        "Page ID:F10.1": ["#1", "#2", "#3"],
        "Page ID:F11.1": ["#1", "#2", "#3"],
        "Page ID:F11.3": ["#1", "#2", "#3"],
        "Page ID:F12.0": ["#1", "#2", "#3"],
        "Page ID:F13.0": ["#1", "#2", "#3"],
        "Page ID:F13.1" : ["#1", "#2", "#3"],
        "Page ID:P1.0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:P2.0" : ["#1", "#2", "#3", "#4", "#5", "#6"], 
        "Page ID:P2.1" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Page ID:P3:0" : ["#1", "#2", "#3", "#4", "#5", "#6"],
        "Payment Estimator:Retail/Buy" : [],
        "Payment Estimator:Lease Only" : [],
        "Payment Estimator:Both" : []
    },
    "DisclaimerMap" : {
        "#1" : {"text" : 'Precio Inicial Minorista Sugerido por el Fabricante, sin incluir cargo por destino/entrega, impuestos, t\355tulo y tarifas de registro. Equipo opcional no incluido.', "type" : ''},
        "#2" : {"text" : 'Estimaci\363n de mpg en ciudad y en carretera seg\372n la EPA, basada en la configuraci\363n de motor/transmisi\363n.', "type" : ''},
        "#3" : {"text" : 'El Pago en Efectivo del Cliente, la Bonificaci\363n en Efectivo de Ford Credit y otros incentivos pueden variar seg\372n el lugar de residencia del cliente. Contacta a tu Concesionario Ford para obtener todos los detalles sobre la elegibilidad del programa. Bonificaci\363n en Efectivo de Ford Credit compatible con Oferta de Financiamiento de Ford Credit.', "type" : ''},
        "#4" : {"text" : 'Los c\341lculos del Pago por arrendamiento son s\363lo estimaciones y se basan en el c\341lculo del millaje anual determinado por el concesionario. Se calcula un cargo por cada millaje recorrido que exceda este l\355mite. Consulta a tu concesionario para obtener m\341s detalles.', "type" : ''},
        "#5" : {"text" : "El Precio Base Total Minorista Sugerido por el Fabricante y Opciones (MSRP Total) es el precio que el fabricante recomienda para un veh\355culo que incluye todas las opciones seleccionadas. El MSRP Base Total y Opciones puede no incluir cargos por destino y gastos de despacho. El MSRP Base Total no incluye impuestos a las ventas o de otra \355ndole, etiquetas, tarifas de registro, tarifas gubernamentales, certificados de control de contaminaci\363n, cargos por pruebas de emisi\363n de gases, cargos por financiaci\363n ni cargos por elaboraci\363n de documentos del concesionario. El precio y la disponibilidad var\355an, entre otros, seg\372n la Regi\363n, las Opciones, el Concesionario, la Marca, el Modelo y los Costos. Estos datos se brindan \372nicamente con fines informativos y s\363lo en tu concesionario local podr\341s obtener los precios reales.", "type" : ''},
        "#6" : {"text" : 'Las im\341genes que se muestran no representan necesariamente las opciones configurables seleccionadas o disponibles en el veh\355culo.', "type" : ''},
        "#7" : {"text" : 'En general, el inventario del concesionario se actualiza diariamente, pero no hay garant\355a de que el inventario que se muestra est\351 disponible en el concesionario. Los cambios en la fabricaci\363n del modelo a mediados de a\361o y los accesorios agregados por el concesionario en el veh\355culo real pueden diferir de las opciones y caracter\355sticas enumeradas. Los veh\355culos identificados como \047Coincidencias Exactas\047 tienen un precio o caracter\355sticas diferentes no presentados en el sitio.', "type" : ''},
        "#9" : {"text" : 'El monto total a pagar por el arrendatario antes o al momento de la firma del arrendamiento o de la entrega del veh\355culo. El Monto Adeudado Estimado a la Firma del Arrendamiento se calcula despu\351s de la aplicaci\363n de todos los incentivos y reembolsos que califiquen. No incluye impuestos, t\355tulo y/o tarifas de registro. Incluye el pago del primer mes, una tarifa de adquisici\363n de $595 y cualquier pago inicial. Exento del Dep\363sito de Garant\355a.', "type" : ''},
        "#10" : {"text" : 'La capacidad de carga y pasajeros est\341 limitada por el peso y su distribuci\363n.', "type" : 'dynamic'},
        "#11" : {"text" : 'NO TODOS LOS ARRENDATARIOS CALIFICAR\301N PARA EL PAGO POR ARRENDAMIENTO RED CARPET M\301S BAJO<p>Pago por arrendamiento basado en el Arrendamiento Red Carpet de Ford Credit. Los precios y pagos son S\323LO ESTIMATIVOS e incluyen cargos de Destino y Despacho.</p><p>Los pagos por arrendamiento ser\341n m\341s elevados de lo estimado en Arkansas, Connecticut, Kentucky, Massachusetts, Missouri, North Carolina, Rhode Island,Texas, Virginia, West Virginia.</p><p>El Monto Adeudado Estimado a la Firma del Arrendamiento se calcula despu\351s de la aplicaci\363n de todos los incentivos y reembolsos. No incluye impuestos, t\355tulo y/o tarifas de registro. Incluye el pago del primer mes, una tarifa de adquisici\363n de $595 y cualquier Pago Inicial. Exento del Dep\363sito de Garant\355a.</p><p>El arrendatario tiene la opci\363n de comprar un veh\355culo al finalizar el arrendamiento al precio negociado con el concesionario al momento de la firma o el inicio del arrendamiento. El arrendatario es responsable por el Desgaste excesivo y por el Millaje superior a las \042millas permitidas por el per\355odo\042 seg\372n se detalla a continuaci\363n:</p><p>$0.15 por milla para veh\355culos con MSRP de hasta $30,000</p><p>$0.20 por milla para veh\355culos con MSRP entre $30,001 y $49,999</p><p>$0.25 por milla para veh\355culos con MSRP mayor a $50,000</p><p>Ten en cuenta: EL PRECIO Y LOS PAGOS REALES pueden diferir debido a reembolsos locales, ofertas especiales, tarifas y calificaciones crediticias. Consulta a tu concesionario el precio real, los pagos y todos los detalles.</p><p>Se puede requerir el financiamiento de Ford Credit para ofertas de bonificaci\363n en efectivo. No todos los compradores calificar\341n para el financiamiento de Ford Credit. Consulta el concesionario para las calificaciones y los detalles completos. Las ofertas de bonificaci\363n en efectivo de la Calculadora de Pagos son compatibles con APR independiente, pago en efectivo del cliente independiente y efectivo m\341s ofertas de incentivos APR.</p>', "type" : ''},
        "#12" : {"text" : 'Remolque: Correctamente equipado.</p>', "type" : ''},
        "#13" : {"text" : 'No somos responsables de errores tipogr\341ficos y otros, como transmisiones de datos o errores de software, que puedan aparecer en el sitio. Si el precio, incentivo, oferta u otro servicio publicado es incorrecto debido a un error tipogr\341fico u otro, tu concesionario local es el \372nico responsable del precio, oferta o incentivo correcto. Nos esforzamos para proporcionar la informaci\363n m\341s precisa y actualizada, sin embargo, es tu responsabilidad verificar con tu concesionario local que los detalles sean exactos. </p>', "type" : ''}
        
        
    },
    "APR_Percentages" : [0.0,0.9,1.9,2.9,3.9,4.9,5.9,6.9,7.9,8.9],
    "APR_Months" : [36,48,60,72],
    "APR_Rates" : [
        [27.28,28.16,28.60,29.04,29.48,29.93,30.38,30.83,31.29,31.75],
        [20.83,21.22,21.65,22.09,22.53,22.98,23.44,23.90,24.37,24.84],
        [16.67,17.05,17.48,17.92,18.37,18.83,19.29,19.75,20.23,20.71],
        [13.89,14.27,14.71,15.15,15.60,16.06,16.53,17.00,17.48,17.98]
    ]
};
// Generated Code. Please don't edit
getPackageForName( 'com.forddirect.application.bp20.metrics' ).MetricsTracker = function(){
this.trackMacroData = function(pageName){
switch(pageName){
case 'Showroom':
   this.Showroom();
   break;
case 'ShowroomCar':
   this.ShowroomCar();
   break;
case 'ShowroomTruck':
   this.ShowroomTruck();
   break;
case 'ShowroomSuv':
   this.ShowroomSuv();
   break;
case 'ShowroomCrossover':
   this.ShowroomCrossover();
   break;
case 'ShowroomFuture':
   this.ShowroomFuture();
   break;
case 'ShowroomVehicle':
   this.ShowroomVehicle();
   break;
case 'ShowroomSelectCurrent':
   this.ShowroomSelectCurrent();
   break;
case 'ShowroomSelectPrevious':
   this.ShowroomSelectPrevious();
   break;
default: 
}
}
this.trackMicroData = function(onClickName){
switch(onClickName){
case 'Showroom360':
   this.Showroom360();
   break;
case 'ShowroomPhotos':
   this.ShowroomPhotos();
   break;
case 'ShowroomLearn':
   this.ShowroomLearn();
   break;
case 'ShowroomInternetPrice':
   this.ShowroomInternetPrice();
   break;
case 'ShowroomSearchInventory':
   this.ShowroomSearchInventory();
   break;
case 'ShowroomBuildPrice':
   this.ShowroomBuildPrice();
   break;
case 'ReferralExitsFV':
   this.ReferralExitsFV();
   break;
case 'ReferralExitsFMCC':
   this.ReferralExitsFMCC();
   break;
case 'ReferralExitsFR':
   this.ReferralExitsFR();
   break;
case 'ReferralExitsSYNC':
   this.ReferralExitsSYNC();
   break;
case 'ReferralExitsCPO':
   this.ReferralExitsCPO();
   break;
case 'ReferralExitsFLMO':
   this.ReferralExitsFLMO();
   break;
case 'ReferralExitsGENS':
   this.ReferralExitsGENS();
   break;
case 'ReferralExitsGFA':
   this.ReferralExitsGFA();
   break;
case 'ReferralExitsMerchandise':
   this.ReferralExitsMerchandise();
   break;
default: 
}
}
//Start of onPageLoad function calls
this.Showroom = function(){
s.pageName = 'fv: showroom '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='fv: showroom';
s.prop11='fv: showroom';
s.referrer= this.referrer();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomCar = function(){
s.pageName = 'fv: showroom: car '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='fv: showroom: car';
s.prop11='fv: showroom: car';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomTruck = function(){
s.pageName = 'fv: showroom: truck '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='fv: showroom: truck';
s.prop11='fv: showroom: truck';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomSuv = function(){
s.pageName = 'fv: showroom: suv '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='fv: showroom: suv';
s.prop11='fv: showroom: suv';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomCrossover = function(){
s.pageName = 'fv: showroom: crossover '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='fv: showroom: crossover';
s.prop11='fv: showroom: crossover';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomFuture = function(){
s.pageName = 'fv: showroom: future '+this.namePlate();
//Variables for - Omniture

s.hier1='showroom:future';
s.eVar11='fv: showroom: future';
s.prop11='fv: showroom: future';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomVehicle = function(){
s.pageName = 'fv: showroom: vehicle: '+this.namePlate();
//Variables for - Omniture

s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar11='fv: showroom: vehicle';
s.prop11='fv: showroom: vehicle';
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomSelectCurrent = function(){
s.pageName = 'dd: showroom: select: current'+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='dd: showroom: select';
s.prop11='dd: showroom: select';
s.referrer= this.referrer();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4='eng';
s.prop4='eng';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}
this.ShowroomSelectPrevious = function(){
s.pageName = 'dd: showroom: select: previous'+this.namePlate();
//Variables for - Omniture

s.hier1='showroom';
s.eVar11='dd: showroom: select';
s.prop11='dd: showroom: select';
s.referrer= this.referrer();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4='eng';
s.prop4='eng';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

void(s.t());
this.clearVars();
}

//End of onPageLoad function calls
//Start of onClick event function calls
this.Showroom360 = function(){

//Variables for - Omniture
s.linkTrackVars='eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: 360';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: 360');
this.clearVars();
}
this.ShowroomPhotos = function(){

//Variables for - Omniture
s.linkTrackVars='eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: photos';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: photos');
this.clearVars();
}
this.ShowroomLearn = function(){

//Variables for - Omniture
s.linkTrackVars='eVar13,prop13,eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar13='fv: showroom: learn more';
s.prop13='fv: showroom: learn more';
s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: learn more';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: learn more');
this.clearVars();
}
this.ShowroomInternetPrice = function(){

//Variables for - Omniture
s.linkTrackVars='eVar13,prop13,eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar13='fv: showroom: internet price';
s.prop13='fv: showroom: internet price';
s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: internet price';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: internet price');
this.clearVars();
}
this.ShowroomSearchInventory = function(){

//Variables for - Omniture
s.linkTrackVars='eVar13,prop13,eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar13='fv: showroom: search inventory';
s.prop13='fv: showroom: search inventory';
s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: search inventory';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: search inventory');
this.clearVars();
}
this.ShowroomBuildPrice = function(){

//Variables for - Omniture
s.linkTrackVars='eVar13,prop13,eVar16,prop16,hier1,eVar12,prop12,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel';

s.eVar13='fv: showroom: build price';
s.prop13='fv: showroom: build price';
s.eVar16= this.namePlate();
s.prop16= this.namePlate();
s.hier1= this.hierarchy();
s.eVar12= this.modelYear();
s.prop12= this.modelYear();
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='fv: showroom: build price';
s.eVar14= this.client();
s.prop14= this.client();
s.channel= this.channel();

s.tl(this,'o','fv: showroom: build price');
this.clearVars();
}
this.ReferralExitsFV = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='fv: home';
s.eVar6='fv: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: fv: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsFMCC = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='fmcc: home';
s.eVar6='fmcc: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: fmcc: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsFR = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='ford racing: home';
s.eVar6='ford racing: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: ford racing: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsSYNC = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='sync: home';
s.eVar6='sync: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: sync: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsCPO = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='cpo: home';
s.eVar6='cpo: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: cpo: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsFLMO = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='flmo: home';
s.eVar6='flmo: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: flmo: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsGENS = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='gens: home';
s.eVar6='gens: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: gens: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsGFA = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='gfa: home';
s.eVar6='gfa: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: gfa: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}
this.ReferralExitsMerchandise = function(){

//Variables for - Omniture
s.linkTrackVars='hier1,prop6,eVar6,campaign,eVar15,prop15,eVar4,prop4,prop5,eVar14,prop14,channel,events';

s.hier1='home';
s.prop6='merchandise: home';
s.eVar6='merchandise: home';
s.campaign= this.bannerID();
s.eVar15= this.site();
s.prop15= this.site();
s.eVar4= this.userLanguage();
s.prop4= this.userLanguage();
s.prop5='referral: merchandise: home';
s.eVar14= this.client();
s.prop14= this.client();
s.channel='home';

//Events for - Omniture
var conditionalEvents = '';
if( conditionalEvents.length > 0 ){
conditionalEvents = conditionalEvents.substring(0, conditionalEvents.length - 1)

s.linkTrackEvents='event4,'+conditionalEvents+';'
s.events='event4,'+conditionalEvents;
}else{
s.linkTrackEvents='event4';
s.events='event4';

}s.tl(this,'o','referral: exit');
this.clearVars();
}

//End of onClick event function calls
//Start of getters
this.namePlate = function(){ 

                if ( !commUtils.isDefinedAndNotNull(wtk.widgetInstances["showroom"])){
                return "";
                }
                return (wtk.widgetInstances["showroom"].getSelectedModelMake()+" "+wtk.widgetInstances["showroom"].getSelectedModelName()).toLowerCase();
            
}
this.hierarchy = function(){ 

                return 'showroom:' + this.modelYear() + ":" +wtk.widgetInstances["showroom"].getSelectedSpaceName() + ":" + this.namePlate();
            
}
this.modelYear = function(){ 

                if ( !commUtils.isDefinedAndNotNull(wtk.widgetInstances["showroom"])){
                return "";
                }
                return wtk.widgetInstances["showroom"].getSelectedModelYear();
            
}
this.referrer = function(){ 

            if ( !commUtils.isDefinedAndNotNull(urlParamMap.document_referrer)){
                return "";
            }else{
                return urlParamMap.document_referrer;
            }
            
}
this.bannerID = function(){ 

                var s_campaign = "";
                
                /* 
                    * If the bannerid in the query string is null, check the  cookies for bannerid. If it is there, assign to s_campaign 
                    * variable. if it is not there then find it from urlParamMap and assign to s_campaign variable.
                */
             
                var shared_bannerid = cookieManager.readCookie("bannerID");
                if (shared_bannerid != null && shared_bannerid != "") {
                        s_campaign = shared_bannerid;
                }else{
                    if(commUtils.isDefinedAndNotNull(urlParamMap["bannerID"])){
                        s_campaign  = urlParamMap[paramHelper.URL_MAP_BANNERID];
                    }
                    else{
                        return "";
                    }     
                }
                
                if (browserSessionManager.isCampaignDataToBeSentForThisSession()){
                    return s_campaign;
                }else{
                    return "";
                }
            
}
this.site = function(){ 

                return "fordvehicles.com"
            
}
this.userLanguage = function(){ 

                if ( !commUtils.isDefinedAndNotNull(wtk.widgetInstances["showroom"])){
                return "";
                }
                return wtk.widgetInstances["showroom"].getUserLanguage();
            
}
this.client = function(){ 

                return "ford"
            
}
this.channel = function(){ 

                return "showroom"
            
}
//End of getters
//Start of clear vars
this.clearVars = function(){
//Clearing Omniture Variables
s.eVar13='';
s.prop13='';
s.prop6='';
s.eVar6='';
s.eVar12='';
s.prop12='';
s.campaign='';
s.referrer='';
s.eVar14='';
s.prop14='';
s.eVar16='';
s.prop16='';
s.hier1='';
s.eVar11='';
s.prop11='';
s.eVar15='';
s.prop15='';
s.eVar4='';
s.prop4='';
s.prop5='';
s.channel='';
s.linkTrackVars='';
s.events='';
s.linkTrackEvents='';
s.pageName='';
}
}
var metricsTracker = new com.forddirect.application.bp20.metrics.MetricsTracker();/*************************************************************************
 Copyright (C) Unpublished Versata Software, Inc. All rights reserved.
 Versata Software, Inc., Confidential and Proprietary.
 This software is subject to copyright protection
 under the laws of the United States and other countries.
 Unless otherwise explicitly stated, this software is provided
 by Versata "AS IS".
 *************************************************************************/
/**
 * @author Praveen DS
 * @version 1.0
 */
/**
 * This downloads the spotlight (aka DART ) pixels.  For details please refer
 * http://wiki.ngptools.com/confluence/download/attachments/32831/FD_Floodlight-Spotlight_Tag_Refresh_eng_req_ver2_073107.doc?version=1
 *
 * We support only javascript enabled browsers, so downloading the pixel can be
 * 1) through AJAX
 * 	ruled out as it is cross-domain and is NOT working
 * 2) through document.write as mentioned in the URL above
 * 	but it clears the page of its DOM and no content which was already present was visible, hence ruled out.
 * 3) Have an iframe in the index.jsp and start using the id of the iframe and set the 'src' attribute in there.
 * 	this works and seems to be an apt solution
 */
getPackageForName("com.forddirect.application.bp20.metrics").DART = function(){
    this.trackEvent = function(templateTag, nameplateValue){
        log4javascript.getDefaultLogger().debug("Spotlight !!");
        var axel = Math.random() + "";
        var a = axel * 10000000000000;
        var url = this.getSpotlightURL(templateTag, nameplateValue, a);
        
        log4javascript.getDefaultLogger().debug("Blocked :: Spotlight URL " + url);
        YAHOO.util.Dom.get("doubleclick").setAttribute("src", url);
        document.getElementsByTagName("body").item(0).removeChild(document.getElementById("doubleclick"));
        this.generate_iframe("doubleclick", url, "1px", "1px", "none", "0px");
        log4javascript.getDefaultLogger().debug("Track Event Ends here" + url);
        
    }
    
    this.getSpotlightURL = function(templateTag, nameplateValue, a){
        log4javascript.getDefaultLogger().debug("Inside Spotlight URL ");
        return 'http://fls.doubleclick.net/activityi;src=690327;type=fvflup;cat=' + templateTag + ';u1=' + nameplateValue + ';ord=' + a + '?';
    }
    
    /**
     * Creates a DOM (iframe) with the following details
     * @param {Object} id <String> the id of this DOM
     * @param {Object} url <String> the src url
     * @param {Object} height <String> the height of this iframe
     * @param {Object} width <String> the width of this iframe
     * @param {Object} display <String> block|none
     * @param {Object} border <String> the border
     *
     * Src taken from:
     * http://www.oreillynet.com/pub/a/javascript/2002/02/08/iframe.html?page=last&x-order=date 
     * http://www.mabaloo.com/Web-Development/Creating-controlling-and-manipulating-an-Iframe-through-JavaScript.html
     */
    this.generate_iframe = function(id, url, height, width, display, border){
        var IFrameObj;
        if (!document.createElement) {
            return true
        };
        var IFrameDoc;
        if (!IFrameObj && document.createElement) {
            // create the IFrame and assign a reference to the
            // object to our global variable IFrameObj.
            // this will only happen the first time
            // callToServer() is called
            try {
                var tempIFrame = document.createElement('iframe');
                tempIFrame.setAttribute('id', id);
                tempIFrame.setAttribute('name', id);
                tempIFrame.style.border = border;
                tempIFrame.style.width = width;
                tempIFrame.style.height = height;
                tempIFrame.style.display = display;
                IFrameObj = document.body.appendChild(tempIFrame);
                
                if (document.frames) {
                    // this is for IE5 Mac, because it will only
                    // allow access to the document object
                    // of the IFrame if we access it through
                    // the document.frames array
                    IFrameObj = document.frames[id];
                }
            } catch (exception) {
                // This is for IE5 PC, which does not allow dynamic creation
                // and manipulation of an iframe object. Instead, we'll fake
                // it up by creating our own objects.
                iframeHTML = '\<iframe id="' + id + '" style="';
                iframeHTML += 'border:0px;';
                iframeHTML += 'width:0px;';
                iframeHTML += 'height:0px;';
                iframeHTML += '"><\/iframe>';
                document.body.innerHTML += iframeHTML;
                IFrameObj = new Object();
                IFrameObj.document = new Object();
                IFrameObj.document.location = new Object();
                IFrameObj.document.location.iframe = document.getElementById(id);
                IFrameObj.document.location.replace = function(location){
                    this.iframe.src = location;
                }
            }
        }
        
        
        if (navigator.userAgent.indexOf('Gecko') != -1 && !IFrameObj.contentDocument) {
            // we have to give NS6 a fraction of a second
            // to recognize the new IFrame
            setTimeout('callToServer()', 10);
            return false;
        }
        
        if (IFrameObj.contentDocument) {
            // For NS6
            IFrameDoc = IFrameObj.contentDocument;
        } else if (IFrameObj.contentWindow) {
            // For IE5.5 and IE6
            IFrameDoc = IFrameObj.contentWindow.document;
        } else if (IFrameObj.document) {
            // For IE5
            IFrameDoc = IFrameObj.document;
        } else {
            return true;
        }
        try {
            IFrameDoc.location.replace(url);
        }
        catch(e) {
            log4javascript.getDefaultLogger().debug("***** DESHABILITA ADBLOCKPLUS SI DESEAS HACER DOBLE CLIC P/TRABAJAR *****");
        }
        return false;
    }
}

//The global instance of the ParamHelper
var dartTracker = new com.forddirect.application.bp20.metrics.DART();
/*************************************************************************
 Copyright (C) Unpublished Versata Software, Inc. All rights reserved.
 Versata Software, Inc., Confidential and Proprietary.

 This software is subject to copyright protection
 under the laws of the United States and other countries.

 Unless otherwise explicitly stated, this software is provided
 by Versata "AS IS".

 *************************************************************************/

/**
 * @author Abhishek Joshi
 * @version 1.0
 */

/**
 * This function represents the com.forddirect.presentation.wtk.WidgetToolkit class.
 */
getPackageForName( "com.forddirect.presentation.wtk" ).WidgetToolkit = function()
{

    this.loader = "" ;
    this.initCompleteEvent = new YAHOO.util.CustomEvent( "wtkInitCompleteEvent", null ) ;
    this.widgetInstances = "" ;
    this.widgetTypes = "" ;
    this.path = "" ;
    this.widgetInitCompleteEvents = "";
    this.wtkWidgetEvents = "";

    /**
     * This function initialized the Widget Toolkit. Any function waiting for the successful initialization
     * of the WidgetToolkit should use the event wtk.initCompleteEvent
     */
    this.init = function( )
    {
        log4javascript.getDefaultLogger( ).debug( "WTK::INIT" ) ;
        if( typeof __baseClassPath != "undefined" )
            this.path = __baseClassPath ;
        this.widgetInstances = new Object( ) ;
        this.widgetTypes = new Object( ) ;
        this.widgetInitCompleteEvents = new Object( ) ;
        this.wtkWidgetEvents = new Object( );
        this.loader = new com.forddirect.presentation.wtk.Loader( this ) ;
        //The last parameter is of the classpath for the swf files.
        var loaderInitEvent = this.loader.init( this.path + "static/", this.path + "static/", this.path + "static/",this.path + "flash/" ) ;
        loaderInitEvent.subscribe( this.onLoaderInitComplete ) ;
    }

    //This function is called on successful initialization of the loader instance. Any dependency on the
    //loader instance should execute the logic in this function.
    this.onLoaderInitComplete = function( )
    {
        log4javascript.getDefaultLogger( ).info( "Widget Toolkit class initialized" ) ;
        this.initCompleteEvent.fire( ) ;
    }

    /**
    * This function loads an array of widgets. Each widget should have the following attributes: -
    * 1) widgetClass: The class of the component. for example, com.forddirect.presentation.widgets.config.Config
    * 2) baseDiv: The div in which the component is to be loaded
    * @param {Array} widgets
    * @return an event which signals the successful loading of the group of widgets. The calling function should
    * use the id to index and subscribe to the success event.
    */
    this.loadWidgets = function( widgets, shouldInit, clubWidgets )
    {
        //log4javascript.getDefaultLogger( ).debug( "WTK::loadWidgets" ) ;
        var widgetsDownloadedEvent = this.loader.loadComponents( widgets , shouldInit , clubWidgets) ;

        //If the widgets are to be initialized after downloading, the initWidgets subscribes to
        //the downloadedEvent and the once all the widgets are initialized, the
        //widgetsInitCompletionEvent is fired.
        if( shouldInit )
        {
            var widgetsInitCompleteEvent = new YAHOO.util.CustomEvent("widgetsInitCompleteEvent", this);
            widgetsDownloadedEvent.subscribe( this.initWidgets, {  "widgets" : widgets,"widgetsInitCompleteEvent" : widgetsInitCompleteEvent } ) ;
            return widgetsInitCompleteEvent ;
        }
        return widgetsDownloadedEvent ;
    }

    /**
     * This function listens to the initWidgetsEvent which signals WTK to start the initialization
     * of the widgets. This function initializes the widget by passing the widget initParams along
     * and fires the widgetsInitCompleteEvent on successfully initializing all the widgets.
     *
     * @param {Object} initWidgetsEvent
     * @param {Object} firedData {}
     * @param {Object} subscribedData {'widgetsInitCompleteEvent', 'widgets'}
     */
    this.initWidgets = function( initWidgetsEvent, firedData, subscribedData )
    {
        //log4javascript.getDefaultLogger( ).debug( "WTK::initWidgets" ) ;
        var widgets = subscribedData[ "widgets" ] ;
        var widgetsInitCompleteEvent = subscribedData[ "widgetsInitCompleteEvent" ] ;
        var widget = null ;

        //log4javascript.getDefaultLogger( ).debug( "WTK::" + widgets.length + " widgets to be init" ) ;

        for( var widgetName in widgets ) {
            for (i in widgets[widgetName]) {
                widget = widgets[widgetName][i];
                
                log4javascript.getDefaultLogger().debug("WTK::Initializing " + i + " : " + widget.widgetId);
                
                //widget.constructorParams["widgetLocation"] = widget["widgetLocation"] ;
                if (null == widget.initParams || "undefined" == typeof widget.initParams) {
                    widget.initParams = {};
                }
                widget.isInit = false;
                widget.initParams["widgetId"] = widget["widgetId"];
                widget.initParams["widgetName"] = widget["widgetName"];
                widget.initParams["initCompletionEvent"] = new YAHOO.util.CustomEvent("widgetInitCompleteEvent", this);
                widget.initParams["initCompletionEvent"].subscribe(this.onWidgetInitComplete, {
                    "widgetsInitCompleteEvent": widgetsInitCompleteEvent,
                    "widgets": widgets
                });
                
                if (null == widget["js"] || "undefined" == typeof widget["js"]) {
                    widget.initParams["baseDivId"] = widget["baseDivId"];
                    this.widgetInstances[widget.widgetId] = eval('new ' + widget.widgetClass + '(widget.initParams)');
                }
                else {
                    //log4javascript.getDefaultLogger( ).debug( widget["js"] ) ;
                    eval(widget["js"][widget["widgetName"]]);
                    if ("swf" == widget["type"]) {
                        widget.initParams["src"] = widget["swf"];
                    }
                    else {
                        widget.initParams["baseDivId"] = widget["baseDivId"];
                        widget.initParams["css"] = widget["css"];
                        widget.initParams["html"] = widget["html"];
                        widget.initParams["js"] = widget["js"];
                    }
                    
                    this.widgetInstances[widget.widgetId] = eval('new ' + widget.widgetClass + '(widget.initParams)');
                }
                
                this.widgetTypes[widget.widgetId] = widget["type"];
                log4javascript.getDefaultLogger().debug("WTK::Adding to WidgetInstance " + i + " : " + widget.widgetId);
                this.widgetInstances[widget.widgetId].init(widget.initParams);
                log4javascript.getDefaultLogger().debug("WTK::Added to WidgetInstance " + i + " : " + widget.widgetId);
            }
        }
    }

    /**
     * This event listens to the event initCompletionEvent which the successful initialization
     * of the widget. This function then checks if all the other widgets have been initialized
     * or not. If all widgets are initialized successfully, the widgetsInitCompleteEvent is
     * fired.
     *
     * @param {Object} initCompletionEvent
     * @param {Object} firedData
     * @param {Object} subscribedData
     */
    this.onWidgetInitComplete = function( initCompletionEvent, firedData, subscribedData )
    {
        //log4javascript.getDefaultLogger( ).info( "WTK::" + firedData[0] + " initialized" );
        var widgets = subscribedData['widgets'] ;
        var someWidgetsLeft = false ;

        for(var widgetName in widgets ) {
            for (w in widgets[widgetName]) {
                //Check if the widget that fired the initCompletionEvent is the same as the current
                //widget
                if (firedData[0] == widgets[widgetName][w].widgetId) {
                    widgets[widgetName][w].isInit = true;
                }
                //Check if the current widget is init. If it is NOT init, then set the flag to false
                if (!widgets[widgetName][w].isInit) {
                    someWidgetsLeft = true;
                //log4javascript.getDefaultLogger( ).info( "WTK:: " + widgets[widgetName][w].widgetId + " not initialized" );
                }
            }
        }
        //If at least one widget is left, do NOT fire the widgetsInitCompletionEvent
        if( someWidgetsLeft ){
            return ;
        }
        log4javascript.getDefaultLogger( ).info( "WTK::All widget inits completed" ) ;
        subscribedData['widgetsInitCompleteEvent'].fire( ) ;
    }

    /**
     * This function unloads a widget. It calls loader.unloadWidget to unload it. It then removes the instance of the
     * widget from widgetInstances object.
     * @param widgetId
     * @return success true if succeeds in unloading widget; false otherwise
     */
    this.unload = function(widgetId)
    {
        log4javascript.getDefaultLogger().debug(widgetId + " getting unloaded");
        //find the widget instance in the array
        if(this.widgetInstances[widgetId] != null) {
            var _widget = this.widgetInstances[widgetId];
            //var _baseDivId = (typeof _widget["m_baseDivId"] == "undefined") ? _widget.baseDivId : _widget.m_baseDivId ;
            //this.loader.unload(_baseDivId, widgetId);
            this.widgetInstances[widgetId].unload();
            this.widgetInstances[widgetId] = null;
            this.widgetTypes[widgetId] = null;
            return true;
        } else {
            log4javascript.getDefaultLogger().error(widgetId + " widget not present");
            return false;
        }
    }

}

//The global instance of the Widget Toolkit
var wtk = new com.forddirect.presentation.wtk.WidgetToolkit( ) ;

/**
 * This global function creates and initializes the Widget Toolkit instance
 */
var initWtk = function ( )
{
    wtk.init( ) ;
}

//This statement calls the global function initWtk when the DOM is loaded.
if(typeof wtkLoadOnDomReady == "undefined"){
    YAHOO.util.Event.onDOMReady( initWtk ) ;
}
/**
 * @author Amit Yadav
 * This provides a utility method to load PNG images.
 * The caller code just has to call the fix function with all required paramaters to get this working.
 * Example Usage:
 * PNGLoader.attachPNG(
 *    ponyImg,'/static/com/forddirect/presentation/widgets/ui/internetPrice/images/pony.png', {}
 * );
 * @param {Object} el - dom element to which the image is to be attached.
 * @param {string} url - url path to the image to be loaded
 * @param {Object} params - any additional params that need to be passed. eg {isBackground: true}
 * @constructor
 */
getPackageForName( "com.forddirect.presentation.wtk.util" ).PNGLoader =  {

        /**
         * This checks if the client browser is IE.
         *  if DXImageTransform.Microsoft.AlphaImageLoader
         * method is to be used for loading images in case of IE version less than 6.
         */
        attachPNG : function(el,url,params) {

            if( params.isBackground ) {
                if( commUtils.isIE6 ) {
                    el.style.backgroundImage = "" ;
                    el.style.background = "" ;
                    var sizingMethod = (typeof params.sizingMethod == "undefined") ? "scale" : params.sizingMethod;
                    el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + url + '\', sizingMethod=\'' + sizingMethod + '\')' ;
                } else {
                    el.style.backgroundImage = 'url(' + url + ')' ;
                }
            } else {
                var img = new Image( ) ;
                if( commUtils.isIE6 ) {
                    /* Call the function that will return a reference to the inner function
                       object created in its execution context. Passing the parameters that
                       the inner function will use when it is eventually executed as
                       arguments to the outer function. The returned reference to the inner
                       function object is assigned to a local variable:-
                    */
                    img.onload = this.onLoadRef(this.onLoadAfterTimeOutRef(el, img, url ,params ));

                    img.onerror = this.imgOnError;
                    img.onabort =this.imgOnAbort ;

                } else if( el.tagName == 'IMG' ) {
                    el.src = url ;
                } else {
                    el.appendChild( img ) ;
                }
                img.src = url ;
            }
        },

        imgOnError : function( e ) {
           //ToDo
        },

        imgOnAbort : function( e ) {
            //ToDo
        },

        onLoadAfterTimeOutRef : function(elem, imgObject, src, params){
            /* Return a reference to an anonymous inner function created
               with a function expression:-
            */
            return (function(){
                /* This inner function is to be executed with - setTimeout
                   - and when it is executed it can read, and act upon, the
                   parameters passed to the outer function:-
                */

                //get dimensions of the PNG image
                if( params.overrideDimensions ) {
                    d = {
                        height: params.height,
                        width: params.width
                    }
                } else {
                    d = {
                        height: imgObject.height + 'px',
                        width: imgObject.width + 'px'
                    }
                }
                var sizingMethod = (typeof params.sizingMethod == "undefined") ? "scale" : params.sizingMethod;

                if( elem.tagName == 'IMG' ) {
                    var imgID = ( elem.id ) ? "id='" + elem.id + "' " : "" ;
                    var imgClass = ( elem.className ) ? "class='" + elem.className + "' " : "" ;
                    var imgTitle = ( elem.title ) ? "title='" + elem.title + "' " : "title='" + elem.alt + "' " ;
                    var imgStyle = "display:inline-block;" + elem.style.cssText  ;
                    if ( elem.align == "left" ) imgStyle = "float:left;" + imgStyle ;
                    if ( elem.align == "right" ) imgStyle = "float:right;" + imgStyle ;
                    var strNewHTML = "<span " + imgID + imgClass + imgTitle
                        + " style=\"" + "width:" + d.width + "; height:" + d.height + ";" + imgStyle + ";"
                        + "filter:" + 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + src + '\', sizingMethod=\''+ sizingMethod +'\')' + ";\"></span>" ;
                    elem.outerHTML = strNewHTML ;
                } else {
                    var imgStyle = "display:inline-block;" ;
                    var strNewHTML = "<span id=\"PNGLoadedImg\" style=\"" + "width:" + d.width + "; height:" + d.height + ";"
                        + imgStyle + ";filter:" + 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + src + '\', sizingMethod=\''+ sizingMethod +'\')'  + ";\"></span>" ;
                    var nodeList = elem.getElementsByTagName('span');
                    var toBeRemovedNode = null;
                    for(var i = 0; i < nodeList.length; i++) {
                        var nodeItem = nodeList[i];
                        if(nodeItem.id == "PNGLoadedImg") {
                            toBeRemovedNode = nodeItem;
                        }
                    }
                    if(toBeRemovedNode != null) {
                        elem.removeChild(toBeRemovedNode);
                    }
                    elem.innerHTML += strNewHTML ;
                }
            });
        },
        onLoadRef : function(functRef){
            return (function(){
                 // HACK: Added timeout so that img node is ready before applying filter.
                    setTimeout(functRef, 50);
                    return null;
            });
        }
    }

var PNGLoader = com.forddirect.presentation.wtk.util.PNGLoader;/**
 * This is the LayoutManager.  Its primary job is to create a div at the appropriate position we wanted to
 */
getPackageForName("com.forddirect.presentation.wtk.util").LayoutManager = function()
{
    this.init = function()
    {
		this.m_buffer = (BrowserDetect.getWindowSize().width- g_siteWidth - 15)/2;
	}
	
	/**
	 * The function returns the new value of x, centered on the page. 
	 * @param {Object} x
	 */
	this.getCenteredX = function(x)
	{
           var buffer = this.getOffset();
           if(buffer > 0 )
           {
              x = parseInt(x) + buffer;
           }
           return x;
	}

	this.m_buffer = 0 ;
    /**
     * TODO: This is the final version once the index.jsp is cleaned up
     * @param {Object} divId
     * @param {Object} x
     * @param {Object} y
     * @param {Object} z
     */
    this.getDivNew = function(divId, x, y, z)
    {
        var div = YAHOO.util.Dom.get(divId);
        if (typeof div != "undefined" && null !== div)
        {
			var styleSTR = "position:absolute; left:" + x + "px; top:" + y + "px; z-index:" + z;
            div.setAttribute("style", styleSTR);
            return div;
        }
        else
        {
			x = this.getCenteredX(x);
		    var styleSTR = "position:absolute; left:" + x + "px; top:" + y + "px; z-index:" + z;
            div = document.createElement("div");
            div.setAttribute("id", divId);
            div.setAttribute("style", styleSTR);
            YAHOO.util.Dom.get("display-div").appendChild(div);
            return div;
        }
    }

   /**
    * This is the hack version for transition which will eventually help cleaning up the index.jsp.
    * @param {Object} parentDivId
    * @param {Object} divId
    * @param {Object} x
    * @param {Object} y
    * @param {Object} z
    */
    this.getDiv = function(parentDivId, divId, x, y, z)
    {
        log4javascript.getDefaultLogger( ).debug( "GETDIV:: BaseDiv " + divId ) ;
		if(null == parentDivId || parentDivId == 'display-div' )
		{
			x = this.getCenteredX(x);
		}
        var styleSTR = "position:absolute; left:" + x + "px; top:" + y + "px; z-index:" + z;

        var div = this.findDiv(parentDivId, divId);

        if(div != null)
        {
            // IE doesn't support style to be applied  by setting the attribute. Hence outerHTML approached is to be applied.
            // However, outerHTML doesn't work with Firefox and other browsers, hence 'setAttribute' method is used.
            if(BrowserDetect.browser == 'Explorer')
            {
                // TODO: Need to verify if this works properly for already existing widgets. Currently there is no such senario in the framework. Also this implementation can cause slight jerkyness.
                div.outerHTML = "<div id='" + divId + "' style='" + styleSTR + "'>" + div.innerHTML + "</div>";
            }else
            {
                div.setAttribute("style", styleSTR);
            }
        }
		log4javascript.getDefaultLogger( ).debug( "GETDIV:: BaseDiv " + divId + " Completed") ;
        // When outerHTML is set, you cann't just return the div object created earlier, hence need to return the newly created dom element.
        return this.findDiv(parentDivId, divId);
    }

   /**
    * This method find the divId in the 'display-div'. And if it doesn't find it, then it searches in the given parentDivId.
    * If the div with given divId is not found, but parentDivId is found, it creates a div and returns it.
    * However if the parentDivId or 'display-div' is also not found, then it returns 'null'
    *
    * @param {Object} parentDivId
    * @param {Object} divId
    */
    this.findDiv = function(parentDivId, divId)
    {
        var __displayDiv = document.getElementById("display-div");
        if (null != parentDivId)
        {
            __displayDiv = document.getElementById(parentDivId);
        }
        var div = null;

        if (null != __displayDiv)
        {
            var childDivs = __displayDiv.getElementsByTagName("div");

            var isBaseDivPresent = false;
            //for( var i in childDivs ) {
            for (var i = 0; i < childDivs.length; i++)
            {
                try
                {
                    if (divId == childDivs[i].getAttribute("id"))
                    {
                        div = childDivs[i];
                        break;
                    }
                }
                catch (e)
                {
                }
            }

            if (null == div)
            {
                var div = document.createElement("div");
                div.setAttribute("id", divId);
                __displayDiv.appendChild(div);
            }
        }
        return div;
    }

	this.getOffset = function(){
		return this.m_buffer;
	}	
}

//The global instance of the LayoutManager
var wtkLayoutManager = new com.forddirect.presentation.wtk.util.LayoutManager();

//The global variable for site width
var g_siteWidth = 960;
wtkLayoutManager.init();
/*************************************************************************
 Copyright (C) Unpublished Versata Software, Inc. All rights reserved.
 Versata Software, Inc., Confidential and Proprietary.
 This software is subject to copyright protection
 under the laws of the United States and other countries.
 Unless otherwise explicitly stated, this software is provided
 by Versata "AS IS".
 *************************************************************************/
/**
 * @author Praveen DS
 * @version 1.0
 */
/**
 * This is a very basic session manager to detect 30 minutes of user inactivity.
 */
getPackageForName("com.forddirect.presentation.wtk.util").BrowserSessionManager = function(){
    /*
     * Mentions whether the session is on or NOT (boolean)
     */
    this.inSession = false;
    this.count = 1;
    this.intervalSet = null;
    this.sessionDurationInSec = 60*30; //30min
    this.isBPStarted = false;
	this.campaignInfoSentForSession = false;
    this.start = function(){
        this.reset(null);
		//TODO: Use YUI here
		YAHOO.util.Event.on(document, 'mousemove', this.reset, this, true);
		YAHOO.util.Event.on(document, 'keydown', this.reset, this, true);
		YAHOO.util.Event.on(document, 'mouseup', this.reset, this, true);
    }
    
	/**
	 * Doesn't work with this as is a callback from setInterval
	 */
    this.updateCount = function(){
        if (!browserSessionManager.inSession) {
            return;
        }
        if (browserSessionManager.sessionDurationInSec <= browserSessionManager.count) {
            browserSessionManager.inSession = false;
            //This might expand to a map if more such variables are needed.
            browserSessionManager.isBPStarted = false;
			browserSessionManager.campaignInfoSentForSession = false;
        } else {
            browserSessionManager.count++;
        }
    }
    
    this.reset = function(e){
        this.count = 1;
        this.inSession = true;
        if (null != this.intervalSet) {
            clearInterval(this.intervalSet);
        }
        this.intervalSet = setInterval(this.updateCount, 1000);
    }
    
    /**
     * TODO:  If in the future more such variables are needed, create a map
     * Used by s.event6 in OMTR tracking
     */
	this.isBPForANewSession = function(){
		if(this.isBPStarted){
			return false;
		}else{
			this.isBPStarted = true;
			return true;
		}
	}
	/*
	 * Used by s.campaign in OMTR tracking
	 */
	this.isCampaignDataToBeSentForThisSession = function(){
		if(this.campaignInfoSentForSession){
			return false;
		}else{
			this.campaignInfoSentForSession = true;
			return true;
		}
	}
}

//The global instance of the BrowserSessionManager
var browserSessionManager = new com.forddirect.presentation.wtk.util.BrowserSessionManager();
browserSessionManager.start();
/**
 * This function represents the com.forddirect.presentation.wtk.CommunicationManager class.
 */
getPackageForName( "com.forddirect.presentation.wtk" ).CommunicationManager = function( )
{
    /**
     * 'this.eventList' is a Dictionary and contains an array of handlers as value for corresponding Event Name as keys
     */
    this.recvList=new Object();
    this.subscriberList=new Object();

    /**
     * Adds and entry of Handler for the events in the this.sendList
     * @param {Object} senderWidgetID - widgetID of the widget that is going to handle the event
     * @param {Object} receiverWidgetID - widgetID of the widget that is going to handle the event
     * @param {Object} receiverWidgetType - type of the receiver widget
     * @param {Object} eventName - Name of the event
     * @param {Object} handler - Name of the handler for the event
     */
    this.subscribe = function( params )
    {
        //Creates a new entry of { widgetID , handler } for adding to the array in the 'this.addThrows'
        //Make sure that there is an entry in the throws before the receiver comes
        var senderWidgetID = params[0];
        var receiverWidgetID = params[1];
        var receiverWidgetType = params[2];
        var eventName = params[3];
        var handler = params[4];
        var context = params[5];
        log4javascript.getDefaultLogger().debug("subscribing to event " + eventName + " from " + senderWidgetID + " receiver:" + receiverWidgetID);

        var key = senderWidgetID + ":" + eventName;
        var yuiEvent = this.recvList[key];
        if(yuiEvent == null ) {
            yuiEvent = new YAHOO.util.CustomEvent( key ) ;
            this.recvList[key] = yuiEvent;
            this.subscriberList[key] = new Object();
        }

        //check if subscribe has already subscribed to the event
        if(receiverWidgetID in this.subscriberList[key]) {
            //log4javascript.getDefaultLogger().debug(receiverWidgetID + " already subscribed to " + key);
            //subscriber already subscribed; Do nothing
        } else {
            var subscribers = this.subscriberList[key];
            //add to the subscribe list
            //and populate subscriberList of the key
            if( "js" == receiverWidgetType || "swf" == receiverWidgetType) {
                //log4javascript.getDefaultLogger().debug("js widget receiever");
            	yuiEvent.subscribe(handler, context);
                subscribers[receiverWidgetID] = receiverWidgetID;
            } else {
                log4javascript.getDefaultLogger().debug("anonymous widget receiever");
            }
        }
        log4javascript.getDefaultLogger().debug("WTCOMM::subscribED to event " + eventName + " from " + senderWidgetID + " receiver:" + receiverWidgetID);
    }

    /**
     * Event to fire an event in a JS or FLASH widget
     * @param {Object} widgetID - ID of the widget that fires the event
     * @param {Object} widgetType - Type of the widget
     * @param {Object} eventName - Name of the event that is to be fired
     */
    this.fire = function(params)
    {
        var senderWidgetID = params[0];
        var senderWidgetType = params[1];
        var eventName = params[2];
        var eventObj = params[3];

        var key = senderWidgetID  + ":" + eventName;
        var yuiEvent = this.recvList[key];
        if(yuiEvent == null)
        {
            log4javascript.getDefaultLogger().debug("No listener configured for the event " + eventName + " from " + senderWidgetID);
            return;
        }

        log4javascript.getDefaultLogger().debug("firing the event " + eventName);
        
        //fire yui event
        yuiEvent.fire(eventObj);
    }

    /**
     * This function gets the name of the movie which is a flash movie.
     *
     * This just makes it browser independent. The way to get the movie by name is different in
     * firefox and microsoft. This handles the same.
     * @param The name of the movie which has been placed on the page. This should correspond to
     * the id being used for the object tag for the flash movie.
     * @return The instance of the movie on the page.
     */
    this.thisMovie = function(movieName)
    {
        if (navigator.appName.indexOf("Microsoft") != -1)
        {
           return window[movieName];
        } else {
            return document[movieName];
        }
    }

    /**
     * This basically initiates the flash instance of the widget on the flash controller.
     * Note: At this point the "init()" method in the equivalent JS file, would have been executing.
     *       This call should be perhaps the last statement in the JS 'init' method.
     *
     * @param widgetId   :String : The ID of the flash widget.
     * @param xVal       :String : X position.
     * @param yVal       :String : Y position.
     * @param depthVal   :String : The depth (z-index) of the flash movie among the other Flash widgets.
     * @param srcVal     :String : The SWF url for the flash movie.
     * @param initParams :String : Initial parameters
     */
    this.addFlashWidget = function(widgetId,xVal,yVal,depthVal,srcVal)
    {
        var subscribedData = new Object();
        subscribedData.widgetId = widgetId;
        subscribedData.xVal = xVal;
        subscribedData.yVal = yVal;
        subscribedData.depthVal = depthVal;
        subscribedData.srcVal = __appBasePath + srcVal;

        /**
         * Variable '__isFlashControllerLoaded' is to be defined at the main page, example: index.jsp , where FlashController is embedded.
         * This is to ensure that variable '__isFlashControllerLoaded' and method '__flashAppInitialized' are available
         * whenever FlashController is initialized. If we place these functions anywhere else in a separate JS file, we run
         * into the condition where those functions are not available, when Flash Controller is initialized.
         */
        if(__isFlashControllerLoaded == false)
        {
            /*
             * If the Flash Controller is not initiated, all the flash widgets to be initialized are subscribed here.
             * Once controller is loaded then, then the subscribed flash widgets are initiated in Flash.
             */
            //log4javascript.getDefaultLogger().debug("Add Flash getting subscribed for widget :" + widgetId);
            wtk.loader.flashControllerLoadEvent.subscribe(this.callAddFlashWidget, subscribedData);
        }else
        {
            // If flash controller is already loaded, then it call the method to load flash object
            this.callAddFlashWidget(null, null, subscribedData);
        }
    }

    /**
     * This method initiates the flash widget loading on the flash controller.
     * This is called when flash controller has been initialized successfully. It assumes that flash controller has
     * been loaded successfully and JS-AS communication is established.
     *
     * It is either passed as event handler method when flash widgets subscribe themselves to the flash controller
     * initiation event, or called directly if controller is already initiated.
     *
     * @param event          String : event that is fired. Not used in the method.
     * @param firedData      String : fired data. This is also ignored in the method.
     * @param subscribedData String : subscribed data. It contains for the relevant information for initiating flash widget.
     */
    this.callAddFlashWidget = function(event, firedData, subscribedData)
    {
        wtkComm.thisMovie('FlashController').addWidget(subscribedData.widgetId, subscribedData.xVal, subscribedData.yVal,
                                            subscribedData.depthVal,subscribedData.srcVal);
    }

    /**
     * This function clears the communication table
     */
    this.unmash = function ()
    {
        for(var key in this.recvList)
        {
            var yuiEvent = this.recvList[key];
            yuiEvent.unsubscribeAll();
            this.recvList[key] = null;
            this.subscriberList[key] = null;
        }

        this.recvList = null;
        this.recvList=new Object();
        this.subscriberList = null;
        this.subscriberList = new Object();
    }
}

//The global instance of the Widget Toolkit
var wtkComm = new com.forddirect.presentation.wtk.CommunicationManager( );
/*************************************************************************
 Copyright (C) Unpublished Versata Software, Inc. All rights reserved.
 Versata Software, Inc., Confidential and Proprietary.
 This software is subject to copyright protection
 under the laws of the United States and other countries.
 Unless otherwise explicitly stated, this software is provided
 by Versata "AS IS".
 *************************************************************************/
/**
 * @author Praveen DS
 * @version 1.0
 */
/**
 * This is a collection of utilities for manipulating the anchor element of BP application
 * in the case of cookies being disabled.
 */
getPackageForName("com.forddirect.presentation.wtk.util").URLUtils = function(){
    this.PAGE_POS = 1;
    this.YEAR_POS = 2;
    this.MAKE_POS = 3;
    this.MODEL_POS = 4;
    this.DEFINERS_POS = 5;
    this.ZIPCODE_POS = 6;
    this.CONF_TOK_POS = 7;
    this.TRANS_POS = 8;
    this.BANNERID_POS = 9;
    
    /**
     * Any URL which has a '#' in it is a bookmarked URL.
     * Returns true if URL has a '#', false otherwise.
     * @param {Object} url
     */
    this.isBookmarkedURL = function(url){
        return (-1 != url.indexOf("#"));
    }
    
    /**
     * Pathname passed should not have any '/'.  Example
     * http://fordirect.fordvehicles.com/Mustang?param1=value1
     * @param {Object} pathname
     */
    this.isShortURL = function(pathname){
        //remove the first / in the pathname ie.  /Mustang/whatever will become Mustang/whatever
        return (-1 == pathname.replace(/\//, "").indexOf("/"));
    }
    
    /**
     * Constructs the URL anchor element without the page info
     * Ex: 2008/Ford/Mustang/Coupe/10001/Config%5B%7CFord%7CMustang%7C2008%7C1%7C1.%7C180A......%7EYZKAA.%5D/Manual
     */
    this.getParametersPathWithoutPageInfo = function(){
        var anchorElement = "";
        if (typeof urlParamMap[paramHelper.URL_MAP_YEAR] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_YEAR]) {
            anchorElement = anchorElement.concat("_");
        } else {
            anchorElement = anchorElement.concat(urlParamMap[paramHelper.URL_MAP_YEAR]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_MAKE] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_MAKE]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_MAKE]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_MODEL] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_MODEL]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_MODEL]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_DEFINERS] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_DEFINERS]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_DEFINERS]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_ZIPCODE] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_ZIPCODE]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_ZIPCODE]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_CONFIGID] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_CONFIGID]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_CONFIGID]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_TRANS] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_TRANS]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_TRANS]);
        }
        
        if (typeof urlParamMap[paramHelper.URL_MAP_BANNERID] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_BANNERID]) {
            anchorElement = anchorElement.concat("/_");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_BANNERID]);
        }
        return anchorElement;
    }
    
    /**
     * Construct the URL anchor element with page info
     * Ex: /Style/2008/Ford/Mustang/Coupe/10001/Config%5B%7CFord%7CMustang%7C2008%7C1%7C1.%7C180A......%7EYZKAA.%5D/Manual
     */
    this.getParametersPathWithPageInfo = function(){
        var anchorElement = "";
        if (typeof urlParamMap == "undefined" || urlParamMap == null) {
            return "";
        }
        if (typeof urlParamMap[paramHelper.URL_MAP_PAGE] == "undefined" || null == urlParamMap[paramHelper.URL_MAP_PAGE]) {
            anchorElement = anchorElement.concat("/_/");
        } else {
            anchorElement = anchorElement.concat("/", urlParamMap[paramHelper.URL_MAP_PAGE], "/");
        }
        return anchorElement + this.getParametersPathWithoutPageInfo();
    }
    
    /**
     * Updates the urlParamMap object with the values taken from the anchorElement value that is passed.
     * Note:  the passed string should be of the form 
     * /Style/2008/Ford/Mustang/Coupe/10001/Config%5B%7CFord%7CMustang%7C2008%7C1%7C1.%7C180A......%7EYZKAA.%5D/Manual
     * @param {Object} anchorElement
     */
    this.updateURLParamMapWithAnchorElementValue = function(anchorElement){
        var compArray = anchorElement.split("/");
        if (10 != compArray.length) {
            return false;
        }
        
        if (compArray[this.MAKE_POS] == "_" || compArray[this.MODEL_POS] == "_" || compArray[this.YEAR_POS] == "_" || compArray[this.PAGE_POS] == "_") {
            return false;
        }
        
        urlParamMap[paramHelper.URL_MAP_PAGE] = compArray[this.PAGE_POS];
        urlParamMap[paramHelper.URL_MAP_YEAR] = compArray[this.YEAR_POS];
        urlParamMap[paramHelper.URL_MAP_MAKE] = compArray[this.MAKE_POS];
        urlParamMap[paramHelper.URL_MAP_MODEL] = compArray[this.MODEL_POS];
        
        if (compArray[this.DEFINERS_POS] != "_") {
            urlParamMap[paramHelper.URL_MAP_DEFINERS] = compArray[this.DEFINERS_POS];
        } else {
            urlParamMap[paramHelper.URL_MAP_DEFINERS] = null;
        }
        
        if (compArray[this.TRANS_POS] != "_") {
            urlParamMap[paramHelper.URL_MAP_TRANS] = compArray[this.TRANS_POS];
        } else {
            urlParamMap[paramHelper.URL_MAP_TRANS] = null;
        }
        
        if (compArray[this.CONF_TOK_POS] != "_") {
            urlParamMap[paramHelper.URL_MAP_CONFIGID] = compArray[this.CONF_TOK_POS];
        } else {
            urlParamMap[paramHelper.URL_MAP_CONFIGID] = null;
        }
        
        if (compArray[this.ZIPCODE_POS] != "_") {
            urlParamMap[paramHelper.URL_MAP_ZIPCODE] = compArray[this.ZIPCODE_POS];
        } else {
            urlParamMap[paramHelper.URL_MAP_ZIPCODE] = null;
        }
        
        if (compArray[this.BANNERID_POS] != "_") {
            urlParamMap[paramHelper.URL_MAP_BANNERID] = compArray[this.BANNERID_POS];
        } else {
            urlParamMap[paramHelper.URL_MAP_BANNERID] = null;
        }
        
    }
    
    /**
     * 
     */
    this.createPathNameFromURLParamMap = function(){
        var pathName = urlParamMap[paramHelper.URL_MAP_YEAR]+'-'+urlParamMap[paramHelper.URL_MAP_MAKE]+'-'+urlParamMap[paramHelper.URL_MAP_MODEL]+'?';
        for(x in urlParamMap){
            if( x === paramHelper.URL_MAP_YEAR  || x === paramHelper.URL_MAP_MAKE  || x === paramHelper.URL_MAP_MODEL  || x === paramHelper.URL_MAP_DEFINERS  || x === paramHelper.URL_MAP_TRANS  || x === paramHelper.URL_MAP_PAGE  || x === paramHelper.URL_MAP_CONFIGID  || x === paramHelper.URL_MAP_ZIPCODE  || x === paramHelper.URL_MAP_SELECT  || x === paramHelper.URL_MAP_UNSELECT ){
                continue;
            }else if( x === 'document_referrer'){
                pathName += x+'='+escape(urlParamMap[x])+'&'
            }
            else{
                pathName += x+'='+urlParamMap[x]+'&'
            }
        }
        return pathName;
    }
}

var urlUtils = new com.forddirect.presentation.wtk.util.URLUtils();
/*
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.5.0
*/
/**
 * Utilities for cookie management
 * @namespace YAHOO.util
 * @module cookie
 * @beta
 */
YAHOO.namespace("util");

/**
 * Cookie utility.
 * @class Cookie
 * @static
 */
YAHOO.util.Cookie = {
    
    //-------------------------------------------------------------------------
    // Private Methods
    //-------------------------------------------------------------------------
    
    /**
     * Creates a cookie string that can be assigned into document.cookie.
     * @param {String} name The name of the cookie.
     * @param {String} value The value of the cookie.
     * @param {encodeValue} encodeValue True to encode the value, false to leave as-is.
     * @param {Object} options (Optional) Options for the cookie.
     * @return {String} The formatted cookie string.
     * @method _createCookieString
     * @private
     * @static
     */
    _createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ {
    
        //shortcut
        var lang = YAHOO.lang;
    
        var text /*:String*/ = encodeURIComponent(name) + "=" + (encodeValue ? encodeURIComponent(value) : value);
        
    
        if (lang.isObject(options)){
            //expiration date
            if (options.expires instanceof Date){
                text += "; expires=" + options.expires.toGMTString();
            }
        
            //path
            if (lang.isString(options.path) && options.path != ""){
                text += "; path=" + options.path;
            }
    
            //domain
            if (lang.isString(options.domain) && options.domain != ""){
                text += "; domain=" + options.domain;
            }
            
            //secure
            if (options.secure === true){
                text += "; secure";
            }
        }
        
        return text;
    },
    
    /**
     * Formats a cookie value for an object containing multiple values.
     * @param {Object} hash An object of key-value pairs to create a string for.
     * @return {String} A string suitable for use as a cookie value.
     * @method _createCookieHash
     * @private
     * @static
     */
    _createCookieHashString : function (hash /*:Object*/) /*:String*/ {
        
        //shortcuts
        var lang = YAHOO.lang;
        
        if (!lang.isObject(hash)){
            throw new TypeError("Cookie._createCookieHashString(): Argument must be an object.");
        }
        
        var text /*:Array*/ = new Array();
        
        for (var key in hash){
            if (lang.hasOwnProperty(hash, key) && !lang.isFunction(hash[key]) && !lang.isUndefined(hash[key])){
                text.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(hash[key])));
            }
        }
        
        return text.join("&");
    },
    
    /**
     * Parses a cookie hash string into an object.
     * @param {String} text The cookie hash string to parse. The string should already be URL-decoded.
     * @return {Object} An object containing entries for each cookie value.
     * @method _parseCookieHash
     * @private
     * @static
     */
    _parseCookieHash : function (text /*:String*/) /*:Object*/ {
    
        var hashParts /*:Array*/ = text.split("&");
        var hashPart /*:Array*/ = null;
        var hash /*:Object*/ = new Object();
        
        for (var i=0, len=hashParts.length; i < len; i++){
            hashPart = hashParts[i].split("=");
            hash[hashPart[0]] = hashPart[1];
        }
        
        return hash;
    },    
    
    /**
     * Parses a cookie string into an object representing all accessible cookies.
     * @param {String} text The cookie string to parse.
     * @return {Object} An object containing entries for each accessible cookie.
     * @method _parseCookieString
     * @private
     * @static
     */
    _parseCookieString : function (text /*:String*/) /*:Object*/ {
    
        var cookies /*:Object*/ = new Object();
        
        if (YAHOO.lang.isString(text) && text.length > 0) {
        
            if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)){            
                var cookieParts /*:Array*/ = text.split(/;\s/g);
                var cookieName /*:String*/ = null;
                var cookieValue /*:String*/ = null;
                
                for (var i=0, len=cookieParts.length; i < len; i++){
                    cookieName = decodeURIComponent(cookieParts[i].match(/([a-z\d]+)=/i)[1]);
                    cookieValue = decodeURIComponent(cookieParts[i].substring(cookieName.length+1));
                    cookies[cookieName] = cookieValue;
                }
            }
        }
        
        return cookies;
    },    
    
    //-------------------------------------------------------------------------
    // Public Methods
    //-------------------------------------------------------------------------

    /**
     * Returns the cookie value for the given name.
     * @param {String} name The name of the cookie to retrieve.
     * @param {Function} converter (Optional) A function to run on the value before returning
     *      it. The function is not used if the cookie doesn't exist.
     * @return {Variant} If no converter is specified, returns a string or null if
     *      the cookie doesn't exist. If the converter is specified, returns the value
     *      returned from the converter or null if the cookie doesn't exist.
     * @method get
     * @static
     */
    get : function (name /*:String*/, converter /*:Function*/) /*:Variant*/{
        
        var lang = YAHOO.lang;
        var cookies /*:Object*/ = this._parseCookieString(document.cookie);        
        
        if (!lang.isString(name) || name === ""){
            throw new TypeError("Cookie.get(): Cookie name must be a non-empty string.");
        }
        
        if (lang.isUndefined(cookies[name])) {
            return null;
        }
        
        if (!lang.isFunction(converter)){
            return cookies[name];
        } else {
            return converter(cookies[name]);
        }
    },
    
    /**
     * Returns the value of a subcookie.
     * @param {String} name The name of the cookie to retrieve.
     * @param {String} subName The name of the subcookie to retrieve.
     * @param {Function} converter (Optional) A function to run on the value before returning
     *      it. The function is not used if the cookie doesn't exist.
     * @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie
     *      doesn't exist, null if also returned. If no converter is specified and the
     *      subcookie exists, a string is returned. If a converter is specified and the
     *      subcookie exists, the value returned from the converter is returned.
     * @method getSub
     * @static
     */
    getSub : function (name /*:String*/, subName /*:String*/, converter /*:Function*/) /*:Variant*/ {
    
        var lang = YAHOO.lang;    
        var hash /*:Variant*/ = this.getSubs(name);  

        if (hash !== null) {
            
            if (!lang.isString(subName) || subName === ""){
                throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string.");
            }
            
            if (lang.isUndefined(hash[subName])){
                return null;
            }            
        
            if (!lang.isFunction(converter)){
                return hash[subName];
            } else {
                return converter(hash[subName]);
            }
        } else {
            return null;
        }
    
    },
    
    /**
     * Returns an object containing name-value pairs stored in the cookie with the given name.
     * @param {String} name The name of the cookie to retrieve.
     * @return {Object} An object of name-value pairs if the cookie with the given name
     *      exists, null if it does not.
     * @method getHash
     * @static
     */
    getSubs : function (name /*:String*/) /*:Object*/ {
        return this.get(name, this._parseCookieHash);    
    },
    
    /**
     * Removes a cookie from the machine by setting its expiration date to
     * sometime in the past.
     * @param {String} name The name of the cookie to remove.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), 
     *      and secure (true/false). The expires option will be overwritten
     *      by the method.
     * @return {String} The created cookie string.
     * @method remove
     * @static
     */
    remove : function (name /*:String*/, options /*:Object*/) /*:String*/ {
        
        //check cookie name
        if (!YAHOO.lang.isString(name) || name === ""){
            throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string.");
        }
        
        //set options
        options = options || {};
        options.expires = new Date(0);
        
        //set cookie
        return this.set(name, "", options);
    },

    /**
     * Sets a cookie with a given name and value.
     * @param {String} name The name of the cookie to set.
     * @param {Variant} value The value to set for the cookie.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      and secure (true/false).
     * @return {String} The created cookie string.
     * @method set
     * @static
     */
    set : function (name /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
    
        var lang = YAHOO.lang;
    
        if (!lang.isString(name)){
            throw new TypeError("Cookie.set(): Cookie name must be a string.");
        }
        
        if (lang.isUndefined(value)){
            throw new TypeError("Cookie.set(): Value cannot be undefined.");
        }
        
    
        var text /*:String*/ = this._createCookieString(name, value, true, options);
        document.cookie = text;
        return text;
    },
        
    /**
     * Sets a sub cookie with a given name to a particular value.
     * @param {String} name The name of the cookie to set.
     * @param {String} subName The name of the subcookie to set.
     * @param {Variant} value The value to set.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      and secure (true/false).
     * @return {String} The created cookie string.
     * @method setSub
     * @static
     */
    setSub : function (name /*:String*/, subName /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;

        if (!lang.isString(name) || name === ""){
            throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string.");
        }

        if (!lang.isString(subName) || subName === ""){
            throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string.");
        }
        
        if (lang.isUndefined(value)){
            throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined.");
        }

        var hash /*:Object*/ = this.getSubs(name);
        
        if (!lang.isObject(hash)){
            hash = new Object();
        }
        
        hash[subName] = value;        
        
        return this.setSubs(name, hash, options);
        
    },
    
    /**
     * Sets a cookie with a given name to contain a hash of name-value pairs.
     * @param {String} name The name of the cookie to set.
     * @param {Object} value An object containing name-value pairs.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      and secure (true/false).
     * @return {String} The created cookie string.
     * @method setSubs
     * @static
     */
    setSubs : function (name /*:String*/, value /*:Object*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;
        
        if (!lang.isString(name)){
            throw new TypeError("Cookie.setSubs(): Cookie name must be a string.");
        }
        
        if (!lang.isObject(value)){
            throw new TypeError("Cookie.setSubs(): Cookie value must be an object.");
        }
    
        var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options);
        document.cookie = text;
        return text;        
    }    

};
YAHOO.register("cookie", YAHOO.util.Cookie, {version: "2.5.0", build: "895"});
/**
 * This is the CookieManager.  Its primary job is to create a cookie by given name n details
 * @author sdua
 * @author Praveen DS
 */
getPackageForName("com.forddirect.presentation.wtk.util").CookieManager = function(){
    this.isCookieEnabled = false;
    this.init = function(){
        var testCookieValue = 'fdtestCookie' + Math.round(Math.random() * Math.PI * 1000);
        YAHOO.util.Cookie.set("example", testCookieValue);
        var value = YAHOO.util.Cookie.get("example");
        if (null == value) {
            this.isCookieEnabled = false;
        } else if (value === testCookieValue) {
            this.isCookieEnabled = true;
        } else {
            this.isCookieEnabled = false;
        }
        YAHOO.util.Cookie.remove("example");
    }

    /**
     * this method creates a cookie given the cookie name, value and extra optional parameters.
     * @param {Object} name - the name of the cooke.
     * @param {Object} value - value for the cookie to be set.
     * @param {Object} params - optional cookie parameters such as path (a string), domain (a string),
     * expires (a Date object), and secure (true/false).
     */
    this.createCookie = function(name, value, params){
        log4javascript.getDefaultLogger().debug("CookieManager:: Cookie( " + name + " ) = " + value);
        if (null != params && typeof params != 'undefined') {
            YAHOO.util.Cookie.set(unescape(name), unescape(value), params);
        } else {
            YAHOO.util.Cookie.set(unescape(name), unescape(value));
        }
    }

    this.setSubs = function(name, value, params){
        log4javascript.getDefaultLogger().debug("CookieManager:: Sub - Cookie( " + name + " ) = " + value);
        if (null != params && typeof params != 'undefined') {
            YAHOO.util.Cookie.setSubs(name, value, params);
        } else {
            YAHOO.util.Cookie.setSubs(name, value);
        }
    }

    this.setSub = function(name, subname, value, params){
        log4javascript.getDefaultLogger().debug("CookieManager:: Sub - Cookie( " + name + " ) = " + value);
        if (null != params && typeof params != 'undefined') {
            YAHOO.util.Cookie.setSub(name, subname, value, params);
        } else {
            YAHOO.util.Cookie.setSub(name, subname, value);
        }
    }

    /**
     * this method reads a cookie given its name
     * @param {Object} name - name of the cookie to be read
     * @param {Object} conversion - Optional param. A function to run on the value before returning it.
     * The function is not used if the cookie doesn't exist.
     */
    this.readCookie = function(name, conversion){
        log4javascript.getDefaultLogger().debug("CookieManager:: Entering readCookie");
        if (null != conversion && typeof conversion != 'undefined') {
            return YAHOO.util.Cookie.get(name, conversion);
        } else {
            return YAHOO.util.Cookie.get(name);
        }
    }

    this.getSubs = function(name){
        log4javascript.getDefaultLogger().debug("CookieManager:: Entering readSubs");
        return YAHOO.util.Cookie.getSubs(name);
    }

    this.getSub = function(name, subname){
        log4javascript.getDefaultLogger().debug("CookieManager:: Entering readSubs");
        return YAHOO.util.Cookie.getSub(name, subname);
    }

    /**
     * Removes a cookie from the machine by setting its expiration date to sometime in the past.
     * @param {Object} name <String>  The name of the cookie to remove.
     * @param {Object} options <Object>  (Optional) An object containing one or more cookie options: path (a string), domain (a string),
     * and secure (true/false). The expires option will be overwritten by the method.
     */
    this.removeCookie = function(name, options){
        if(null != options){
            return YAHOO.util.Cookie.remove(name, options);
        }else{
            return YAHOO.util.Cookie.remove(name);
        }
    }
}

//The global instance of the CookieManager
var cookieManager = new com.forddirect.presentation.wtk.util.CookieManager();
/**
 * This function represents the com.forddirect.presentation.wtk.CommunicationManager class.
 */
getPackageForName( "com.forddirect.presentation.wtk" ).CommunicationManager = function( )
{
    /**
     * 'this.eventList' is a Dictionary and contains an array of handlers as value for corresponding Event Name as keys
     */
    this.recvList=new Object();
    this.subscriberList=new Object();

    /**
     * Adds and entry of Handler for the events in the this.sendList
     * @param {Object} senderWidgetID - widgetID of the widget that is going to handle the event
     * @param {Object} receiverWidgetID - widgetID of the widget that is going to handle the event
     * @param {Object} receiverWidgetType - type of the receiver widget
     * @param {Object} eventName - Name of the event
     * @param {Object} handler - Name of the handler for the event
     */
    this.subscribe = function( params )
    {
        //Creates a new entry of { widgetID , handler } for adding to the array in the 'this.addThrows'
        //Make sure that there is an entry in the throws before the receiver comes
        var senderWidgetID = params[0];
        var receiverWidgetID = params[1];
        var receiverWidgetType = params[2];
        var eventName = params[3];
        var handler = params[4];
        var context = params[5];
        log4javascript.getDefaultLogger().debug("subscribing to event " + eventName + " from " + senderWidgetID + " receiver:" + receiverWidgetID);

        var key = senderWidgetID + ":" + eventName;
        var yuiEvent = this.recvList[key];
        if(yuiEvent == null ) {
            yuiEvent = new YAHOO.util.CustomEvent( key ) ;
            this.recvList[key] = yuiEvent;
            this.subscriberList[key] = new Object();
        }

        //check if subscribe has already subscribed to the event
        if(receiverWidgetID in this.subscriberList[key]) {
            //log4javascript.getDefaultLogger().debug(receiverWidgetID + " already subscribed to " + key);
            //subscriber already subscribed; Do nothing
        } else {
            var subscribers = this.subscriberList[key];
            //add to the subscribe list
            //and populate subscriberList of the key
            if( "js" == receiverWidgetType || "swf" == receiverWidgetType) {
                //log4javascript.getDefaultLogger().debug("js widget receiever");
            	yuiEvent.subscribe(handler, context);
                subscribers[receiverWidgetID] = receiverWidgetID;
            } else {
                log4javascript.getDefaultLogger().debug("anonymous widget receiever");
            }
        }
        log4javascript.getDefaultLogger().debug("WTCOMM::subscribED to event " + eventName + " from " + senderWidgetID + " receiver:" + receiverWidgetID);
    }

    /**
     * Event to fire an event in a JS or FLASH widget
     * @param {Object} widgetID - ID of the widget that fires the event
     * @param {Object} widgetType - Type of the widget
     * @param {Object} eventName - Name of the event that is to be fired
     */
    this.fire = function(params)
    {
        var senderWidgetID = params[0];
        var senderWidgetType = params[1];
        var eventName = params[2];
        var eventObj = params[3];

        var key = senderWidgetID  + ":" + eventName;
        var yuiEvent = this.recvList[key];
        if(yuiEvent == null)
        {
            log4javascript.getDefaultLogger().debug("No listener configured for the event " + eventName + " from " + senderWidgetID);
            return;
        }

        log4javascript.getDefaultLogger().debug("firing the event " + eventName);
        
        //fire yui event
        yuiEvent.fire(eventObj);
    }

    /**
     * This function gets the name of the movie which is a flash movie.
     *
     * This just makes it browser independent. The way to get the movie by name is different in
     * firefox and microsoft. This handles the same.
     * @param The name of the movie which has been placed on the page. This should correspond to
     * the id being used for the object tag for the flash movie.
     * @return The instance of the movie on the page.
     */
    this.thisMovie = function(movieName)
    {
        if (navigator.appName.indexOf("Microsoft") != -1)
        {
           return window[movieName];
        } else {
            return document[movieName];
        }
    }

    /**
     * This basically initiates the flash instance of the widget on the flash controller.
     * Note: At this point the "init()" method in the equivalent JS file, would have been executing.
     *       This call should be perhaps the last statement in the JS 'init' method.
     *
     * @param widgetId   :String : The ID of the flash widget.
     * @param xVal       :String : X position.
     * @param yVal       :String : Y position.
     * @param depthVal   :String : The depth (z-index) of the flash movie among the other Flash widgets.
     * @param srcVal     :String : The SWF url for the flash movie.
     * @param initParams :String : Initial parameters
     */
    this.addFlashWidget = function(widgetId,xVal,yVal,depthVal,srcVal)
    {
        var subscribedData = new Object();
        subscribedData.widgetId = widgetId;
        subscribedData.xVal = xVal;
        subscribedData.yVal = yVal;
        subscribedData.depthVal = depthVal;
        subscribedData.srcVal = __appBasePath + srcVal;

        /**
         * Variable '__isFlashControllerLoaded' is to be defined at the main page, example: index.jsp , where FlashController is embedded.
         * This is to ensure that variable '__isFlashControllerLoaded' and method '__flashAppInitialized' are available
         * whenever FlashController is initialized. If we place these functions anywhere else in a separate JS file, we run
         * into the condition where those functions are not available, when Flash Controller is initialized.
         */
        if(__isFlashControllerLoaded == false)
        {
            /*
             * If the Flash Controller is not initiated, all the flash widgets to be initialized are subscribed here.
             * Once controller is loaded then, then the subscribed flash widgets are initiated in Flash.
             */
            //log4javascript.getDefaultLogger().debug("Add Flash getting subscribed for widget :" + widgetId);
            wtk.loader.flashControllerLoadEvent.subscribe(this.callAddFlashWidget, subscribedData);
        }else
        {
            // If flash controller is already loaded, then it call the method to load flash object
            this.callAddFlashWidget(null, null, subscribedData);
        }
    }

    /**
     * This method initiates the flash widget loading on the flash controller.
     * This is called when flash controller has been initialized successfully. It assumes that flash controller has
     * been loaded successfully and JS-AS communication is established.
     *
     * It is either passed as event handler method when flash widgets subscribe themselves to the flash controller
     * initiation event, or called directly if controller is already initiated.
     *
     * @param event          String : event that is fired. Not used in the method.
     * @param firedData      String : fired data. This is also ignored in the method.
     * @param subscribedData String : subscribed data. It contains for the relevant information for initiating flash widget.
     */
    this.callAddFlashWidget = function(event, firedData, subscribedData)
    {
        wtkComm.thisMovie('FlashController').addWidget(subscribedData.widgetId, subscribedData.xVal, subscribedData.yVal,
                                            subscribedData.depthVal,subscribedData.srcVal);
    }

    /**
     * This function clears the communication table
     */
    this.unmash = function ()
    {
        for(var key in this.recvList)
        {
            var yuiEvent = this.recvList[key];
            yuiEvent.unsubscribeAll();
            this.recvList[key] = null;
            this.subscriberList[key] = null;
        }

        this.recvList = null;
        this.recvList=new Object();
        this.subscriberList = null;
        this.subscriberList = new Object();
    }
}

//The global instance of the Widget Toolkit
var wtkComm = new com.forddirect.presentation.wtk.CommunicationManager( );
/**
 * This class parses the input url parameters stored in URLParamMap.
 * This also creates a legacyMap which is used for storing the parameters
 * passed to BP from FD. These parameters are to be used for any linkouts from bp.
 * @author sdua
 */
getPackageForName("com.forddirect.application.bp20.flow").LegacyParamsParser = function(){

    this.m_runInDebugMode = true;
    
    /**
     * this map provides a uniform naming for all FD linkin parameters
     */
    this.m_linkinParamsMapping = {
        "BRANDING": "branding",
        "REFERRING_SITE": "referringSite",
        "REFERRER": "referrer",
        "REFERRER_URL": "referrerURL",
        "PARTNER": "partner",
        "REFERRED_ON_DATE": "referredOnDate",
        "SITE_ID": "siteID",
        "OVMTC": "OVMTC",
        "CAMPAIGN_ID": "campaignID",
        "CONFIG_ID": "configID",
        "ZIPCODE": "zipCode",
        "BANNERID": "bannerID",
        "PLAN_TYPE": "planType",
        "LANG": "lang",
        "MAKETRANSITION" : "makeTransition",
        "BRAND" : "brand",
        "HTTPREFERER" : "httpReferer",
        "MODEL" : "model",
        "BP_AFFILIATE_EXTERNAL_SOURCE": "BP_AFFILIATE_EXTERNAL_SOURCE", // this is not a linkin param but is populated by the backend if the referrer is in the affiliates.properties
        "AFFILIATE_TRANSACTION_SUB_ID": "AFFILIATE_TRANSACTION_SUB_ID" // not a linkin param, but populated from linkin param and used for leads
    }
    
    /*
     * This provides the map for the bp2 variable and the legacy variable names.
     */
    this.m_bp2LegacyMapping = {
        "branding": "sUserInterfaceBranding",
        "referringSite": "sReferringSite",
        "referrer": "referrer",
        "referrerURL": "ReferrerURL",
        "partner": "partner",
        "referredOnDate": "referredOnDate",
        "siteID": "cookieValue",
        "OVMTC": "OVMTC",
        "campaignID": "sCampaignID",
        "configID": "configToken",
        "zipCode": "sZip",
        "bannerID": "bannerID",
        "planType": "PlanType",
        "lang": "lang",
        "makeTransition" : "makeTransition",
        "brand" : "sBrand",
        "httpReferer" : "httpReferer",
        "model" : "sModel"
    }
    
    /*
     * this provides a uniform naming for keying into the url map for BP2 params
     */
    this.m_bp2ParamKeys = {
        "ZIPCODE": "zipCode"
    }
    
    /**
     * this defines constants used for generic constants
     */
    this.m_constants = {
        "UI_BRANDING_FORDVEHICLES": "1",
        "UI_BRANDING_FORDDIRECT": "3",
        "UI_BRANDING_DEALERCONNECTION": "2" //this value has been assumed
    }
    
    /**
     * this defines constants for referrring site
     */
    this.s_referringSiteConsts = {
        "LEAD_SOURCE_UNKNOWN": "0",
        "LEAD_SOURCE_FORDVEHICLES": "1",
        "LEAD_SOURCE_DEALERCONNECTION": "2",
        "LEAD_SOURCE_FORD_COM": "3",
        "LEAD_SOURCE_LINKSHARE": "4",
        "LEAD_SOURCE_SEARCHENGINE": "5",
        "LEAD_SOURCE_DEALERDIRECT": "6",
        "LEAD_SOURCE_AXZPLAN": "7",
        "LEAD_SOURCE_MARKETINGPARTNER": "8",
        "LEAD_SOURCE_FORDVEHICLES_PKG": "9",
        "LEAD_SOURCE_DEALERCONNECTION_DIRECT_URL": "10",
        "LEAD_SOURCE_LINCOLNVEHICLES": "11",
        "LEAD_SOURCE_MERCURYVEHICLES": "12",
        "LEAD_SOURCE_SMART_GUIDE": "13",
        "LEAD_SOURCE_MY_FOLDER": "14"
    };
    /**
     * This defines the various states from showroom given by the key makeTransition
     */
    this.showroomTargets = {
        "config" : "ToTrimSelect",
        "build" : "ToTrimSelect",
        "inventory" : "ToFilter",
        "quote" : "ToQuickQuote"
     };
    this.showroomDefaultTarget = "config";
    /**
     * this defines constants for lead source
     */
    this.s_leadSourceConsts = {
        "FORD_VEHICLES": "FordVehicles.com",
        "FORD_DOT_COM": "Ford.com",
        "LINKSHARE": "LinkShare",
        "SEARCH_ENGINE_SITE": "SearchEngine",
        "UNKNOWN_MARKETING_AFFILIATE": "UnknownMarketingAffiliate",
        "FORDDIRECT": "FordDirect",
        "DC_SUBSOURCE_FORD": "DC New Quote Ford",
        "UNKNOWN_SOURCE": "UnknownSource",
        "LEAD_SOURCE_DEALERDIRECT": "FORDDIRECT"
    }
    this.m_legacyParamMap = {};
    
    this.getReferringSite = function(name){
        return this.s_referringSiteConsts[name];
    }
    
    this.getLeadSource = function(name){
        return this.s_leadSourceConsts[name];
    }
    
    this.getConstant = function(key){
        return this.m_constants[key];
    }
    
    this.debug = function(){
        return this.m_runInDebugMode;
    }
    
    this.getLinkinParamNames = function(param){
        return this.m_linkinParamsMapping[param];
    }
    
    /**
     * This funtion is used to set the value in the m_legacyParamMap structure.It can only be added if the Param is present in the m_linkinParamsMapping structure.And does not have any current value.
     * This funtion doesn't support overwritting of values in m_legacyParamMap.Only new values can be added.
     * @param {Object} ParamName  The Param should be present in m_linkinParamsMapping structure.
     * @param {Object} Value
     */
    this.setLegacyParams = function(ParamName, Value){
        if (typeof this.m_linkinParamsMapping[ParamName] != "undefined") {
            if (typeof this.m_legacyParamMap[this.m_linkinParamsMapping[ParamName]] == "undefined") {
                this.m_legacyParamMap[this.m_linkinParamsMapping[ParamName]] = Value;
            }
        }
        
    }
    
    /**
     * This method reads the FD linkin parameters from urlParamsMap and
     * 1. stores the original values in a legacy map {m_legacyParamMap}
     * 2. Transforms each of the values
     * The parameters it works on are
     * {branding, referringSite, referrer, referrerURL, partner, referredOnDate, siteID, ovmtc, campaignID, configID, zipCode, lang}
     * @param {Object} paramMap
     */
    this.parseLegacyParams = function(paramMap){
        //parse the value for branding
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: Entering parseLegacyParams");
        this.m_legacyParamMap = {};
        this.transformBranding(paramMap, this.m_legacyParamMap);
        
        //Set the param referringSite from the param map
        var referringSite = paramMap[this.m_linkinParamsMapping.REFERRING_SITE];
        if (null != referringSite) {
            this.m_legacyParamMap[this.m_linkinParamsMapping.REFERRING_SITE] = referringSite;
        }
        //Set the param referr from the param Map
        var referrer = paramMap[this.m_linkinParamsMapping.REFERRER];
        if (null != referrer) {
            this.m_legacyParamMap[this.m_linkinParamsMapping.REFERRER] = referrer;
        }
        //Set the param referrerURL from the param Map
        var referrerURL = paramMap[this.m_linkinParamsMapping.REFERRER_URL];
        if (null != referrerURL) {
            this.m_legacyParamMap[this.m_linkinParamsMapping.REFERRER_URL] = referrerURL;
        }
        //Set the param referrerURL from the param Map
        var partner = paramMap[this.m_linkinParamsMapping.PARTNER];
        if (null != partner) {
            this.m_legacyParamMap[this.m_linkinParamsMapping.PARTNER] = partner;
        }
        //Set the param referrerURL from the param Map
        var referredOnDate = paramMap[this.m_linkinParamsMapping.REFERRED_ON_DATE];
        if (null != partner) {
            this.m_legacyParamMap[this.m_linkinParamsMapping.REFERRED_ON_DATE] = referredOnDate;
        }
        
        this.transformSiteID(paramMap, this.m_legacyParamMap);
        this.transformOVMTC(paramMap, this.m_legacyParamMap);
        var campaignID = paramMap[this.m_linkinParamsMapping["CAMPAIGN_ID"]];
        if (null != campaignID) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["CAMPAIGN_ID"]] = campaignID;
        }
        var configID = paramMap[this.m_linkinParamsMapping["CONFIG_ID"]];
        if (null != configID) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["CONFIG_ID"]] = configID;
        }
        var zipcode = paramMap[this.m_linkinParamsMapping["ZIPCODE"]];
        if (null != zipcode) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["ZIPCODE"]] = zipcode;
        }
        
        var bannerID = paramMap[this.m_linkinParamsMapping["BANNERID"]];
        if (null != bannerID) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["BANNERID"]] = bannerID;
        }
        this.transformPlanType(paramMap, this.m_legacyParamMap);
        var lang = paramMap[this.m_linkinParamsMapping["LANG"]];
        if (null != lang) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["LANG"]] = lang;
        }
        
        if (!paramMap["FVS"]) {
            var makeTransition = paramMap[this.m_linkinParamsMapping.MAKETRANSITION];
            if (null != makeTransition && typeof this.showroomTargets[makeTransition.toLowerCase()] != "undefined") {
                makeTransition = makeTransition.toLowerCase();
                this.m_legacyParamMap[this.m_linkinParamsMapping.MAKETRANSITION] = this.showroomTargets[makeTransition];
            }
            else {
                this.m_legacyParamMap[this.m_linkinParamsMapping.MAKETRANSITION] = this.showroomTargets[this.showroomDefaultTarget];
            }
        }
        //log the linkin params
        this.printMap(this.m_legacyParamMap);
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: Leaving parseLegacyParams");
    }
    
    /**
     * This function updates the legacyParamMap when zipCode is changed
     * @param {Object} paramMap
     */
    this.updateLegacyParamZipCode = function(paramMap){
        var zipcode = paramMap[this.m_linkinParamsMapping["ZIPCODE"]];
        if (null != zipcode) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["ZIPCODE"]] = zipcode;
        }
    }
    
    /**
     * This function updated the legacyParamMap when configId is changed
     * @param {Object} paramMap
     */
    this.updateLegacyParamConfigId = function(paramMap){
        var configID = paramMap[this.m_linkinParamsMapping["CONFIG_ID"]];
        if (null != configID) {
            this.m_legacyParamMap[this.m_linkinParamsMapping["CONFIG_ID"]] = configID;
        }
    }
    
    /**
     * This method logs the transformed paramter map from FD. This is used for integration testing
     * and verification.
     * @param {Object} legacyMap
     */
    this.printMap = function(legacyMap){
        log4javascript.getDefaultLogger().debug("---------------START: LegacyMap for outbound links is--------------------");
        for (var a in legacyMap) {
            log4javascript.getDefaultLogger().debug("" + a + " : " + legacyMap[a]);
        }
        log4javascript.getDefaultLogger().debug("---------------LegacyMap for outbound links is--------------------");
    }
    /**
     * this method extracts the plantype value from paramMap.
     * It then computes a decoded plantype based on the plan type value and stores the decoded value in the param map
     * it then checks if referrerr or referrerURL is not "DirectAZPlan" or "DirectXPlan", and sets a "PricingPlan" cookie if
     * the condition is valid.
     * @param {Object} paramMap
     * @param {Object} legacyMap
     */
    this.transformPlanType = function(paramMap, legacyMap){
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: Entering transformPlanType");
        var planType = paramMap[this.m_linkinParamsMapping["PLAN_TYPE"]];
        
        var referrer = paramMap[this.m_linkinParamsMapping["REFERRER"]];
        var referrerURL = paramMap[this.m_linkinParamsMapping["REFERRER_URL"]];
        if (null != planType) {
            legacyMap[this.m_linkinParamsMapping["PLAN_TYPE"]] = planType;
            planType = BPTrim(planType).planType = planType.toUpperCase();
            var decodedPlan = "";
            switch (planType) {
                case 'A':
                case 'Z':
                case 'AZ':
                case 'AXZ':
                case 'A/Z-PLAN':
                    decodedPlan = unescape("A/Z-Plan");
                    break;
                case 'X':
                case 'X-Plan':
                    decodedPlan = 'X-Plan';
                    break;
                default:
                    decodedPlan = null;
            }
            paramMap["DECODED_PLAN"] = decodedPlan;
            /*
 * if (referrer or referrerURL) is not one of "DirectAZPlan", "DirectXPlan" then, set a cookie by name "PricingPlan" with value
 * "<decodedPlanName> from <referrer>" for domain "forddirect.fordvehicles.com" with maxage as 30 days.
 */
            if ("DirectAZPlan" != referrer && "DirectXPlan" != referrer && "DirectAZPlan" != referrerURL && "DirectXPlan" != referrerURL) {
                var now = new Date();
                cookieManager.createCookie("PricingPlan", decodedPlan + ' from ' + referrer, {
                    domain: 'forddirect.fordvehicles.com',
                    expires: now.setDate(now.getDate() + 30),
                    path: '/'
                });
            }
        }
        
    }
    
    /**
     * This method transforms the branding value passed as linkin param.
     * If no valid value is passed, a value of UI_BRANDING_FORDDIRECT is taken as default
     * @param {Object} paramMap
     * @param {Object} legacyMap
     */
    this.transformBranding = function(paramMap, legacyMap){
        var branding = paramMap[this.m_linkinParamsMapping["BRANDING"]];
        if (null != branding) {
            //set the old value for branding
            legacyMap[this.m_linkinParamsMapping["BRANDING"]] = branding;
            //check if the value is valid
            if (this.getConstant("UI_BRANDING_FORDDIRECT") != branding && this.getConstant("UI_BRANDING_FORDVEHICLES") != branding && this.getConstant("UI_BRANDING_DEALERCONNECTION") != branding) {
                paramMap[this.m_linkinParamsMapping["BRANDING"]] = this.getConstant("UI_BRANDING_FORDDIRECT");
            }
            else {
                paramMap[this.m_linkinParamsMapping["BRANDING"]] = branding;
            }
        }
        else {
            paramMap[this.m_linkinParamsMapping["BRANDING"]] = this.getConstant("UI_BRANDING_FORDDIRECT");
        }
    }
    
    /**
     * This method transforms the site ID. It creates a cookie by name "Linkshare" with a value
     * <siteID>@<timestamp> if siteid exists.
     * This also checks if a "Linkshare" cookie exists and sets the referring site with LeadSourceLinkshare
     * and referredOndate with Timestamp if the cookie exists.
     * @param {Object} paramMap
     * @param {Object} legacyMap
     */
    this.transformSiteID = function(paramMap, legacyMap){
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: Entering transformSiteID");
        var siteID = paramMap[this.m_linkinParamsMapping["SITE_ID"]];
        if (null != siteID) {
            legacyMap[this.m_linkinParamsMapping["SITE_ID"]] = siteID;
            /*
 * call the cookie manager and create a cookie by name 'Linkshare'
 * with value <siteID>@<timestamp> for the domain ".forddirect.fordvehicles.com"
 * with the path set to "/" for a period of 365 days.
 * <timestamp> is the current time in the format yyyy-MM-dd/HH:mm:ss
 */
            var cookieValue = siteID + '@' + formatDate(new Date(), "yyyy-MM-dd/HH:mm:ss")
            //get a date 365 days from now
            var d2 = new Date();
            var tm = d2.getTime() + 365 * 24 * 60 * 60 * 1000;
            d2.setTime(tm);
            cookieManager.createCookie("Linkshare", cookieValue, {
                expires: d2,
                path: "/",
                domain: "forddirect.fordvehicles.com"
            });
        }
        
        /*
 * If the "Linkshare" cookie is available, then
 override the referringSite by setting it to LEAD_SOURCE_LINKSHARE
 override referredOnDate with <timestamp>
 override "Affiliate Transaction Sub ID"
 */
        var linkShareCookie = cookieManager.readCookie("Linkshare");
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: TransformSiteID Linkshare cookie value is " + linkShareCookie);
        if (null != linkShareCookie) {
            //override the referringSite by setting it to LEAD_SOURCE_LINKSHARE
            paramMap[this.m_linkinParamsMapping["REFERRING_SITE"]] = this.getReferringSite("LEAD_SOURCE_LINKSHARE");
            //override referredOnDate with <timestamp>
            var cookieValueArray = linkShareCookie.split("@");
            paramMap[this.m_linkinParamsMapping["REFERRED_ON_DATE"]] = cookieValueArray[1];
            //override "Affiliate Transaction Sub ID"
            paramMap[this.m_linkinParamsMapping["AFFILIATE_TRANSACTION_SUB_ID"]] = cookieValueArray[0];
        }
        log4javascript.getDefaultLogger().debug("LegacyParamsParser:: Leaving transformSiteID");
    }
    
    /**
     * sets the ovmtc param in legacy map.
     * @param {Object} paramMap
     * @param {Object} legacyMap
     */
    this.transformOVMTC = function(paramMap, legacyMap){
        var ovmtc = paramMap[this.m_linkinParamsMapping["OVMTC"]];
        if (null != ovmtc) {
            legacyMap[this.m_linkinParamsMapping["OVMTC"]] = ovmtc;
            
        //TODO: validate the OVMTC value?
        }
    }
    /**
     * This functions return the legacy url containing the legacy variables.
     */
    this.getLegacyURLParams = function(){
        var urlString = "";
        for (var item in this.m_legacyParamMap) {
            if (typeof(this.m_bp2LegacyMapping[item]) != "undefined" || typeof(this.m_legacyParamMap[item]) != "undefined") {
                urlString = urlString + "&" + this.m_bp2LegacyMapping[item] + "=" + this.m_legacyParamMap[item];
            }
        }
        return urlString;
    }
    this.displayUsedCarsBanner = function(){
        log4javascript.getDefaultLogger( ).debug( "displayUsedCarsBanner--- STARTED");
        var referringSite = urlParamMap[this.m_linkinParamsMapping["REFERRING_SITE"]];
        var branding = urlParamMap[this.m_linkinParamsMapping["BRANDING"]];
        
            return(branding != this.getConstant("UI_BRANDING_DEALERCONNECTION") && referringSite != this.getReferringSite("LEAD_SOURCE_AXZPLAN") && referringSite != this.getReferringSite("LEAD_SOURCE_FORDVEHICLES") && referringSite != this.getReferringSite("LEAD_SOURCE_MERCURYVEHICLES") && referringSite != this.getReferringSite("LEAD_SOURCE_LINCOLNVEHICLES") && referringSite != this.getReferringSite("LEAD_SOURCE_SMART_GUIDE") && referringSite != this.getReferringSite("LEAD_SOURCE_MY_FOLDER"));                        
    }
}
var legacyParser = new com.forddirect.application.bp20.flow.LegacyParamsParser();
/**
 * This global function creates and initializes the Widget Toolkit instance
 */
var redirectPage=null;
var initParser = function(){
    log4javascript.getDefaultLogger().debug("Started legacy param parsing");
    legacyParser.parseLegacyParams(urlParamMap);
    redirectPage=urlParamMap["redirectPage"];
    if (legacyParser.debug()) {
        log4javascript.getDefaultLogger().debug('---------------------START: PARAMS MAP IN BP2.0 ---------------');
        for (var k in urlParamMap) {
            log4javascript.getDefaultLogger().debug(k + ' : ' + urlParamMap[k]);
        }
        log4javascript.getDefaultLogger().debug('---------------------END: PARAMS MAP IN BP2.0 ---------------');
    }
}

//This statement calls the global function initWtk when the DOM is loaded.
//YAHOO.util.Event.onDOMReady( initParser ) ;
/**
 * This function represents the com.forddirect.presentation.wtk.HistoryManager class.
 * @author Sachin Kulkarni
 * @author Praveen DS
 */
getPackageForName("com.forddirect.presentation.wtk").HistoryManager = function(){
    this.stateChangeHandlerFunc;
    
    this.context;
    
    this.historyEnabled = "";
    
    this.appName = "page";
    
    this.dependenciesInitialized = false;
    this.historyManagerInitialized = false;
    this.bookMarkLoaded = false;
    
    /**
     * This function initializes YUI HistoryManager. It registers onStageChange function. It also sets the
     * initial state of the application. Later it subscribes to onLoad event of HistoryManager.
     * @param stateChangeHandler function to be called when a state change occurs (clicking browser back/forward
     * button of a call to navigate function)
     * @param initialState default initial state of the application
     */
    this.init = function(){
        var bookmarkedSection = YAHOO.util.History.getBookmarkedState(this.appName);
        /*
         * This value is being returned when the YAHOO.util.History.getCurrentState() function is called
         */
        var initSection = bookmarkedSection || __pathinfo;
        this.stateChangeHandlerFunc = null;
        
        // Register our only module. Module registration MUST take
        // place before calling YAHOO.util.History.initialize.
        YAHOO.util.History.register(this.appName, initSection, function(state){
            if (wtkHistoryManager.stateChangeHandlerFunc != null) {
                wtkHistoryManager.stateChangeHandlerFunc(state, wtkHistoryManager.context);
            }
        });
        
        // Subscribe to this event before calling YAHOO.util.History.initialize,
        // or it may never get fired! Note that this is guaranteed to be fired
        // after the window's onload event.
        YAHOO.util.History.onLoadEvent.subscribe(function(){
            wtkHistoryManager.historyManagerInitialized = true;
            wtkHistoryManager.bookMarkLoaderOnHistoryManagerInitialization();
        });
        
        // The call to YAHOO.util.History.initialize should ALWAYS be from within
        // a script block located RIGHT AFTER the opening body tag (this seems to prevent
        // an edge case bug on IE - IE seems to sometimes forget the history when
        // coming back to a page, and the back - or forward button depending on the
        // situation - is disabled...)
        try {
            YAHOO.util.History.initialize("yui-history-field","yui-history-iframe");
        } catch (e) {
            // The only exception that gets thrown here is when the browser is not A-grade.
            // Since scripting is enabled, we still try to provide the user with a better
            // experience using AJAX. The only caveat is that the browser history will not work.
            log4javascript.getDefaultLogger().error("Error in enabling history" + e);
        }
    }
    
    /**
     * Note: this is one time call only
     * In IE 7, the history manager is initialized first and then the flowcontroller is downloaded (and the other way in Firefox)
     * here the dependenciesInitialized is used to check whether the init() of flowcontroller is successfully called as it
     * the init() there makes a call to this.initializeHandler where this variable is set to true
     */
    this.bookMarkLoaderOnHistoryManagerInitialization = function(){
        if (!this.bookMarkLoaded) {
            if (this.dependenciesInitialized && this.historyManagerInitialized) {
                log4javascript.getDefaultLogger().info("Yahoo History manager initialized");
                var myModuleCurrentState = YAHOO.util.History.getCurrentState(wtkHistoryManager.appName);
                if (null !== myModuleCurrentState) {
                    log4javascript.getDefaultLogger().info("Current state NOT null " + myModuleCurrentState);
                    this.stateChangeHandlerFunc(myModuleCurrentState, this.context);
                } else {
                    log4javascript.getDefaultLogger().error("current state is NULL. Should NOT happen");
                }
                this.bookMarkLoaded = true;
            }
            
        }
    }
    
    /**
     *
     */
    this.initializeHandler = function(stateChangeHandler, handlerContext){
        this.dependenciesInitialized = true;
        this.stateChangeHandlerFunc = stateChangeHandler;
        this.context = handlerContext;
        this.bookMarkLoaderOnHistoryManagerInitialization();
        
    }
    
    /**
     * This function adds the new state to YUI history. Once history is updated callback to stateChangeHandler
     * is called.
     * @param stateToken new state token
     */
    this.addToHistory = function(stateToken){
        if (typeof stateToken == "undefined") {
            log4javascript.getDefaultLogger().error("ConfigStateToken is undefined!  Cannot proceed");
            return;
        }
        if (null == stateToken) {
            log4javascript.getDefaultLogger().error("ConfigStateToken is null!  Cannot proceed");
            return;
        }
        try {
            YAHOO.util.History.navigate(this.appName, stateToken);
            
        } catch (e) {
            log4javascript.getDefaultLogger().error("History not enabled. manually calling state change handler for " + stateToken);
            this.stateChangeHandlerFunc(state, this.context);
        }
        
    }
}

//The global instance of the Widget Toolkit History Manager
var wtkHistoryManager = new com.forddirect.presentation.wtk.HistoryManager();
wtkHistoryManager.init();
/*************************************************************************
 Copyright (C) Unpublished Versata Software, Inc. All rights reserved.
 Versata Software, Inc., Confidential and Proprietary.
 This software is subject to copyright protection
 under the laws of the United States and other countries.
 Unless otherwise explicitly stated, this software is provided
 by Versata "AS IS".
 *************************************************************************/
/**
 * @author Praveen DS
 * @version 1.0
 */
/**
 * This is a helper class (yeah function) for manipulating URL parameters.  In the current implementation
 * the parameters extracted from the URL can either reside in cookies or in the URLParamMap.
 * Note:
 * Initial parameter values alone will be read using this helper class.  Subsequently only writing will happen
 * 1) For cookie enabled browsers, it will be the cookies to which the data is written
 * 2) For cookie disabled browsers, it will be the URL that will hold the updated values.
 *
 * Requires cookie manager to be initialized
 * @see com.forddirect.presentation.wtk.util.CookieManager
 */
getPackageForName("com.forddirect.presentation.wtk.util").ParamHelper = function(){
    /*
     * for URL Param map
     *
     */
    this.URL_MAP_YEAR = "Year";
    this.URL_MAP_MAKE = "Make";
    this.URL_MAP_MODEL = "Model";
    this.URL_MAP_DEFINERS = "Definers";
    this.URL_MAP_TRANS = "Transmissions";
    this.URL_MAP_PAGE = "Page";
    this.URL_MAP_CONFIGID = "configID";
    this.URL_MAP_ZIPCODE = "zipCode";
    this.URL_MAP_SELECT = "select";
    this.URL_MAP_UNSELECT = "unselect";
    this.URL_MAP_BANNERID = "bannerID";
    
    
    this.getMake = function(){
        return urlParamMap[this.URL_MAP_MAKE];
    }
    
    this.getModel = function(){
        return urlParamMap[this.URL_MAP_MODEL];
    }
    
    this.getYear = function(){
        return urlParamMap[this.URL_MAP_YEAR];
    }
    
    this.getPage = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.getSub(this.urlParamMapCookieName, this.pageCookieName);
        } else {
            return urlParamMap[this.URL_MAP_PAGE];
        }
        
    }
    
    this.getDefiners = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.getSub(this.urlParamMapCookieName, this.definersCookieName);
        } else {
            return urlParamMap[this.URL_MAP_DEFINERS];
        }
        
    }
    
    this.getTransmission = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.getSub(this.urlParamMapCookieName, this.transmissionCookieName);
        } else {
            return urlParamMap[this.URL_MAP_TRANS];
        }
        
    }
    
    /*
     * This is common across vehicles
     */
    this.getZipCode = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.readCookie(this.zipCodeCookieName);
        } else {
            return urlParamMap[this.URL_MAP_ZIPCODE];
        }
        
    }
      /*
     * This is common across all vehicles
     */
    
    this.getBannerID = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.readCookie(this.bannerIDCookieName);
        } else {
            return urlParamMap[this.URL_MAP_BANNERID];
        }
        
    }
    
    this.getConfigToken = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.getSub(this.urlParamMapCookieName, this.configTokenCookieName);
        } else {
            return urlParamMap[this.URL_MAP_CONFIGID];
        }
        
    }
    
    this.getSelectPart = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.cookieManager.getSub(this.urlParamMapCookieName, this.selectPartCookieName);
        } else {
            return urlParamMap[this.URL_MAP_SELECT];
        }
        
    }
    
    this.getUnselectPart = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.cookieManager.getSub(this.urlParamMapCookieName, this.unselectPartCookieName);
        } else {
            return urlParamMap[this.URL_MAP_UNSELECT];
        }
        
    }
    
    this.getURLParamMapFromPreviousVisit = function(){
        if (cookieManager.isCookieEnabled) {
            return cookieManager.getSubs(this.urlParamMapCookieName);
        } else {
            //Captured in the URL already.
            return null;
        }
    }
    
    this.setMake = function(value){
        //TODO : Remove this cookie. We are approaching launch, and this 
        //cookie is kept to refrain from breaking any functionality due to 
        //the use of cookie directly.
        urlParamMap[this.URL_MAP_MAKE] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.makeCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setModel = function(value){
        //TODO : Remove this cookie. We are approaching launch, and this 
        //cookie is kept to refrain from breaking any functionality due to 
        //the use of cookie directly.
        urlParamMap[this.URL_MAP_MODEL] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.modelCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setYear = function(value){
        //TODO : Remove this cookie. We are approaching launch, and this 
        //cookie is kept to refrain from breaking any functionality due to 
        //the use of cookie directly.
        urlParamMap[this.URL_MAP_YEAR] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.yearCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setPage = function(value){
        urlParamMap[this.URL_MAP_PAGE] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.pageCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setDefiners = function(value){
        urlParamMap[this.URL_MAP_DEFINERS] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.definersCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setTransmission = function(value){
        urlParamMap[this.URL_MAP_TRANS] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.transmissionCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    /*
     * As zipcode is common across vehicles, we need to set it as a cookie as well.
     */
    this.setZipCode = function(value){
        urlParamMap[this.URL_MAP_ZIPCODE] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.createCookie(this.zipCodeCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
            cookieManager.setSub(this.urlParamMapCookieName, this.zipCodeCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
            
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        legacyParser.updateLegacyParamZipCode(urlParamMap);
    }
    
   /*
     * Set bannerId in url map and cookies.
     */
    
    this.setBannerID = function(value){
        urlParamMap[this.URL_MAP_BANNERID] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.createCookie(this.bannerIDCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
            cookieManager.setSub(this.urlParamMapCookieName, this.bannerIDCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
            
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
    }
    this.setConfigToken = function(value){
        urlParamMap[this.URL_MAP_CONFIGID] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.configTokenCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        legacyParser.updateLegacyParamConfigId(urlParamMap);
    }
    
    this.setSelectPart = function(value){
        urlParamMap[this.URL_MAP_SELECT] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.selectPartCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
        
    }
    
    this.setUnselectPart = function(value){
        urlParamMap[this.URL_MAP_UNSELECT] = value;
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSub(this.urlParamMapCookieName, this.unselectPartCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            wtkHistoryManager.addToHistory(urlUtils.getParametersPathWithPageInfo());
        }
    }
    
    this.setURLParamMap = function(value){
        if (cookieManager.isCookieEnabled) {
            cookieManager.setSubs(this.urlParamMapCookieName, value, {
                expires: new Date("January 12, 2025"),
                path: "/"
            });
        } else {
            //Here it is already captured in the URL
        }
    }
    
    /**
     * Removes all the cookies set for identifying a vehicle (make, model, year, parts, transmission, definers, configID, zipCode)
     */
    this.removeAllVehicleCookies = function(){
        var options = {
            path: "/"
        };
        cookieManager.removeCookie(this.makeCookieName, options);
        cookieManager.removeCookie(this.modelCookieName, options);
        cookieManager.removeCookie(this.yearCookieName, options);
        cookieManager.removeCookie(this.definersCookieName, options);
        cookieManager.removeCookie(this.transmissionCookieName, options);
        cookieManager.removeCookie(this.zipCodeCookieName, options);
        cookieManager.removeCookie(this.configTokenCookieName, options);
        cookieManager.removeCookie(this.selectPartCookieName, options);
        cookieManager.removeCookie(this.unselectPartCookieName, options);
    }
    
    
    this.getCookieName = function(name){
        return this.getModel() + this.getMake() + this.getYear() + name;
    }
    
    this.makeCookieName = this.URL_MAP_MAKE;
    this.modelCookieName = this.URL_MAP_MODEL;
    this.yearCookieName = this.URL_MAP_YEAR;
    this.pageCookieName = this.URL_MAP_PAGE;
    this.definersCookieName = this.URL_MAP_DEFINERS;
    this.transmissionCookieName = this.URL_MAP_TRANS;
    this.zipCodeCookieName = this.URL_MAP_ZIPCODE;
    this.bannerIDCookieName = this.URL_MAP_BANNERID;
    this.configTokenCookieName = this.URL_MAP_CONFIGID;
    this.selectPartCookieName = this.URL_MAP_SELECT;
    this.unselectPartCookieName = this.URL_MAP_UNSELECT;
    this.urlParamMapCookieName = this.getCookieName("urlParamMap");
}

//The global instance of the ParamHelper
var paramHelper = new com.forddirect.presentation.wtk.util.ParamHelper();/*
 * Copied from Forddirect.com (called CookieUtility in FD code).   For retrieving some session cookies set by FV.
 * Please refer http://issues.ngptools.com/jira/browse/BP-921 for details
 */
var sDefaultLifeTime = 65;
var sPATH = ";path=/";
var sEXPIRES = ";expires=";
var sDOMAIN = ";domain=";

SessionCookieUtility = function(){
}

// if non-blank, cookies shared among servers in this domain
SessionCookieUtility.COOKIE_DOMAIN = "";

//APIs to be supported for MyFolder. Please remove/modify them after 8.14
SessionCookieUtility.createCookie = function(name, value, days){
    SessionCookieUtility.setCookieValue(name, value, days);
}

SessionCookieUtility.readCookie = function(name){
    return SessionCookieUtility.getCookieValue(name);
}

SessionCookieUtility.eraseCookie = function(name){
    SessionCookieUtility.eraseCookie(name, SessionCookieUtility.COOKIE_DOMAIN);
}

SessionCookieUtility.eraseCookie = function(name, domain){
    SessionCookieUtility.setCookieValue(name, "", 0, domain);
}

/* Public Functions  Ported from Ford Vehicles for maintaining consistency of the Cookie Storage and retrieval from a sub-cookie format*/
SessionCookieUtility.setCookieValue = function(name, val, nDays){
    SessionCookieUtility.setCookieValue(name, val, nDays, SessionCookieUtility.COOKIE_DOMAIN);
}

SessionCookieUtility.setCookieValue = function(name, val, nDays, domain){
    var sCookie = "";
    var oToday = new Date();
    var oExpire = new Date();
    
    if (typeof(domain) != "string" || domain == null || domain == "") {
        domain = SessionCookieUtility.COOKIE_DOMAIN;
    }
    
    if (name == "MyFolderUserId") {
        SessionCookieUtility.COOKIE_DOMAIN = '.fordvehicles.com';
        domain = '.fordvehicles.com';
        nDays = "";
    }
    
    if (typeof(name) == "string") {
        if (name.length != 0) {
            sCookie = name + "=" + escape(val) + sPATH;
            if (typeof(nDays) == "number") {
                oExpire.setTime(oToday.getTime() + 60 * 60 * 1000 * 24 * nDays);
                sCookie = sCookie + sEXPIRES + oExpire.toGMTString();
            }
            if (domain != '') {
                domain = sDOMAIN + domain; //Add domain prefix
            }
            sCookie = sCookie + domain;
            document.cookie = sCookie;
        }
    }
}

SessionCookieUtility.setSubCookieValue = function(sname, subname, val, nDays){
    var cookie = document.cookie;
    var chkdCookie = SessionCookieUtility.removeBlanks(cookie);
    var aCookie = chkdCookie.split(";");
    var iCookie = SessionCookieUtility.getCookieIndex(aCookie, sname);
    
    if (iCookie >= 0) {
        var aSubCookie = aCookie[iCookie].split("&");
        var iSubCookie = SessionCookieUtility.getSubCookieIndex(aSubCookie, subname);
    }
    
    var sCookie = "";
    var oToday = new Date();
    var oExpire = new Date();
    
    if (typeof(sname) == "string") {
        if (subname.length != 0) {
            // Replace subcookie 
            if (Number(iSubCookie) >= 0) {
                for (j = 0; j < aSubCookie.length; ++j) {
                    splitValues = aSubCookie[j].split("=");
                    if (splitValues[0] == subname) {
                        aSubCookie[j] = subname + "=" + escape(val);
                    } else if (splitValues[0] == sname) {
                        if (splitValues[1] == subname) {
                            aSubCookie[j] = sname + "=" + subname + "=" + escape(val);
                        }
                    }
                    
                    if ((j > 0) && (j < aSubCookie.length)) {
                        sCookie = sCookie + "&";
                    }
                    sCookie = sCookie + aSubCookie[j];
                }
                sCookie = sCookie + sPATH;
            } else {
                if (iCookie >= 0) {
                    // Add to existing cookie         
                    sCookie = aCookie[iCookie] + "&" + subname + "=" + escape(val) + sPATH;
                } else {
                    sCookie = sname + "=" + subname + "=" + escape(val) + sPATH;
                }
            }
            if (typeof(nDays) == "number") {
                var sDomain = SessionCookieUtility.getDomain();
                oExpire.setTime(oToday.getTime() + 3600000 * 24 * nDays);
                sCookie = sCookie + sEXPIRES + oExpire.toGMTString() + sDomain;
            }
            document.cookie = sCookie;
        }
    }
}

SessionCookieUtility.getCookieValue = function(cname){
    var nameEQ = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') 
            c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}

SessionCookieUtility.getSubCookieValue = function(cname, subname){
    var cookie = document.cookie;
    var chkdCookie = SessionCookieUtility.removeBlanks(cookie);
    var aCookie = chkdCookie.split(";");
    var iCookie = SessionCookieUtility.getCookieIndex(aCookie, cname);
    var iSubCookie = -1;
    
    if (iCookie >= 0) {
        var aSubCookie = unescape(aCookie[iCookie]).split("&");
        var iSubCookie = SessionCookieUtility.getSubCookieIndex(aSubCookie, subname);
    }
    
    var sReturn = "";
    if (iSubCookie >= 0) {
        var aT = aSubCookie[iSubCookie].split("=");
        if (iSubCookie > 0) {
            sReturn = aT[1];
        } else {
            sReturn = aT[2];
        }
    }
    return sReturn;
}

//Method to get the domain string in case the cookie is shared accross servers
SessionCookieUtility.getDomain = function(){
    var domain = '';
    if (SessionCookieUtility.COOKIE_DOMAIN != '') {
        domain = sDOMAIN + SessionCookieUtility.COOKIE_DOMAIN;
    }
    return domain;
}

SessionCookieUtility.getSubCookieIndex = function(aSubCookies, thename){
    var splitValues;
    var i;
    for (i = 0; i < aSubCookies.length; ++i) {
        if (i == 0) {
            splitValues = aSubCookies[i].split("=");
            if (splitValues[1] == thename) {
                return i;
            }
        } else {
            splitValues = aSubCookies[i].split("=");
            if (splitValues[0] == thename) {
                return i;
            }
        }
    }
    return -1;
}

SessionCookieUtility.getCookieIndex = function(aCookies, thename){
    var splitValues;
    var i;
    for (i = 0; i < aCookies.length; ++i) {
        splitValues = aCookies[i].split("=");
        if (splitValues[0] == thename) {
            return i;
        }
    }
    return -1;
}

SessionCookieUtility.removeBlanks = function(strng){
    var result = "";
    var i;
    var chrn;
    for (i = 0; i < strng.length; ++i) {
        chrn = strng.charAt(i);
        if (chrn != " ") result += chrn;
    }
    return result;
}


//this function is load all the widgets required for the build and price page.
function startWidgets() {
    var widgets = new Object( ) ;
    var widgetName = "flowController";
    widgets[widgetName] = new Array();
    widgets[widgetName].push( {'widgetClass': "com.forddirect.application.bp20.flow.FlowController", 'widgetLocation': __appBasePath+"static/com/forddirect/application/bp20/flow/FlowController.js", 'widgetId': "flowController", "type":"js", "initParams":{}});
    log4javascript.getDefaultLogger( ).debug( "START the first call" ) ; 
    var completionEvent = wtk.loadWidgets( widgets, true ) ;
    log4javascript.getDefaultLogger( ).debug( "START the last call" ) ;
    completionEvent.subscribe( mashup, widgets ) ; 
}

//function to glue the individual widgets.
function mashup(mashupEvent, firedData, subscribedData ) {

    
    var widgets = subscribedData ;
    var widgetArray = wtk.widgetInstances ;

    log4javascript.getDefaultLogger( ).debug( "START OF---mashup" ) ; 
    //The reference to all the objects is copied in the variable by the name Object <Widget> for easy usage
    var flowControllerWidget = widgetArray[widgets[0].loaderId] ;

    //The initial load event is defined and the *DATA* Widgets are set. Subscribing Data Widgets rather UI Widgets to the initial load event might save some checks when 2 UI widgets share the same Data Widget. In the latter case, all the UI Widgets might trigger the call to the same Data widget and in some synchronization issue, the data widget might trigger 2 calls for the data.
    //The UI Widgets can subscribe to the event and use it for locking and flagging.
    initialLoadEvent = new YAHOO.util.CustomEvent("initialLoadEvent");
    initialLoadEvent.fire( ) ;

    log4javascript.getDefaultLogger( ).debug( "END OF---mashup" ) ; 
}
//widgets for the build and price page should be loaded after the widget tool kit has been loaded completely.

wtk.initCompleteEvent.subscribe( startWidgets ) ; 
//Refer to the file WidgetToolkit.js for more information about the wtk object 