// Copyright Wall Street On Demand
// JSJuiced using http://labs.gueschla.com/jsjuicer/, http://adrian3.googlepages.com/jsjuicer.html
// ------------------------------------------------------------------------------------------------
// slimmed down Function methods from wsdom.js
Function.prototype.Extend=function(superClass){this.prototype=new superClass();this.prototype.getSuperClass=function(){return superClass;};this.getSuperClass=this.prototype.getSuperClass;return this;};
Function.prototype.Super=function(context,methodName,args){if(null!=methodName){var method=this.getSuperClass().prototype[methodName];}
else{var method=this.getSuperClass();}
if(!args){return method.call(context);}
else{return method.apply(context,args);}};
Function.prototype.Context=function(obj){var fnReference=this;return function(){return typeof fnReference=="function"?fnReference.apply(obj,arguments):obj[fnReference].apply(obj,arguments);};};
// really slimmed down Element.3.js
var Element_class=function(){}
Element_class.prototype.get=function(el){if(typeof el=="string")el=document.getElementById(el);return el;};Element_class.prototype.create=function(tag,attributes,children,parent,ElementObjectInstance){var element=document.createElement(tag);var attributeMap={"for":["htmlFor"],"colspan":["colSpan"]}
for(var i in attributes)
{if(i=="className"||i=="class")
{element.className=attributes[i];}
else if(document.all&&attributeMap[i])
{for(var j=0;j<attributeMap[i].length;j++){element.setAttribute(attributeMap[i][j],attributes[i]);}
element.setAttribute(i,attributes[i]);}
else if(i=="style")
{this.setStyle(element,attributes[i]);}
else if(i=="Events")
{if(typeof Events!="undefined"){var elEvents=attributes[i];if(!this.isArray(elEvents)){elEvents=[elEvents]}
for(var j=0;j<elEvents.length;j++){elEvents[j].element=element;Events.add(elEvents[j]);}}
else{alert(":: DEV ERROR :: \n Location: Element.3.js -- Element_class.prototype.create \n Type: Dependency \n Message: Expecting Events Lib for use of Events in Element.create")}}
else
{element.setAttribute(i,attributes[i]);};};if(arguments.length>2&&children){this.addChild(element,children);};if(parent){this.addChild(parent,element);}
return(ElementObjectInstance)?new ElementObject(element):element;};Element_class.prototype.addChild=function(el,child){el=this.get(el);if(!this.isArray(child)){child=[child]}
for(var i=0;i<child.length;i++){if(typeof child[i]=="object"){el.appendChild(child[i]);}
else if(typeof child[i]=="string"||typeof child[i]=="number"){el.innerHTML+=child[i];};}};Element_class.prototype.remove=function(el){el=this.get(el);if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){el[i].parentNode.removeChild(el[i])}};Element_class.prototype.removeChildNodes=function(el){el=this.get(el);while(el.childNodes.length){el.removeChild(el.firstChild);}
return el;};
Element_class.prototype.setOpacity=function(el,opacity){el=this.get(el);if(!el)return
if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){el[i].style.filter="alpha(opacity:"+opacity+")";el[i].style.KHTMLOpacity=opacity/100;el[i].style.MozOpacity=opacity/100;el[i].style.opacity=opacity/100;}};
/* parseselector */
Element_class.prototype.parseSelector=(function(){var SEPERATOR=/\s*,\s*/;function parseSelector(selector,node,num){node=node||document.documentElement;node=Element.get(node);var argSelectors=selector.split(SEPERATOR);var result=[];for(var i=0;i<argSelectors.length;i++){var nodes=[node];var stream=toStream(argSelectors[i]);for(var j=0;j<stream.length;){var token=stream[j++];var filter=stream[j++];var args='';if(stream[j]=='('){while(stream[j++]!=')'&&j<stream.length)args+=stream[j];args=args.slice(0,-1);}
if(stream[j]=='['){while(stream[j++]!=']'&&j<stream.length)args+=stream[j];args=args.slice(0,-1);token="[";}
nodes=select(nodes,token,filter,args);}
result=result.concat(nodes);}
if(num!=undefined){if(result.length&&num=="first"){return result[0]}
else if(result.length&&num=="last"){return result[result.length-1]}
else if(result.length&&!isNaN(num)&&result.length>=num){return result[num]}
else{return null;}}
return result;}
var WHITESPACE=/\s*([\s>+~(),]|^|$)\s*/g;var IMPLIED_ALL=/([\s>+~,]|[^(]\+|^)([#.:@])/g;var STANDARD_SELECT=/^[^\s>+~]/;var STREAM=/[\s#.:>+~[\]()@!]|[^\s#.:>+~[\]()@!]+/g;function toStream(selector){var stream=selector.replace(WHITESPACE,'$1').replace(IMPLIED_ALL,'$1*$2');if(STANDARD_SELECT.test(stream))stream=' '+stream;return stream.match(STREAM)||[];}
function select(nodes,token,filter,args){return(selectors[token])?selectors[token](nodes,filter,args):[];}
var util={toArray:function(enumerable){var a=[];for(var i=0;i<enumerable.length;i++)util.push(a,enumerable[i]);return a;},push:function(arr,val){arr.push(val)
return arr.length;}};var dom={isTag:function(node,tag){return(tag=='*')||(tag.toLowerCase()==node.nodeName.toLowerCase().replace(':html',''));},previousSiblingElement:function(node){do node=node.previousSibling;while(node&&node.nodeType!=1);return node;},nextSiblingElement:function(node){do node=node.nextSibling;while(node&&node.nodeType!=1);return node;},hasClass:function(name,node){return(node.className||'').match('(^|\\s)'+name+'(\\s|$)');},getByTag:function(tag,node){if(tag=='*'){var nodes=node.getElementsByTagName(tag);if(nodes.length==0&&node.all!=null)return node.all
return nodes;}
return node.getElementsByTagName(tag);}};var selectors={'#':function(nodes,filter){for(var i=0;i<nodes.length;i++){if(nodes[i].getAttribute('id')==filter)return[nodes[i]];}
return[];},' ':function(nodes,filter){var result=[];for(var i=0;i<nodes.length;i++){result=result.concat(util.toArray(dom.getByTag(filter,nodes[i])));}
return result;},'>':function(nodes,filter){var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];for(var j=0,child;j<node.childNodes.length;j++){child=node.childNodes[j];if(child.nodeType==1&&dom.isTag(child,filter)){util.push(result,child);}}}
return result;},'.':function(nodes,filter){var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];if(dom.hasClass([filter],node))util.push(result,node);}
return result;},'!':function(nodes,filter){var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];if(!dom.hasClass([filter],node))util.push(result,node);}
return result;},':':function(nodes,filter,args){return(pseudoClasses[filter])?pseudoClasses[filter](nodes,args):[];},'+':function(nodes,filter){var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];var sibling=parseSelector.dom.nextSiblingElement(node);if(sibling&&parseSelector.dom.isTag(sibling,filter)){util.push(result,sibling);}}
return result;},'~':function(nodes,filter){var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];var sibling=parseSelector.dom.previousSiblingElement(node);if(parseSelector.dom.isTag(sibling,filter))util.push(result,sibling);}
return result;},'[':function(nodes,filter,args){args=args.replace(/'/g,'"');var attributeProps=[];if(!/=/.test(args)){attributeProps=["",args,"",""];}
else{var params=args.match(/([^^$!*]*)(!\*=|\*=|\$=|\^=|!=|=)(i)?["'](.*)["']/);if(params){attributeProps=params;}}
attributeProps={name:attributeProps[1],operator:attributeProps[2],casei:attributeProps[3],value:attributeProps[4]};if(attributeProps.casei){attributeProps.value=attributeProps.value.toLowerCase();}
var result=[];for(var i=0,node;i<nodes.length;i++){node=nodes[i];var children=parseSelector.dom.getByTag(filter,node);for(var j=0;j<children.length;j++){var att=children[j].getAttribute(attributeProps.name);if(!att)continue;if(attributeProps.casei){att=att.toLowerCase();}
if(!attributeProps.operator){util.push(result,children[j])}
else{switch(attributeProps.operator){case'=':if(att==attributeProps.value)util.push(result,children[j]);break;case'!=':if(att!=attributeProps.value)util.push(result,children[j]);break;case'*=':if(att.match(attributeProps.value))util.push(result,children[j]);break;case'!*=':if(!att.match(attributeProps.value))util.push(result,children[j]);break;case'^=':if(att.match('^'+attributeProps.value))util.push(result,children[j]);break;case'$=':if(att.match(attributeProps.value+'$'))util.push(result,children[j]);}}}}
return result;}};parseSelector.selectors=selectors;var pseudoClasses={};parseSelector.pseudoClasses=pseudoClasses;parseSelector.util=util;parseSelector.dom=dom;return parseSelector;})();
/* end parseselector */
Element_class.prototype.getParent=function(el,tag,includeSelf){var el=Element.get(el);if(!tag){tag=el.tagName;}
if(!includeSelf&&el.parentNode){el=el.parentNode;}
if(el.tagName&&el.tagName.match(/^BODY$/i)&&!tag.match(/^BODY$/i)){return null;}
if(el.nodeType==1&&el.tagName.toLowerCase()==tag.toLowerCase()){return el;}
else{return this.getParent(el.parentNode,tag,true);}}
Element_class.prototype.getParentBySelector=function(el,selector,includeSelf){el=this.get(el);var pNode=includeSelf?el:el.parentNode;selector=selector.replace(/\s+/," ").split(" ");var levels=selector.length;var level=0;var selectorType,isMatch,isTag,isClass;function getSelectorType(){var sel=selector[level];selectorType={tag:sel};if(sel.match(/(\D*)\#(\D*)/)){selectorType.tag=RegExp.$1;selectorType.id=RegExp.$2;}
else if(sel.match(/(.*)\[([^^$*]*?)((\*=|\$=|\^=|=)+["'](.*)["'])?]/)){selectorType.tag=RegExp.$1;selectorType.attribute=RegExp.$2;selectorType.operator=RegExp.$4;selectorType.value=RegExp.$5;}
else if(sel.match(/(\D*)\.(\D*)/)){selectorType.tag=RegExp.$1;selectorType.className=RegExp.$2;}}
getSelectorType();while(pNode&&!pNode.tagName.match(/^BODY$/i)){isMatch=false;isTag=pNode.tagName.match(new RegExp("^"+selectorType.tag.replace(/([*])/,"\\$1")+"$","i"))||selectorType.tag=="*"||selectorType.tag=="";isClass=selectorType.className&&this.hasClass(pNode,selectorType.className);if(isTag&&selectorType.attribute){var att=pNode.getAttribute(selectorType.attribute);if(!selectorType.operator){if(att)isMatch=true;}
else{switch(selectorType.operator){case'*=':;if(att.match(selectorType.value))isMatch=true;break;case'=':if(att==selectorType.value)isMatch=true;break;case'^=':if(att.match('^'+selectorType.value))isMatch=true;break;case'$=':if(att.match(selectorType.value+'$'))isMatch=true;}}}
else if(isTag){if(isClass){isMatch=true;}
else if(selectorType.tag&&!selectorType.className){isMatch=true;}
else if(selectorType.id&&pNode.getAttribute("id")==selectorType.id){isMatch=true;}}
else if(isTag&&isClass){isMatch=true;}
if(isMatch){if(level==levels-1){return pNode;}
level++;getSelectorType();}
pNode=pNode.parentNode||null;}
return null;}

Element_class.prototype.getXY=function(el){el=this.get(el);if(!el)return;var x=0,y=0;while(el.offsetParent){x+=el.offsetLeft;y+=el.offsetTop;el=el.offsetParent;}
return{x:x,y:y};};Element_class.prototype.setXY=function(el,x,y){el=this.get(el);if(!el)return;if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){if(x!==null)el[i].style.left=x+"px";if(y!==null)el[i].style.top=y+"px";}};Element_class.prototype.getSize=function(el){el=this.get(el);if(!el)return;var height=el.offsetHeight;var width=el.offsetWidth;return{height:height,width:width};};Element_class.prototype.setSize=function(el,width,height){el=this.get(el);if(!el)return;if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){this.setWidth(el[i],width);this.setHeight(el[i],height);}};Element_class.prototype.setWidth=function(el,width){el=this.get(el);if(!el)return;if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){el[i].style.width=width+"px";}};Element_class.prototype.setHeight=function(el,height){el=this.get(el);if(!el)return;if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){el[i].style.height=height+"px";}};Element_class.prototype.addClass=function(el,classname){el=this.get(el);if(!el)return;if(!this.isArray(el)){el=[el]}
for(var i=0;i<el.length;i++){if(!this.hasClass(el[i],classname)){el[i].className+=(el[i].className?" ":"")+classname;}}
if(el.length){return el[0].className;}};Element_class.prototype.removeClass=function(el,classname){el=this.get(el);if(!this.isArray(el)){el=[el]}
var re=this._getClassnameRegEx(classname);for(var i=0;i<el.length;i++){el[i].className=el[i].className.replace(re,"$1$3");}
if(el.length){return el[0].className;}};Element_class.prototype.hasClass=function(el,classname){el=this.get(el);return(el.className&&el.className.match(this._getClassnameRegEx(classname))!=null);};Element_class.prototype._getClassnameRegEx=function(classname){return new RegExp("(\\s|^)("+classname+")(\\s|$)","g")};Element_class.prototype.isArray=function(o){return(o instanceof Array);};var Element=new Element_class();
Element_class.prototype.setStyle=function(el,styles){el=this.get(el);if(!el)return;var pairs=[];styles=styles.split(";");for(var i=0;i<styles.length;i++){var nv=styles[i].replace(":","{:}").split("{:}");if(nv.length>1){nv[0]=nv[0].replace(/\-(.)/g,function(){return arguments[1].toUpperCase();}).replace(/\s/g,"");pairs.push({n:nv[0],v:nv[1].replace(/^\s*|\s*$/g,"")});}}
if(!this.isArray(el)){el=[el]}
var attributeMap={"float":["cssFloat","styleFloat"]}
for(var i=0;i<el.length;i++){for(var j=0;j<pairs.length;j++){if(attributeMap[pairs[j].n]){for(var k=0;k<attributeMap[pairs[j].n].length;k++){pairs.push({n:attributeMap[pairs[j].n][k],v:pairs[j].v});}}
el[i].style[pairs[j].n]=pairs[j].v;}}}
/* thanks to PPK - http://www.quirksmode.org/viewport/compatibility.html */
Element_class.prototype.getWindowSize=function(){if(self.innerHeight){var width=self.innerWidth;var height=self.innerHeight;}else if(document.documentElement&&document.documentElement.clientHeight){var width=document.documentElement.clientWidth;var height=document.documentElement.clientHeight;}else if(document.body){var width=document.body.clientWidth;var height=document.body.clientHeight;};return{width:width,height:height};};Element_class.prototype.getWindowScrollOffset=function(){if(typeof window.pageYOffset=='number'){var x=window.pageXOffset;var y=window.pageYOffset;}else if(document.body&&(document.body.scrollLeft||document.body.scrollTop)){var x=document.body.scrollLeft;var y=document.body.scrollTop;}else if(document.documentElement&&(document.documentElement.scrollLeft||document.documentElement.scrollTop)){var x=document.documentElement.scrollLeft;var y=document.documentElement.scrollTop;};return{x:x||0,y:y||0};};Element_class.prototype.getViewport=function(){var windowSize=this.getWindowSize();var scrollOffset=this.getWindowScrollOffset();var top=scrollOffset.y;var bottom=scrollOffset.y+windowSize.height;var left=scrollOffset.x;var right=scrollOffset.x+windowSize.width;return{top:top,left:left,bottom:bottom,right:right,width:windowSize.width,height:windowSize.height};};

// Events.3.js
var EventSource=function(type){this.listeners=[];this.type=type;};EventSource.prototype.addListener=function(listener,context){if(listener instanceof Function){listener={handler:listener,context:context}}
if(!listener.context){listener.context=window;}
this.listeners.push(listener);return listener;};EventSource.prototype.removeListener=function(listener){for(var i=0;i<this.listeners.length;i++){if(listener==this.listeners[i]){this.listeners.splice(i);}}};EventSource.prototype.removeAll=function(){this.listeners=[];};EventSource.prototype.fire=function(){for(var i=0;i<this.listeners.length;i++){Array.prototype.unshift.call(arguments,this.type);this.listeners[i].handler.apply(this.listeners[i].context,arguments);}};var DOMEventSource=function(type){DOMEventSource.Super(this,null,arguments);this.delayTimeouts=[];this.typeIE="on"+this.type;this.elements=[];};DOMEventSource.Extend(EventSource);DOMEventSource.prototype._getBrowserEventName=function(){switch(this.type){case"load":case"change":case"reset":case"select":case"submit":case"blur":case"focus":case"resize":case"scroll":case"abort":case"error":case"unload":return"HTMLEvents";case"mouseover":case"mouseout":case"click":case"dblclick":case"mouseup":case"mousedown":case"mouseenter":case"mouseleave":case"mousemove":case"contextmenu":return"MouseEvents";case"keypress":case"keydown":case"keyup":return"UIEvents";default:return null;}};DOMEventSource.prototype._createDOMHandlerClosure=function(listener,element){var theDOMEventSource=this;for(var i=0,DOMHandler;i<element.length;i++){DOMHandler=function(){var el=element[i].node;if(listener.delay){return function(){var e=new DOMEvent(window.event||arguments[0]);theDOMEventSource.clearDelayTimeouts();theDOMEventSource.delayTimeouts.push(window.setTimeout(function(){listener.handler.call(listener.context,e,el,listener.data);},listener.delay));}}
else{return function(){var e=new DOMEvent(window.event||arguments[0]);listener.handler.call(listener.context,e,el,listener.data);}}}();this._addEventListener(element[i].node,DOMHandler);element[i].registeredListeners.push({DOMHandler:DOMHandler,listener:listener});}};DOMEventSource.prototype.addElement=function(element,removeIfExisting){if(!(element instanceof Array)){element=[element];}
if(removeIfExisting){this.removeElement(element);}
var elements=[];for(var i=0;i<element.length;i++){elements.push({node:element[i],registeredListeners:[]});}
for(var i=0;i<this.listeners.length;i++){this._createDOMHandlerClosure(this.listeners[i],elements);}
this.elements=this.elements.concat(elements);};DOMEventSource.prototype.removeElement=function(element){var element=[].concat(element);for(var i=0,elWrapper;i<this.elements.length;i++){elWrapper=this.elements[i];if(!element.length){break;}
for(var j=0,el;j<element.length;j++){el=element[j];if(elWrapper.node===el){for(var k=0;k<elWrapper.registeredListeners.length;k++){this._removeEventListener(el,elWrapper.registeredListeners[k].DOMHandler);}
element.splice(j,1);this.elements.splice(i,1);j--;i--;}}}};DOMEventSource.prototype.removeAll=function(){this.removeAllElements();this.listeners=[];};DOMEventSource.prototype.removeAllElements=function(){for(var i=0,el;i<this.elements.length;i++){el=this.elements[i];for(var j=0;j<el.registeredListeners.length;j++){this._removeEventListener(el.node,el.registeredListeners[j].DOMHandler);}}
this.elements=[];};DOMEventSource.prototype.addListener=function(listener,context,delay){if(listener instanceof Function){listener={handler:listener,context:context,delay:delay}}
if(!listener.context){listener.context=window;}
this._createDOMHandlerClosure(listener,this.elements);this.listeners.push(listener);return listener;};DOMEventSource.prototype.removeListener=function(listener){for(var i=0,el;i<this.elements.length;i++){el=this.elements[i];for(var j=0,rl;j<el.registeredListeners.length;j++){rl=el.registeredListeners[j];if(rl.listener==listener){this._removeEventListener(el.node,rl.DOMHandler);el.registeredListeners.splice(j,1);break;}}}
for(var i=0,listener;i<this.listeners.length;i++){if(listener==this.listeners[i]){this.listeners.splice(i,1);break;}}};DOMEventSource.prototype.fire=function(element){if(undefined==element){for(var i=0;i<this.elements.length;i++){this._dispatchEvent(this.elements[i].node);}}
else{if(!(element instanceof Array)){element=[element];}
for(var i=0;i<element.length;i++){this._dispatchEvent(element[i]);}}};DOMEventSource.prototype._addEventListener=function(el,handler){if(document.attachEvent){DOMEventSource.prototype._addEventListener=function(el,handler){el.attachEvent(this.typeIE,handler);};}
else if(document.addEventListener){DOMEventSource.prototype._addEventListener=function(el,handler){el.addEventListener(this.type,handler,false);};}
this._addEventListener=DOMEventSource.prototype._addEventListener;this._addEventListener(el,handler);};DOMEventSource.prototype._removeEventListener=function(el,handler){if(el.detachEvent){DOMEventSource.prototype._removeEventListener=function(el,handler){el.detachEvent(this.typeIE,handler);};}
else if(el.removeEventListener){DOMEventSource.prototype._removeEventListener=function(el,handler){el.removeEventListener(this.type,handler,false);};}
this._removeEventListener=DOMEventSource.prototype._removeEventListener;this._removeEventListener(el,handler);};DOMEventSource.prototype._dispatchEvent=function(el){if(document.createEventObject){DOMEventSource.prototype._dispatchEvent=function(el){var event=document.createEventObject();event.srcElement=el;event.type=this.type;el.fireEvent(this.typeIE,event);};}
else{DOMEventSource.prototype._dispatchEvent=function(el){var event=document.createEvent(this._getBrowserEventName(this.type));event.initEvent(this.type,true,true);el.dispatchEvent(event);};}
this._dispatchEvent=DOMEventSource.prototype._dispatchEvent;this._dispatchEvent(el);};DOMEventSource.prototype.clearDelayTimeouts=function(){for(var i=0;i<this.delayTimeouts.length;i++){window.clearTimeout(this.delayTimeouts[i]);}
this.delayTimeouts=[];};var DOMEvent=function(nativeEvent){this.nativeEvent=nativeEvent;};DOMEvent.prototype.cancel=function(){if(this.nativeEvent.stopPropagation){this.nativeEvent.stopPropagation();}
else{try{this.nativeEvent.cancelBubble=true;}catch(e){}}
if(this.nativeEvent.preventDefault){this.nativeEvent.preventDefault();}
else{try{this.nativeEvent.returnValue=false;}catch(e){}}
return this.nativeEvent;};DOMEvent.prototype.getTarget=function(){var target=this.nativeEvent.srcElement||this.nativeEvent.target;this.getTarget=function(){return target;}
return this.getTarget();};var EventManager=function(){this.events=[];this.add(window,"unload",this.removeAll,this);};EventManager.prototype.add=function(element,type,handler,context,data,delay){if(arguments.length>1){var inputs={element:element,type:type,handler:handler,context:context,data:data,delay:delay}}
else if(arguments[0]instanceof Object){var inputs=arguments[0];}
else{var inputs={type:arguments[0]}}
var listener={handler:inputs.handler,context:inputs.context,delay:inputs.delay,data:inputs.data};if(this._isDomEventType(inputs.type)){var e=this._addDOMEvent(inputs.type,listener,inputs.element);}
else{var e=this._addCustomEvent(inputs.type,listener);}
this.events.push(e);return e;};EventManager.prototype._isDomEventType=function(type){if(null==DOMEventSource.prototype._getBrowserEventName.apply({type:type})){return false;}
return true;};EventManager.prototype._addDOMEvent=function(type,listener,element){var e=new DOMEventSource(type);if(listener.handler){e.addListener(listener);}
if(element){e.addElement(element);}
return e;};EventManager.prototype._addCustomEvent=function(type,listener){var e=new EventSource(type);if(listener.handler){e.addListener(listener);}
return e;};EventManager.prototype.remove=function(e,listener){if(e instanceof EventSource){if(undefined==listener){e.removeAll();}else{e.removeListener(listener);}
for(var i=0;i<this.events.length;i++){if(e==this.events[i]){this.events.splice(i,1);break;}}}
else if(e.nodeName||e instanceof Array){for(var i=0,event;i<this.events.length;i++){event=this.events[i];if(event instanceof DOMEventSource){event.removeElement(e);}}}};EventManager.prototype.removeAll=function(){for(var i=0;i<this.events.length;i++){this.events[i].removeAll();}
this.events=[];};EventManager.prototype.cancel=function(e){if(e instanceof DOMEvent){e.cancel();}
else if(e.srcElement){DOMEvent.prototype.cancel.apply({nativeEvent:e});}};var Events=new EventManager();

WSDOM=new function(){var loadedClasses={};var usingReferences=[];this.defineClass=function(className,superClass,constructor){loadedClasses[className]=constructor;if(null!=superClass){loadedClasses[className].Extend(superClass);}
loadedClasses[className].prototype.getClassName=function(){return className;};loadedClasses[className].getClassName=loadedClasses[className].prototype.getClassName;return loadedClasses[className];};this.loadClass=function(className){var classDef=parseClassName(className);var o=this.getClass(classDef.className);if(o){this[o.getClassName()]=o;this[o.getClassName()].prototype.namespace=classDef.namespace;this[o.getClassName()].prototype.version=classDef.version;};};this.loadSingleton=function(className){var classDef=parseClassName(className);var o=this.getClass(classDef.className);if(o){this[o.getClassName()]=new o();this[o.getClassName()].namespace=classDef.namespace;this[o.getClassName()].version=classDef.version;};};this.getClass=function(className){return loadedClasses[className];};this.getLoadedClasses=function(){return loadedClasses;};this.using=function(originatingClassName,className){usingReferences.push({originatingClassName:originatingClassName,className:className});}
this._processUsingReferences=function(){var usingErrors=false;for(var x=0;x<usingReferences.length;x++){var originatingClassName=usingReferences[x].originatingClassName;var className=usingReferences[x].className;var classDef=parseClassName(className);if(!this[classDef.className]){if(this.Console){this.Console.warn("Missing class definition for "+className+".  Called from "+originatingClassName+".");};usingErrors=true;}else if(this[classDef.className].version!=classDef.version&&(!this[classDef.className].prototype||this[classDef.className].prototype.version!=classDef.version)){if(this.Console){this.Console.warn("Version incompatibility for loaded class, "+classDef.className+"."+this[classDef.className].version+", versus requested class, version "+classDef.version+".  Called from "+originatingClassName+".");};usingErrors=false;};};if(!usingErrors&&this.Console){this.Console.log("WSDOM Framework loaded successfully.");};};this._onload=function(){var element=window;var eventType="load";var fnHandler=this._processUsingReferences.Context(this);if(element.addEventListener){element.addEventListener(eventType,fnHandler,false);}
else if(element.attachEvent){element.attachEvent("on"+eventType,fnHandler);};};this._onload();function parseClassName(className){var tokens=className.split('.');var classDef={namespace:tokens[0],className:tokens[1],version:tokens[2]};return classDef;};this.identity=function(x){return x};}();WSDOM.using("WSDOM.Element.3","WSDOM.Events.2");WSDOM.defineClass("Element",null,Element_class);WSDOM.loadSingleton("WSDOM.Element.3");WSDOM.defineClass("Events",null,EventManager);WSDOM.loadSingleton("WSDOM.Events.3");

/* Fader.1.js */
function Fader(){};Fader.prototype.INTERVAL=3;Fader.prototype.FRAME_TIME=5;Fader.prototype.MAX_ANIMATION_TIME=1000;Fader.prototype.START_OPACITY=0;Fader.prototype.FINISH_OPACITY=100;Fader.prototype.fadeIn=function(el){return this.initFadeTimeouts(el,function(value,stop){return Math.ceil(this.getTweenIn(value,stop))},this.START_OPACITY,this.FINISH_OPACITY);};Fader.prototype.fadeOut=function(el,removeFromDOM){return this.initFadeTimeouts(el,function(value,stop){return Math.floor(this.getTweenOut(value,stop))},this.FINISH_OPACITY,this.START_OPACITY,removeFromDOM);};Fader.prototype.initFadeTimeouts=function(el,fnTween,startOpacity,finishOpacity,removeFromDOM){this.clearFadeTimeouts(el);var finishTime=new Date().getTime()+this.MAX_ANIMATION_TIME;var theFader=this;var fadeComplete=new EventSource("fadeComplete");var getFadeFunctionClosure=function(opacity){return function(){theFader.fade(opacity,el,finishOpacity,finishTime,removeFromDOM,fadeComplete);};};el._faderOpacity=el._faderOpacity||0;var startFade=Math.floor(Math.min(el._faderOpacity,finishOpacity));var stopFade=Math.ceil(Math.max(el._faderOpacity,finishOpacity));for(var i=startFade,delay,opacity;i<=stopFade;i+=this.INTERVAL){delay=(i-startFade)*this.FRAME_TIME;opacity=fnTween.call(this,i,stopFade);el._faderTimeouts.push(setTimeout(getFadeFunctionClosure(opacity),delay));}
return fadeComplete;};Fader.prototype.fade=function(opacity,el,finishOpacity,finishTime,removeFromDOM,fadeComplete){var now=new Date().getTime();if(now>finishTime){opacity=finishOpacity;}
WSDOM.Element.setOpacity(el,opacity);el._faderOpacity=opacity;if(opacity==finishOpacity){this.clearFadeTimeouts(el);if(removeFromDOM){this.clearFadeProperties(el);WSDOM.Element.remove(el);}
fadeComplete.fire(el);}};Fader.prototype.clearFadeProperties=function(el){try{delete el._faderOpacity;delete el._faderTimeouts;}
catch(e){}};Fader.prototype.getTweenIn=function(value,stop){return(1-Math.cos((value/stop)*Math.PI))/2*stop;};Fader.prototype.getTweenOut=function(value,stop){return stop-this.getTweenIn(value,stop);};Fader.prototype.clearFadeTimeouts=function(el){if(el._faderTimeouts){for(var i=0;i<el._faderTimeouts.length;i++){clearTimeout(el._faderTimeouts[i]);}}
el._faderTimeouts=[];};

//wch.js
var WCH_Constructor=function(){if(!(document.all&&document.getElementById&&!window.opera&&navigator.userAgent.toLowerCase().indexOf("mac")==-1)){this.Apply=function(){};this.Discard=function(){};return;}
var _bIE55=false;var _bIE6=false;var _oRule=null;var _bSetup=true;var _oSelf=this;this.Apply=function(vLayer,vContainer,bResize){if(_bSetup)_Setup();if(_bIE55&&(oIframe=_Hider(vLayer,vContainer,bResize))){oIframe.style.visibility="visible";}else if(_oRule!=null){_oRule.style.visibility="hidden";}};this.Discard=function(vLayer,vContainer){if(_bIE55&&(oIframe=_Hider(vLayer,vContainer,false))){oIframe.style.visibility="hidden";}else if(_oRule!=null){_oRule.style.visibility="visible";}};function _Hider(vLayer,vContainer,bResize){var oLayer=_GetObj(vLayer);var oContainer=((oTmp=_GetObj(vContainer))?oTmp:document.getElementsByTagName("body")[0]);if(!oLayer||!oContainer)return;var oIframe=document.getElementById("WCHhider"+oLayer.id);if(!oIframe){var sFilter=(_bIE6)?"filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);":"";var zIndex=oLayer.style.zIndex;if(zIndex=="")zIndex=oLayer.currentStyle.zIndex;zIndex=parseInt(zIndex);if(isNaN(zIndex))return null;if(zIndex<2)return null;zIndex--;var sHiderID="WCHhider"+oLayer.id;oContainer.insertAdjacentHTML("afterBegin",'<iframe class="WCHiframe" src="javascript:false;" id="'+sHiderID+'" scroll="no" frameborder="0" style="position:absolute;visibility:hidden;'+sFilter+'border:0;top:0;left;0;width:0;height:0;background-color:#ccc;z-index:'+zIndex+';"></iframe>');oIframe=document.getElementById(sHiderID);_SetPos(oIframe,oLayer);}else if(bResize){_SetPos(oIframe,oLayer);}
return oIframe;};function _SetPos(oIframe,oLayer){oIframe.style.width=oLayer.offsetWidth+"px";oIframe.style.height=oLayer.offsetHeight+"px";oIframe.style.left=oLayer.offsetLeft+"px";oIframe.style.top=oLayer.offsetTop+"px";};function _GetObj(vObj){var oObj=null;switch(typeof(vObj)){case"object":oObj=vObj;break;case"string":oObj=document.getElementById(vObj);break;}
return oObj;};function _Setup(){_bIE55=(typeof(document.body.contentEditable)!="undefined");_bIE6=(typeof(document.compatMode)!="undefined");if(!_bIE55){if(document.styleSheets.length==0)
document.createStyleSheet();var oSheet=document.styleSheets[0];oSheet.addRule(".WCHhider","visibility:visible");_oRule=oSheet.rules(oSheet.rules.length-1);}
_bSetup=false;};};var WCH=new WCH_Constructor();

// MouseHover.js
function MouseHover(){this.eventManager=new EventManager();this.fader=new Fader();};MouseHover.prototype.CSS_MOUSE_HOVER="mouseHover";MouseHover.prototype.CSS_CONTENT="mouseHoverContent";MouseHover.prototype.CSS_HIDDEN="wsodHidden";MouseHover.prototype.SHOW_EVENT="mouseHoverShow";MouseHover.prototype.HIDE_DELAY=150;MouseHover.prototype.MARGIN=10;MouseHover.prototype.addTarget=function(el){this.getMouseOverEvent().addElement(el);};MouseHover.prototype.removeTarget=function(el){this.getMouseOverEvent().removeElement(el);};MouseHover.prototype.attachStopEvents=function(el){var e=this.getMouseOutEvent();e.addElement([el,document]);};MouseHover.prototype.attachMoveEvents=function(el){var e=this.getMouseMoveEvent();e.addElement([document]);};MouseHover.prototype.getMouseOverEvent=function(){var e=this.eventManager.add(null,"mouseover",this.show,this);this.getMouseOverEvent=function(){return e;};return this.getMouseOverEvent();};MouseHover.prototype.getMouseOutEvent=function(){var e=this.eventManager.add(null,"mouseout",this.hide,this,null,this.HIDE_DELAY);this.getMouseOutEvent=function(){return e;};return this.getMouseOutEvent();};MouseHover.prototype.getMouseMoveEvent=function(){var e=this.eventManager.add(null,"mousemove",this.move,this);this.getMouseMoveEvent=function(){return e;};return this.getMouseMoveEvent();};MouseHover.prototype.getShowEvent=function(){var e=this.eventManager.add(this.SHOW_EVENT);this.getShowEvent=function(){return e;};return this.getShowEvent();};MouseHover.prototype.getParent=function(){var el=WSDOM.Element.get("wsod");this.getParent=function(){return el;};return this.getParent();};MouseHover.prototype.setParent=function(el){this.getParent=function(){return el;};};MouseHover.prototype.getContainer=function(){var el=WSDOM.Element.create("div",{"class":this.CSS_MOUSE_HOVER},[WSDOM.Element.create("iframe",{src:"javascript:false;",frameborder:0}),WSDOM.Element.create("div",{"class":this.CSS_CONTENT})]);WSDOM.Element.addClass(el,this.CSS_HIDDEN);WSDOM.Element.addChild(this.getParent(),el);this.getContainer=function(){return el;};return this.getContainer();};MouseHover.prototype.getIframeShim=function(){return this.getContainer().childNodes[0];};MouseHover.prototype.getContent=function(){return this.getContainer().childNodes[1];};MouseHover.prototype.clearContent=function(){WSDOM.Element.removeChildNodes(this.getContent());};MouseHover.prototype.setCurrentTarget=function(el){this.getCurrentTarget=function(){return el;};};MouseHover.prototype.getCurrentTarget=function(){return null;};MouseHover.prototype.show=function(e,el){this.getMouseOutEvent().clearDelayTimeouts();if(el==this.getCurrentTarget()){return;}
this.hide();this.setCurrentTarget(el);this.getShowEvent().fire(el);var container=this.getContainer();WSDOM.Element.setStyle("visibility: hidden;");WSDOM.Element.removeClass(container,this.CSS_HIDDEN);this.parentOffset=WSDOM.Element.getXY(container.offsetParent);this.viewport=WSDOM.Element.getViewport();this.size=WSDOM.Element.getSize(container);WSDOM.Element.setSize(this.getIframeShim(),this.size.width,this.size.height);this.move(e);WSDOM.Element.setStyle("visibility: visible;");this.fader.fadeIn(this.getContainer());this.attachMoveEvents();this.attachStopEvents(el);};MouseHover.prototype.move=function(e){if(e.nativeEvent.pageY==e.nativeEvent.clientY){var x=e.nativeEvent.clientX-this.parentOffset.x+this.MARGIN;var y=e.nativeEvent.clientY-this.parentOffset.y+this.MARGIN;}
else{var x=e.nativeEvent.clientX-this.parentOffset.x+this.viewport.left+this.MARGIN;var y=e.nativeEvent.clientY-this.parentOffset.y+this.viewport.top+this.MARGIN;}
var right=x+this.size.width;var bottom=y+this.size.height;if(right>this.viewport.right-this.MARGIN){x-=(right-this.viewport.right+this.MARGIN);}
if(bottom>this.viewport.bottom-this.MARGIN){y-=(bottom-this.viewport.bottom+this.MARGIN);}
WSDOM.Element.setXY(this.getContainer(),x,y);};MouseHover.prototype.hide=function(e,el){this.fader.fadeOut(this.getContainer()).addListener(function(){this.fader.clearFadeProperties(this.getContainer());WSDOM.Element.addClass(this.getContainer(),this.CSS_HIDDEN);},this);this.setCurrentTarget(null);this.getMouseMoveEvent().removeAllElements();this.getMouseOutEvent().removeAllElements();};

// juice this when you have a moment
var CrossDomainRequestor = function() {
	this.globalContext = "window";
	this.requests = [];
};

CrossDomainRequestor.prototype.setGlobalContext = function(value) {
	this.globalContext = value;
};

CrossDomainRequestor.prototype.buildRequestURL = function(params) {
	var url = params.url + "?";
	
	if (params.contentType) {
		params.data["..contentType.."] = params.contentType;
	}
	
	params.data.context = this.globalContext;
	
	for (var i in params.data) {
		url += i + "=" + encodeURIComponent(params.data[i]) + "&";
	}
	
	return url;
};

CrossDomainRequestor.prototype.load = function(params) {
	var elScript = document.createElement("script");
	elScript.src = this.buildRequestURL(params);
	document.getElementsByTagName("head")[0].appendChild(elScript);
	this.requests.push(elScript);
};

CrossDomainRequestor.prototype.abortRequests = function() {
	for (var i=0; i<this.requests.length; i++) {
		//try {
			this.requests[i].src = "javascript:false;"
			WSDOM.Element.remove(this.requests[i]);
		//} catch (e) {}
	}
	this.requests = [];
};// Yep, it slices, dices and serialices!
Serializer = function () {

	this._nameExclusions = {};
	this._typeExclusions = {};
	// client-side will encode by default unless you set this to false;
	this._encode = true;
	this._strictJson = true;
	this._safeDeserialize = false;

	this._sBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

};

// toss the object into an JS string
Serializer.prototype.serialize = function (o) {

	this._data = [];
	this._serializeNode([o], o, 0);
	this._data = this._data.join("");
	this._data = this._data.replace(/,}/g, "}");
	this._data = this._data.replace(/,]/g, "]");
	this._data = this._data.substr(0, this._data.length-1);

	if (this.allowEncoding()) {

		this._data = this.base64encode(this._data);

	}

	return this._data;

}

// exclude certain named elements from serialization
Serializer.prototype.addNameExclusion = function () {

	var l = arguments.length;

	for (var i=0; i<l; i++) {

		this._nameExclusions[arguments[i]] = true;

	}

}

// remove exclusion of named elements
Serializer.prototype.removeNameExclusion = function () {

	var l = arguments.length;

	for (var i=0; i<l; i++) {

		this._nameExclusions[arguments[i]] = false;

	}

}

// exclude certain data types from serialization
Serializer.prototype.addTypeExclusion = function () {

	var l = arguments.length;

	for (var i=0; i<l; i++) {

		this._typeExclusions[arguments[i].toLowerCase()] = true;

	}

}

// remove exclusion of data types
Serializer.prototype.removeTypeExclusion = function () {

	var l = arguments.length;

	for (var i=0; i<l; i++) {

		this._typeExclusions[arguments[i].toLowerCase()] = false;

	}

}

// getter/setter specifies whether to follow strict Json serialization
Serializer.prototype.requireStrictJson = function (value) {

	if (typeof(value) != "undefined") {

		this._strictJson = value;

	}

	return this._strictJson;

}

// getter/setter specifies whether we need to check for wellformedness before deserializing
Serializer.prototype.requireSafeDeserialize = function (value) {

	if (typeof(value) != "undefined") {

		this._safeDeserialize = value;

	}

	return this._safeDeserialize;

}

// getter/setter specifies if results should be encoded
Serializer.prototype.allowEncoding = function (value) {

	if (typeof(value) != "undefined") {

		this._encode = value;

	}

	return this._encode;

}

Serializer.prototype._unicodeEscape = function (str) {
	var dec = str.charCodeAt(0);
	var hexStr = "\\u";
	var hexVals = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' ];

	var hexPlace;

	// Loop unrolled for speed
	hexPlace = 4096 // 16^3
	hexStr += hexVals[Math.floor(dec / hexPlace)];
	dec = dec % hexPlace;

	hexPlace = 256  // 16^2
	hexStr += hexVals[Math.floor(dec / hexPlace)];
	dec = dec % hexPlace;

	hexPlace = 16   // 16^1
	hexStr += hexVals[Math.floor(dec / hexPlace)];
	dec = dec % hexPlace;

	hexPlace = 1    // 16^0
	hexStr += hexVals[Math.floor(dec / hexPlace)];
	dec = dec % hexPlace;

	return hexStr;
}

// serialize each member of the object
Serializer.prototype._serializeNode = function (o, startObj, depth, parentType) {

	var t, f, d1, d2;

	// loop through all of the members
	for (var i in o) {

		if (o[i] === null) {
			t = "null";
		} else {
			t = typeof(o[i]);
		}


		f = t == "object" ? true : false;
		t = t == "object" && typeof(o[i].length) != "undefined" && o[i].constructor == Array ? "array" : t;

		if (!this._typeExclusions[t] && !this._nameExclusions[i] && !(this._strictJson && t == "function")) {

			switch (t) {

				case "string" :

					d1 = "\"";
					d2 = "\"";
					break;

				case "object" :

					d1 = "{";
					d2 = "}";
					break;

				case "array" :

					d1 = "[";
					d2 = "]";
					break;

				default :

					d1 = "";
					d2 = "";
					break;

			 }

			// build the JS string for this node
			if (isFinite(i) && !(parentType && parentType == "object")) {

				this._data.push(d1);

			} else {

				var n = typeof(i) == "string" ? "\"" + i + "\"" : i;
				this._data.push(n + ":" + d1);

			}

			if (f) {

				if (depth == 0 || o[i] !== startObj) {

					this._serializeNode(o[i],null,null,t);

				}

			} else {

				if (t == "string") {

				// Replace all non-printable, non-ASCII, ", and \ characters with their unicode encoding
					this._data.push(o[i].replace(/[^ -~]|[\"\\]/g, this._unicodeEscape));

				} else if (t == "undefined" || t == "null") {

					this._data.push(this._strictJson ? "null" : t);

				} else {

					this._data.push(o[i]);

				}
			}

			this._data.push(d2 + ",");

		}

	}
}

Serializer.prototype.deserialize = function (o) {

	try {

		if (this.hasEncodingLeader(o)) {

			o = this.base64decode(o);

		}

		// eval("var x = " + o);
		if (this._safeDeserialize) {
			return this.safeDeserialize(o);
		} else {
			return this.unsafeDeserialize(o);
		}

	} catch(e) {

		if (typeof(dbg) == "function") {

			dbg("Serializer.deserialize() error", "", "red");
			dbgObject(e);
			dbg("Serializer Source", o);

		}

		var x = "";

	}

	return x;

}
Serializer.prototype.safeDeserialize = function (o) {
	try {
		var p = new jsonParser();
		p.parse(o);
		eval("var x = " + o);
	} catch(e) {
		var x = null;
	}
	return x;
}
Serializer.prototype.unsafeDeserialize = function (o) {
	try {
		eval("var x = " + o);
	} catch(e) {
		var x = null;
	}
	return x;
}

Serializer.prototype.hasEncodingLeader = function (s) {

	return s.indexOf("B64ENC") == 0 ? true : false;

}

Serializer.prototype.stripLeader = function (s) {

	return s.substr(6, s.length);

}

Serializer.prototype.prependLeader = function (s) {

	return "B64ENC" + s;

}

// ripped from /includes/asplib/Base64
Serializer.prototype.base64decode = function (sIn) {

	var i;
	var iBits;
	var sOut = [];

	if (this.hasEncodingLeader(sIn)) {

		sIn = this.stripLeader(sIn);

	} else {

		return sIn;

	}

	sIn = sIn.replace(/=/g, "");

	for(i = 0; i < sIn.length; i += 4) {

		iBits = (this._sBase64.indexOf(sIn.charAt(i)) << 18) |
			(this._sBase64.indexOf(sIn.charAt(i + 1)) << 12) |
			((this._sBase64.indexOf(sIn.charAt(i + 2)) & 0xff) << 6) |
			(this._sBase64.indexOf(sIn.charAt(i + 3)) & 0xff);

		/* Removing the below += results in a 90% speed improvement on large objects/long strings */
		/*sOut += String.fromCharCode(iBits >> 16 & 0xff);
		sOut += (i > sIn.length - 3) ? "" : String.fromCharCode(iBits >> 8 & 0xff);
		sOut += (i > sIn.length - 4) ? "" : String.fromCharCode(iBits & 0xff);*/

		sOut.push(String.fromCharCode(iBits >> 16 & 0xff));
		sOut.push((i > sIn.length - 3) ? "" : String.fromCharCode(iBits >> 8 & 0xff));
		sOut.push((i > sIn.length - 4) ? "" : String.fromCharCode(iBits & 0xff));

	}

	return sOut.join("");

}

// ripped from /includes/asplib/Base64
Serializer.prototype.base64encode = function (sIn) {

	var i;
	var iBits;
	var sOut = [];

	for(i = 0; i < sIn.length; i += 3) {

		iBits = (sIn.charCodeAt(i) << 16) +
			((sIn.charCodeAt(i + 1) & 0xff) << 8) +
			(sIn.charCodeAt(i + 2) & 0xff);

		/* Removing the below += results in a 90% speed improvement on large objects/long strings */
		/*sOut += this._sBase64.charAt(iBits >> 18 & 0x3f);
		sOut += this._sBase64.charAt(iBits >> 12 & 0x3f);
		sOut += (i > sIn.length - 2) ? "=" : this._sBase64.charAt(iBits >> 6 & 0x3f);
		sOut += (i > sIn.length - 3) ? "=" : this._sBase64.charAt(iBits & 0x3f);*/

		sOut.push(this._sBase64.charAt(iBits >> 18 & 0x3f));
		sOut.push(this._sBase64.charAt(iBits >> 12 & 0x3f));
		sOut.push((i > sIn.length - 2) ? "=" : this._sBase64.charAt(iBits >> 6 & 0x3f));
		sOut.push((i > sIn.length - 3) ? "=" : this._sBase64.charAt(iBits & 0x3f));

	}

	sOut = this.prependLeader(sOut.join(""));

	return sOut;

}


// This is a validating JSON recursive descent parser.  It does not actually
// build a data structure; it merely tests the input string for validity
// according to the JSON grammar.  This greatly simplifies the parser, and also
// allows a few otherwise impossible performance tweaks.  For a description of
// the JSON grammer, see http:// json.org.  For a description of Recursive
// Descent parsers, how they work, and how to write one, see
// http://www.antlr.org/book/byhand.pdf

// The purpose of this parser is to ensure that calling eval() on a serialized
// string will not have any potentially nasty side effects.  This is an
// important safety measure when you don't know who has been serializing the
// strings you are about to deserialize...


function jsonParser () {
	this.lexer = null;
	this.tokens = [ ];
}

jsonParser.prototype.parse = function (str) {
	this.lexer = new jsonLexer(str);

	return this._json();
}

// not really necessary, since JSON seems to be parsable with an LL(1) grammar,
// but defining this now makes it far easier to extend in the future.
jsonParser.prototype.lookAhead = function (k) {
	while (this.tokens.length <= k) {
		this.tokens.push(this.lexer.nextToken());
	}
	return this.tokens[k].type;
}

// Remove a token from the token stream.  Since we are only parsing and not
// actually doing anything with the parsed string, we can merely remove the
// token from the stream here, and not worry about building a syntax tree.
jsonParser.prototype.consume = function (type) {
	if (this.tokens.length == 0) {
		this.tokens.push(this.lexer.nextToken());
	}

	if (this.tokens[0].type == type) {
		this.tokens.shift();
	} else {
		throw { message: 'JSON: invalid token encountered validating string; Expected '+type+', got '+this.tokens[0].type };
	}
}

// A JSON serialized string consists of exactly one value.
jsonParser.prototype._json = function () {
	this._value();
	this.consume('_EOF');
}

// A value can be one of
//   an object
//   an array
//   a string
//   a number
//   true, false, or null
jsonParser.prototype._value = function () {
	switch(this.lookAhead(0)) {
		case '_OBJ_OPEN':
			this._object();
			break;
		case '_ARR_OPEN':
			this._array();
			break;
		case '_DIGITS':
		case '_NEG':
			this._number();
			break;
		case '_STRING':
			this.consume('_STRING');
			break;
		case '_TRUE':
			this.consume('_TRUE');
			break;
		case '_FALSE':
			this.consume('_FALSE');
			break;
		case '_NULL':
			this.consume('_NULL');
			break;
	}
}

// An object consists of an open brace, zero or more comma separated
// string/value pairs, and a close brace
jsonParser.prototype._object = function () {
	this.consume('_OBJ_OPEN');
	if (this.lookAhead(0) != '_OBJ_CLOSE') {
		this._member();
	}
	while (this.lookAhead(0) != '_OBJ_CLOSE') {
		this.consume('_SEP');
		this._member();
	}
	this.consume('_OBJ_CLOSE');
}

jsonParser.prototype._member = function () {
	this.consume('_STRING');
	this.consume('_ASSIGN');
	this._value();
}

// An array consists of an open bracket, zero or more comma separated values,
// and a close bracket
jsonParser.prototype._array = function () {
	this.consume('_ARR_OPEN');
	if (this.lookAhead(0) != '_ARR_CLOSE') {
		this._value();
	}
	while (this.lookAhead(0) != '_ARR_CLOSE') {
		this.consume('_SEP');
		this._value();
	}
	this.consume('_ARR_CLOSE');
}

// A number consists of an optional leading '-', any number of digits, followed
// by an optional fractional component and an optional exponential component.
jsonParser.prototype._number = function () {
	if (this.lookAhead(0) == '_NEG') {
		this.consume('_NEG');
	}

	this.consume('_DIGITS');

	if (this.lookAhead(0) == '_DOT') {
		this.consume('_DOT');
		this.consume('_DIGITS');
	}

	if (this.lookAhead(0) == '_EXP') {
		this.consume('_EXP');
		if (this.lookAhead(0) == '_POS') {
			this.consume('_POS');
		} else if (this.lookAhead(0) == '_NEG') {
			this.consume('_NEG');
		}
		this.consume('_DIGITS');
	}
}

function jsonLexer (input) {
	// since we only care about validity and not the actual value, we can
	// easily reduce these multi-character tokens down to one character now
	// resulting in a significant gain in overall efficiency.
	input = input.replace(/"([^"\\]|\\"|\\)*"/g, 'S');
	input = input.replace(/[0-9]+/g, '0');

	this.input = input;
}

// Tokens represented by a single character
jsonLexer.prototype.charTokens = {
								'{': '_OBJ_OPEN',
								'}': '_OBJ_CLOSE',
								'[': '_ARR_OPEN',
								']': '_ARR_CLOSE',
								':': '_ASSIGN',
								',': '_SEP',
								'.': '_DOT',
								'-': '_NEG',
								'+': '_POS',
								'e': '_EXP',
								'E': '_EXP',
								'S': '_STRING',
								'0': '_DIGITS'
							};
// Other Tokens that are scanned for:
// 									 '_TRUE',
// 									 '_FALSE',
// 									 '_NULL',

jsonLexer.prototype.nextToken = function () {
	if (this.input.length == 0) {
		return new jsonToken("_EOF", null);
	}

	var first = this.input.substr(0,1);
	if (this.charTokens[first]) {
		this.input = this.input.substr(1);
		return new jsonToken(this.charTokens[first], first);
	}

	switch (first) {
		case 't':
		case 'T':
			if (this.input.substr(0,4).toLowerCase() == 'true') {
				this.input = this.input.substr(4);
				return new jsonToken('_TRUE', true);
			}
			break;

		case 'f':
		case 'F':
			if (this.input.substr(0,5).toLowerCase() == 'false') {
				this.input = this.input.substr(5);
				return new jsonToken('_FALSE', true);
			}
			break;

		case 'n':
			if (this.input.substr(0,4) == 'null') {
				this.input = this.input.substr(4);
				return new jsonToken('_NULL', true);
			}
			break;
	}

	throw { message: 'JSON: Unexpected character ('+first+') encountered validating string' };
}

// Simple representation of a lexer token
function jsonToken (type, value) {
	this.type = type;
	this.value = value;
}
/*
This file requires Element and Events from jslib

Creates 3 select boxes (month, day, year)
arguments for DateSelect:
	container = what the select boxes should be appended to
	date = could be single date or date array.  Format can be either JS or Alerts style
	labels = could be single label or label array.
	monthType = how the months should be displayed (abbr: 3 letter month; upper: 3 letter month uppercase; num: month number; full: full month name
	yearCount = how many years to display in select box
	separator = include an html element to separate more than one group of select boxes.
	selectedDate = single date or date array.
*/

function DateSelect(args) { this.setDateSelectBoxes(args); }

DateSelect.prototype.setDateSelectBoxes = function(args)
{
	this.container = args.container || document.body;
	var date = args.date || new Date();
	var labels = args.labels || "";
	this.separator = args.separator || "";

	this.monthType = args.monthType || "full";
	this.yearCount = args.yearCount || 3;

	this.labels = this.convertToArray(labels);
	this.dates = this.convertToArray(date);
	this.selectedDates = this.convertToArray(args.selectedDate);

	this.monthSelect = [];
	this.daySelect = [];
	this.yearSelect = [];

	var months, splitDate, dayCount, selectedDate;
	for ( var i=0; i<this.dates.length; i++ )
	{
		if ( i  && this.separator != "" ) {
			Element.create("span", "", this.separator, this.container);
		}

		if (!(this.dates[i] instanceof Date)) {
			this.dates[i] = this.convertDateFormat(this.dates[i]);
		}
		if (this.selectedDates)
		{
			if (!(this.selectedDates[i] instanceof Date))
			{
				this.selectedDates[i] = this.convertDateFormat(this.selectedDates[i]);
			}
		}

		var currentDate = new Date();
		var currentYear = currentDate.getFullYear();

		splitDate = this.splitSelectDates(this.dates[i]);

		if (this.selectedDates instanceof Array) {
			selectedDate = this.splitSelectDates(this.selectedDates[i]);
		} else {
			selectedDate = splitDate;
		}

		months = this.getMonthLabels();

		if ( this.labels[i] ) {
			Element.create("label", "", this.labels[i], this.container);
		}

		var selects = this.buildSelectElements();
		this.monthSelect[i] = selects.month;
		this.daySelect[i] = selects.day;
		this.yearSelect[i] = selects.year;

		this.setMonth(i, months, splitDate, selectedDate);
		this.setDate(i, splitDate, selectedDate);
		this.setYear(i, currentYear, splitDate, selectedDate);
	}
	this.setDatesJS();
}

DateSelect.prototype.buildSelectElements = function()
{
	return {
		month: Element.create("select", { className: "dateSelectMonth" }, "", this.container)
		,day: Element.create("select", { className: "dateSelectDay" }, "", this.container)
		,year: Element.create("select", { className: "dateSelectYear" }, "", this.container)
	};
}


DateSelect.prototype.setMonth = function(i, months, splitDate, selectedDate) {
	for ( var x=0; x<months.length; x++ ) {
		var opt = Element.create("option", { value:x+1 }, months[x][this.monthType], this.monthSelect[i]);
		if ( x == selectedDate.mm ) {
			opt.selected = true;
		}
	}
	Events.add({ element:this.monthSelect[i], type:"change", handler:this.daysInMonthSelectBox, context:this, data:{index:i} });
}


DateSelect.prototype.setDate = function(i, splitDate, selectedDate) {
	dayCount = this.daysInMonth(selectedDate.yy, (selectedDate.mm+1));
	for ( var x=1; x<dayCount+1; x++ ) {
		var opt = Element.create("option", { value:x }, x, this.daySelect[i]);
		if ( x == selectedDate.dd ) {
			opt.selected = true;
		}
	}
	Events.add({ element:this.daySelect[i], type:"change", handler:this.setDatesJS, context:this });
}


DateSelect.prototype.setYear = function(i, currentYear, splitDate, selectedDate) {
	currentYear = splitDate.yy < currentYear ? splitDate.yy : currentYear;

	for ( var x=0; x<this.yearCount; x++ ) {
		var opt = Element.create("option", { value:currentYear }, currentYear, this.yearSelect[i]);
		if ( currentYear == selectedDate.yy ) {
			opt.selected = true;
		}
		currentYear++;
	}
	// this event will check to see if it is leap year
	Events.add({ element:this.yearSelect[i], type:"change", handler:this.daysInMonthSelectBox, context:this, data:{index:i} });
}


DateSelect.prototype.getMonthLabels = function() {
	var months = [
		{ abbr: "Jan", upper: "JAN", num: "01", full: "January" },
		{ abbr: "Feb", upper: "FEB", num: "02", full: "February" },
		{ abbr: "Mar", upper: "MAR", num: "03", full: "March" },
		{ abbr: "Apr", upper: "APR", num: "04", full: "April" },
		{ abbr: "May", upper: "MAY", num: "05", full: "May" },
		{ abbr: "Jun", upper: "JUN", num: "06", full: "June" },
		{ abbr: "Jul", upper: "JUL", num: "07", full: "July" },
		{ abbr: "Aug", upper: "AUG", num: "08", full: "August" },
		{ abbr: "Sep", upper: "SEP", num: "09", full: "September" },
		{ abbr: "Oct", upper: "OCT", num: "10", full: "October" },
		{ abbr: "Nov", upper: "NOV", num: "11", full: "November" },
		{ abbr: "Dec", upper: "DEC", num: "12", full: "December" }
		];

	return months;
}

// gets the number of days in a month and factors in Leap Year
DateSelect.prototype.daysInMonth = function(year, month) {
	var days = new Date(year, month, 0);
	var monthDays = days.getDate();

	return monthDays;
}

DateSelect.prototype.daysInMonthSelectBox = function(e, el, data) {
	var dayIndex = this.daySelect[data.index].value;

	var monthDays = this.daysInMonth(this.yearSelect[data.index].value, this.monthSelect[data.index].value);

	Element.removeChildNodes(this.daySelect[data.index], "");
	for ( var i=1; i<monthDays+1; i++ ) {
		var opt = Element.create("option", { value:i }, i, this.daySelect[data.index]);

		if ( i == dayIndex ) {
			opt.selected = true;
		}
	}

	this.setDatesJS();
}

DateSelect.prototype.convertToArray= function(arr) {
	if (arr !== undefined && !Element.isArray(arr) ) {
		arr = [arr];
	}

	return arr;
}

//converts Alert format dates to JS dates
DateSelect.prototype.convertDateFormat= function(str) {
	str = String(str);

	if ( str.length == 8) {
        var date =  new Date(str.substring(0,4), str.substring(4,6) - 1, str.substring(6,8));
		if ( isNaN(date) ) {
			date = new Date();
		}
		return date;
    } else if ( str.length == 5 || str.indexOf(".") > -1 ) {
		var date = this.msToJsDate(str);
		return date;
    } else if ( str.length == 10 || str.indexOf("-") > -1 ) {
        var date =  new Date(str.substring(0,4), str.substring(5,7) - 1, str.substring(8,10));
		if ( isNaN(date) ) {
			date = new Date();
		}
		return date;
    } else {
        return new Date();
    }
}

DateSelect.prototype.splitSelectDates = function(date, prepend) {
	var yy = date.getFullYear();
	var mm = date.getMonth();
	var dd = date.getDate();

	if ( prepend ) {
		mm = mm+1 < 10 ? "0"+(mm+1) : mm+1;
		dd = dd < 10 ? "0"+dd : dd;
	}

	return { yy:yy, mm:mm, dd:dd };
}

DateSelect.prototype.msToJsDate = function(date) {
	var jsdate =new Date(((date-25569)*86400000));
	var timezone = jsdate.getTimezoneOffset();
	var d = new Date(((date-25569+(timezone/(60*24)))*86400000));

	return d;
}

DateSelect.prototype.setDatesJS = function() {
	var date = [];
	
	var count = (this.dates && this.dates.length) ? this.dates.length : 0;
	for ( var i=0; i<count; i++ ) {
		date.push(new Date(this.yearSelect[i].value, this.monthSelect[i].value-1, this.daySelect[i].value));
	}
	this.selectDate = date;

	if (this.dateChangedCallback) {
		this.dateChangedCallback(date);
	}
}

// returns date or date array as a JS date
DateSelect.prototype.getDatesArray = function() {
	var selectDate = this.selectDate;
	
	return selectDate;
}

// returns date or date array as a JS date
DateSelect.prototype.getDatesJS = function() {
	var jsDates = this.getDatesArray();

	return jsDates.length > 1 ? jsDates: jsDates[0];
}

// returns date or date array in Alerts date format
DateSelect.prototype.getDatesAlerts = function() {
	var alertDates = [];
	var splitDate;
	var dates = this.getDatesArray();

	var count = (dates && dates.length) ? dates.length : 0;
	for ( var i=0; i<count; i++ ) {
		splitDate = this.splitSelectDates(dates[i], true);
		alertDates.push( splitDate.yy + "" + splitDate.mm + "" + splitDate.dd );
	}
	return alertDates.length > 1 ? alertDates : alertDates[0];
}

// returns date or date array in MS date format
DateSelect.prototype.getDatesMS = function() {
	var msDates = [];
	var dates = this.getDatesArray();

	if (!(dates instanceof Array)) {
		dates = [dates];
	}

	var count = (dates && dates.length) ? dates.length : 0;
	for ( var i=0; i<count; i++ ) {
		var timezoneOffset = dates[i].getTimezoneOffset() / (60 * 24);
		var msDateObj = (dates[i].getTime() / 86400000) + (25569 - timezoneOffset);
		msDates.push(msDateObj);
	}
	return msDates.length > 1 ? msDates : msDates[0];
}

// returns date or date array in DM date format
DateSelect.prototype.getDatesDM = function() {
	var dmDates = [];
	var splitDate;
	var dates = this.getDatesArray();

	var count = (dates && dates.length) ? dates.length : 0;
	for ( var i=0; i<count; i++ ) {
		splitDate = this.splitSelectDates(dates[i], true);
		dmDates.push( splitDate.yy + "-" + splitDate.mm + "-" + splitDate.dd );
	}
	return dmDates.length > 1 ? dmDates : dmDates[0];
}

function Popup() {
	
	// defaults
	this.hasCloseLink(true);
	this.hasTitle(true);

    this.log(arguments.callee.caller);
};

Popup.prototype.CSS_MAIN = "popup";
Popup.prototype.CSS_INNER = "popupInner";
Popup.prototype.CSS_HEADER = "popupHeader";
Popup.prototype.CSS_CONTENT = "popupContent";

Popup.prototype.CSS_HIDDEN = "wsodHidden";

Popup.prototype.log = function (caller)
{
    if(caller == Function.Extend){ return; }
    if(!Popup.instances){ Popup.instances = new Array(); }
    Popup.instances.push(this);
}

Popup.prototype.hasCloseLink = function(value) {
	if (undefined !== value) {
		this._hasCloseLink = value;
	}
	return this._hasCloseLink;
};

Popup.prototype.hasTitle = function(value) {
	if (undefined !== value) {
		this._hasTitle = value;
	}
	return this._hasTitle;
};

Popup.prototype.getEventManager = function() {
	var em = new EventManager();
	this.getEventManager = function() {
		return em;
	};
	return this.getEventManager();
};

Popup.prototype.getParent = function() {
	//var parent = Element.get("wsod");
	var parent = Element.get("wsodPop");
	if(!parent){
		parent = Element.create("div", {id:"wsodPop"}, null, document.body);
	}
	this.getParent = function() { return parent; };
	
	return this.getParent();
};

Popup.prototype.setParent = function(parent) {
	this.getParent = function() { return parent; };
};

Popup.prototype.getFrame = function() {
	var closeLink = title = document.createDocumentFragment();
	
	if (this.hasCloseLink()) {
		closeLink = this.getCloseLink();
	}
	
	if (this.hasTitle()) {
		title = this.getTitle();	
	}
	
	var frame = Element.create('div', { "class": this.CSS_MAIN }, [
		Element.create("iframe", { "src": "javascript:false;", "frameborder": 0 }),
		Element.create('div', { "class": this.CSS_INNER }, [
			// dumb header container
			Element.create('div', {"class":this.CSS_HEADER}, [
				title, closeLink
			]), 
			// dumb content container
			Element.create('div', {"class":this.CSS_CONTENT}) 
		])
	], this.getParent());
	
	// need to move all of this to a cssclass
	Element.addClass(frame, this.CSS_HIDDEN); 
	frame.style.visibility = "hidden";
	
	this.getFrame = function() { return frame };
	this.getDragStartEvent().addElement(this.getHeader());
	
	return this.getFrame(); 
};

Popup.prototype.getIframeShim = function() {
	return this.getFrame().childNodes[0];
};

Popup.prototype.getCanvas = function() {
	return this.getFrame().childNodes[1];
};

Popup.prototype.getHeader = function() {
	return this.getCanvas().childNodes[0];
};
Popup.prototype.getContent = function() {
	return this.getCanvas().childNodes[1];
};

Popup.prototype.clearContent = function() {
	Element.removeChildNodes(this.getContent());
};

Popup.prototype.isVisible = function() {
	return !Element.hasClass(this.getFrame(), this.CSS_HIDDEN);
};

Popup.prototype.allowOthers = function(value) {
	if (undefined !== value) {
		this._allowOthers = value;
	}
	else if (undefined === this._allowOthers) {
		this._allowOthers = false; //false by default
	}
	
	return this._allowOthers;
}

Popup.prototype.getCloseEvent = function() {
	var closeEvent = this.getEventManager().add(null, 'click', this.close, this);	
	this.getCloseEvent = function() { return closeEvent };
	return this.getCloseEvent();

};

Popup.prototype.getCloseLink = function () {
	var closeEvent = this.getCloseEvent();
	var closeLink = Element.create('a', { href: 'javascript:void(0)', className: 'closeLink' }, 'close'); 
	
	closeEvent.addElement(closeLink);
	this.getCloseLink = function() {
		
		return closeLink;
	
	};

	return this.getCloseLink();
};

Popup.prototype.getDragStartEvent = function() {
	var dragStartEvent = this.getEventManager().add(null, "mousedown", this.dragStart, this);
	this.getDragStartEvent = function() {
		return dragStartEvent;
	};
	return this.getDragStartEvent();
};

Popup.prototype.getDragStopEvent = function() {
	var dragStopEvent = this.getEventManager().add(null, "mouseup", this.dragStop, this);
	this.getDragStopEvent = function() {
		return dragStopEvent;
	};
	return this.getDragStopEvent();
};

Popup.prototype.getDragStopEvent = function() {
	var dragStopEvent = this.getEventManager().add(null, "mouseup", this.dragStop, this);
	this.getDragStopEvent = function() {
		return dragStopEvent;
	};
	return this.getDragStopEvent();
};

Popup.prototype.getDragEvent = function() {
	var dragEvent = this.getEventManager().add(null, "mousemove", this.drag, this);
	this.getDragEvent = function() {
		return dragEvent;
	};
	return this.getDragEvent();
};

Popup.prototype.getMouseOutWindow = function() {
	var dragEvent = this.getEventManager().add(null, "mouseout", function(e) {
		
		var outTarget = e.nativeEvent.toElement || e.nativeEvent.relatedTarget;
		
		if (!outTarget || "HTML" == outTarget.tagName) {	
			this.getDragStopEvent().fire();
		}
		
	}, this, null, 50);
	
	this.getMouseOutWindow = function() {
		return dragEvent;
	};
	
	return this.getMouseOutWindow();
};

Popup.prototype.getMouseOverWindow = function() {
	var dragEvent = this.getEventManager().add(null, "mouseover", function(e) {
		//console.info("mouseoverwindow", e);
	}, this);
	this.getMouseOverWindow = function() {
		return dragEvent;
	};
	return this.getMouseOverWindow();
};

Popup.prototype.onDragStop = function() {
	var dragStop = this.getEventManager().add("dragStop");
	this.onDragStop = function() {
		return dragStop;
	};
	return this.onDragStop();
};

Popup.prototype.getTitle = function () {
    var title = Element.create('h2');
	this.getTitle = function() { return title };
	return this.getTitle();
};

Popup.prototype.setTitleText = function(text) {
	this.getTitle().innerHTML = text;
};

Popup.prototype.draw = function() {
	if (this.isVisible()) {
		return;
	}
	
	if (!this.allowOthers()) {
	    for( var i = 0; i < Popup.instances.length; i++ )
	    {
	        Popup.instances[i].close();
	    }
	}
	
	var frame = this.getFrame();
	Element.removeClass(frame, this.CSS_HIDDEN);

	this.position();

	return true;
};

Popup.prototype.position = function () {
    var contentWell = Element.getXY(this.getFrame().offsetParent);
	
    var view = Element.getViewport();
	var frame = this.getFrame();

    var frameSize = Element.getSize(frame);

    var frameTop = Math.max(view.top + ( view.height / 2 ) - ( frameSize.height / 2 ), 0) - contentWell.y;
    var frameLeft = Math.max(( view.width / 2 ) - ( frameSize.width / 2 ), 0) - contentWell.x;

    Element.setXY(frame, frameLeft, frameTop);
	this.sizeShim();
	// add a class instead
    frame.style.visibility = 'visible';
};

Popup.prototype.sizeShim = function() {
	var size = Element.getSize(this.getFrame());
	Element.setSize(this.getIframeShim(), size.width, size.height);
};

Popup.prototype.close = function (e, el) {
	if(e){ e.cancel() };

	var frame = this.getFrame();
	
	if (!frame) {
		return;
	}
	
    Element.addClass(frame, this.CSS_HIDDEN);
	frame.style.visibility = "hidden";
};

Popup.prototype.drag = function(e) {
	e.cancel();
	
	this.getMouseOutWindow().clearDelayTimeouts();
	
	var coords = this.getMouseCoords(e);
	
	var x = coords.x - this.dragOffset.x;
	var y = coords.y - this.dragOffset.y;

	Element.setXY(
		this.getFrame(), x, y
	);
};

Popup.prototype.getMouseCoords = function(e) {
	var x, y;
	
	if (e.nativeEvent.pageX || e.nativeEvent.pageY) {
		x = e.nativeEvent.pageX;
		y = e.nativeEvent.pageY;
	}
	else if (e.nativeEvent.clientX || e.nativeEvent.clientY) {
		x = e.nativeEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		y = e.nativeEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	
	return {x: x, y: y};
}

Popup.prototype.dragStart = function(e, el) {
	e.cancel();
	
	this.getDragEvent().addElement(document);
	this.getDragStopEvent().addElement(document);
	
	this.getMouseOutWindow().addElement(document);
	this.getMouseOverWindow().addElement(document);
	
	var frame = this.getFrame();
	var startPos = Element.getXY(frame);
	
	// once we've dragged, position should stop being called
	this.position = function() {
		this.getFrame().style.visibility = 'visible';
	};

	var coords = this.getMouseCoords(e);	
	this.dragOffset = {
		x: coords.x - startPos.x,
		y: coords.y - startPos.y
	};
};

Popup.prototype.dragStop = function(e, el) {
	e.cancel();
	
	this.getDragEvent().removeAllElements();
	this.getDragStopEvent().removeAllElements();
	
	this.getMouseOutWindow().removeAllElements();
	this.getMouseOverWindow().removeAllElements();
	
	var frame = this.getFrame();
	var viewport = Element.getViewport();
	var size = Element.getSize(frame);
	var pos = Element.getXY(frame);
	var padding = 20;
	
	if (pos.x + this.dragOffset.x < viewport.left + padding ) {
		Element.setXY(frame, viewport.left - this.dragOffset.x + padding, null);
	}
	else if (pos.x + this.dragOffset.x > viewport.right - padding) {
		Element.setXY(frame, viewport.right - this.dragOffset.x - padding, null);
	}
	
	if (pos.y + this.dragOffset.y < viewport.top + padding) {
		Element.setXY(frame, null, viewport.top - this.dragOffset.y + padding);
	}
	else if (pos.y + this.dragOffset.y > viewport.bottom) {
		Element.setXY(frame, null, viewport.bottom - this.dragOffset.y - padding);
	}
	
	this.onDragStop().fire(Element.getXY(this.getFrame()));
};
function AddToPortfolioPopup() {
	AddToPortfolioPopup.Super(this);
	this.setDetails();
}

AddToPortfolioPopup.Extend(Popup);

AddToPortfolioPopup.prototype.DEFAULT_URL = '/ft/portfolio/bufferRequest.asp';

AddToPortfolioPopup.prototype.getURL = function() {
	return this.DEFAULT_URL;
};

AddToPortfolioPopup.prototype.setURL = function(URL) {
	this.getURL = function() {
		return URL;
	};
};


AddToPortfolioPopup.prototype.getContainerCashCurrencies = function() {
	return null;
};


AddToPortfolioPopup.prototype.setContainerCashCurrencies = function(arrayOfCurrencies) {
	var deserializedArrayOfCurrencies = this.getSerializer().deserialize(arrayOfCurrencies);

	this.getContainerCashCurrencies = function() {
		return deserializedArrayOfCurrencies;
	};
};


AddToPortfolioPopup.prototype.setDetails = function(symbol, name, assetType) {
	this.symbol		= symbol;
	this.name		= name;
	this.assetType	= assetType;
};

AddToPortfolioPopup.prototype.setRequestor = function(requestor) {
	this.requestor = requestor;
};

AddToPortfolioPopup.prototype.getRequestor = function() {
	if (!this.requestor) {
		this.requestor = new ContentBuffer();
	}

	this.getRequestor = function() {
		return this.requestor;
	}
	return this.getRequestor();
};

AddToPortfolioPopup.prototype.getSerializer = function() {
	var s = new Serializer();
	this.getSerializer = function() {
		return s;
	}
	return this.getSerializer();
};


AddToPortfolioPopup.prototype.close = function() {
	AddToPortfolioPopup.Super(this, 'close');

	if (this.form) {
		// hide the additional fields for new portfolios
		WSDOM.Element.addClass(this.nameAndCurrency, this.CSS_HIDDEN);

		// reset the values
		this.portfolioSelect.selectedIndex			= 0;
		this.containerNameToAdd.value				= '';
		this.containerCashCurrency.selectedIndex	= 0;

		if (this.assetType && 'Index' != this.assetType) {
			// show
			WSDOM.Element.removeClass(this.transactionUserInputs, this.CSS_HIDDEN);

			// reset
			this.transactionType.selectedIndex		= 0;
			this.transactionQuantity.value			= 'Number of shares';
			this.transactionPrice.value				= '';
			this.transactionFees.value				= '';

			this.handleTransactionDateReset();
		}
	}
}


AddToPortfolioPopup.prototype.draw = function(portfolios) {
	if (this.isVisible()) {
		return;
	}

	portfolios = (typeof portfolios == "object") ? portfolios : this.getSerializer().deserialize(portfolios);
	var name = this.name || this.symbol;

	if (!this.form) {
		WSDOM.Element.addClass(this.getFrame(), 'addToPortfolioPopup');
		this.setTitleText('ADD ' + this.symbol + ' TO PORTFOLIO');

		this.form = WSDOM.Element.create('form', { id: 'addToPortfolioForm', className: 'addToPortfolio' }, null, this.getContent());
		this.instructions = WSDOM.Element.create('div', { className: 'instructions' }, 'To add ' + name + ' to your assets, choose a portfolio from the dropdown menu and click the "Add to Portfolio" button.', this.form);
		this.drawPortfolioNameSelectMenu(portfolios);

		if (this.assetType && 'Index' != this.assetType) {
			// Indices can be watched, but not purchased

			this.inputEvents		= [];
			this.formErrors			= [];

			this.transactionDetails	= WSDOM.Element.create('fieldset', { id:'transactionDetailsFieldset' }, null, this.form);

			this.transactionTypes	= [
				{ value:'Buy', text:'Buy (Long)' },
				{ value:'Watch', text:'Watch' }
			];

			if ('Equity' == this.assetType) {
				this.transactionTypes.splice(1, 0, { value:'Sell', text:'Sell (Short)' });
			}
			this.transactionTypeOptions = [];
			for (var i = 0; i < this.transactionTypes.length; i++) {
				this.transactionTypeOptions.push(WSDOM.Element.create('option', { value:this.transactionTypes[i].value }, this.transactionTypes[i].text));
			}

			this.transactionTypeLayout	= WSDOM.Element.create('div', { className:'fleft' }, WSDOM.Element.create('label', { 'for': 'transactionType' }, 'Transaction'), this.transactionDetails);
			this.transactionType		= WSDOM.Element.create('select', { id:'transactionType', name:'transactionType', className:'bottomRow' }, this.transactionTypeOptions, this.transactionTypeLayout);

			Events.add(
				{
					context:	this,
					element:	this.transactionType,
					type:		'change',
					handler:	this.showOrHideTransactionDetails
				}
			);

			this.transactionUserInputs		= WSDOM.Element.create('div', { id:'transactionUserInputs' }, null, this.transactionDetails);


			// ----- Quantity -----
			this.transactionQuantityLayout	= WSDOM.Element.create('div', { className:'fleft' }, WSDOM.Element.create('label', { 'for': 'transactionQuantity' }, 'Quantity'), this.transactionUserInputs);
			this.transactionQuantity		= WSDOM.Element.create('input', { type:'text', id:'transactionQuantity', name:'transactionQuantity', className:'bottomRow', maxlength:'255', value:'Number of shares' }, null, this.transactionQuantityLayout);

			Events.add(
				{
					context:	this,
					element:	this.transactionQuantity,
					type:		'focus',
					handler:	this.handleTransactionQuantityDefaultText
				}
			);

			this.inputEvents.push(
				Events.add(
					{
						element:	this.transactionQuantity,
						context:	this,
						type:		'blur',
						handler:	this.handleTransactionQuantity
					}
				)
			);


			// ----- Price -----

			this.transactionPriceLink		= WSDOM.Element.create('a', { target:'blank', href:'javascript:void(0)' }, 'Use Last');

			this.getQuoteAndCurrency = Events.add(
				{
					context:	this,
					element:	this.transactionPriceLink,
					type:		'click',
					handler:	this.handleTransactionPriceLink
				}
			);

			this.transactionPriceLayout		= WSDOM.Element.create('div', { className:'fleft' }, [ WSDOM.Element.create('label', { 'for': 'transactionPrice' }, 'Price'), WSDOM.Element.create('span', { id:'transactionPriceUseLast' }, this.transactionPriceLink) ] , this.transactionUserInputs);
			this.transactionPrice			= WSDOM.Element.create('input', { type:'text', id:'transactionPrice', name:'transactionPrice', className:'bottomRow', maxlength:'255' }, null, this.transactionPriceLayout);

			this.transactionCurrencyCode	= WSDOM.Element.create('span', { id:'transactionCurrencyCode' }, null, this.transactionPriceLayout);
			this.transactionPriceIsHidden	= true;  // For the initial page load
			this.getQuoteAndCurrency.fire();

			this.inputEvents.push(
				Events.add(
					{
						element:	this.transactionPrice,
						context:	this,
						type:		'blur',
						handler:	this.handleTransactionPrice
					}
				)
			);


			// ----- Fees -----
			this.transactionFeesLayout		= WSDOM.Element.create('div', { className:'fleft' }, WSDOM.Element.create('label', { 'for': 'transactionFees' }, 'Fees'), this.transactionUserInputs);
			this.transactionFees			= WSDOM.Element.create('input', { type:'text', id:'transactionFees', name:'transactionFees', className:'bottomRow', maxlength:'255' }, null, this.transactionFeesLayout);

			this.inputEvents.push(
				Events.add(
					{
						element:	this.transactionFees,
						context:	this,
						type:		'blur',
						handler:	this.handleTransactionFees
					}
				)
			);


			// ----- Date -----
			this.transactionDateOuterLayout	= WSDOM.Element.create('div', { className:'fright' }, WSDOM.Element.create('label', { 'for': 'transactionDate' }, 'Date'), this.transactionUserInputs);
			this.transactionDateInnerLayout	= WSDOM.Element.create('div', { id:'transactionDate', name:'transactionDate', className:'bottomRow' }, null, this.transactionDateOuterLayout);

			this.earliestYear				= 1997;
			this.drawTransactionDateSelect();
		}

		this.formDetailsAndButtons = WSDOM.Element.create('fieldset', { className: 'submitSet' }, null, this.form);

		this.formButtons = WSDOM.Element.create('div', { id: 'addToPortfolioFormButtons' }, null, this.formDetailsAndButtons);
		this.waitMessage = WSDOM.Element.create('div', { "class": "waitMessage " }, null, this.formDetailsAndButtons);

		this.drawAddToPortfolioButton();
	}

	WSDOM.Element.removeClass(this.formButtons, this.CSS_HIDDEN);
	WSDOM.Element.addClass(this.waitMessage, this.CSS_HIDDEN);

	AddToPortfolioPopup.Super(this, 'draw');
}


AddToPortfolioPopup.prototype.drawPortfolioNameSelectMenu = function(portfolioNameArray) {
	var options = [];

	if (portfolioNameArray && portfolioNameArray.length) {
		for (var i = 0; i < portfolioNameArray.length; i++) {
			options.push(WSDOM.Element.create('option', { value: portfolioNameArray[i] }, portfolioNameArray[i], null));
		}
	}

	this.containerFormFields	= WSDOM.Element.create('fieldset', {}, null, this.form);
	this.nameDropDown			= WSDOM.Element.create('div', { className: 'fleft' }, null, this.containerFormFields);

	WSDOM.Element.create('label', { 'for': 'portfolioSelect' }, 'Select', this.nameDropDown);
	this.portfolioSelect = WSDOM.Element.create('select', { id: 'portfolioSelect' }, options, this.nameDropDown);
	Events.add(
		{
			context:	this,
			element:	this.portfolioSelect,
			type:		'change',
			handler:	this.showOrHideNewPortfolioDetails
		}
	);

	this.nameAndCurrency	= WSDOM.Element.create('div', { className: this.CSS_HIDDEN + ' fright' }, null, this.containerFormFields);

	WSDOM.Element.create('label', { 'for':'containerNameToAdd' }, 'Name', this.nameAndCurrency);
	this.containerNameToAdd	= WSDOM.Element.create('input', { type:'text', id:'containerNameToAdd', name:'containerNameToAdd', maxlength:'255' }, null, this.nameAndCurrency);

	var containerCashCurrencies = this.getContainerCashCurrencies();
	if (containerCashCurrencies) {
		WSDOM.Element.create('label', { 'for':'containerCashCurrency' }, 'Currency', this.nameAndCurrency);
		this.containerCashCurrency = WSDOM.Element.create('select', { id:'containerCashCurrency', name:'containerCashCurrency' }, null, this.nameAndCurrency);

		WSDOM.Element.create('option', {}, 'Select a Currency', this.containerCashCurrency);
		for (var i = 0; i < containerCashCurrencies.length; i++) {
			WSDOM.Element.create('option', { value:containerCashCurrencies[i].CurrencyCode }, containerCashCurrencies[i].Name + ' (' + containerCashCurrencies[i].CurrencyCode + ')', this.containerCashCurrency);
		}
	}
}


/**
 * @method drawTransactionDateSelect
 */
AddToPortfolioPopup.prototype.drawTransactionDateSelect = function() {
	var theTransactionManager = this;

	DateSelect.prototype.setMonth = function(i, months, splitDate) {
		for ( var x=0; x<months.length; x++ ) {
			var opt = WSDOM.Element.create("option", { value:x+1 }, months[x][this.monthType], this.monthSelect[i]);
			if ( x == splitDate.mm ) {
				opt.selected = true;
				theTransactionManager.defaultMonthSelectedIndex = this.monthSelect[i].selectedIndex;
			}
		}
		Events.add({ element:this.monthSelect[i], type:"change", handler:this.daysInMonthSelectBox, context:this, data:{index:i} });
	}

	DateSelect.prototype.setDate = function(i, splitDate) {
		dayCount = this.daysInMonth(splitDate.yy, (splitDate.mm+1));
		for ( var x=1; x<dayCount+1; x++ ) {
			var opt = WSDOM.Element.create("option", { value:x }, x, this.daySelect[i]);
			if ( x == splitDate.dd ) {
				opt.selected = true;
				theTransactionManager.defaultDaySelectedIndex = this.daySelect[i].selectedIndex;
			}
		}
		Events.add({ element:this.daySelect[i], type:"change", handler:this.setDatesJS, context:this });
	}


	DateSelect.prototype.setYear = function(i, currentYear, splitDate, selectedDate) {
		var yearForComparison	= currentYear - this.yearCount + 1;
		var selectedYear		= selectedDate.yy;
		currentYear				= (splitDate.yy < yearForComparison) ? splitDate.yy : yearForComparison;

		for (var x = 0; x < this.yearCount; x++) {
			var opt = WSDOM.Element.create('option', {value:currentYear}, currentYear, this.yearSelect[i]);

			if (currentYear == selectedYear) {
				// opt.setAttribute('selected', 'selected');
				opt.selected = true;
				theTransactionManager.defaultYearSelectedIndex = this.yearSelect[i].selectedIndex;
			}

			currentYear++;
		}
		// this event will check to see if it is leap year
		Events.add(
			{
				element:	this.yearSelect[i],
				type:		'change',
				handler:	this.daysInMonthSelectBox,
				context:	this,
				data:		{index:i}
			}
		);
	}

	var today	= new Date();
	var oldDate	= new Date();
		oldDate.setFullYear(this.earliestYear);  // When the site was launched, 1997 was the earliest year available for new transactions

	var yearCount = today.getFullYear() - oldDate.getFullYear() + 1;

	this.dateSelect = new DateSelect(
		{
			container:		this.transactionDateInnerLayout,
			date:			oldDate,
			monthType:		'abbr',
			selectedDate:	today,
			yearCount:		yearCount // 10  // 10, 11, 12...Each year, the drop-down menu will grow by 1 year, for historical editing purposes
		}
	);
	// console.log(this.dateSelect);
}


/**
 * @method handleTransactionDate
 */
AddToPortfolioPopup.prototype.handleTransactionDate = function() {
	this.transactionDateJS = this.dateSelect.getDatesJS();

	if (this.transactionDateJS.getTime() > new Date().getTime()) {
		this.formErrors.push('The date is invalid - it cannot be in the future.');
	}
	else {
		this.transactionDate = this.dateSelect.getDatesMS();
	}

	// console.log('handleTransactionDate: ' + this.transactionDate);
}


/**
 * @method handleTransactionDateReset
 */
AddToPortfolioPopup.prototype.handleTransactionDateReset = function(editDate) {
	this.selectedMonth	= WSDOM.Element.parseSelector('select.dateSelectMonth', this.transactionDateInnerLayout, 'first');
	this.selectedDay	= WSDOM.Element.parseSelector('select.dateSelectDay', this.transactionDateInnerLayout, 'first');
	this.selectedYear	= WSDOM.Element.parseSelector('select.dateSelectYear', this.transactionDateInnerLayout, 'first');

	if (editDate) {
		if (editDate.month) {
			this.selectedMonth.value = editDate.month;
		}
		if (editDate.day) {
			this.selectedDay.value = editDate.day;
		}
		if (editDate.year) {
			this.selectedYear.value = editDate.year;
		}
		this.dateSelect.setDatesJS();
	}
	else {
		this.selectedMonth.selectedIndex	= this.defaultMonthSelectedIndex;
		this.selectedDay.selectedIndex		= this.defaultDaySelectedIndex;
		this.selectedYear.selectedIndex		= this.defaultYearSelectedIndex;
	}
}


/**
 * @method handleTransactionPriceLink
 */
AddToPortfolioPopup.prototype.handleTransactionPriceLink = function(e, el) {
	// var el = e.nativeEvent.target || e.nativeEvent.srcElement;
	el.blur();  // Otherwise, Firefox will put the focus back on the link
	window.focus();  // IE 6 does not support el.blur()

	Events.cancel(e);

	if (this.symbol) {
		var params = {
			action:			'getXref',
			assetType:		this.assetType,
			nameOrSymbol:	this.symbol,
			showLastPrice:	true
		}

		this.bufferRequest(params);
	}
	else {
		alert('This feature is not currently available.');
	}
}


/**
 * @method handleTransactionFees
 */
AddToPortfolioPopup.prototype.handleTransactionFees = function() {
	if (this.transactionFees.value.length) {
		if (this.transactionFees.value.match(/,/) && this.transactionFees.value.match(/\s/)) {
			this.formErrors.push('The fee may not contain a combination of commas and blank spaces.');
		}
		else {
			var input = {
				type:	'fees',
				value:	this.transactionFees.value
			};
			var results = this.validateNumber(input);

			if (!results.isValidNumber) {
				this.formErrors.push('The fee is not valid.');
			}
			else if (!results.isAcceptable) {
				this.formErrors.push('The fee must be between 0 and 999,999,999.');
			}
		}
	}
}


AddToPortfolioPopup.prototype.showOrHideNewPortfolioDetails = function(e, el) {
	if (1 == el.selectedIndex) {
		// show
		WSDOM.Element.removeClass(this.nameAndCurrency, this.CSS_HIDDEN);
	}
	else {
		// hide
		WSDOM.Element.addClass(this.nameAndCurrency, this.CSS_HIDDEN);

		// reset the values
		this.containerNameToAdd.value				= '';
		this.containerCashCurrency.selectedIndex	= 0;
	}
}


AddToPortfolioPopup.prototype.showOrHideTransactionDetails = function(e, el) {
	if ('Watch' == el.value) {
		// hide
		WSDOM.Element.addClass(this.transactionUserInputs, this.CSS_HIDDEN);
	}
	else {
		// show
		WSDOM.Element.removeClass(this.transactionUserInputs, this.CSS_HIDDEN);
	}
}


/**
 * @method handleTransactionQuantityDefaultText
 */
AddToPortfolioPopup.prototype.handleTransactionQuantityDefaultText = function() {
	if (this.transactionQuantity.value.length) {
		if (this.transactionQuantity.value.match(/Number of shares/i)) {
			this.transactionQuantity.value = '';
		}
	}
}


/**
 * @method handleTransactionQuantity
 */
AddToPortfolioPopup.prototype.handleTransactionQuantity = function() {
	if ('Watch' != this.transactionType.value) {
		if (!this.transactionQuantity.value.length) {
			this.formErrors.push('The quantity is missing.');
		}
		else if (this.transactionQuantity.value.length) {
			if (this.transactionQuantity.value.match(/,/) && this.transactionQuantity.value.match(/\s/)) {
				this.formErrors.push('The quantity may not contain a combination of commas and blank spaces.');
			}
			else {
				var input = {
					value: this.transactionQuantity.value
				};
				var results = this.validateNumber(input);
	
				if (!results.isValidNumber) {
					this.formErrors.push('The quantity is not valid.');
				}
				else if (!results.isAcceptable) {
					this.formErrors.push('The quantity must be between 0 and 999,999,999.');
				}
			}
		}
	}
}


/**
 * @method handleTransactionPrice
 */
AddToPortfolioPopup.prototype.handleTransactionPrice = function(e) {
	if ('Watch' != this.transactionType.value) {
		if ('blur' == e.nativeEvent.type) {
			if (!this.transactionPrice.value.length) {
				this.formErrors.push('The price is missing.');
			}
			else if (this.transactionPrice.value.length) {
				if (this.transactionPrice.value.match(/,/) && this.transactionPrice.value.match(/\s/)) {
					this.formErrors.push('The price may not contain a combination of commas and blank spaces.');
				}
				else {
					var input = {
						value: this.transactionPrice.value
					};
					var results = this.validateNumber(input);
	
					if (!results.isValidNumber) {
						this.formErrors.push('The price is not valid.');
					}
					else if (!results.isAcceptable) {
						this.formErrors.push('The price must be between 0 and 999,999,999.');
					}
				}
			}
		}
	}
}


/**
 * @method validateNumber
 */
AddToPortfolioPopup.prototype.validateNumber = function(input) {
	var results = {
		value:			this.trim(input.value),
		isAcceptable:	false,
		isValidNumber:	false
	}

	if (results.value.length) {
		var regEx = /^(\d[\d]{0,2}(([,][\d]{3})*|([\s][\d]{3})*|[\d]*)?)?([.][\d]{1,4})?$/;
		results.isValidNumber = regEx.test(input.value);
	}

	results.value = results.value.replace(/,/g, '').replace(/\s/g, '');

	var resultsNumber = parseFloat(results.value);
	// var resultsNumber = Number(results.value);

	if (input.type && input.type.match(/fees/i)) {
		// Zero is an acceptable value for Fees
		if (resultsNumber >= 0 && resultsNumber <= 999999999) {
			results.isAcceptable = true;
		}
	}
	else {
		if (resultsNumber > 0 && resultsNumber <= 999999999) {
			results.isAcceptable = true;
		}
	}

	// recurseObject({ o:results, n:'results', i:false });
	return results;
}


AddToPortfolioPopup.prototype.drawAddToPortfolioButton = function() {
	var outerButton = WSDOM.Element.create('div', { className: 'basicButton' }, null, this.formButtons);
	var button = WSDOM.Element.create('div', {}, 'Add to Portfolio', outerButton);

	Events.add(
		{
			context:	this,
			element:	button,
			type:		'click',
			handler:	this.submitForm
    	}
    );

	this.getCloseEvent().addElement(outerButton);
}


/**
 * @method displayFormErrors
 */
AddToPortfolioPopup.prototype.displayFormErrors = function() {
	alert('Please correct the following items:\n\n- ' + this.formErrors.join('\n- '));
}


AddToPortfolioPopup.prototype.submitForm = function(e) {
	Events.cancel(e);

	// The array named 'this.formErrors' will be rebuilt each time the user submits the form.
	this.formErrors = [];

	for (i in this.inputEvents) {
		if ("function" != typeof this.inputEvents[i]) {
			var parentDiv = WSDOM.Element.getParent(this.inputEvents[i].elements[0].node, 'div');

			if (!WSDOM.Element.hasClass(parentDiv, 'hidden')) {
				this.inputEvents[i].fire();
			}
		}
	}

	var currentTransactionType = (this.transactionType) ? this.transactionType.options[this.transactionType.selectedIndex].value : 'Watch';

	var params = {
		containerType:		'Holdings',
		assetType:			this.assetType,
		nameOrSymbol:		this.symbol,
		transactionType:	currentTransactionType
	};


	// Portfolio details
	if (0 == this.portfolioSelect.selectedIndex) {
		this.formErrors.push('Please choose a portfolio.');
	}
	else if (1 == this.portfolioSelect.selectedIndex) {
		var containerNameObject				= WSDOM.Element.get('containerNameToAdd');
		var containerCashCurrencyObject 	= WSDOM.Element.get('containerCashCurrency');

		// Validate the new portfolio's name
		if (!this.trim(containerNameObject.value).length) {
			this.formErrors.push('Please enter a name for the portfolio.');

			containerNameObject.value		= this.trim(containerNameObject.value);
		}
		else if (containerNameObject.value.match(/^portfolio$|^portfolio...$|^new portfolio$|^all portfolios$|^total$/i)) {
			this.formErrors.push('That is a reserved name.  Please enter a new name for the portfolio.');

			containerNameObject.value		= this.trim(containerNameObject.value);
		}
		else {
			var repeatedName				= false;
			var numberOfPortfolioNames		= this.portfolioSelect.options.length;

			for (var i = 0; i < numberOfPortfolioNames; i++) {
				if (containerNameObject.value == this.portfolioSelect.options[i].value) {
					repeatedName = true;
					this.formErrors.push('That name is already being used.  Please try a different name.');

					containerNameObject.value = this.trim(containerNameObject.value);
					break;
				}
			}

			if (!repeatedName) {
				params.action					= 'addContainer';
				params.actionIfSuccessful		= 'addTransaction';
				params.containerName			= containerNameObject.value;

				this.waitMessage.innerHTML		= 'Creating a new portfolio named ' + containerNameObject.value + '. Please wait.';
			}
		}

		// Validate the new portfolio's currency
		if (0 == containerCashCurrencyObject.selectedIndex) {
			this.formErrors.push('Please select a currency for the portfolio.');
		}
		else {
			params.containerCashCurrency	= containerCashCurrencyObject.value;
			params.containerCurrency		= containerCashCurrencyObject.value;
		}
	}
	else {
		params.action						= 'addTransaction';
		params.containerName				= this.portfolioSelect.options[this.portfolioSelect.selectedIndex].value;

		this.waitMessage.innerHTML			= 'Adding ' + this.symbol + ' to your portfolio. Please wait.';
	}


	// Transaction details
	if ('Watch' != currentTransactionType) {
		var transactionQuantityObject			= WSDOM.Element.get('transactionQuantity');
		var	transactionPriceObject				= WSDOM.Element.get('transactionPrice');
		var	transactionFeesObject				= WSDOM.Element.get('transactionFees');

		params.transactionQuantity				= this.stripCommasAndSpaces(transactionQuantityObject.value);
		params.transactionPrice					= this.stripCommasAndSpaces(transactionPriceObject.value);
		params.transactionPosition				= ('Buy' == currentTransactionType) ? 'Long' : 'Short';

		if (transactionFeesObject.value) {
			params.transactionFeesIfSuccessful	= this.stripCommasAndSpaces(transactionFeesObject.value);
		}

		this.handleTransactionDate();
		params.transactionDate					= this.transactionDate;
	}


	if (this.formErrors.length) {
		this.displayFormErrors();
	}
	else {
		if (params) {
			WSDOM.Element.addClass(this.formButtons, this.CSS_HIDDEN);
			WSDOM.Element.removeClass(this.waitMessage, this.CSS_HIDDEN);

			this.bufferRequest(params);
		}
	}
}


AddToPortfolioPopup.prototype.bufferRequest = function(params) {
	this.getRequestor().abortRequests();  // If the user happens to click several buttons, only load the most recent request

	var serializedParams = this.getSerializer().serialize(params);
	this.getRequestor().load(
		{
			contentType:	'text/javascript',
			context:		this,
			data:			{
				serializedParams: serializedParams
			},
			method:			'post',
			debug:			true,
			url:			this.getURL()
		}
	);
}


AddToPortfolioPopup.prototype.onBufferLoad = function(serializedResults) {
	var deserializedResults = this.getSerializer().deserialize(serializedResults);
	// console.log(deserializedResults);

	switch (deserializedResults.action) {
		case 'addContainer':
			new ConfirmationPopup().draw('Success', 'Your portfolio has been created.', deserializedResults);
			break;

		case 'addTransaction':
		case 'addTransactionFees':
			var addTransactionDetails = 'Your portfolio has been updated.';
		
			if (deserializedResults.newContainerName) {
				// Add the newly created portfolio to the drop-down list of portfolio names
				WSDOM.Element.create('option', { value:deserializedResults.newContainerName }, deserializedResults.newContainerName, this.portfolioSelect);
		
				addTransactionDetails = 'Your portfolio has been created and updated.';
			}

			new ConfirmationPopup().draw('Success', addTransactionDetails, deserializedResults);
			break;

		case 'getQuote':
			if (!this.transactionPriceIsHidden && deserializedResults.showLastPrice && deserializedResults.details) {
				this.transactionPrice.value = deserializedResults.details;
			}

			if (deserializedResults.currencyCode) {
				this.transactionCurrencyCode.innerHTML = 'in: <span class="bold">' + deserializedResults.currencyCode + '</span>';
			}

			this.transactionPriceIsHidden	= false;
			break;
	}

	if ('getQuote' != deserializedResults.action) {
		// hide
		WSDOM.Element.addClass(this.nameAndCurrency, this.CSS_HIDDEN);

		// reset the values
		this.portfolioSelect.selectedIndex			= 0;
		this.containerNameToAdd.value				= '';
		this.containerCashCurrency.selectedIndex	= 0;

		if (this.assetType && 'Index' != this.assetType) {
			// show
			WSDOM.Element.removeClass(this.transactionUserInputs, this.CSS_HIDDEN);

			// reset
			this.transactionType.selectedIndex		= 0;
			this.transactionQuantity.value			= '';
			this.transactionPrice.value				= '';
			this.transactionFees.value				= '';

			this.handleTransactionDateReset();
		}
	}
}


AddToPortfolioPopup.prototype.onBufferError = function(friendlyMessage) {
	new ConfirmationPopup().draw("Error", friendlyMessage);
};


/**
 * @method stripCommasAndSpaces
 */
AddToPortfolioPopup.prototype.stripCommasAndSpaces = function(input) {
	input = this.trim(input);

	if (input.length) {
		input = input.replace(/,/g, '').replace(/\s/g, '');
	}

	return input;
}


AddToPortfolioPopup.prototype.trim = function(string) {
	return string.replace(/^\s+/, '').replace(/\s+$/, '');
}



function ConfirmationPopup () {
	ConfirmationPopup.Super(this);
	this.hasCloseLink(false);
}
ConfirmationPopup.Extend(Popup);

ConfirmationPopup.prototype.CSS_WIDTH = 'width: 250px;';

ConfirmationPopup.prototype.DEFAULT_URL = '/ft/portfolio/performance.asp';

ConfirmationPopup.prototype.HOST = '';

ConfirmationPopup.prototype.getSerializer = function() {
	var s = new Serializer();
	this.getSerializer = function() {
		return s;
	}
	return this.getSerializer();
};

ConfirmationPopup.prototype.draw = function (title, message, deserializedResults) {
	var serializedContainerName = (deserializedResults) ? this.getSerializer().serialize(deserializedResults.containerName) : '';

	this.setTitleText(title);

	WSDOM.Element.setStyle(WSDOM.Element.getParent(this.getContent()), this.CSS_WIDTH);

	var textDiv = WSDOM.Element.create('div', { className: 'messageDiv' }, null, this.getContent());
	WSDOM.Element.create('p', {}, message, textDiv);
	this.drawConfirmButton();
	this.drawGoToPortfolioButton(serializedContainerName);
	ConfirmationPopup.Super(this, 'draw');
}

ConfirmationPopup.prototype.drawConfirmButton = function () {
	var buttonDiv	= WSDOM.Element.create('div', { className: 'buttonDiv' }, null, this.getContent());
	var outerButton	= WSDOM.Element.create('div', { className: 'basicButton' }, null, buttonDiv);
	var button		= WSDOM.Element.create('div', {}, 'Ok', outerButton);

	Events.add(
		{
			context:	this,
			element:	button,
			type:		'click',
			handler:	this.close
		}
	);
}

ConfirmationPopup.prototype.drawGoToPortfolioButton = function (serializedContainerName) {
	var buttonDiv	= WSDOM.Element.create('div', { id:'goToPortfolio', className: 'buttonDiv' }, null, this.getContent());
	var outerButton	= WSDOM.Element.create('div', { className: 'basicButton' }, null, buttonDiv);
	var button		= WSDOM.Element.create('div', {}, 'Go to portfolio', outerButton);

	Events.add(
		{
			context:	this,
			element:	button,
			type:		'click',
			handler:	function() {
				location.href = this.HOST + this.DEFAULT_URL + '?containerName=' + serializedContainerName;
			}
		}
	);
}
