var the_portable_guider_map = {};
var ig_options = {
  localCSS:    false, //true - don't set the css file because the css is set locally
  startAtRoot: false, //true - ignore history and start at root
  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.
  testDay:     null,  //set to integer to change the day of the month pings are reported as
  versionString: '0.02',
  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_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);
    }
};
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 = $("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(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]; }
  }

  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+"/images/tmp/";
         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);
  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("?"));
    }
    theCxn.ping('guiderlaunch',gid,launchURL,theUserId);
    var guider = new Guider();
    guider.setXML(xml);
    theStack.pushGuider(gid,guider);
    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;}

  
  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 pingCounter = 0;
  var day = null;

  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,gid,qid,uid){
    var d = new Date();
    if (day!=null){d.setDate(day);}
    
    if (theContext.options.trackUsers==false){return;}
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("charset", "utf-8");
    script.setAttribute("src", theContext.pingURL+"?apikey="+theKey+"&uid="+uid+"&operation="+op+"&gslug="+gid+"&qslug="+qid+"&count="+pingCounter+"&timestamp="+d.getTime());
    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){
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("charset", "utf-8");
    script.setAttribute("src", theApp+"/api/0.01/contact/?apikey="+theKey+"&uid="+userid+"&gid="+gid+"&name="+escape(name)+"&email="+email+"&msg="+escape(msg)+"&phone="+escape(phone)+"&method="+escape(method)+"&path="+escape(path)+"&noCacheIE="+new Date().getTime());
    document.getElementsByTagName("head").item(0).appendChild(script);
  }
  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

  //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";
       div.style.fontFamily = "Arial";
       div.style.fontSize = "14px";
    } 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' 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 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);
  }
  this.display = function(){
    if (isDisplayed) return;
    if (typeof(ig_show_guider)=='function'){
       ig_show_guider('portable-guider-frame');
    } else {
       new Fx.Reveal($('portable-guider-frame'),{onComplete:function(){that.displayComplete();}}).reveal();
    }
    isDisplayed = true; 
  }
  this.displayComplete = function(){
    $('portable-guider-frame').makeDraggable({stopPropagation:true});
  }
  this.hide = function(){
    if (!isDisplayed) return;
    theCxn.ping("guiderclose",theCurrentGID,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 {
       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.build(); //reset the div
    //this.render();
    //this.display();
  }

  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",theCurrentGID,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",theCurrentGID,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",theCurrentGID,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',theCurrentGID,encodeURI(answer.getSlug()),theUserId);
    if (answer.getDestination().isQuestion()){
       this.next(answer.getDestination().getID());
    } else if (answer.getDestination().isExternalLink()){
       var tag_params = guider.getTagParams();
       if (guider.linksNewWindow()){
          window.open(answer.getDestination().getURL() + tag_params,"","width=1000,height=600,resizable,location=1,status=1,menubar=1,scrollbars=yes");
       } else {
          if (theContext.options.popUp == false){
             window.location = answer.getDestination().getURL() + tag_params;
          } else {
             window.opener.location = answer.getDestination().getURL() + tag_params;
          }
       }
    } 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.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',theCurrentGID,encodeURI(answer.getSlug()),theUserId);

    var textelem = $("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',theCurrentGID,encodeURI(question.getID()),theUserId);

    var textelem = $("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",theCurrentGID,gid,theUserId);
    theCurrentGID = gid;
  }
  this.showContactForm = function(){
    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();
      theCxn.sendContactInfo(theBaseGID,theUserId,name,email,msg,phone,method,path);
    }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',theCurrentGID,node.getID(),theUserId);

    var guider = theStack.theState.getGuider();
    var html = "";
    var blankimage = null;
    var renderpictures = (guider.hasPictures() && node.hasImages());
    carouselType = renderpictures?"horizontal":"vertical";
    

    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()){
       html += "<div id='portable-guider-header'>";

       if (node.getGuider().headerTypeIsBanner()){
            html += "<img id='portable-guider-banner' src='"+theContext.imageURL+node.getGuider().getBanner()+"' alt='guider banner' />";
       } else {
         var icon      = node.getGuider().getIcon();
         var logo      = node.getGuider().getLogo();
         var introText = node.getGuider().getIntroText();
         var iconURL   = node.getGuider().getCompanyURL();

         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.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 have read and agree to <a href='http://www.iguiders.com/iguidersprivacy-policy' target='new'>iGuiders 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://www.iguiders.com/pg/latest/img/portable.png' /></a></div>";
    html += "</div>";


    thediv.setHTML(html);
    var icons = $$('.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,theCurrentGID,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);
  }

}
////////////////////////////////////////////////////////////////////////////////
// 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){
    $("pg-comment-length").innerHTML = el.value.length;
};