﻿// Global variables
var MBOT_GLOBAL_BEST_RATE_PATH = 'http://publicservices.mortgagewebcenter.com/BestRates/';
var MBOT_GLOBAL_BEST_RATE_WIDGET_IMAGES_PATH = "/WidgetImages/";
var MBOT_GLOBAL_BEST_RATE_SERVICE = 'Service.aspx';
var MBOT_GLOBAL_AUTHID = '';


//////////////////////////////////////////////////
// Static Class: MbotBestRateLanguagePack
//
// Description: Contains widget text
/////////////////////////////////////////////////
MbotBestRateLanguagePack =
{
  "English": {
    "ImportantNoticesTitle": "Important Notices",
    "ImportantNotices": "Rates are subject to change without notice. We've assumed that you have good credit with a FICO score of 740 or higher. We’ve also assumed that you are looking for a mortgage with standard monthly principal and interest payments and would like to pay zero discount points. If these assumptions aren’t accurate, it could affect the rate and fees quoted. <a href=\"http://www.mortgagemarvel.com/Pages/AdvancedSearch.aspx\" target=\"mbotnew\">Click here</a> for more search options.<br><br>The results presented are sorted from lowest to highest based on rate.  If multiple results have the same rate, the result with the lower closing costs will appear first.  Please <a href=\"http://www.mortgagemarvel.com/Pages/AdvancedSearch.aspx\" target=\"mbotnew\">click here</a> if you are interested in sorting the results in a different way.<br><br>The closing cost amounts shown have been rounded up to the nearest dollar and include discount points, if any.<br><br>The monthly payment amount shown has been rounded up to the nearest dollar and includes principal, interest and mortgage insurance, if required. Your actual monthly payment will be higher if the lender requires or you choose to make monthly installment payments for real estate taxes or homeowners insurance.<br><br>Your APR will vary based on your final loan amount and finance charges.",
    "EmptyResults": "No rates found matching the selected criteria",
    "Loading": "Loading Rates...",
    "TimeStamp": "Rates valid as of ",
    "AdvancedSearch": "Advanced Search",
    "SpecialNotice": "Important Notices",
    "RefreshData": "Refresh Rates",
    "CloseDialog": "Back",
    "UnknownError": "An Unknown Error Has Occurred",
    "ErrorTitle": "Error:",
    "CriteriaError": "The criteria supplied is not valid ",
    "InvalidZip": " The zip code is not valid ",
    "InvalidPropertyValue": "The property value is not valid ",
    "InvalidLoanAmount": "The loan amount is not valid ",
    "TitleRate": "Rate",
    "TitlePoints": "Points",
    "TitleAPR": "APR",
    "TitleFees": "Fees",
    "TitlePayment": "Payment",
    "ZeroPoints": "zero"
  }
}

//////////////////////////////////////////////////
// Class: MbotBestRateCriteria
//
// Description: Contains criteria used to search 
//              for rates from the BestRate service
/////////////////////////////////////////////////
function MbotBestRateCriteria()
{
  this.LoanPurpose = 'Purchase';
  this.LoanAmount = '';
  this.PropertyValue = '';
  this.LoanType = 'Fixed';
  this.LoanTerm = 120;
  this.BalloonLoanTerm = 0;
  this.InitialARMFixedPeriod = 0;
  this.PropertyZipCode = '';
  this.PropertyUse = 'PrimaryResidence';
  this.PropertyType = 'SingleFamily';
  this.PaymentType = 'PrincipalAndInterest';
  this.Points = 'Zero';
  this.FinanceType = 'Conventional';
  this.DocumentationLevel = 'FullDocumentation';
  this.LocationRadius = 0;
  this.InstitutionType = 'All';
  this.Limit = 5;
  this.__ValidationMessage = '';
}

// (bool) MbotBestRateCriteria.Validate
// Performs self evaluation. Returns false if the criteria is not valid 
MbotBestRateCriteria.prototype.Validate = function()
{
  this.__ValidationMessage = '';

  // Validate values
  if (this.PropertyZipCode == '' || this.PropertyZipCode.length < 5)
  {
    this.__ValidationMessage = 'InvalidZip';
    return false;
  }

  if ((this.PropertyValue == '') || (this.__isCurrency(this.PropertyValue) != true))
  {
    this.__ValidationMessage = 'InvalidPropertyValue';
    return false;
  }

  if ((this.LoanAmount == '') || (this.__isCurrency(this.LoanAmount) != true))
  {
    this.__ValidationMessage = 'InvalidLoanAmount';
    return false;
  }

  return true;
}



// (string) MbotBestRateCriteria.GetValidationMessage
// Returns validation error message. You may optionally supply a language pack (english is default)
MbotBestRateCriteria.prototype.GetValidationMessage = function(languagePack)
{
  if (this.__ValidationMessage == '')
  {
    return '';
  }

  var languagePack = MbotBestRateLanguagePack["English"];
  if (typeof language != 'undefined')
  {
    languagePack = MbotBestRateLanguagePack[language];
  }

  return languagePack[this.__ValidationMessage];
}

// (bool) MbotBestRateCriteria.__isCurrency
// Determines if the supplied value is a valid currency value
MbotBestRateCriteria.prototype.__isCurrency = function(data)
{
  var chars = '0123456789$.,';
  for (i = 0; i < data.length; i++)
  {
    if (chars.indexOf(data.charAt(i), 0) == -1) return false;
  }
  return true;
}

//////////////////////////////////////////////////
// Class: MbotBestRateService
//
// Description: Wrapper for BestRate Web Service.
//              Supports async cross domain
//              communication
/////////////////////////////////////////////////
function MbotBestRateService()
{
}

// MbotBestRateService.RequestBestRates
// Calls the service. You must supply an instance of MbotBestRateCriteria and the callback method
MbotBestRateService.prototype.RequestBestRates = function(criteria, onLoadedCallBack)
{
  var headTag = document.getElementsByTagName("head")[0];
  var scriptTag = document.createElement('script');
  scriptTag.type = "text/javascript";
  var url = MBOT_GLOBAL_BEST_RATE_PATH + MBOT_GLOBAL_BEST_RATE_SERVICE;
  url += "?Output=json";
  url += "&JsCallBack=" + onLoadedCallBack;
  url += "&AuthenticationID=" + MBOT_GLOBAL_AUTHID;
  url += "&LoanPurpose=" + criteria.LoanPurpose;
  url += "&LoanAmount=" + criteria.LoanAmount;
  url += "&PropertyValue=" + criteria.PropertyValue;
  url += "&LoanType=" + criteria.LoanType;
  url += "&LoanTerm=" + criteria.LoanTerm;
  url += "&BalloonLoanTerm=" + criteria.BalloonLoanTerm;
  url += "&InitialARMFixedPeriod=" + criteria.InitialARMFixedPeriod;
  url += "&PropertyZipCode=" + criteria.PropertyZipCode;
  url += "&PropertyUse=" + criteria.PropertyUse;
  url += "&PropertyType=" + criteria.PropertyType;
  url += "&PaymentType=" + criteria.PaymentType;
  url += "&Points=" + criteria.Points;
  url += "&FinanceType=" + criteria.FinanceType;
  url += "&DocumentationLevel=" + criteria.DocumentationLevel;
  url += "&LocationRadius=" + criteria.LocationRadius;
  url += "&InstitutionType=" + criteria.InstitutionType;
  url += "&Limit=" + criteria.Limit;
  scriptTag.src = url;
  headTag.appendChild(scriptTag);
}

// MbotBestRateService.RequestPaymentStream
// Calls the service. You must supply an instance of MbotBestRateCriteria and the callback method
MbotBestRateService.prototype.RequestPaymentStream = function(url, onLoadedCallBack)
{
  var headTag = document.getElementsByTagName("head")[0];
  var scriptTag = document.createElement('script');
  scriptTag.type = "text/javascript";
  url += "&Output=json";
  url += "&JsCallBack=" + onLoadedCallBack;
  url += "&AuthenticationID=" + MBOT_GLOBAL_AUTHID;
  scriptTag.src = url;
  headTag.appendChild(scriptTag);
}

//////////////////////////////////////////////////
// Static Class: MbotBestRateWidgetSkin
//
// Description: Contains widget style instructions
/////////////////////////////////////////////////
MbotBestRateWidgetSkin =
{
  "Auto": {}, // Keyword reserved for auto detection based on screen resolution

  "Large-Border":
    {
      // Field values
      "Image_Root": "/WidgetImages/Standard/",

      // Root
      "OuterContainerStyle": "width:100%;padding:0px;margin:0px;width:100%;font-size:9px;font-family: arial, 'lucida console', sans-serif;background-color:White;color:#2e5566;",
      "InnerContainerStyle": "padding-left:25px;padding-right:25px;",
      "LinkColor": "#f69433",

      // Border
      "CornerWidth": "43px",
      "MinWidth": "200px",
      "TopBorderLeftStyle": "width:43px;height:43px;background-image:url('{image_root}/topleft.png');",
      "TopBorderStyle": "height:43px;background-image:url('{image_root}/topmiddle.png');background-repeat:repeat-x;",
      "TopBorderRightStyle": "width:43px;height:43px;background-image:url('{image_root}/topright.png');",
      "MiddleBorderLeftStyle": "background-image:url('{image_root}/left.png');background-repeat:repeat-y;",
      "MiddleBorderRightStyle": "background-image:url('{image_root}/right.png');background-repeat:repeat-y;background-position:top right;",
      "BottomBorderLeftStyle": "width:43px;height:43px;background-image:url('{image_root}/bottomleft.png');",
      "BottomBorderStyle": "height:43px;background-image:url('{image_root}/bottommiddle.png');",
      "BottomBorderRightStyle": "width:43px;height:43px;background-image:url('{image_root}/bottomright.png');",

      // Results
      "ResultsContainerStyle1": "border-top:1px solid #577785;border-bottom:1px solid #577785;background-color:#ffffff;",
      "ResultsContainerStyle2": "border-top:1px solid #577785;border-bottom:1px solid #577785;background-color:#d9eff8;",
      "ResultsGridContainerStyle1": "width:100%;border-top:1px solid #577785;",
      "ResultsGridContainerStyle2": "width:100%;border-top:1px solid #577785;",
      "ResultsTitleStyle1": "padding:1px;border-right:1px solid #577785;font-weight:bold;font-size:10px;color:#2e5566;text-align:center;",
      "ResultsTitleStyle2": "padding:1px;border-right:1px solid #577785;font-weight:bold;font-size:10px;color:#2e5566;text-align:center;",
      "ResultsItemStyle1": "padding:1px;border-top:1px solid #577785;border-right:1px solid #577785;font-size:10px;color:#2e5566;text-align:center;",
      "ResultsItemStyle2": "padding:1px;border-top:1px solid #577785;border-right:1px solid #577785;font-size:10px;color:#2e5566;text-align:center;",
      "PartnerStyle1": "text-align:center;padding-left:3px;padding-right:3px;padding-top:8px;padding-bottom:3px;",
      "PartnerLinkStyle1": "font-size:14px;font-weight:bold;color:#2e5566;text-align:center;text-decoration:none;",
      "PartnerStyle2": "text-align:center;padding-left:3px;padding-right:3px;padding-top:8px;padding-bottom:3px;",
      "PartnerLinkStyle2": "font-size:14px;font-weight:bold;color:#2e5566;text-align:center;text-decoration:none;",
      "ProductStyle1": "font-size:12px;font-weight:bold;color:#2e5566;text-align:center;padding-bottom:3px;",
      "ProductStyle2": "font-size:12px;font-weight:bold;color:#2e5566;text-align:center;padding-bottom:3px;",
      "ApplyImage": "{image_root}/apply.png",
      "AprLinkStyle1": "color:#2e5566;text-decoration:underline;",
      "AprLinkStyle2": "color:#2e5566;text-decoration:underline;",

      // Header
      "HeaderStyle": "text-align:center;padding-bottom:14px;",
      "TitleImage": "",
      "LogoImage": "{image_root}/marvelLogo.png",

      // Dialog
      "DialogStyle": "text-align:center;",
      "DialogTitleStyle": "padding-top:10px;text-align:center;color:#2e5566;font-size:14px;font-weight:bold;",
      "DialogSmallContentStyle": "word-wrap:break-word;height:30px;padding:5px;font-size:12px;color:#f69433;text-align:center;",
      "DialogLargeContentStyle": "text-align:left;padding:15px;font-size:10px;color:#f2e5566;",
      "CloseDialogLinkStyle": "color:#2e5566;text-decoration:underline;font-weight:bold;",
      "CloseDialogStyle": "padding-right:15px;padding-top:5px;font-size:11px;",

      // Footer
      "TimeStampStyle": "font-size:10px;color:#2e5566;padding-top:4px;text-align:center;",
      "RefreshDataStyle": "text-align:center;padding-bottom:5px;",
      "RefreshDataLinkStyle": "font-size:10px;color:#2e5566;text-decoration:underline;",
      "SpecialNoticeStyle": "padding:1px;",
      "SpecialNoticeLinkStyle": "font-size:9px;color:#2e5566;text-decoration:underline;font-weight:bold;",
      "AdvancedSearchStyle": "padding:4px;",
      "AdvancedSearchLinkStyle": "font-size:9px;color:#2e5566;text-decoration:underline;font-weight:bold;",

      // Behavior
      "ShowTimeStamp": true
    },


  "Simple":
    {
      "Inherits": "Large-Border",
      "CornerWidth": "0px",
      "MinWidth": "0px",
      "TopBorderLeftStyle": "display:none;",
      "TopBorderStyle": "display:none;",
      "TopBorderRightStyle": "display:none;",
      "MiddleBorderLeftStyle": "",
      "MiddleBorderRightStyle": "",
      "BottomBorderLeftStyle": "display:none;",
      "BottomBorderStyle": "display:none;",
      "BottomBorderRightStyle": "display:none;",
      "InnerContainerStyle": "padding-left:0px;padding-right:0px;",
      "ResultsTitleStyle1": "padding:1px;font-size:9px;color:#2e5566;text-align:center;",
      "ResultsTitleStyle2": "padding:1px;font-size:9px;color:#2e5566;text-align:center;",
      "PartnerLinkStyle1": "font-size:12px;font-weight:bold;color:#2e5566;text-align:center;text-decoration:none;",
      "PartnerLinkStyle2": "font-size:12px;font-weight:bold;color:#2e5566;text-align:center;text-decoration:none;",
      "ResultsItemStyle1": "padding:0px;font-size:9px;color:#2e5566;text-align:center;",
      "ResultsItemStyle2": "padding:0px;font-size:9px;color:#2e5566;text-align:center;",
      "ResultsGridContainerStyle1": "padding-bottom:5px;width:100%;",
      "ResultsGridContainerStyle2": "padding-bottom:5px;width:100%;",
      "TitleImage": ""
    },

  // The following are aliases used by the google widget

  "Option1":
    { "Inherits": "Simple" },

  "Option2":
    { "Inherits": "Large-Border" },

  // (object) MbotBestRateWidgetSkin.CreateSkin: 
  // This method create an cloned instance of a skin, and updates fields with
  // the full image paths (must be done when instantiated, otherwise global overrides
  // to MBOT_GLOBAL_BEST_RATE_PATH will not be applied 
  "CreateSkin": function(name)
  {
    var imagePath = MBOT_GLOBAL_BEST_RATE_PATH + MbotBestRateWidgetSkin[name]["Image_Root"];
    var skinInstance = new Object();

    if (typeof MbotBestRateWidgetSkin[name]["Inherits"] != 'undefined')
    {
      skinInstance = MbotBestRateWidgetSkin.CreateSkin(MbotBestRateWidgetSkin[name]["Inherits"]);
    }

    for (var i in MbotBestRateWidgetSkin[name])

      if (typeof MbotBestRateWidgetSkin[name][i] == "string")
    {
      skinInstance[i] = MbotBestRateWidgetSkin[name][i].replace('{image_root}', imagePath);
    }
    else
    {
      skinInstance[i] = MbotBestRateWidgetSkin[name][i];
    }

    return skinInstance;
  },

  // (string) MbotBestRateWidgetSkin.AutoSelect:
  // Chooses the default skin if no skin has been provided
  "AutoSelect": function()
  {
    return "Simple";
  }
}


/////////////////////////////////////////////////////////////////////////
// Class: MbotBestRateWidget
//
// Description: Widget which displays top rates
//
// Ctor: contentElement - The element in which the widget will render to
//       skin - the name of the skin to intialize the widget with
//
/////////////////////////////////////////////////////////////////////////
function MbotBestRateWidget(contentElement, skin)
{
  // private fields
  this.__ContentElement = contentElement;
  this.__Status = 0; // 0 = initialized  1 = searching  2 = data loaded   

  // public fields                       
  this.Id = MbotBestRateWidgetInstanceManager.RegisterInstance(this);
  this.ShowErrors = false;
  this.LanguagePack = MbotBestRateLanguagePack["English"];
  this.onContentChange = "";

  // initialize skin. If no skin has been selected, automaticall detect
  // a default skin
  if (typeof skin != "undefined" & skin != 'Auto')
  {
    this.Skin = MbotBestRateWidgetSkin.CreateSkin(skin);
  }
  else
  {
    this.Skin = MbotBestRateWidgetSkin.CreateSkin(MbotBestRateWidgetSkin.AutoSelect());
  }
}

// (void) MbotBestRateWidget.StartSearch
// Causes the widget to begin searching based on the supplied criteria (instance of MbotBestRateCriteria)
MbotBestRateWidget.prototype.StartSearch = function(criteria)
{

  // if a search is already running, ignore this request
  if (this.WidgetStatus == 1)
  {
    return;
  }

  // validate criteria first
  if (criteria.Validate() != true)
  {
    // invalid search criteria: display message
    this.RenderDialog(this.LanguagePack["ErrorTitle"], this.LanguagePack["CriteriaError"] + "<br>" + criteria.GetValidationMessage(this.LanguagePack), '');
    return;
  }

  // Kick off the search
  this.SearchCriteria = criteria;
  this.__Status = 1;
  this.Render();
  var service = new MbotBestRateService();
  service.RequestBestRates(this.SearchCriteria, MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, 'LoadData'));
}

// (void) MbotBestRateWidget.StartLastSearch
// Attempts to search using the last set of criteria.
MbotBestRateWidget.prototype.StartLastSearch = function()
{
  if (typeof this.SearchCriteria != "undefined")
  {
    this.StartSearch(this.SearchCriteria);
  }
}

// (void) MbotBestRateWidget.Render
// Renders the widget out to the user in its current state. 
MbotBestRateWidget.prototype.Render = function()
{
  var outputBuilder = '';

  // If a search has not yet been initiated, dont do anything
  if (this.__Status == 0)
  {
    return;
  }

  // If the data has not been loaded yet, display loading message
  if (this.__Status == 1)
  {
    this.RenderDialog('', this.LanguagePack["Loading"], '');
    return;
  }

  // If an error was returned and show errors is enabled, display the error
  if (typeof this.Data.Error != "undefined")
  {
    if (this.ShowErrors)
    {
      this.RenderDialog(this.LanguagePack["ErrorTitle"], this.Data.Error, '');
      return;
    }
    else
    {
      this.RenderDialog(this.LanguagePack["ErrorTitle"], this.LanguagePack["UnknownError"], '');
      return;
    }
  }

  // If data has been loaded, but no results were found , display a message 
  if (this.Data.RateCount < 1)
  {
    outputBuilder = this.RenderDialog('', this.LanguagePack["EmptyResults"], '');
    return;
  }

  // We have data, so display it
  outputBuilder += this.__BuildResultsHtml();
  this.__UpdateContentElement(outputBuilder);
}

// (void) MbotBestRateWidget.RenderDialog
// Renders a message within the widget framework
// dialogTitle: An optional title for the dialog 
// dialogContent: Html to display within the dialog
// onCloseCallBack: An optional function name to call when the user closes the dialog
//                 (if you do not supply a callback, the close dialog link will not be displayed)
MbotBestRateWidget.prototype.RenderDialog = function(dialogTitle, dialogContent, onCloseCallBack)
{

  var outputBuilder = "<div style=\"" + this.Skin["DialogStyle"] + "\">";

  if (dialogTitle != '')
  {
    outputBuilder += "<div style=\"" + this.Skin["DialogTitleStyle"] + "\">";
    outputBuilder += dialogTitle;
    outputBuilder += "</div>";
  }

  var contentStyle = '';

  if (dialogContent.length < 100)
  {
    contentStyle = "DialogSmallContentStyle";
  }
  else
  {
    contentStyle = "DialogLargeContentStyle";
  }

  outputBuilder += "<div style=\"" + this.Skin[contentStyle] + "\">";
  outputBuilder += dialogContent;
  outputBuilder += "</div>";

  if (onCloseCallBack != '')
  {
    outputBuilder += "<div style=\"" + this.Skin["CloseDialogStyle"] + "\">";

    var closeLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, onCloseCallBack);
    outputBuilder += this.__BuildRolloverLink(closeLink, this.LanguagePack["CloseDialog"], this.Skin["CloseDialogLinkStyle"]);
  }

  outputBuilder += "</div>";

  this.__UpdateContentElement(outputBuilder);
}

///////////////////////////////////////
// (void) MbotBestRateWidget.DisplayImportantNotices
// Renders the important notices dialog
MbotBestRateWidget.prototype.DisplayImportantNotices = function()
{
  var title = this.LanguagePack["ImportantNoticesTitle"];
  var content = this.LanguagePack["ImportantNotices"];
  this.RenderDialog(title, content, "Render();");
}

// (void) MbotBestRateWidget.DisplayPaymentStream
// Renders the live payment stream dialog
MbotBestRateWidget.prototype.DisplayPaymentStream = function(index)
{
  var url = this.Data.Rate[index].PaymentStreamUrl;
  var productName = this.Data.Rate[index].ProductName
  var service = new MbotBestRateService();

  service.RequestPaymentStream(url, MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, "LoadPaymentStreamData(\"" + productName + "\","));
}

// (void) MbotBestRateWidget.LoadPaymentStreamData
// Used as a call back method on PaymentStream service. Loads the payment stream data into the widget and displays
// data: the json data response from PaymentStream service to load
MbotBestRateWidget.prototype.LoadPaymentStreamData = function(productName, data)
{
  var title = productName;
  //var content = this.__MakeWrapSafe(data.LivePaymentStream.PaymentStreamUrl, 40);
  var content = data.LivePaymentStream.PaymentStreamUrl;
  this.RenderDialog(title, content, "Render();");
}

// (void) MbotBestRateWidget.LoadData
// Used as a call back method on BestRates service. Loads the data into the widget and displays
// data: the json data response from BestRates service to load
MbotBestRateWidget.prototype.LoadData = function(data)
{
  this.Data = data.BestRateResults;
  this.__Status = 2;
  this.Render();
}

// (void) MbotBestRateWidget.__UpdateContentElement
// Updates the content element with the supplied HTML content
MbotBestRateWidget.prototype.__UpdateContentElement = function(html)
{
  document.getElementById(this.__ContentElement).innerHTML = this.__BuildContainerHtml(html);

  if (this.onContentChange != "")
  {
    eval(this.onContentChange);
  }
}

// (string) MbotBestRateWidget.__BuildRolloverLink
// Since this widget uses inline styles heavily we cant assign link rollover
// effects via css. This workaround uses a javascript DOM manipulation technique to emulate the effect.
// Returns link html with rollover affect applied.
// link: the href
// text: link text
// style: the inline style to use
// target: optional link target
MbotBestRateWidget.prototype.__BuildRolloverLink = function(link, text, style, target)
{
  var output = "<a href=\"";
  output += link;
  output += "\" ";

  if (typeof target != 'undefined')
  {
    output += " target=\"";
    output += target;
    output += "\" ";
  }

  output += " style=\"";
  output += style;
  output += "\" ";
  output += " onmouseover=\"this.normalcolor=this.style.color;this.style.color='";
  output += this.Skin["LinkColor"];
  output += "'\" ";
  output += " onmouseout=\"this.style.color=this.normalcolor;\"";
  output += ">";
  output += text;
  output += "</a>";
  return output;
}

// (string) MbotBestRateWidget.__MakeWrapSafe
// This function will insert break tags within words that
// exceed the maxChars. It should be html safe, so html attributes
// should be excluded from the scan.
MbotBestRateWidget.prototype.__MakeWrapSafe = function(text, maxChars)
{
  var returnVal = "";
  var wordLength = 0;
  var inString = false;

  for (index = 0; index < text.length; index++)
  {
    if (text.charAt(index) == '"')
    {
      if (inString != true)
      {
        inString = true;
      }
      else
      {
        inString = false;
        wordLength = 0;
      }
    }

    if (text.charAt(index) == '<' || text.charAt(index) == '>')
    {
      wordLength = 0;
    }

    if (text.charAt(index) != ' ')
    {
      wordLength++;
    }
    else
    {
      wordLength = 0;
    }

    if (!inString && wordLength >= maxChars)
    {
      returnVal += "<br>";
      wordLength = 0;
    }

    returnVal += text.charAt(index);
  }

  return returnVal;
}

// (string) MbotBestRateWidget.__BuildResultsHtml
// Generates html based on the widgets current data array.
// Returns the formatted html
MbotBestRateWidget.prototype.__BuildResultsHtml = function()
{
  var styleIndex = 1;
  var outputBuilder = "";

  // Build data items
  for (index = 0; index < this.Data.Rate.length; index++)
  {
    outputBuilder += this.__BuildResultItemHtml(index, styleIndex);

    styleIndex++;
    if (styleIndex > 2)
    {
      styleIndex = 1;
    }
  }

  //Build timestamp
  if (this.Skin["ShowTimeStamp"] == true)
  {
    outputBuilder += "<div style=\"" + this.Skin["TimeStampStyle"] + "\">";
    outputBuilder += this.LanguagePack["TimeStamp"];
    outputBuilder += this.Data.TimeStamp + " CST ";
    outputBuilder += "</div>";
  }

  //Build refresh link
  outputBuilder += "<div style=\"" + this.Skin["RefreshDataStyle"] + "\">";

  var refreshLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, 'StartLastSearch') + "();\"";
  outputBuilder += this.__BuildRolloverLink(refreshLink, this.LanguagePack["RefreshData"], this.Skin["RefreshDataLinkStyle"]);

  outputBuilder += "</div>";


  // build important notice and advanced search links
  outputBuilder += "<div style=\"padding:0px;margin:0px;width:100%;\">";
  outputBuilder += "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" >";
  outputBuilder += "<tr><td align=\"left\">";
  outputBuilder += "<div style=\"" + this.Skin["SpecialNoticeStyle"] + "\">";

  var noticeLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, "DisplayImportantNotices" + "();");
  outputBuilder += this.__BuildRolloverLink(noticeLink, this.LanguagePack["SpecialNotice"], this.Skin["SpecialNoticeLinkStyle"]);

  outputBuilder += "</div>";
  outputBuilder += "</td><td align=\"right\">";
  outputBuilder += "<div style=\"" + this.Skin["AdvancedSearchStyle"] + "\">";

  var advancedLink = "http://www.mortgagemarvel.com/Pages/AdvancedSearch.aspx";
  outputBuilder += this.__BuildRolloverLink(advancedLink, this.LanguagePack["AdvancedSearch"], this.Skin["AdvancedSearchLinkStyle"], "mbotnew");

  outputBuilder += "</div>";
  outputBuilder += "</td></tr></table>";
  outputBuilder += "</div>";

  return outputBuilder;
}


// (string) MbotBestRateWidget.__BuildResultItemHtml
// Generates html for a specific data item (Rate).
// Returns the formatted html
MbotBestRateWidget.prototype.__BuildResultItemHtml = function(itemIndex, styleIndex)
{
  var outputBuilder = "";
  var item = this.Data.Rate[index];

  outputBuilder += "<div style=\"" + this.Skin["ResultsContainerStyle" + styleIndex] + "\">";
  // build partner and product title
  outputBuilder += "<div style=\"" + this.Skin["PartnerStyle" + styleIndex] + "\">";
  outputBuilder += this.__BuildRolloverLink(item.HomePageUrl, item.InstitutionName, this.Skin["PartnerLinkStyle" + styleIndex], "mbotnew");
  outputBuilder += "</div>";
  outputBuilder += "<div style=\"" + this.Skin["ProductStyle" + styleIndex] + "\">";
  outputBuilder += item.ProductName;
  outputBuilder += "</div>";


  // build data container 
  outputBuilder += "<div style=\"" + this.Skin["ResultsGridContainerStyle" + styleIndex] + "\">";
  outputBuilder += "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"  >";
  outputBuilder += "<tr><td>";
  // build data
  outputBuilder += "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" >";
  outputBuilder += "<tr>";
  outputBuilder += "<td><div style=\"" + this.Skin["ResultsTitleStyle" + styleIndex] + "\">" + this.LanguagePack["TitleRate"] + "</div></td>";
  outputBuilder += "<td><div style=\"" + this.Skin["ResultsTitleStyle" + styleIndex] + "\">" + this.LanguagePack["TitlePoints"] + "</div></td>";
  outputBuilder += "<td><div style=\"" + this.Skin["ResultsTitleStyle" + styleIndex] + "\">" + this.LanguagePack["TitleAPR"] + "</div></td>";
  outputBuilder += "<td><div style=\"" + this.Skin["ResultsTitleStyle" + styleIndex] + "\">" + this.LanguagePack["TitleFees"] + "</div></td>";
  outputBuilder += "<td><div style=\"" + this.Skin["ResultsTitleStyle" + styleIndex] + "\">" + this.LanguagePack["TitlePayment"] + "</div></td>";
  outputBuilder += "</tr><tr>";

  outputBuilder += "<td><div style=\"" + this.Skin["ResultsItemStyle" + styleIndex] + "\">";
  var rateLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, 'DisplayPaymentStream') + "(" + itemIndex + ");\""
  outputBuilder += this.__BuildRolloverLink(rateLink, item.InterestRate.toPrecision(4) + "%", this.Skin["AprLinkStyle" + styleIndex]);
  outputBuilder += "</div></td>";

  outputBuilder += "<td><div style=\"" + this.Skin["ResultsItemStyle" + styleIndex] + "\">"
  outputBuilder += item.Points.toPrecision(4) + "%";
  outputBuilder += "</div></td>";

  outputBuilder += "<td><div style=\"" + this.Skin["ResultsItemStyle" + styleIndex] + "\">";
  var aprLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, 'DisplayPaymentStream') + "(" + itemIndex + ");\""
  outputBuilder += this.__BuildRolloverLink(aprLink, item.APR.toPrecision(4) + "%", this.Skin["AprLinkStyle" + styleIndex]);
  outputBuilder += "</div></td>";

  outputBuilder += "<td><div style=\"" + this.Skin["ResultsItemStyle" + styleIndex] + "\">$" + Math.round(item.TotalClosingFees) + "</div></td>";

  outputBuilder += "<td><div style=\"" + this.Skin["ResultsItemStyle" + styleIndex] + "\">";
  var paymentLink = "javascript:" + MbotBestRateWidgetInstanceManager.GenerateCallBack(this.Id, 'DisplayPaymentStream') + "(" + itemIndex + ");\""
  outputBuilder += this.__BuildRolloverLink(paymentLink, "$" + Math.round(item.TotalPaymentWithoutEscrow), this.Skin["AprLinkStyle" + styleIndex]);
  outputBuilder += "</div></td>";

  outputBuilder += "</tr>";
  outputBuilder += "</table>";
  // add app link
  outputBuilder += "</td><td align=\"center\" valign=\"middle\">";
  outputBuilder += "<a href=\"" + item.ApplyNowUrl + "\" target=\"mbotnew\"><img src=\"" + this.Skin["ApplyImage"] + "\" border=\"0\"></a>";
  // close up container
  outputBuilder += "</td></table>";
  outputBuilder += "</div></div>";

  return outputBuilder;

}


// (string) MbotBestRateWidget.__BuildContainerHtml
// Generates container html around the inner content. Responsable for creating
// elements surrounding the data. 
// innerCOntent: The content to render within the container
// Returns the formatted html
MbotBestRateWidget.prototype.__BuildContainerHtml = function(innerContent)
{
  var outputBuilder = "<div style=\"" + this.Skin.OuterContainerStyle + "\">";

  //Build top border
  outputBuilder += "<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" >";
  outputBuilder += "<tr>";
  outputBuilder += "<td style=\"width:" + this.Skin["CornerWidth"] + ";\">";
  outputBuilder += "<div style=\"" + this.Skin["TopBorderLeftStyle"] + "\">&nbsp;</div>";
  outputBuilder += "</td>";
  outputBuilder += "<td>";
  outputBuilder += "<div style=\"" + this.Skin["TopBorderStyle"] + "\">";
  outputBuilder += "<div style=\"width:" + this.Skin["MinWidth"] + "\">&nbsp;</div>";
  outputBuilder += "</div>";
  outputBuilder += "</td>";
  outputBuilder += "<td style=\"width:" + this.Skin["CornerWidth"] + ";\">";
  outputBuilder += "<div style=\"" + this.Skin["TopBorderRightStyle"] + "\">&nbsp;</div>";
  outputBuilder += "</td>";
  outputBuilder += "</tr>";
  outputBuilder += "</table>";

  //Build middle border and inner container
  outputBuilder += "<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" >";
  outputBuilder += "<tr>";
  outputBuilder += "<td>";
  outputBuilder += "<div style=\"" + this.Skin["MiddleBorderLeftStyle"] + "\">";
  outputBuilder += "<div style=\"" + this.Skin["MiddleBorderRightStyle"] + "\">";
  outputBuilder += "<div style=\"" + this.Skin["InnerContainerStyle"] + "\">";

  // add logo and title image
  outputBuilder += "<div style=\"" + this.Skin["HeaderStyle"] + ";\">";
  outputBuilder += "<a style=\"border:none 0px white;\" href=\"http://www.mortgagemarvel.com\" target=\"mbotnew\">";
  outputBuilder += "<img src=\"" + this.Skin["LogoImage"] + "\" border=\"0\">";
  outputBuilder += "</a>";

  if (this.Skin["TitleImage"] != '')
  {
    outputBuilder += "<img src=\"" + this.Skin["TitleImage"] + "\" border=\"0\">";
  }
  outputBuilder += "</div>";


  // End of inner container
  outputBuilder += innerContent;
  outputBuilder += "</div></div></div>";
  outputBuilder += "</td>";
  outputBuilder += "</tr>";
  outputBuilder += "</table>";
  outputBuilder += "</div>";

  //Build bottom border
  outputBuilder += "<table border=\"0\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" >";
  outputBuilder += "<tr>";
  outputBuilder += "<td style=\"width:" + this.Skin["CornerWidth"] + ";\">";
  outputBuilder += "<div style=\"" + this.Skin["BottomBorderLeftStyle"] + "\">&nbsp;</div>";
  outputBuilder += "</td>";
  outputBuilder += "<td>";
  outputBuilder += "<div style=\"" + this.Skin["BottomBorderStyle"] + "\">";
  outputBuilder += "<div style=\"width:" + this.Skin["MinWidth"] + "\">&nbsp;</div>";
  outputBuilder += "</div>";
  outputBuilder += "</td>";
  outputBuilder += "<td style=\"width:" + this.Skin["CornerWidth"] + ";\">";
  outputBuilder += "<div style=\"" + this.Skin["BottomBorderRightStyle"] + "\">&nbsp;</div>";
  outputBuilder += "</td>";
  outputBuilder += "</tr>";

  outputBuilder += "</table>";
  outputBuilder += "</div>";

  return outputBuilder;
}

/////////////////////////////////////////////////////////////////////////
// Static Class: MbotBestRateWidgetInstanceManager
//
// Description: Tracks instances of the widget,
//              and maps callbacks to the correct instance. This allows an instance
//              to generate a call back to itself from external elements and objects
//              such as a link or a json service.
/////////////////////////////////////////////////////////////////////////
MbotBestRateWidgetInstanceManager =
{
  // Contains reference to all instances of the widget
  Instances: new Array(),

  // Called by the ctor of each widget. Assigns its id and adds it to the array
  RegisterInstance: function(instance)
  {
    return MbotBestRateWidgetInstanceManager.Instances.push(instance) - 1;
  },

  // Generates the callback function script to be returned and executed by the service
  GenerateCallBack: function(id, functionName)
  {
    return "MbotBestRateWidgetInstanceManager.Instances[" + id + "]." + functionName;
  }
}
