Browse Source

No mootools dep outside of default_controls.js.

Some basic functions from mootools implemented in util.js.

Also, some more DOM separation. Move clipboard focus logic into
default_controls and canvas and out of vnc.js.

JSLint cleanup.
Joel Martin 15 years ago
parent
commit
15046f0042
4 changed files with 302 additions and 181 deletions
  1. 61 47
      include/canvas.js
  2. 21 3
      include/default_controls.js
  3. 159 59
      include/util.js
  4. 61 72
      include/vnc.js

+ 61 - 47
include/canvas.js

@@ -5,9 +5,10 @@
  *
  * See README.md for usage and integration instructions.
  */
-"use strict";
 
-/*global window, $, Browser */
+"use strict";
+/*jslint white: false, bitwise: false */
+/*global window, console, $, Util */
 
 // Everything namespaced inside Canvas
 var Canvas = {
@@ -23,20 +24,21 @@ ctx  : null,
 
 prevStyle: "",
 
+focused     : true,
 keyPress    : null,
 mouseButton : null,
 mouseMove   : null,
 
 onMouseButton: function(e, down) {
     var evt, pos, bmask;
-    evt = e.event || window.event;
-    pos = getEventPosition(e, $(Canvas.id));
+    evt = (e ? e : window.event);
+    pos = Util.getEventPosition(e, $(Canvas.id));
     bmask = 1 << evt.button;
-    //console.log('mouse ' + evt.which + '/' + evt.button + ' down:' + pos.x + "," + pos.y);
+    //console.log('mouse ' + pos.x + "," + pos.y + " down: " + down + " bmask: " + bmask);
     if (Canvas.mouseButton) {
         Canvas.mouseButton(pos.x, pos.y, down, bmask);
     }
-    e.stop();
+    Util.stopEvent(e);
     return false;
 },
 
@@ -49,10 +51,10 @@ onMouseUp: function (e) {
 },
 
 onMouseWheel: function (e) {
-    var evt, pos, bmask;
-    evt = e.event || window.event;
-    pos = getEventPosition(e, $(Canvas.id));
-    var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
+    var evt, pos, bmask, wheelData;
+    evt = (e ? e : window.event);
+    pos = Util.getEventPosition(e, $(Canvas.id));
+    wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
     if (wheelData > 0) {
         bmask = 1 << 3;
     } else {
@@ -63,15 +65,15 @@ onMouseWheel: function (e) {
         Canvas.mouseButton(pos.x, pos.y, 1, bmask);
         Canvas.mouseButton(pos.x, pos.y, 0, bmask);
     }
-    e.stop();
+    Util.stopEvent(e);
     return false;
 },
 
 
 onMouseMove: function (e) {
     var evt, pos;
-    evt = e.event || window.event;
-    pos = getEventPosition(e, $(Canvas.id));
+    evt = (e ? e : window.event);
+    pos = Util.getEventPosition(e, $(Canvas.id));
     //console.log('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
     if (Canvas.mouseMove) {
         Canvas.mouseMove(pos.x, pos.y);
@@ -79,41 +81,50 @@ onMouseMove: function (e) {
 },
 
 onKeyDown: function (e) {
-    //console.log("keydown: " + e.key + "(" + e.code + ")");
+    //console.log("keydown: " + Canvas.getKeysym(e));
+    if (! Canvas.focused) {
+        return true;
+    }
     if (Canvas.keyPress) {
         Canvas.keyPress(Canvas.getKeysym(e), 1);
     }
-    e.stop();
+    Util.stopEvent(e);
     return false;
 },
 
 onKeyUp : function (e) {
     //console.log("keyup: " + e.key + "(" + e.code + ")");
+    if (! Canvas.focused) {
+        return true;
+    }
     if (Canvas.keyPress) {
         Canvas.keyPress(Canvas.getKeysym(e), 0);
     }
-    e.stop();
+    Util.stopEvent(e);
     return false;
 },
 
 onMouseDisable: function (e) {
     var evt, pos;
-    evt = e.event || window.event;
-    pos = getPosition($(Canvas.id));
+    evt = (e ? e : window.event);
+    pos = Util.getPosition($(Canvas.id));
     /* Stop propagation if inside canvas area */
     if ((evt.clientX >= pos.x) &&
         (evt.clientY >= pos.y) &&
         (evt.clientX < (pos.x + Canvas.c_wx)) &&
         (evt.clientY < (pos.y + Canvas.c_wy))) {
-        e.stop();
+        //console.log("mouse event disabled");
+        Util.stopEvent(e);
         return false;
     }
+    //console.log("mouse event not disabled");
+    return true;
 },
 
 
 init: function (id, width, height, true_color, keyPress,
                 mouseButton, mouseMove) {
-    //console.log(">> Canvas.init");
+    console.log(">> Canvas.init");
 
     Canvas.id = id;
 
@@ -122,20 +133,21 @@ init: function (id, width, height, true_color, keyPress,
     Canvas.mouseMove = mouseMove || null;
 
     var c = $(Canvas.id);
-    document.addEvent('keydown', Canvas.onKeyDown);
-    document.addEvent('keyup', Canvas.onKeyUp);
-    c.addEvent('mousedown', Canvas.onMouseDown);
-    c.addEvent('mouseup', Canvas.onMouseUp);
-    c.addEvent('mousewheel', Canvas.onMouseWheel);
-    c.addEvent('mousemove', Canvas.onMouseMove);
+    Util.addEvent(document, 'keydown', Canvas.onKeyDown);
+    Util.addEvent(document, 'keyup', Canvas.onKeyUp);
+    Util.addEvent(c, 'mousedown', Canvas.onMouseDown);
+    Util.addEvent(c, 'mouseup', Canvas.onMouseUp);
+    Util.addEvent(c, 'mousemove', Canvas.onMouseMove);
+    Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
+            Canvas.onMouseWheel);
 
     /* Work around right and middle click browser behaviors */
-    document.addEvent('click', Canvas.onMouseDisable);
-    document.body.addEvent('contextmenu', Canvas.onMouseDisable);
+    Util.addEvent(document, 'click', Canvas.onMouseDisable);
+    Util.addEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
 
     Canvas.resize(width, height);
-    Canvas.c_wx = c.getSize().x;
-    Canvas.c_wy = c.getSize().y;
+    Canvas.c_wx = c.offsetWidth;
+    Canvas.c_wy = c.offsetHeight;
     Canvas.true_color = true_color;
     Canvas.colourMap = [];
 
@@ -143,8 +155,9 @@ init: function (id, width, height, true_color, keyPress,
     Canvas.ctx = c.getContext('2d'); 
     
     Canvas.prevStyle = "";
+    Canvas.focused = true;
 
-    if (Browser.Engine.webkit) {
+    if (Util.Engine.webkit) {
         Canvas.prefer_js = true;
     }
 
@@ -164,16 +177,17 @@ resize: function (width, height) {
 
 stop: function () {
     var c = $(Canvas.id);
-    document.removeEvents('keydown');
-    document.removeEvents('keyup');
-    c.removeEvents('mousedown');
-    c.removeEvents('mouseup');
-    c.removeEvents('mousemove');
-    c.removeEvents('DOMMouseScroll');
+    Util.removeEvent(document, 'keydown', Canvas.onKeyDown);
+    Util.removeEvent(document, 'keyup', Canvas.onKeyUp);
+    Util.removeEvent(c, 'mousedown', Canvas.onMouseDown);
+    Util.removeEvent(c, 'mouseup', Canvas.onMouseUp);
+    Util.removeEvent(c, 'mousemove', Canvas.onMouseMove);
+    Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
+            Canvas.onMouseWheel);
 
     /* Work around right and middle click browser behaviors */
-    document.removeEvents('click');
-    document.body.removeEvents('contextmenu');
+    Util.removeEvent(document, 'click', Canvas.onMouseDisable);
+    Util.removeEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
 },
 
 /*
@@ -197,8 +211,8 @@ getTile: function(x, y, width, height, color) {
         red = rgb[0];
         green = rgb[1];
         blue = rgb[2];
-        for (j = 0; j < height; j++) {
-            for (i = 0; i < width; i++) {
+        for (j = 0; j < height; j += 1) {
+            for (i = 0; i < width; i += 1) {
                 p = (i + (j * width) ) * 4;
                 data[p + 0] = red;
                 data[p + 1] = green;
@@ -225,8 +239,8 @@ setTile: function(img, x, y, w, h, color) {
         red = rgb[0];
         green = rgb[1];
         blue = rgb[2];
-        for (j = 0; j < h; j++) {
-            for (i = 0; i < w; i++) {
+        for (j = 0; j < h; j += 1) {
+            for (i = 0; i < w; i += 1) {
                 p = (x + i + ((y + j) * width) ) * 4;
                 data[p + 0] = red;
                 data[p + 1] = green;
@@ -265,12 +279,12 @@ rgbxImage: function(x, y, width, height, arr, offset) {
 },
 
 cmapImage: function(x, y, width, height, arr, offset) {
-    var img, i, j, k, data, rgb, cmap;
+    var img, i, j, data, rgb, cmap;
     img = Canvas.ctx.getImageData(0, 0, width, height);
     data = img.data;
     cmap = Canvas.colourMap;
     //console.log("cmapImage x: " + x + ", y: " + y + "arr.slice(0,20): " + arr.slice(0,20));
-    for (i=0, j=offset; i < (width * height * 4); i=i+4, j++) {
+    for (i=0, j=offset; i < (width * height * 4); i=i+4, j += 1) {
         rgb = cmap[arr[j]];
         data[i + 0] = rgb[0];
         data[i + 1] = rgb[1];
@@ -311,7 +325,7 @@ copyImage: function(old_x, old_y, new_x, new_y, width, height) {
 /* Translate DOM key event to keysym value */
 getKeysym: function(e) {
     var evt, keysym;
-    evt = e.event || window.event;
+    evt = (e ? e : window.event);
 
     /* Remap modifier and special keys */
     switch ( evt.keyCode ) {
@@ -354,7 +368,7 @@ getKeysym: function(e) {
         case 187       : keysym = 61; break; // =  (IE)
         case 188       : keysym = 44; break; // ,  (Mozilla, IE)
         case 109       :                     // -  (Mozilla)
-            if (Browser.Engine.gecko) {
+            if (Util.Engine.gecko) {
                          keysym = 45; }
                                       break;
         case 189       : keysym = 45; break; // -  (IE)

+ 21 - 3
include/default_controls.js

@@ -1,3 +1,21 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2010 Joel Martin
+ * Licensed under LGPL-3 (see LICENSE.LGPL-3)
+ *
+ * See README.md for usage and integration instructions.
+ */
+"use strict";
+
+// Load mootools
+(function () {
+    var prefix = (typeof VNC_uri_prefix !== "undefined") ?
+                           VNC_uri_prefix : "include/";
+    document.write("<script src='" + prefix +
+                   "mootools.js'><\/script>");
+}());
+
+
 DefaultControls = {
 
 load: function(target) {
@@ -58,7 +76,7 @@ load: function(target) {
 
     $('VNC_screen').onmousemove = function () {
             // Unfocus clipboard when over the VNC area
-            if (RFB.clipboardFocus) {
+            if (! Canvas.focused) {
                 $('VNC_clipboard_text').blur();
             }
         };
@@ -119,11 +137,11 @@ disconnect: function() {
 },
 
 clipFocus: function() {
-    RFB.clipboardFocus = true;
+    Canvas.focused = false;
 },
 
 clipBlur: function() {
-    RFB.clipboardFocus = false;
+    Canvas.focused = true;
 },
 
 clipClear: function() {

+ 159 - 59
include/util.js

@@ -1,67 +1,39 @@
-if ((!window.console) || (! /__debug__$/i.test(document.location.href))) {
-  // non-debug mode, an empty function  
-  window.console = window.console || {};  
-  window.console.log = function(message) {}; 
-  window.console.warn = function(message) {}; 
-  window.console.error = function(message) {}; 
-}
-
-function dirObj(obj, depth, parent) {
-    var msg = "";
-    var val = "";
-    if (! depth) { depth=2; }
-    if (! parent) { parent= ""; }
-
-    // Print the properties of the passed-in object 
-    for (var i in obj) {
-        if ((depth > 1) && (typeof obj[i] == "object")) { 
-            // Recurse attributes that are objects
-            msg += dirObj(obj[i], depth-1, parent + "." + i);
-        } else {
-            val = new String(obj[i]).replace("\n", " ");
-            if (val.length > 30) {
-                val = val.substr(0,30) + "...";
-            } 
-            msg += parent + "." + i + ": " + val + "\n";
-        }
-    }
-    return msg;
-}
-
 /*
- * Cross-browser positioning
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2010 Joel Martin
+ * Licensed under LGPL-3 (see LICENSE.LGPL-3)
+ *
+ * See README.md for usage and integration instructions.
  */
 
-// Get DOM element position on page
-function getPosition(obj) {
-    var x = 0, y = 0;
-    if (obj.offsetParent) {
-        do {
-            x += obj.offsetLeft;
-            y += obj.offsetTop;
-        } while (obj = obj.offsetParent);
-    }
-    return {'x': x, 'y': y};
-}
+"use strict";
+/*jslint bitwise: false, white: false */
+/*global window, document, navigator, ActiveXObject*/
 
-// Get mouse event position in DOM element
-function getEventPosition(e, obj) {
-    var evt, docX, docY, pos;
-    //if (!e) evt = window.event;
-    evt = e.event || window.event;
-    if (evt.pageX || evt.pageY) {
-        docX = evt.pageX;
-        docY = evt.pageY;
-    } else if (evt.clientX || evt.clientY) {
-        docX = evt.clientX + document.body.scrollLeft +
-            document.documentElement.scrollLeft;
-        docY = evt.clientY + document.body.scrollTop +
-            document.documentElement.scrollTop;
-    }
-    pos = getPosition(obj);
-    return {'x': docX - pos.x, 'y': docY - pos.y};
+// Globals defined here
+var Util = {}, $;
+
+if ((!window.console) || (! /__debug__$/i.test(document.location.href))) {
+    // non-debug mode, an empty function  
+    window.console = window.console || {};  
+    window.console.log = function (message) {}; 
+    window.console.warn = function (message) {}; 
+    window.console.error = function (message) {}; 
 }
 
+// Simple DOM selector by ID
+if (!window.$) {
+    $ = function (id) {
+        if (document.getElementById) {
+            return document.getElementById(id);
+        } else if (document.all) {
+            return document.all[id];
+        } else if (document.layers) {
+            return document.layers[id];
+        }
+        return undefined;
+    };
+}
 
 /*
  * Make arrays quack
@@ -110,7 +82,7 @@ Array.prototype.shiftStr = function (len) {
 };
 Array.prototype.pushStr = function (str) {
     var i, n = str.length;
-    for (i=0; i < n; i++) {
+    for (i=0; i < n; i+=1) {
         this.push(str.charCodeAt(i));
     }
 };
@@ -119,3 +91,131 @@ Array.prototype.shiftBytes = function (len) {
     return this.splice(0, len);
 };
 
+/* 
+ * ------------------------------------------------------
+ * Namespaced in Util
+ * ------------------------------------------------------
+ */
+
+Util.dirObj = function (obj, depth, parent) {
+    var i, msg = "", val = "";
+    if (! depth) { depth=2; }
+    if (! parent) { parent= ""; }
+
+    // Print the properties of the passed-in object 
+    for (i in obj) {
+        if ((depth > 1) && (typeof obj[i] === "object")) { 
+            // Recurse attributes that are objects
+            msg += Util.dirObj(obj[i], depth-1, parent + "." + i);
+        } else {
+            //val = new String(obj[i]).replace("\n", " ");
+            val = obj[i].toString().replace("\n", " ");
+            if (val.length > 30) {
+                val = val.substr(0,30) + "...";
+            } 
+            msg += parent + "." + i + ": " + val + "\n";
+        }
+    }
+    return msg;
+};
+
+/*
+ * Cross-browser routines
+ */
+
+// Get DOM element position on page
+Util.getPosition = function (obj) {
+    var x = 0, y = 0;
+    if (obj.offsetParent) {
+        do {
+            x += obj.offsetLeft;
+            y += obj.offsetTop;
+            obj = obj.offsetParent;
+        } while (obj);
+    }
+    return {'x': x, 'y': y};
+};
+
+// Get mouse event position in DOM element
+Util.getEventPosition = function (e, obj) {
+    var evt, docX, docY, pos;
+    //if (!e) evt = window.event;
+    evt = (e ? e : window.event);
+    if (evt.pageX || evt.pageY) {
+        docX = evt.pageX;
+        docY = evt.pageY;
+    } else if (evt.clientX || evt.clientY) {
+        docX = evt.clientX + document.body.scrollLeft +
+            document.documentElement.scrollLeft;
+        docY = evt.clientY + document.body.scrollTop +
+            document.documentElement.scrollTop;
+    }
+    pos = Util.getPosition(obj);
+    return {'x': docX - pos.x, 'y': docY - pos.y};
+};
+
+
+// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
+Util.addEvent = function (obj, evType, fn){
+    if (obj.addEventListener){
+        obj.addEventListener(evType, fn, false); 
+        return true;
+    } else if (obj.attachEvent){
+        var r = obj.attachEvent("on"+evType, fn);
+        return r;
+    } else {
+        throw("Handler could not be attached");
+    }
+};
+
+Util.removeEvent = function(obj, evType, fn){
+    if (obj.removeEventListener){
+        obj.removeEventListener(evType, fn, false);
+        return true;
+    } else if (obj.detachEvent){
+        var r = obj.detachEvent("on"+evType, fn);
+        return r;
+    } else {
+        throw("Handler could not be removed");
+    }
+};
+
+Util.stopEvent = function(e) {
+    if (e.stopPropagation) { e.stopPropagation(); }
+    else                   { e.cancelBubble = true; }
+
+    if (e.preventDefault)  { e.preventDefault(); }
+    else                   { e.returnValue = false; }
+};
+
+
+// Set browser engine versions. Based on mootools.
+Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
+
+Util.Engine = {
+    'presto': (function() {
+            return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
+    'trident': (function() {
+            return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
+    'webkit': (function() {
+            return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
+    'gecko': (function() {
+            return (!document.getBoxObjectFor && !window.mozInnerScreenX) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
+};
+
+Util.Flash = (function(){
+    var v, version;
+    try {
+        v = navigator.plugins['Shockwave Flash'].description;
+    } catch(err1) {
+        try {
+            v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+        } catch(err2) {
+            v = '0 r0';
+        }
+    }
+    version = v.match(/\d+/g);
+    return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
+}()); 
+
+

+ 61 - 72
include/vnc.js

@@ -5,9 +5,10 @@
  *
  * See README.md for usage and integration instructions.
  */
-"use strict";
 
-/*global window, WebSocket, $, Browser, Canvas, VNC_uri_prefix Base64, DES */
+"use strict";
+/*jslint white: false, nomen: false, browser: true, bitwise: false */
+/*global window, console, WebSocket, Util, Canvas, VNC_uri_prefix, Base64, DES */
 
 // Globals defined here
 var VNC_native_ws, RFB;
@@ -15,24 +16,23 @@ var VNC_native_ws, RFB;
 /*
  * Load supporting scripts
  */
+function get_VNC_uri_prefix() {
+    return (typeof VNC_uri_prefix !== "undefined") ? VNC_uri_prefix : "include/";
+}
+
 (function () {
-    var extra, start, end;
+    var extra = "", start, end;
 
-    if (typeof VNC_uri_prefix === "undefined") {
-        VNC_uri_prefix="include/";
-    }
-    extra = "";
-    start = "<script src='" + VNC_uri_prefix;
+    start = "<script src='" + get_VNC_uri_prefix();
     end = "'><\/script>";
 
     // Uncomment to activate firebug lite
     //extra += start + "http://getfirebug.com/releases/lite/1.2/" + 
     //         "firebug-lite-compressed.js" + end;
 
-    extra += start + "mootools.js" + end;
+    extra += start + "util.js" + end;
     extra += start + "base64.js" + end;
     extra += start + "des.js" + end;
-    extra += start + "util.js" + end;
     extra += start + "canvas.js" + end;
 
     /* If no builtin websockets then load web_socket.js */
@@ -65,7 +65,6 @@ true_color     : false,
 b64encode      : true,  // false means UTF-8 on the wire
 //b64encode      : false,  // false means UTF-8 on the wire
 
-clipboardFocus : false,
 
 // In preference order
 encodings      : [
@@ -111,14 +110,14 @@ load: function () {
         RFB.updateState('disconnected', 'Disconnected');
     } else {
         console.warn("Using web-socket-js flash bridge");
-        if ((! Browser.Plugins.Flash) ||
-            (Browser.Plugins.Flash.version < 9)) {
+        if ((! Util.Flash) ||
+            (Util.Flash.version < 9)) {
             RFB.updateState('failed', "WebSockets or Adobe Flash is required");
-        } else if (location.href.substr(0, 7) === "file://") {
+        } else if (document.location.href.substr(0, 7) === "file://") {
             RFB.updateState('failed',
                     "'file://' URL is incompatible with Adobe Flash");
         } else {
-            WebSocket.__swfLocation = VNC_uri_prefix +
+            WebSocket.__swfLocation = get_VNC_uri_prefix() +
                         "web-socket-js/WebSocketMain.swf";
             WebSocket.__initialize();
             RFB.use_seq = true;
@@ -129,7 +128,7 @@ load: function () {
     // Populate encoding lookup tables
     RFB.encHandlers = {};
     RFB.encNames = {};
-    for (i=0; i < RFB.encodings.length; i++) {
+    for (i=0; i < RFB.encodings.length; i+=1) {
         RFB.encHandlers[RFB.encodings[i][1]] = RFB[RFB.encodings[i][2]];
         RFB.encNames[RFB.encodings[i][1]] = RFB.encodings[i][0];
     }
@@ -158,7 +157,7 @@ connect: function (host, port, password, encrypt, true_color) {
     }
 
     if ((!RFB.host) || (!RFB.port)) {
-        updateState('disconnected', "Must set host and port");
+        RFB.updateState('disconnected', "Must set host and port");
         return;
     }
 
@@ -470,10 +469,10 @@ init_msg: function () {
         RFB.send_array(response);
         
         /* Start pushing/polling */
-        RFB.checkEvents.delay(RFB.check_rate);
-        RFB.scan_tight_imgs.delay(RFB.scan_imgs_rate)
+        setTimeout(RFB.checkEvents, RFB.check_rate);
+        setTimeout(RFB.scan_tight_imgs, RFB.scan_imgs_rate);
         RFB.timing.history_start = (new Date()).getTime();
-        RFB.update_timings.delay(1000);
+        setTimeout(RFB.update_timings, 1000);
 
         RFB.updateState('normal', "Connected to: " + RFB.fb_name);
         break;
@@ -505,7 +504,7 @@ normal_msg: function () {
         RQ.shift8();  // Padding
         first_colour = RQ.shift16(); // First colour
         num_colours = RQ.shift16();
-        for (c=0; c < num_colours; c++) { 
+        for (c=0; c < num_colours; c+=1) { 
             red = RQ.shift16();
             //console.log("red before: " + red);
             red = parseInt(red / 256, 10);
@@ -554,7 +553,7 @@ normal_msg: function () {
 
 framebufferUpdate: function() {
     var RQ = RFB.RQ, FBU = RFB.FBU, timing = RFB.timing,
-        now, fbu_rt_diff, last_rects, last_length,
+        now, fbu_rt_diff, last_bytes, last_rects,
         ret = true, msg;
 
     if (FBU.rects === 0) {
@@ -607,8 +606,8 @@ framebufferUpdate: function() {
         }
 
         timing.last_fbu = (new Date()).getTime();
-        last_rects = FBU.rects;
         last_bytes = RQ.length;
+        last_rects = FBU.rects;
 
         ret = RFB.encHandlers[FBU.encoding]();
 
@@ -682,7 +681,7 @@ display_raw: function () {
     if (FBU.lines > 0) {
         FBU.bytes = FBU.width * RFB.fb_Bpp; // At least another line
     } else {
-        FBU.rects --;
+        FBU.rects -= 1;
         FBU.bytes = 0;
     }
 },
@@ -700,7 +699,7 @@ display_copy_rect: function () {
     old_x = RQ.shift16();
     old_y = RQ.shift16();
     Canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
-    FBU.rects --;
+    FBU.rects -= 1;
     FBU.bytes = 0;
 },
 
@@ -724,7 +723,7 @@ display_rre: function () {
         width = RQ.shift16();
         height = RQ.shift16();
         Canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
-        FBU.subrects --;
+        FBU.subrects -= 1;
     }
     //console.log("   display_rre: rects: " + FBU.rects +
     //            ", FBU.subrects: " + FBU.subrects);
@@ -733,7 +732,7 @@ display_rre: function () {
         chunk = Math.min(RFB.rre_chunk, FBU.subrects);
         FBU.bytes = (RFB.fb_Bpp + 8) * chunk;
     } else {
-        FBU.rects --;
+        FBU.rects -= 1;
         FBU.bytes = 0;
     }
     //console.log("<< display_rre, FBU.bytes: " + FBU.bytes);
@@ -787,7 +786,7 @@ display_hextile: function() {
                 FBU.bytes += RFB.fb_Bpp;
             }
             if (subencoding & 0x08) { // AnySubrects
-                FBU.bytes++;   // Since we aren't shifting it off
+                FBU.bytes += 1;   // Since we aren't shifting it off
                 if (RQ.length < FBU.bytes) {
                     /* Wait for subrects byte */
                     //console.log("   waiting for hextile subrects header byte");
@@ -841,8 +840,8 @@ display_hextile: function() {
             tile = Canvas.getTile(x, y, w, h, FBU.background);
             if (FBU.subencoding & 0x08) { // AnySubrects
                 subrects = RQ[idx];
-                idx++;
-                for (s = 0; s < subrects; s ++) {
+                idx += 1;
+                for (s = 0; s < subrects; s += 1) {
                     if (FBU.subencoding & 0x10) { // SubrectsColoured
                         color = RQ.slice(idx, idx + RFB.fb_Bpp);
                         idx += RFB.fb_Bpp;
@@ -850,12 +849,12 @@ display_hextile: function() {
                         color = FBU.foreground;
                     }
                     xy = RQ[idx];
-                    idx++;
+                    idx += 1;
                     sx = (xy >> 4);
                     sy = (xy & 0x0f);
 
                     wh = RQ[idx];
-                    idx++;
+                    idx += 1;
                     sw = (wh >> 4)   + 1;
                     sh = (wh & 0x0f) + 1;
 
@@ -867,11 +866,11 @@ display_hextile: function() {
         RQ.shiftBytes(FBU.bytes);
         FBU.lastsubencoding = FBU.subencoding;
         FBU.bytes = 0;
-        FBU.tiles --;
+        FBU.tiles -= 1;
     }
 
     if (FBU.tiles === 0) {
-        FBU.rects --;
+        FBU.rects -= 1;
     }
 
     //console.log("<< display_hextile");
@@ -881,7 +880,7 @@ display_hextile: function() {
 display_tight_png: function() {
     //console.log(">> display_tight_png");
     var RQ = RFB.RQ, FBU = RFB.FBU, 
-        ctl, cmode, i, clength, color, img;
+        ctl, cmode, clength, getCLength, color, img;
     //console.log("   FBU.rects: " + FBU.rects);
     //console.log("   RQ.length: " + RQ.length);
     //console.log("   RQ.slice(0,20): " + RQ.slice(0,20));
@@ -898,22 +897,22 @@ display_tight_png: function() {
         var header = 1, data = 0;
         data += arr[offset + 0] & 0x7f;
         if (arr[offset + 0] & 0x80) {
-            header++;
+            header += 1;
             data += (arr[offset + 1] & 0x7f) << 7;
             if (arr[offset + 1] & 0x80) {
-                header++;
+                header += 1;
                 data += arr[offset + 2] << 14;
             }
         }
         return [header, data];
-    }
+    };
 
     ctl = RQ[0];
     switch (ctl >> 4) {
         case 0x08: cmode = "fill"; break;
         case 0x09: cmode = "jpeg"; break;
         case 0x0A: cmode = "png";  break;
-        default:   throw("Illegal ctl: " + ctl); break;
+        default:   throw("Illegal ctl: " + ctl);
     }
     switch (cmode) {
         // fill uses fb_depth because TPIXELs drop the padding byte
@@ -958,14 +957,14 @@ display_tight_png: function() {
         break;
     }
     FBU.bytes = 0;
-    FBU.rects --;
+    FBU.rects -= 1;
     //console.log("   ending RQ.length: " + RQ.length);
     //console.log("   ending RQ.slice(0,20): " + RQ.slice(0,20));
 },
 
 extract_data_uri : function (arr) {
     var i, stra = [];
-    for (i=0; i< arr.length; i++) {
+    for (i=0; i< arr.length; i += 1) {
         stra.push(String.fromCharCode(arr[i]));
     }
     //return "," + escape(stra.join(''));
@@ -973,14 +972,14 @@ extract_data_uri : function (arr) {
 },
 
 scan_tight_imgs : function () {
-    var i, imgs;
+    var img, imgs;
     if (RFB.state === 'normal') {
         imgs = RFB.FBU.imgs;
         while ((imgs.length > 0) && (imgs[0][0].complete)) {
             img = imgs.shift();
             Canvas.ctx.drawImage(img[0], img[1], img[2]);
         }
-        RFB.scan_tight_imgs.delay(RFB.scan_imgs_rate);
+        setTimeout(RFB.scan_tight_imgs, RFB.scan_imgs_rate);
     }
 },
 
@@ -996,7 +995,7 @@ set_desktopsize : function () {
     console.log("<< set_desktopsize");
 
     RFB.FBU.bytes = 0;
-    RFB.FBU.rects --;
+    RFB.FBU.rects -= 1;
 },
 
 set_jpeg_quality : function () {
@@ -1048,7 +1047,7 @@ clientEncodings: function () {
 
     arr.push16(RFB.encodings.length); // encoding count
 
-    for (i=0; i<RFB.encodings.length; i++) {
+    for (i=0; i<RFB.encodings.length; i += 1) {
         arr.push32(RFB.encodings[i][1]);
     }
     //console.log("<< clientEncodings: " + arr);
@@ -1129,7 +1128,7 @@ decode_message: function(data, offset) {
     } else {
         // A bit faster in firefox
         var i, length = data.length, RQ = RFB.RQ;
-        for (i=offset; i < length; i++) {
+        for (i=offset; i < length; i += 1) {
             RQ.push(data.charCodeAt(i) % 256);
         }
     }
@@ -1167,13 +1166,13 @@ recv_message: function(e) {
 recv_message_reorder: function(e) {
     //console.log(">> recv_message_reorder");
 
-    var RQ_reorder = RFB.RQ_reorder, offset, seq_num, i;
+    var offset, seq_num, i;
 
     offset = e.data.indexOf(":") + 1;
     seq_num = parseInt(e.data.substr(0, offset-1), 10);
     if (RFB.RQ_seq_num === seq_num) {
         RFB.decode_message(e.data, offset);
-        RFB.RQ_seq_num++;
+        RFB.RQ_seq_num += 1;
     } else {
         console.warn("sequence number mismatch: expected " +
                      RFB.RQ_seq_num + ", got " + seq_num);
@@ -1192,10 +1191,10 @@ recv_message_reorder: function(e) {
                         * add it to the receive queue */
                     console.log("Found re-ordered packet seq_num " + seq_num);
                     RFB.decode_message(RFB.RQ_reorder.splice(i, 1)[0], offset);
-                    RFB.RQ_seq_num++;
+                    RFB.RQ_seq_num += 1;
                     i = 0;  // Start search again for next one
                 } else {
-                    i++;
+                    i += 1;
                 }
             }
             
@@ -1257,7 +1256,7 @@ DES: function (password, challenge) {
     var i, passwd, response;
     passwd = [];
     response = challenge.slice();
-    for (i=0; i < password.length; i++) {
+    for (i=0; i < password.length; i += 1) {
         passwd.push(password.charCodeAt(i));
     }
 
@@ -1293,14 +1292,11 @@ checkEvents: function () {
             }
         }
     }
-    RFB.checkEvents.delay(RFB.check_rate);
+    setTimeout(RFB.checkEvents, RFB.check_rate);
 },
 
 keyPress: function (keysym, down) {
     var arr;
-    if (RFB.clipboardFocus) {
-        return true;
-    }
     arr = RFB.keyEvent(keysym, down);
     arr = arr.concat(RFB.fbUpdateRequest(1));
     RFB.send_array(arr);
@@ -1333,16 +1329,10 @@ externalUpdateState: function(state, msg) {
 
 updateState: function(state, statusMsg) {
     var func, cmsg;
-    func = function(msg) { console.log(msg); };
-    switch (state) {
-        case 'failed':
-            func = function(msg) { console.error(msg); };
-            break;
-        case 'normal':
-        case 'disconnected':
-        default:
-            func = function(msg) { console.warn(msg); };
-            break;
+    if (state === 'failed') {
+        func = function(msg) { console.error(msg); };
+    } else {
+        func = function(msg) { console.warn(msg); };
     }
 
     cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
@@ -1355,13 +1345,11 @@ updateState: function(state, statusMsg) {
     if ((RFB.state === 'failed') &&
         ((state === 'disconnected') || (state === 'closed'))) {
         // Leave the failed message
-        RFB.externalUpdateState(state)
+        RFB.externalUpdateState(state);
     } else {
         RFB.state = state;
-        RFB.externalUpdateState(state, statusMsg)
+        RFB.externalUpdateState(state, statusMsg);
     }
-
-
 },
 
 update_timings: function() {
@@ -1380,9 +1368,9 @@ update_timings: function() {
         // Try for every second
         offset = (now - timing.history_start) % 1000;
         if (offset < 500) {
-            RFB.update_timings.delay(1000 - offset);
+            setTimeout(RFB.update_timings, 1000 - offset);
         } else {
-            RFB.update_timings.delay(2000 - offset);
+            setTimeout(RFB.update_timings, 2000 - offset);
         }
     }
 },
@@ -1391,11 +1379,12 @@ show_timings: function() {
     var i, timing = RFB.timing, history, msg,
         delta, tot_time = 0, tot_fbus = 0, tot_rects = 0,
         tot_bytes = 0, tot_pixels = 0;
+    if (timing.history_start === 0) { return; }
     console.log(">> show_timings");
     RFB.update_timings();  // Final accumulate
     msg = "\nTimings\n";
     msg += "  time: fbus,rects,bytes,pixels\n";
-    for (i=0; i < timing.history.length; i++) {
+    for (i=0; i < timing.history.length; i += 1) {
         history = timing.history[i];
         delta = ((history[0]-timing.history_start)/1000);
         tot_time = delta;