/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var ig_swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
/*

This file contains javascript wrappers for common html elements
which allows for easy element specific manipulation.

It is based on the prototype javascript library

Each element in this library can exist in one of two states.  Either it
represents an object actually in the document, or it represents an object
that doesn't yet exist in the document.  The second case is indended for 
creating an element in memory prior to writing it to the document.




*/

//----------------------------------------------------------------------
// This is the base object that handles the basic operations common
// to all HTML elements
//----------------------------------------------------------------------
function element_base(){
  this.element_name  = null;
  this.element_attrs = null;
  this.my_element = null;
  
  //
  //Connects this element up to an existing select element
  //
  this.bind = function(element){
    this.my_element = document.getElementById(element);
  }
  //
  //Creates an element as a child to the given element
  //
  this.createAt = function(elem){
    var targetElem = this.normalizeElem(elem);
    this.my_element = new Element(this.element_name,this.element_attrs);
    targetElem.my_element.insert(this.my_element);
    this.addContent();    
  }
  //
  //Creates an element after (as a sibling of) the given element
  //
  this.createAfter = function(elem){
    var targetElem = this.normalizeElem(elem);
    this.my_element = new Element(this.element_name,this.element_attrs);
    targetElem.my_element.insert({after:this.my_element});
    this.addContent();    
  }
  //
  //This method should be overridden by subclasses.  After an element is created, this 
  //method is called to fill out any content.  The overriden method should depend on 
  //this.my_element being initialized with the element to initialize
  //
  this.addContent = function(){}
  
  this.setFocus = function(){
    my_element.focus();  
  }
  
  //
  //an element being passed in could be one of several types.
  //it could be an element_base derived type, or it could be
  //the id of an element.  This method takes the various types
  //and converts it to a element_base related object
  //
  this.normalizeElem = function(elem){
    //alert(typeof elem);
    if ( typeof elem == "object") {
       return elem;
    } else {
       //assume it's an id
       var new_elem = new element_base();
       new_elem.bind(elem);
       return new_elem;  
    }
    
  }
  
  this.escapeHTML = function (str) {                                       
    return str.replace(/&/g,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;').replace(/"/g,'&quot;');
  }
  

}
//----------------------------------------------------------------------
// checkbox
//----------------------------------------------------------------------
function Checkbox(){

  this.check = function(){
    this.my_element.checked = true;
  }
  this.uncheck = function(){
    this.my_element.checked = false;
  }
  this.toggle = function(){
    if (this.isChecked()){
       this.uncheck();
    } else {
       this.check();
    }
  }
  this.isChecked = function(){
    return this.my_element.checked;
  }

}
Checkbox.prototype = new element_base;

//----------------------------------------------------------------------
// text element
//----------------------------------------------------------------------
function Text(){
  this.element_name = "input";
  this.element_attrs = {"type":"text"}; 
  this.value = null;  //value can be stored here temporarily until the 
                      //element is actually created
  
  this.getValue = function(){return this.my_element.value;};
  this.setValue = function(v){
    if (this.my_element!=null){
       this.my_element.value = v;
    } else {
       this.value = v;  
    } 
  }
  this.addContent = function(){
      this.setValue(this.value);
  }
}
Text.prototype = new element_base;
//----------------------------------------------------------------------
// Select element
//----------------------------------------------------------------------
function Select(){
  this.element_name = "select";
  var optiongroups
  
  this.addOption = function(text,value){
    //this.my_element.insert("<option value='"+value+"'>"+this.escapeHTML(text)+"</option>");
    this.my_element.options[this.my_element.options.length] = new Option(this.escapeHTML(text), value);

  }
  this.addOptionGroup = function(text){
    this.my_element.insert("<optgroup label='"+text+"'/>");
  }
  this.addOptionToGroup = function(text,value,groupname){
    var optgroups = this.my_element.select("optgroup");
    var found = false;
    for (var i=0; i<optgroups.length; i++){
        if (optgroups[i].label==groupname){
           var opt = document.createElement("option");
           opt.value = value;
           opt.appendChild(document.createTextNode(text));
           optgroups[i].appendChild(opt);
           found = true;
        }
    }
    if (!found) return false;
    return true;
  }
  this.isItemSelected  = function(){
    return (this.my_element.selectedIndex!=-1);
  }
  this.getSelectedText  = function(){
    return  this.my_element.options[this.my_element.selectedIndex].text;  //returns the text that the user sees
  };
  this.getSelectedValue = function(){
    return this.my_element.options[this.my_element.selectedIndex].value; //returns the value attribute
  };
  this.scrollToTop = function(){
    this.my_element.selectedIndex=0;
  }
  this.selectByName  = function(name){
    for (var i=0; i<this.my_element.options.length; i++){
        if (this.my_element.options[i].text == name){
           this.my_element.selectedIndex = i;
        }
    }  
  }
  this.selectByValue  = function(value){
    for (var i=0; i<this.my_element.options.length; i++){
        if (this.my_element.options[i].value == value){
           this.my_element.selectedIndex = i;
        }
    }  
  }
  this.sort = function(){
    var o = new Array();
  
    if (this.my_element.options==null) { return; }
  
    for (var i=0; i<this.my_element.options.length; i++) {
        o[o.length] = new Option( this.my_element.options[i].text, this.my_element.options[i].value, 
        this.my_element.options[i].defaultSelected,        
        this.my_element.options[i].selected) ;
    }
    if (o.length==0) { return; }
    o = o.sort( function(a,b) { 
                  if ((a.text.toLowerCase()+"") < (b.text.toLowerCase()+"")) { return -1; }
                  if ((a.text.toLowerCase()+"") > (b.text.toLowerCase()+"")) { return 1; }
                  return 0;
                  } 
               );
  
    for (var i=0; i<o.length; i++) {
        this.my_element.options[i] = new Option(o[i].text, o[i].value, o[i].defaultSelected, o[i].selected);
    }

  }
  
  
  // Gets the number of selectable items in the list (option groups 
  // are visible in the list but are not selectable and so are 
  // not counted.
  this.count = function(){
    return this.my_element.length;
  }
  this.clear = function(){
    this.my_element.length = 0;
  }
}
Select.prototype = new element_base;

function OptionGroup(){

}



//----------------------------------------------------------------------
// table element
//----------------------------------------------------------------------
function Table(){
  this.element_name = "table";
  this.deleteContents = function(){
    if (this.my_element==null){
       alert("Table.deleteContents is not implemented for the case where the Table object is not bound to an actual table element");  
       return;
    }
    var elementarray = this.my_element.down('tbody').childElements();
    for (var i=0; i<elementarray.length; i++){
        elementarray[i].remove();
    }
  }
}
Table.prototype = new element_base;
//----------------------------------------------------------------------
// div element
//----------------------------------------------------------------------
function Div(){
  this.element_name = "div";
  this.setHTML = function(html){
    this.my_element.innerHTML = html;
  }
  this.appendHTML = function(html){
    this.my_element.innerHTML = this.my_element.innerHTML+html;
  }

}
Div.prototype = new element_base;


//----------------------------------------------------------------------
// RadioButtonGroup
// radio buttons cannot be handled in the same way as other 
// controls because a radio button is not a single control.
//----------------------------------------------------------------------
function RadioButtonGroup(){
  var rblist = null;

  this.bind = function(group_name){
    rblist = document.getElementsByName(group_name);
  }
  this.getSelectedValue = function(){
    for (var i=0; i<rblist.length; i++){
        if (rblist[i].checked){
           return rblist[i].value;
        }
    }    
    return null;
  }
  this.getSelectedText = function(){
    for (var i=0; i<rblist.length; i++){
        if (rblist[i].checked){
           return rblist[i].nextSibling.wholeText;
        }
    }
    return null;
  }
  this.setSelected = function(value){
    alert('Radio.setSelected not implemented');
  }

}
//-------------------------------------------------------------------
//  cookie.js - a client side persistance framework
//              backed by cookies
//
//
//
//
//-------------------------------------------------------------------
//Client-side cookie manipulation
function createCookie(name,value,days)
{
	if (days)
	{
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name)
{
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++)
	{
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name)
{
	createCookie(name,"",-1);
}

//-------------------------------------------------------------------
//Client-side storage
function ClientSideStorage(storageName){
  var storage = null;
  var error_string = "";
  var name = storageName;

  storage = new Object();
  //some javascript libraries add extra properties to objects.  We need to remove
  //them all from the storage object
  for (var i in storage){
      delete storage[i];
  }
  
  try { //You might not be able to trust the values in the cookes so watch for exceptions
      parse(readCookie(name));
  } catch (errstring){
      error_string = errstring;
  }
  //-------------------------------------------------------
  // clear
  // clears the value from a cookie in memory but does
  // not alter the cookie on disk.  To change the cookie
  // on disk you must save after clearing.
  //-------------------------------------------------------
  this.clear = function(){
    storage = new Object();
    //some javascript libraries add extra properties to objects.  We need to remove
    //them all from the storage object
    for (var i in storage){
        delete storage[i];
    }
  }
  //-------------------------------------------------------
  // contains
  //-------------------------------------------------------
  this.contains = function(name){
    if (name in storage){
       return true;
    } else { //it might be an array
       return  name+"*" in storage;
    }
  }
  //-------------------------------------------------------
  // remove
  //-------------------------------------------------------
  this.remove = function(){
    eraseCookie(name);
  }
  //-------------------------------------------------------
  // put
  //-------------------------------------------------------
  this.put = function(name,value){
    storage[name]=value;
    return this;
  }
  //-------------------------------------------------------
  // get
  //-------------------------------------------------------
  this.get = function(name){
    return storage[name];
  }
  //-------------------------------------------------------
  // get default
  //-------------------------------------------------------
  this.getDefault = function(name,def){
    if (storage[name]==undefined){
       return def;
    }
    return storage[name];
  }
  //-------------------------------------------------------
  // putArray
  //
  // We add an asterix to the name of the array so that we
  // know it's an array. JavaScript doesn't provide a
  // reliable way of recognizing arrays
  //-------------------------------------------------------
  this.putArray = function(name,array){
    storage[name+"*"]=array;
    return this;
  }
  //-------------------------------------------------------
  // getArray
  //-------------------------------------------------------
  this.getArray = function(name){
    return storage[name+"*"];
  }

  //-------------------------------------------------------
  // save
  //-------------------------------------------------------
  this.save = function(){
    var result = "";
    for (var i in storage){
        var value = storage[i];
        if (i.indexOf("*")==-1){ //Then save a single value
           //alert("value "+value+" encoded: "+encode(value));
           result = result + "~" + i + "~=" + ig_cookie_encode(value);
        } else { //then save an array of values

           result = result + "~" + i;
           if (value.length==0){
              result = result + "~0"; //special indicator for empty arrays
           } else {
             for (var i=0; i<value.length; i++){
                 result += "~*" + ig_cookie_encode(value[i]);
             }
           }
        }
    }
    createCookie(name,result,1000);
  }
  //tildes become double tildes
  function ig_cookie_encode(mystring){
    var encoded = "";
    var parts = String(mystring).split("~"); //split on tildes
    //rebuild replacing the tildes with 2 tildes
    var delim = "";
    for (var i=0; i<parts.length; i++){
        encoded += delim + parts[i];
        delim = "~~";
    }
    return encoded;
  }
  //double tildes become single tildes
  function ig_cookie_decode(mystring){
    var result = "";
    var parts = String(mystring).split("~~"); //split on double tildes
    var delim = "";
    for (var i=0; i<parts.length; i++){ //reassemble with single tildes
        result += delim + parts[i];
        delim = "~";
    }
    return result;
  }
  
  // String format is a repetition of this pattern "~ name ~= value" that is
  // each name value pair starts with a single tilde.  Names are separated from
  // values by a tilde-equals pair.  Tildes that existed in the value strings
  // are replaced by double tildes.
  //
  // So the algorithm is to first break up the string into pairs acording to the
  // single tildes.  Then break the pairs into names and values using the ~=.
  // Then replace any double tildes in the value with single tildes.
  //
  function parse(string){
    var pairs = breakAtSingleTildes(string);
    for (var i=0; i<pairs.length; i++){
        //first determine if this is an array or a singular value
        //the character following the first tilde makes the determination
        //"=" means single "*" means array
        var index = pairs[i].indexOf("~");
        var name = pairs[i].substr(0,index);
        if (pairs[i].charAt(index+1)=="="){     //handle single value
           var value = pairs[i].substr(index+2);
           storage[name] = ig_cookie_decode(value);

        } else if (pairs[i].charAt(index+1)=="*"){ //handles array
           var valuestring = pairs[i].substr(index+2);
           var values = new Array();
           var array_index=0;
           var value_index=0;
           while ((value_index = valuestring.indexOf("~*",value_index)) > -1){   //values are separated by a "~*" so we need to find one
                 //if (value_index==0 || valuestring.charAt(value_index-1)!="~"){   //not preceaded by a tilde
                 if (value_index==0 || preceededByEvenNumberOfTildes(valuestring,value_index)){   //not preceaded by a tilde
                    //we found a value end
                    var value = valuestring.substr(0,value_index);
                    values[array_index++] = ig_cookie_decode(value);
                    valuestring = valuestring.substr(value_index+2);
                    value_index=0;
                 } else { //its a false seperator.  skip it
                   value_index++;
                 }
           }
           values[array_index++]=ig_cookie_decode(valuestring);
           storage[name]=values;
        } else if (pairs[i].charAt(index+1)=="0"){ //handles empty array
           storage[name]= new Array();
        } else {
          throw "error parsing value.  Expeced ~=, ~* or ~0 to separate name from value";
        }

    }

  }

  function preceededByEvenNumberOfTildes(string, index){
    var count=0;
    var back_count = index-1;
    if (index==0){
       return true;
    }
    while (back_count > -1 && string.charAt(back_count--)=="~"){
          count++;
    }
    return ((count%2)==0);
  }

  // Breaks a string up at single tildes and discards the tildes.
  // returns the pieces as an array
  function breakAtSingleTildes(string){
    var results = new Array();
    var remainder = string;
    var results_index = 0;

    if (string==null){
       return results;
    }

    while (true){

          if (remainder.length==0){ //then we're done
             return results;        //return what we have
          }

          if (remainder.charAt(0)!="~"){
             throw "Badly formed storage string.  missing a tilde separator";
          }
          remainder = remainder.substr(1); //remove the initial tilde

          //find the next tilde not followed by an equals or another tilde
          index = remainder.indexOf("~");
          //This one should be followed by an equals
          var nextchar = remainder.charAt(index+1);
          if (nextchar!= "=" && nextchar!="*" && nextchar!="0"){
             throw "Badly formed storage string.  missing an equals, or asterix value separator"
          }
          while (true){
                //Now we're in the value portion.  Keep searching until
                //you find a single tilde
                index = remainder.indexOf("~",index+1);
                if (index==-1){ //we're at the end of the string and we're done
                   results[results_index++] = remainder;
                   return results;
                }
                //if The index is at the end of the string, we have a problem
                if (index+1 == remainder.length) {
                   throw "Badly formed storage string.  missing an equals value separator";
                }
                //if the tilde is not followed by another tilde
                //we've found the end of this pair
                if (remainder.charAt(index+1)=="~"){
                   index++; //skip the 2nd tilde
                } else if (remainder.charAt(index+1)=="*"){
                   index++; //skip the asterix
                } else {
                   results[results_index++] = remainder.substr(0,index);
                   remainder = remainder.substr(index);
                   break;
                }
          }
    }

  }


  this.test =  function(){

    alert("ig_cookie_decode: "+ig_cookie_decode("~~one~~two~~"));


  }

  return this;
}
function Carousel(car_id,carouselType,ping_fxn){
  var theCarouselID = car_id;
  var theCarousel = document.getElementById(theCarouselID);

  var ping = ping_fxn;
  
  var thePrevButton = null;
  var theNextButton = null;
  var theDisabledPrevButton = null;
  var theDisabledNextButton = null;
  var theNoButton   = null;

  var theLengths = Array();
  var thePosition = 0;
  var theStops = Array();

  var theInterval = 20;
  var theIncrement = 1;


  var theBottom = null;
  var theEndPosition = null;
  var theMaxPosition = null;
  var theWindow = null;
  var theWindowLength = null;
  
  var rotating = false;
  var theTimerId = null;

  var that = this;

  for (var i=0; i<theCarousel.childNodes.length; i++){
      var child = theCarousel.childNodes[i];
      switch (child.className){
             case "portable-guider-carousel-prev-button":
                  thePrevButton = child;
                  break;
             case "portable-guider-carousel-next-button":
                  theNextButton = child;
                  break;
             case "portable-guider-carousel-disabled-prev-button":
                  theDisabledPrevButton = child;
                  break;
             case "portable-guider-carousel-no-button":
                  theNoButton = child;
                  break;
             case "portable-guider-carousel-disabled-next-button":
                  theDisabledNextButton = child;
                  break;
             case "portable-guider-carousel-window":
                  theWindow = child;
                  break;
      }
  }
  //////////////////////////////////////////////////////////////////////////////
  // Public Methods
  //////////////////////////////////////////////////////////////////////////////
  this.calculateCarouselDimensions = function(){
    if (carouselType=="horizontal"){
       theWindowLength = document.getElementById('portable-guider-carousel-window').offsetWidth;
    } else {
       theWindowLength = document.getElementById('portable-guider-carousel-window').offsetHeight;
    }
    var itemsdiv = document.getElementById('portable-guider-answers');
    var lastChild = null;
    var childCount = 0;
    var margin = null;
    for (var i=0; i<itemsdiv.childNodes.length; i++){
        var child = itemsdiv.childNodes[i];
        if (child.className=="portable-guider-answer"){
           childCount++;
           if (carouselType=="horizontal"){
              if (margin==null){
                 margin = child.offsetLeft;
              }
              theStops.push(child.offsetLeft-margin);
              theLengths.push(child.offsetWidth);
              theIncrement += child.offsetWidth;
           } else {
              if (margin==null){
                 margin = child.offsetTop;
              }
              theStops.push(child.offsetTop-margin);
              theLengths.push(child.offsetHeight);
              theIncrement += child.offsetHeight;
           }
           lastChild = child;
        }
    }
    
    theIncrement = Math.floor(theIncrement/childCount/10);
    //theIncrement = 10;

    if (carouselType=="horizontal"){
       theBottom = lastChild.offsetWidth+lastChild.offsetLeft;
    } else {
       theBottom = lastChild.offsetHeight+lastChild.offsetTop;
    }
    //Calculate the max position.  We want it to be aposition such that
    //there is a complete item on top.  So after calculating what the 
    //exact max length is we find item stop just past it.
    theMaxPosition = theBottom-theWindowLength;
    for (var i=0; i<theStops.length; i++){
        if (theStops[i]>=theMaxPosition){
           theMaxPosition=theStops[i];
           break;
        }
    }
    
  }

  
  this.startPrev = function(){
    if (rotating) return;
    if (thePosition==0){return;}
    rotating=true;
//    //Move one slot only
//    for (var i=0; i<stops.length; i++){
//        if (stops[i]==thePosition){
//           theEndPosition = stops[i-1];
//           break;
//        }
//    }

    //move whole screen
    var newTop = thePosition-theWindowLength;
    if (newTop>0){
       for (var i=0; i<theStops.length; i++){
           if (theStops[i]>=newTop){
              theEndPosition = theStops[i];
              break;
           }
       }
    } else {
       theEndPosition=0;
    }
    theTimerID = setInterval(that.continuePrev, theInterval);
    ping_fxn("carouselleft");
  }
  this.continuePrev = function(){
    var slider = document.getElementById('portable-guider-answers');
    thePosition-=theIncrement;
    if (thePosition <= theEndPosition){
       thePosition = theEndPosition;
       clearInterval(theTimerID);
       rotating=false;
       that.updateButtons();
    }
    if (carouselType=="horizontal"){
       slider.style.left = -thePosition+"px";
    } else {
       slider.style.top = -thePosition+"px";
    }  
  }

  this.startNext = function(){
    if (rotating) return;
    if (thePosition >= theMaxPosition){return;}
    rotating=true;
    //calculate the stop position

  //  //Move one slot only
  //  for (var i=0; i<theStops.length; i++){
  //      if (theStops[i]<thePosition){
  //         theEndPosition = theStops[i];
  //         break;
  //      }
  //  }

    //Move full screen
    for (var i=1; i<theStops.length; i++){
        if ((theStops[i]+theLengths[i]-thePosition)>theWindowLength){
           theEndPosition = theStops[i];
           break;
        }
    }
    if (theEndPosition>=theMaxPosition){theEndPosition=theMaxPosition;}

    theTimerID = setInterval(that.continueNext, theInterval);
    ping_fxn("carouselright");

  }
  this.continueNext = function(){
    var slider = document.getElementById('portable-guider-answers');
    thePosition+=theIncrement;
  
    if (thePosition>=theEndPosition){
       thePosition = theEndPosition;
       clearInterval(theTimerID);
       rotating=false;
       that.updateButtons();
    }

    if (carouselType=="horizontal"){
       slider.style.left = -thePosition+"px";
    } else {
       slider.style.top = -thePosition+"px";
    }
  }

  this.updateButtons = function(){
    var enablePrev = true;
    var enableNext = true;
    if (thePosition==0){enablePrev = false;}
    if (thePosition>=theMaxPosition){enableNext = false;}

    if (!enablePrev && !enableNext){
       //then don't even show the buttons
       theDisabledPrevButton.style.display = "none";
       thePrevButton.style.display = "none";
       theDisabledNextButton.style.display = "none";
       theNextButton.style.display = "none";
       theNoButton.style.display = "";
    } else {

      if (enablePrev){
         theDisabledPrevButton.style.display = "none";
         thePrevButton.style.display = "";
         theNoButton.style.display = "none";
      } else {
         thePrevButton.style.display = "none";
         theDisabledPrevButton.style.display = "";
         theNoButton.style.display = "none";
      }

      if (enableNext){
         theDisabledNextButton.style.display = "none";
         theNextButton.style.display = "";
         theNoButton.style.display = "none";
      } else {
         theDisabledNextButton.style.display = "";
         theNextButton.style.display = "none";
         theNoButton.style.display = "none";
      }
    }
  }

  thePrevButton.onclick = this.startPrev;
  theNextButton.onclick = this.startNext;
  that.calculateCarouselDimensions();
  this.updateButtons();

  return this;
}
function getXMLDoc(xmldata){
  try { //Internet Explorer
      xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
      xmlDoc.async="false";
      xmlDoc.loadXML(xmldata);
  }
  catch(e){
      try { //Firefox, Mozilla, Opera, etc.
          parser=new DOMParser();
          xmlDoc=parser.parseFromString(xmldata,"text/xml");
      } 
      catch(e){
          alert(e.message);
          return;
      }
  } 
  return xmlDoc;
}

/*

    This presents simplified interface to XML
    Text is handled differently than sub elements
    Children are just elements and not text nodes
    Text made up of whitespace is ignored

*/

function XMLDoc(){
  
  var xmldoc = null;

  this.setXML = function(xmlstr){
    try { //Internet Explorer
      xmldoc=new ActiveXObject("Microsoft.XMLDOM");
      xmldoc.async="false";
      xmldoc.loadXML(xmlstr);
    }
    catch(e){
      try { //Firefox, Mozilla, Opera, etc.
          parser=new DOMParser();
          xmldoc=parser.parseFromString(xmlstr,"text/xml");
      } 
      catch(e){
          alert(e.message);
          return;
      }
    }
  }

  this.getRoot = function(){
    return new XMLElement(xmldoc.documentElement);
  }

}

function XMLElement(node){
  
  var thisnode = node;
  var childArray = new Array();
  var mytext = "";
  
  //////////////////////////////////////////////////////////////////////////////
  this.getName = function(){
    return thisnode.nodeName;
  }
  //////////////////////////////////////////////////////////////////////////////
  this.hasAttribute = function(attrName){
    return !(thisnode.attributes.getNamedItem(attrName)==null);
  }
  //////////////////////////////////////////////////////////////////////////////
  this.getAttribute = function(attrName){
    var attrs = thisnode.attributes;
    if (!this.hasAttribute(attrName)) return null;
    return attrs.getNamedItem(attrName).nodeValue
  }
  //////////////////////////////////////////////////////////////////////////////
  this.hasChildren = function(){
    return (childArray.length>0);
  }
  //////////////////////////////////////////////////////////////////////////////
  this.getChildren = function(){
    var retarr = new Array();
    for (var i=0; i<childArray.length; i++){
        retarr.push(new XMLElement(childArray[i]));
    }
    return retarr;
  }
  //////////////////////////////////////////////////////////////////////////////
  // Get a list of child elements of a given element name
  //////////////////////////////////////////////////////////////////////////////
  this.getChildrenByName = function(name){
    var retarr = new Array();
    for (var i=0; i<childArray.length; i++){
        if (childArray[i].nodeName == name){
           retarr.push(new XMLElement(childArray[i]));
        }
    }
    return retarr;
  }
  //////////////////////////////////////////////////////////////////////////////
  // Get a child element by name.  This is a convenience method for situations
  // where you are expecting only a single returned element.  It's convenient 
  // because you don't have to dereference an array when you only have a single
  // return value.
  //
  // An exception will be thrown if there are more than one match;
  //
  //////////////////////////////////////////////////////////////////////////////
  this.getChildByName = function(name){
    var arr = this.getChildrenByName(name);
    if (arr.length>1){
       throw { name: "XML Format Error", message: "getChildByName found more than one element for: "+name};
    }
    if (arr.length==0) return null;
    return arr[0];
  }
  //////////////////////////////////////////////////////////////////////////////
  this.hasText = function(){
    return (mytext.length>0);
  }
  //////////////////////////////////////////////////////////////////////////////
  this.getText = function(){
    return mytext;
  }
  
  //parse the node
  for (var i=0; i<thisnode.childNodes.length; i++){
      parseNode(thisnode.childNodes.item(i));
  }
  //trim the text;
  mytext = mytext.replace(/^\s+|\s+$/g,"");

  function parseNode(node){
    switch (node.nodeType){
      case 1:
           childArray.push(node);
           break;
      case 3:
           mytext = mytext+node.nodeValue+" ";
           break;
      default:
           alert("Unknown node type "+node.nodeType+" in XMLElement.parseNode");
    }
  }
  
  
}


/*

  PortableGuiderDraggableElement
  
  This class makes an HTML object draggable.  Pass to it the ID of the div 
  you want to be draggable.  You can pass 2 IDs if you want a div to be draggable
  only when clicked in a certain area, which is the way windows typically behave
  on the desktop.  You would do this say if the div has a title bar and you 
  wanted to allow the div to be dragged only when the title bar is clicked and
  dragged.
  
  
  
*/
function PortableGuiderDraggableElement(clickid,dragid){

  var theClickID = clickid;
  var theDragID = ((typeof dragid == 'undefined')?theClickID:dragid);

  var theClickElem = document.getElementById(theClickID);
  var theDragElem  = document.getElementById(theDragID);
  
  if (theClickElem==null || theDragElem==null) throw {name:"Portable Guider Draggable Element initialization error",message:"Could not find the draggable element or the clickable element"};
  
  var theStartX  = null;
  var theStartY  = null;
  var theOffsetX = null;
  var theOffsetY = null;
  var that = this;


  
  function pgMove(evt){
    if (!evt) evt = window.event;
    theDragElem.style.left = evt.clientX+theOffsetX+"px";
    theDragElem.style.top = evt.clientY+theOffsetY+"px";
   	evt.cancelBubble = true;
	  if (evt.stopPropagation) evt.stopPropagation();
	  return false;
  }
  function pgStopMove(evt){
    if (document.removeEventListener) {
       document.removeEventListener("mousemove",pgMove,false);
       document.removeEventListener("mouseup",pgStopMove,false);
    } else {
       document.detachEvent("onmousemove",pgMove);
       document.detachEvent("onmouseup",pgStopMove);
    }
  }  
  
  theClickElem.onmousedown = function(evt){
    if (!evt) evt = window.event;
  	var target = (evt.target)?evt.target:(evt.srcElement)?evt.srcElement:null;
  	
  	switch (target.tagName.toUpperCase()){
           case 'INPUT':
           case 'SELECT':
           case 'OPTION':
           case 'BUTTON':
           case 'TEXTAREA':
  	            return;
  	}

    theStartX  = evt.clientX;
    theStartY  = evt.clientY;
    theOffsetX = theDragElem.offsetLeft-evt.clientX;
    theOffsetY = theDragElem.offsetTop-evt.clientY;
    
    
    if (document.addEventListener){
       document.addEventListener('mousemove',pgMove,false);
       document.addEventListener('mouseup',pgStopMove,false);
    } else {
       document.attachEvent('onmousemove',pgMove);
       document.attachEvent('onmouseup',pgStopMove);
    } 

    if (evt.preventDefault)evt.preventDefault();
   	evt.cancelBubble = true;
	  if (evt.stopPropagation) evt.stopPropagation();
	  return false;    
  }
}
/*

*/
function Guider(){

  var xmldoc;
  var icon                     = null;
  var logo                     = null;
  var banner                   = null;
  var headerType               = null;
  var introText                = null;
  var companyURL               = null;
  var contactEmail             = null;
  var contactName              = null;
  var cssFile                  = null;
  var contactText              = null;
  var linkLaunch               = null;
  var version                  = null;
  var facebook_friend_enable   = null;
  var facebook_like_enable     = null;
  var facebook_id              = null;
  var facebook_friend_text     = null;
  var twitter_tweet_enable     = null;
  var twitter_follow_enable    = null;
  var twitter_id               = null;
  var twitter_follow_text      = null;
  var twitter_default_text     = null;
  var hasPictures              = false;
  var guiderFormat             = 't'; //assume text
  var analyticsTaging          = false;
  var campaignSource           = null;
  var campaignMedium           = null;
  var campaignTerm             = null;
  var campaignContent          = null;
  var campaignName             = null;
  //This is the array of nodes by node id
  var nodearray = new Object();

  this.setXML = function(xmlstr){    
    xmldoc = new XMLDoc();
    xmldoc.setXML(xmlstr);
    //build map of nodes by id
    var root  = xmldoc.getRoot();
    var noderoot = root.getChildByName("nodes");
    var nodes = noderoot.getChildrenByName("node");
    for (var i=0; i<nodes.length; i++){
        var node = nodes[i];
        var id = node.getAttribute("id");
        nodearray[id]=node;
    }
    //get the guider header stuff if available
    var options = root.getChildByName('portable-options');
    if (options != null){
       var icon_node                     = options.getChildByName('icon');
       var logo_node                     = options.getChildByName('logo');
       var banner_node                   = options.getChildByName('banner');
       var headerType_node               = options.getChildByName('header-type');
       var introText_node                = options.getChildByName('intro-text');
       var companyURL_node               = options.getChildByName('company-url');
       var contactEmail_node             = options.getChildByName('contact-email');
       var contactName_node              = options.getChildByName('contact-name');
       var contactText_node              = options.getChildByName('contact-text');
       var cssFile_node                  = options.getChildByName('css-file');
       var guiderFormat_node             = options.getChildByName('guider-type');
       var linkLaunch_node               = options.getChildByName('links-new-window');
       var facebook_friend_enable_node   = options.getChildByName('facebook-friend-enable');
       var facebook_like_enable_node     = options.getChildByName('facebook-like-enable');
       var facebook_id_node              = options.getChildByName('facebook-id');
       var facebook_url_node             = options.getChildByName('facebook-like-URL');
       var facebook_friend_text_node     = options.getChildByName('facebook-friend-text');
       var twitter_tweet_enable_node     = options.getChildByName('twitter-tweet-enable');
       var twitter_follow_enable_node    = options.getChildByName('twitter-follow-enable');
       var twitter_id_node               = options.getChildByName('twitter-id');
       var twitter_default_text_node     = options.getChildByName('twitter-default-text');
       var twitter_follow_text_node      = options.getChildByName('twitter-follow-text');
       var analyticsTagging_node         = options.getChildByName('analytics-tagging');       
       var campaignSource_node           = options.getChildByName('campaign-source');       
       var campaignMedium_node           = options.getChildByName('campaign-medium');       
       var campaignTerm_node             = options.getChildByName('campaign-term');       
       var campaignName_node             = options.getChildByName('campaign-name');       
       var campaignContent_node          = options.getChildByName('campaign-content');       
               
       if (icon_node!=null)                     icon                     = icon_node.getChildByName('url').getText();
       if (logo_node!=null)                     logo                     = logo_node.getChildByName('url').getText();
       if (banner_node!=null)                   banner                   = banner_node.getChildByName('url').getText();
       if (headerType_node!=null)               headerType               = headerType_node.getText();
       if (introText_node!=null)                introText                = introText_node.getText();
       if (companyURL_node!=null)               companyURL               = companyURL_node.getText();
       if (contactEmail_node!=null)             contactEmail             = contactEmail_node.getText();
       if (contactName_node!=null)              contactName              = contactName_node.getText();
       if (contactText_node!=null)              contactText              = contactText_node.getText();
       if (cssFile_node!=null)                  cssFile                  = cssFile_node.getText();
       if (guiderFormat_node!=null)             guiderFormat             = guiderFormat_node.getText();
       if (linkLaunch_node!=null)               linkLaunch               = linkLaunch_node.getText();
       if (facebook_friend_enable_node!=null)   facebook_friend_enable   = facebook_friend_enable_node.getText();
       if (facebook_like_enable_node!=null)     facebook_like_enable     = facebook_like_enable_node.getText();
       if (facebook_id_node!=null)              facebook_id              = facebook_id_node.getText();
       if (facebook_url_node!=null)             facebook_url             = facebook_url_node.getText();
       if (twitter_tweet_enable_node!=null)     twitter_tweet_enable     = twitter_tweet_enable_node.getText();
       if (twitter_follow_enable_node!=null)    twitter_follow_enable    = twitter_follow_enable_node.getText();
       if (twitter_id_node!=null)               twitter_id               = twitter_id_node.getText();
       if (analyticsTagging_node!=null)         analyticsTagging         = analyticsTagging_node.getText();
       if (campaignSource_node!=null)           campaignSource           = campaignSource_node.getText();
       if (campaignMedium_node!=null)           campaignMedium           = campaignMedium_node.getText();
       if (campaignTerm_node!=null)             campaignTerm             = campaignTerm_node.getText();
       if (campaignContent_node!=null)          campaignContent          = campaignContent_node.getText();
       if (campaignName_node!=null)             campaignName             = campaignName_node.getText();
       if (twitter_follow_text_node!=null){
          twitter_follow_text = twitter_follow_text_node.getText();
       } else {
          twitter_follow_text = "Follow us on Twitter";
       }
       if (twitter_default_text_node!=null){
          twitter_default_text = twitter_default_text_node.getText();
       } else {
          twitter_default_text = "Try this Guided Shopping Experience(tm)";
       }
       if (facebook_friend_text_node!=null){
          facebook_friend_text = facebook_friend_text_node.getText();
       } else {
          facebook_friend_text = "Friend us on Facebook";
       }
       hasPictures = (guiderFormat=='g'||guiderFormat=='h'||guiderFormat=='w');      
    }
  }
  this.getIcon = function(){
    return icon;
  }
  this.getLogo = function(){
    return logo;
  }
  this.getBanner = function(){   
    return banner;
  }
  this.headerTypeIsBanner = function(){
    if (headerType==null) {return false;}
    return (headerType=="banner");
  }
  this.getIntroText = function(){
    return introText;
  }
  this.getCssFile = function(){
    return cssFile;
  }
  this.hasCompanyURL = function(){
    return (companyURL!=null);
  }
  this.getCompanyURL = function(){
    if (companyURL!=null && companyURL.substring(0, 4)!="http") return "http://"+companyURL;
    return companyURL;
  }
  this.facebookEnabled = function(){
    if (facebook_friend_enable==1 || facebook_like_enable==1){
       return true;
    }
    return false;
  }
  this.facebookFriendEnabled = function(){
    if (facebook_friend_enable==1){
       return true;
    }
    return false;
  }
  this.facebookLikeEnabled = function(){
    if (facebook_like_enable==1){
       return true;
    }
    return false;
  }
  this.twitterEnabled = function(){
    if (twitter_tweet_enable==1 || twitter_follow_enable==1){
       return true;
    }
    return false;
  }
  this.twitterTweetEnabled = function(){
    if (twitter_tweet_enable==1){
       return true;
    }
    return false;
  }
  this.twitterFollowEnabled = function(){
    if (twitter_follow_enable==1){
       return true;
    }
    return false;
  }
  this.getFacebookAccount = function(){
    return facebook_id;
  }
  this.getFacebookFriendText = function(){
    return facebook_friend_text;
  }
  this.getTwitterAccount = function(){
    return twitter_id;
  }
  this.getTwitterDefaultText = function(){
    return twitter_default_text;
  }
  this.getTwitterFollowText = function(){
    return twitter_follow_text;
  }
  this.getContactEmail = function(){
    return contactEmail;
  }
  this.getContactText = function(){
    if(contactText != null){
        return contactText;
    }else{
        return "";
    }
  }
  this.hasContactName = function(){
    return (contactName!=null);
  }
  this.getContactName = function(){
    return contactName;
  }
  this.hasPictures = function(){
    return hasPictures;
  }
  this.getFormat = function(){
    return guiderFormat;
  }
  this.linksNewWindow = function(){
    return (linkLaunch!=null && linkLaunch=="1");
  }
  this.getTagParams = function(){
    if(typeof analyticsTagging != 'undefined' && analyticsTagging != null){
        return  "utm_source="      + campaignSource +
            "&utm_medium="      + campaignMedium +
            "&utm_term="        + campaignTerm +
            "&utm_content="     + campaignContent +
            "&utm_campaign="    + campaignName;
    }else{
        return "";
    }
  }
  this.getTitle = function(){
    var rootNodeElement = xmldoc.getRoot().getChildByName("name");
    return rootNodeElement.getText();
  }
  this.getVersion = function(){   
    var rootNodeElement = xmldoc.getRoot().getChildByName("version");
    if (rootNodeElement==null){return null;}
    return rootNodeElement.getText();
  }
  this.getRootQuestion = function(){
    var rootNodeElement = xmldoc.getRoot().getChildByName("root-node");
    var rootid = rootNodeElement.getText();
    var node = nodearray[rootid];
    var rootQ = new Question(node);
    rootQ.setGuider(this);
    return rootQ;
  }
  this.getNode = function(id){
    var node = nodearray[id];
    var desttype = node.getAttribute('type');
    var dest = null;
    switch (desttype){
      case "Question":
           dest = new Question(node);
           break;
      case "GoogleSearch":
           dest = new Results(node);
           break;
      case "BingSearch":
           dest = new Results(node);
           break;
      case "ExternalLink":
           dest = new Results(node);
           break;
      case "SolutionPage":
           dest = new Results(node);
           break;
      case "VideoPage":
           dest = new Results(node);
           break;
      default:
           alert("Unknown destination type of '"+desttype+"' in Answer.getDestination");
           break;
    }
    dest.setGuider(this);
    return dest;
  }
  //----------------------------------------------------------------------------
  // This method gets an external link node with the given destination URL
  //----------------------------------------------------------------------------
  this.getNodeByDestination = function(desturl){
    for (var i in nodearray){
        var node = nodearray[i];
        if (node.getAttribute('type')=="ExternalLink"){
           var result = new Results(node);
           result.setGuider(this);
           if (result.getURL()==desturl){
              return node;
           }
        }
    }
    return null;
  }
  //----------------------------------------------------------------------------
  // This method gets the parent of a given node
  //----------------------------------------------------------------------------
  this.getParent = function(childnode){
    var target = childnode.getAttribute('id');
    for (var i in nodearray){
        var node = nodearray[i];
        if (node.getAttribute('type')=="Question"){ //all parent nodes are Questions
           var question = new Question(node);
           question.setGuider(this);
           var answers = question.getAnswers();
           for (var j=0; j<answers.length; j++){
               var destnode = answers[j].getDestination();
               if (destnode==null){
                  continue;
               }               
               if (destnode.getID()==target){
                  return question;
               }
           }
        }
    }
    return null;
  }
  this.hasHeader = function(){
    return ( (icon!=null) || (logo!=null) || (introText!=null) || (banner!=null) );
  }

}

//Takes an XMLElement representing the node from the guider xml
function Question(node){

  var thisnode = node;
  var myguider = null;
  var answers  = null;

  this.getText = function(){
    var qtnode = thisnode.getChildByName("question-text");
    return qtnode.getText().replace(/\[.*?:(.*?)\]/g,"$1"); //remove definitions which are not supported in portable guiders
  }
  this.getAnswers = function(){
    if (answers!=null) return answers;
    var answersnode = thisnode.getChildByName("answers");
    var answerelems = answersnode.getChildrenByName("answer");
    var answers = new Array();
    for (var i=0; i<answerelems.length; i++){
        var a = new Answer(answerelems[i]);
        a.setGuider(myguider);
        answers.push(a);
    }
    return answers;
  }
  this.getID = function(){
    return thisnode.getAttribute('id');
  }
  this.hasExplanation = function(){
    return (thisnode.getChildByName("explanation-text")!=null);
  }
  this.getExplanation = function(){
    var exnode = thisnode.getChildByName("explanation-text");
    return exnode.getText();
  }
  this.getGuider = function(){
    return myguider;
  }
  this.hasImages = function(){
    var answers = this.getAnswers();
    for (var i=0; i<answers.length; i++){
        if (answers[i].hasImage()){
           return true;
        }
    }
    return false;
  }
  this.isQuestion     = function(){return true;}
  this.isResult       = function(){return false;}
  this.isGoogleSearch = function(){return false;}
  this.isVideoPage    = function(){return false;}
  this.isBingSearch   = function(){return false;}
  this.isExternalLink = function(){return false;}
  this.isRoot     = function(){
    if (myguider==null){
       alert("question object has no link to the guider.");
       return false;
    }
    if (this.getID()==myguider.getRootQuestion().getID()){
       return true;
    }
    return false;
  }
  this.setGuider  = function(guider){myguider = guider;};
  //parse the XML node

}

function Answer(node){
  this.SUB_NODE      = 1;
  this.GOOGLE_SEARCH = 2;
  this.EXTERNAL_LINK = 3;
  this.BING_SEARCH   = 4;

  var thisnode = node;
  var myguider = null;

  this.getDestination = function(){
    var dest = null;
    var linkselem = thisnode.getChildByName('links');
    if (linkselem!=null){
       var linkelem = linkselem.getChildByName('link');
       if (linkelem != null){ //Then it's an internal link
          var nodeid = linkelem.getAttribute('subsequent-node-id');
          dest = myguider.getNode(nodeid);
       } else { //is it a link to an external guider
          linkelem = linkselem.getChildByName('external-link');
          if (linkelem != null){ //Then it's an external guider
             var linkid = linkelem.getAttribute('guider');
             dest = new ExternalGuider(linkid);
          }
       }
    }
    if (dest!=null){
       dest.setGuider(myguider);
    }
    return dest;
  }

  this.getText = function(){
    var atnode = thisnode.getChildByName("answer-text");
    return atnode.getText().replace(/\[.*?:(.*?)\]/g,"$1"); //remove definitions which are not supported in portable guiders
  }
  this.hasExplanation = function(){
    return (thisnode.getChildByName("explanation-text")!=null);
  }
  this.getExplanation = function(){
    var exnode = thisnode.getChildByName("explanation-text");
    return exnode.getText();
  }
  this.hasImage = function(){
    return (thisnode.getChildByName("image")!=null);
  }
  this.getImage = function(){
    return thisnode.getChildByName("image").getChildByName("url").getText();
  }
  this.getSlug = function(){
    var exnode = thisnode.getChildByName("slug");
    return exnode.getText();
  }
  this.setGuider  = function(guider){myguider = guider;};
}

function Results(node){

  var thisnode = node;
  var myguider = null;

  function htmldecode(str){
    var out = str;
    out = out.replace( /\&amp;/g, '&' );
    out = out.replace( /\&lt;/g, '<' );
    out = out.replace( /\&gt;/g, '>' );
    out = out.replace( /\&quot;/g, '"' );
    out = out.replace( /\&copy;/g, '©' );
    out = out.replace( /\&reg;/g, '®' );
    out = out.replace( /\&laquo;/g, '«' );
    out = out.replace( /\&raqou;/g, '»' );
    out = out.replace( /\&apos;/g, "'" );
    return out;
  }

  this.getID = function(){
    return thisnode.getAttribute('id');
  }
  this.getURL = function(){
    var type  = thisnode.getAttribute('type');
    var query = null;
    switch (type){
      case "GoogleSearch":
           var query = thisnode.getChildByName('query').getText();
           query = "http://www.google.com/search?hl=en&q="+query.replace(/\s/g,"+");
           break;
      case "BingSearch":
           var query = thisnode.getChildByName('query').getText();
           query = "http://www.bing.com/search?q="+query.replace(/\s/g,"+");
           break;
      case "VideoPage":
           var query = thisnode.getChildByName('query').getText();
           query = "http://www.bing.com/search?q="+query.replace(/\s/g,"+");
           break;
      case "ExternalLink":
           var query = thisnode.getChildByName('query').getText();
           break;
    }
    return query;
  }
  this.getContent = function(){
    return htmldecode(thisnode.getChildByName('content').getText());
  }
  this.isQuestion      = function(){return false;}
  this.isResult        = function(){return true;}
  this.isGoogleSearch  = function(){return (thisnode.getAttribute('type')=='GoogleSearch')}
  this.isBingSearch    = function(){return (thisnode.getAttribute('type')=='BingSearch')}
  this.isVideoPage     = function(){return (thisnode.getAttribute('type')=='VideoPage')}
  this.isExternalLink  = function(){return (thisnode.getAttribute('type')=='ExternalLink')}
  this.isSolutionPage  = function(){return (thisnode.getAttribute('type')=='SolutionPage')}
  this.isGuider        = function(){return false;}
  this.setGuider  = function(guider){myguider = guider;};
}

function ExternalGuider(gid){
  var myguider = null;
  var destgid  = gid;
  this.getExternalGID  = function(){return destgid;}
  this.isQuestion      = function(){return false;}
  this.isResult        = function(){return true;}
  this.isGoogleSearch  = function(){return false;}
  this.isBingSearch    = function(){return false;}
  this.isExternalLink  = function(){return false;}
  this.isVideoPage     = function(){return false;}
  this.isSolutionPage  = function(){return false;}
  this.isGuider        = function(){return true;}
  this.setGuider  = function(guider){myguider = guider;};

}
var the_portable_guider_map = {};
var ig_options = {
  localCSS:      false, //true - don't set the css file because the css is set locally
  startAtRoot:   true,  //false - if it's a returning user, start where they left off
  popUp:         false, //true - display guider as a pop up
  trackUsers:    true,  //true - pings are sent back to track users
  pingURL:       null,  //set to URL to override default ping URL.
  userID:        null,  //set to override the user.
  linkShareURL:  null,  //When being used in a linkShare context this is the URL
                        //to actually send external links to
  campaign:      null,                      
  trackingCode:  null,  //set to cause the portable guider to pull a tracking ID from 
                        //the URL and append it to external URLs.  The argument is of
                        //the form abc:xyz where abc is the source URL parameter and 
                        //xyz is the  
  testDay:       null,  //set to integer to change the day of the month pings are reported as
  versionString: '0.02',
  pjn:           null,  //Options specific to pepperjam network.  Accepted sub-options are: 
                        //affiliate_id, sid & website_id
  source : 'prod'      //Possible values: prod, dev, unpublished
};
var portable_guider_progress_margin = 190;
var portable_guider_progress_direction = "right";
var portable_guider_progress_interval;
var portable_guider_progress_timeout;
var portable_guider_calculate_progress_margin = function(){
    if(portable_guider_progress_direction == "right"){
        if(portable_guider_progress_margin <= 0){
            portable_guider_progress_direction = "left";
        }else{
            portable_guider_progress_margin -= 5;        
        }
    }else{
        if(portable_guider_progress_margin >= 185){
            portable_guider_progress_direction = "right";
        }else{
            portable_guider_progress_margin += 5;
        }
    }
    return portable_guider_progress_margin;
};
var portable_guider_move_progress_bar = function(){
    var bar = document.getElementById("portable-guider-progress");
    if(null != bar){
        new_margin = portable_guider_calculate_progress_margin();
        bar.style.marginRight = new_margin + "px";
    }else{
        clearInterval(portable_guider_progress_interval);
    }
};
var portable_guider_kill_progress_bar = function(){
    var elem = document.getElementById('portable-guider-loading-progress');
    //if (elem!=null)elem.style.display='none'
    if (elem!=null)elem.innerHTML = "<img src='http://www.iguiders.com/images/errortracker.gif' />I'm sorry.  This guider could not be loaded....<a href='javascript:(function(){var elem=document.getElementById(\"portable-guider-frame\");if (elem!=null)elem.style.display=\"none\";})();'>close</a>"
    clearInterval(portable_guider_progress_interval);
}
function portable_guider_callback(xml,uid,gid){
  var storage = new ClientSideStorage("portableguideruser");
  var userid = null;
  if (storage.contains('userid')){
     userid = storage.get('userid');
  } else {
     userid = portable_guider_randomstring();
     storage.put('userid',userid);
     storage.save();
  }
  the_portable_guider_map[gid].setUserId(userid);
  the_portable_guider_map[gid].setXML(gid,xml);
  if (document.getElementById("portable-guider-frame-close")!=null){
     document.getElementById("portable-guider-frame-close").style.display = "inline";
  }
}
function portable_guider_randomstring(){
  var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
  var ret = '';
  for (var i=0; i<36; i++) {
    var rnum = Math.floor(Math.random() * chars.length);
    ret += chars.charAt(rnum);
  }
  return ret;
}
function portable_guider_closeWhatsThis(evt){
  if (!evt) evt = window.event;
  var target = (evt.target)?evt.target:(evt.srcElement)?evt.srcElement:null;
  var id = target.id.substring(target.id.lastIndexOf("-")+1);
  var textelem = document.getElementById("portable-guider-whats-this-text-"+id);
  textelem.style.display = (textelem.style.display != 'none' ? 'none' : '' );

  if (evt.stopPropagation) evt.stopPropagation();
  evt.cancelBubble = true;
  return false;
}
function PortableGuider_getElementsByClassName(className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;				
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
}
function PortableGuider(app,gid,key,viewer,options){
  var theUserId = null;
  var that = this;
  var theCSSTimer = null;
  
  this.options = ig_options;
  if (typeof options != 'undefined'){
     for (attr in options) { this.options[attr] = options[attr]; }
  }

  if(this.options['campaign'] != null){
     this.campaign = true;
     this.campaign_id = this.options['campaign'];
  }else{
     this.campaign = false;
  }

  this.trackingCode = null;
  this.trackingParamName = null;
  if (this.options.trackingCode!=null){
     var params = this.options.trackingCode.split(':');
     this.trackingCode = urlParam(params[0]);
     this.trackingParamName = params[1];
  }

  this.linkShareURL = this.options.linkShareURL;

  this.cssURL = null;
  this.guiderURL = null;
  this.imageURL  = null;
  this.pingURL  = null;
  this.appURL = app;

  switch (this.options.source){
    case 'prod':
         this.cssURL = "http://pgstatic.iguiders.com/"+this.options.versionString+"/css/";
         this.guiderURL = "http://guiders.iguiders.com/";
         this.imageURL  = "http://pgimages.iguiders.com/";
         this.pingURL   = "http://ping.iguiders.com/0.02/ping/";
         this.unpublished = false;
         break;
    case 'dev':
         this.cssURL = "http://pgstatic-dev.iguiders.com/"+this.options.versionString+"/css/";
         this.guiderURL = "http://guiders-dev.iguiders.com/";
         this.imageURL  = "http://pgimages-dev.iguiders.com/";
         this.pingURL   = "http://ping.iguiders.com/0.02/ping/";
         this.unpublished = false;
         break;
    case 'unpublished':
         this.cssURL = app+"/pg/"+this.options.versionString+"/css/";
         this.guiderURL = app+"/api/"+this.options.versionString+"/";
         this.imageURL  = app+"/user_images/";
         this.pingURL   = app+"/api/"+this.options.versionString+"/ping/";
         this.unpublished = true;
         break;
  }
  
  if (this.options.pingURL!=null){
     this.pingURL = this.options.pingURL;
  }
  if (this.options.userID!=null){
     theUserId = this.options.userID;
  }
  var theStack  = new PortableGuider.StateStack(gid);

  if (this.options.startAtRoot){
     theStack.resetHistory();
  }

  var theCxn    = new PortableGuider.Connection(app,key,this);
  theCxn.setPingDay(this.options.testDay);
  theCxn.setStack(theStack);
  this.theViewer = viewer;
  this.theViewer.setContext(this);
  this.theViewer.setStack(theStack);
  this.theViewer.setConnection(theCxn);

  var theRootGID    = gid;
  var theApp = app;

  the_portable_guider_map[theRootGID] = this;

  this.hideViewer = function(){
    if (this.theViewer!=null){
       this.theViewer.hide();
    }
  }

  this.setXML = function(gid,xml){
    var launchURL = window.location.href;
    if (launchURL.indexOf("?")!=-1){
       launchURL = launchURL.substring(0, launchURL.indexOf("?"));
    }
    var guider = new Guider();
    guider.setXML(xml);
    theStack.pushGuider(gid,guider);
    theCxn.ping('guiderlaunch',launchURL,theUserId);
    if (this.options.localCSS == false && guider.getCssFile() != null){
       theCxn.requestCSS(guider.getCssFile());
       theCSSTimer = setInterval(this.cssCheck,50);
    } else {
       this.cssReady();
    } 
  }    
  
  this.cssCheck = function(){
    var cssCheckElem = document.getElementById('portable-guider-css-check');
    //if the checkElem item doesn't exist or the css has arrived.
    if (cssCheckElem==null || cssCheckElem.offsetWidth==100){
       clearInterval(theCSSTimer);
       that.cssReady();
    }
  }
    
  this.cssReady = function(){  
    this.theViewer.render()
  }
  
  this.setUserId = function(uid){
    if (theUserId==null){theUserId=uid;}
    this.theViewer.setUserId(theUserId);
  }
  
  this.getVersion = function(){return this.options.versionString;}
  this.getApp     = function(){return app;}

  
  function urlParam( name ){
    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( window.location.href );
    if( results == null )
      return "";
    else
      return results[1];
  }
  
  theCxn.requestXML(theStack.theState.getGID());

}

////////////////////////////////////////////////////////////////////////////////
// Class: PortableGuider.Connection
////////////////////////////////////////////////////////////////////////////////
PortableGuider.Connection = function(app,key,pg_context){
  var theApp      = app;
  var theKey      = key;
  var theContext  = pg_context;
  var theStack    = null;
  var pingCounter = 0;
  var day = null;
  var session_id = portable_guider_randomstring();

  this.requestXML = function(gid){
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("charset", "utf-8");
    script.setAttribute("src", theContext.guiderURL+gid+(theContext.unpublished?"?callback=portable_guider_callback&":".js?")+"noCacheIE="+new Date().getTime());
    document.getElementsByTagName("head").item(0).appendChild(script);
    the_portable_guider_map[gid] = theContext;
  }

  this.ping = function(op,qid,uid){

    var d = new Date();
    if (day!=null){d.setDate(day);}
    
    if (theContext.options.trackUsers==false){return;}
    var gid = theStack.theState.getGID();
    var version = theStack.theState.getGuider().getVersion();
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("charset", "utf-8");
    var campaign_param = "";
    if(theContext.campaign == true){
        var campaign_param = "&campaign_id=" + theContext.campaign_id;
    }
    script.setAttribute("src", theContext.pingURL+"?apikey="+theKey+"&session_id="+session_id+"&uid="+uid+"&operation="+op+"&gslug="+gid+(version!=null?"&v="+version:"")+"&qslug="+qid+"&count="+pingCounter+"&timestamp="+d.getTime()+campaign_param);
    pingCounter++;
    document.getElementsByTagName("head").item(0).appendChild(script);

  }

  this.requestCSS = function(cssfile){
    //remove any path from the cssfile spec
    var filename = cssfile;
    if (cssfile.indexOf('/')!=-1){
       filename = cssfile.substring(cssfile.lastIndexOf('/')+1);
    }
    var linkelem = document.getElementById('portable_guider_css');
    if (linkelem==null){ //then we need to build a new one
       linkelem=document.createElement("link")
       linkelem.setAttribute("id", "portable_guider_css")
       linkelem.setAttribute("rel", "stylesheet")
       linkelem.setAttribute("type", "text/css")
       linkelem.setAttribute("href", theContext.cssURL+filename);
       document.getElementsByTagName("head")[0].appendChild(linkelem);
    } else { // then we have an existing element
       linkelem.setAttribute("href", theContext.cssURL+filename);
    }
  }
  this.sendContactInfo = function(gid,userid,name,email,msg,phone,method,path,campaign_param){
    var script = document.createElement("script");
    var version = theStack.theState.getGuider().getVersion();
    var tag = portable_guider_randomstring();
    if (version==null || version==0){
       version=1;
    }
    script.setAttribute("type", "text/javascript");
    script.setAttribute("charset", "utf-8");
    //script.setAttribute("src", theApp+"/api/0.01/contact/?apikey="+theKey+"&uid="+userid+"&gid="+gid+(version!=null?"&v="+version:"")+"&name="+escape(name)+"&email="+email+"&msg="+escape(msg)+"&phone="+escape(phone)+"&method="+escape(method)+"&path="+escape(path)+"&noCacheIE="+new Date().getTime()+campaign_param);
    script.setAttribute("src", theApp+"/api/0.01/contact/?apikey="+theKey+"&uid="+userid+"&gid="+gid+"&v="+version+"&name="+escape(name)+"&email="+email+"&msg="+escape(msg)+"&phone="+escape(phone)+"&method="+escape(method)+"&tag="+tag+"&noCacheIE="+new Date().getTime()+campaign_param);
    document.getElementsByTagName("head").item(0).appendChild(script);
    this.ping("contactnow",tag,userid);
  }
  this.setStack = function(stack){
    theStack = stack;
  }
  this.getAppURL = function(){
    return theApp;
  }
  this.setPingDay = function(d){day=d;}

}
////////////////////////////////////////////////////////////////////////////////
// Class: PortableGuider.Viewer
////////////////////////////////////////////////////////////////////////////////

PortableGuider.Viewer = function(gid){
  var that            = this;
  var theBaseGID      = gid;
  var theCurrentGID   = gid;
  var theStack        = null;
  var theCxn          = null;
  var thecanvasdiv    = null;
  var thecallback     = null;
  var theUserId       = null;
  var theContext      = null;
  var isDisplayed     = false;
  var carouselType    = null;
  var theCanvasID     = "portable-guider-canvas";
  var thediv          = null;  //The div of the canvas into which the body of the guider is written
//  var SetTrackingCookie = true;

  //builds the portable guider frame and canvas then inserts into the document
  //use this method instead of setCanvasDiv if you are willing to accept the
  //default position and behavior of the
  this.buildFrame = function(){
    //remove existing frame if any
    var div = document.getElementById("portable-guider-frame");
    if (div != null){
       while (div.childNodes.length>0){
             var child = div.childNodes[0];
             div.removeChild(child);
       }
       //var parentNode = prevDiv.parentNode;
       //parentNode.removeChild(prevDiv);
       div.style.zIndex = "1100";
       div.style.background = "#FFF";
       div.style.border = "1px solid #999";
       div.style.display = "block";
       div.style.position = "fixed";
       div.style.top = "50px";
       div.style.left = "50px";
    } else {
       div = document.createElement("div");
       div.id = "portable-guider-frame";
       div.style.position = "fixed";
       div.style.top  = "50px";
       div.style.left = "50px";
       div.style.zIndex = "1100";
       div.style.background = "#FFF";
       div.style.border = "1px solid #999";
    }
    div.innerHTML  = "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].hideViewer();' title='Close the guider'><span id='portable-guider-frame-close' class='portable-guider-frame-close' style='display:none;'>Close</span></a>"+
                     "<div id='"+theCanvasID+"'></div>";
    var body_elem  = document.getElementsByTagName("body").item(0);
    body_elem.insertBefore(div,body_elem.firstChild);
    thediv = new Div();
    thediv.bind(theCanvasID);
    this.displayInitMsg(); 
  }

  this.setCallback = function(callback){
    thecallback = callback;
  }

  this.setCanvasDivId = function(divid){
    thediv = new Div();
    thediv.bind(divid);
  }
  this.displayInitMsg = function(){
    var canvas = document.getElementById(theCanvasID);
    canvas.innerHTML = "<div id='portable-guider-loading-progress' style='background:#FFF; color:#101f69; padding:10px; border:1px solid #88; z-index:15; font-size: 16px; font-weight:bold;width:605px;'>Loading Guider... <div style='width:200px;float:right;margin-right:230px;background:#EEE;border:1px solid #999;'><div id='portable-guider-progress' style='width:15px;float:right; background: #101f69; background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.4, rgb(16,31,105)), color-stop(0.61, rgb(23,59,166))); background-image: -moz-linear-gradient(center bottom, rgb(16,31,105) 40%, rgb(23,59,166) 61%);'>.</div></div></div><div id='portable-guider-css-check'>&nbsp;</div>";   
    portable_guider_progress_margin = 190;
    portable_guider_progress_interval = setInterval("portable_guider_move_progress_bar()", 50);
    portable_guider_progress_timeout = setTimeout(function(){portable_guider_kill_progress_bar();}, 30000);
  }
  this.display = function(){
    if (isDisplayed) return;
    if (typeof(ig_show_guider)=='function'){
       ig_show_guider('portable-guider-frame');
    } else {
       startFadeIn('portable-guider-frame',function(){that.displayComplete();});
       //var elem = document.getElementById('portable-guider-frame');
       //elem.style.display='';
       //that.displayComplete();
       //new Fx.Reveal($('portable-guider-frame'),{onComplete:function(){that.displayComplete();}}).reveal();
    }
    isDisplayed = true; 
  }
  this.displayComplete = function(){
    new PortableGuiderDraggableElement('portable-guider-frame');
    //$('portable-guider-frame').makeDraggable({stopPropagation:true});
  }
  this.hide = function(){
    if (!isDisplayed) return;
    theCxn.ping("guiderclose",theStack.theState.getGID(),theUserId);
    var autolaunch = new ClientSideStorage("autolaunch");
    autolaunch.clear();
    autolaunch.save();

    if (typeof(ig_hide_guider)=='function'){
       ig_hide_guider('portable-guider-frame');
    } else {
       startFadeOut('portable-guider-frame');
       //var elem = document.getElementById('portable-guider-frame');
       //elem.style.display='none';
       //new Fx.Reveal($('portable-guider-frame')).dissolve();
    }
    isDisplayed = false;
  }
  this.setConnection = function(cxn){
    theCxn = cxn;
  }
  this.setContext = function(context){
    theContext = context;
  }
  this.setStack = function(stack){
    theStack = stack;
  }
  this.setState = function(state){
    alert('Viewer.setState() not implemented');
  }
  this.setUserId = function(uid){
    theUserId = uid;
  }
//  //----------------------------------------------------------------------------
//  // Tells the portable guider to ignore any exisiting cookies and just start
//  // at the top of the guider tree.
//  //----------------------------------------------------------------------------
//  this.resetHistory = function(){
//    idstack = new Array();
//    currentid = "";
//    storage.clear();
//    storage.save();
//  }
  this.goToRoot = function(){
    theStack.theState.addPathElement("home button");
    theCxn.ping("guiderhome",theStack.theState.getCurrentID(),theUserId);
    var question = theStack.theState.getGuider().getRootQuestion();
    theStack.theState.pushID(question.getID());
    renderQuestion(question);
  }
  this.goBack = function(){
    theStack.theState.addPathElement("back button");
    theCxn.ping("guiderback",theStack.theState.getCurrentID(),theUserId);
    if (theStack.theState.hasPreviousID()){
       //Then we can stay in the existing guider
       var currentid = theStack.theState.popID();
       var node = theStack.theState.getGuider().getNode(currentid);
       if (!node.isQuestion()){ alert("PortableGuider.renderQuestion received node which is not a question"); }
       renderQuestion(node);
    } else if (theStack.hasPreviousGuider()){
       //Then we need to go to a previous guider
       theStack.pop();
       //send ping about guider change
       theCxn.ping("guiderchange",theStack.theState.getGID(),theUserId);
       if (theStack.theState.getGuider()==null){
          //Then we need to request the XML and wait for it.
          theCxn.requestXML(theStack.theState.getGID());
       } else {
          //The state has the guider it needs
          var guider = theStack.theState.getGuider();
          theCurrentGID = theStack.theState.getGID();
          theCxn.requestCSS(guider.getCssFile());
          var currentid = theStack.theState.getCurrentID();
          var node = theStack.theState.getGuider().getNode(currentid);
          if (!node.isQuestion()){ alert("PortableGuider.renderQuestion received node which is not a question"); }
          renderQuestion(node);
       }
       theCurrentGID = theStack.theState.getGID();
    }
    theStack.saveState();
  }
  this.handleAnswer =  function(qid,answer_index){
   
    var guider = theStack.theState.getGuider();
    var question = guider.getNode(qid);  
    var answers = question.getAnswers();
    if (answer_index >= answers.length){
       alert('answer index out of bounds in handleAnswer');
    }
    var answer = answers[answer_index];
    
    theStack.theState.addPathElement('  Answer Clicked: '+answer.getText());

    theCxn.ping('answerclick',encodeURI(answer.getSlug()),theUserId);
    
    if (answer.getDestination().isQuestion()){
       this.next(answer.getDestination().getID());
    } else if (answer.getDestination().isExternalLink()){
       var DestinationURL = answer.getDestination().getURL(); 
       var tag_params = guider.getTagParams();
       if (theContext.trackingCode!=null && theContext.trackingCode!=""){
          var code = theContext.trackingParamName+"="+theContext.trackingCode;
          tag_params = (tag_params==""?code:'&'+code);
       }
       tag_params = (tag_params==""?"":'?'+tag_params);

       if (theContext.linkShareURL){
          DestinationURL = theContext.linkShareURL + encodeURIComponent(DestinationURL);
       }
       
       if (theContext.options.pjn!=null){
          var affiliate_id = theContext.options.pjn.affiliate_id;
          var sid = theContext.options.pjn.sid;
          var website_id = theContext.options.pjn.website;
          DestinationURL = pjnModifyURL(DestinationURL,affiliate_id,sid,website_id);
       }
       
       if (guider.linksNewWindow()){
          window.open(DestinationURL + tag_params,"","width=1000,height=600,resizable,location=1,status=1,menubar=1,scrollbars=yes");
       } else {
          if (theContext.options.popUp == false){
             setTimeout(function(){ window.location = DestinationURL + tag_params; }, 250);
          } else {
             setTimeout(function(){ window.opener.location = DestinationURL + tag_params; }, 250);
          }
       }
    } else if (answer.getDestination().isSolutionPage()){
       theStack.theState.pushID(-1);
       renderSolutionPage(answer.getDestination());
    } else if (answer.getDestination().isVideoPage()){
       theStack.theState.pushID(-1);
       renderVideoPage(answer.getDestination());
    } else if (answer.getDestination().isGoogleSearch()){
       window.open(answer.getDestination().getURL(),"Google","width=1000,height=600,resizable,scrollbars=yes");
    } else if (answer.getDestination().isBingSearch()){
       window.open(answer.getDestination().getURL(),"Bing","width=1000,height=600,resizable,scrollbars=yes");
    } else { //it's an external guider link
      this.nextGuider(answer.getDestination().getExternalGID());
    }
  }
  this.handleBannerClick =  function(url){
    var guider = theStack.theState.getGuider();
    theCxn.ping('bannerclick',null,theUserId);
    if (guider.linksNewWindow()){
       window.open(url,"","width=1000,height=600,resizable,location=1,status=1,menubar=1,scrollbars=yes");
    } else {
       if (theContext.options.popUp == false){
          setTimeout(function(){ window.location = url; }, 250);
       } else {
          setTimeout(function(){ window.opener.location = url; }, 250);
       }
    }    
  }
  this.handleWhatsThisAnswer =  function(qid,answer_index){
    var guider = theStack.theState.getGuider();
    var question = guider.getNode(qid);
    var answers = question.getAnswers();
    if (answer_index >= answers.length){
       alert('answer index out of bounds in handleAnswer');
    }
    var answer = answers[answer_index];

    theCxn.ping('whatsthisanswer',encodeURI(answer.getSlug()),theUserId);

    var textelem = document.getElementById("portable-guider-whats-this-text-"+answer_index);
    textelem.style.display = (textelem.style.display != 'none' ? 'none' : '' );

    return false;
  }
  this.handleWhatsThisQuestion =  function(qid){
    var guider = theStack.theState.getGuider();
    var question = guider.getNode(qid);

    theCxn.ping('whatsthisquestion',encodeURI(question.getID()),theUserId);

    var textelem = document.getElementById("portable-guider-whats-this-text-q");
    textelem.style.display = (textelem.style.display != 'none' ? 'none' : '' );

    return false;
  }
  this.next = function(id){
      
    var guider = theStack.theState.getGuider()   

    var node = guider.getNode(id);  
    if (!node.isQuestion()){ alert("PortableGuider.renderQuestion received node which is not a question"); }

    theStack.theState.pushID(id);

    renderQuestion(node);

    theStack.saveState();
  }
  this.nextGuider = function(gid){
    //Show interstitial
    renderLoadingMessage();
    //make call for new guider definition.
    theCxn.requestXML(gid);

    theCxn.ping("guiderchange",gid,theUserId);
    theCurrentGID = gid;
  }
  this.showFacebookPanel = function(){
    this.hideSocialPanels();
    var elem = document.getElementById('portable-guider-facebook-panel');
    elem.style.display='';
  }
  this.showTwitterPanel = function(){
    this.hideSocialPanels();
    var elem = document.getElementById('portable-guider-twitter-panel');
    elem.style.display='';
  }
  this.hideSocialPanels= function(){
    var elem = document.getElementById('portable-guider-facebook-panel');
    if (elem!=null){ elem.style.display='none';}
    var elem = document.getElementById('portable-guider-twitter-panel');
    if (elem!=null){ elem.style.display='none'};
  }
  this.showContactForm = function(){
    theCxn.ping("contactnowopen",0,theUserId);
    var elem = document.getElementById('portable-guider-contact-link');
    elem.style.display='none';
    var elem = document.getElementById('portable-guider-contact-form');
    elem.style.display='';
  }
  this.sendContactInfo = function(){
    if(this.verifyEmail()){
      var elem = document.getElementById('portable-guider-contact-form');
      elem.style.display='none';
      var elem = document.getElementById('portable-guider-contact-link');
      elem.style.display='';
      var name  =  document.getElementById('portable-guider-contact-name').value;
      var email =  document.getElementById('portable-guider-contact-email').value;
      var msg   =  document.getElementById('portable-guider-contact-message').value;
      var phone =  document.getElementById('portable-guider-contact-phone').value;
      var method = document.getElementById('portable-guider-contact-method').value;
      var path   = theStack.theState.getPath();
      var campaign_param = "";
      if(theContext.campaign == true){
        var campaign_param = "&campaign=" + theContext.campaign_id;
      }
      theCxn.sendContactInfo(theBaseGID,theUserId,name,email,msg,phone,method,path,campaign_param);
    }else{
      alert("Emails do not match.");
    }
  }
  this.verifyEmail = function(){
    if( document.getElementById("portable-guider-contact-email").value ==
        document.getElementById("portable-guider-contact-email-verify").value){
      return true;
    }else{
      return false;
    }
  }
  this.hideContactInfo = function(){
    var elem = document.getElementById('portable-guider-contact-form');
    elem.style.display='none';
    var elem = document.getElementById('portable-guider-contact-link');
    elem.style.display='';
  }

  this.render = function(){
    var question = null;
    var guider = theStack.theState.getGuider();
    theCurrentGID = theStack.theState.getGID();
//    if (theContext.options.localCSS == false && guider.getCssFile() != null){
//       theCxn.requestCSS(guider.getCssFile());
//    }
    title = guider.getTitle();
    question = theStack.theState.getInitialQuestion();
    try {
        renderQuestion(question);
    } catch (e){
        renderError(e);
    }
    this.display();
  }
  //////////////////////////////////////////////////////////////////////////////
  // Private methods
  //////////////////////////////////////////////////////////////////////////////
  function renderQuestion(node){

    theStack.theState.addPathElement('Page: '+node.getText());

    theCxn.ping('pageview',node.getID(),theUserId);

    var guider = theStack.theState.getGuider();
    var html = "";
    var blankimage = null;
    var renderpictures = (guider.hasPictures() && node.hasImages());
    carouselType = renderpictures?"horizontal":"vertical";

//    if (SetTrackingCookie){
//       html += "<img src='"+theContext.appURL+"/track/view/"+theStack.theState.getGID()+"/"+theUserId+"?noCacheIE="+new Date().getTime()+"' />";
//       SetTrackingCookie=false;
//    }

    if (theStack.previousAvailable()){
       html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goBack();' title='Return to the previous question'><span id='portable-guider-enabled-back-Button'>&lt;Back</span></a>";
    } else {
       html += "<span id='portable-guider-disabled-back-Button'>&nbsp;</span>";
    }
    if (node.isRoot()){
       html += "<span id='portable-guider-disabled-root-Button'>&nbsp;</span>";
    } else {
       html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goToRoot();' title='Return to the root of this guider'><span id='portable-guider-enabled-root-Button'>^top</span></a>";
    }

    if (renderpictures){
       switch (guider.getFormat()){
         case 'g':
              blankimage = theCxn.getAppURL()+'/BlankImage_grid.png';
              html += "<div id='portable-guider-canvas-content' class='portable-guider-content-format-grid'>";
              break;
         case 'h':
              blankimage = theCxn.getAppURL()+'/BlankImage_high.png';
              html += "<div id='portable-guider-canvas-content' class='portable-guider-content-format-high'>";
              break;
         case 'w':
              blankimage = theCxn.getAppURL()+'/BlankImage_wide.png';
              html += "<div id='portable-guider-canvas-content' class='portable-guider-content-format-wide'>";
              break;
       }
    }else{
       html += "<div id='portable-guider-canvas-content' class='portable-guider-content-format-text'>";
    }
    
    html += "<div id='portable-guider-title'>"+theStack.theState.getGuider().getTitle()+"</div>";
    // render the header if necessary
    if (node.getGuider().hasHeader()){
       var icon      = node.getGuider().getIcon();
       var logo      = node.getGuider().getLogo();
       var introText = node.getGuider().getIntroText();
       var iconURL   = node.getGuider().getCompanyURL();       
       
       html += "<div id='portable-guider-header'>";

       if (node.getGuider().headerTypeIsBanner()){
          if (iconURL!=null){html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleBannerClick(\""+iconURL+"\");' >";}
          html += "<img id='portable-guider-banner' src='"+theContext.imageURL+node.getGuider().getBanner()+"' alt='guider banner' />";
          if (iconURL!=null){html += "</a>";}
       } else {

         if (logo!=null){
            html += "<img id='portable-guider-logo' src='"+theContext.imageURL+logo+"' alt='guider logo' />";
         }
         if (icon!=null){
            if (iconURL!=null){html += "<a href='"+iconURL+"'>";}
            html += "<img id='portable-guider-company-logo' src='"+theContext.imageURL+icon+"' alt='company logo' />";
            if (iconURL!=null){html += "</a>";}
         }
         if (introText!=null){
            html += "<p>"+introText+"</p>";
         }
       }

       html += "</div>";
    }
    html += "<div id='portable-guider-body'>";
    html += "<h4 class='portable-guider-question'>"+node.getText();
    if (node.hasExplanation()){
       html += "<a href='javascript:void();' onclick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleWhatsThisQuestion(\""+node.getID()+"\"); return false;' id='portable-guider-whats-this-button-q' class='portable-guider-whats-this-button-question' >What's this</a>";
    }
    html += "</h4>";
    if (node.hasExplanation()){
       html += "<div id='portable-guider-whats-this-text-q' class='portable-guider-whats-this-text' style='display: none;' ><a href='javascript:void(0);' id='portable-guider-whats-this-close-button-q' class='portable-guider-whats-this-close-button' onClick='showQuestionWhatsThis();' >close</a>"+node.getExplanation()+"</div>";
    }

    var answers = node.getAnswers();
    for (var i=0; i<answers.length; i++){
        if (answers[i].hasExplanation()){
           html +=  "<div id='portable-guider-whats-this-text-"+i+"' class='portable-guider-whats-this-text' style='display: none;'><a href='javascript:void(0);' id='portable-guider-whats-this-close-button-"+i+"' class='portable-guider-whats-this-close-button' >close</a>"+answers[i].getExplanation()+"</div>";
        }
    }

    if (carouselType=="horizontal"){
       html += "<div id='portable-guider-carousel' class='portable-guider-hcarousel'>";
    }else{
       html += "<div id='portable-guider-carousel' class='portable-guider-vcarousel'>";
    }
    
    html += "<div class='portable-guider-carousel-prev-button' style='display: none'>prev</div>";
    html += "<div class='portable-guider-carousel-disabled-prev-button'>prev</div>";
    html += "<div class='portable-guider-carousel-no-button'>&nbsp;</div>";
    html += "<div id='portable-guider-carousel-window' class='portable-guider-carousel-window'>";
    html += "<div id='portable-guider-answers' class='portable-guider-answers'>";

    for (var i=0; i<answers.length; i++){

        //reusable code for building an answer line
        function renderAnswerLine(link_class,link_text){
            var html = "<div id='"+answers[i].getSlug()+"' class='portable-guider-answer'>";
            if (renderpictures){
               if (answers[i].hasImage()){
                  html += "<a class='portable-guider-answer-image-link' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleAnswer(\""+node.getID()+"\","+i+")' ><img class='portable-guider-answer-illustration' src='"+theContext.imageURL+answers[i].getImage()+"' /></a>";
               } else if (guider.hasPictures()){ //display blank picture
                  html += "<a class='portable-guider-answer-image-link' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleAnswer(\""+node.getID()+"\","+i+")' ><img class='portable-guider-answer-illustration' src='"+blankimage+"' /></a>";
               }
            }
            html += "<a class='portable-guider-answer-link' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleAnswer(\""+node.getID()+"\","+i+")' >";
            html += "<span class='"+link_class+"'>"+link_text;
            html += "</span><span class='portable-guider-answer-text'>" + answers[i].getText();
            if (answers[i].hasExplanation()){
               html += "<em id='portable-guider-whats-this-button-"+i+"' class='portable-guider-whats-this-button-answer' onclick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleWhatsThisAnswer(\""+node.getID()+"\","+i+"); return false;'>What's this</em>";
            }
            html += "</span>";
            html +=  "</a>";
            html += "</div>";
            return html;
        }


        var destination = answers[i].getDestination();
        if (destination==null){
           html += "<div id='"+answers[i].getSlug()+"' class='portable-guider-answer' ><span class='portable-guider-destination-icon-none'>no destination</span><span class='portable-guider-answer-text'>"+answers[i].getText();
           if (answers[i].hasExplanation()){
              html += "<em class='portable-guider-whats-this-button-answer' onclick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.handleWhatsThisAnswer(\""+node.getID()+"\","+i+"); return false;'>What's This</em>";
           }
           html += "</span>";
           if (renderpictures){
              if (answers[i].hasImage()){
                 html += "<img class='portable-guider-answer-illustration' src='"+theContext.imageURL+answers[i].getImage()+"' alt='answer illustration' />";
              } else if (guider.hasPictures()){ //display blank picture
                 html += "<img class='portable-guider-answer-illustration' src='"+blankimage+"' alt='answer illustration' />";
              }
           }
           if (answers[i].hasExplanation()){
              html +=  "<div id='portable-guider-whats-this-text-"+i+"' class='portable-guider-whats-this-text' style='display: none;'><a href='javascript:void' class='portable-guider-whats-this-close-button' >close</a>"+answers[i].getExplanation()+"</div>";
           }
           html += "</div>";
        } else {
          if (destination.isQuestion()){
             html += renderAnswerLine("portable-guider-destination-icon-narrow","narrow");
          } else if (destination.isExternalLink()){
             html += renderAnswerLine("portable-guider-destination-icon-link","website");
          } else if (destination.isSolutionPage()){
             html += renderAnswerLine("portable-guider-destination-icon-solution","solution");
          } else if (destination.isGoogleSearch()){
             html += renderAnswerLine("portable-guider-destination-icon-google","website");
          } else if (destination.isVideoPage()){
             html += renderAnswerLine("portable-guider-destination-icon-video","video");
          } else if (destination.isBingSearch()){
             html += renderAnswerLine("portable-guider-destination-icon-bing","website");
          } else { //it's a link to an external guider
             html += renderAnswerLine("portable-guider-destination-icon-guider","guider");
          }
        }
    }
    html += "</div>"; //close portable-guider-answers


    html += "</div>"; //close portable-guider-carousel
    html += "<div class='portable-guider-carousel-next-button'>next</div>";
    html += "<div class='portable-guider-carousel-disabled-next-button' style='display: none'>next</div>";
    html += "</div>"; //close portable-guider-carousel-window


    html += "</div>";
    html += "</div>";

    html += "<div id='portable-guider-footer'>";
    if (guider.facebookEnabled()){
       html += "<div id='portable-guider-facebook-panel' class='portable-guider-social-panel' style='display:none'>";
       html += "<a class='portable-guider-frame-close' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.hideSocialPanels();'>X</a>";
       if (guider.facebookLikeEnabled()){
          html += "<iframe src='http://www.facebook.com/plugins/like.php?href="+escape(window.location.href)+"&amp;layout=standard&amp;show_faces=false&amp;width=450&amp;action=like&amp;colorscheme=light&amp;height=35' scrolling='no' frameborder='0' style='border:none; overflow:hidden; width:450px; height:35px;' allowTransparency='true'></iframe>";
       }
       if (guider.facebookFriendEnabled()){   
          html += "<div id='portable-guider-friend-us-link' onclick='window.open(\"http://www.facebook.com/addfriend.php?id="+guider.getFacebookAccount()+"\",\"iGuiders\",\"width=1100,height=600,resizable,scrollbars=yes\");'><span id='portable-guider-friend-us-icon'>icon</span><span id='portable-guider-friend-us-text'>"+guider.getFacebookFriendText()+"</span></div>";
       }
       html += "</div>";
       html += "<div id='portable-guider-facebook-icon' class='portable-guider-social-media-icon' onclick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.showFacebookPanel();'>facebook</div>";
    }
    if (guider.twitterEnabled()){
       html += "<div id='portable-guider-twitter-panel' class='portable-guider-social-panel' style='display:none'>";
       html +=    "<a class='portable-guider-frame-close' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.hideSocialPanels();'>X</a>";
       if (guider.twitterTweetEnabled()){
          html +=    "<div id='portable-guider-tweet-button' onclick='window.open(\"http://twitter.com/share?url="+escape(window.location.href);
          if (guider.getTwitterDefaultText()!=null){
             html += "&text="+escape(guider.getTwitterDefaultText());
          }
          html +=    "\",\"iGuiders\",\"width=1100,height=600,resizable,scrollbars=yes\");'>Tweet</div>";
       }
       if (guider.twitterFollowEnabled()){
          html +=    "<div id='portable-guider-follow-button' onclick='window.open(\"http://twitter.com/"+guider.getTwitterAccount()+"\",\"iGuiders\",\"width=1100,height=600,resizable,scrollbars=yes\");'><span id='portable-guider-follow-icon'>icon</span><span id='portable-guider-follow-text'>"+guider.getTwitterFollowText()+"</span></div>"
       }
       html += "</div>";
       html += "<div id='portable-guider-twitter-icon' class='portable-guider-social-media-icon' onclick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.showTwitterPanel();'>twitter</div>";
    }
    if (guider.getContactEmail()!=null){
        html += "<div id='portable-guider-contact-form' style='display: none'>";
        html += "<a id='portable-guider-contact-form-close' href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.hideContactInfo();'>X</a>";
        html += "<table>";
        html += "<tr><td colspan='2'>" + guider.getContactText() + "</td></tr>";
        html += "<tr><td><span>Your name:</span></td>";
        html += "<td><input id='portable-guider-contact-name' type='text'></td></tr>";
        html += "<tr><td><span>Your email:</span></td>";
        html += "<td><input id='portable-guider-contact-email' type='text'></td></tr>";
        html += "<tr><td><span>Verify email:</span></td>";
        html += "<td><input id='portable-guider-contact-email-verify' type='text'></td></tr>";
        html += "<tr><td><span>Cell phone:</span></td>";
        html += "<td><input id='portable-guider-contact-phone' type='text'></td></tr>";
        html += "<tr><td>Contact me via:</td>";
        html += "<td><select id='portable-guider-contact-method'><option value='email'>E-mail</option><option value='phone'>Phone</option></select> <span style='padding-left:30px;'>Characters remaining: <span id='pg-comment-length'>0</span>/140</span></td></tr>";
        html += "<tr><td><span>Message:</span></td>";
        html += "<td><textarea id='portable-guider-contact-message' type='text' onkeyup='countMsgChars(this);' onkeydown='countMsgChars(this);'></textarea></td></tr>";
        html += "<tr><td><input id='portable-guider-contact-now-send' type='button' value='send' disabled='true' onClick='the_portable_guider_map[\""+theBaseGID+"\"].theViewer.sendContactInfo();'></td><td><input type='checkbox' onClick='document.getElementById(\"portable-guider-contact-now-send\").disabled=!this.checked;' />I am over 18 and consent to the <a href='http://www.iguiders.com/iguidersprivacy-policy' target='new'>Privacy Policy</a></td></tr>";
        html += "</table>";
        html += "</div>";
        html += "<div id='portable-guider-contact-link'><span id='portable-guider-contact-icon'>Contact</span>";
        if (guider.hasContactName()){
           html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.showContactForm();'>"+guider.getContactName()+"</a>";
        } else {
           html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.showContactForm();'>Contact me now</a>";
        }
        html += "</div>"

    }
    html += "<div id='portable-guider-pg-logo'><a href='http://www.iguiders.com' ><img id='portable-guider-home-link' src='http://pgstatic.iguiders.com/0.02/img/portable.png' /></a>";
    html += "<div id='portable-guider-privacy-policy'><a href='javascript:void(0);' onclick='window.open(\"http://www.iguiders.com/iguidersprivacy-policy\",\"iGuider\",\"width=1100,height=600,resizable,scrollbars=yes\");'>privacy policy</div>";
    html += "</div>";
    html += "</div>";


    thediv.setHTML(html);
    var icons = getElemsByClassName('portable-guider-whats-this-close-button');
    for (var i=0; i<icons.length; i++){
        icons[i].onclick = portable_guider_closeWhatsThis;
    }
    //notify that a question has been displayed
    if (thecallback!=null){thecallback(node.getID())};

    //Create carousel
    new Carousel("portable-guider-carousel",carouselType,function(ping_type){theCxn.ping(ping_type,theStack.theState.getCurrentID(),theUserId);});

  }
  function renderLoadingMessage(e){
    var div = document.getElementById('portable-guider-canvas-content');
    div.innerHTML = "<div id='portable-guider-loading-message'><span id='portable-guider-loading-message-text'>Loading Next Guider</span><img src='"+theContext.getApp()+"/pg/"+theContext.getVersion()+"/img/progress.gif'/></div>";
  }
  function renderError(e){
    var html = "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].hideViewer();' title='Close the guider' style='float: right'><div id='portable-guider-frame-close' >X</div></a>";
    html += "<div id='portable-guider-error-display'>";
    html += "An error has occurred: "+e.name + ", " + e.message;
    html += "</div>";
    thediv.setHTML(html);
  }
  function renderVideoPage(node){
    var video_url = node.getURL()
    var youtube_id = video_url.replace(/^[^v]+v.(.{11}).*/,"$1");
    var html = "";
    html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goBack();' title='Return to the previous question'><span id='portable-guider-enabled-back-Button'>&lt;Back</span></a>";
    html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goToRoot();' title='Return to the root of this guider'><span id='portable-guider-enabled-root-Button'>^top</span></a>";
    html += "<div id='portable-guider-canvas-content'>";
    html += "<div id='portable-guider-title'>"+theStack.theState.getGuider().getTitle()+"</div>";
    html += "<div class='video-div'>";
    html += "<div id='ytplayer_div'>You need Flash player 8+ and JavaScript enabled to view this video.</div>"
    html += "</div>";

    html += "</div>";
    thediv.setHTML(html);
    ig_swfobject.embedSWF(
       'http://www.youtube.com/v/'+youtube_id+'?enablejsapi=1&version=3',
       'ytplayer_div','600','361','8',null,null,
       { allowScriptAccess: 'always',allowFullScreen: 'true'},
       { id: 'ytplayer_object'}
    );
//    var player = document.getElementById("ytplayer_object");
//    player.loadVideoById(youtube_id, 1);

  }
  function renderSolutionPage(node){
    var html = "";
    html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goBack();' title='Return to the previous question'><span id='portable-guider-enabled-back-Button'>&lt;Back</span></a>";
    html += "<a href='javascript:the_portable_guider_map[\""+theBaseGID+"\"].theViewer.goToRoot();' title='Return to the root of this guider'><span id='portable-guider-enabled-root-Button'>^top</span></a>";
    html += "<div id='portable-guider-canvas-content'>";
     html += "<div id='portable-guider-title'>"+theStack.theState.getGuider().getTitle()+"</div>";
    html += node.getContent();

    html += "</div>";
    thediv.setHTML(html);
  }
  function pjnModifyURL(url,affiliate_id,sid,website_id){
    var ret_url = url;
//    if (startsWith(ret_url,"http://www.gopjn.com/t/")){
       var parts = ret_url.split("-");
       parts[2]=affiliate_id;
       ret_url = parts.join("-");
       ret_url += "&sid="+sid;
       ret_url += "&website="+website_id;
//    }
    return ret_url;
  }
  function startsWith(str,target){
    return (str.match("^"+target)==target);
  }
  function getElemsByClassName(className){
			elm = document.getElementById(theCanvasID);
			var classes = className.split(" "),
				classesToCheck = [],
				elements = elm.getElementsByTagName("*"),
				current,
				returnElements = [],
				match;				
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
  }
  function animateFade(id,level,final,completeFxn){
    var elem = document.getElementById(id);
    var currentLevel = level;
    var finalLevel = final;  
    function nextStep(){
      if (finalLevel>currentLevel){
         currentLevel += 10;       
      }
      if (finalLevel<currentLevel){
         currentLevel -= 10;       
      }
      elem.style.opacity = currentLevel/100;
      elem.style.filter = 'alpha(opacity = ' + currentLevel + ')';
      if (finalLevel!=currentLevel){
         setTimeout(nextStep,50);
      } else {
         if (typeof completeFxn == "function"){completeFxn();}
         (finalLevel==100)?elem.style.display='':elem.style.display='none';
      }
    }
    setTimeout(nextStep,50);
  }  
  function startFadeIn(id,completeFxn){
    animateFade(id,0,100,completeFxn);
  }
  function startFadeOut(id,completeFxn){
    animateFade(id,100,0,completeFxn);
  }

}
////////////////////////////////////////////////////////////////////////////////
// Class: PortableGuider.State
////////////////////////////////////////////////////////////////////////////////

PortableGuider.State = function(){
  var theGuider = null;
  var theGID = null;

  var idstack = new Array();
  var path = new Array();
  var theCurrentID = null;


  this.setGuider = function(guider){
    theGuider = guider;
    if (theCurrentID==null){
       this.pushID(theGuider.getRootQuestion().getID());
    }
  }
  this.getGuider = function(){return theGuider;}
  this.setIDs    = function(ids){
    idstack = ids;
    theCurrentID = idstack[idstack.length-1];
  }
  this.getIDs    = function(){return idstack;}
  this.setGID    = function(gid){theGID = gid;}
  this.getGID    = function(){return theGID;}

  /*
     add an element to the path that's reported on a contact now email.  It 
     should be text such as a question or answer text
  */
  this.addPathElement = function(text){
    path.push(text);
  }
  this.getPath = function(){
    var ret="";
    for (var i=path.length-1; i>=0; i--){
        ret = path[i]+'\n'+ret;
        if (ret.length>500)break;
    }
    return ret;
  }
  /*
    Behavior of the pushID/popID methods.

    They don't really work like a stack.  When you push an ID, it becomes the
    the current ID (as returned from getCurrentID()).  If you popID you don't
    get that ID back.  The previous ID becomes the current ID
    and it's that previous ID that's returned.

  */
  this.pushID    = function(id){
    theCurrentID=id;
    idstack.push(theCurrentID);
  }
  this.popID     = function(){
    if (idstack.length<2) return null;
    idstack.length--;
    theCurrentID =idstack[idstack.length-1];
    return theCurrentID;
  }
  this.hasPreviousID = function(){
    return idstack.length>1;
  }
  //setCurrentID is meant for initialization.  Normally you should use pushID
  //because it moves IDs onto the stack
  //this.setCurrentID = function(id){
  //  theCurrentID = id;
  //}
  this.getCurrentID = function(){
    return theCurrentID;
  }
  //The initial question is either the root of the current guider or it's the last
  //page we were on when accoring to the cookies.  We will use the cookie value if
  //there is one, and it's for the current guider.  Otherwise it's just the root
  //of this guider.
  this.getInitialQuestion = function(){

    if (theGuider==null){alert('State.getInitialPage called before setting the guider');}

    //Determine what the initial node should be.  There are three possiblilties
    //  1. if there's a node that leads to the current URL - use that as the starting node.  otherwise
    //  2. if there is a cookie with the previous location, use that.  Otherwise
    //  3. Use the root node.

    var destnode = theGuider.getNodeByDestination(""+window.location);
    var id = (theCurrentID!=null?theCurrentID:"");
    var question = null;

    if (destnode!=null){
       question = theGuider.getParent(destnode);
    } else if (id!="") {
       question = theGuider.getNode(id);
    } else {
       question = theGuider.getRootQuestion();
    }
    return question;
  }

}

////////////////////////////////////////////////////////////////////////////////
// Class: PortableGuider.StateStack
////////////////////////////////////////////////////////////////////////////////
PortableGuider.StateStack = function(basegid){

  var theBaseGID = basegid;
  var theStack = Array();
  var storage = new ClientSideStorage("portableguiderhistory");
  var that = this;

  this.theState = null; //this is the state at the top of the stack

  this.push = function(state){
    theStack.push(state);
    this.theState = state;
  }
  this.pop = function(){
    theStack.length--;
    this.theState = theStack[theStack.length-1];
    return this.theState;
  }
  this.hasPreviousGuider = function(){
    return theStack.length>0;
  }
  /*
    A guider could be pushed to the stack under two circumstances.
    1 - we are going forward to a new guider
    2 - We are going backwards to a guider we didn't have the
        definition for yet.

    So, when a guider is received we check the top of the stack.
    If the top state is for the same guider we just assign the new
    guider to that state.  Otherwise, we create a brand new state.

  */
  this.pushGuider = function(gid,guider){
    if (theStack.length>0 && this.theState.getGID()==gid){
       this.theState.setGuider(guider);
    } else {
       var newState = new PortableGuider.State();
       newState.setGuider(guider);
       newState.setGID(gid);
       this.push(newState);
    }
    this.saveState();
  }
  /*
    Indicates if there is a previous location to go back to.
  */
  this.previousAvailable = function(){
    if (theStack.length>1) return true;
    if (this.theState.hasPreviousID()) return true;
    return false;
  }
  this.saveState = function(){
    var guiderstack = Array();
    var idstack     = Array();
    for (var i=0; i<theStack.length; i++){
        guiderstack.push(theStack[i].getGID());
        var thisIDStack = theStack[i].getIDs();
        for (var j=0; j<thisIDStack.length; j++){
            idstack.push(""+i+":"+thisIDStack[j]); //prefix each id with the guider stack number
        }
    }
    storage.put("basegid",theBaseGID);
    //storage.put("currentid",theStack[theStack.length-1].getCurrentID());
    storage.putArray("guiderstack",guiderstack);
    storage.putArray("idstack",idstack);
    storage.save();
  }

  this.resetHistory = function(){
     theStack = Array();
     this.theState = null;
     state = new PortableGuider.State();
     state.setGID(theBaseGID);
     this.push(state);
  }

  this.alertState = function(){
    var str = "Length: "+theStack.length+"\n";
    for (var i=0; i<theStack.length; i++){
        if (theStack[i].getGuider()!=null){
           str += theStack[i].getGuider().getTitle()+"\n";
        } else {
           str += "no guider.  GID: "+theStack[i].getGID()+"\n";
        }
    }
    alert(str);
  }

  //Try to rebuild the stack from cookies.  Return true if possible,
  //false otherwise
  this.rebuildFromCookies = function(){
    //if the cookie does not exist, there's nothing to rebuild
    var idstack = Array();
    var guiderstack = Array();

    if (!storage.contains("basegid")){
       return false;
    }
    //cookie has data.  See if it's useful.  If the given gid, the base gid
    //is the save as the basegid in the cookie we should be able to use it.
    if (storage.get("basegid")!=theBaseGID){
       return false;
    }
    //OK, the cookie is for this guider.  We should be able to load up the
    //stack
    if (storage.contains("idstack")){
       idstack = storage.getArray("idstack");
    } else return false;
    if (storage.contains("guiderstack")){
       guiderstack = storage.getArray("guiderstack");
    } else return false;

    for (var i=0; i<guiderstack.length; i++){
        var state = new PortableGuider.State();
        state.setGID(guiderstack[i]);
        var ids = Array();
        for (var j=0; j<idstack.length; j++){
            if (idstack[j].substr(0,2)==(""+i+":")){
               //if the id is prefixed by this gid index
               ids.push(idstack[j].substr(2));
            }
        }
        state.setIDs(ids);
        this.push(state);
    }
    //if (storage.contains("currentid")){
    //   this.theState.setCurrentID(storage.get("currentid"));
    //}
    return true;
  }

  //Try to reconstitute the state from cookies
  if (!this.rebuildFromCookies()){
     //we were unable to rebuild from cookies so initialize normally
     var state = new PortableGuider.State();
     state.setGID(theBaseGID);
     this.push(state);
  }

  //this.alertState();



}

var countMsgChars = function(el){
    document.getElementById("pg-comment-length").innerHTML = el.value.length;
};
//provides the public interface to igpg.js
function ig_launch_guider(app,gid,apikey,options){
      var viewer = new PortableGuider.Viewer(gid);
      //viewer.displayInitMsg();
      viewer.buildFrame();
      viewer.display();
      //create auto launch cookie
      var autolaunch = new ClientSideStorage("autolaunch");
      autolaunch.put("autolaunch",gid);
      autolaunch.save();
      var pg = new PortableGuider(app,gid,apikey,viewer,options);
}
function ig_can_autolaunch(gid){
  var autolaunch = new ClientSideStorage("autolaunch");
  if (autolaunch.get("autolaunch")==gid){
     return true;
  }
  return false;
}


