var pTimer = {
  timerTable: new Array(),
  dateObj:    new Date(),
  baseTime:    Math.round ((new Date()).getTime() / 1000),
  lastTime:    Math.round ((new Date()).getTime() / 1000),
  ajaxOn:      false,
  ON_CREATE_FROM_PERIODIC:   false,
  ON_COMPLETE_FROM_PERIODIC: false,
  onTick:      function() {
     // CHECK AJAX
      var i=0;
      var now = Math.round ((new Date()).getTime() / 1000);
      var next;
  
      if (pTimer.ajaxOn) {
          if (now - pTimer.lastTime > 30) {
              pTimer.resetTable();            
              pTimer.ajaxOn = false;
              //alert ('ajax timeout');
          } else {
              return;
          }
      }
  
      var comp = now - pTimer.baseTime;
      var longestWaitIndex = -1;
      var longestWaitTime = -1;
      var last = 0;
      var waitTime = 0;
  
      // find the fn that's been waiting the longest
      for (i=0; i<pTimer.timerTable.length; ++i) {
          next = pTimer.timerTable[i]['next'];
          last = pTimer.timerTable[i]['last'];
          waitTime = comp - last;
  
          if (comp >= next) {
              if (waitTime > longestWaitTime) {
                  longestWaitIndex = i;
                  longestWaitTime = comp - pTimer.timerTable[i]['last'];
              }
          }
      }
  
      // run the guy who's been waiting the longest and postpone others who should have run
      for (i=0; i<pTimer.timerTable.length; ++i) {
          next = pTimer.timerTable[i]['next'];
          if (comp >= next) {
              if (i == longestWaitIndex) {
                  pTimer.timerTable[i]['fn']();
                  pTimer.timerTable[i]['last'] = comp;                          
                  pTimer.timerTable[i]['next'] += pTimer.timerTable[i]['period'];
                  //alert ('run ' + pTimer.timerTable[i]['fn'] + ', next= ' +  pTimer.timerTable[i]['next']);
              } else {
                  pTimer.timerTable[i]['next'] += Math.min (pTimer.timerTable[i]['period'], 5);
                  //alert ('delay ' + i + ' until ' +  pTimer.timerTable[i]['next'] + 'lwi=' + longestWaitIndex);
              }
          }
      }
  
      pTimer.lastTime = now;
  },
  onAjaxCreate:   function() { if (!pTimer.ON_CREATE_FROM_PERIODIC && (Ajax.activeRequestCount > 0)) {pTimer.ajaxOn=true; /*alert ('ajax on');*/ } },
  onAjaxComplete: function() { if (!pTimer.ON_COMPLETE_FROM_PERIODIC && (Ajax.activeRequestCount <= 0)) {pTimer.ajaxOn=false; pTimer.resetTable (); /*alert('ajax off');*/ } },
  resetTable:     function() {
      pTimer.baseTime = Math.round ((new Date()).getTime() / 1000);
      var i=0;
      for (i=0; i<pTimer.timerTable.length; ++i) {
          pTimer.timerTable[i]['next'] = pTimer.timerTable[i]['period'] + pTimer.timerTable[i]['offset'];
          pTimer.timerTable[i]['last'] = 0;
          //alert(i + ': next=' +  pTimer.timerTable[i]['next']);
      }
      
  },

  updateOnCreateVar:   function(ajaxRequest) { pTimer.ON_CREATE_FROM_PERIODIC=(typeof(ajaxRequest.options['FROM_PERIODIC'])!='undefined' && ajaxRequest.options['FROM_PERIODIC']); },

  updateOnCompleteVar: function(ajaxRequest) { pTimer.ON_COMPLETE_FROM_PERIODIC=(typeof(ajaxRequest.options['FROM_PERIODIC'])!='undefined' && ajaxRequest.options['FROM_PERIODIC']); }

};

