<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Google Finance Portfolios"
    title_url="/finance"
    author="Matt Gundersen (Gundy)"
    author_affiliation="Google Inc."
    author_email="gundy@google.com"
    author_location="Mountain View, CA"
    directory_title="Google Finance Portfolios"
    category="finance"
    thumbnail="/ig/modules/finance_portfolios-thm.png"
    screenshot="/ig/modules/finance_portfolios.png"
    description="Track your stocks and market indices using Google Finance."
    singleton="true">
    <Require feature="dynamic-height"/>
    <Require feature="finance"/>
    <Require feature="finance_internal"/>
    <Require feature="flash"/>
    <Require feature="google-domain"/>
    <Require feature="minimessage"/>
    <Require feature="setprefs"/>
    <Require feature="views"/>
    <Locale messages="finance/finance_content/en_ALL.xml"/>
  </ModulePrefs>
  <UserPref name="contracted" default_value="," datatype="hidden"/>
  <UserPref name="showtotals" datatype="bool"
            display_name="Show portfolio value"/>
  <UserPref name="country" display_name="Country"
            datatype="enum" default_value="USA">
    <EnumValue value="USA" />
    <EnumValue value="Australia" />
    <EnumValue value="Canada" />
    <EnumValue value="New Zealand" />
    <EnumValue value="UK" />
  </UserPref>
  <UserPref name="chartLinkPromo" datatype="hidden" default_value="0" />
  <UserPref name="displayVolume"
            datatype="bool"
            default_value="false"
            display_name="Display volume on chart"/>
  <UserPref name="displayExtendedHours"
            datatype="bool"
            default_value="false"
            display_name="Display extended hours on chart"/>
  <UserPref name="defaultZoomDays"
            datatype="string"
            default_value="3"
            display_name="Default displayed days on chart"/>
  <UserPref name="numvisible"
            display_name="Num visible news items"
            datatype="enum"
            default_value="3">
    <EnumValue value="1" display_value="1"/>
    <EnumValue value="2" display_value="2"/>
    <EnumValue value="3" display_value="3"/>
    <EnumValue value="4" display_value="4"/>
    <EnumValue value="5" display_value="5"/>
    <EnumValue value="6" display_value="6"/>
    <EnumValue value="7" display_value="7"/>
    <EnumValue value="8" display_value="8"/>
    <EnumValue value="9" display_value="9"/>
  </UserPref>
  <UserPref name="history"
            default_value=""
            datatype="hidden"/>
  <Content type="html">
    <![CDATA[

<style type="text/css">
  .GF__MODULE_ID__showhidespan { color: #7777CC; text-decoration:underline; cursor:pointer; cursor:hand; }
  .GF__MODULE_ID__edit { color: #7777CC; text-decoration:underline }
  #GF__MODULE_ID__mkt { margin-top: 2px; }
  #GF__MODULE_ID__Main { overflow:hidden; width:100%; }
  #GF__MODULE_ID__Main td { font:arial,sans-serif; }
  .GF__MODULE_ID__Block { margin-bottom: 0.25em;}
  .GF__MODULE_ID__Table {padding:0 1px 0 1px;margin:0;}
  .GF__MODULE_ID__Table td {white-space: nowrap; padding:2px}
  .GF__MODULE_ID__green {color:#008000;}
  .GF__MODULE_ID__red {color:#AA0033;}
  .GF__MODULE_ID__grey {color:#676767;}
  .GF__MODULE_ID__greybg {background-color:#F7F7F7;}
 tr.GF__MODULE_ID__selected {outline:1px solid #66C}
  .GF__MODULE_ID__line td {border-top:1px solid #DDDDDD;}
  .GF__MODULE_ID__hide .GF__MODULE_ID__hideable {display:none}
  .GF__MODULE_ID__show .GF__MODULE_ID__hideable {display:auto}
</style>

<div id="GF__MODULE_ID__mkt"></div>
<div id="GF__MODULE_ID__Main"></div>

<script type="text/javascript">

// The igoogle finance module makes 2 calls to the finance server:
// 1) To get JSON rep of Market indices info
// 2) To get XML rep of User-related portfolio info
// The Finance server identifies the user through the gaia account and the
// cookies

// Note: "contracted" is a hidden userpref which initially contains "," and is
// a comma separated list of portfolios.
// When the user contracts a portfolio - it appends the pid of the portfolio.
// For instance, if the user contracted the first portfolio, "contracted"
// becomes ",1," and when he expands it - it again becomes "," This makes the
// expansion and contraction of portfolios sticky.
// Also when market indices is contracted "contracted" becomes ",m,"

var GF__MODULE_ID__ = {

  port  : null, // 2D array of companies per portfolio
  ports : null, // Contains metadata about portfolios (pid,name,net-profit etc)
  userhash : "",  // hash of the user for add requests
  sentPost : false,
  mhtml : "", // Contains the html of the market indices table
  mdone : false,
  phtml : "", // Contains html of the portfolios
  pdone : false,

  // id to name mapping for Market indices
  indexName : new Object({
    '.AORD' : 'All Ordinaries',
    '.AXJO' : 'S&amp;P/ASX&nbsp;200',
    '.DJI' : 'Dow',
    '.FTAS' : 'FTSE&nbsp;All&nbsp;Shares',
    '.FTSE' : 'FTSE&nbsp;100',
    '.GSPTSE' : 'S&amp;P&nbsp;TSX',
    '.IXIC' : 'Nasdaq',
    '.INX' : 'S&amp;P&nbsp;500',
    '.NZ50' : 'NZX&nbsp;50',
    '.NZ15' : 'NZX&nbsp;15',
    '.NZC50' : 'NZX&nbsp;50&nbsp;Portfolio' }),

  // UserPrefs
  prefs: new _IG_Prefs(__MODULE_ID__),

  getMarketIndexList : function() {
    var country = GF__MODULE_ID__.prefs.getString("country");
    var marketIndices;
    switch (country) {
      case 'USA':
        marketIndices = [
          '.DJI', // Dow
          '.IXIC', // Nasdaq
          '.INX' // S&P 500
        ];
        break;

      case 'Australia':
        marketIndices = [
          'INDEXASX:.AORD', // All Ordinaries
          'INDEXASX:.AXJO', // S&P/ASX 200
          '.DJI', // Dow
          '.IXIC' // Nasdaq
        ];
        break;

      case 'Canada':
        marketIndices = [
          'TSE:OSPTX', // S&P TSX
          '.DJI', // Dow
          '.IXIC', // Nasdaq
          '.INX' // S&P 500
        ];
        break;

      case 'New Zealand':
        marketIndices = [
          'NZE:.NZ50', // NZX 50 Index
          'NZE:.NZ15', // NZX 15 Index
          'NZE:.NZC50' // NZX 50 Portfolio Index
        ];
        break;

      case 'UK':
        marketIndices = [
          'INDEXFTSE:.FTSE', // FTSE 100
          'INDEXFTSE:.FTAS' // FTSE All Shares
        ];
        break;

      default:
    }
    return marketIndices;
  },

  init : function() {
    var is_first_load = true;
    if (GF__MODULE_ID__.pdone) {
      is_first_load = false;
    }
    GF__MODULE_ID__.port = new Array();
    GF__MODULE_ID__.ports = new Array();
    GF__MODULE_ID__.sentPost = false;
    GF__MODULE_ID__.mhtml = "";
    GF__MODULE_ID__.phtml = "";
    GF__MODULE_ID__.mdone = false;
    GF__MODULE_ID__.pdone = false;

    // Call to fetch the JSON market indices info -
    // handleMarketIndices() is invoked and JSON object passed to it
    var request = "/finance/info?client=ig&q=";
    var marketIndices = GF__MODULE_ID__.getMarketIndexList();
    request += marketIndices.join(",");
    request += "&zx=" + (new Date).getTime();
    _sendx(request, GF__MODULE_ID__.handleMarketIndices, false);

    // Call to fetch XML Portfolio from Google Finance
    var url = "/finance/portfolio?" +
              "client=ig&action=view&output=xml&zx=" + (new Date).getTime();
    if (!is_first_load) url += "&auto=1";
    _sendx(url, GF__MODULE_ID__.handlePortfolios, true);
    setTimeout("GF__MODULE_ID__.init()", 240 * 1000);
  },

  html_escape : function(str) {
    return str.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
  },

  // Function to parse and display the XML DOM structure
  handlePortfolios : function(xmldoc) {
    if (typeof(xmldoc) != "object") {
      // Server's response is not an object
      GF__MODULE_ID__.phtml = "<div><font size=-1><i>Information is temporarily unavailable.</i></font></div>";
    } else {
      var portfolios = xmldoc.getElementsByTagName("Portfolio");

      if (portfolios.length == 0) {
        // User not signed in - show login slide
        GF__MODULE_ID__.phtml = "<br><font size=\"-1\">You can use Google Finance to maintain your portfolios. <a target=\"_blank\" href=\""
             + "http://www.google.com/accounts/ServiceLogin?hl=en&service=finance&nui=1&continue="
             + "http://www.google.com/ig\">Sign in</a>.<br><br>"
      } else {
        GF__MODULE_ID__.userhash = xmldoc.getElementsByTagName("HashValue")[0].firstChild.nodeValue;
        // Processing Portfolios
        for (var k=0; k < portfolios.length; k++)
        {
          var net_current_value_ = null, net_investment_ = null, net_profit_ = null;
          if (portfolios[k].getElementsByTagName("NetCurrentValue")[0].firstChild != null)
            net_current_value_ = portfolios[k].getElementsByTagName("NetCurrentValue")[0].firstChild.nodeValue;

          if (portfolios[k].getElementsByTagName("NetInvestment")[0].firstChild != null)
            net_investment_ = portfolios[k].getElementsByTagName("NetInvestment")[0].firstChild.nodeValue;

          if (portfolios[k].getElementsByTagName("NetProfit")[0].firstChild != null)
            net_profit_ = portfolios[k].getElementsByTagName("NetProfit")[0].firstChild.nodeValue;

          var p = {
            pname : GF__MODULE_ID__.html_escape(portfolios[k].getElementsByTagName("Name")[0].firstChild.nodeValue),
            pid : portfolios[k].getElementsByTagName("ID")[0].firstChild.nodeValue,
            net_current_value : net_current_value_,
            net_investment : net_investment_,
            net_profit : net_profit_
          };

          // Update the portfolios array
          GF__MODULE_ID__.ports.push(p);

          var companies = portfolios[k].getElementsByTagName("Item");
          GF__MODULE_ID__.port[k] = new Array();
          if (companies.length > 0) {
            // Processing Companies

            for (var i=0; i<companies.length;i++) {
              var symbol_ = null, exchange_ = null, last_ = null, change_ = null, perc_change_ = null, market_cap_ = null, type_ = null, profit_ = null;
              var ecn_last_ = null, ecn_change_ = null, ecn_perc_change_ = null, total_holdings_ = null;

              if (!companies[i].getElementsByTagName("Symbol") ||
                  companies[i].getElementsByTagName("Symbol").length == 0 ||
                  !companies[i].getElementsByTagName("Symbol")[0].firstChild) {
                continue;
              }
              var full_ticker = companies[i].getElementsByTagName("Symbol")[0].firstChild.nodeValue;
              var colon = full_ticker.indexOf(':');
              if (colon == -1) {
                symbol_ = full_ticker;
              } else {
                exchange_ = full_ticker.substring(0, colon);
                symbol_ = full_ticker.substring(colon + 1);
              }

              if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("LastPrice")[0].firstChild != null)
                last_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("LastPrice")[0].firstChild.nodeValue;

              if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ChangeToday")[0].firstChild != null)
                change_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ChangeToday")[0].firstChild.nodeValue;

              if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("PercentageChange")[0].firstChild != null)
                perc_change_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("PercentageChange")[0].firstChild.nodeValue;


              if (companies[i].getElementsByTagName("Type")[0].firstChild != null)
                type_ = companies[i].getElementsByTagName("Type")[0].firstChild.nodeValue;

              if (type_ == "Company") {
                if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNLast")[0].firstChild != null)
                  ecn_last_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNLast")[0].firstChild.nodeValue;

                if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNChange")[0].firstChild != null)
                  ecn_change_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNChange")[0].firstChild.nodeValue;

                if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNPercentageChange")[0].firstChild != null)
                  ecn_perc_change_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("ECNPercentageChange")[0].firstChild.nodeValue;

                if (companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("MarketCap")[0].firstChild != null)
                  market_cap_ = companies[i].getElementsByTagName("StockInfo")[0].getElementsByTagName("MarketCap")[0].firstChild.nodeValue;
              }

              if(companies[i].getElementsByTagName("TotalHoldings")[0].firstChild != null)
                total_holdings_ = companies[i].getElementsByTagName("TotalHoldings")[0].firstChild.nodeValue;

              if(companies[i].getElementsByTagName("Profit")[0].firstChild != null)
                profit_ = companies[i].getElementsByTagName("Profit")[0].firstChild.nodeValue;

              GF__MODULE_ID__.port[k].push({
                cid : companies[i].getElementsByTagName("ID")[0].firstChild.nodeValue,
                cname : companies[i].getElementsByTagName("Name")[0].firstChild.nodeValue,
                symbol : symbol_,
                exchange: exchange_,
                last : last_,
                holdings : total_holdings_,
                profit : profit_,
                change : change_,
                perc_change : perc_change_,
                type : type_,
                ecn_last : ecn_last_,
                ecn_change : ecn_change_,
                ecn_perc_change : ecn_perc_change_,
                market_cap : market_cap_
              });
            }  // End Of Companies Loop
          }  // End of if (companies.length==0)
        }  // End of Portfolios Loop

        // Fills the entire HTML content
        GF__MODULE_ID__.phtml = GF__MODULE_ID__.printOuterTable();

      }  // End of if (portfolios.length==0)
    }

    GF__MODULE_ID__.pdone = true;
    // TODO: clean up the contracted prefs to not have non-existant pids in it
    GF__MODULE_ID__.maybeDisplay();
  },

  isHidden : function(id) {
    if (!id) return false;
    var str = "," + id + ",";
    var contracted = GF__MODULE_ID__.prefs.getString("contracted");
    if (contracted && contracted.search(str) != -1)
      return true;
    return false;
  },

  getShowHideLinkHTML : function(id) {
    var html;
    html = "<span id=showhide_" + id + " onClick=GF__MODULE_ID__.hide(\"" + id + "\") class=\"GF__MODULE_ID__showhidespan\"><font size=\"-1\">";
    if (GF__MODULE_ID__.isHidden(id)) {
      html += "show";
    } else {
      html += "hide";
    }
    html += "</font></span>";
    return html;
  },

  // Function to show or hide a portfolio
  hide : function(id) {
    var box = document.getElementById("box_" + id);
    var span = document.getElementById("showhide_" + id);

    // Refer above for details about contracted
    var contracted = GF__MODULE_ID__.prefs.getString("contracted");
    var str = "";

    if (box.className == "GF__MODULE_ID__hide") {
      // Contracted to Expanded
      // box.style.display = "table";
      box.className = "GF__MODULE_ID__show";
      span.innerHTML = "<font size=-1>hide</font>";
      str = "," + id +",";
      var index = contracted.indexOf(str);
      if(index == contracted.length-3) {
        contracted = contracted.substring(0,index+1);
      } else {
        var part1 = contracted.substring(0,index+1);
        var part2 = contracted.substring(index+3);
        contracted = part1 + part2;
      }
    } else {
      // Expanded to Contracted
      // box.style.display = "none";
      box.className = "GF__MODULE_ID__hide";
      span.innerHTML = "<font size=-1>show</font>";
      str = "," + id + ",";
      if (contracted.search(str) == -1)
        contracted += id + ',';
    }
    GF__MODULE_ID__.prefs.set("contracted",contracted);
  },


  // Forms the HTML content of all tables except the market indices table
  printInnerTable : function(k) {
    var html="", changeColor, gainLoss=0, gainLossPerc=0;

    // HTML of Inner Table
    var pid = GF__MODULE_ID__.ports[k].pid;
    if (GF__MODULE_ID__.isHidden(pid)) {
      html += "<div class=GF__MODULE_ID__hide id=box_" + pid + ">";
    } else {
      html += "<div class=GF__MODULE_ID__show id=box_" + pid + ">";
    }

    html += "<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" class=\"GF__MODULE_ID__Table\">";

    html += "<tr>"
         + "<td colspan=4><font size=\"-1\"><a target=\"_blank\" href=\"/finance/portfolio?client=ig&action=view&pid=" + pid + "\">"
         + "<b>" + GF__MODULE_ID__.ports[k].pname +"</b></a>&nbsp;-&nbsp;"
         + "<a target=\"_blank\" href=\"/finance/portfolio?client=ig&action=edit_portfolio&pid=" + pid
         + "\"><span class=GF__MODULE_ID__edit>edit</span></a>&nbsp;-&nbsp;</font>";
    if (!GF__MODULE_ID__.port[k] || GF__MODULE_ID__.port[k].length == 0) {
      // No Entries in the Portfolio
      html += "<font size=\"-1\" color=\"#676767\"> This portfolio is empty</font>";
      html += "</td><td></td></tr>";
    } else {
      html += GF__MODULE_ID__.getShowHideLinkHTML(pid);
      html += "</td><td class=GF__MODULE_ID__hideable style=\"text-align:right\"><font size=-1 color=\"#676767\">Mkt&nbsp;Cap</font></td></tr>";
    }

    for (var j=0; j < GF__MODULE_ID__.port[k].length; j++) {
      var name = "";
      var change, perc_change, market_cap;
      var bgClass =
          (j % 2) == 0 ?
              " GF__MODULE_ID__greybg GF__MODULE_ID__hideable" :
              " GF__MODULE_ID__hideable";
      html += "<tr title='" + GF__MODULE_ID__.port[k][j].cname + "'";
      html += " class='" + bgClass + "'";
      html += " style='cursor:pointer;cursor: hand;'";
      html += " id=GF__MODULE_ID__row" + k + "_" + j;
      html += " onclick=\"GF__MODULE_ID__.selectSymbol('"
              + GF__MODULE_ID__.port[k][j].symbol + "')\"";
      html += ">";

      name = "<a target=\"_blank\" href=\"/finance?client=ig&q=";
      if (GF__MODULE_ID__.port[k][j].type == "Mutual Fund") {
        name += GF__MODULE_ID__.port[k][j].symbol;
      } else {
        if (GF__MODULE_ID__.port[k][j].exchange == "NASDAQ" || GF__MODULE_ID__.port[k][j].exchange == "NYSE" || GF__MODULE_ID__.port[k][j].exchange == "AMEX") {
          name += GF__MODULE_ID__.port[k][j].symbol;
        } else {
          name += GF__MODULE_ID__.port[k][j].exchange + ":" + GF__MODULE_ID__.port[k][j].symbol;
        }
      }
      name += "\"><font size=\"-1\">" + GF__MODULE_ID__.port[k][j].symbol + "</font></a>";
      html += "<td width=\"30%\">" + name + "</td>";
      html += "<td width=\"25%\" align=\"right\" style=\"padding-right:0.5em;\"><font size=\"-1\">" + GF__MODULE_ID__.port[k][j].last + "</font></td>";

      change = GF__MODULE_ID__.port[k][j].change;
      perc_change = GF__MODULE_ID__.port[k][j].perc_change;

      if (change && change != "+0.00" && change != "-0.00" && change != "0.00" && change != "0") {
        changeColor = (change.search(/-/g) == -1) ? "GF__MODULE_ID__green" : "GF__MODULE_ID__red";
        html += "<td width=\"15%\" align=\"right\" class=\"" + changeColor + "\">";
        html += "<font size=\"-1\">" + change + "</font></td>";
        html += "<td width=\"10%\" align=\"right\" class=\"" + changeColor + "\">";
        html += "<font size=\"-1\">(" + perc_change + "%)</font></td>";
      }  else {
        html += "<td width=\"15%\" align=\"right\"><font size=\"-1\" color=\"#676767\">0.00</font></td>";
        html += "<td width=\"10%\" align=\"right\"><font size=\"-1\" color=\"#676767\">(0.00%)</font></td>";
      }

      market_cap = GF__MODULE_ID__.port[k][j].market_cap;
      if (market_cap) {
        html += "<td width=\"20%\" align=\"right\">";
        html += "<font size=\"-1\">&nbsp;&nbsp;" + market_cap + "</font></td>";
      } else {
        html += "<td width=\"20%\" align=\"right\">";
        html += "<font size=\"-1\">-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></td>";
      }
      html += "</tr>";

      // Add up the money gained/lossed today on this portfolio
      if (GF__MODULE_ID__.port[k][j].holdings > 0) {
        gainLoss += Math.round((GF__MODULE_ID__.port[k][j].change * GF__MODULE_ID__.port[k][j].holdings) * 100) / 100;
      }

    } // End of Companies Loop

    // If UserPref Set - Show Totals
    if (GF__MODULE_ID__.prefs.getBool("showtotals") == true && GF__MODULE_ID__.ports[k].net_current_value) {
      if (gainLoss.toFixed) {
        gainLoss = gainLoss.toFixed(2);
      } else {
        gainLoss = (Math.round(gainLoss * 100)) / 100;
      }
      var totalValue = GF__MODULE_ID__.ports[k].net_current_value;
      gainLossPerc = "0.00";

      if (totalValue) {
        totalValueNum = +(totalValue.replace(/,/g, ""));
        if (totalValueNum != 0) {
          gainLossPerc = (gainLoss / totalValueNum) * 100;
          if (gainLossPerc.toFixed) {
            gainLossPerc = gainLossPerc.toFixed(2);
          } else {
            gainLossPerc = (Math.round(gainLossPerc * 100)) / 100;
          }
        }
      }

      if (totalValue != null && totalValue != "+0.00" && totalValue != "-0.00" && totalValue != "0.00" && totalValue != "0") {
        if (gainLoss == 0) {
          changeColor = "GF__MODULE_ID__grey";
        } else {
          changeColor = (gainLoss.search(/-/g) == -1) ? "GF__MODULE_ID__green" : "GF__MODULE_ID__red";
        }
        html += "<tr class=\"GF__MODULE_ID__hideable GF__MODULE_ID__line\"><td width=\"30%\"><font size=\"-1\">Value:</font></td>"
             + "<td align=\"right\"><font size=\"-1\">" + totalValue +"</font></td>"
             + "<td align=\"right\" class=\"" + changeColor + "\"><font size=\"-1\">" + gainLoss +"</font></td>"
             + "<td align=\"right\" class=\"" + changeColor + "\"><font size=\"-1\">(" + gainLossPerc + "%)</font></td>"
             + "<td><font size=\"-1\">&nbsp;</font></td></tr>";
      }

    } // End Of Show Totals

    html += "</table></div>";
    return html;
  },

  // Forms the HTML content of the market indices table
  handleMarketIndices : function(JSONtext) {
    // JSON parse - the information from the call
    JSONtext = JSONtext.substring(JSONtext.indexOf("["));
    var JSONobject = eval(JSONtext);
    var bg, changeColor;

    if (GF__MODULE_ID__.isHidden("m")) {
      GF__MODULE_ID__.mhtml = "<div id=box_m class=GF__MODULE_ID__hide>";
    } else {
      GF__MODULE_ID__.mhtml = "<div id=box_m class=GF__MODULE_ID__show>";
    }
    GF__MODULE_ID__.mhtml += "<a target=\"_blank\" href=\"/finance?client=ig\" style=\"padding-left:2px\"><font size=-1><b>Market summary</b></font></a>";
    GF__MODULE_ID__.mhtml += "&nbsp;-&nbsp;";
    GF__MODULE_ID__.mhtml += GF__MODULE_ID__.getShowHideLinkHTML("m");
    GF__MODULE_ID__.mhtml += "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" class=\"GF__MODULE_ID__Table\">";

    var mktClass = " style='cursor:hand;cursor:pointer'";
    if (!JSONobject) {
      // No JSON object to work with, so use the list of indices, but include no data
      var marketIndices = GF__MODULE_ID__.getMarketIndexList();
      for (var i=0; i<marketIndices.length; i++) {
        var mktOnClick = " onclick=\"GF__MODULE_ID__.selectSymbol('"
            + marketIndices[i] + "')\" ";
        bg = (i%2)==0 ? " class=\"GF__MODULE_ID__greybg GF__MODULE_ID__hideable\" " : " class=GF__MODULE_ID__hideable ";
        GF__MODULE_ID__.mhtml += "<tr " + bg + mktClass + mktOnClick + "><td width=\"30%\"><font size=\"-1\"><a target=\"_blank\" href=\"/finance?client=ig&q=";
  GF__MODULE_ID__.mhtml += marketIndices[i] + "\">" + GF__MODULE_ID__.indexName[marketIndices[i]];
        GF__MODULE_ID__.mhtml += "</font></a></td><td width=\"34%\" align=\"right\"><font size=\"-1\">"
            + "</font></td><td width=\"26%\" align=\"right\">"
            + "<font size=\"-1\" color=\"#676767\">0.00</font></td><td align=\"right\"><font size=\"-1\" color=\"#676767\">(0.00%)</font></td>";
      }
      GF__MODULE_ID__.mhtml += "</tr>";
    } else {
      for (var i=0; i<JSONobject.length; i++) {
        var mktOnClick = " onclick=\"GF__MODULE_ID__.selectSymbol('"
            + JSONobject[i].t + "')\"";
        bg = (i%2)==0 ? " class=\"GF__MODULE_ID__greybg GF__MODULE_ID__hideable\" " : " class=GF__MODULE_ID__hideable ";
        GF__MODULE_ID__.mhtml += "<tr " + bg + mktClass + mktOnClick + "><td width=\"30%\"><font size=\"-1\"><a target=\"_blank\" href=\"/finance?client=ig&cid=";

        // Extract IDs from JSON
        GF__MODULE_ID__.mhtml += JSONobject[i].id + "\">" + GF__MODULE_ID__.indexName[JSONobject[i].t];
        GF__MODULE_ID__.mhtml += "</font></a></td><td width=\"34%\" align=\"right\"><font size=\"-1\">"
              + JSONobject[i].l + "</font></td><td width=\"26%\" align=\"right\" class=\"";

        if(JSONobject[i].c != "" && JSONobject[i].c != "+0.00" && JSONobject[i].c != "-0.00" && JSONobject[i].c != "0") {
          changeColor = (JSONobject[i].c.search(/-/g) == -1) ?  "GF__MODULE_ID__green" : "GF__MODULE_ID__red";
          GF__MODULE_ID__.mhtml += changeColor + "\"><font size=\"-1\">" + JSONobject[i].c;
          GF__MODULE_ID__.mhtml += "</font></td><td width=\"10%\" align=\"right\" class=\"";
          GF__MODULE_ID__.mhtml += changeColor + "\"><font size=\"-1\">(" + JSONobject[i].cp + "%)";
          GF__MODULE_ID__.mhtml += "</font></td>";
        } else {
          GF__MODULE_ID__.mhtml += "\"><font size=\"-1\" color=\"#676767\">0.00</font></td><td align=\"right\"><font size=\"-1\" color=\"#676767\">(0.00%)</font></td>";
        }
        GF__MODULE_ID__.mhtml += "</tr>";
      }
    }
    GF__MODULE_ID__.mhtml += "</table>";
    GF__MODULE_ID__.mhtml += "</div>";

    GF__MODULE_ID__.mdone = true;
    GF__MODULE_ID__.maybeDisplay();
  },

  // Prints the Outer structure of the module - The Portfolio Names, Hide/show
  // buttons etc
  printOuterTable : function() {
    var html = "";
    var contracted = GF__MODULE_ID__.prefs.getString("contracted");

    for (var k=0; k < GF__MODULE_ID__.ports.length; k++) {
      var pid = GF__MODULE_ID__.ports[k].pid;
      html += GF__MODULE_ID__.printInnerTable(k);
      html += "<div class=GF__MODULE_ID__Block>";
      html += "</div>";
    }  // End of Portfolios Loop


    // Add form
    html += "<div id=addStock style=\"margin-top:1em;\"><input size=\"15\" id=\"tickertoadd\" autocomplete=off style=\"color: #999999\" value=\"Enter symbol\" onkeydown=\"if (event.keyCode == 13) {GF__MODULE_ID__.addStock(false); return false;}\" onfocus=\"if (this.style.color!='black') {this.style.color = 'black';this.defaultValue = this.value; this.value=''}\" onblur=\"GF__MODULE_ID__.maybeResetTickerToAdd()\">&nbsp;<input type=\"button\" name=Add value=\"Add\" onclick=\"if (document.getElementById('tickertoadd').style.color == 'black') GF__MODULE_ID__.addStock(false); return false;\"></div>";
    html += "<div id=addWhere style=\"margin-top:1em;display:none\"><form name=GF__MODULE_ID__addForm target=\"GF__MODULE_ID__h_iframe\" action=\"http://www.google.com/finance/portfolio?client=ig&action=add&hash=" + GF__MODULE_ID__.userhash + "\" method=\"POST\"><input type=\"hidden\" name=\"add_symbols_1\" value=\"\"><div style=\"padding-bottom:0.5em;\"><font size=-1>Choose a portfolio to add <span style=\"font-weight:bold\" id=\"displayticker\"></span> to:</div>"
    html += "<select width=\"50%\" style=\"width:50%\" name=\"pid\" onkeydown=\"if (event.keyCode == 13) {GF__MODULE_ID__.addStock(true);}\">";
    for (var k=0; k < GF__MODULE_ID__.ports.length; k++) {
      html += "<option value=\"" + GF__MODULE_ID__.ports[k].pid + "\">" + GF__MODULE_ID__.ports[k].pname + "</option>";
    }
    html += "</select>&nbsp;<input type=\"button\" value=\"Add\" onclick=\"GF__MODULE_ID__.addStock(true)\">&nbsp;<input type=\"button\" value=\"Cancel\" onclick=\"GF__MODULE_ID__.cancelWhere()\"></font></form></div>";
    html += "<iframe name=GF__MODULE_ID__h_iframe style=\"border:none; display:none\" onload=\"GF__MODULE_ID__.iframeLoaded();\"></iframe>";

    // Disclaimer
    html += "<div style=\"padding-top:0.5em\"><font size=\"-1\" color=\"#676767\">Quotes delayed up to 20 minutes.&nbsp;"
         + "<a target=\"_blank\" href =\"http://www.google.com/help/stock_disclaimer.html\" style=\"color:#7777cc;\">"
         + "Disclaimer</a></font></div>";

    return html;
  },

  maybeDisplay : function() {
    if (GF__MODULE_ID__.mdone && GF__MODULE_ID__.pdone) {
      _gel('GF__MODULE_ID__mkt').innerHTML = "<div class=GF__MODULE_ID__Block>" + GF__MODULE_ID__.mhtml + "</div>";
      _gel('GF__MODULE_ID__Main').innerHTML = GF__MODULE_ID__.phtml;
      _IG_AdjustIFrameHeight();
    }
  },

  maybeResetTickerToAdd : function() {
    var t = document.getElementById("tickertoadd");
    if (t.value == "" && t.defaultValue) {
      t.value = t.defaultValue;
      t.style.color = "#999999";
    }
  },

  addStock : function(user_has_selected_pid) {
    var ticker = document.getElementById("tickertoadd").value;
    if (!ticker || ticker.length == 0) {
      return;
    }
    // if more than one portfolio then we need to ask the user which portfolio?
    if (!user_has_selected_pid && GF__MODULE_ID__.ports.length > 1) {
      document.getElementById("addStock").style.display = "none";
      document.getElementById("addWhere").style.display = "block";
      document.getElementById("displayticker").innerHTML = ticker + " ";
    } else {
      var pid = null;
      if (user_has_selected_pid) {
        pid = document.GF__MODULE_ID__addForm.pid.value;
      } else if (GF__MODULE_ID__.ports.length == 1) {
  pid = GF__MODULE_ID__.ports[0].pid;
      }
      if (!pid) {
  return;  // should never happen
      }
      document.GF__MODULE_ID__addForm.add_symbols_1.value = ticker;
      document.GF__MODULE_ID__addForm.pid.value = pid;
      // post the form data here
      document.GF__MODULE_ID__addForm.submit();
      GF__MODULE_ID__.sentPost = true;
    }
  },

  cancelWhere : function() {
    document.getElementById("tickertoadd").value = "";
    GF__MODULE_ID__.maybeResetTickerToAdd();
    document.getElementById("displayticker").innerHTML = "";
    document.getElementById("addWhere").style.display = "none";
    document.getElementById("addStock").style.display = "block";
  },

  iframeLoaded : function() {
    if (GF__MODULE_ID__.sentPost) {
      GF__MODULE_ID__.init();
    }
  },

  selectSymbol : function(symbol) {
  },

  createChartLinkPromo : function() {
    var msg = [
      "Symbols in your portfolio can now be linked to the new ",
      "Google Finance Chart gadget! ",
      "<a target=\"_blank\" href=\"#\" onclick=\"GF__MODULE_ID__.addChart()\" > ",
      "Add a Chart gadget.</a>" ];
    var minimsg = new _IG_MiniMessage(__MODULE_ID__);
    minimsg.createDismissibleMessage(msg.join(""));
  },

  addChart : function() {
    setTimeout(function() {
      var gadgetUrl = "http://www.google.com/ig/modules/finance_chart.xml";
      _add_remote_module(gadgetUrl, function() {});
    }, 1000);
  }
};

if (location.href.match(/^https?:\/\/[a-zA-Z-_.0-9]+.google.*(:\d+)?\/.*/)) {
  _IG_RegisterOnloadHandler(GF__MODULE_ID__.init);
} else {
  document.body.innerHTML = '<div><i>Sorry, this gadget is only available for display on <a href="http://www.google.com/ig" target=_top>iGoogle</a>.</i></div>';
}

</script>

    ]]>
  </Content>

</Module>
