buttonCodeToChar = {1: "Left", 2: "Middle", 3: "Right"};
buttonCharToCode = {"Left": 1, "Middle": 2, "Right": 3};
mouseStates = Object.freeze({Idle: "Idle", Moving: "Moving",
                             LeftDown: "LeftDown",
                             LeftUp: "LeftUp",
                             LeftDrag: "LeftDrag"});

function Mouse() {
    // It'd be good to get this info on construction
    this.x = -1;
    this.y = -1;
    this.dx = 0;
    this.dy = 0;
    this.timeout = null;

    this.currentState = mouseStates.Idle;
    this.lastState = mouseStates.Idle;

    this.UpdatePosition = function(nx, ny) {
        clearTimeout(this.timeout);
        // It's impossible to know where the mouse is when it's first created,
        // before a mousemove event is fired. If we do nothing the initial
        // dx and dy are incorrect, so we hack this to not update the dx and dy
        // till we've gotten one mousemove
        if (this.x == -1 && this.y == -1) {
            this.x = nx;
            this.y = ny;
        } else {
            this.dx = nx - this.x;
            this.dy = ny - this.y;
            this.x = nx;
            this.y = ny;
            this.UpdateState();
        }
        this.timeout = setTimeout(this.StoppedMoving.bind(this), 100);
    }

    this.UpdateState = function() {
        this.lastState = this.currentState;
        switch (this.currentState) {
        case mouseStates.Idle:
            if (this.dx != 0 || this.dy != 0) {
                if (this.ButtonDown("Left")) {
                    this.currentState = mouseStates.LeftDrag;
                } else {
                    this.currentState = mouseStates.Moving;
                }
            } else {
                if (this.ButtonDown("Left")) {
                    this.currentState = mouseStates.LeftDown;
                }
            }
            break;
        case mouseStates.Moving:
            if (this.dx == 0 && this.dy == 0) {
                this.currentState = mouseStates.Idle;
            } else {
                if (this.ButtonDown("Left")) {
                    this.currentState = mouseStates.LeftDrag;
                }
            }
            break;
        case mouseStates.LeftDown:
            if (Math.abs(this.dx) >= 5 || Math.abs(this.dy) >= 5 &&
                this.ButtonDown("Left")) {
                this.currentState = mouseStates.LeftDrag;
            } else if (!this.ButtonDown("Left")) {
                this.currentState = mouseStates.LeftUp;
            }
            break;
        case mouseStates.LeftUp:
            if (this.dx != 0 || this.dy != 0) {
                this.currentState = mouseStates.Moving;
            } else {
                this.currentState = mouseStates.Idle;
            }
            break;
        case mouseStates.LeftDrag:
            if (this.dx == 0 && this.dy == 0) {
                if (!this.ButtonDown("Left")) {
                    this.currentState = mouseStates.LeftUp;
                }
            }
            break;
        }
    }

    this.ButtonDown = function(button) {
        if (button instanceof Number) {
            button = buttonCodeToChar(button);
        }
        if (this[button] == false || this[button] == null) {
            return false;
        }
        return true;
    }

    this.StoppedMoving = function() {
        this.dx = 0;
        this.dy = 0;
        this.UpdateState();
    }
}


@@ -1255,27 +1255,16 @@
 ving
-.bind(this)
 , 100);%0A
     
@@ -1259,16 +1259,16 @@
 , 100);%0A
+
     %7D%0A%0A 
@@ -2221,16 +2221,17 @@
      if 
+(
 (Math.ab
@@ -2266,16 +2266,17 @@
 this.dy)
+)
  %3E= 5 &&
@@ -3288,21 +3288,25 @@
  %7D%0A%0A    
-this.
+function 
 StoppedM
@@ -3310,27 +3310,16 @@
 edMoving
- = function
 () %7B%0A   
@@ -3385,17 +3385,69 @@
 tate();%0A
-
     %7D
+%0A%0A    this.StoppedMoving = StoppedMoving.bind(this);
 %0A%7D%0A%0A

Clarify some mouse commands