// ==UserScript==
// @name           Hammerfall RPG Helper
// @namespace      http://hammerfallhelper.dustywilson.com
// @include        http://apps.facebook.com/hammerfall/*
// ==/UserScript==

// THIS CODE IS LICENSED GPL.  Copyright 2009 Dusty Wilson, http://www.dustywilson.com/

try
{
  var timeLoaded = (new Date()).getTime();

  var failReload = setTimeout('document.location.href=document.location.href', 30000); // 30 secs

  document.getElementsByTagName('body')[0].style.backgroundColor = '#CCC';

  var correctLocation = 0;
  var delayDone = 0;
  var recharging = 1;
  var ur = GM_getValue('ur') || Math.floor(Math.random() * 9999999999).toString(32);
  GM_setValue('ur', String(ur));
  var attribs = new Object();
  attribs.staminaInterval = 300; // in seconds
  attribs.staminaIncrement = (GM_getValue('incr/stamina') || 1); // 2
  attribs.healthInterval = 180; // in seconds
  attribs.healthIncrement = (GM_getValue('incr/health') || 1); // 12
  attribs.energyInterval = 120; // in seconds
  attribs.energyIncrement = (GM_getValue('incr/energy') || 1); // 12
  attribs.guildMax = 0;
  attribs.guildCount = 0;
  attribs.guildEffective = 0;
  var flashDelaySeconds = 300;
  var flashDelayDiv;
  var jobReq = new Object();
  var invInfo = new Object();
  var autoPlay = new Array();
  var sayFade = new Array();

  var initInfo = new Array();
  var info = document.createElement('div');
  info.id = 'GM-INFOBOX';
  info.style.zIndex = '99999999';
  info.style.position = 'fixed';
  info.style.height = 'auto';
  info.style.width = '300px';
  info.style.bottom = '25px';
  info.style.left = '0';
  info.style.margin = '0';
  info.style.padding = '0';
  info.style.color = '#FFFFFF';
  info.style.backgroundColor = '#172C40';
  info.style.textAlign = 'left';
  var infoTitle = document.createElement('div');
  infoTitle.innerHTML = '<a style="color: #FFFFFF; text-decoration: underline" href="http://www.dustywilson.com/hammerfall/" target="dustywilson">Hammerfall RPG Helper</a>';
  infoTitle.style.backgroundColor = '#3b5998';
  infoTitle.style.margin = '0 0 3px 0';
  infoTitle.style.padding = '2px 5px';
  infoTitle.style.fontSize = '80%';
  info.appendChild(infoTitle);
  initInfo.push(say("<b>[ LOADING ]</b>"));
  document.getElementsByTagName('body')[0].appendChild(info);

  var $J;
  var GM_JQ = document.createElement('script');
  GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js';
  GM_JQ.type = 'text/javascript';
  document.getElementsByTagName('head')[0].appendChild(GM_JQ);
  function GM_wait() {
    if (typeof unsafeWindow.jQuery == 'undefined')
    {
      window.setTimeout(GM_wait, 100);
    }
    else
    {
      $J = unsafeWindow.jQuery.noConflict();
      initInfo.push(say("<b>[ READY ]</b>"));
      setTimeout(initInfoWipe, 500);
      ready();
    }
  }
  GM_wait();

  function initInfoWipe()
  {
    try
    {
      if (initInfo.length)
      {
        var obj = initInfo.shift();
        $J(obj).fadeOut(2500, function() {
          $J(this).remove();
        });
        if (initInfo.length)
        {
          setTimeout(initInfoWipe, 500);
        }
      }
    }
    catch(e)
    {
      say(e);
    }
  }

  // All your GM code must be inside this function
  function ready()
  {
    try
    {
      clearTimeout(failReload);
      if ($J('title').text().match('Error loading'))
      {
        say("Error loading Hammerfall.  Reloading in 30 seconds.", 1);
        setTimeout("window.location.href=window.location.href", 30000);
        return false;
      }

      setInterval(sayFadeWipe, 1000);

      autoPlay = autoPlay.concat((GM_getValue('autoplay/once') || '').split(','), (GM_getValue('autoplay/repeat') || '').split(','));

      var location = '';
      var locationLabel = $J('b:contains("Location:")');
      if (locationLabel && locationLabel.length)
      {
        var locationLabelNode = locationLabel[0];
        var locationContainer = locationLabelNode.parentNode;
        var locationContent = $J(locationContainer).text();
        location = locationContent.replace('Location: ', '');
      }
      if (location)
      {
        var locationHfwiki = location.replace(/\s+/, '-').toLowerCase();
        say('Location: <b><i><a style="color: #BBBBFF; text-decoration: underline" target="hfwiki" href="http://hfwiki.wikidot.com/location:' + locationHfwiki + '">' + location + '</a></i></b>', 1);
      }
      else
      {
        say('[ <b><i>Location Unknown</i></b> ]', 1);
      }

      var flashGains = $J('div[class="flash"] img ~ br');
      if (flashGains && flashGains.length)
      {
        for (var i=0; i<flashGains.length; i++)
        {
          var container = flashGains[i].parentNode;
          var itemName = container.innerHTML;
          itemName = itemName.replace(/<.*/g, '');
          itemName = itemName.replace(/^\s+/g, '');
          itemName = itemName.replace(/\s+$/g, '');
          var gotQty = 1;
          var gotQtyAry = $J(container.parentNode.parentNode.parentNode).text().match(/(received|dropped)\s+([0-9]+)/);
          if (gotQtyAry && gotQtyAry.length) gotQty = parseInt(gotQtyAry[2]);
          GM_setValue('inv/' + itemName + '/qty', parseInt(GM_getValue('inv/' + itemName + '/qty') || '0') + gotQty);
          var itemHfwiki = itemName.replace(/\s+/, '-').toLowerCase();
          say('You gained ' + gotQty + ' and now have ' + GM_getValue('inv/' + itemName + '/qty') + 'x <a style="color: #BBBBFF; text-decoration: underline" target="hfwiki" href="http://hfwiki.wikidot.com/item:' + itemHfwiki + '">' + itemName + '</a>.', 1);
        }
      }

      //autoPlay.push(808); // Go Fishing @ South Sea to Leafy Forest
      //autoPlay.push(520); // Coyote @ Leafy Forest Beach
      //autoPlay.push(814); // Trader @ East Leafy Forest 1
      //autoPlay.push(523); // Vikram @ Chandabrook Bridge
      //autoPlay.push(931); // Search for an Oasis @ Crossing the Desert 1
      //autoPlay.push(932); // Relax at the Oasis @ Crossing the Desert 1
      //autoPlay.push(529); // Brown Back Auroch @ Crossing the Desert 2
      //autoPlay.push(933); // Search for Precious Metals @ Crossing the Desert 2
      //autoPlay.push(522); // Desert Wolf @ Crossing the Desert 2
      //autoPlay.push(935); // Shieldsmith @ Avgard
      //autoPlay.push(934); // Swordsmith @ Avgard
      //autoPlay.push(614); // Head North into Lindellin Forest @ Avgard

      //var chance = Math.ceil(Math.random() * 4); // 1/3 chance
      //autoPlay.push(chance == 1 ? 522 : (chance == 2 ? 933 : 529)); // 1/4 for 522, 1/4 for 933, 2/4 for 529

      //var chance = Math.ceil(Math.random() * 2); // 1/2 chance
      //autoPlay.push(chance == 1 ? 935 : 934); // 1/2 for 935, 1/2 for 934

      if (0) // to auto-farm for Spiked Shield/Tempest Blade in Avgard
      {
        var items = Array('Iron Ore', 'Steel Ore', 'Black Crystal', 'Orihalcon Ore', 'Mithril Ore', 'Spiked Shield', 'Tempest Blade');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }

        var oreMin = 6;
        if (parseInt(GM_getValue('inv/Iron Ore/qty')) >= oreMin && parseInt(GM_getValue('inv/Steel Ore/qty')) >= oreMin && parseInt(GM_getValue('inv/Black Crystal/qty')) >= oreMin && parseInt(GM_getValue('inv/Orihalcon Ore/qty')) >= oreMin && parseInt(GM_getValue('inv/Mithril Ore/qty')) >= oreMin)
        {
          say("<b style='color: green'>We have enough ore, let's go back to Avgard.</b>");
          autoPlay.push(425); // Head West to Avgard @ Crossing the Desert 2
        }
        else if (parseInt(GM_getValue('inv/Iron Ore/qty')) < oreMin || parseInt(GM_getValue('inv/Mithril Ore/qty')) < oreMin)
        {
          autoPlay.push(529); // Brown Back Auroch @ Crossing the Desert 2
        }
        else if (parseInt(GM_getValue('inv/Steel Ore/qty')) < oreMin)
        {
          autoPlay.push(522); // Desert Wolf @ Crossing the Desert 2
        }
        else
        {
          autoPlay.push(933); // Search for Precious Metals @ Crossing the Desert 2
        }

        autoPlay.push(parseInt(GM_getValue('inv/Spiked Shield/qty')) < parseInt(GM_getValue('inv/Tempest Blade/qty')) ? 935 : 934); // "load balancing" - get a sword or shield depending on quantity comparison
        autoPlay.push(parseInt(GM_getValue('inv/Spiked Shield/qty')) < parseInt(GM_getValue('inv/Tempest Blade/qty')) ? 934 : 935); // "load balancing" - get a sword or shield depending on quantity comparison
        autoPlay.push(424); // Head East Towards Chandabrook Bridge @ Avgard
      }

      if (0) // Lindellin Forest 1 pass prerequisite
      {
        var items = Array('Money Pouch', 'Stag Antlers');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }
        if (!parseInt(GM_getValue('inv/Money Pouch/qty')))
        {
          if (parseInt(GM_getValue('inv/Stag Antlers/qty')) >= 3)
          {
            autoPlay.push(940); // Forest Ranger @ Lindellin Forest 1
          }
          else
          {
            autoPlay.push(524); // Mystic Stag @ Lindellin Forest 1
          }
        }
        else
        {
          autoPlay.push(616); // Head North Towards Lindellin @ Lindellin Forest 1
        }
      }

      if (0) // Lindellin Forest 2 pass prerequisite
      {
        var items = Array('Onx Spirit', 'Norg Spirit', 'Fire Crystal');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }
        if (!parseInt(GM_getValue('inv/Fire Crystal/qty')))
        {
          if (!parseInt(GM_getValue('inv/Onx Spirit/qty')))
          {
            autoPlay.push(533); // Onx @ Lindellin Forest 2
          }
          else if (!parseInt(GM_getValue('inv/Norg Spirit/qty')))
          {
            autoPlay.push(534); // Norg @ Lindellin Forest 2
          }
          else
          {
            autoPlay.push(941); // Ethina @ Lindellin Forest 2
          }
        }
        else
        {
          autoPlay.push(618); // Head North to Lindellin @ Lindellin Forest 2
        }
      }

      if (0) // Lindellin pass prerequisite
      {
        //autoPlay.push(); // Wallace @ Lindellin // forgot to get job id
        var items = Array('Daniels Help', 'Expert Riding', 'Armored Horse');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }
        if (!parseInt(GM_getValue('inv/Armored Horse/qty')))
        {
          if (parseInt(GM_getValue('inv/Daniels Help/qty')))
          {
            autoPlay.push(942); // Sherwin @ Lindellin
          }
          else
          {
            if (!parseInt(GM_getValue('inv/Expert Riding/qty')))
            {
              autoPlay.push(945); // Become an Expert Rider @ Lindellin
            }
            else
            {
              //autoPlay.push(943); // Claire @ Lindellin // gives white horses
              autoPlay.push(944); // Daniel @ Lindellin
            }
          }
        }
        else
        {
          //autoPlay.push(618); // Head East into Lindellin Forest @ Lindellin
        }
      }

      if (0) // to auto-farm for Rebuilds in Tarnwood
      { // rocks x33, wood x37, vines x5, log x4, indebted inn, indebted tavern
        var items = Array('Indebted Inn', 'Indebted Tavern', 'Rocks', 'Wood', 'Vines', 'Log');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }

        autoPlay.push(972); // Rebuild Ellias Home @ Tarnwood
        autoPlay.push(971); // Rebuild Inn @ Tarnwood
        autoPlay.push(974); // Rebuild Tavern @ Tarnwood

        if (parseInt(GM_getValue('inv/Wood/qty') || 0) < 37)
        {
          autoPlay.push(969); // Gather Wood @ East Lindellin Forest 2
        }
        else if (parseInt(GM_getValue('inv/Rocks/qty') || 0) < 33)
        {
          autoPlay.push(968); // Gather Rocks @ East Lindellin Forest 2
        }
        else if (parseInt(GM_getValue('inv/Vines/qty') || 0) < 5)
        {
          autoPlay.push(543); // Forest Troll @ East Lindellin Forest 2
        }
        else if (parseInt(GM_getValue('inv/Log/qty') || 0) < 4)
        {
          autoPlay.push(513); // Young Boar @ East Lindellin Forest 2
        }
        else
        {
          autoPlay.push(630); // Head East to Tarnwood Bridge @ East Lindellin Forest 2
          autoPlay.push(632); // Head East to Tarnwood @ Tarnwood Bridge
        }
      }

      if (0) // to auto-farm for Lindellin Forest 4
      { // x20 Red Elixir, x3 Bear Claw, x1 Forest Orb
        var items = Array('Red Elixir', 'Bear Claw', 'Forest Orb');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }

        if (parseInt(GM_getValue('inv/Red Elixir/qty') || 0) < 20)
        {
          autoPlay.push(996); // Trader @ West Lindellin Forest Bridge
        }
        else
        {
          autoPlay.push(1207); // Rhia @ Lindellin Forest 4
          autoPlay.push(1009); // Forest Shaman @ Lindellin Forest 4
          autoPlay.push(720); // Head West Through the Lindellin Forest @ West Lindellin Forest Bridge
          autoPlay.push(722); // Head West @ Lindellin Forest 3
        }

        if (!parseInt(GM_getValue('inv/Forest Orb/qty') || 0))
        {
          if (parseInt(GM_getValue('inv/Bear Claw/qty') || 0) < 3)
          {
            autoPlay.push(550); // Shambling Bear @ Lindellin Forest 4
          }
          else
          {
            autoPlay.push(1008); // Forest Ranger @ Lindellin Forest 4
          }
        }
      }

      if (0) // to auto-farm for Unprocessed Crystal @ Guldin Falls Road 1
      { // x25 Unprocessed Crystal
        var items = Array('Unprocessed Crystal');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }

        if (parseInt(GM_getValue('inv/Unprocessed Crystal/qty') || 0) < 25)
        {
          autoPlay.push(1016); // Pick Through the Rocks @ Guldin Falls Road 1
        }
        else
        {
          autoPlay.push(739); // Head South to Guldin Falls @ Guldin Falls Road 1
          autoPlay.push(707); // Head West Through Lindellin Forest @ Guldin Falls
          autoPlay.push(1010); // Bernard @ Guldin Falls
        }
      }

      if (0) // auto-farm Fairy Flute @ West Lindellin Forest 2
      {
        if (parseInt(GM_getValue('inv/Blue Fairy Charm/qty') || 0) < 1)
        {
          if (parseInt(GM_getValue('inv/Fairy Flute/qty') || 0) < 1)
          {
            autoPlay.push(551); // Woodland Dragon @ West Lindellin Forest 2
          }
          else
          {
            autoPlay.push(710); // Head South Along the Hidden Path @ West Lindellin Forest 2
            autoPlay.push(1025); // Blue Fairy @ Hidden Path - Moving Forest
          }
        }
        else
        {
          autoPlay.push(709); // Head Back to the Road @ Hidden Path - Moving Forest
        }
      }

      if (0) // auto-farm Ancient Stone, Aellia Flower @ West Lindellin Forest 1
      {
        var items = Array('Ancient Stone', 'Aellia Flower');
        for (var i in items)
        {
          var item = items[i];
          say('You have ' + (GM_getValue('inv/' + item + '/qty') || 0) + 'x ' + item + '.');
        }
        if (parseInt(GM_getValue('inv/Ancient Stone/qty') || 0) < 3)
        {
          autoPlay.push(552); // Rock Golem @ West Lindellin Forest 1
        }
        if (parseInt(GM_getValue('inv/Aellia Flower/qty') || 0) < 3)
        {
          autoPlay.push(551); // Woodland Dragon @ West Lindellin Forest 1
        }
      }

      //autoPlay.push(699); // Head West Across Grandeur Lake @ Thistledown Beach
      //autoPlay.push(701); // Head South to Thistledown Beach @ Thistledown
      //autoPlay.push(1036); // Fish (Spinning Lure) @ Across Grandeur Lake
      //autoPlay.push(1035); // Fish (Sinking Lure) @ Across Grandeur Lake
      //autoPlay.push(1033); // Fish off the Dock @ Thistledown Beach
      //autoPlay.push(1028); // Oreli @ Thistledown
      //autoPlay.push(1029); // Viri @ Thistledown
      //autoPlay.push(556); // Lake Dragon @ Across Grandeur Lake
      //autoPlay.push(1082); // Rita @ Styrewood Dock
      //autoPlay.push(1083); // Milford @ Styrewood Dock
      //autoPlay.push(1212); // Rit @ Styrewood Dock
      //autoPlay.push(1084); // Aresai @ East Whispering Forest

      autoPlayOrder();

      var pageIdAry = window.location.href.match('/([0-9]+)(\\?.*)?$');
      var pageId = 0;
      if (pageIdAry)
      {
        pageId = parseInt(pageIdAry[1]);
      }

      var pageNameAry = window.location.href.match('/([^\\s\\?/]+)(\\?.*)?$');
      var pageName = 'unknown';
      if (pageNameAry)
      {
        pageName = pageNameAry[1];
      }

      var rechargeInfo = $J('div.rechargeInfo').text();
      if (rechargeInfo)
      {
        var attrs = Array('stamina', 'health', 'energy');
        for (var i in attrs)
        {
          var attr = attrs[i];
          var attrMatchAry = rechargeInfo.match(attr + ' in ([0-9]+) (min|sec)');
          if (attrMatchAry)
          {
            var time = attrMatchAry[1];
            var unit = attrMatchAry[2];
            attribs[attr + 'More'] = time * (unit == 'min' ? 60 : 1);
          }
        }
      }

      var progressBars = Array('Stamina', 'Health', 'Energy', 'Exp');
      for (var i in progressBars)
      {
        var v = progressBars[i];
        var attribBar = $J('tr td.btitle:contains("' + v + ':") ~ td div.progressBar');
        if (attribBar.length)
        {
          attribs[v.toLowerCase() + 'Bar'] = attribBar[0]; // for later updating

          var attribContainerDiv = document.createElement('div');
          attribContainerDiv.style.backgroundImage = false;
          attribContainerDiv.style.position = 'relative';
          attribContainerDiv.style.top = '2px';
          attribContainerDiv.style.zIndex = '99997';
          attribContainerDiv.style.right = '253px';
          attribContainerDiv.style.width = '100px';
          attribContainerDiv.style.textAlign = 'right';
          attribContainerDiv.style.padding = '0 2px 0 2px';
          attribContainerDiv.style.margin = '0';
          attribContainerDiv.innerHTML = '';
          attribBar[0].parentNode.parentNode.appendChild(attribContainerDiv);
          attribs[v.toLowerCase() + 'Container'] = attribContainerDiv;

          var attribProgressDiv = document.createElement('div');
          attribProgressDiv.style.backgroundColor = '#AAA';
          if (v.toLowerCase() == 'health') attribProgressDiv.style.backgroundColor = '#098e89';
          if (v.toLowerCase() == 'stamina') attribProgressDiv.style.backgroundColor = '#972900';
          if (v.toLowerCase() == 'energy') attribProgressDiv.style.backgroundColor = '#978b00';
          attribProgressDiv.style.opacity = '0.65';
          attribProgressDiv.style.position = 'absolute';
          attribProgressDiv.style.top = '0';
          attribProgressDiv.style.zIndex = '99998';
          attribProgressDiv.style.right = '0';
          attribProgressDiv.style.width = '0';
          attribProgressDiv.style.height = '0';
          attribProgressDiv.style.textAlign = 'right';
          attribProgressDiv.style.padding = '0';
          attribProgressDiv.style.margin = '0';
          attribProgressDiv.style.paddingLeft = '0';
          attribProgressDiv.style.paddingTop = '14px';
          attribProgressDiv.innerHTML = '&nbsp;';
          attribContainerDiv.appendChild(attribProgressDiv);
          attribs[v.toLowerCase() + 'Progress'] = attribProgressDiv;

          var attribInfoDiv = document.createElement('div');
          attribInfoDiv.className = 'transBox';
          attribInfoDiv.style.position = 'absolute';
          attribInfoDiv.style.top = '0';
          attribInfoDiv.style.zIndex = '99999';
          attribInfoDiv.style.right = '0';
          attribInfoDiv.style.width = '100px';
          attribInfoDiv.style.textAlign = 'right';
          attribInfoDiv.style.padding = '0 2px 0 2px';
          attribInfoDiv.style.margin = '0';
          attribInfoDiv.innerHTML = v;
          attribInfoDiv.setAttribute('title', v);
          attribInfoDiv.addEventListener('click', function(event)
          {
            var div = event.wrappedJSObject.originalTarget;
            var typeMatchAry = div.innerHTML.match('<!-- (.*?) -->');
            if (typeMatchAry && typeMatchAry.length)
            {
              var attributeLabel = typeMatchAry[1];
              var newIncrement = parseInt(prompt(' Set new ' + attributeLabel + ' increment.'));
              if (newIncrement)
              {
                attribs[attributeLabel.toLowerCase() + 'Increment'] = newIncrement;
                GM_setValue('incr/' + attributeLabel.toLowerCase(), newIncrement);
              }
            }
          }, false);
          attribInfoDiv.style.cursor = 'pointer';
          attribContainerDiv.appendChild(attribInfoDiv);
          $J(attribInfoDiv).fadeOut(0);
          attribs[v.toLowerCase() + 'Info'] = attribInfoDiv;

          var attribAry = attribBar[0].innerHTML.match(">([0-9,]+)");
          attrib = parseInt(attribAry[1].replace(',', ''));
          attribs[v.toLowerCase()] = attrib;

          var attribMaxAry = attribBar[0].innerHTML.match("([0-9,]+)<");
          attribMax = parseInt(attribMaxAry[1].replace(',', ''));
          attribs[v.toLowerCase() + 'Max'] = attribMax;

          attribPct = Math.round(attrib / attribMax * 10000) / 100;
          attribs[v.toLowerCase() + 'Pct'] = attribPct;

          //say("<b>" + (v.toLowerCase() == 'exp' ? 'XP' : v) + ":</b> <span id='" + v.toLowerCase() + "Attr'>" + attrib + "</span>/" + attribMax + " [" + (v.toLowerCase() == 'exp' ? '-' : attribPct + "%, -") + (attribMax - attrib) + "]" + (attribs[v.toLowerCase() + 'More'] ? ' +' + attribs[v.toLowerCase() + 'More'] + ' secs' : ''));
        }
      }

      var progressInfo = Array('Level', 'Gold');
      for (var i in progressInfo)
      {
        var v = progressInfo[i];
        var levelInfo = $J('tr td.btitle:contains("' + v + ':")').parent().find('td');
        if (levelInfo.length)
        {
          attrib = parseInt(levelInfo[1].innerHTML.replace(',', ''));
          //say("<b>" + v + ":</b> " + attrib);
          attribs[v.toLowerCase()] = attrib;
        }
      }

      var guildCountAry = $J('a:contains("Guild ")');
      if (guildCountAry.length)
      {
        var guildCount = guildCountAry[0];
        var guildCountContent = guildCount.innerHTML;

        var guildCountCurrentAry = guildCountContent.match('\\(([0-9]+)/');
        if (guildCountCurrentAry)
        {
          attribs.guildCount = parseInt(guildCountCurrentAry[1]);
        }

        var guildMaxCurrentAry = guildCountContent.match('/([0-9]+)\\)');
        if (guildMaxCurrentAry)
        {
          attribs.guildMax = parseInt(guildMaxCurrentAry[1]);
        }

        attribs.guildEffective = (attribs.guildCount < attribs.guildMax ? attribs.guildCount : attribs.guildMax);

        //say('<b>Guild Count:</b> ' + attribs.guildCount);
        //say('<b>Guild Usable:</b> ' + attribs.guildMax);
        //say('<b>Guild Effective:</b> ' + attribs.guildEffective);
      }

      // inventory items
      invInfo[771] = new Object(); // Mushrooms
      invInfo[771].name = 'Mushrooms';
      invInfo[771].singular = 'Mushroom';
      invInfo[771].plural = 'Mushrooms';
      invInfo[722] = new Object(); // Watermelon
      invInfo[722].name = 'Watermelon';
      invInfo[722].singular = 'Watermelon';
      invInfo[722].plural = 'Watermelons';
      invInfo[773] = new Object(); // Fish
      invInfo[773].name = 'Fish';
      invInfo[773].singular = 'Fish';
      invInfo[773].plural = 'Fish';
      invInfo[785] = new Object(); // Sprites
      invInfo[785].name = 'Sprites';
      invInfo[785].singular = 'Sprite';
      invInfo[785].plural = 'Sprites';
      invInfo[786] = new Object(); // Blue Elixir
      invInfo[786].name = 'Blue Elixir';
      invInfo[786].singular = 'Blue Elixir';
      invInfo[786].plural = 'Blue Elixirs';

      // jobReq entries
      jobReq[538] = new Object(); // Goblin @ Throncrest
      jobReq[538].attack = 1;
      jobReq[538].healthMin = 30;
      jobReq[538].energyMin = 32;
      jobReq[538].healthHealMin = 30;
      jobReq[538].energyHealMin = 8;
      jobReq[520] = new Object(); // Coyote @ East Leafy Forest 1
      jobReq[520].attack = 1;
      jobReq[520].healthMin = 30;
      jobReq[520].energyMin = 32;
      jobReq[520].healthHealMin = 30;
      jobReq[520].energyHealMin = 8;
      jobReq[523] = new Object(); // Vikram @ Chandabrook Bridge
      jobReq[523].attack = 1;
      jobReq[523].healthMin = attribs.healthMax;
      jobReq[523].energyMin = attribs.energyMax;
      jobReq[523].healthHealMin = 60;
      jobReq[523].energyHealMin = 8;
      jobReq[529] = new Object(); // Brown Back Auroch @ Crossing the Desert 2
      jobReq[529].attack = 1;
      jobReq[529].healthMin = 30;
      jobReq[529].energyMin = 32;
      jobReq[529].healthHealMin = 30;
      jobReq[529].energyHealMin = 8;
      jobReq[522] = new Object(); // Desert Wolf @ Crossing the Desert 2
      jobReq[522].attack = 1;
      jobReq[522].healthMin = 30;
      jobReq[522].energyMin = 32;
      jobReq[522].healthHealMin = 30;
      jobReq[522].energyHealMin = 8;
      jobReq[524] = new Object(); // Mystic Stag @ Lindellin Forest 1
      jobReq[524].attack = 1;
      jobReq[524].healthMin = 60;
      jobReq[524].energyMin = 40;
      jobReq[524].healthHealMin = 45;
      jobReq[524].energyHealMin = 8;
      jobReq[533] = new Object(); // Onx @ Lindellin Forest 2
      jobReq[533].attack = 1;
      jobReq[533].healthMin = 80;
      jobReq[533].energyMin = 40;
      jobReq[533].healthHealMin = 45;
      jobReq[533].energyHealMin = 8;
      jobReq[534] = new Object(); // Norg @ Lindellin Forest 2
      jobReq[534].attack = 1;
      jobReq[534].healthMin = 80;
      jobReq[534].energyMin = 40;
      jobReq[534].healthHealMin = 45;
      jobReq[534].energyHealMin = 8;
      jobReq[32] = new Object(); // Shadow Knives Henchmen @ Tarnwood Bridge
      jobReq[32].attack = 1;
      jobReq[32].healthMin = attribs.healthMax;
      jobReq[32].energyMin = attribs.energyMax;
      jobReq[32].healthHealMin = 75;
      jobReq[32].energyHealMin = 8;
      jobReq[543] = new Object(); // Forest Troll @ East Lindellin Forest 2
      jobReq[543].attack = 1;
      jobReq[543].healthMin = 80;
      jobReq[543].energyMin = 40;
      jobReq[543].healthHealMin = 75;
      jobReq[543].energyHealMin = 8;
      jobReq[513] = new Object(); // Young Boar @ East Lindellin Forest 2
      jobReq[513].attack = 1;
      jobReq[513].healthMin = 80;
      jobReq[513].energyMin = 40;
      jobReq[513].healthHealMin = 75;
      jobReq[513].energyHealMin = 8;
      jobReq[550] = new Object(); // Shambling Bear @ Lindellin Forest 4
      jobReq[550].attack = 1;
      jobReq[550].healthMin = 80;
      jobReq[550].energyMin = 40;
      jobReq[550].healthHealMin = 75;
      jobReq[550].energyHealMin = 8;
      jobReq[551] = new Object(); // Woodland Dragon @ West Lindellin Forest 2
      jobReq[551].attack = 1;
      jobReq[551].healthMin = 80;
      jobReq[551].energyMin = 40;
      jobReq[551].healthHealMin = 75;
      jobReq[551].energyHealMin = 8;
      jobReq[552] = new Object(); // Rock Golem @ West Lindellin Forest 1
      jobReq[552].attack = 1;
      jobReq[552].healthMin = 80;
      jobReq[552].energyMin = 80;
      jobReq[552].healthHealMin = 75;
      jobReq[552].energyHealMin = 8;
      jobReq[556] = new Object(); // Lake Dragon @ Across Grandeur Lake
      jobReq[556].attack = 1;
      jobReq[556].healthMin = 80;
      jobReq[556].energyMin = 80;
      jobReq[556].healthHealMin = 100;
      jobReq[556].energyHealMin = 8;
      jobReq[561] = new Object(); // Ogre @ North Whispering Forest
      jobReq[561].attack = 1;
      jobReq[561].healthMin = 80;
      jobReq[561].energyMin = 80;
      jobReq[561].healthHealMin = 100;
      jobReq[561].energyHealMin = 8;
      jobReq[563] = new Object(); // Orc Scout @ South Whispering Forest
      jobReq[563].attack = 1;
      jobReq[563].healthMin = 80;
      jobReq[563].energyMin = 80;
      jobReq[563].healthHealMin = 100;
      jobReq[563].energyHealMin = 8;

      var thisJobReq = jobReq[pageId];

      // auto-fight
      if (thisJobReq && thisJobReq.attack)
      {
        recharging = 0; // no recharge during attack
        if (thisJobReq.healthHealMin <= attribs.health)
        {
          if (thisJobReq.energyHealMin <= attribs.energy)
          {
            autoPlay.push(45, 31, 22, 20); // two-handed slash, stun, double strike, basic
          }
          else
          {
            autoPlay.push(785, 786, 21); // sprites +40, blueelixir +60, runaway
          }
        }
        else
        {
          autoPlay.push(771, 722, 773); // mushrooms +75, watermelon +100, fish +150
        }
      }

      if (pageName == 'myguild')
      {
        say('<i><b>Viewing My Guild</b></i>');
        var allMembers = $J('h1:contains("Friends") ~ table tr td div:nth-child(2)');
        for (var i=0; i<allMembers.length; i++)
        {
          memberNameDiv = allMembers[i];
          var x = i + 1;
          if (x <= attribs.guildEffective)
          {
            $J(memberNameDiv).prepend('<b style="color: green">#' + x + '</b>: ');
          }
          else
          {
            $J(memberNameDiv).prepend('<b style="color: red">#' + x + '</b>: ');
          }
        }
      }

      if (pageName == 'character')
      {
        say('<i><b>Viewing My Character</b></i>');
        var allVars = GM_listValues();
        for (var i=0; i<allVars.length; i++)
        {
          var thisVar = allVars[i];
          if (thisVar.match('^inv/') && thisVar.match('/qty$'))
          {
            GM_deleteValue(thisVar);
          }
        }
        var qtys = new Object();
        var allImgs = $J('img[src*="small"]');
        for (var i=0; i<allImgs.length; i++)
        {
          var itemImg = allImgs[i];
          var itemDiv = itemImg.parentNode;
          var itemContent = itemDiv.innerHTML;
          var itemQtyAry = itemContent.match('<b>([0-9]+)x</b>');
          var itemQty = 1;
          if (itemQtyAry) itemQty = itemQtyAry[1];
          var itemName = itemContent;
          itemName = itemName.replace(/<.*/g, '');
          itemName = itemName.replace(/^\s+/g, '');
          itemName = itemName.replace(/\s+$/g, '');
          if (!itemName) continue;
          qtys[itemName] = itemQty;
          //say("I: [" + itemName + "] == " + itemQty);
        }
        for (var item in qtys)
        {
          if (typeof(item) == 'string')
          {
            GM_setValue('inv/' + item + '/qty', qtys[item]);
          }
        }
      }

      var allPlayForms = $J('form');
      if (allPlayForms.length)
      {
        for (var i=0; i<allPlayForms.length; i++)
        {
          var form = allPlayForms[i];
          var formAction = form.getAttribute('action');
          var formActionAry = formAction.match('/hammerfall/.*?/([0-9]+)$');
          if (formActionAry)
          {
            var formId = formActionAry[1];

            if (pageName == 'quest')
            {
              $J(form).find('input').css('height', '50px');

              form.style.textAlign = 'right';

              var brTag = document.createElement('br');
              form.appendChild(brTag);

              var autoPlayOnceAry = (GM_getValue('autoplay/once') || '').split(',');
              var autoPlayOnceBtn = document.createElement('input');
              autoPlayOnceBtn.setAttribute('value', 'Auto Play Once');
              autoPlayOnceBtn.setAttribute('type', 'button');
              autoPlayOnceBtn.setAttribute('jobId', formId);
              autoPlayOnceBtn.style.backgroundColor = (autoPlayOnceAry.indexOf(formId) >= 0 ? '#66CC66' : '#CC6666');
              autoPlayOnceBtn.style.margin = '2px';
              autoPlayOnceBtn.addEventListener('click', function(event) {
                var btn = event.wrappedJSObject.originalTarget;
                var added = autoPlayOnce(btn.getAttribute('jobId'));
                btn.style.backgroundColor = (added ? '#66CC66' : '#CC6666');
              }, false);
              form.appendChild(autoPlayOnceBtn);

              var brTag = document.createElement('br');
              form.appendChild(brTag);

              var autoPlayRepeatAry = (GM_getValue('autoplay/repeat') || '').split(',');
              var autoPlayRepeatBtn = document.createElement('input');
              autoPlayRepeatBtn.setAttribute('value', 'Auto Play Repeat');
              autoPlayRepeatBtn.setAttribute('type', 'button');
              autoPlayRepeatBtn.setAttribute('jobId', formId);
              autoPlayRepeatBtn.style.backgroundColor = (autoPlayRepeatAry.indexOf(formId) >= 0 ? '#66CC66' : '#CC6666');
              autoPlayRepeatBtn.style.margin = '2px';
              autoPlayRepeatBtn.addEventListener('click', function(event) {
                var btn = event.wrappedJSObject.originalTarget;
                var added = autoPlayRepeat(btn.getAttribute('jobId'));
                btn.style.backgroundColor = (added ? '#66CC66' : '#CC6666');
              }, false);
              form.appendChild(autoPlayRepeatBtn);

              $J(form).find('input').css('width', '150px');
            }
            var jobParent = $J(form).parent().parent().parent().parent().parent().parent();
            var jobNameDiv = $J(jobParent).find('div.itemName');
            if (jobNameDiv)
            {
              var jobName = jobNameDiv.text() || 'Job #' + formId;
              jobName = jobName.replace(' (Back)', '');
              var jobImageCell = $J(jobParent).find('td.itemCol0');
              if (jobImageCell)
              {
                jobImageCell = jobImageCell[0];
                if (jobImageCell)
                {
                  var jobIdentify = document.createElement('div');
                  jobIdentify.innerHTML = '<b style="margin: 5px 0 5px 23px">Job ID: ' + formId + '</b>';
                  jobImageCell.appendChild(jobIdentify);
                }
              }
            }
          }
        }
      }

      autoPlayCheck();

      if (recharging)
      {
        setInterval(rechargeStatus, 1000);
      }
    }
    catch(e)
    {
      say(e);
    }
  }

  function jobAvailable(id)
  {
    return $J('form[action$="/' + id + '"]').length;
  }

  function autoPlayOrder()
  {
    if (autoPlay.length)
    {
      var autoPlayOrderAry = new Array();
      for (var i=0; i<autoPlay.length; i++)
      {
        var jobId = autoPlay[i];
        if (jobAvailable(jobId)) autoPlayOrderAry.push(jobId);
      }
      if (autoPlayOrderAry.length)
      {
        say("<b>Auto Play Order:</b>");
        for (var i=0; i<autoPlayOrderAry.length; i++)
        {
          var jobId = autoPlayOrderAry[i];
          say("> Job ID: " + jobId);
        }
      }
      else
      {
        say("<b>No Auto Play Jobs in this area.</b>");
      }
    }
    else
    {
      say("<b>No Auto Play Jobs are set.</b>");
    }
  }

  function autoPlayCheck()
  {
    try
    {
      for (var i in autoPlay)
      {
        var id = autoPlay[i];
        var thisJob = jobReq[id];
        var forms = $J('form[action$="/' + id + '"]');
        if (forms.length)
        {
          var form = forms[0];
          var jobParent = $J(form).parent().parent().parent().parent().parent().parent();

          var jobGivesDiv = $J(jobParent).find('td.itemCol1 div.itemGives')[0];
          var jobGivesText = $J(jobGivesDiv).text();
          var expAry = jobGivesText.match('Experience: \\+([0-9]+)');
          if (expAry)
          {
            var experience = expAry[1];
            //say('<b>Job ' + id + ' gives ' + experience + ' Exp.</b>');
            var turnsToLevel = Math.ceil((attribs.expMax - attribs.exp) / experience);
            say('Level up in ' + turnsToLevel + ' turns with job ' + id + '.');
            attribs['expInfo'].innerHTML = turnsToLevel + ' turns @ #' + id;
            $J(attribs['expInfo']).fadeIn(500);
          }

          var jobInfoCell = $J(jobParent).find('td.itemCol2');
          var jobReqItemsDivs = $J(jobInfoCell).find('div[class="requiredItemName"]');
          var inventoryRequiredMet = 1;
          var itemUsed = new Object();
          if (jobReqItemsDivs && jobReqItemsDivs.length)
          {
            for (var iDiv=0; iDiv<jobReqItemsDivs.length; iDiv++)
            {
              var jobReqItemDiv = jobReqItemsDivs[iDiv];
              var itemReq = $J(jobReqItemDiv).text().replace(/^[\r\n\s]+/, '').replace(/[\r\n\s]+$/, '').replace(/[\r\n\s]+/, ' ');
              var itemUsed = 0;
              if (itemReq.match(/\s+\*\s*$/))
              {
                itemReq = itemReq.replace(/\s+\*\s*$/, '');
                itemUsed = 1;
              }
              var itemQtyReqAry = itemReq.match(/ x([0-9]+)$/);
              var itemQtyReq = 1;
              if (itemQtyReqAry && itemQtyReqAry.length)
              {
                itemQtyReq = itemQtyReqAry[1];
                itemReq = itemReq.replace(/ x([0-9]+)$/, '');
              }
              if (itemUsed)
              {
                itemUsed[itemReq] = itemQtyReq;
              }
              var itemQtyHas = GM_getValue('inv/' + itemReq + '/qty') || 0;
              //say("ITEM: [" + itemReq + "] [" + itemQtyReq + "] [" + itemQtyHas + "]");
              if (parseInt(itemQtyReq) > parseInt(itemQtyHas))
              {
                var itemHfwiki = itemReq.replace(/\s+/, '-').toLowerCase();
                say('<b style="color: red">Need ' + itemQtyReq + 'x <a style="color: #BBBBFF; text-decoration: underline" target="hfwiki" href="http://hfwiki.wikidot.com/item:' + itemHfwiki + '">' + itemReq + '</a> for job ' + id + '.</b>');
                inventoryRequiredMet = 0;
              }
            }
          }
          if (inventoryRequiredMet)
          {
            var jobInfoText = $J(jobInfoCell).text();
            var required = 0;
            var staminaAry = jobInfoText.match('Stamina: ([0-9]+)');
            if (staminaAry)
            {
              required = staminaAry[1];
            }
            //required *= 5; // TEMP FIXME TESTING
            if (attribs.stamina >= required)
            {
              if (thisJob && thisJob.healthMin > attribs.health)
              {
                say("<b style='color: orange'>Not enough health for job " + id + ".</b>");
              }
              else if (thisJob && thisJob.energyMin > attribs.energy)
              {
                say("<b style='color: orange'>Not enough energy for job " + id + ".</b>");
              }
              else
              {
                say("<b style='color: orange'>Auto-play job " + id + ".</b>");
                var item = invInfo[id];
                if (item)
                {
                  say("<b style='color: red'><i>Using a " + item.singular + ".</i></b>");
                  GM_setValue('inv/' + item.name + '/qty', GM_getValue('inv/' + item.name + '/qty') - 1);
                }
                for (var itemReq in itemUsed)
                {
                  say("<b style='color: red'>Using " + itemUsed[itemReq] + "x " + itemReq + ".</b>");
                  GM_setValue('inv/' + itemReq + '/qty', GM_getValue('inv/' + itemReq + '/qty') - itemUsed[itemReq]);
                }
                var autoPlayOnce = GM_getValue('autoplay/once').split(',');
                for (var ap=0; ap<autoPlayOnce.length; ap++)
                {
                  if (autoPlayOnce[ap] == id)
                  {
                    delete autoPlayOnce[ap];
                  }
                }
                var autoPlayOnceStr = autoPlayOnce.join(',').replace(/,+/, ',').replace(/^,/, '').replace(/,$/, '');
                GM_setValue('autoplay/once', autoPlayOnceStr);
                form.submit();
              }
            }
            else
            {
              say("<b style='color: orange'>Not enough stamina for job " + id + ".</b>");
            }
            break;
          }
        }
      }
    }
    catch(e)
    {
      say(e);
    }
  }

  function rechargeStatus()
  {
    try
    {
      var rechargeInfo = $J('div.rechargeInfo');
      if (rechargeInfo)
      {
        rechargeInfo = rechargeInfo[0];
        rechargeInfo.innerHTML = "";
        rechargeInfo.style.display = 'none';

        var attrs = Array('Stamina', 'Health', 'Energy');
        for (var i in attrs)
        {
          var attr = attrs[i];
          var attrL = attr.toLowerCase();
          var time = attribs[attrL + 'More'];
          if (time > 0 && attribs[attrL] < attribs[attrL + 'Max'])
          {
            var timeNow = (new Date()).getTime();
            var targetTime = time * 1000 + timeLoaded;
            var remainingTime = Math.round((targetTime - timeNow) / 10) / 100;
            if (remainingTime > 0)
            {
              //$J(rechargeInfo).append(attr + ' in ' + remainingTime.toFixed(0) + ' sec' + (remainingTime == 1 ? '' : 's') + '<br />');
              attribs[attr.toLowerCase() + 'Info'].innerHTML = '+' + attribs[attrL + 'Increment'] + ' in ' + remainingTime.toFixed(0) + ' sec' + (remainingTime.toFixed(0) == 1 ? '' : 's') + '<!-- ' + attr + ' -->';
              //attribs[attr.toLowerCase() + 'Progress'].style.paddingLeft = Math.round(remainingTime / attribs[attr.toLowerCase() + 'Interval'] * 346) + 'px';
              $J(attribs[attr.toLowerCase() + 'Progress']).animate({'paddingLeft': Math.round(remainingTime / attribs[attr.toLowerCase() + 'Interval'] * 243 + 103) + 'px'}, 'slow');
              $J(attribs[attr.toLowerCase() + 'Info']).fadeIn(500);
            }
            else
            {
              attribs[attrL + 'More'] = time + attribs[attrL + 'Interval'];
              attribs[attrL] += attribs[attrL + 'Increment'];
              attribs[attrL] = (attribs[attrL] > attribs[attrL + 'Max'] ? attribs[attrL + 'Max'] : attribs[attrL])
              $J('#' + attrL + 'Attr').text(attribs[attrL]);
              autoPlayCheck();
            }
          }
          else
          {
            $J(attribs[attr.toLowerCase() + 'Info']).fadeOut(500);
            attribs[attr.toLowerCase() + 'Progress'].style.paddingLeft = '0';
          }
          var progressBar = attribs[attrL + 'Bar'];
          var progressBarLabel = progressBar.firstChild;
          var progressBarImage = progressBar.lastChild;
          progressBar.style.backgroundColor = '#000';
          progressBar.style.backgroundPosition = '-10px -5px';
          progressBar.style.backgroundImage = (attribs[attrL] < attribs[attrL + 'Max'] ? 'url(http://www.dustywilson.com/img/progressBar.gif?' + ur + ')' : '');
          progressBarLabel.style.top = '-3px';
          progressBarLabel.style.left = '-4px';
          progressBarLabel.innerHTML = attribs[attrL] + ' / ' + attribs[attrL + 'Max'];
          progressBarImage.style.width = (attribs[attrL] / attribs[attrL + 'Max'] * 73) + 'px';
          progressBarImage.style.opacity = '0.4';
          progressBarLabel.style.zIndex = '99999';
        }
      }
    }
    catch(e)
    {
      say(e);
    }
  }

  function autoPlayOnce(id)
  {
    try
    {
      var autoPlayOnceAry = (GM_getValue('autoplay/once') || '').split(',');
      var autoPlayOnceIndex = autoPlayOnceAry.indexOf(id);
      var autoPlayRepeatAry = (GM_getValue('autoplay/repeat') || '').split(',');
      var autoPlayRepeatIndex = autoPlayRepeatAry.indexOf(id);
      var autoPlayIndex = autoPlay.indexOf(id);
      var added = 0;
      if (autoPlayOnceIndex < 0)
      {
        say("Adding job " + id + " to Auto Play Once.");
        autoPlayOnceAry.push(id);
        if (autoPlayIndex < 0)
        {
          autoPlay.push(id);
        }
        added = 1;
      }
      else
      {
        say("Removing job " + id + " from Auto Play Once.");
        autoPlayOnceAry[autoPlayOnceIndex] = '';
        if (autoPlayIndex >= 0 && autoPlayRepeatIndex < 0)
        {
          autoPlay[autoPlayIndex] = '';
        }
        added = 0;
      }
      var autoPlayOnceStr = autoPlayOnceAry.join(',').replace(/,+/, ',').replace(/^,/, '').replace(/,$/, '');
      GM_setValue('autoplay/once', autoPlayOnceStr);
      autoPlayOrder();
      autoPlayCheck();
      return added;
    }
    catch(e)
    {
      say(e);
    }
  }

  function autoPlayRepeat(id)
  {
    try
    {
      var autoPlayOnceAry = (GM_getValue('autoplay/once') || '').split(',');
      var autoPlayOnceIndex = autoPlayOnceAry.indexOf(id);
      var autoPlayRepeatAry = (GM_getValue('autoplay/repeat') || '').split(',');
      var autoPlayRepeatIndex = autoPlayRepeatAry.indexOf(id);
      var autoPlayIndex = autoPlay.indexOf(id);
      var added = 0;
      if (autoPlayRepeatIndex < 0)
      {
        say("Adding job " + id + " to Auto Play Repeat.");
        autoPlayRepeatAry.push(id);
        if (autoPlayIndex < 0)
        {
          autoPlay.push(id);
        }
        added = 1;
      }
      else
      {
        say("Removing job " + id + " from Auto Play Repeat.");
        autoPlayRepeatAry[autoPlayRepeatIndex] = '';
        if (autoPlayIndex >= 0 && autoPlayOnceIndex < 0)
        {
          autoPlay[autoPlayIndex] = '';
        }
        added = 0;
      }
      var autoPlayRepeatStr = autoPlayRepeatAry.join(',').replace(/,+/, ',').replace(/^,/, '').replace(/,$/, '');
      GM_setValue('autoplay/repeat', autoPlayRepeatStr);
      autoPlayOrder();
      autoPlayCheck();
      return added;
    }
    catch(e)
    {
      say(e);
    }
  }
}
catch(e)
{
  say(e);
}

function say(content, keep)
{
  var newDiv = document.createElement('div');
  newDiv.innerHTML = '<nobr>' + content + '</nobr>';
  newDiv.style.padding = '0 5px 2px 5px';
  info.appendChild(newDiv);
  if ($J)
  {
    infoSizer();
    $J(newDiv).fadeOut(0, function() {
      $J(this).fadeIn(1500);
    });
  }
  else
  {
    info.style.height = 'auto';
  }
  if (!keep) sayFade.push(newDiv);
  return newDiv;
}

function sayFadeWipe()
{
  try
  {
    if (sayFade.length)
    {
      var obj = sayFade.shift();
      $J(obj).fadeOut(5000, function() {
        $J(this).remove();
        infoSizer();
      });
    }
  }
  catch(e)
  {
    say(e);
  }
}

function infoSizer()
{
  var height = info.childNodes[0].offsetHeight + (info.childNodes.length > 1 ? (info.childNodes[1].offsetHeight * (info.childNodes.length - 1)) : 0) + 5;
  $J(info).animate({'height': height + 'px'}, 'fast');
}
