// ==UserScript== (by Edward Lee)
// @name          Firefox Download Counter
// @namespace     http://ed.agadak.net/
// @description	  Put an animated counter on every page (click to fade, click/drag to move, double click firefox to close)
// ==/UserScript== (edilee gmail)

window.addEventListener('load', function() {
var removed = false;

// click fader, click n drag movement, double click closer
with (document.getElementById('ffCounter')) {
  var unfade = false, fading = false;
  var fadeRate = .1, fadeTime = 20;
  var maxOpacity = 1, minOpacity = .1;

  style.MozOpacity = minOpacity;

  function fade() {
    fading = true;

    function doFade(inc) {
      style.MozOpacity = Math.min(.999, Math.max(0, style.MozOpacity - 0 + inc)); // image flickers when it hits 1, so go to .999
      setTimeout(function() { fade(); }, fadeTime);
    }

    if (unfade == true && style.MozOpacity < Math.min(1, maxOpacity)) {
      doFade(fadeRate);
    } else if (unfade == false && style.MozOpacity > Math.max(0, minOpacity)) {
      doFade(-fadeRate);
    } else {
      fading = false;
    }
  }

  var lastClick = false, movable = false;
  addEventListener('mousedown', function(e) {
    movable = {"x": e.screenX, "y": e.screenY, "startX": style.left, "startY": style.top}; // style is of ffCounter

    if (e.layerX > intVal(style.width) - 31) { // clicked on the firefox part of the image
      var time = new Date().getTime();
      if (lastClick && time - lastClick < 500) {
        style.display = parentNode.style.display = 'none'; // remove container.. and counter? too (not sure why the counter stays)
        removed = true;
//        parentNode.parentNode.removeChild(parentNode); // remove ffCounter's parent ffContainer
      } else {
        lastClick = time;
      }
    }
  }, false);
  window.addEventListener('mouseup', function(e) {
    if (movable.x == e.screenX && movable.y == e.screenY) { // fade toggle if not moved
      unfade = !unfade;
      if (fading == false) { // only fade if not already fading
        fade();
      }
    }
    movable = false;
  }, false);
  window.addEventListener('mousemove', function(e) {
    if (movable != false) {
      with (style) { // this is ffCounter's style
        top = (e.screenY - intVal(movable.y) + intVal(movable.startY)) + 'px';
        left = (e.screenX - intVal(movable.x) + intVal(movable.startX)) + 'px';
      }
    }
  }, false);
//  addEventListener('mouseover', function() { unfade = true; fading || fade(); }, true);
//  addEventListener('mouseout', function() { unfade = false; fading || fade(); }, true);
}

// various images in base64 data uri encoding
var images = {
  'blank': 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
  'comma': 'data:image/gif;base64,R0lGODlhAwASAKIAAAAIPgAIPQAKRwAKRgAIOx0mXjxEdP///yH5BAEAAAcALAAAAAADABIAAAMOeLrc/pCZcoo4g5xwgEoAOw==',
  '0': 'data:image/gif;base64,R0lGODlhCgAPANUAAAAJRQAJRAAHNQAGLw0WUhMcV0JJeEZNe0dOfFthigAKSQAKRgAJQwAJQAAIPAAIOgAIOAAINwAHMgAHMAAGLAAGKQAGKAAFJQoUUBYfWRskXCIrYSQtYyUuYy83azE5bDQ8bjpCcklQfUtSf1hfiGVrkf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACYALAAAAAAKAA8AAAZLQJNwSCSWEqOiiSQygTTFwxATGCI+Q0Vj6CkMF44hhxh4CA0bIgAiDEGHDIHw8xY2JMJOBs4WEhRDEANfDEMTRQ4RJhMVShQWF0RBADs=',
  '1': 'data:image/gif;base64,R0lGODlhCgAPAMQAAAAJRQAJRAAIPQAHNQ0WUhEaVUZNe1thil9ljQAKSQAKSAAIPAAIOwAIOgAIOAAHNAAHMgAHMAAGKwAFJQAFIwELSgQOTBYfWSQtYyYvZDI6bTtDcz5GdUpRfmNpkP///yH5BAEAAB8ALAAAAAAKAA8AAAUz4CeOZClupnhgpodwSmmI15JmAZQSTfolA18lmCo4Uh2CTwPwXQSpS+BRsgQYkQlFIgoBADs=',
  '2': 'data:image/gif;base64,R0lGODlhCgAPAMQAAAAJRAAIPgAHNgAFJykxZkRLegAKSQAKSAAKRwAJQgAJQQAJPwAIOwAIOQAIOAAINwAHMwAHMAAGKhcgWRkiWxwlXSUuYyw0aC83azhAcUxTf1JZhFNahFVchv///wAAACH5BAEAAB4ALAAAAAAKAA8AAAUzoCeOZMl1Rell4mWQ1jglqng0tRdEuZMDghoFkCPkNLlObkNikDCVw+IBGSEUjohkMAoBADs=',
  '3': 'data:image/gif;base64,R0lGODlhCgAPAMQAAAAJRQAJRAAHNwAHNQ0WUhAZVCcvZSgwZgAKRwAKRgAJQQAJPwAJPgAIOwAHMgAGLAIMSgUPTQgSTxghWhskXB8oXy42ai83azY+cDhAcT5GdUxTf09Wgf///wAAAAAAACH5BAEAAB0ALAAAAAAKAA8AAAUwYCeOZLlxWtlh4gGpIoXAHRHQEW3Ql1XRnUmCVljQEAJa4wFTOEgZESBJkgAYA1IIADs=',
  '4': 'data:image/gif;base64,R0lGODlhCgAPANUAAAAJRAAHNgAGLgAFJwAFJgAEIBAZVEBHd0JJeFxiiwAKSAAKRwAKRgAJQwAJQAAJPwAJPgAHNAAHMgAHMAAGKQAFJQAFIwELSgQOTBkiWy42ajE5bDQ8bk1UgFJZhFhfiFlgiWdtk2pwlW1zl////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACQALAAAAAAKAA8AAAY+QJJwSCwOOUahJ5MMbQDGUQdjFIE0D2OCZAgUP0LGhIg4CB0ComYISQsXl4uiERlUjAFKkiQh7AUWexQFQ0EAOw==',
  '5': 'data:image/gif;base64,R0lGODlhCgAPAMQAAAAJRQAJRAAHNREaVRIbVikxZl9ljQAKRgAJQwAJQgAJQQAJQAAJPwAJPgAIOwAIOgAIOAAINwAHNAAHMgELSgIMSgcRTggSTwoUUAsVURwlXS42ajc/cElQff///wAAACH5BAEAAB4ALAAAAAAKAA8AAAUxoCeOI0Z6RldQzMkRSOSe5EaPWnaL1rF7GMVusNhVHrvEZOfYBSQkjggAOV0ADcEpBAA7',
  '6': 'data:image/gif;base64,R0lGODlhCgAPANUAAAAJRQAJRAAIPQAHNgAHNQAGLgAFKAAFJxIbVhMcVykxZkBHd1xii11jiwAKSQAJQQAJQAAJPgAIPAAIOwAIOAAGLAAFJAELSgUPTRQdVxUeWBskXB4nXh8oXzlBcjpCcj9HdklQfU5VgVVchmpwlXB2mf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACYALAAAAAAKAA8AAAZDQJNwSCwKMw7jgmM0jZomUqhZakAZoA0mQBR9EkKIZOjpDCOUoQIxFBCGmqRwUBheuMLCYQh4DCtFEwMmekYVBhZEQQA7',
  '7': 'data:image/gif;base64,R0lGODlhCgAPAMQAAAAJRAAIPgAHNQAFKA4XUxAZVENKeQAKSQAKRwAKRgAJQwAJQgAIOwAIOAAHMwAGLAAGKgQOTAYQTQwWUhwlXR0mXiMsYjA4a1hfiGFnjv///wAAAAAAAAAAAAAAAAAAACH5BAEAABoALAAAAAAKAA8AAAUtoCaO5JhlmFEhwHhZRcI443SMT0kOugj1GgFQEwAShhIgZRgBFhZARQPI6IUAADs=',
  '8': 'data:image/gif;base64,R0lGODlhCgAPANUAAAAJRQAJRAAIPgAIPQAHNgAFJwAFJigwZkBHd0FId0VMegAKSQAKRwAJPgAIPAAIOgAIOQAINwAHMQAHMAAGLQAGLAAGKgAGKQAFJAAFIwMNSxYfWR8oXyEqYTQ8bjU9bzc/cDhAcUlQfUpRflBXglFYg1deh1ZdhlhfiGFnjmJoj////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAKAA8AAAZIwJVwSCyiEIfiSqUQBoip07BBNImGEOIoJIwoDxqqkrMYKEErhqNI8ggfkmEpMSRQhp/OcHIZbhZDFgZDAAJCFRhFEhYFGURBADs=',
  '9': 'data:image/gif;base64,R0lGODlhCgAPANUAAAAJRQAJRAAIPgAIPQAHNgAHNQAGLwAFKAAEIA0WUigwZlxii2BmjgAKRwAJQwAJQgAJQQAJPwAIPAAHNAAHMwAHMAAGKwAGKQAFIwcRTgsVURkiWxwlXSIrYTc/cDhAcT9HdkxTf05VgVJZhFVchmdtk////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACYALAAAAAAKAA8AAAZEQJNwSCSWFqGiiTEyeThF0jABGJJEwwxkCPoMG5KhojN8EIabzXBQGWoSQsfkQmwEIoWDUnhB7A0YeyYWewIUSg1bSkEAOw=='
};

// set up the initial value of the counter
var lastValueRev; // reversed last value
function prepCounter(num) {
  if (lastValueRev != undefined) { // only prep once
    return;
  }

  lastValueRev = reverseNum(num);
  var len = getCommaSize(num);
  updateWidth(len);
  with (document.getElementById('ffNumbers')) {
    for (var i = 0, j = 0; i < len; i++) {
      var img = document.createElement('img');
      if (isCommaSpot(len - i)) { // it's a comma
        img.src = images['comma'];
        img.className = 'comma'
        img.style.position = 'relative';
        img.style.left = '-1px';
      } else {
        img.id = 'ffNumber' + (num.length - j - 1);
        img.src = images[num[j]];
        j++;
      }
      appendChild(img);
    }
  }
}

// update the counter
var updateTime = 100;
function updateCounter(num) {
  var len = num.length, lenC = getCommaSize(num);
  var len2 = lastValueRev.length, lenC2 = getCommaSize(lastValueRev);
  var reverse = reverseNum(num);

  if (len < len2) { // need to shrink
    for (var i = lenC2, ii = len2; i > lenC; i--) {
      var img, j, k;
      if (isCommaSpot(i)) {
        img = null;
      } else {
        img = document.getElementById('ffNumber' + (ii - 1));
        ii--;
      }

      for (var j = 0; j > -1; j -= 1/20) {
        var k = i + j;
        setTimeout((function(j, k, img) { return function() {
          updateWidth(k);

          if (img != null) {
            var scaling = Math.max(0, 1 + j - 1/20);
            img.style.MozOpacity = scaling;
            img.style.width = Math.round(scaling * 10) + 'px';
            with (img.nextSibling) {
              if (className == 'comma') { // it's a comma
                style.MozOpacity = scaling;
              img.style.width = Math.round(scaling * 3) + 'px';
              }
            }
          }
        }; }) (j, k, img), (lenC2 - k) * updateTime);
      }
      setTimeout((function(img) { return function() {
        if (img != null) {
          img.src = images['blank'];
          img.style.backgroundImage = 'url(' + images['blank'] + ')';
          img.style.display = 'none';
          with (img.nextSibling) {
            if (className == 'comma') { // it's a comma
              style.display = 'none';
            }
          }
        }
      }; }) (img), (lenC2 - i - j) * updateTime);
    }
  } else if (len > len2) { // need to grow
    for (var i = lenC2, ii = len2; i < lenC; i++) {
      var img, j, k;
      if (isCommaSpot(i)) {
        img = null;
      } else {
        img = document.getElementById('ffNumber' + (ii - 0));
        if (img == null) { // need to make a new image
          if (isCommaSpot(i+1)) { // new comma too
            img = document.createElement('img');
            img.className = 'comma';
            img.src = images['comma'];
            img.style.width = '0px';
            img.style.position = 'relative';
            img.style.left = '-1px';
            img.style.display = 'none';
            img.style.MozOpacity = 0;
            with (document.getElementById('ffNumbers')) {
              insertBefore(img, firstChild);
            }
          }
          img = document.createElement('img');
          img.id = 'ffNumber' + (ii - 0);
          img.src = images['blank'];
          img.style.width = '0px';
          img.style.display = 'none';
          img.style.MozOpacity = 0;
          with (document.getElementById('ffNumbers')) {
            insertBefore(img, firstChild);
          }
        }
        ii++;
      }

      setTimeout((function(img) { return function() {
        if (img != null) {
          img.style.display = '';
          with (img.nextSibling) {
            if (className == 'comma') { // it's a comma
              style.display = '';
            }
          }
        }
      }; }) (img), (i - lenC2) * updateTime);

      for (var j = 0; j < 1; j += 1/20) {
        var k = i + j;
        setTimeout((function(j, k, img) { return function() {
          updateWidth(k);

          if (img != null) {
            var scaling = Math.min(1, j + 1/20);
            img.style.MozOpacity = scaling;
            img.style.width = Math.round(scaling * 10) + 'px';
            with (img.nextSibling) {
              if (className == 'comma') { // it's a comma
                style.MozOpacity = scaling;
                style.width = Math.round(scaling * 3) + 'px';
              }
            }
          }
        }; }) (j, k, img), (k - lenC2) * updateTime);
      }
    }
  }

  // update the digits after the space has been prepared
  for (var i = 0; i < len; i++) {
    if (reverse[i] != lastValueRev[i]) {
      setTimeout((function(i) { return function() {
        shiftDigit('ffNumber' + i, reverse[i]);
      }; }) (i), i * updateTime);
    }
  }

  lastValueRev = reverse;
}

function updateWidth(lengthWithComma) {
  document.getElementById('ffCounter').style.width = Math.round(10 + lengthWithComma * 8.234 + 31) + 'px'; // scale the size of the "background" image plus partial comma
}

function getCommaSize(num) {
  return num.length + Math.floor((num.length - 1) / 3);
}

function isCommaSpot(num) {
 return num % 4 == 0;
}

// digit shifting
var shiftRate = 1, shiftTime = 15, shiftAmount = 20; // 15 * 20 = 300 ms to update
function shiftDigit(id, num) {
  with (document.getElementById(id)) {
    style.backgroundImage = 'url(' + images[num] + ')'; // insert the new number
    style.backgroundPosition = '0px ' + shiftAmount + 'px';
    var i;
    for (i = 0; i <= shiftAmount; i += shiftRate) {
      setTimeout((function(i) { return function() {
        style.backgroundPosition = '0px ' + Math.min(0, -shiftAmount + 1 + i) + 'px';
        style.paddingTop = Math.min(shiftAmount, i) + 'px';
      }; }) (i), i * shiftTime);
    }
    setTimeout(function() {
      src = images[num]; // make the new number the actual image
      style.paddingTop = '0px'; // shift it back to the original position
    }, i * shiftTime);
  }
}


// test cases
prepCounter('50000000');
updateIt = updateCounter;

/*// shift it! with different sizes.. choppyish...
prepCounter('0');
setTimeout(function() { updateCounter('50123456'); }, 1000);
setTimeout(function() { updateCounter('546'); }, 2500);
setTimeout(function() { updateCounter('56105987'); }, 5000);
*/

/*// really really realllyy long number :o
prepCounter('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
*/

/*// watch all 100 digits shift down! wheeee ;)
prepCounter('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
for (var i = 0; i < 100; i++) {
  setTimeout((function(i) { return function() { shiftDigit('ffNumber' + i, i % 10); }; }) (i), 100*i);
}
*/

// utility stuff

function intVal(str) {
  str = '' + str;
  if (str == '') return 0;
  return str.match(/-?\d+/) - 0;
}

function reverseNum(num) {
  return num.split('').reverse().join('');
}

function debug(out) {
  with(document) {
    body.appendChild(createTextNode(out));
    body.appendChild(createElement('br'));
  }
}

}, false);
