/* Copyright (c) 2007, Akita Noek.  All rights reserved. */


/** Config **/
/* This controls how long a download should take. */
var OneSpeedTestTestTime            = 2000;      // in miliseconds (this will only be an aproximation)
var OneSpeedTestTerminalLineHeight  = 40;
var OneSpeedTestNumPingsToRun       = 5;
var OneSpeedTestNumDLsToRun         = 3;
var OneSpeedTestNumULsToRun         = 5;
var OneSpeedTestAvgModemSpeed       = 28800;
var OneSpeedTestAvgWebPageSize      = 130000;
var OneSpeedTestProtocolOverhead    = 1.08;      // +8% or there abouts ... 
var OneSpeedTestCalibrationSlack    = 0.86;       /* 0.86 seems like a good magic number?  Decrease this if you are
                                                * on a jittery network or running at high speeds (>=Gb networks). */
var OneSpeedTestInitialDownloadSize = 20480;      /* bytes */
var OneSpeedTestInitialUploadSize   = 20480;      /* bytes */
var OneSpeedTestMaxCalibrationSteps = 3;        /* In most cases we don't need this many and will exit before this, but when 
                                                * we have a lot of jitter we need to limit our selves so we don't attemp to
                                                * sit there and calibrate in a sea of chaos network chaos... */
var OneSpeedTestMinCalibrationSteps = 3;         /* Always calibrate a little bit .. */
var OneSpeedTestMaxUploadSize       = 1048575;   /* 8 MB. This is the default post size limit for 
                                                * most server setups, so more than this is useless. 
                                                * Note: Opera browsers don't seem to let JS allocate 8MB, so
                                                *       in the browser detection code this is again capped to
                                                *       3.5 MB, if you don't it lower here.  */
var OneSpeedTestMaxDownloadSize     = 1048575;  /* 64 MB (67108864). Be surinpute to set this in OneSpeedTest.php too. */

/* Only change these if you are messing with the .svg file. */
var OneSpeedTestViewWidth           = 1600;      // you need to change this in the .svg file as well.
var OneSpeedTestViewHeight          = 1200;      // you need to change htis in the .svg file as well.

var pingTestCounter = 0;

// all measurements in bits
var OneSpeedTestLineTypes = new Array(
       {name: '14.4k Modem'         , up:14400.0       , down:14400.0       } 
     , {name: '28.8k Modem'         , up:28800.0       , down:28800.0       } 
     , {name: '56k Modem'           , up:56000.0       , down:56200.0       } 
     , {name: '1.5M ADSL'           , up:512000.0      , down:1500000.0     } 
     , {name: 'T1'                  , up:1544000.0     , down:1544000.0     } 
     , {name: 'E1'                  , up:2048000.0     , down:2048000.0     } 
     , {name: 'Dual T1'             , up:3088000.0     , down:3088000.0     } 
     , {name: '7M ADSL'             , up:1000000.0     , down:7000000.0     } 
     , {name: 'High Speed Wireless' , up:7000000.0     , down:7000000.0     } 
     , {name: '10M Ethernet'        , up:10000000.0    , down:10000000.0    } 
     , {name: 'T3'                  , up:44736000.0    , down:44736000.0    } 
     , {name: 'OC1'                 , up:51800000.0    , down:51800000.0    } 
     , {name: '100M Ethernet'       , up:100000000.0   , down:100000000.0   } 
     , {name: 'OC3'                 , up:155000000.0   , down:155000000.0   } 
     , {name: 'T4'                  , up:274760000.0   , down:274760000.0   } 
     , {name: 'OC12'                , up:622000000.0   , down:622000000.0   } 
     , {name: '1G Ethernet'         , up:1000000000.0  , down:1000000000.0  } 
     , {name: 'OC48'                , up:2500000000.0  , down:2500000000.0  } 
     , {name: 'OC192'               , up:9600000000.0  , down:9600000000.0  } 
     , {name: '10G Ethernet'        , up:10000000000.0 , down:10000000000.0 } 
  );


/** Browser Detection **/
var OneSpeedTestBrowser = "";
if (/Opera/.test(navigator.userAgent)) {
  OneSpeedTestBrowser="opera";
  if (OneSpeedTestMaxUploadSize > 3670016) 
    OneSpeedTestMaxUploadSize = 3670016.0;   /* 3.5 MB. limit Opera doesn't like to let JS have much memory I guess */
}
else if (/Microsoft Internet Explorer/.test(navigator.appName))
  OneSpeedTestBrowser="ie";
else if (/Adobe/.test(navigator.appName)) // We can also be in the Adobe SVG Viewer ... its probably not really correct to assume ie, but oh well. 
  OneSpeedTestBrowser="ie";
else
  OneSpeedTestBrowser="mozilla";



/* added for custom test stop */
var OneSpeedTestStop = false;


/** Code **/
function OneSpeedTest(e) { /* {{{ */
  var self = this;
  
  this.status                 = "";
  this.latency                = new Array();
  this.downbps                = new Array();
  this.upbps                  = new Array();
  this.lastRunTime            = 0;
  this.goodDLSize             = 0;
  this.goodULSize             = 0;

  this.terminalBuffer         = new Array();

  this.numPingsRan            = 0;
  this.numDLCalibration       = 0;
  this.numULCalibration       = 0;
  this.numDLReal              = 0;
  this.numULReal              = 0;
  this.totalDLCalibrationTime = 0;
  this.totalULCalibrationTime = 0;

  this.progressCircle         = null;
  this.progressTriangle       = null;
  this.progressText           = null;

  
  this.svg         = null;
  this.svgDocument = null;

  OneSpeedTestStop = false;
  pingTestCounter = 0;

  this.updateStats();
  this.speedTest();
} /* }}} */

OneSpeedTest.prototype.newElt                      = function (type) { /* {{{ */
  if (this.svg == null) return;
/* Takes a variable number of arguments. 
 * The first argument should be a string of the type of dom object
 * you want to create. The second argument is an associative array
 * with property:value pairs. The remaining arguments, if they exist,
 * are any children you want to append to the element.

 * All properties are applied with a null attribute name space, so if you need
 * a namespace (for example, for xlink:href's), then you need to apply the attribute
 * after you have created it with this function. 

 * Eg. this.newElt('rect', {width: 200, height: 100, x: 400, y: 500}, child1, child2);

 */


  var svgns = "http://www.w3.org/2000/svg";


  var elt = this.svgDocument.createElementNS(svgns, type);
  if (arguments.length >= 2 && typeof arguments[1] == "object") {
    for (var j in arguments[1])
      if (arguments[1][j] != null) {
        if (OneSpeedTestBrowser == "ie") {
          switch (j.toLowerCase()) {
            case "onclick":
            case "onkeyup":
            case "onkeydown":
              elt[j.toLowerCase()] = new Function(arguments[1][j]); /* gay special cases for IE 6 */
            break;
            case "class":
              elt.setAttributeNS(null, "className", arguments[1][j]); /* ie needs this instead.. */
            elt.setAttributeNS(null, "class", arguments[1][j]);
            break;
            default:
            elt.setAttributeNS(null, j, arguments[1][j]);
            break;
          }
        } else {
          elt.setAttributeNS(null, j, arguments[1][j]);
        }
      }
  }
  for (var i=2; i < arguments.length; ++i ) {
    if (arguments[i] != null) {
      if (isArray(arguments[i])) {
        for (var j in arguments[i]) {
          elt.appendChild(arguments[i][j]);
        }
      } else {
        elt.appendChild(arguments[i]);
      }
    }
  }
  return elt;
} /* }}} */
OneSpeedTest.prototype.newText                     = function (txt, config) { /* {{{ */
  if (this.svg == null) return;

  var data = this.svgDocument.createTextNode(txt);

  var textNode = this.newElt("text", config, data);
  return textNode;
} /* }}} */
OneSpeedTest.prototype.updateStats                 = function () { /* {{{ */
  return;

} /* }}} */
OneSpeedTest.prototype.elt                         = function (id) { /* {{{ */
return null;

} /* }}} */
OneSpeedTest.prototype.ping                        = function (toCallWhenDone) { /* {{{ */
  var self = this;
  self.terminalPrint("ping() -> ");
  	

  var start = new Date();
  //pull("OneSpeedTest.php?len=1", "", function () {
  pull("/fileadmin/dslspeed/speedtest.ping", "", function () {
    var end = new Date();
    var latency = end.getTime() - start.getTime();
    self.latency.push(latency);
    self.lastRunTime  = latency;
    self.updateStats();
    self.printResults("<p>Pingtest -> " + latency + " ms</p>");
    if(pingTestCounter == OneSpeedTestNumPingsToRun)  self.printResults("<p>&nbsp;</p>");
    if (toCallWhenDone) { toCallWhenDone(); }
  });
  pingTestCounter++;
} /* }}} */
OneSpeedTest.prototype.downloadTest                = function (size, toCallWhenDone) { /* {{{ */
  var self = this;

  self.terminalPrint("download("+prettySize(size)+") -> ");

  var start = new Date();
  pull("/fileadmin/dslspeed/speedtest.php?len="+size,"", function () {
    var end = new Date();
    self.lastRunTime = end.getTime() - start.getTime();
    var downloadTime = (self.lastRunTime - self.getAvgLatency()) * 0.001;
    self.downbps.push(size/downloadTime);
    self.printResults("<p>Download von "+prettySize(size)+" in " + self.lastRunTime + " ms</p>");
    
    self.updateStats();
    if (toCallWhenDone) { toCallWhenDone(); }
  });

} /* }}} */

OneSpeedTest.prototype.uploadTest                  = function (size, toCallWhenDone) { /* {{{ */
  var self = this;

  if (this.data_size == null || this.data_size != size)
    this.data = "data="+genStr(size); /* When we kick it into high gear on fast networks, lets not eat up all our ram. */
  this.data_size = size;

  self.terminalPrint("upload("+prettySize(size)+") -> ");

  var start = new Date();
  pull("/fileadmin/dslspeed/speedtest.php", this.data, function () {
    var end = new Date();
    self.lastRunTime = end.getTime() - start.getTime();
    var uploadTime = (self.lastRunTime - self.getAvgLatency()) * 0.001;
    self.upbps.push(size/uploadTime);
    self.printResults("<p>Upload von "+prettySize(size)+" in " + self.lastRunTime + " ms</p>");
    if((self.numULReal+1) == OneSpeedTestNumDLsToRun)  self.printResults("<p>&nbsp;</p>");
    self.updateStats();
    if (toCallWhenDone) { toCallWhenDone(); }
  });
} /* }}} */

OneSpeedTest.prototype.calibrate                   = function (toCallWhenDone) { /* {{{ */
  var self = this;
  var total_dl_calib = 0;
  var total_ul_calib = 0;
  

  function runPingTest(ct, toCallWhenDone) {
    if (ct > 0) {
      self.numPingsRan++;
      self.ping(function () { runPingTest(ct-1, toCallWhenDone); });
    } else {
      self.snapshotProgress("Latency: ~" + self.getAvgLatency() + " ms");
       
      document.getElementById('hidden_inputs').innerHTML += '<input type="hidden" value="'+self.getAvgLatency()+'" name="ping"/>';
      if (toCallWhenDone != null) toCallWhenDone();
    }
  }

  function runDownloadTest(size, toCallWhenDone) {
  
  	
    self.totalDLCalibrationTime += self.lastRunTime;
    if (self.numDLCalibration < OneSpeedTestMinCalibrationSteps ||
        (self.numDLCalibration < OneSpeedTestMaxCalibrationSteps &&
         ((self.lastRunTime < OneSpeedTestTestTime * OneSpeedTestCalibrationSlack) ||
          (self.lastRunTime > (OneSpeedTestTestTime*2))))) 
    {
      var oldSize = size;
      if (self.lastRunTime*2 > OneSpeedTestTestTime) 
        size *= (OneSpeedTestTestTime/self.lastRunTime)*0.6;
      if (size == Infinity) {
        size = oldSize;
        self.numDLCalibration = OneSpeedTestMaxCalibrationSteps;
      } else if (size > OneSpeedTestMaxDownloadSize) {
        size = OneSpeedTestMaxDownloadSize/2;
        self.numDLCalibration = OneSpeedTestMaxCalibrationSteps;
      } else if (size < 1) {
        size = 1;
      }
      self.numDLCalibration++;
      self.downloadTest(size, function () { runDownloadTest(size*2, toCallWhenDone); });
    } else {
      self.goodDLSize = size * .5 * (OneSpeedTestTestTime/self.lastRunTime); 
      if (self.goodDLSize > OneSpeedTestMaxDownloadSize) {
        self.goodDLSize = OneSpeedTestMaxDownloadSize;
      }
      if (self.goodDLSize < 1) self.goodDLSize = 1;
      self.terminalPrint("DL Size Calibrated: " + prettySize(self.goodDLSize));
      self.printResults("<p>&nbsp;</p><p>Kalibrierte Download-Datei: " + prettySize(self.goodDLSize)+"</p>");
      if (toCallWhenDone != null) toCallWhenDone();
    }
  }

  function runUploadTest(size, toCallWhenDone) {
    self.totalULCalibrationTime += self.lastRunTime;
    if (self.numULCalibration < OneSpeedTestMinCalibrationSteps ||
        (self.numULCalibration < OneSpeedTestMaxCalibrationSteps &&
         ((self.lastRunTime < OneSpeedTestTestTime * OneSpeedTestCalibrationSlack) ||
          (self.lastRunTime > (OneSpeedTestTestTime*2))))) 
    {
      var oldSize = size;
      if (self.lastRunTime*2 > OneSpeedTestTestTime) 
        size *= (OneSpeedTestTestTime/self.lastRunTime)*0.6; 
      if (size == Infinity) {
        size = oldSize;
        self.numULCalibration = OneSpeedTestMaxCalibrationSteps;
      } else if (size > OneSpeedTestMaxUploadSize) {
        size = OneSpeedTestMaxUploadSize/2;
        self.numULCalibration = OneSpeedTestMaxCalibrationSteps;
      } else if (size < 1) {
        size = 1;
      } 
      self.numULCalibration++;
      self.uploadTest(size, function () { runUploadTest(size*2, toCallWhenDone); });
    } else {
      self.goodULSize = size * .5 * (OneSpeedTestTestTime/self.lastRunTime); 
      if (self.goodULSize > OneSpeedTestMaxUploadSize) {
        self.goodULSize = OneSpeedTestMaxUploadSize;
      }
      if (self.goodULSize < 1) self.goodULSize = 1;
      self.terminalPrint("UL Size Calibrated: " + prettySize(self.goodULSize));
      self.printResults("<p>&nbsp;</p><p>Kalibrierte Upload-Datei: " + prettySize(self.goodULSize)+"</p>");
      self.snapshotProgress("Calibration Done");
      if (toCallWhenDone != null) toCallWhenDone();
    }
  }
  
 
 
  runPingTest(OneSpeedTestNumPingsToRun, function () { 
    self.lastRunTime = 0;
    runDownloadTest(OneSpeedTestInitialDownloadSize, function () {
      self.lastRunTime = 0;
      runUploadTest(OneSpeedTestInitialUploadSize, toCallWhenDone);
    }); 
  });
} /* }}} */
OneSpeedTest.prototype.speedTest                   = function () { /* {{{ */
  var self = this;

  function runDLTest(ct, toCallWhenDone) {
    if(OneSpeedTestStop) return;
    if (ct > 0) {
   	  
      self.downloadTest(self.goodDLSize, function () { 
          self.numDLReal++;
          runDLTest(ct-1, toCallWhenDone); });
    } else {
      self.snapshotProgress("Download: " + self.getAvgDLRate());
      document.getElementById('speedtest_result_download_speed').innerHTML = self.getAvgDLRate();
      document.getElementById('hidden_inputs').innerHTML += '<input type="hidden" value="'+self.getAvgDLRate()+'" name="download" />';
      if (toCallWhenDone != null) toCallWhenDone();
    }
  }
  function runULTest(ct, toCallWhenDone) {
    if(OneSpeedTestStop) return;
    if (ct > 0) {
      
      self.uploadTest(self.goodULSize, function () { 
          self.numULReal++;
          runULTest(ct-1, toCallWhenDone); });
    } else {
      self.snapshotProgress("Upload: " + self.getAvgULRate());
      document.getElementById('speedtest_result_upload_speed').innerHTML = self.getAvgULRate();
      document.getElementById('hidden_inputs').innerHTML += '<input type="hidden" value="'+self.getAvgULRate()+'" name="upload" />';
      if (toCallWhenDone != null) toCallWhenDone();
    }
  }

  function printSummary() {
    
  }

  function RunSpeedTests () {
    if(OneSpeedTestStop) return;

    self.upbps   = new Array();
    self.downbps = new Array();

    document.getElementById('progress_icon').src = "/fileadmin/images/speedtest/icon-upload.gif";
    document.getElementById('progress_label').innerHTML = "Upload-Geschwindigkeit messen";
    runULTest(OneSpeedTestNumDLsToRun, function () {
        document.getElementById('progress_icon').src = "/fileadmin/images/speedtest/icon-download.gif";
        document.getElementById('progress_label').innerHTML = "Download-Geschwindigkeit messen";
        runDLTest(OneSpeedTestNumULsToRun, function () { 
            document.getElementById('protocoldiv').style.display = "none";
         
            document.getElementById('mb_center').style.height = "330px";
            document.getElementById('mb_center').style.width = "630px";
            document.getElementById('resultdiv').style.display = "block";

            document.getElementById('mb_contents').style.width = "630px";

            //setTimeout("document.getElementById('mb_contents').innerHTML = document.getElementById('protocoldiv').innerHTML;", 500);
            //setTimeout("document.getElementById('header_end').style.display = \"\";",600);
            printSummary();
            self.updateStats();
        });
    });
  }

  this.calibrate(RunSpeedTests);

} /* }}} */
OneSpeedTest.prototype.terminalPrint               = function (str, replaceLastLine) { /* {{{ */
  return;
} /* }}} */
OneSpeedTest.prototype.printResults                = function (str) { /* {{{ */
  document.getElementById("OneSpeedTestPlainOutput").innerHTML = str + document.getElementById("OneSpeedTestPlainOutput").innerHTML;

} /* }}} */
OneSpeedTest.prototype.printSummary                = function (str) { /* {{{ */
    if(OneSpeedTestStop) return;
    document.getElementById("OneSpeedTestPlainOutput").innerHTML = str + document.getElementById("OneSpeedTestPlainOutput").innerHTML;
  
} /* }}} */
OneSpeedTest.prototype.percentDone                 = function () { /* {{{ */
  var PingsPortion              = 0.125;
  var DLCalibrationPortion      = 0.125;
  var ULCalibrationPortion      = 0.125;
  var DLRealPortion             = 0.3125;
  var ULRealPortion             = 0.3125;

  //var magicCalibrationTimeRatio = 2.7;
  var magicCalibrationTimeRatio = 2.5;
  var magicCalibrationTime      = OneSpeedTestTestTime * magicCalibrationTimeRatio;

  var DLCalibDone               = this.totalDLCalibrationTime / magicCalibrationTime;
  var ULCalibDone               = this.totalULCalibrationTime / magicCalibrationTime;
  DLCalibDone                   = DLCalibDone > 1 ? 1 : DLCalibDone;
  ULCalibDone                   = ULCalibDone > 1 ? 1 : ULCalibDone;
  DLCalibDone                   = this.numDLReal > 0 ? 1 : DLCalibDone;
  ULCalibDone                   = this.numULReal > 0 ? 1 : ULCalibDone;



  return 0.0
    + (this.numPingsRan / OneSpeedTestNumPingsToRun) * PingsPortion 
    + DLCalibDone * DLCalibrationPortion 
    + ULCalibDone * ULCalibrationPortion 
    + (this.numDLReal / OneSpeedTestNumDLsToRun) * DLRealPortion 
    + (this.numULReal / OneSpeedTestNumULsToRun) * ULRealPortion 
    ;
} /* }}} */
OneSpeedTest.prototype.snapshotProgress            = function (txt) { /* {{{ */
  if (this.svg == null) return;

  this.status = txt;
  this.updateStats();

  this.progressTriangle = this.progressTriangle.cloneNode(true);
  this.svg.appendChild(this.progressTriangle);

  this.progressText = this.progressText.cloneNode(true);
  this.svg.appendChild(this.progressText);

  this.status = "";

} /* }}} */
OneSpeedTest.prototype.getAvgLatency               = function () { /* {{{ */
  this.latency.filterMin();
  return (this.latency.sum()/this.latency.length).toFixed(1);
} /* }}} */
OneSpeedTest.prototype.getAvgDLRate                = function () { /* {{{ */
  this.downbps.filterMax();
  return prettyRate(this.downbps.avg()*8);
} /* }}} */
OneSpeedTest.prototype.getAvgULRate                = function () { /* {{{ */
  this.upbps.filterMax();
  return prettyRate(this.upbps.avg()*8);
} /* }}} */
OneSpeedTest.prototype.getAvgDLbps                 = function () { /* {{{ */
  this.downbps.filterMax();
  return this.downbps.avg()*8*OneSpeedTestProtocolOverhead;
} /* }}} */
OneSpeedTest.prototype.getAvgULbps                 = function () { /* {{{ */
  this.upbps.filterMax();
  return this.upbps.avg()*8*OneSpeedTestProtocolOverhead;
} /* }}} */
OneSpeedTest.prototype.estimateLineType            = function () { /* {{{ */
  var ubps          = this.getAvgULbps();
  var dbps          = this.getAvgDLbps();
  var minErr        = 99999;
  var name          = "";
  var errThreshold  = 1.1;

  for (var i in OneSpeedTestLineTypes) {
    var line = OneSpeedTestLineTypes[i];

    if (ubps > line.up*errThreshold || dbps > line.down*errThreshold)
      continue;

    var err = Math.abs(1-line.up/ubps) + Math.abs(1-line.down/dbps);
    if (err < minErr) { 
      minErr = err;
      name = line.name;
    }
  }

  return name;
} /* }}} */



/** Utility Functions {{{ **/

function getHttpRequester() { /* {{{ */
  var xmlhttp=false;
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
   try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
   } catch (e) {
     try {
       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
     } catch (E) {
       xmlhttp = false;
    }
   }
  @end @*/
  if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
    xmlhttp = new XMLHttpRequest();
  }

  return xmlhttp;
} /* }}} */
function pull(src, post, callback, opt_method) { /* {{{ */
  //try {
    if (src) {
    
      var X = getHttpRequester();
  
      if (X) {
        X.open("POST", src,true);
        X.onreadystatechange=function() {
          if (X.readyState==4) {
            if (opt_method != null) {
              if (!callback[opt_method]) {
                alert(opt_method + " was not found within class");
              }
  
              callback[opt_method]();
            } else if (callback) {
              callback();
            } 
          } 
        };
        X.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        X.send(post);
      } else {
        alert("pull called but the browser doesn't seem to support the XMLHttpRequest object.");
      }
    } else {
      alert("pull called with an invalid source = null");
    }
    /*
  } catch (e) {
    if (opt_method != null) {
      if (!callback[opt_method]) {
        alert(opt_method + " was not found within class");
      }

      callback[opt_method]();
    } else if (callback) {
      callback();
    } 
  }
  */
} /* }}} */
function isArray(obj) { /* {{{ */
  return (obj &&
      obj.splice &&
      obj.reverse &&
      obj.push &&
      obj.pop &&
      obj.sort &&
      obj.slice &&
      obj.shift &&
      obj.unshift) ? true : false;
} /* }}} */
function prettySize(size) { /* {{{ */
  var html = "";

  if (typeof(size) == "string") {
    size = parseFloat(size);
  }
  if (size > 1073741824) {
     html += (size/1073741824).toFixed(1) + " GB";
  } else if (size > 1048576) {
     html += (size/1048576).toFixed(1) + " MB";
  } else if (size > 1024) {
     html += (size/1024).toFixed(1) + " KB";
  } else {
     html += size.toFixed(0) + " B";
  }

  return html;
} /* }}} */
function prettyRate(bps) { /* {{{ */
  var rate = prettySize(bps);
  rate = rate.substring(0,rate.length-1) + 'bit/s';
  return rate;
} /* }}} */
function prettyTime(pSecs) { /* {{{ */
  

  /* recursive for days, hours and minutes */
  switch ( true ) {
    case ( isNaN(pSecs) ):
      return "Unknown";
    case ( pSecs == Infinity ):
      return "Unknown";
    case (pSecs >= 86400):     /* days */
      return (pSecs / 86400).toFixed(0) + " days " + prettyTime(pSecs % 86400);
    case (pSecs >= 3600 ):     /* hours */
      return (pSecs / 3600).toFixed(0) + " hr " + prettyTime(pSecs % 3600);
    case (pSecs >= 60 ):       /* minutes */
      return (pSecs / 60).toFixed(0) + " min " + prettyTime(pSecs % 60);
    case (pSecs < 60):
      return (1.0*pSecs).toFixed(1) + " s "
    default:
        return "Unknown";
  }


} /* }}} */
var n_genStr = 0;
function genStr(len) { /* {{{ */
    n_genStr = (n_genStr+1)%10;
    var ret = n_genStr+"";

    while (1) {
      if (ret.length > len) {
        return ret.substr(0, len);
      }
      if (ret.length*2 >= len) {
        return ret+ret.substr(0, len-ret.length);
      }
      ret += ret;
    }

  /* IE has issues creating 'big' arrays, so we need to work around it .. and not do something like this :( */
  /* 
    n_genStr = (n_genStr+1)%10;
    var seg_size = parseInt((parseInt(len+"")/100)+"");
    var rem_size = parseInt((parseInt(len+"")%100)+"");

    return new Array(100).join(new Array(seg_size).join(n_genStr+""))+new Array(rem_size).join(n_genStr);
  */
} /* }}} */

Array.prototype.sum                              = function() { /* {{{ */
	for(var i=0,sum=0;i<this.length;sum+=this[i++]);
	return sum;
} /* }}} */
Array.prototype.filterMin                        = function() { /* {{{ */
  var min = this.min();
  for (var i=0; i < this.length; ++i) {
    if (this[i] > (min*2)) {
      this.splice(i, 1);
      i-=1;
    }
  }
} /* }}} */
Array.prototype.filterMax                        = function() { /* {{{ */
  var max = this.max();
  for (var i=0; i < this.length; ++i) {
    if (this[i] < (max/2)) {
      this.splice(i, 1);
      i-=1;
    }
  }
} /* }}} */
Array.prototype.avg                              = function(){ /* {{{ */
  return this.sum()/this.length;
} /* }}} */
Array.prototype.min                              = function(){ /* {{{ */
	return Math.min.apply({},this)
} /* }}} */
Array.prototype.max                              = function(){ /* {{{ */
	return Math.max.apply({},this)
} /* }}} */

/* }}} */
