{ "version": 3, "sources": ["../../../../../../../../source/febe/ui/src/scripts/ext/Guac.js"], "sourcesContent": ["/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nexport var Guacamole = Guacamole || {};\n\n/**\n * A reader which automatically handles the given input stream, returning\n * strictly received packets as array buffers. Note that this object will\n * overwrite any installed event handlers on the given Guacamole.InputStream.\n *\n * @constructor\n * @param {Guacamole.InputStream} stream The stream that data will be read\n * from.\n */\nGuacamole.ArrayBufferReader = function(stream) {\n\n /**\n * Reference to this Guacamole.InputStream.\n * @private\n */\n var guac_reader = this;\n\n // Receive blobs as array buffers\n stream.onblob = function(data) {\n\n // Convert to ArrayBuffer\n var binary = window.atob(data);\n var arrayBuffer = new ArrayBuffer(binary.length);\n var bufferView = new Uint8Array(arrayBuffer);\n\n for (var i=0; istart and ending at end - 1.\n */\n var slice = function slice(blob, start, end) {\n\n // Use prefixed implementations if necessary\n var sliceImplementation = (\n blob.slice\n || blob.webkitSlice\n || blob.mozSlice\n ).bind(blob);\n\n var length = end - start;\n\n // The old Blob.slice() was length-based (not end-based). Try the\n // length version first, if the two calls are not equivalent.\n if (length !== end) {\n\n // If the result of the slice() call matches the expected length,\n // trust that result. It must be correct.\n var sliceResult = sliceImplementation(start, length);\n if (sliceResult.size === length)\n return sliceResult;\n\n }\n\n // Otherwise, use the most-recent standard: end-based slice()\n return sliceImplementation(start, end);\n\n };\n\n /**\n * Sends the contents of the given blob over the underlying stream.\n *\n * @param {Blob} blob\n * The blob to send.\n */\n this.sendBlob = function sendBlob(blob) {\n\n var offset = 0;\n var reader = new FileReader();\n\n /**\n * Reads the next chunk of the blob provided to\n * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The chunk itself\n * is read asynchronously, and will not be available until\n * reader.onload fires.\n *\n * @private\n */\n var readNextChunk = function readNextChunk() {\n\n // If no further chunks remain, inform of completion and stop\n if (offset >= blob.size) {\n\n // Fire completion event for completed blob\n if (guacWriter.oncomplete)\n guacWriter.oncomplete(blob);\n\n // No further chunks to read\n return;\n\n }\n\n // Obtain reference to next chunk as a new blob\n var chunk = slice(blob, offset, offset + arrayBufferWriter.blobLength);\n offset += arrayBufferWriter.blobLength;\n\n // Attempt to read the blob contents represented by the blob into\n // a new array buffer\n reader.readAsArrayBuffer(chunk);\n\n };\n\n // Send each chunk over the stream, continue reading the next chunk\n reader.onload = function chunkLoadComplete() {\n\n // Send the successfully-read chunk\n arrayBufferWriter.sendData(reader.result);\n\n // Continue sending more chunks after the latest chunk is\n // acknowledged\n arrayBufferWriter.onack = function sendMoreChunks(status) {\n\n if (guacWriter.onack)\n guacWriter.onack(status);\n\n // Abort transfer if an error occurs\n if (status.isError())\n return;\n\n // Inform of blob upload progress via progress events\n if (guacWriter.onprogress)\n guacWriter.onprogress(blob, offset - arrayBufferWriter.blobLength);\n\n // Queue the next chunk for reading\n readNextChunk();\n\n };\n\n };\n\n // If an error prevents further reading, inform of error and stop\n reader.onerror = function chunkLoadFailed() {\n\n // Fire error event, including the context of the error\n if (guacWriter.onerror)\n guacWriter.onerror(blob, offset, reader.error);\n\n };\n\n // Begin reading the first chunk\n readNextChunk();\n\n };\n\n /**\n * Signals that no further text will be sent, effectively closing the\n * stream.\n */\n this.sendEnd = function sendEnd() {\n arrayBufferWriter.sendEnd();\n };\n\n /**\n * Fired for received data, if acknowledged by the server.\n *\n * @event\n * @param {Guacamole.Status} status\n * The status of the operation.\n */\n this.onack = null;\n\n /**\n * Fired when an error occurs reading a blob passed to\n * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The transfer for the\n * the given blob will cease, but the stream will remain open.\n *\n * @event\n * @param {Blob} blob\n * The blob that was being read when the error occurred.\n *\n * @param {Number} offset\n * The offset of the failed read attempt within the blob, in bytes.\n *\n * @param {DOMError} error\n * The error that occurred.\n */\n this.onerror = null;\n\n /**\n * Fired for each successfully-read chunk of data as a blob is being sent\n * via [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}.\n *\n * @event\n * @param {Blob} blob\n * The blob that is being read.\n *\n * @param {Number} offset\n * The offset of the read that just succeeded.\n */\n this.onprogress = null;\n\n /**\n * Fired when a blob passed to\n * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob} has finished being\n * sent.\n *\n * @event\n * @param {Blob} blob\n * The blob that was sent.\n */\n this.oncomplete = null;\n\n};\n\n\n/**\n * Guacamole protocol client. Given a {@link Guacamole.Tunnel},\n * automatically handles incoming and outgoing Guacamole instructions via the\n * provided tunnel, updating its display using one or more canvas elements.\n *\n * @constructor\n * @param {Guacamole.Tunnel} tunnel The tunnel to use to send and receive\n * Guacamole instructions.\n */\nGuacamole.Client = function(tunnel) {\n\n var guac_client = this;\n\n var STATE_IDLE = 0;\n var STATE_CONNECTING = 1;\n var STATE_WAITING = 2;\n var STATE_CONNECTED = 3;\n var STATE_DISCONNECTING = 4;\n var STATE_DISCONNECTED = 5;\n\n var currentState = STATE_IDLE;\n\n var currentTimestamp = 0;\n var pingInterval = null;\n\n /**\n * Translation from Guacamole protocol line caps to Layer line caps.\n * @private\n */\n var lineCap = {\n 0: \"butt\",\n 1: \"round\",\n 2: \"square\"\n };\n\n /**\n * Translation from Guacamole protocol line caps to Layer line caps.\n * @private\n */\n var lineJoin = {\n 0: \"bevel\",\n 1: \"miter\",\n 2: \"round\"\n };\n\n /**\n * The underlying Guacamole display.\n *\n * @private\n * @type {Guacamole.Display}\n */\n var display = new Guacamole.Display();\n\n /**\n * All available layers and buffers\n *\n * @private\n * @type {Object.}\n */\n var layers = {};\n\n /**\n * All audio players currently in use by the client. Initially, this will\n * be empty, but audio players may be allocated by the server upon request.\n *\n * @private\n * @type {Object.}\n */\n var audioPlayers = {};\n\n /**\n * All video players currently in use by the client. Initially, this will\n * be empty, but video players may be allocated by the server upon request.\n *\n * @private\n * @type {Object.}\n */\n var videoPlayers = {};\n\n // No initial parsers\n var parsers = [];\n\n // No initial streams\n var streams = [];\n\n /**\n * All current objects. The index of each object is dictated by the\n * Guacamole server.\n *\n * @private\n * @type {Guacamole.Object[]}\n */\n var objects = [];\n\n // Pool of available stream indices\n var stream_indices = new Guacamole.IntegerPool();\n\n // Array of allocated output streams by index\n var output_streams = [];\n\n function setState(state) {\n if (state != currentState) {\n currentState = state;\n if (guac_client.onstatechange)\n guac_client.onstatechange(currentState);\n }\n }\n\n function isConnected() {\n return currentState == STATE_CONNECTED\n || currentState == STATE_WAITING;\n }\n\n /**\n * Produces an opaque representation of Guacamole.Client state which can be\n * later imported through a call to importState(). This object is\n * effectively an independent, compressed snapshot of protocol and display\n * state. Invoking this function implicitly flushes the display.\n *\n * @param {function} callback\n * Callback which should be invoked once the state object is ready. The\n * state object will be passed to the callback as the sole parameter.\n * This callback may be invoked immediately, or later as the display\n * finishes rendering and becomes ready.\n */\n this.exportState = function exportState(callback) {\n\n // Start with empty state\n var state = {\n 'currentState' : currentState,\n 'currentTimestamp' : currentTimestamp,\n 'layers' : {}\n };\n\n var layersSnapshot = {};\n\n // Make a copy of all current layers (protocol state)\n for (var key in layers) {\n layersSnapshot[key] = layers[key];\n }\n\n // Populate layers once data is available (display state, requires flush)\n display.flush(function populateLayers() {\n\n // Export each defined layer/buffer\n for (var key in layersSnapshot) {\n\n var index = parseInt(key);\n var layer = layersSnapshot[key];\n var canvas = layer.toCanvas();\n\n // Store layer/buffer dimensions\n var exportLayer = {\n 'width' : layer.width,\n 'height' : layer.height\n };\n\n // Store layer/buffer image data, if it can be generated\n if (layer.width && layer.height)\n exportLayer.url = canvas.toDataURL('image/png');\n\n // Add layer properties if not a buffer nor the default layer\n if (index > 0) {\n exportLayer.x = layer.x;\n exportLayer.y = layer.y;\n exportLayer.z = layer.z;\n exportLayer.alpha = layer.alpha;\n exportLayer.matrix = layer.matrix;\n exportLayer.parent = getLayerIndex(layer.parent);\n }\n\n // Store exported layer\n state.layers[key] = exportLayer;\n\n }\n\n // Invoke callback now that the state is ready\n callback(state);\n\n });\n\n };\n\n /**\n * Restores Guacamole.Client protocol and display state based on an opaque\n * object from a prior call to exportState(). The Guacamole.Client instance\n * used to export that state need not be the same as this instance.\n *\n * @param {Object} state\n * An opaque representation of Guacamole.Client state from a prior call\n * to exportState().\n *\n * @param {function} [callback]\n * The function to invoke when state has finished being imported. This\n * may happen immediately, or later as images within the provided state\n * object are loaded.\n */\n this.importState = function importState(state, callback) {\n\n var key;\n var index;\n\n currentState = state.currentState;\n currentTimestamp = state.currentTimestamp;\n\n // Dispose of all layers\n for (key in layers) {\n index = parseInt(key);\n if (index > 0)\n display.dispose(layers[key]);\n }\n\n layers = {};\n\n // Import state of each layer/buffer\n for (key in state.layers) {\n\n index = parseInt(key);\n\n var importLayer = state.layers[key];\n var layer = getLayer(index);\n\n // Reset layer size\n display.resize(layer, importLayer.width, importLayer.height);\n\n // Initialize new layer if it has associated data\n if (importLayer.url) {\n display.setChannelMask(layer, Guacamole.Layer.SRC);\n display.draw(layer, 0, 0, importLayer.url);\n }\n\n // Set layer-specific properties if not a buffer nor the default layer\n if (index > 0 && importLayer.parent >= 0) {\n\n // Apply layer position and set parent\n var parent = getLayer(importLayer.parent);\n display.move(layer, parent, importLayer.x, importLayer.y, importLayer.z);\n\n // Set layer transparency\n display.shade(layer, importLayer.alpha);\n\n // Apply matrix transform\n var matrix = importLayer.matrix;\n display.distort(layer,\n matrix[0], matrix[1], matrix[2],\n matrix[3], matrix[4], matrix[5]);\n\n }\n\n }\n\n // Flush changes to display\n display.flush(callback);\n\n };\n\n /**\n * Returns the underlying display of this Guacamole.Client. The display\n * contains an Element which can be added to the DOM, causing the\n * display to become visible.\n *\n * @return {Guacamole.Display} The underlying display of this\n * Guacamole.Client.\n */\n this.getDisplay = function() {\n return display;\n };\n\n /**\n * Sends the current size of the screen.\n *\n * @param {Number} width The width of the screen.\n * @param {Number} height The height of the screen.\n */\n this.sendSize = function(width, height) {\n\n // Do not send requests if not connected\n if (!isConnected())\n return;\n\n tunnel.sendMessage(\"size\", width, height);\n\n };\n\n /**\n * Sends a key event having the given properties as if the user\n * pressed or released a key.\n *\n * @param {Boolean} pressed Whether the key is pressed (true) or released\n * (false).\n * @param {Number} keysym The keysym of the key being pressed or released.\n */\n this.sendKeyEvent = function(pressed, keysym) {\n // Do not send requests if not connected\n if (!isConnected())\n return;\n\n tunnel.sendMessage(\"key\", keysym, pressed);\n };\n\n /**\n * Sends a mouse event having the properties provided by the given mouse\n * state.\n *\n * @param {Guacamole.Mouse.State} mouseState The state of the mouse to send\n * in the mouse event.\n */\n this.sendMouseState = function(mouseState) {\n\n // Do not send requests if not connected\n if (!isConnected())\n return;\n\n // Update client-side cursor\n display.moveCursor(\n Math.floor(mouseState.x),\n Math.floor(mouseState.y)\n );\n\n // Build mask\n var buttonMask = 0;\n if (mouseState.left) buttonMask |= 1;\n if (mouseState.middle) buttonMask |= 2;\n if (mouseState.right) buttonMask |= 4;\n if (mouseState.up) buttonMask |= 8;\n if (mouseState.down) buttonMask |= 16;\n\n // Send message\n tunnel.sendMessage(\"mouse\", Math.floor(mouseState.x), Math.floor(mouseState.y), buttonMask);\n };\n\n this.sendExec = function(args) {\n if (Array.isArray(args)) {\n tunnel.sendMessage(\"exec\", ...args);\n return\n }\n tunnel.sendMessage(\"exec\", args);\n };\n\n /**\n * Sets the clipboard of the remote client to the given text data.\n *\n * @deprecated Use createClipboardStream() instead.\n * @param {String} data The data to send as the clipboard contents.\n */\n this.setClipboard = function(data) {\n\n // Do not send requests if not connected\n if (!isConnected())\n return;\n\n // Open stream\n var stream = guac_client.createClipboardStream(\"text/plain\");\n var writer = new Guacamole.StringWriter(stream);\n\n // Send text chunks\n for (var i=0; i 0)\n layer = display.createLayer();\n else\n layer = display.createBuffer();\n\n // Add new layer\n layers[index] = layer;\n\n }\n\n return layer;\n\n };\n\n /**\n * Returns the index passed to getLayer() when the given layer was created.\n * Positive indices refer to visible layers, an index of zero refers to the\n * default layer, and negative indices refer to buffers.\n *\n * @param {Guacamole.Display.VisibleLayer|Guacamole.Layer} layer\n * The layer whose index should be determined.\n *\n * @returns {Number}\n * The index of the given layer, or null if no such layer is associated\n * with this client.\n */\n var getLayerIndex = function getLayerIndex(layer) {\n\n // Avoid searching if there clearly is no such layer\n if (!layer)\n return null;\n\n // Search through each layer, returning the index of the given layer\n // once found\n for (var key in layers) {\n if (layer === layers[key])\n return parseInt(key);\n }\n\n // Otherwise, no such index\n return null;\n\n };\n\n function getParser(index) {\n\n var parser = parsers[index];\n\n // If parser not yet created, create it, and tie to the\n // oninstruction handler of the tunnel.\n if (parser == null) {\n parser = parsers[index] = new Guacamole.Parser();\n parser.oninstruction = tunnel.oninstruction;\n }\n\n return parser;\n\n }\n\n /**\n * Handlers for all defined layer properties.\n * @private\n */\n var layerPropertyHandlers = {\n\n \"miter-limit\": function(layer, value) {\n display.setMiterLimit(layer, parseFloat(value));\n }\n\n };\n\n /**\n * Handlers for all instruction opcodes receivable by a Guacamole protocol\n * client.\n * @private\n */\n var instructionHandlers = {\n\n \"ack\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var reason = parameters[1];\n var code = parseInt(parameters[2]);\n\n // Get stream\n var stream = output_streams[stream_index];\n if (stream) {\n\n // Signal ack if handler defined\n if (stream.onack)\n stream.onack(new Guacamole.Status(code, reason));\n\n // If code is an error, invalidate stream if not already\n // invalidated by onack handler\n if (code >= 0x0100 && output_streams[stream_index] === stream) {\n stream_indices.free(stream_index);\n delete output_streams[stream_index];\n }\n\n }\n\n },\n\n \"arc\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var x = parseInt(parameters[1]);\n var y = parseInt(parameters[2]);\n var radius = parseInt(parameters[3]);\n var startAngle = parseFloat(parameters[4]);\n var endAngle = parseFloat(parameters[5]);\n var negative = parseInt(parameters[6]);\n\n display.arc(layer, x, y, radius, startAngle, endAngle, negative != 0);\n\n },\n\n \"audio\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var mimetype = parameters[1];\n\n // Create stream\n var stream = streams[stream_index] =\n new Guacamole.InputStream(guac_client, stream_index);\n\n // Get player instance via callback\n var audioPlayer = null;\n if (guac_client.onaudio)\n audioPlayer = guac_client.onaudio(stream, mimetype);\n\n // If unsuccessful, try to use a default implementation\n if (!audioPlayer)\n audioPlayer = Guacamole.AudioPlayer.getInstance(stream, mimetype);\n\n // If we have successfully retrieved an audio player, send success response\n if (audioPlayer) {\n audioPlayers[stream_index] = audioPlayer;\n guac_client.sendAck(stream_index, \"OK\", 0x0000);\n }\n\n // Otherwise, mimetype must be unsupported\n else\n guac_client.sendAck(stream_index, \"BAD TYPE\", 0x030F);\n\n },\n\n \"blob\": function(parameters) {\n\n // Get stream\n var stream_index = parseInt(parameters[0]);\n var data = parameters[1];\n var stream = streams[stream_index];\n\n // Write data\n if (stream && stream.onblob)\n stream.onblob(data);\n\n },\n\n \"body\" : function handleBody(parameters) {\n\n // Get object\n var objectIndex = parseInt(parameters[0]);\n var object = objects[objectIndex];\n\n var streamIndex = parseInt(parameters[1]);\n var mimetype = parameters[2];\n var name = parameters[3];\n\n // Create stream if handler defined\n if (object && object.onbody) {\n var stream = streams[streamIndex] = new Guacamole.InputStream(guac_client, streamIndex);\n object.onbody(stream, mimetype, name);\n }\n\n // Otherwise, unsupported\n else\n guac_client.sendAck(streamIndex, \"Receipt of body unsupported\", 0x0100);\n\n },\n\n \"cfill\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var r = parseInt(parameters[2]);\n var g = parseInt(parameters[3]);\n var b = parseInt(parameters[4]);\n var a = parseInt(parameters[5]);\n\n display.setChannelMask(layer, channelMask);\n display.fillColor(layer, r, g, b, a);\n\n },\n\n \"clip\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.clip(layer);\n\n },\n\n \"clipboard\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var mimetype = parameters[1];\n\n // Create stream\n if (guac_client.onclipboard) {\n var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);\n guac_client.onclipboard(stream, mimetype);\n }\n\n // Otherwise, unsupported\n else\n guac_client.sendAck(stream_index, \"Clipboard unsupported\", 0x0100);\n\n },\n\n \"close\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.close(layer);\n\n },\n\n \"copy\": function(parameters) {\n\n var srcL = getLayer(parseInt(parameters[0]));\n var srcX = parseInt(parameters[1]);\n var srcY = parseInt(parameters[2]);\n var srcWidth = parseInt(parameters[3]);\n var srcHeight = parseInt(parameters[4]);\n var channelMask = parseInt(parameters[5]);\n var dstL = getLayer(parseInt(parameters[6]));\n var dstX = parseInt(parameters[7]);\n var dstY = parseInt(parameters[8]);\n\n display.setChannelMask(dstL, channelMask);\n display.copy(srcL, srcX, srcY, srcWidth, srcHeight,\n dstL, dstX, dstY);\n\n },\n\n \"cstroke\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var cap = lineCap[parseInt(parameters[2])];\n var join = lineJoin[parseInt(parameters[3])];\n var thickness = parseInt(parameters[4]);\n var r = parseInt(parameters[5]);\n var g = parseInt(parameters[6]);\n var b = parseInt(parameters[7]);\n var a = parseInt(parameters[8]);\n\n display.setChannelMask(layer, channelMask);\n display.strokeColor(layer, cap, join, thickness, r, g, b, a);\n\n },\n\n \"cursor\": function(parameters) {\n\n var cursorHotspotX = parseInt(parameters[0]);\n var cursorHotspotY = parseInt(parameters[1]);\n var srcL = getLayer(parseInt(parameters[2]));\n var srcX = parseInt(parameters[3]);\n var srcY = parseInt(parameters[4]);\n var srcWidth = parseInt(parameters[5]);\n var srcHeight = parseInt(parameters[6]);\n\n display.setCursor(cursorHotspotX, cursorHotspotY,\n srcL, srcX, srcY, srcWidth, srcHeight);\n\n },\n\n \"curve\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var cp1x = parseInt(parameters[1]);\n var cp1y = parseInt(parameters[2]);\n var cp2x = parseInt(parameters[3]);\n var cp2y = parseInt(parameters[4]);\n var x = parseInt(parameters[5]);\n var y = parseInt(parameters[6]);\n\n display.curveTo(layer, cp1x, cp1y, cp2x, cp2y, x, y);\n\n },\n\n \"disconnect\" : function handleDisconnect(parameters) {\n\n // Explicitly tear down connection\n guac_client.disconnect();\n\n },\n\n \"dispose\": function(parameters) {\n\n var layer_index = parseInt(parameters[0]);\n\n // If visible layer, remove from parent\n if (layer_index > 0) {\n\n // Remove from parent\n var layer = getLayer(layer_index);\n display.dispose(layer);\n\n // Delete reference\n delete layers[layer_index];\n\n }\n\n // If buffer, just delete reference\n else if (layer_index < 0)\n delete layers[layer_index];\n\n // Attempting to dispose the root layer currently has no effect.\n\n },\n\n \"distort\": function(parameters) {\n\n var layer_index = parseInt(parameters[0]);\n var a = parseFloat(parameters[1]);\n var b = parseFloat(parameters[2]);\n var c = parseFloat(parameters[3]);\n var d = parseFloat(parameters[4]);\n var e = parseFloat(parameters[5]);\n var f = parseFloat(parameters[6]);\n\n // Only valid for visible layers (not buffers)\n if (layer_index >= 0) {\n var layer = getLayer(layer_index);\n display.distort(layer, a, b, c, d, e, f);\n }\n\n },\n\n \"error\": function(parameters) {\n\n var reason = parameters[0];\n var code = parseInt(parameters[1]);\n\n // Call handler if defined\n if (guac_client.onerror)\n guac_client.onerror(new Guacamole.Status(code, reason));\n\n guac_client.disconnect();\n\n },\n\n \"end\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n\n // Get stream\n var stream = streams[stream_index];\n if (stream) {\n\n // Signal end of stream if handler defined\n if (stream.onend)\n stream.onend();\n\n // Invalidate stream\n delete streams[stream_index];\n\n }\n\n },\n\n \"file\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var mimetype = parameters[1];\n var filename = parameters[2];\n\n // Create stream\n if (guac_client.onfile) {\n var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);\n guac_client.onfile(stream, mimetype, filename);\n }\n\n // Otherwise, unsupported\n else\n guac_client.sendAck(stream_index, \"File transfer unsupported\", 0x0100);\n\n },\n\n \"filesystem\" : function handleFilesystem(parameters) {\n\n var objectIndex = parseInt(parameters[0]);\n var name = parameters[1];\n\n // Create object, if supported\n if (guac_client.onfilesystem) {\n var object = objects[objectIndex] = new Guacamole.Object(guac_client, objectIndex);\n guac_client.onfilesystem(object, name);\n }\n\n // If unsupported, simply ignore the availability of the filesystem\n\n },\n\n \"identity\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.setTransform(layer, 1, 0, 0, 1, 0, 0);\n\n },\n\n \"img\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var channelMask = parseInt(parameters[1]);\n var layer = getLayer(parseInt(parameters[2]));\n var mimetype = parameters[3];\n var x = parseInt(parameters[4]);\n var y = parseInt(parameters[5]);\n\n // Create stream\n var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);\n var reader = new Guacamole.DataURIReader(stream, mimetype);\n\n // Draw image when stream is complete\n reader.onend = function drawImageBlob() {\n display.setChannelMask(layer, channelMask);\n display.draw(layer, x, y, reader.getURI());\n };\n\n },\n\n \"jpeg\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var x = parseInt(parameters[2]);\n var y = parseInt(parameters[3]);\n var data = parameters[4];\n\n display.setChannelMask(layer, channelMask);\n display.draw(layer, x, y, \"data:image/jpeg;base64,\" + data);\n\n },\n\n \"lfill\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var srcLayer = getLayer(parseInt(parameters[2]));\n\n display.setChannelMask(layer, channelMask);\n display.fillLayer(layer, srcLayer);\n\n },\n\n \"line\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var x = parseInt(parameters[1]);\n var y = parseInt(parameters[2]);\n\n display.lineTo(layer, x, y);\n\n },\n\n \"lstroke\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var srcLayer = getLayer(parseInt(parameters[2]));\n\n display.setChannelMask(layer, channelMask);\n display.strokeLayer(layer, srcLayer);\n\n },\n\n \"mouse\" : function handleMouse(parameters) {\n\n var x = parseInt(parameters[0]);\n var y = parseInt(parameters[1]);\n\n // Display and move software cursor to received coordinates\n display.showCursor(true);\n display.moveCursor(x, y);\n\n },\n\n \"move\": function(parameters) {\n\n var layer_index = parseInt(parameters[0]);\n var parent_index = parseInt(parameters[1]);\n var x = parseInt(parameters[2]);\n var y = parseInt(parameters[3]);\n var z = parseInt(parameters[4]);\n\n // Only valid for non-default layers\n if (layer_index > 0 && parent_index >= 0) {\n var layer = getLayer(layer_index);\n var parent = getLayer(parent_index);\n display.move(layer, parent, x, y, z);\n }\n\n },\n\n \"name\": function(parameters) {\n if (guac_client.onname) guac_client.onname(parameters[0]);\n },\n\n \"custom\": function(parameters) {\n if (guac_client.oncustom) guac_client.oncustom(parameters[0]);\n },\n\n \"nest\": function(parameters) {\n var parser = getParser(parseInt(parameters[0]));\n parser.receive(parameters[1]);\n },\n\n \"pipe\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var mimetype = parameters[1];\n var name = parameters[2];\n\n // Create stream\n if (guac_client.onpipe) {\n var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);\n guac_client.onpipe(stream, mimetype, name);\n }\n\n // Otherwise, unsupported\n else\n guac_client.sendAck(stream_index, \"Named pipes unsupported\", 0x0100);\n\n },\n\n \"png\": function(parameters) {\n\n var channelMask = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var x = parseInt(parameters[2]);\n var y = parseInt(parameters[3]);\n var data = parameters[4];\n\n display.setChannelMask(layer, channelMask);\n display.draw(layer, x, y, \"data:image/png;base64,\" + data);\n\n },\n\n \"pop\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.pop(layer);\n\n },\n\n \"push\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.push(layer);\n\n },\n\n \"rect\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var x = parseInt(parameters[1]);\n var y = parseInt(parameters[2]);\n var w = parseInt(parameters[3]);\n var h = parseInt(parameters[4]);\n\n display.rect(layer, x, y, w, h);\n\n },\n\n \"reset\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n\n display.reset(layer);\n\n },\n\n \"set\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var name = parameters[1];\n var value = parameters[2];\n\n // Call property handler if defined\n var handler = layerPropertyHandlers[name];\n if (handler)\n handler(layer, value);\n\n },\n\n \"shade\": function(parameters) {\n\n var layer_index = parseInt(parameters[0]);\n var a = parseInt(parameters[1]);\n\n // Only valid for visible layers (not buffers)\n if (layer_index >= 0) {\n var layer = getLayer(layer_index);\n display.shade(layer, a);\n }\n\n },\n\n \"size\": function(parameters) {\n\n var layer_index = parseInt(parameters[0]);\n var layer = getLayer(layer_index);\n var width = parseInt(parameters[1]);\n var height = parseInt(parameters[2]);\n\n display.resize(layer, width, height);\n\n },\n\n \"start\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var x = parseInt(parameters[1]);\n var y = parseInt(parameters[2]);\n\n display.moveTo(layer, x, y);\n\n },\n\n \"sync\": function(parameters) {\n\n var timestamp = parseInt(parameters[0]);\n\n // Flush display, send sync when done\n display.flush(function displaySyncComplete() {\n\n // Synchronize all audio players\n for (var index in audioPlayers) {\n var audioPlayer = audioPlayers[index];\n if (audioPlayer)\n audioPlayer.sync();\n }\n\n // Send sync response to server\n if (timestamp !== currentTimestamp) {\n tunnel.sendMessage(\"sync\", timestamp);\n currentTimestamp = timestamp;\n }\n\n });\n\n // If received first update, no longer waiting.\n if (currentState === STATE_WAITING)\n setState(STATE_CONNECTED);\n\n // Call sync handler if defined\n if (guac_client.onsync)\n guac_client.onsync(timestamp);\n\n },\n\n \"transfer\": function(parameters) {\n\n var srcL = getLayer(parseInt(parameters[0]));\n var srcX = parseInt(parameters[1]);\n var srcY = parseInt(parameters[2]);\n var srcWidth = parseInt(parameters[3]);\n var srcHeight = parseInt(parameters[4]);\n var function_index = parseInt(parameters[5]);\n var dstL = getLayer(parseInt(parameters[6]));\n var dstX = parseInt(parameters[7]);\n var dstY = parseInt(parameters[8]);\n\n /* SRC */\n if (function_index === 0x3)\n display.put(srcL, srcX, srcY, srcWidth, srcHeight,\n dstL, dstX, dstY);\n\n /* Anything else that isn't a NO-OP */\n else if (function_index !== 0x5)\n display.transfer(srcL, srcX, srcY, srcWidth, srcHeight,\n dstL, dstX, dstY, Guacamole.Client.DefaultTransferFunction[function_index]);\n\n },\n\n \"transform\": function(parameters) {\n\n var layer = getLayer(parseInt(parameters[0]));\n var a = parseFloat(parameters[1]);\n var b = parseFloat(parameters[2]);\n var c = parseFloat(parameters[3]);\n var d = parseFloat(parameters[4]);\n var e = parseFloat(parameters[5]);\n var f = parseFloat(parameters[6]);\n\n display.transform(layer, a, b, c, d, e, f);\n\n },\n\n \"undefine\" : function handleUndefine(parameters) {\n\n // Get object\n var objectIndex = parseInt(parameters[0]);\n var object = objects[objectIndex];\n\n // Signal end of object definition\n if (object && object.onundefine)\n object.onundefine();\n\n },\n\n \"video\": function(parameters) {\n\n var stream_index = parseInt(parameters[0]);\n var layer = getLayer(parseInt(parameters[1]));\n var mimetype = parameters[2];\n\n // Create stream\n var stream = streams[stream_index] =\n new Guacamole.InputStream(guac_client, stream_index);\n\n // Get player instance via callback\n var videoPlayer = null;\n if (guac_client.onvideo)\n videoPlayer = guac_client.onvideo(stream, layer, mimetype);\n\n // If unsuccessful, try to use a default implementation\n if (!videoPlayer)\n videoPlayer = Guacamole.VideoPlayer.getInstance(stream, layer, mimetype);\n\n // If we have successfully retrieved an video player, send success response\n if (videoPlayer) {\n videoPlayers[stream_index] = videoPlayer;\n guac_client.sendAck(stream_index, \"OK\", 0x0000);\n }\n\n // Otherwise, mimetype must be unsupported\n else\n guac_client.sendAck(stream_index, \"BAD TYPE\", 0x030F);\n\n }\n\n };\n\n tunnel.oninstruction = function(opcode, parameters) {\n\n var handler = instructionHandlers[opcode];\n if (handler)\n handler(parameters);\n\n };\n\n /**\n * Sends a disconnect instruction to the server and closes the tunnel.\n */\n this.disconnect = function() {\n\n // Only attempt disconnection not disconnected.\n if (currentState != STATE_DISCONNECTED\n && currentState != STATE_DISCONNECTING) {\n\n setState(STATE_DISCONNECTING);\n\n // Stop ping\n if (pingInterval)\n window.clearInterval(pingInterval);\n\n // Send disconnect message and disconnect\n tunnel.sendMessage(\"disconnect\");\n tunnel.disconnect();\n setState(STATE_DISCONNECTED);\n\n }\n\n };\n\n /**\n * Connects the underlying tunnel of this Guacamole.Client, passing the\n * given arbitrary data to the tunnel during the connection process.\n *\n * @param data Arbitrary connection data to be sent to the underlying\n * tunnel during the connection process.\n * @throws {Guacamole.Status} If an error occurs during connection.\n */\n this.connect = function(data) {\n\n setState(STATE_CONNECTING);\n\n try {\n tunnel.connect(data);\n }\n catch (status) {\n setState(STATE_IDLE);\n throw status;\n }\n\n // Ping every 5 seconds (ensure connection alive)\n pingInterval = window.setInterval(function() {\n tunnel.sendMessage(\"nop\");\n }, 5000);\n\n setState(STATE_WAITING);\n };\n\n};\n\n/**\n * Map of all Guacamole binary raster operations to transfer functions.\n * @private\n */\nGuacamole.Client.DefaultTransferFunction = {\n\n /* BLACK */\n 0x0: function (src, dst) {\n dst.red = dst.green = dst.blue = 0x00;\n },\n\n /* WHITE */\n 0xF: function (src, dst) {\n dst.red = dst.green = dst.blue = 0xFF;\n },\n\n /* SRC */\n 0x3: function (src, dst) {\n dst.red = src.red;\n dst.green = src.green;\n dst.blue = src.blue;\n dst.alpha = src.alpha;\n },\n\n /* DEST (no-op) */\n 0x5: function (src, dst) {\n // Do nothing\n },\n\n /* Invert SRC */\n 0xC: function (src, dst) {\n dst.red = 0xFF & ~src.red;\n dst.green = 0xFF & ~src.green;\n dst.blue = 0xFF & ~src.blue;\n dst.alpha = src.alpha;\n },\n\n /* Invert DEST */\n 0xA: function (src, dst) {\n dst.red = 0xFF & ~dst.red;\n dst.green = 0xFF & ~dst.green;\n dst.blue = 0xFF & ~dst.blue;\n },\n\n /* AND */\n 0x1: function (src, dst) {\n dst.red = ( src.red & dst.red);\n dst.green = ( src.green & dst.green);\n dst.blue = ( src.blue & dst.blue);\n },\n\n /* NAND */\n 0xE: function (src, dst) {\n dst.red = 0xFF & ~( src.red & dst.red);\n dst.green = 0xFF & ~( src.green & dst.green);\n dst.blue = 0xFF & ~( src.blue & dst.blue);\n },\n\n /* OR */\n 0x7: function (src, dst) {\n dst.red = ( src.red | dst.red);\n dst.green = ( src.green | dst.green);\n dst.blue = ( src.blue | dst.blue);\n },\n\n /* NOR */\n 0x8: function (src, dst) {\n dst.red = 0xFF & ~( src.red | dst.red);\n dst.green = 0xFF & ~( src.green | dst.green);\n dst.blue = 0xFF & ~( src.blue | dst.blue);\n },\n\n /* XOR */\n 0x6: function (src, dst) {\n dst.red = ( src.red ^ dst.red);\n dst.green = ( src.green ^ dst.green);\n dst.blue = ( src.blue ^ dst.blue);\n },\n\n /* XNOR */\n 0x9: function (src, dst) {\n dst.red = 0xFF & ~( src.red ^ dst.red);\n dst.green = 0xFF & ~( src.green ^ dst.green);\n dst.blue = 0xFF & ~( src.blue ^ dst.blue);\n },\n\n /* AND inverted source */\n 0x4: function (src, dst) {\n dst.red = 0xFF & (~src.red & dst.red);\n dst.green = 0xFF & (~src.green & dst.green);\n dst.blue = 0xFF & (~src.blue & dst.blue);\n },\n\n /* OR inverted source */\n 0xD: function (src, dst) {\n dst.red = 0xFF & (~src.red | dst.red);\n dst.green = 0xFF & (~src.green | dst.green);\n dst.blue = 0xFF & (~src.blue | dst.blue);\n },\n\n /* AND inverted destination */\n 0x2: function (src, dst) {\n dst.red = 0xFF & ( src.red & ~dst.red);\n dst.green = 0xFF & ( src.green & ~dst.green);\n dst.blue = 0xFF & ( src.blue & ~dst.blue);\n },\n\n /* OR inverted destination */\n 0xB: function (src, dst) {\n dst.red = 0xFF & ( src.red | ~dst.red);\n dst.green = 0xFF & ( src.green | ~dst.green);\n dst.blue = 0xFF & ( src.blue | ~dst.blue);\n }\n\n};\n\n\n/**\n * A reader which automatically handles the given input stream, returning\n * received blobs as a single data URI built over the course of the stream.\n * Note that this object will overwrite any installed event handlers on the\n * given Guacamole.InputStream.\n *\n * @constructor\n * @param {Guacamole.InputStream} stream\n * The stream that data will be read from.\n */\nGuacamole.DataURIReader = function(stream, mimetype) {\n\n /**\n * Reference to this Guacamole.DataURIReader.\n * @private\n */\n var guac_reader = this;\n\n /**\n * Current data URI.\n *\n * @private\n * @type {String}\n */\n var uri = 'data:' + mimetype + ';base64,';\n\n // Receive blobs as array buffers\n stream.onblob = function dataURIReaderBlob(data) {\n\n // Currently assuming data will ALWAYS be safe to simply append. This\n // will not be true if the received base64 data encodes a number of\n // bytes that isn't a multiple of three (as base64 expands in a ratio\n // of exactly 3:4).\n uri += data;\n\n };\n\n // Simply call onend when end received\n stream.onend = function dataURIReaderEnd() {\n if (guac_reader.onend)\n guac_reader.onend();\n };\n\n /**\n * Returns the data URI of all data received through the underlying stream\n * thus far.\n *\n * @returns {String}\n * The data URI of all data received through the underlying stream thus\n * far.\n */\n this.getURI = function getURI() {\n return uri;\n };\n\n /**\n * Fired once this stream is finished and no further data will be written.\n *\n * @event\n */\n this.onend = null;\n\n};\n\n/**\n * The Guacamole display. The display does not deal with the Guacamole\n * protocol, and instead implements a set of graphical operations which\n * embody the set of operations present in the protocol. The order operations\n * are executed is guaranteed to be in the same order as their corresponding\n * functions are called.\n *\n * @constructor\n */\nGuacamole.Display = function() {\n\n /**\n * Reference to this Guacamole.Display.\n * @private\n */\n var guac_display = this;\n\n var displayWidth = 0;\n var displayHeight = 0;\n var displayScale = 1;\n\n // Create display\n var display = document.createElement(\"div\");\n display.style.position = \"relative\";\n display.style.width = displayWidth + \"px\";\n display.style.height = displayHeight + \"px\";\n\n // Ensure transformations on display originate at 0,0\n display.style.transformOrigin =\n display.style.webkitTransformOrigin =\n display.style.MozTransformOrigin =\n display.style.OTransformOrigin =\n display.style.msTransformOrigin =\n \"0 0\";\n\n // Create default layer\n var default_layer = new Guacamole.Display.VisibleLayer(displayWidth, displayHeight);\n\n // Create cursor layer\n var cursor = new Guacamole.Display.VisibleLayer(0, 0);\n cursor.setChannelMask(Guacamole.Layer.SRC);\n\n // Add default layer and cursor to display\n display.appendChild(default_layer.getElement());\n display.appendChild(cursor.getElement());\n\n // Create bounding div\n var bounds = document.createElement(\"div\");\n bounds.style.position = \"relative\";\n bounds.style.width = (displayWidth*displayScale) + \"px\";\n bounds.style.height = (displayHeight*displayScale) + \"px\";\n\n // Add display to bounds\n bounds.appendChild(display);\n\n /**\n * The X coordinate of the hotspot of the mouse cursor. The hotspot is\n * the relative location within the image of the mouse cursor at which\n * each click occurs.\n *\n * @type {Number}\n */\n this.cursorHotspotX = 0;\n\n /**\n * The Y coordinate of the hotspot of the mouse cursor. The hotspot is\n * the relative location within the image of the mouse cursor at which\n * each click occurs.\n *\n * @type {Number}\n */\n this.cursorHotspotY = 0;\n\n /**\n * The current X coordinate of the local mouse cursor. This is not\n * necessarily the location of the actual mouse - it refers only to\n * the location of the cursor image within the Guacamole display, as\n * last set by moveCursor().\n *\n * @type {Number}\n */\n this.cursorX = 0;\n\n /**\n * The current X coordinate of the local mouse cursor. This is not\n * necessarily the location of the actual mouse - it refers only to\n * the location of the cursor image within the Guacamole display, as\n * last set by moveCursor().\n *\n * @type {Number}\n */\n this.cursorY = 0;\n\n /**\n * Fired when the default layer (and thus the entire Guacamole display)\n * is resized.\n *\n * @event\n * @param {Number} width The new width of the Guacamole display.\n * @param {Number} height The new height of the Guacamole display.\n */\n this.onresize = null;\n\n /**\n * Fired whenever the local cursor image is changed. This can be used to\n * implement special handling of the client-side cursor, or to override\n * the default use of a software cursor layer.\n *\n * @event\n * @param {HTMLCanvasElement} canvas The cursor image.\n * @param {Number} x The X-coordinate of the cursor hotspot.\n * @param {Number} y The Y-coordinate of the cursor hotspot.\n */\n this.oncursor = null;\n\n /**\n * The queue of all pending Tasks. Tasks will be run in order, with new\n * tasks added at the end of the queue and old tasks removed from the\n * front of the queue (FIFO). These tasks will eventually be grouped\n * into a Frame.\n * @private\n * @type {Task[]}\n */\n var tasks = [];\n\n /**\n * The queue of all frames. Each frame is a pairing of an array of tasks\n * and a callback which must be called when the frame is rendered.\n * @private\n * @type {Frame[]}\n */\n var frames = [];\n\n /**\n * Flushes all pending frames.\n * @private\n */\n function __flush_frames() {\n\n var rendered_frames = 0;\n\n // Draw all pending frames, if ready\n while (rendered_frames < frames.length) {\n\n var frame = frames[rendered_frames];\n if (!frame.isReady())\n break;\n\n frame.flush();\n rendered_frames++;\n\n }\n\n // Remove rendered frames from array\n frames.splice(0, rendered_frames);\n\n }\n\n /**\n * An ordered list of tasks which must be executed atomically. Once\n * executed, an associated (and optional) callback will be called.\n *\n * @private\n * @constructor\n * @param {function} callback The function to call when this frame is\n * rendered.\n * @param {Task[]} tasks The set of tasks which must be executed to render\n * this frame.\n */\n function Frame(callback, tasks) {\n\n /**\n * Returns whether this frame is ready to be rendered. This function\n * returns true if and only if ALL underlying tasks are unblocked.\n *\n * @returns {Boolean} true if all underlying tasks are unblocked,\n * false otherwise.\n */\n this.isReady = function() {\n\n // Search for blocked tasks\n for (var i=0; i < tasks.length; i++) {\n if (tasks[i].blocked)\n return false;\n }\n\n // If no blocked tasks, the frame is ready\n return true;\n\n };\n\n /**\n * Renders this frame, calling the associated callback, if any, after\n * the frame is complete. This function MUST only be called when no\n * blocked tasks exist. Calling this function with blocked tasks\n * will result in undefined behavior.\n */\n this.flush = function() {\n\n // Draw all pending tasks.\n for (var i=0; i < tasks.length; i++)\n tasks[i].execute();\n\n // Call callback\n if (callback) callback();\n\n };\n\n }\n\n /**\n * A container for an task handler. Each operation which must be ordered\n * is associated with a Task that goes into a task queue. Tasks in this\n * queue are executed in order once their handlers are set, while Tasks\n * without handlers block themselves and any following Tasks from running.\n *\n * @constructor\n * @private\n * @param {function} taskHandler The function to call when this task\n * runs, if any.\n * @param {boolean} blocked Whether this task should start blocked.\n */\n function Task(taskHandler, blocked) {\n\n var task = this;\n\n /**\n * Whether this Task is blocked.\n *\n * @type {boolean}\n */\n this.blocked = blocked;\n\n /**\n * Unblocks this Task, allowing it to run.\n */\n this.unblock = function() {\n if (task.blocked) {\n task.blocked = false;\n __flush_frames();\n }\n };\n\n /**\n * Calls the handler associated with this task IMMEDIATELY. This\n * function does not track whether this task is marked as blocked.\n * Enforcing the blocked status of tasks is up to the caller.\n */\n this.execute = function() {\n if (taskHandler) taskHandler();\n };\n\n }\n\n /**\n * Schedules a task for future execution. The given handler will execute\n * immediately after all previous tasks upon frame flush, unless this\n * task is blocked. If any tasks is blocked, the entire frame will not\n * render (and no tasks within will execute) until all tasks are unblocked.\n *\n * @private\n * @param {function} handler The function to call when possible, if any.\n * @param {boolean} blocked Whether the task should start blocked.\n * @returns {Task} The Task created and added to the queue for future\n * running.\n */\n function scheduleTask(handler, blocked) {\n var task = new Task(handler, blocked);\n tasks.push(task);\n return task;\n }\n\n /**\n * Returns the element which contains the Guacamole display.\n *\n * @return {Element} The element containing the Guacamole display.\n */\n this.getElement = function() {\n return bounds;\n };\n\n /**\n * Returns the width of this display.\n *\n * @return {Number} The width of this display;\n */\n this.getWidth = function() {\n return displayWidth;\n };\n\n /**\n * Returns the height of this display.\n *\n * @return {Number} The height of this display;\n */\n this.getHeight = function() {\n return displayHeight;\n };\n\n /**\n * Returns the default layer of this display. Each Guacamole display always\n * has at least one layer. Other layers can optionally be created within\n * this layer, but the default layer cannot be removed and is the absolute\n * ancestor of all other layers.\n *\n * @return {Guacamole.Display.VisibleLayer} The default layer.\n */\n this.getDefaultLayer = function() {\n return default_layer;\n };\n\n /**\n * Returns the cursor layer of this display. Each Guacamole display contains\n * a layer for the image of the mouse cursor. This layer is a special case\n * and exists above all other layers, similar to the hardware mouse cursor.\n *\n * @return {Guacamole.Display.VisibleLayer} The cursor layer.\n */\n this.getCursorLayer = function() {\n return cursor;\n };\n\n /**\n * Creates a new layer. The new layer will be a direct child of the default\n * layer, but can be moved to be a child of any other layer. Layers returned\n * by this function are visible.\n *\n * @return {Guacamole.Display.VisibleLayer} The newly-created layer.\n */\n this.createLayer = function() {\n var layer = new Guacamole.Display.VisibleLayer(displayWidth, displayHeight);\n layer.move(default_layer, 0, 0, 0);\n return layer;\n };\n\n /**\n * Creates a new buffer. Buffers are invisible, off-screen surfaces. They\n * are implemented in the same manner as layers, but do not provide the\n * same nesting semantics.\n *\n * @return {Guacamole.Layer} The newly-created buffer.\n */\n this.createBuffer = function() {\n var buffer = new Guacamole.Layer(0, 0);\n buffer.autosize = 1;\n return buffer;\n };\n\n /**\n * Flush all pending draw tasks, if possible, as a new frame. If the entire\n * frame is not ready, the flush will wait until all required tasks are\n * unblocked.\n *\n * @param {function} callback The function to call when this frame is\n * flushed. This may happen immediately, or\n * later when blocked tasks become unblocked.\n */\n this.flush = function(callback) {\n\n // Add frame, reset tasks\n frames.push(new Frame(callback, tasks));\n tasks = [];\n\n // Attempt flush\n __flush_frames();\n\n };\n\n /**\n * Sets the hotspot and image of the mouse cursor displayed within the\n * Guacamole display.\n *\n * @param {Number} hotspotX The X coordinate of the cursor hotspot.\n * @param {Number} hotspotY The Y coordinate of the cursor hotspot.\n * @param {Guacamole.Layer} layer The source layer containing the data which\n * should be used as the mouse cursor image.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * layer's coordinate space to copy data from.\n\n */\n this.setCursor = function(hotspotX, hotspotY, layer, srcx, srcy, srcw, srch) {\n scheduleTask(function __display_set_cursor() {\n\n // Set hotspot\n guac_display.cursorHotspotX = hotspotX;\n guac_display.cursorHotspotY = hotspotY;\n\n // Reset cursor size\n cursor.resize(srcw, srch);\n\n // Draw cursor to cursor layer\n cursor.copy(layer, srcx, srcy, srcw, srch, 0, 0);\n guac_display.moveCursor(guac_display.cursorX, guac_display.cursorY);\n\n // Fire cursor change event\n if (guac_display.oncursor)\n guac_display.oncursor(cursor.toCanvas(), hotspotX, hotspotY);\n\n });\n };\n\n /**\n * Sets whether the software-rendered cursor is shown. This cursor differs\n * from the hardware cursor in that it is built into the Guacamole.Display,\n * and relies on its own Guacamole layer to render.\n *\n * @param {Boolean} [shown=true] Whether to show the software cursor.\n */\n this.showCursor = function(shown) {\n\n var element = cursor.getElement();\n var parent = element.parentElement;\n\n // Remove from DOM if hidden\n if (shown === false) {\n if (parent)\n parent.removeChild(element);\n }\n\n // Otherwise, ensure cursor is child of display\n else if (parent !== display)\n display.appendChild(element);\n\n };\n\n /**\n * Sets the location of the local cursor to the given coordinates. For the\n * sake of responsiveness, this function performs its action immediately.\n * Cursor motion is not maintained within atomic frames.\n *\n * @param {Number} x The X coordinate to move the cursor to.\n * @param {Number} y The Y coordinate to move the cursor to.\n */\n this.moveCursor = function(x, y) {\n\n // Move cursor layer\n cursor.translate(x - guac_display.cursorHotspotX,\n y - guac_display.cursorHotspotY);\n\n // Update stored position\n guac_display.cursorX = x;\n guac_display.cursorY = y;\n\n };\n\n /**\n * Changes the size of the given Layer to the given width and height.\n * Resizing is only attempted if the new size provided is actually different\n * from the current size.\n *\n * @param {Guacamole.Layer} layer The layer to resize.\n * @param {Number} width The new width.\n * @param {Number} height The new height.\n */\n this.resize = function(layer, width, height) {\n scheduleTask(function __display_resize() {\n\n layer.resize(width, height);\n\n // Resize display if default layer is resized\n if (layer === default_layer) {\n\n // Update (set) display size\n displayWidth = width;\n displayHeight = height;\n display.style.width = displayWidth + \"px\";\n display.style.height = displayHeight + \"px\";\n\n // Update bounds size\n bounds.style.width = (displayWidth*displayScale) + \"px\";\n bounds.style.height = (displayHeight*displayScale) + \"px\";\n\n // Notify of resize\n if (guac_display.onresize)\n guac_display.onresize(width, height);\n\n }\n\n });\n };\n\n /**\n * Draws the specified image at the given coordinates. The image specified\n * must already be loaded.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n * @param {Image} image The image to draw. Note that this is an Image\n * object - not a URL.\n */\n this.drawImage = function(layer, x, y, image) {\n scheduleTask(function __display_drawImage() {\n layer.drawImage(x, y, image);\n });\n };\n\n /**\n * Draws the image contained within the specified Blob at the given\n * coordinates. The Blob specified must already be populated with image\n * data.\n *\n * @param {Guacamole.Layer} layer\n * The layer to draw upon.\n *\n * @param {Number} x\n * The destination X coordinate.\n *\n * @param {Number} y\n * The destination Y coordinate.\n *\n * @param {Blob} blob\n * The Blob containing the image data to draw.\n */\n this.drawBlob = function(layer, x, y, blob) {\n\n // Create URL for blob\n var url = URL.createObjectURL(blob);\n\n // Draw and free blob URL when ready\n var task = scheduleTask(function __display_drawBlob() {\n\n // Draw the image only if it loaded without errors\n if (image.width && image.height)\n layer.drawImage(x, y, image);\n\n // Blob URL no longer needed\n URL.revokeObjectURL(url);\n\n }, true);\n\n // Load image from URL\n var image = new Image();\n image.onload = task.unblock;\n image.onerror = task.unblock;\n image.src = url;\n\n };\n\n /**\n * Draws the image at the specified URL at the given coordinates. The image\n * will be loaded automatically, and this and any future operations will\n * wait for the image to finish loading.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n * @param {String} url The URL of the image to draw.\n */\n this.draw = function(layer, x, y, url) {\n\n var task = scheduleTask(function __display_draw() {\n\n // Draw the image only if it loaded without errors\n if (image.width && image.height)\n layer.drawImage(x, y, image);\n\n }, true);\n\n var image = new Image();\n image.onload = task.unblock;\n image.onerror = task.unblock;\n image.src = url;\n\n };\n\n /**\n * Plays the video at the specified URL within this layer. The video\n * will be loaded automatically, and this and any future operations will\n * wait for the video to finish loading. Future operations will not be\n * executed until the video finishes playing.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {String} mimetype The mimetype of the video to play.\n * @param {Number} duration The duration of the video in milliseconds.\n * @param {String} url The URL of the video to play.\n */\n this.play = function(layer, mimetype, duration, url) {\n\n // Start loading the video\n var video = document.createElement(\"video\");\n video.type = mimetype;\n video.src = url;\n\n // Start copying frames when playing\n video.addEventListener(\"play\", function() {\n\n function render_callback() {\n layer.drawImage(0, 0, video);\n if (!video.ended)\n window.setTimeout(render_callback, 20);\n }\n\n render_callback();\n\n }, false);\n\n scheduleTask(video.play);\n\n };\n\n /**\n * Transfer a rectangle of image data from one Layer to this Layer using the\n * specified transfer function.\n *\n * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source Layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * Layer's coordinate space to copy data from.\n * @param {Guacamole.Layer} dstLayer The layer to draw upon.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n * @param {Function} transferFunction The transfer function to use to\n * transfer data from source to\n * destination.\n */\n this.transfer = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y, transferFunction) {\n scheduleTask(function __display_transfer() {\n dstLayer.transfer(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction);\n });\n };\n\n /**\n * Put a rectangle of image data from one Layer to this Layer directly\n * without performing any alpha blending. Simply copy the data.\n *\n * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source Layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * Layer's coordinate space to copy data from.\n * @param {Guacamole.Layer} dstLayer The layer to draw upon.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n */\n this.put = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y) {\n scheduleTask(function __display_put() {\n dstLayer.put(srcLayer, srcx, srcy, srcw, srch, x, y);\n });\n };\n\n /**\n * Copy a rectangle of image data from one Layer to this Layer. This\n * operation will copy exactly the image data that will be drawn once all\n * operations of the source Layer that were pending at the time this\n * function was called are complete. This operation will not alter the\n * size of the source Layer even if its autosize property is set to true.\n *\n * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source Layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * Layer's coordinate space to copy data from.\n * @param {Guacamole.Layer} dstLayer The layer to draw upon.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n */\n this.copy = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y) {\n scheduleTask(function __display_copy() {\n dstLayer.copy(srcLayer, srcx, srcy, srcw, srch, x, y);\n });\n };\n\n /**\n * Starts a new path at the specified point.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The X coordinate of the point to draw.\n * @param {Number} y The Y coordinate of the point to draw.\n */\n this.moveTo = function(layer, x, y) {\n scheduleTask(function __display_moveTo() {\n layer.moveTo(x, y);\n });\n };\n\n /**\n * Add the specified line to the current path.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The X coordinate of the endpoint of the line to draw.\n * @param {Number} y The Y coordinate of the endpoint of the line to draw.\n */\n this.lineTo = function(layer, x, y) {\n scheduleTask(function __display_lineTo() {\n layer.lineTo(x, y);\n });\n };\n\n /**\n * Add the specified arc to the current path.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The X coordinate of the center of the circle which\n * will contain the arc.\n * @param {Number} y The Y coordinate of the center of the circle which\n * will contain the arc.\n * @param {Number} radius The radius of the circle.\n * @param {Number} startAngle The starting angle of the arc, in radians.\n * @param {Number} endAngle The ending angle of the arc, in radians.\n * @param {Boolean} negative Whether the arc should be drawn in order of\n * decreasing angle.\n */\n this.arc = function(layer, x, y, radius, startAngle, endAngle, negative) {\n scheduleTask(function __display_arc() {\n layer.arc(x, y, radius, startAngle, endAngle, negative);\n });\n };\n\n /**\n * Starts a new path at the specified point.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} cp1x The X coordinate of the first control point.\n * @param {Number} cp1y The Y coordinate of the first control point.\n * @param {Number} cp2x The X coordinate of the second control point.\n * @param {Number} cp2y The Y coordinate of the second control point.\n * @param {Number} x The X coordinate of the endpoint of the curve.\n * @param {Number} y The Y coordinate of the endpoint of the curve.\n */\n this.curveTo = function(layer, cp1x, cp1y, cp2x, cp2y, x, y) {\n scheduleTask(function __display_curveTo() {\n layer.curveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n });\n };\n\n /**\n * Closes the current path by connecting the end point with the start\n * point (if any) with a straight line.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n */\n this.close = function(layer) {\n scheduleTask(function __display_close() {\n layer.close();\n });\n };\n\n /**\n * Add the specified rectangle to the current path.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} x The X coordinate of the upper-left corner of the\n * rectangle to draw.\n * @param {Number} y The Y coordinate of the upper-left corner of the\n * rectangle to draw.\n * @param {Number} w The width of the rectangle to draw.\n * @param {Number} h The height of the rectangle to draw.\n */\n this.rect = function(layer, x, y, w, h) {\n scheduleTask(function __display_rect() {\n layer.rect(x, y, w, h);\n });\n };\n\n /**\n * Clip all future drawing operations by the current path. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as fillColor()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} layer The layer to affect.\n */\n this.clip = function(layer) {\n scheduleTask(function __display_clip() {\n layer.clip();\n });\n };\n\n /**\n * Stroke the current path with the specified color. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {String} cap The line cap style. Can be \"round\", \"square\",\n * or \"butt\".\n * @param {String} join The line join style. Can be \"round\", \"bevel\",\n * or \"miter\".\n * @param {Number} thickness The line thickness in pixels.\n * @param {Number} r The red component of the color to fill.\n * @param {Number} g The green component of the color to fill.\n * @param {Number} b The blue component of the color to fill.\n * @param {Number} a The alpha component of the color to fill.\n */\n this.strokeColor = function(layer, cap, join, thickness, r, g, b, a) {\n scheduleTask(function __display_strokeColor() {\n layer.strokeColor(cap, join, thickness, r, g, b, a);\n });\n };\n\n /**\n * Fills the current path with the specified color. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Number} r The red component of the color to fill.\n * @param {Number} g The green component of the color to fill.\n * @param {Number} b The blue component of the color to fill.\n * @param {Number} a The alpha component of the color to fill.\n */\n this.fillColor = function(layer, r, g, b, a) {\n scheduleTask(function __display_fillColor() {\n layer.fillColor(r, g, b, a);\n });\n };\n\n /**\n * Stroke the current path with the image within the specified layer. The\n * image data will be tiled infinitely within the stroke. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {String} cap The line cap style. Can be \"round\", \"square\",\n * or \"butt\".\n * @param {String} join The line join style. Can be \"round\", \"bevel\",\n * or \"miter\".\n * @param {Number} thickness The line thickness in pixels.\n * @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern\n * within the stroke.\n */\n this.strokeLayer = function(layer, cap, join, thickness, srcLayer) {\n scheduleTask(function __display_strokeLayer() {\n layer.strokeLayer(cap, join, thickness, srcLayer);\n });\n };\n\n /**\n * Fills the current path with the image within the specified layer. The\n * image data will be tiled infinitely within the stroke. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n * @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern\n * within the fill.\n */\n this.fillLayer = function(layer, srcLayer) {\n scheduleTask(function __display_fillLayer() {\n layer.fillLayer(srcLayer);\n });\n };\n\n /**\n * Push current layer state onto stack.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n */\n this.push = function(layer) {\n scheduleTask(function __display_push() {\n layer.push();\n });\n };\n\n /**\n * Pop layer state off stack.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n */\n this.pop = function(layer) {\n scheduleTask(function __display_pop() {\n layer.pop();\n });\n };\n\n /**\n * Reset the layer, clearing the stack, the current path, and any transform\n * matrix.\n *\n * @param {Guacamole.Layer} layer The layer to draw upon.\n */\n this.reset = function(layer) {\n scheduleTask(function __display_reset() {\n layer.reset();\n });\n };\n\n /**\n * Sets the given affine transform (defined with six values from the\n * transform's matrix).\n *\n * @param {Guacamole.Layer} layer The layer to modify.\n * @param {Number} a The first value in the affine transform's matrix.\n * @param {Number} b The second value in the affine transform's matrix.\n * @param {Number} c The third value in the affine transform's matrix.\n * @param {Number} d The fourth value in the affine transform's matrix.\n * @param {Number} e The fifth value in the affine transform's matrix.\n * @param {Number} f The sixth value in the affine transform's matrix.\n */\n this.setTransform = function(layer, a, b, c, d, e, f) {\n scheduleTask(function __display_setTransform() {\n layer.setTransform(a, b, c, d, e, f);\n });\n };\n\n /**\n * Applies the given affine transform (defined with six values from the\n * transform's matrix).\n *\n * @param {Guacamole.Layer} layer The layer to modify.\n * @param {Number} a The first value in the affine transform's matrix.\n * @param {Number} b The second value in the affine transform's matrix.\n * @param {Number} c The third value in the affine transform's matrix.\n * @param {Number} d The fourth value in the affine transform's matrix.\n * @param {Number} e The fifth value in the affine transform's matrix.\n * @param {Number} f The sixth value in the affine transform's matrix.\n */\n this.transform = function(layer, a, b, c, d, e, f) {\n scheduleTask(function __display_transform() {\n layer.transform(a, b, c, d, e, f);\n });\n };\n\n /**\n * Sets the channel mask for future operations on this Layer.\n *\n * The channel mask is a Guacamole-specific compositing operation identifier\n * with a single bit representing each of four channels (in order): source\n * image where destination transparent, source where destination opaque,\n * destination where source transparent, and destination where source\n * opaque.\n *\n * @param {Guacamole.Layer} layer The layer to modify.\n * @param {Number} mask The channel mask for future operations on this\n * Layer.\n */\n this.setChannelMask = function(layer, mask) {\n scheduleTask(function __display_setChannelMask() {\n layer.setChannelMask(mask);\n });\n };\n\n /**\n * Sets the miter limit for stroke operations using the miter join. This\n * limit is the maximum ratio of the size of the miter join to the stroke\n * width. If this ratio is exceeded, the miter will not be drawn for that\n * joint of the path.\n *\n * @param {Guacamole.Layer} layer The layer to modify.\n * @param {Number} limit The miter limit for stroke operations using the\n * miter join.\n */\n this.setMiterLimit = function(layer, limit) {\n scheduleTask(function __display_setMiterLimit() {\n layer.setMiterLimit(limit);\n });\n };\n\n /**\n * Removes the given layer container entirely, such that it is no longer\n * contained within its parent layer, if any.\n *\n * @param {Guacamole.Display.VisibleLayer} layer\n * The layer being removed from its parent.\n */\n this.dispose = function dispose(layer) {\n scheduleTask(function disposeLayer() {\n layer.dispose();\n });\n };\n\n /**\n * Applies the given affine transform (defined with six values from the\n * transform's matrix) to the given layer.\n *\n * @param {Guacamole.Display.VisibleLayer} layer\n * The layer being distorted.\n *\n * @param {Number} a\n * The first value in the affine transform's matrix.\n *\n * @param {Number} b\n * The second value in the affine transform's matrix.\n *\n * @param {Number} c\n * The third value in the affine transform's matrix.\n *\n * @param {Number} d\n * The fourth value in the affine transform's matrix.\n *\n * @param {Number} e\n * The fifth value in the affine transform's matrix.\n *\n * @param {Number} f\n * The sixth value in the affine transform's matrix.\n */\n this.distort = function distort(layer, a, b, c, d, e, f) {\n scheduleTask(function distortLayer() {\n layer.distort(a, b, c, d, e, f);\n });\n };\n\n /**\n * Moves the upper-left corner of the given layer to the given X and Y\n * coordinate, sets the Z stacking order, and reparents the layer\n * to the given parent layer.\n *\n * @param {Guacamole.Display.VisibleLayer} layer\n * The layer being moved.\n *\n * @param {Guacamole.Display.VisibleLayer} parent\n * The parent to set.\n *\n * @param {Number} x\n * The X coordinate to move to.\n *\n * @param {Number} y\n * The Y coordinate to move to.\n *\n * @param {Number} z\n * The Z coordinate to move to.\n */\n this.move = function move(layer, parent, x, y, z) {\n scheduleTask(function moveLayer() {\n layer.move(parent, x, y, z);\n });\n };\n\n /**\n * Sets the opacity of the given layer to the given value, where 255 is\n * fully opaque and 0 is fully transparent.\n *\n * @param {Guacamole.Display.VisibleLayer} layer\n * The layer whose opacity should be set.\n *\n * @param {Number} alpha\n * The opacity to set.\n */\n this.shade = function shade(layer, alpha) {\n scheduleTask(function shadeLayer() {\n layer.shade(alpha);\n });\n };\n\n /**\n * Sets the scale of the client display element such that it renders at\n * a relatively smaller or larger size, without affecting the true\n * resolution of the display.\n *\n * @param {Number} scale The scale to resize to, where 1.0 is normal\n * size (1:1 scale).\n */\n this.scale = function(scale) {\n\n display.style.transform =\n display.style.WebkitTransform =\n display.style.MozTransform =\n display.style.OTransform =\n display.style.msTransform =\n\n \"scale(\" + scale + \",\" + scale + \")\";\n\n displayScale = scale;\n\n // Update bounds size\n bounds.style.width = (displayWidth*displayScale) + \"px\";\n bounds.style.height = (displayHeight*displayScale) + \"px\";\n\n };\n\n /**\n * Returns the scale of the display.\n *\n * @return {Number} The scale of the display.\n */\n this.getScale = function() {\n return displayScale;\n };\n\n /**\n * Returns a canvas element containing the entire display, with all child\n * layers composited within.\n *\n * @return {HTMLCanvasElement} A new canvas element containing a copy of\n * the display.\n */\n this.flatten = function() {\n\n // Get destination canvas\n var canvas = document.createElement(\"canvas\");\n canvas.width = default_layer.width;\n canvas.height = default_layer.height;\n\n var context = canvas.getContext(\"2d\");\n\n // Returns sorted array of children\n function get_children(layer) {\n\n // Build array of children\n var children = [];\n for (var index in layer.children)\n children.push(layer.children[index]);\n\n // Sort\n children.sort(function children_comparator(a, b) {\n\n // Compare based on Z order\n var diff = a.z - b.z;\n if (diff !== 0)\n return diff;\n\n // If Z order identical, use document order\n var a_element = a.getElement();\n var b_element = b.getElement();\n var position = b_element.compareDocumentPosition(a_element);\n\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return -1;\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return 1;\n\n // Otherwise, assume same\n return 0;\n\n });\n\n // Done\n return children;\n\n }\n\n // Draws the contents of the given layer at the given coordinates\n function draw_layer(layer, x, y) {\n\n // Draw layer\n if (layer.width > 0 && layer.height > 0) {\n\n // Save and update alpha\n var initial_alpha = context.globalAlpha;\n context.globalAlpha *= layer.alpha / 255.0;\n\n // Copy data\n context.drawImage(layer.getCanvas(), x, y);\n\n // Draw all children\n var children = get_children(layer);\n for (var i=0; i 0)\n return pool.shift();\n\n // Otherwise, return a new integer\n return guac_pool.next_int++;\n\n };\n\n /**\n * Frees the given integer, allowing it to be reused.\n *\n * @param {Number} integer The integer to free.\n */\n this.free = function(integer) {\n pool.push(integer);\n };\n\n};\n\n\n/**\n * A reader which automatically handles the given input stream, assembling all\n * received blobs into a JavaScript object by appending them to each other, in\n * order, and decoding the result as JSON. Note that this object will overwrite\n * any installed event handlers on the given Guacamole.InputStream.\n *\n * @constructor\n * @param {Guacamole.InputStream} stream\n * The stream that JSON will be read from.\n */\nGuacamole.JSONReader = function guacamoleJSONReader(stream) {\n\n /**\n * Reference to this Guacamole.JSONReader.\n *\n * @private\n * @type {Guacamole.JSONReader}\n */\n var guacReader = this;\n\n /**\n * Wrapped Guacamole.StringReader.\n *\n * @private\n * @type {Guacamole.StringReader}\n */\n var stringReader = new Guacamole.StringReader(stream);\n\n /**\n * All JSON read thus far.\n *\n * @private\n * @type {String}\n */\n var json = '';\n\n /**\n * Returns the current length of this Guacamole.JSONReader, in characters.\n *\n * @return {Number}\n * The current length of this Guacamole.JSONReader.\n */\n this.getLength = function getLength() {\n return json.length;\n };\n\n /**\n * Returns the contents of this Guacamole.JSONReader as a JavaScript\n * object.\n *\n * @return {Object}\n * The contents of this Guacamole.JSONReader, as parsed from the JSON\n * contents of the input stream.\n */\n this.getJSON = function getJSON() {\n return JSON.parse(json);\n };\n\n // Append all received text\n stringReader.ontext = function ontext(text) {\n\n // Append received text\n json += text;\n\n // Call handler, if present\n if (guacReader.onprogress)\n guacReader.onprogress(text.length);\n\n };\n\n // Simply call onend when end received\n stringReader.onend = function onend() {\n if (guacReader.onend)\n guacReader.onend();\n };\n\n /**\n * Fired once for every blob of data received.\n *\n * @event\n * @param {Number} length\n * The number of characters received.\n */\n this.onprogress = null;\n\n /**\n * Fired once this stream is finished and no further data will be written.\n *\n * @event\n */\n this.onend = null;\n\n};\n\n\n/**\n * Provides cross-browser and cross-keyboard keyboard for a specific element.\n * Browser and keyboard layout variation is abstracted away, providing events\n * which represent keys as their corresponding X11 keysym.\n *\n * @constructor\n * @param {Element} element The Element to use to provide keyboard events.\n */\nGuacamole.Keyboard = function(element) {\n\n /**\n * Reference to this Guacamole.Keyboard.\n * @private\n */\n var guac_keyboard = this;\n\n /**\n * Fired whenever the user presses a key with the element associated\n * with this Guacamole.Keyboard in focus.\n *\n * @event\n * @param {Number} keysym The keysym of the key being pressed.\n * @return {Boolean} true if the key event should be allowed through to the\n * browser, false otherwise.\n */\n this.onkeydown = null;\n\n /**\n * Fired whenever the user releases a key with the element associated\n * with this Guacamole.Keyboard in focus.\n *\n * @event\n * @param {Number} keysym The keysym of the key being released.\n */\n this.onkeyup = null;\n\n /**\n * A key event having a corresponding timestamp. This event is non-specific.\n * Its subclasses should be used instead when recording specific key\n * events.\n *\n * @private\n * @constructor\n */\n var KeyEvent = function() {\n\n /**\n * Reference to this key event.\n */\n var key_event = this;\n\n /**\n * An arbitrary timestamp in milliseconds, indicating this event's\n * position in time relative to other events.\n *\n * @type {Number}\n */\n this.timestamp = new Date().getTime();\n\n /**\n * Whether the default action of this key event should be prevented.\n *\n * @type {Boolean}\n */\n this.defaultPrevented = false;\n\n /**\n * The keysym of the key associated with this key event, as determined\n * by a best-effort guess using available event properties and keyboard\n * state.\n *\n * @type {Number}\n */\n this.keysym = null;\n\n /**\n * Whether the keysym value of this key event is known to be reliable.\n * If false, the keysym may still be valid, but it's only a best guess,\n * and future key events may be a better source of information.\n *\n * @type {Boolean}\n */\n this.reliable = false;\n\n /**\n * Returns the number of milliseconds elapsed since this event was\n * received.\n *\n * @return {Number} The number of milliseconds elapsed since this\n * event was received.\n */\n this.getAge = function() {\n return new Date().getTime() - key_event.timestamp;\n };\n\n };\n\n /**\n * Information related to the pressing of a key, which need not be a key\n * associated with a printable character. The presence or absence of any\n * information within this object is browser-dependent.\n *\n * @private\n * @constructor\n * @augments Guacamole.Keyboard.KeyEvent\n * @param {Number} keyCode The JavaScript key code of the key pressed.\n * @param {String} keyIdentifier The legacy DOM3 \"keyIdentifier\" of the key\n * pressed, as defined at:\n * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent\n * @param {String} key The standard name of the key pressed, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n * @param {Number} location The location on the keyboard corresponding to\n * the key pressed, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n */\n var KeydownEvent = function(keyCode, keyIdentifier, key, location) {\n\n // We extend KeyEvent\n KeyEvent.apply(this);\n\n /**\n * The JavaScript key code of the key pressed.\n *\n * @type {Number}\n */\n this.keyCode = keyCode;\n\n /**\n * The legacy DOM3 \"keyIdentifier\" of the key pressed, as defined at:\n * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent\n *\n * @type {String}\n */\n this.keyIdentifier = keyIdentifier;\n\n /**\n * The standard name of the key pressed, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n *\n * @type {String}\n */\n this.key = key;\n\n /**\n * The location on the keyboard corresponding to the key pressed, as\n * defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n *\n * @type {Number}\n */\n this.location = location;\n\n // If key is known from keyCode or DOM3 alone, use that\n this.keysym = keysym_from_key_identifier(key, location)\n || keysym_from_keycode(keyCode, location);\n\n // DOM3 and keyCode are reliable sources if the corresponding key is\n // not a printable key\n if (this.keysym && !isPrintable(this.keysym))\n this.reliable = true;\n\n // Use legacy keyIdentifier as a last resort, if it looks sane\n if (!this.keysym && key_identifier_sane(keyCode, keyIdentifier))\n this.keysym = keysym_from_key_identifier(keyIdentifier, location, guac_keyboard.modifiers.shift);\n\n // Determine whether default action for Alt+combinations must be prevented\n var prevent_alt = !guac_keyboard.modifiers.ctrl\n && !(navigator && navigator.platform && navigator.platform.match(/^mac/i));\n\n // Determine whether default action for Ctrl+combinations must be prevented\n var prevent_ctrl = !guac_keyboard.modifiers.alt;\n\n // We must rely on the (potentially buggy) keyIdentifier if preventing\n // the default action is important\n if ((prevent_ctrl && guac_keyboard.modifiers.ctrl)\n || (prevent_alt && guac_keyboard.modifiers.alt)\n || guac_keyboard.modifiers.meta\n || guac_keyboard.modifiers.hyper)\n this.reliable = true;\n\n // Record most recently known keysym by associated key code\n recentKeysym[keyCode] = this.keysym;\n\n };\n\n KeydownEvent.prototype = new KeyEvent();\n\n /**\n * Information related to the pressing of a key, which MUST be\n * associated with a printable character. The presence or absence of any\n * information within this object is browser-dependent.\n *\n * @private\n * @constructor\n * @augments Guacamole.Keyboard.KeyEvent\n * @param {Number} charCode The Unicode codepoint of the character that\n * would be typed by the key pressed.\n */\n var KeypressEvent = function(charCode) {\n\n // We extend KeyEvent\n KeyEvent.apply(this);\n\n /**\n * The Unicode codepoint of the character that would be typed by the\n * key pressed.\n *\n * @type {Number}\n */\n this.charCode = charCode;\n\n // Pull keysym from char code\n this.keysym = keysym_from_charcode(charCode);\n\n // Keypress is always reliable\n this.reliable = true;\n\n };\n\n KeypressEvent.prototype = new KeyEvent();\n\n /**\n * Information related to the pressing of a key, which need not be a key\n * associated with a printable character. The presence or absence of any\n * information within this object is browser-dependent.\n *\n * @private\n * @constructor\n * @augments Guacamole.Keyboard.KeyEvent\n * @param {Number} keyCode The JavaScript key code of the key released.\n * @param {String} keyIdentifier The legacy DOM3 \"keyIdentifier\" of the key\n * released, as defined at:\n * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent\n * @param {String} key The standard name of the key released, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n * @param {Number} location The location on the keyboard corresponding to\n * the key released, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n */\n var KeyupEvent = function(keyCode, keyIdentifier, key, location) {\n\n // We extend KeyEvent\n KeyEvent.apply(this);\n\n /**\n * The JavaScript key code of the key released.\n *\n * @type {Number}\n */\n this.keyCode = keyCode;\n\n /**\n * The legacy DOM3 \"keyIdentifier\" of the key released, as defined at:\n * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent\n *\n * @type {String}\n */\n this.keyIdentifier = keyIdentifier;\n\n /**\n * The standard name of the key released, as defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n *\n * @type {String}\n */\n this.key = key;\n\n /**\n * The location on the keyboard corresponding to the key released, as\n * defined at:\n * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n *\n * @type {Number}\n */\n this.location = location;\n\n // If key is known from keyCode or DOM3 alone, use that\n this.keysym = recentKeysym[keyCode]\n || keysym_from_keycode(keyCode, location)\n || keysym_from_key_identifier(key, location); // keyCode is still more reliable for keyup when dead keys are in use\n\n // Keyup is as reliable as it will ever be\n this.reliable = true;\n\n };\n\n KeyupEvent.prototype = new KeyEvent();\n\n /**\n * An array of recorded events, which can be instances of the private\n * KeydownEvent, KeypressEvent, and KeyupEvent classes.\n *\n * @private\n * @type {KeyEvent[]}\n */\n var eventLog = [];\n\n /**\n * Map of known JavaScript keycodes which do not map to typable characters\n * to their X11 keysym equivalents.\n * @private\n */\n var keycodeKeysyms = {\n 8: [0xFF08], // backspace\n 9: [0xFF09], // tab\n 12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5\n 13: [0xFF0D], // enter\n 16: [0xFFE1, 0xFFE1, 0xFFE2], // shift\n 17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl\n 18: [0xFFE9, 0xFFE9, 0xFE03], // alt\n 19: [0xFF13], // pause/break\n 20: [0xFFE5], // caps lock\n 27: [0xFF1B], // escape\n 32: [0x0020], // space\n 33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9\n 34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3\n 35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1\n 36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7\n 37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4\n 38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8\n 39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6\n 40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2\n 45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0\n 46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal\n 91: [0xFFEB], // left window key (hyper_l)\n 92: [0xFF67], // right window key (menu key?)\n 93: null, // select key\n 96: [0xFFB0], // KP 0\n 97: [0xFFB1], // KP 1\n 98: [0xFFB2], // KP 2\n 99: [0xFFB3], // KP 3\n 100: [0xFFB4], // KP 4\n 101: [0xFFB5], // KP 5\n 102: [0xFFB6], // KP 6\n 103: [0xFFB7], // KP 7\n 104: [0xFFB8], // KP 8\n 105: [0xFFB9], // KP 9\n 106: [0xFFAA], // KP multiply\n 107: [0xFFAB], // KP add\n 109: [0xFFAD], // KP subtract\n 110: [0xFFAE], // KP decimal\n 111: [0xFFAF], // KP divide\n 112: [0xFFBE], // f1\n 113: [0xFFBF], // f2\n 114: [0xFFC0], // f3\n 115: [0xFFC1], // f4\n 116: [0xFFC2], // f5\n 117: [0xFFC3], // f6\n 118: [0xFFC4], // f7\n 119: [0xFFC5], // f8\n 120: [0xFFC6], // f9\n 121: [0xFFC7], // f10\n 122: [0xFFC8], // f11\n 123: [0xFFC9], // f12\n 144: [0xFF7F], // num lock\n 145: [0xFF14], // scroll lock\n 225: [0xFE03] // altgraph (iso_level3_shift)\n };\n\n /**\n * Map of known JavaScript keyidentifiers which do not map to typable\n * characters to their unshifted X11 keysym equivalents.\n * @private\n */\n var keyidentifier_keysym = {\n \"Again\": [0xFF66],\n \"AllCandidates\": [0xFF3D],\n \"Alphanumeric\": [0xFF30],\n \"Alt\": [0xFFE9, 0xFFE9, 0xFE03],\n \"Attn\": [0xFD0E],\n \"AltGraph\": [0xFE03],\n \"ArrowDown\": [0xFF54],\n \"ArrowLeft\": [0xFF51],\n \"ArrowRight\": [0xFF53],\n \"ArrowUp\": [0xFF52],\n \"Backspace\": [0xFF08],\n \"CapsLock\": [0xFFE5],\n \"Cancel\": [0xFF69],\n \"Clear\": [0xFF0B],\n \"Convert\": [0xFF21],\n \"Copy\": [0xFD15],\n \"Crsel\": [0xFD1C],\n \"CrSel\": [0xFD1C],\n \"CodeInput\": [0xFF37],\n \"Compose\": [0xFF20],\n \"Control\": [0xFFE3, 0xFFE3, 0xFFE4],\n \"ContextMenu\": [0xFF67],\n \"DeadGrave\": [0xFE50],\n \"DeadAcute\": [0xFE51],\n \"DeadCircumflex\": [0xFE52],\n \"DeadTilde\": [0xFE53],\n \"DeadMacron\": [0xFE54],\n \"DeadBreve\": [0xFE55],\n \"DeadAboveDot\": [0xFE56],\n \"DeadUmlaut\": [0xFE57],\n \"DeadAboveRing\": [0xFE58],\n \"DeadDoubleacute\": [0xFE59],\n \"DeadCaron\": [0xFE5A],\n \"DeadCedilla\": [0xFE5B],\n \"DeadOgonek\": [0xFE5C],\n \"DeadIota\": [0xFE5D],\n \"DeadVoicedSound\": [0xFE5E],\n \"DeadSemivoicedSound\": [0xFE5F],\n \"Delete\": [0xFFFF],\n \"Down\": [0xFF54],\n \"End\": [0xFF57],\n \"Enter\": [0xFF0D],\n \"EraseEof\": [0xFD06],\n \"Escape\": [0xFF1B],\n \"Execute\": [0xFF62],\n \"Exsel\": [0xFD1D],\n \"ExSel\": [0xFD1D],\n \"F1\": [0xFFBE],\n \"F2\": [0xFFBF],\n \"F3\": [0xFFC0],\n \"F4\": [0xFFC1],\n \"F5\": [0xFFC2],\n \"F6\": [0xFFC3],\n \"F7\": [0xFFC4],\n \"F8\": [0xFFC5],\n \"F9\": [0xFFC6],\n \"F10\": [0xFFC7],\n \"F11\": [0xFFC8],\n \"F12\": [0xFFC9],\n \"F13\": [0xFFCA],\n \"F14\": [0xFFCB],\n \"F15\": [0xFFCC],\n \"F16\": [0xFFCD],\n \"F17\": [0xFFCE],\n \"F18\": [0xFFCF],\n \"F19\": [0xFFD0],\n \"F20\": [0xFFD1],\n \"F21\": [0xFFD2],\n \"F22\": [0xFFD3],\n \"F23\": [0xFFD4],\n \"F24\": [0xFFD5],\n \"Find\": [0xFF68],\n \"GroupFirst\": [0xFE0C],\n \"GroupLast\": [0xFE0E],\n \"GroupNext\": [0xFE08],\n \"GroupPrevious\": [0xFE0A],\n \"FullWidth\": null,\n \"HalfWidth\": null,\n \"HangulMode\": [0xFF31],\n \"Hankaku\": [0xFF29],\n \"HanjaMode\": [0xFF34],\n \"Help\": [0xFF6A],\n \"Hiragana\": [0xFF25],\n \"HiraganaKatakana\": [0xFF27],\n \"Home\": [0xFF50],\n \"Hyper\": [0xFFED, 0xFFED, 0xFFEE],\n \"Insert\": [0xFF63],\n \"JapaneseHiragana\": [0xFF25],\n \"JapaneseKatakana\": [0xFF26],\n \"JapaneseRomaji\": [0xFF24],\n \"JunjaMode\": [0xFF38],\n \"KanaMode\": [0xFF2D],\n \"KanjiMode\": [0xFF21],\n \"Katakana\": [0xFF26],\n \"Left\": [0xFF51],\n \"Meta\": [0xFFE7, 0xFFE7, 0xFFE8],\n \"ModeChange\": [0xFF7E],\n \"NumLock\": [0xFF7F],\n \"PageDown\": [0xFF56],\n \"PageUp\": [0xFF55],\n \"Pause\": [0xFF13],\n \"Play\": [0xFD16],\n \"PreviousCandidate\": [0xFF3E],\n \"PrintScreen\": [0xFD1D],\n \"Redo\": [0xFF66],\n \"Right\": [0xFF53],\n \"RomanCharacters\": null,\n \"Scroll\": [0xFF14],\n \"Select\": [0xFF60],\n \"Separator\": [0xFFAC],\n \"Shift\": [0xFFE1, 0xFFE1, 0xFFE2],\n \"SingleCandidate\": [0xFF3C],\n \"Super\": [0xFFEB, 0xFFEB, 0xFFEC],\n \"Tab\": [0xFF09],\n \"Up\": [0xFF52],\n \"Undo\": [0xFF65],\n \"Win\": [0xFFEB],\n \"Zenkaku\": [0xFF28],\n \"ZenkakuHankaku\": [0xFF2A]\n };\n\n /**\n * All keysyms which should not repeat when held down.\n * @private\n */\n var no_repeat = {\n 0xFE03: true, // ISO Level 3 Shift (AltGr)\n 0xFFE1: true, // Left shift\n 0xFFE2: true, // Right shift\n 0xFFE3: true, // Left ctrl\n 0xFFE4: true, // Right ctrl\n 0xFFE7: true, // Left meta\n 0xFFE8: true, // Right meta\n 0xFFE9: true, // Left alt\n 0xFFEA: true, // Right alt\n 0xFFEB: true, // Left hyper\n 0xFFEC: true // Right hyper\n };\n\n /**\n * All modifiers and their states.\n */\n this.modifiers = new Guacamole.Keyboard.ModifierState();\n\n /**\n * The state of every key, indexed by keysym. If a particular key is\n * pressed, the value of pressed for that keysym will be true. If a key\n * is not currently pressed, it will not be defined.\n */\n this.pressed = {};\n\n /**\n * The last result of calling the onkeydown handler for each key, indexed\n * by keysym. This is used to prevent/allow default actions for key events,\n * even when the onkeydown handler cannot be called again because the key\n * is (theoretically) still pressed.\n *\n * @private\n */\n var last_keydown_result = {};\n\n /**\n * The keysym most recently associated with a given keycode when keydown\n * fired. This object maps keycodes to keysyms.\n *\n * @private\n * @type {Object.}\n */\n var recentKeysym = {};\n\n /**\n * Timeout before key repeat starts.\n * @private\n */\n var key_repeat_timeout = null;\n\n /**\n * Interval which presses and releases the last key pressed while that\n * key is still being held down.\n * @private\n */\n var key_repeat_interval = null;\n\n /**\n * Given an array of keysyms indexed by location, returns the keysym\n * for the given location, or the keysym for the standard location if\n * undefined.\n *\n * @private\n * @param {Number[]} keysyms\n * An array of keysyms, where the index of the keysym in the array is\n * the location value.\n *\n * @param {Number} location\n * The location on the keyboard corresponding to the key pressed, as\n * defined at: http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent\n */\n var get_keysym = function get_keysym(keysyms, location) {\n\n if (!keysyms)\n return null;\n\n return keysyms[location] || keysyms[0];\n };\n\n /**\n * Returns true if the given keysym corresponds to a printable character,\n * false otherwise.\n *\n * @param {Number} keysym\n * The keysym to check.\n *\n * @returns {Boolean}\n * true if the given keysym corresponds to a printable character,\n * false otherwise.\n */\n var isPrintable = function isPrintable(keysym) {\n\n // Keysyms with Unicode equivalents are printable\n return (keysym >= 0x00 && keysym <= 0xFF)\n || (keysym & 0xFFFF0000) === 0x01000000;\n\n };\n\n function keysym_from_key_identifier(identifier, location, shifted) {\n\n if (!identifier)\n return null;\n\n var typedCharacter;\n\n // If identifier is U+xxxx, decode Unicode character\n var unicodePrefixLocation = identifier.indexOf(\"U+\");\n if (unicodePrefixLocation >= 0) {\n var hex = identifier.substring(unicodePrefixLocation+2);\n typedCharacter = String.fromCharCode(parseInt(hex, 16));\n }\n\n // If single character and not keypad, use that as typed character\n else if (identifier.length === 1 && location !== 3)\n typedCharacter = identifier;\n\n // Otherwise, look up corresponding keysym\n else\n return get_keysym(keyidentifier_keysym[identifier], location);\n\n // Alter case if necessary\n if (shifted === true)\n typedCharacter = typedCharacter.toUpperCase();\n else if (shifted === false)\n typedCharacter = typedCharacter.toLowerCase();\n\n // Get codepoint\n var codepoint = typedCharacter.charCodeAt(0);\n return keysym_from_charcode(codepoint);\n\n }\n\n function isControlCharacter(codepoint) {\n return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);\n }\n\n function keysym_from_charcode(codepoint) {\n\n // Keysyms for control characters\n if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;\n\n // Keysyms for ASCII chars\n if (codepoint >= 0x0000 && codepoint <= 0x00FF)\n return codepoint;\n\n // Keysyms for Unicode\n if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)\n return 0x01000000 | codepoint;\n\n return null;\n\n }\n\n function keysym_from_keycode(keyCode, location) {\n return get_keysym(keycodeKeysyms[keyCode], location);\n }\n\n /**\n * Heuristically detects if the legacy keyIdentifier property of\n * a keydown/keyup event looks incorrectly derived. Chrome, and\n * presumably others, will produce the keyIdentifier by assuming\n * the keyCode is the Unicode codepoint for that key. This is not\n * correct in all cases.\n *\n * @private\n * @param {Number} keyCode\n * The keyCode from a browser keydown/keyup event.\n *\n * @param {String} keyIdentifier\n * The legacy keyIdentifier from a browser keydown/keyup event.\n *\n * @returns {Boolean}\n * true if the keyIdentifier looks sane, false if the keyIdentifier\n * appears incorrectly derived or is missing entirely.\n */\n var key_identifier_sane = function key_identifier_sane(keyCode, keyIdentifier) {\n\n // Missing identifier is not sane\n if (!keyIdentifier)\n return false;\n\n // Assume non-Unicode keyIdentifier values are sane\n var unicodePrefixLocation = keyIdentifier.indexOf(\"U+\");\n if (unicodePrefixLocation === -1)\n return true;\n\n // If the Unicode codepoint isn't identical to the keyCode,\n // then the identifier is likely correct\n var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16);\n if (keyCode !== codepoint)\n return true;\n\n // The keyCodes for A-Z and 0-9 are actually identical to their\n // Unicode codepoints\n if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57))\n return true;\n\n // The keyIdentifier does NOT appear sane\n return false;\n\n };\n\n /**\n * Marks a key as pressed, firing the keydown event if registered. Key\n * repeat for the pressed key will start after a delay if that key is\n * not a modifier. The return value of this function depends on the\n * return value of the keydown event handler, if any.\n *\n * @param {Number} keysym The keysym of the key to press.\n * @return {Boolean} true if event should NOT be canceled, false otherwise.\n */\n this.press = function(keysym) {\n\n // Don't bother with pressing the key if the key is unknown\n if (keysym === null) return;\n\n // Only press if released\n if (!guac_keyboard.pressed[keysym]) {\n\n // Mark key as pressed\n guac_keyboard.pressed[keysym] = true;\n\n // Send key event\n if (guac_keyboard.onkeydown) {\n var result = guac_keyboard.onkeydown(keysym);\n last_keydown_result[keysym] = result;\n\n // Stop any current repeat\n window.clearTimeout(key_repeat_timeout);\n window.clearInterval(key_repeat_interval);\n\n // Repeat after a delay as long as pressed\n if (!no_repeat[keysym])\n key_repeat_timeout = window.setTimeout(function() {\n key_repeat_interval = window.setInterval(function() {\n guac_keyboard.onkeyup(keysym);\n guac_keyboard.onkeydown(keysym);\n }, 50);\n }, 500);\n\n return result;\n }\n }\n\n // Return the last keydown result by default, resort to false if unknown\n return last_keydown_result[keysym] || false;\n\n };\n\n /**\n * Marks a key as released, firing the keyup event if registered.\n *\n * @param {Number} keysym The keysym of the key to release.\n */\n this.release = function(keysym) {\n\n // Only release if pressed\n if (guac_keyboard.pressed[keysym]) {\n\n // Mark key as released\n delete guac_keyboard.pressed[keysym];\n\n // Stop repeat\n window.clearTimeout(key_repeat_timeout);\n window.clearInterval(key_repeat_interval);\n\n // Send key event\n if (keysym !== null && guac_keyboard.onkeyup)\n guac_keyboard.onkeyup(keysym);\n\n }\n\n };\n\n /**\n * Resets the state of this keyboard, releasing all keys, and firing keyup\n * events for each released key.\n */\n this.reset = function() {\n\n // Release all pressed keys\n for (var keysym in guac_keyboard.pressed)\n guac_keyboard.release(parseInt(keysym));\n\n // Clear event log\n eventLog = [];\n\n };\n\n /**\n * Given a keyboard event, updates the local modifier state and remote\n * key state based on the modifier flags within the event. This function\n * pays no attention to keycodes.\n *\n * @private\n * @param {KeyboardEvent} e\n * The keyboard event containing the flags to update.\n */\n var update_modifier_state = function update_modifier_state(e) {\n\n // Get state\n var state = Guacamole.Keyboard.ModifierState.fromKeyboardEvent(e);\n\n // Release alt if implicitly released\n if (guac_keyboard.modifiers.alt && state.alt === false) {\n guac_keyboard.release(0xFFE9); // Left alt\n guac_keyboard.release(0xFFEA); // Right alt\n guac_keyboard.release(0xFE03); // AltGr\n }\n\n // Release shift if implicitly released\n if (guac_keyboard.modifiers.shift && state.shift === false) {\n guac_keyboard.release(0xFFE1); // Left shift\n guac_keyboard.release(0xFFE2); // Right shift\n }\n\n // Release ctrl if implicitly released\n if (guac_keyboard.modifiers.ctrl && state.ctrl === false) {\n guac_keyboard.release(0xFFE3); // Left ctrl\n guac_keyboard.release(0xFFE4); // Right ctrl\n }\n\n // Release meta if implicitly released\n if (guac_keyboard.modifiers.meta && state.meta === false) {\n guac_keyboard.release(0xFFE7); // Left meta\n guac_keyboard.release(0xFFE8); // Right meta\n }\n\n // Release hyper if implicitly released\n if (guac_keyboard.modifiers.hyper && state.hyper === false) {\n guac_keyboard.release(0xFFEB); // Left hyper\n guac_keyboard.release(0xFFEC); // Right hyper\n }\n\n // Update state\n guac_keyboard.modifiers = state;\n\n };\n\n /**\n * Reads through the event log, removing events from the head of the log\n * when the corresponding true key presses are known (or as known as they\n * can be).\n *\n * @private\n * @return {Boolean} Whether the default action of the latest event should\n * be prevented.\n */\n function interpret_events() {\n\n // Do not prevent default if no event could be interpreted\n var handled_event = interpret_event();\n if (!handled_event)\n return false;\n\n // Interpret as much as possible\n var last_event;\n do {\n last_event = handled_event;\n handled_event = interpret_event();\n } while (handled_event !== null);\n\n return last_event.defaultPrevented;\n\n }\n\n /**\n * Releases Ctrl+Alt, if both are currently pressed and the given keysym\n * looks like a key that may require AltGr.\n *\n * @private\n * @param {Number} keysym The key that was just pressed.\n */\n var release_simulated_altgr = function release_simulated_altgr(keysym) {\n\n // Both Ctrl+Alt must be pressed if simulated AltGr is in use\n if (!guac_keyboard.modifiers.ctrl || !guac_keyboard.modifiers.alt)\n return;\n\n // Assume [A-Z] never require AltGr\n if (keysym >= 0x0041 && keysym <= 0x005A)\n return;\n\n // Assume [a-z] never require AltGr\n if (keysym >= 0x0061 && keysym <= 0x007A)\n return;\n\n // Release Ctrl+Alt if the keysym is printable\n if (keysym <= 0xFF || (keysym & 0xFF000000) === 0x01000000) {\n guac_keyboard.release(0xFFE3); // Left ctrl\n guac_keyboard.release(0xFFE4); // Right ctrl\n guac_keyboard.release(0xFFE9); // Left alt\n guac_keyboard.release(0xFFEA); // Right alt\n }\n\n };\n\n /**\n * Reads through the event log, interpreting the first event, if possible,\n * and returning that event. If no events can be interpreted, due to a\n * total lack of events or the need for more events, null is returned. Any\n * interpreted events are automatically removed from the log.\n *\n * @private\n * @return {KeyEvent}\n * The first key event in the log, if it can be interpreted, or null\n * otherwise.\n */\n var interpret_event = function interpret_event() {\n\n // Peek at first event in log\n var first = eventLog[0];\n if (!first)\n return null;\n\n // Keydown event\n if (first instanceof KeydownEvent) {\n\n var keysym = null;\n var accepted_events = [];\n\n // If event itself is reliable, no need to wait for other events\n if (first.reliable) {\n keysym = first.keysym;\n accepted_events = eventLog.splice(0, 1);\n }\n\n // If keydown is immediately followed by a keypress, use the indicated character\n else if (eventLog[1] instanceof KeypressEvent) {\n keysym = eventLog[1].keysym;\n accepted_events = eventLog.splice(0, 2);\n }\n\n // If keydown is immediately followed by anything else, then no\n // keypress can possibly occur to clarify this event, and we must\n // handle it now\n else if (eventLog[1]) {\n keysym = first.keysym;\n accepted_events = eventLog.splice(0, 1);\n }\n\n // Fire a key press if valid events were found\n if (accepted_events.length > 0) {\n\n if (keysym) {\n\n // Fire event\n release_simulated_altgr(keysym);\n var defaultPrevented = !guac_keyboard.press(keysym);\n recentKeysym[first.keyCode] = keysym;\n\n // If a key is pressed while meta is held down, the keyup will\n // never be sent in Chrome, so send it now. (bug #108404)\n if (guac_keyboard.modifiers.meta && keysym !== 0xFFE7 && keysym !== 0xFFE8)\n guac_keyboard.release(keysym);\n\n // Record whether default was prevented\n for (var i=0; i layer.width)\n resizeWidth = opBoundX;\n else\n resizeWidth = layer.width;\n\n // Determine max height\n var resizeHeight;\n if (opBoundY > layer.height)\n resizeHeight = opBoundY;\n else\n resizeHeight = layer.height;\n\n // Resize if necessary\n layer.resize(resizeWidth, resizeHeight);\n\n }\n\n /**\n * Set to true if this Layer should resize itself to accomodate the\n * dimensions of any drawing operation, and false (the default) otherwise.\n *\n * Note that setting this property takes effect immediately, and thus may\n * take effect on operations that were started in the past but have not\n * yet completed. If you wish the setting of this flag to only modify\n * future operations, you will need to make the setting of this flag an\n * operation with sync().\n *\n * @example\n * // Set autosize to true for all future operations\n * layer.sync(function() {\n * layer.autosize = true;\n * });\n *\n * @type {Boolean}\n * @default false\n */\n this.autosize = false;\n\n /**\n * The current width of this layer.\n * @type {Number}\n */\n this.width = width;\n\n /**\n * The current height of this layer.\n * @type {Number}\n */\n this.height = height;\n\n /**\n * Returns the canvas element backing this Layer. Note that the dimensions\n * of the canvas may not exactly match those of the Layer, as resizing a\n * canvas while maintaining its state is an expensive operation.\n *\n * @returns {HTMLCanvasElement}\n * The canvas element backing this Layer.\n */\n this.getCanvas = function getCanvas() {\n return canvas;\n };\n\n /**\n * Returns a new canvas element containing the same image as this Layer.\n * Unlike getCanvas(), the canvas element returned is guaranteed to have\n * the exact same dimensions as the Layer.\n *\n * @returns {HTMLCanvasElement}\n * A new canvas element containing a copy of the image content this\n * Layer.\n */\n this.toCanvas = function toCanvas() {\n\n // Create new canvas having same dimensions\n var canvas = document.createElement('canvas');\n canvas.width = layer.width;\n canvas.height = layer.height;\n\n // Copy image contents to new canvas\n var context = canvas.getContext('2d');\n context.drawImage(layer.getCanvas(), 0, 0);\n\n return canvas;\n\n };\n\n /**\n * Changes the size of this Layer to the given width and height. Resizing\n * is only attempted if the new size provided is actually different from\n * the current size.\n *\n * @param {Number} newWidth The new width to assign to this Layer.\n * @param {Number} newHeight The new height to assign to this Layer.\n */\n this.resize = function(newWidth, newHeight) {\n if (newWidth !== layer.width || newHeight !== layer.height)\n resize(newWidth, newHeight);\n };\n\n /**\n * Draws the specified image at the given coordinates. The image specified\n * must already be loaded.\n *\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n * @param {Image} image The image to draw. Note that this is an Image\n * object - not a URL.\n */\n this.drawImage = function(x, y, image) {\n if (layer.autosize) fitRect(x, y, image.width, image.height);\n context.drawImage(image, x, y);\n empty = false;\n };\n\n /**\n * Transfer a rectangle of image data from one Layer to this Layer using the\n * specified transfer function.\n *\n * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source Layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * Layer's coordinate space to copy data from.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n * @param {Function} transferFunction The transfer function to use to\n * transfer data from source to\n * destination.\n */\n this.transfer = function(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction) {\n\n var srcCanvas = srcLayer.getCanvas();\n\n // If entire rectangle outside source canvas, stop\n if (srcx >= srcCanvas.width || srcy >= srcCanvas.height) return;\n\n // Otherwise, clip rectangle to area\n if (srcx + srcw > srcCanvas.width)\n srcw = srcCanvas.width - srcx;\n\n if (srcy + srch > srcCanvas.height)\n srch = srcCanvas.height - srcy;\n\n // Stop if nothing to draw.\n if (srcw === 0 || srch === 0) return;\n\n if (layer.autosize) fitRect(x, y, srcw, srch);\n\n // Get image data from src and dst\n var src = srcLayer.getCanvas().getContext(\"2d\").getImageData(srcx, srcy, srcw, srch);\n var dst = context.getImageData(x , y, srcw, srch);\n\n // Apply transfer for each pixel\n for (var i=0; i= srcCanvas.width || srcy >= srcCanvas.height) return;\n\n // Otherwise, clip rectangle to area\n if (srcx + srcw > srcCanvas.width)\n srcw = srcCanvas.width - srcx;\n\n if (srcy + srch > srcCanvas.height)\n srch = srcCanvas.height - srcy;\n\n // Stop if nothing to draw.\n if (srcw === 0 || srch === 0) return;\n\n if (layer.autosize) fitRect(x, y, srcw, srch);\n\n // Get image data from src and dst\n var src = srcLayer.getCanvas().getContext(\"2d\").getImageData(srcx, srcy, srcw, srch);\n context.putImageData(src, x, y);\n empty = false;\n\n };\n\n /**\n * Copy a rectangle of image data from one Layer to this Layer. This\n * operation will copy exactly the image data that will be drawn once all\n * operations of the source Layer that were pending at the time this\n * function was called are complete. This operation will not alter the\n * size of the source Layer even if its autosize property is set to true.\n *\n * @param {Guacamole.Layer} srcLayer The Layer to copy image data from.\n * @param {Number} srcx The X coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcy The Y coordinate of the upper-left corner of the\n * rectangle within the source Layer's coordinate\n * space to copy data from.\n * @param {Number} srcw The width of the rectangle within the source Layer's\n * coordinate space to copy data from.\n * @param {Number} srch The height of the rectangle within the source\n * Layer's coordinate space to copy data from.\n * @param {Number} x The destination X coordinate.\n * @param {Number} y The destination Y coordinate.\n */\n this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) {\n\n var srcCanvas = srcLayer.getCanvas();\n\n // If entire rectangle outside source canvas, stop\n if (srcx >= srcCanvas.width || srcy >= srcCanvas.height) return;\n\n // Otherwise, clip rectangle to area\n if (srcx + srcw > srcCanvas.width)\n srcw = srcCanvas.width - srcx;\n\n if (srcy + srch > srcCanvas.height)\n srch = srcCanvas.height - srcy;\n\n // Stop if nothing to draw.\n if (srcw === 0 || srch === 0) return;\n\n if (layer.autosize) fitRect(x, y, srcw, srch);\n context.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch);\n empty = false;\n\n };\n\n /**\n * Starts a new path at the specified point.\n *\n * @param {Number} x The X coordinate of the point to draw.\n * @param {Number} y The Y coordinate of the point to draw.\n */\n this.moveTo = function(x, y) {\n\n // Start a new path if current path is closed\n if (pathClosed) {\n context.beginPath();\n pathClosed = false;\n }\n\n if (layer.autosize) fitRect(x, y, 0, 0);\n context.moveTo(x, y);\n\n };\n\n /**\n * Add the specified line to the current path.\n *\n * @param {Number} x The X coordinate of the endpoint of the line to draw.\n * @param {Number} y The Y coordinate of the endpoint of the line to draw.\n */\n this.lineTo = function(x, y) {\n\n // Start a new path if current path is closed\n if (pathClosed) {\n context.beginPath();\n pathClosed = false;\n }\n\n if (layer.autosize) fitRect(x, y, 0, 0);\n context.lineTo(x, y);\n\n };\n\n /**\n * Add the specified arc to the current path.\n *\n * @param {Number} x The X coordinate of the center of the circle which\n * will contain the arc.\n * @param {Number} y The Y coordinate of the center of the circle which\n * will contain the arc.\n * @param {Number} radius The radius of the circle.\n * @param {Number} startAngle The starting angle of the arc, in radians.\n * @param {Number} endAngle The ending angle of the arc, in radians.\n * @param {Boolean} negative Whether the arc should be drawn in order of\n * decreasing angle.\n */\n this.arc = function(x, y, radius, startAngle, endAngle, negative) {\n\n // Start a new path if current path is closed\n if (pathClosed) {\n context.beginPath();\n pathClosed = false;\n }\n\n if (layer.autosize) fitRect(x, y, 0, 0);\n context.arc(x, y, radius, startAngle, endAngle, negative);\n\n };\n\n /**\n * Starts a new path at the specified point.\n *\n * @param {Number} cp1x The X coordinate of the first control point.\n * @param {Number} cp1y The Y coordinate of the first control point.\n * @param {Number} cp2x The X coordinate of the second control point.\n * @param {Number} cp2y The Y coordinate of the second control point.\n * @param {Number} x The X coordinate of the endpoint of the curve.\n * @param {Number} y The Y coordinate of the endpoint of the curve.\n */\n this.curveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {\n\n // Start a new path if current path is closed\n if (pathClosed) {\n context.beginPath();\n pathClosed = false;\n }\n\n if (layer.autosize) fitRect(x, y, 0, 0);\n context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n\n };\n\n /**\n * Closes the current path by connecting the end point with the start\n * point (if any) with a straight line.\n */\n this.close = function() {\n context.closePath();\n pathClosed = true;\n };\n\n /**\n * Add the specified rectangle to the current path.\n *\n * @param {Number} x The X coordinate of the upper-left corner of the\n * rectangle to draw.\n * @param {Number} y The Y coordinate of the upper-left corner of the\n * rectangle to draw.\n * @param {Number} w The width of the rectangle to draw.\n * @param {Number} h The height of the rectangle to draw.\n */\n this.rect = function(x, y, w, h) {\n\n // Start a new path if current path is closed\n if (pathClosed) {\n context.beginPath();\n pathClosed = false;\n }\n\n if (layer.autosize) fitRect(x, y, w, h);\n context.rect(x, y, w, h);\n\n };\n\n /**\n * Clip all future drawing operations by the current path. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as fillColor()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n */\n this.clip = function() {\n\n // Set new clipping region\n context.clip();\n\n // Path now implicitly closed\n pathClosed = true;\n\n };\n\n /**\n * Stroke the current path with the specified color. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {String} cap The line cap style. Can be \"round\", \"square\",\n * or \"butt\".\n * @param {String} join The line join style. Can be \"round\", \"bevel\",\n * or \"miter\".\n * @param {Number} thickness The line thickness in pixels.\n * @param {Number} r The red component of the color to fill.\n * @param {Number} g The green component of the color to fill.\n * @param {Number} b The blue component of the color to fill.\n * @param {Number} a The alpha component of the color to fill.\n */\n this.strokeColor = function(cap, join, thickness, r, g, b, a) {\n\n // Stroke with color\n context.lineCap = cap;\n context.lineJoin = join;\n context.lineWidth = thickness;\n context.strokeStyle = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + a/255.0 + \")\";\n context.stroke();\n empty = false;\n\n // Path now implicitly closed\n pathClosed = true;\n\n };\n\n /**\n * Fills the current path with the specified color. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Number} r The red component of the color to fill.\n * @param {Number} g The green component of the color to fill.\n * @param {Number} b The blue component of the color to fill.\n * @param {Number} a The alpha component of the color to fill.\n */\n this.fillColor = function(r, g, b, a) {\n\n // Fill with color\n context.fillStyle = \"rgba(\" + r + \",\" + g + \",\" + b + \",\" + a/255.0 + \")\";\n context.fill();\n empty = false;\n\n // Path now implicitly closed\n pathClosed = true;\n\n };\n\n /**\n * Stroke the current path with the image within the specified layer. The\n * image data will be tiled infinitely within the stroke. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {String} cap The line cap style. Can be \"round\", \"square\",\n * or \"butt\".\n * @param {String} join The line join style. Can be \"round\", \"bevel\",\n * or \"miter\".\n * @param {Number} thickness The line thickness in pixels.\n * @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern\n * within the stroke.\n */\n this.strokeLayer = function(cap, join, thickness, srcLayer) {\n\n // Stroke with image data\n context.lineCap = cap;\n context.lineJoin = join;\n context.lineWidth = thickness;\n context.strokeStyle = context.createPattern(\n srcLayer.getCanvas(),\n \"repeat\"\n );\n context.stroke();\n empty = false;\n\n // Path now implicitly closed\n pathClosed = true;\n\n };\n\n /**\n * Fills the current path with the image within the specified layer. The\n * image data will be tiled infinitely within the stroke. The current path\n * is implicitly closed. The current path can continue to be reused\n * for other operations (such as clip()) but a new path will be started\n * once a path drawing operation (path() or rect()) is used.\n *\n * @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern\n * within the fill.\n */\n this.fillLayer = function(srcLayer) {\n\n // Fill with image data\n context.fillStyle = context.createPattern(\n srcLayer.getCanvas(),\n \"repeat\"\n );\n context.fill();\n empty = false;\n\n // Path now implicitly closed\n pathClosed = true;\n\n };\n\n /**\n * Push current layer state onto stack.\n */\n this.push = function() {\n\n // Save current state onto stack\n context.save();\n stackSize++;\n\n };\n\n /**\n * Pop layer state off stack.\n */\n this.pop = function() {\n\n // Restore current state from stack\n if (stackSize > 0) {\n context.restore();\n stackSize--;\n }\n\n };\n\n /**\n * Reset the layer, clearing the stack, the current path, and any transform\n * matrix.\n */\n this.reset = function() {\n\n // Clear stack\n while (stackSize > 0) {\n context.restore();\n stackSize--;\n }\n\n // Restore to initial state\n context.restore();\n context.save();\n\n // Clear path\n context.beginPath();\n pathClosed = false;\n\n };\n\n /**\n * Sets the given affine transform (defined with six values from the\n * transform's matrix).\n *\n * @param {Number} a The first value in the affine transform's matrix.\n * @param {Number} b The second value in the affine transform's matrix.\n * @param {Number} c The third value in the affine transform's matrix.\n * @param {Number} d The fourth value in the affine transform's matrix.\n * @param {Number} e The fifth value in the affine transform's matrix.\n * @param {Number} f The sixth value in the affine transform's matrix.\n */\n this.setTransform = function(a, b, c, d, e, f) {\n context.setTransform(\n a, b, c,\n d, e, f\n /*0, 0, 1*/\n );\n };\n\n /**\n * Applies the given affine transform (defined with six values from the\n * transform's matrix).\n *\n * @param {Number} a The first value in the affine transform's matrix.\n * @param {Number} b The second value in the affine transform's matrix.\n * @param {Number} c The third value in the affine transform's matrix.\n * @param {Number} d The fourth value in the affine transform's matrix.\n * @param {Number} e The fifth value in the affine transform's matrix.\n * @param {Number} f The sixth value in the affine transform's matrix.\n */\n this.transform = function(a, b, c, d, e, f) {\n context.transform(\n a, b, c,\n d, e, f\n /*0, 0, 1*/\n );\n };\n\n /**\n * Sets the channel mask for future operations on this Layer.\n *\n * The channel mask is a Guacamole-specific compositing operation identifier\n * with a single bit representing each of four channels (in order): source\n * image where destination transparent, source where destination opaque,\n * destination where source transparent, and destination where source\n * opaque.\n *\n * @param {Number} mask The channel mask for future operations on this\n * Layer.\n */\n this.setChannelMask = function(mask) {\n context.globalCompositeOperation = compositeOperation[mask];\n };\n\n /**\n * Sets the miter limit for stroke operations using the miter join. This\n * limit is the maximum ratio of the size of the miter join to the stroke\n * width. If this ratio is exceeded, the miter will not be drawn for that\n * joint of the path.\n *\n * @param {Number} limit The miter limit for stroke operations using the\n * miter join.\n */\n this.setMiterLimit = function(limit) {\n context.miterLimit = limit;\n };\n\n // Initialize canvas dimensions\n resize(width, height);\n\n // Explicitly render canvas below other elements in the layer (such as\n // child layers). Chrome and others may fail to render layers properly\n // without this.\n canvas.style.zIndex = -1;\n\n};\n\n/**\n * Channel mask for the composite operation \"rout\".\n */\nGuacamole.Layer.ROUT = 0x2;\n\n/**\n * Channel mask for the composite operation \"atop\".\n */\nGuacamole.Layer.ATOP = 0x6;\n\n/**\n * Channel mask for the composite operation \"xor\".\n */\nGuacamole.Layer.XOR = 0xA;\n\n/**\n * Channel mask for the composite operation \"rover\".\n */\nGuacamole.Layer.ROVER = 0xB;\n\n/**\n * Channel mask for the composite operation \"over\".\n */\nGuacamole.Layer.OVER = 0xE;\n\n/**\n * Channel mask for the composite operation \"plus\".\n */\nGuacamole.Layer.PLUS = 0xF;\n\n/**\n * Channel mask for the composite operation \"rin\".\n * Beware that WebKit-based browsers may leave the contents of the destionation\n * layer where the source layer is transparent, despite the definition of this\n * operation.\n */\nGuacamole.Layer.RIN = 0x1;\n\n/**\n * Channel mask for the composite operation \"in\".\n * Beware that WebKit-based browsers may leave the contents of the destionation\n * layer where the source layer is transparent, despite the definition of this\n * operation.\n */\nGuacamole.Layer.IN = 0x4;\n\n/**\n * Channel mask for the composite operation \"out\".\n * Beware that WebKit-based browsers may leave the contents of the destionation\n * layer where the source layer is transparent, despite the definition of this\n * operation.\n */\nGuacamole.Layer.OUT = 0x8;\n\n/**\n * Channel mask for the composite operation \"ratop\".\n * Beware that WebKit-based browsers may leave the contents of the destionation\n * layer where the source layer is transparent, despite the definition of this\n * operation.\n */\nGuacamole.Layer.RATOP = 0x9;\n\n/**\n * Channel mask for the composite operation \"src\".\n * Beware that WebKit-based browsers may leave the contents of the destionation\n * layer where the source layer is transparent, despite the definition of this\n * operation.\n */\nGuacamole.Layer.SRC = 0xC;\n\n/**\n * Represents a single pixel of image data. All components have a minimum value\n * of 0 and a maximum value of 255.\n *\n * @constructor\n *\n * @param {Number} r The red component of this pixel.\n * @param {Number} g The green component of this pixel.\n * @param {Number} b The blue component of this pixel.\n * @param {Number} a The alpha component of this pixel.\n */\nGuacamole.Layer.Pixel = function(r, g, b, a) {\n\n /**\n * The red component of this pixel, where 0 is the minimum value,\n * and 255 is the maximum.\n */\n this.red = r;\n\n /**\n * The green component of this pixel, where 0 is the minimum value,\n * and 255 is the maximum.\n */\n this.green = g;\n\n /**\n * The blue component of this pixel, where 0 is the minimum value,\n * and 255 is the maximum.\n */\n this.blue = b;\n\n /**\n * The alpha component of this pixel, where 0 is the minimum value,\n * and 255 is the maximum.\n */\n this.alpha = a;\n\n};\n\n\n/**\n * Provides cross-browser mouse events for a given element. The events of\n * the given element are automatically populated with handlers that translate\n * mouse events into a non-browser-specific event provided by the\n * Guacamole.Mouse instance.\n *\n * @constructor\n * @param {Element} element The Element to use to provide mouse events.\n */\nGuacamole.Mouse = function(element) {\n\n /**\n * Reference to this Guacamole.Mouse.\n * @private\n */\n var guac_mouse = this;\n\n /**\n * The number of mousemove events to require before re-enabling mouse\n * event handling after receiving a touch event.\n */\n this.touchMouseThreshold = 3;\n\n /**\n * The minimum amount of pixels scrolled required for a single scroll button\n * click.\n */\n this.scrollThreshold = 53;\n\n /**\n * The number of pixels to scroll per line.\n */\n this.PIXELS_PER_LINE = 18;\n\n /**\n * The number of pixels to scroll per page.\n */\n this.PIXELS_PER_PAGE = this.PIXELS_PER_LINE * 16;\n\n /**\n * The current mouse state. The properties of this state are updated when\n * mouse events fire. This state object is also passed in as a parameter to\n * the handler of any mouse events.\n *\n * @type {Guacamole.Mouse.State}\n */\n this.currentState = new Guacamole.Mouse.State(\n 0, 0,\n false, false, false, false, false\n );\n\n /**\n * Fired whenever the user presses a mouse button down over the element\n * associated with this Guacamole.Mouse.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousedown = null;\n\n /**\n * Fired whenever the user releases a mouse button down over the element\n * associated with this Guacamole.Mouse.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmouseup = null;\n\n /**\n * Fired whenever the user moves the mouse over the element associated with\n * this Guacamole.Mouse.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousemove = null;\n\n /**\n * Fired whenever the mouse leaves the boundaries of the element associated\n * with this Guacamole.Mouse.\n *\n * @event\n */\n this.onmouseout = null;\n\n /**\n * Counter of mouse events to ignore. This decremented by mousemove, and\n * while non-zero, mouse events will have no effect.\n * @private\n */\n var ignore_mouse = 0;\n\n /**\n * Cumulative scroll delta amount. This value is accumulated through scroll\n * events and results in scroll button clicks if it exceeds a certain\n * threshold.\n *\n * @private\n */\n var scroll_delta = 0;\n\n function cancelEvent(e) {\n e.stopPropagation();\n if (e.preventDefault) e.preventDefault();\n e.returnValue = false;\n }\n\n // Block context menu so right-click gets sent properly\n element.addEventListener(\"contextmenu\", function(e) {\n cancelEvent(e);\n }, false);\n\n element.addEventListener(\"mousemove\", function(e) {\n\n cancelEvent(e);\n\n // If ignoring events, decrement counter\n if (ignore_mouse) {\n ignore_mouse--;\n return;\n }\n\n guac_mouse.currentState.fromClientPosition(element, e.clientX, e.clientY);\n\n if (guac_mouse.onmousemove)\n guac_mouse.onmousemove(guac_mouse.currentState);\n\n }, false);\n\n element.addEventListener(\"mousedown\", function(e) {\n\n cancelEvent(e);\n\n // Do not handle if ignoring events\n if (ignore_mouse)\n return;\n\n switch (e.button) {\n case 0:\n guac_mouse.currentState.left = true;\n break;\n case 1:\n guac_mouse.currentState.middle = true;\n break;\n case 2:\n guac_mouse.currentState.right = true;\n break;\n }\n\n if (guac_mouse.onmousedown)\n guac_mouse.onmousedown(guac_mouse.currentState);\n\n }, false);\n\n element.addEventListener(\"mouseup\", function(e) {\n\n cancelEvent(e);\n\n // Do not handle if ignoring events\n if (ignore_mouse)\n return;\n\n switch (e.button) {\n case 0:\n guac_mouse.currentState.left = false;\n break;\n case 1:\n guac_mouse.currentState.middle = false;\n break;\n case 2:\n guac_mouse.currentState.right = false;\n break;\n }\n\n if (guac_mouse.onmouseup)\n guac_mouse.onmouseup(guac_mouse.currentState);\n\n }, false);\n\n element.addEventListener(\"mouseout\", function(e) {\n\n // Get parent of the element the mouse pointer is leaving\n if (!e) e = window.event;\n\n // Check that mouseout is due to actually LEAVING the element\n var target = e.relatedTarget || e.toElement;\n while (target) {\n if (target === element)\n return;\n target = target.parentElement;\n }\n\n cancelEvent(e);\n\n // Release all buttons\n if (guac_mouse.currentState.left\n || guac_mouse.currentState.middle\n || guac_mouse.currentState.right) {\n\n guac_mouse.currentState.left = false;\n guac_mouse.currentState.middle = false;\n guac_mouse.currentState.right = false;\n\n if (guac_mouse.onmouseup)\n guac_mouse.onmouseup(guac_mouse.currentState);\n }\n\n // Fire onmouseout event\n if (guac_mouse.onmouseout)\n guac_mouse.onmouseout();\n\n }, false);\n\n // Override selection on mouse event element.\n element.addEventListener(\"selectstart\", function(e) {\n cancelEvent(e);\n }, false);\n\n // Ignore all pending mouse events when touch events are the apparent source\n function ignorePendingMouseEvents() { ignore_mouse = guac_mouse.touchMouseThreshold; }\n\n element.addEventListener(\"touchmove\", ignorePendingMouseEvents, false);\n element.addEventListener(\"touchstart\", ignorePendingMouseEvents, false);\n element.addEventListener(\"touchend\", ignorePendingMouseEvents, false);\n\n // Scroll wheel support\n function mousewheel_handler(e) {\n\n // Determine approximate scroll amount (in pixels)\n var delta = e.deltaY || -e.wheelDeltaY || -e.wheelDelta;\n\n // If successfully retrieved scroll amount, convert to pixels if not\n // already in pixels\n if (delta) {\n\n // Convert to pixels if delta was lines\n if (e.deltaMode === 1)\n delta = e.deltaY * guac_mouse.PIXELS_PER_LINE;\n\n // Convert to pixels if delta was pages\n else if (e.deltaMode === 2)\n delta = e.deltaY * guac_mouse.PIXELS_PER_PAGE;\n\n }\n\n // Otherwise, assume legacy mousewheel event and line scrolling\n else\n delta = e.detail * guac_mouse.PIXELS_PER_LINE;\n\n // Update overall delta\n scroll_delta += delta;\n\n // Up\n if (scroll_delta <= -guac_mouse.scrollThreshold) {\n\n // Repeatedly click the up button until insufficient delta remains\n do {\n\n if (guac_mouse.onmousedown) {\n guac_mouse.currentState.up = true;\n guac_mouse.onmousedown(guac_mouse.currentState);\n }\n\n if (guac_mouse.onmouseup) {\n guac_mouse.currentState.up = false;\n guac_mouse.onmouseup(guac_mouse.currentState);\n }\n\n scroll_delta += guac_mouse.scrollThreshold;\n\n } while (scroll_delta <= -guac_mouse.scrollThreshold);\n\n // Reset delta\n scroll_delta = 0;\n\n }\n\n // Down\n if (scroll_delta >= guac_mouse.scrollThreshold) {\n\n // Repeatedly click the down button until insufficient delta remains\n do {\n\n if (guac_mouse.onmousedown) {\n guac_mouse.currentState.down = true;\n guac_mouse.onmousedown(guac_mouse.currentState);\n }\n\n if (guac_mouse.onmouseup) {\n guac_mouse.currentState.down = false;\n guac_mouse.onmouseup(guac_mouse.currentState);\n }\n\n scroll_delta -= guac_mouse.scrollThreshold;\n\n } while (scroll_delta >= guac_mouse.scrollThreshold);\n\n // Reset delta\n scroll_delta = 0;\n\n }\n\n cancelEvent(e);\n\n }\n\n element.addEventListener('DOMMouseScroll', mousewheel_handler, false);\n element.addEventListener('mousewheel', mousewheel_handler, false);\n element.addEventListener('wheel', mousewheel_handler, false);\n\n /**\n * Whether the browser supports CSS3 cursor styling, including hotspot\n * coordinates.\n *\n * @private\n * @type {Boolean}\n */\n var CSS3_CURSOR_SUPPORTED = (function() {\n\n var div = document.createElement(\"div\");\n\n // If no cursor property at all, then no support\n if (!(\"cursor\" in div.style))\n return false;\n\n try {\n // Apply simple 1x1 PNG\n div.style.cursor = \"url(data:image/png;base64,\"\n + \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB\"\n + \"AQMAAAAl21bKAAAAA1BMVEX///+nxBvI\"\n + \"AAAACklEQVQI12NgAAAAAgAB4iG8MwAA\"\n + \"AABJRU5ErkJggg==) 0 0, auto\";\n }\n catch (e) {\n return false;\n }\n\n // Verify cursor property is set to URL with hotspot\n return /\\burl\\([^()]*\\)\\s+0\\s+0\\b/.test(div.style.cursor || \"\");\n\n })();\n\n /**\n * Changes the local mouse cursor to the given canvas, having the given\n * hotspot coordinates. This affects styling of the element backing this\n * Guacamole.Mouse only, and may fail depending on browser support for\n * setting the mouse cursor.\n *\n * If setting the local cursor is desired, it is up to the implementation\n * to do something else, such as use the software cursor built into\n * Guacamole.Display, if the local cursor cannot be set.\n *\n * @param {HTMLCanvasElement} canvas The cursor image.\n * @param {Number} x The X-coordinate of the cursor hotspot.\n * @param {Number} y The Y-coordinate of the cursor hotspot.\n * @return {Boolean} true if the cursor was successfully set, false if the\n * cursor could not be set for any reason.\n */\n this.setCursor = function(canvas, x, y) {\n\n // Attempt to set via CSS3 cursor styling\n if (CSS3_CURSOR_SUPPORTED) {\n var dataURL = canvas.toDataURL('image/png');\n element.style.cursor = \"url(\" + dataURL + \") \" + x + \" \" + y + \", auto\";\n return true;\n }\n\n // Otherwise, setting cursor failed\n return false;\n\n };\n\n};\n\n/**\n * Simple container for properties describing the state of a mouse.\n *\n * @constructor\n * @param {Number} x The X position of the mouse pointer in pixels.\n * @param {Number} y The Y position of the mouse pointer in pixels.\n * @param {Boolean} left Whether the left mouse button is pressed.\n * @param {Boolean} middle Whether the middle mouse button is pressed.\n * @param {Boolean} right Whether the right mouse button is pressed.\n * @param {Boolean} up Whether the up mouse button is pressed (the fourth\n * button, usually part of a scroll wheel).\n * @param {Boolean} down Whether the down mouse button is pressed (the fifth\n * button, usually part of a scroll wheel).\n */\nGuacamole.Mouse.State = function(x, y, left, middle, right, up, down) {\n\n /**\n * Reference to this Guacamole.Mouse.State.\n * @private\n */\n var guac_state = this;\n\n /**\n * The current X position of the mouse pointer.\n * @type {Number}\n */\n this.x = x;\n\n /**\n * The current Y position of the mouse pointer.\n * @type {Number}\n */\n this.y = y;\n\n /**\n * Whether the left mouse button is currently pressed.\n * @type {Boolean}\n */\n this.left = left;\n\n /**\n * Whether the middle mouse button is currently pressed.\n * @type {Boolean}\n */\n this.middle = middle;\n\n /**\n * Whether the right mouse button is currently pressed.\n * @type {Boolean}\n */\n this.right = right;\n\n /**\n * Whether the up mouse button is currently pressed. This is the fourth\n * mouse button, associated with upward scrolling of the mouse scroll\n * wheel.\n * @type {Boolean}\n */\n this.up = up;\n\n /**\n * Whether the down mouse button is currently pressed. This is the fifth\n * mouse button, associated with downward scrolling of the mouse scroll\n * wheel.\n * @type {Boolean}\n */\n this.down = down;\n\n /**\n * Updates the position represented within this state object by the given\n * element and clientX/clientY coordinates (commonly available within event\n * objects). Position is translated from clientX/clientY (relative to\n * viewport) to element-relative coordinates.\n *\n * @param {Element} element The element the coordinates should be relative\n * to.\n * @param {Number} clientX The X coordinate to translate, viewport-relative.\n * @param {Number} clientY The Y coordinate to translate, viewport-relative.\n */\n this.fromClientPosition = function(element, clientX, clientY) {\n\n guac_state.x = clientX - element.offsetLeft;\n guac_state.y = clientY - element.offsetTop;\n\n // This is all JUST so we can get the mouse position within the element\n var parent = element.offsetParent;\n while (parent && !(parent === document.body)) {\n guac_state.x -= parent.offsetLeft - parent.scrollLeft;\n guac_state.y -= parent.offsetTop - parent.scrollTop;\n\n parent = parent.offsetParent;\n }\n\n // Element ultimately depends on positioning within document body,\n // take document scroll into account.\n if (parent) {\n var documentScrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;\n var documentScrollTop = document.body.scrollTop || document.documentElement.scrollTop;\n\n guac_state.x -= parent.offsetLeft - documentScrollLeft;\n guac_state.y -= parent.offsetTop - documentScrollTop;\n }\n\n };\n\n};\n\n/**\n * Provides cross-browser relative touch event translation for a given element.\n *\n * Touch events are translated into mouse events as if the touches occurred\n * on a touchpad (drag to push the mouse pointer, tap to click).\n *\n * @constructor\n * @param {Element} element The Element to use to provide touch events.\n */\nGuacamole.Mouse.Touchpad = function(element) {\n\n /**\n * Reference to this Guacamole.Mouse.Touchpad.\n * @private\n */\n var guac_touchpad = this;\n\n /**\n * The distance a two-finger touch must move per scrollwheel event, in\n * pixels.\n */\n this.scrollThreshold = 20 * (window.devicePixelRatio || 1);\n\n /**\n * The maximum number of milliseconds to wait for a touch to end for the\n * gesture to be considered a click.\n */\n this.clickTimingThreshold = 250;\n\n /**\n * The maximum number of pixels to allow a touch to move for the gesture to\n * be considered a click.\n */\n this.clickMoveThreshold = 10 * (window.devicePixelRatio || 1);\n\n /**\n * The current mouse state. The properties of this state are updated when\n * mouse events fire. This state object is also passed in as a parameter to\n * the handler of any mouse events.\n *\n * @type {Guacamole.Mouse.State}\n */\n this.currentState = new Guacamole.Mouse.State(\n 0, 0,\n false, false, false, false, false\n );\n\n /**\n * Fired whenever a mouse button is effectively pressed. This can happen\n * as part of a \"click\" gesture initiated by the user by tapping one\n * or more fingers over the touchpad element, as part of a \"scroll\"\n * gesture initiated by dragging two fingers up or down, etc.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousedown = null;\n\n /**\n * Fired whenever a mouse button is effectively released. This can happen\n * as part of a \"click\" gesture initiated by the user by tapping one\n * or more fingers over the touchpad element, as part of a \"scroll\"\n * gesture initiated by dragging two fingers up or down, etc.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmouseup = null;\n\n /**\n * Fired whenever the user moves the mouse by dragging their finger over\n * the touchpad element.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousemove = null;\n\n var touch_count = 0;\n var last_touch_x = 0;\n var last_touch_y = 0;\n var last_touch_time = 0;\n var pixels_moved = 0;\n\n var touch_buttons = {\n 1: \"left\",\n 2: \"right\",\n 3: \"middle\"\n };\n\n var gesture_in_progress = false;\n var click_release_timeout = null;\n\n element.addEventListener(\"touchend\", function(e) {\n\n e.preventDefault();\n\n // If we're handling a gesture AND this is the last touch\n if (gesture_in_progress && e.touches.length === 0) {\n\n var time = new Date().getTime();\n\n // Get corresponding mouse button\n var button = touch_buttons[touch_count];\n\n // If mouse already down, release anad clear timeout\n if (guac_touchpad.currentState[button]) {\n\n // Fire button up event\n guac_touchpad.currentState[button] = false;\n if (guac_touchpad.onmouseup)\n guac_touchpad.onmouseup(guac_touchpad.currentState);\n\n // Clear timeout, if set\n if (click_release_timeout) {\n window.clearTimeout(click_release_timeout);\n click_release_timeout = null;\n }\n\n }\n\n // If single tap detected (based on time and distance)\n if (time - last_touch_time <= guac_touchpad.clickTimingThreshold\n && pixels_moved < guac_touchpad.clickMoveThreshold) {\n\n // Fire button down event\n guac_touchpad.currentState[button] = true;\n if (guac_touchpad.onmousedown)\n guac_touchpad.onmousedown(guac_touchpad.currentState);\n\n // Delay mouse up - mouse up should be canceled if\n // touchstart within timeout.\n click_release_timeout = window.setTimeout(function() {\n\n // Fire button up event\n guac_touchpad.currentState[button] = false;\n if (guac_touchpad.onmouseup)\n guac_touchpad.onmouseup(guac_touchpad.currentState);\n\n // Gesture now over\n gesture_in_progress = false;\n\n }, guac_touchpad.clickTimingThreshold);\n\n }\n\n // If we're not waiting to see if this is a click, stop gesture\n if (!click_release_timeout)\n gesture_in_progress = false;\n\n }\n\n }, false);\n\n element.addEventListener(\"touchstart\", function(e) {\n\n e.preventDefault();\n\n // Track number of touches, but no more than three\n touch_count = Math.min(e.touches.length, 3);\n\n // Clear timeout, if set\n if (click_release_timeout) {\n window.clearTimeout(click_release_timeout);\n click_release_timeout = null;\n }\n\n // Record initial touch location and time for touch movement\n // and tap gestures\n if (!gesture_in_progress) {\n\n // Stop mouse events while touching\n gesture_in_progress = true;\n\n // Record touch location and time\n var starting_touch = e.touches[0];\n last_touch_x = starting_touch.clientX;\n last_touch_y = starting_touch.clientY;\n last_touch_time = new Date().getTime();\n pixels_moved = 0;\n\n }\n\n }, false);\n\n element.addEventListener(\"touchmove\", function(e) {\n\n e.preventDefault();\n\n // Get change in touch location\n var touch = e.touches[0];\n var delta_x = touch.clientX - last_touch_x;\n var delta_y = touch.clientY - last_touch_y;\n\n // Track pixels moved\n pixels_moved += Math.abs(delta_x) + Math.abs(delta_y);\n\n // If only one touch involved, this is mouse move\n if (touch_count === 1) {\n\n // Calculate average velocity in Manhatten pixels per millisecond\n var velocity = pixels_moved / (new Date().getTime() - last_touch_time);\n\n // Scale mouse movement relative to velocity\n var scale = 1 + velocity;\n\n // Update mouse location\n guac_touchpad.currentState.x += delta_x*scale;\n guac_touchpad.currentState.y += delta_y*scale;\n\n // Prevent mouse from leaving screen\n\n if (guac_touchpad.currentState.x < 0)\n guac_touchpad.currentState.x = 0;\n else if (guac_touchpad.currentState.x >= element.offsetWidth)\n guac_touchpad.currentState.x = element.offsetWidth - 1;\n\n if (guac_touchpad.currentState.y < 0)\n guac_touchpad.currentState.y = 0;\n else if (guac_touchpad.currentState.y >= element.offsetHeight)\n guac_touchpad.currentState.y = element.offsetHeight - 1;\n\n // Fire movement event, if defined\n if (guac_touchpad.onmousemove)\n guac_touchpad.onmousemove(guac_touchpad.currentState);\n\n // Update touch location\n last_touch_x = touch.clientX;\n last_touch_y = touch.clientY;\n\n }\n\n // Interpret two-finger swipe as scrollwheel\n else if (touch_count === 2) {\n\n // If change in location passes threshold for scroll\n if (Math.abs(delta_y) >= guac_touchpad.scrollThreshold) {\n\n // Decide button based on Y movement direction\n var button;\n if (delta_y > 0) button = \"down\";\n else button = \"up\";\n\n // Fire button down event\n guac_touchpad.currentState[button] = true;\n if (guac_touchpad.onmousedown)\n guac_touchpad.onmousedown(guac_touchpad.currentState);\n\n // Fire button up event\n guac_touchpad.currentState[button] = false;\n if (guac_touchpad.onmouseup)\n guac_touchpad.onmouseup(guac_touchpad.currentState);\n\n // Only update touch location after a scroll has been\n // detected\n last_touch_x = touch.clientX;\n last_touch_y = touch.clientY;\n\n }\n\n }\n\n }, false);\n\n};\n\n/**\n * Provides cross-browser absolute touch event translation for a given element.\n *\n * Touch events are translated into mouse events as if the touches occurred\n * on a touchscreen (tapping anywhere on the screen clicks at that point,\n * long-press to right-click).\n *\n * @constructor\n * @param {Element} element The Element to use to provide touch events.\n */\nGuacamole.Mouse.Touchscreen = function(element) {\n\n /**\n * Reference to this Guacamole.Mouse.Touchscreen.\n * @private\n */\n var guac_touchscreen = this;\n\n /**\n * Whether a gesture is known to be in progress. If false, touch events\n * will be ignored.\n *\n * @private\n */\n var gesture_in_progress = false;\n\n /**\n * The start X location of a gesture.\n * @private\n */\n var gesture_start_x = null;\n\n /**\n * The start Y location of a gesture.\n * @private\n */\n var gesture_start_y = null;\n\n /**\n * The timeout associated with the delayed, cancellable click release.\n *\n * @private\n */\n var click_release_timeout = null;\n\n /**\n * The timeout associated with long-press for right click.\n *\n * @private\n */\n var long_press_timeout = null;\n\n /**\n * The distance a two-finger touch must move per scrollwheel event, in\n * pixels.\n */\n this.scrollThreshold = 20 * (window.devicePixelRatio || 1);\n\n /**\n * The maximum number of milliseconds to wait for a touch to end for the\n * gesture to be considered a click.\n */\n this.clickTimingThreshold = 250;\n\n /**\n * The maximum number of pixels to allow a touch to move for the gesture to\n * be considered a click.\n */\n this.clickMoveThreshold = 16 * (window.devicePixelRatio || 1);\n\n /**\n * The amount of time a press must be held for long press to be\n * detected.\n */\n this.longPressThreshold = 500;\n\n /**\n * The current mouse state. The properties of this state are updated when\n * mouse events fire. This state object is also passed in as a parameter to\n * the handler of any mouse events.\n *\n * @type {Guacamole.Mouse.State}\n */\n this.currentState = new Guacamole.Mouse.State(\n 0, 0,\n false, false, false, false, false\n );\n\n /**\n * Fired whenever a mouse button is effectively pressed. This can happen\n * as part of a \"mousedown\" gesture initiated by the user by pressing one\n * finger over the touchscreen element, as part of a \"scroll\" gesture\n * initiated by dragging two fingers up or down, etc.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousedown = null;\n\n /**\n * Fired whenever a mouse button is effectively released. This can happen\n * as part of a \"mouseup\" gesture initiated by the user by removing the\n * finger pressed against the touchscreen element, or as part of a \"scroll\"\n * gesture initiated by dragging two fingers up or down, etc.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmouseup = null;\n\n /**\n * Fired whenever the user moves the mouse by dragging their finger over\n * the touchscreen element. Note that unlike Guacamole.Mouse.Touchpad,\n * dragging a finger over the touchscreen element will always cause\n * the mouse button to be effectively down, as if clicking-and-dragging.\n *\n * @event\n * @param {Guacamole.Mouse.State} state The current mouse state.\n */\n this.onmousemove = null;\n\n /**\n * Presses the given mouse button, if it isn't already pressed. Valid\n * button values are \"left\", \"middle\", \"right\", \"up\", and \"down\".\n *\n * @private\n * @param {String} button The mouse button to press.\n */\n function press_button(button) {\n if (!guac_touchscreen.currentState[button]) {\n guac_touchscreen.currentState[button] = true;\n if (guac_touchscreen.onmousedown)\n guac_touchscreen.onmousedown(guac_touchscreen.currentState);\n }\n }\n\n /**\n * Releases the given mouse button, if it isn't already released. Valid\n * button values are \"left\", \"middle\", \"right\", \"up\", and \"down\".\n *\n * @private\n * @param {String} button The mouse button to release.\n */\n function release_button(button) {\n if (guac_touchscreen.currentState[button]) {\n guac_touchscreen.currentState[button] = false;\n if (guac_touchscreen.onmouseup)\n guac_touchscreen.onmouseup(guac_touchscreen.currentState);\n }\n }\n\n /**\n * Clicks (presses and releases) the given mouse button. Valid button\n * values are \"left\", \"middle\", \"right\", \"up\", and \"down\".\n *\n * @private\n * @param {String} button The mouse button to click.\n */\n function click_button(button) {\n press_button(button);\n release_button(button);\n }\n\n /**\n * Moves the mouse to the given coordinates. These coordinates must be\n * relative to the browser window, as they will be translated based on\n * the touch event target's location within the browser window.\n *\n * @private\n * @param {Number} x The X coordinate of the mouse pointer.\n * @param {Number} y The Y coordinate of the mouse pointer.\n */\n function move_mouse(x, y) {\n guac_touchscreen.currentState.fromClientPosition(element, x, y);\n if (guac_touchscreen.onmousemove)\n guac_touchscreen.onmousemove(guac_touchscreen.currentState);\n }\n\n /**\n * Returns whether the given touch event exceeds the movement threshold for\n * clicking, based on where the touch gesture began.\n *\n * @private\n * @param {TouchEvent} e The touch event to check.\n * @return {Boolean} true if the movement threshold is exceeded, false\n * otherwise.\n */\n function finger_moved(e) {\n var touch = e.touches[0] || e.changedTouches[0];\n var delta_x = touch.clientX - gesture_start_x;\n var delta_y = touch.clientY - gesture_start_y;\n return Math.sqrt(delta_x*delta_x + delta_y*delta_y) >= guac_touchscreen.clickMoveThreshold;\n }\n\n /**\n * Begins a new gesture at the location of the first touch in the given\n * touch event.\n *\n * @private\n * @param {TouchEvent} e The touch event beginning this new gesture.\n */\n function begin_gesture(e) {\n var touch = e.touches[0];\n gesture_in_progress = true;\n gesture_start_x = touch.clientX;\n gesture_start_y = touch.clientY;\n }\n\n /**\n * End the current gesture entirely. Wait for all touches to be done before\n * resuming gesture detection.\n *\n * @private\n */\n function end_gesture() {\n window.clearTimeout(click_release_timeout);\n window.clearTimeout(long_press_timeout);\n gesture_in_progress = false;\n }\n\n element.addEventListener(\"touchend\", function(e) {\n\n // Do not handle if no gesture\n if (!gesture_in_progress)\n return;\n\n // Ignore if more than one touch\n if (e.touches.length !== 0 || e.changedTouches.length !== 1) {\n end_gesture();\n return;\n }\n\n // Long-press, if any, is over\n window.clearTimeout(long_press_timeout);\n\n // Always release mouse button if pressed\n release_button(\"left\");\n\n // If finger hasn't moved enough to cancel the click\n if (!finger_moved(e)) {\n\n e.preventDefault();\n\n // If not yet pressed, press and start delay release\n if (!guac_touchscreen.currentState.left) {\n\n var touch = e.changedTouches[0];\n move_mouse(touch.clientX, touch.clientY);\n press_button(\"left\");\n\n // Release button after a delay, if not canceled\n click_release_timeout = window.setTimeout(function() {\n release_button(\"left\");\n end_gesture();\n }, guac_touchscreen.clickTimingThreshold);\n\n }\n\n } // end if finger not moved\n\n }, false);\n\n element.addEventListener(\"touchstart\", function(e) {\n\n // Ignore if more than one touch\n if (e.touches.length !== 1) {\n end_gesture();\n return;\n }\n\n e.preventDefault();\n\n // New touch begins a new gesture\n begin_gesture(e);\n\n // Keep button pressed if tap after left click\n window.clearTimeout(click_release_timeout);\n\n // Click right button if this turns into a long-press\n long_press_timeout = window.setTimeout(function() {\n var touch = e.touches[0];\n move_mouse(touch.clientX, touch.clientY);\n click_button(\"right\");\n end_gesture();\n }, guac_touchscreen.longPressThreshold);\n\n }, false);\n\n element.addEventListener(\"touchmove\", function(e) {\n\n // Do not handle if no gesture\n if (!gesture_in_progress)\n return;\n\n // Cancel long press if finger moved\n if (finger_moved(e))\n window.clearTimeout(long_press_timeout);\n\n // Ignore if more than one touch\n if (e.touches.length !== 1) {\n end_gesture();\n return;\n }\n\n // Update mouse position if dragging\n if (guac_touchscreen.currentState.left) {\n\n e.preventDefault();\n\n // Update state\n var touch = e.touches[0];\n move_mouse(touch.clientX, touch.clientY);\n\n }\n\n }, false);\n\n};\n\n/**\n * The namespace used by the Guacamole JavaScript API. Absolutely all classes\n * defined by the Guacamole JavaScript API will be within this namespace.\n *\n * @namespace\n */\n\n\n/**\n * An object used by the Guacamole client to house arbitrarily-many named\n * input and output streams.\n *\n * @constructor\n * @param {Guacamole.Client} client\n * The client owning this object.\n *\n * @param {Number} index\n * The index of this object.\n */\nGuacamole.Object = function guacamoleObject(client, index) {\n\n /**\n * Reference to this Guacamole.Object.\n *\n * @private\n * @type {Guacamole.Object}\n */\n var guacObject = this;\n\n /**\n * Map of stream name to corresponding queue of callbacks. The queue of\n * callbacks is guaranteed to be in order of request.\n *\n * @private\n * @type {Object.}\n */\n var bodyCallbacks = {};\n\n /**\n * Removes and returns the callback at the head of the callback queue for\n * the stream having the given name. If no such callbacks exist, null is\n * returned.\n *\n * @private\n * @param {String} name\n * The name of the stream to retrieve a callback for.\n *\n * @returns {Function}\n * The next callback associated with the stream having the given name,\n * or null if no such callback exists.\n */\n var dequeueBodyCallback = function dequeueBodyCallback(name) {\n\n // If no callbacks defined, simply return null\n var callbacks = bodyCallbacks[name];\n if (!callbacks)\n return null;\n\n // Otherwise, pull off first callback, deleting the queue if empty\n var callback = callbacks.shift();\n if (callbacks.length === 0)\n delete bodyCallbacks[name];\n\n // Return found callback\n return callback;\n\n };\n\n /**\n * Adds the given callback to the tail of the callback queue for the stream\n * having the given name.\n *\n * @private\n * @param {String} name\n * The name of the stream to associate with the given callback.\n *\n * @param {Function} callback\n * The callback to add to the queue of the stream with the given name.\n */\n var enqueueBodyCallback = function enqueueBodyCallback(name, callback) {\n\n // Get callback queue by name, creating first if necessary\n var callbacks = bodyCallbacks[name];\n if (!callbacks) {\n callbacks = [];\n bodyCallbacks[name] = callbacks;\n }\n\n // Add callback to end of queue\n callbacks.push(callback);\n\n };\n\n /**\n * The index of this object.\n *\n * @type {Number}\n */\n this.index = index;\n\n /**\n * Called when this object receives the body of a requested input stream.\n * By default, all objects will invoke the callbacks provided to their\n * requestInputStream() functions based on the name of the stream\n * requested. This behavior can be overridden by specifying a different\n * handler here.\n *\n * @event\n * @param {Guacamole.InputStream} inputStream\n * The input stream of the received body.\n *\n * @param {String} mimetype\n * The mimetype of the data being received.\n *\n * @param {String} name\n * The name of the stream whose body has been received.\n */\n this.onbody = function defaultBodyHandler(inputStream, mimetype, name) {\n\n // Call queued callback for the received body, if any\n var callback = dequeueBodyCallback(name);\n if (callback)\n callback(inputStream, mimetype);\n\n };\n\n /**\n * Called when this object is being undefined. Once undefined, no further\n * communication involving this object may occur.\n *\n * @event\n */\n this.onundefine = null;\n\n /**\n * Requests read access to the input stream having the given name. If\n * successful, a new input stream will be created.\n *\n * @param {String} name\n * The name of the input stream to request.\n *\n * @param {Function} [bodyCallback]\n * The callback to invoke when the body of the requested input stream\n * is received. This callback will be provided a Guacamole.InputStream\n * and its mimetype as its two only arguments. If the onbody handler of\n * this object is overridden, this callback will not be invoked.\n */\n this.requestInputStream = function requestInputStream(name, bodyCallback) {\n\n // Queue body callback if provided\n if (bodyCallback)\n enqueueBodyCallback(name, bodyCallback);\n\n // Send request for input stream\n client.requestObjectInputStream(guacObject.index, name);\n\n };\n\n /**\n * Creates a new output stream associated with this object and having the\n * given mimetype and name. The legality of a mimetype and name is dictated\n * by the object itself.\n *\n * @param {String} mimetype\n * The mimetype of the data which will be sent to the output stream.\n *\n * @param {String} name\n * The defined name of an output stream within this object.\n *\n * @returns {Guacamole.OutputStream}\n * An output stream which will write blobs to the named output stream\n * of this object.\n */\n this.createOutputStream = function createOutputStream(mimetype, name) {\n return client.createObjectOutputStream(guacObject.index, mimetype, name);\n };\n\n};\n\n/**\n * The reserved name denoting the root stream of any object. The contents of\n * the root stream MUST be a JSON map of stream name to mimetype.\n *\n * @constant\n * @type {String}\n */\nGuacamole.Object.ROOT_STREAM = '/';\n\n/**\n * The mimetype of a stream containing JSON which maps available stream names\n * to their corresponding mimetype. The root stream of a Guacamole.Object MUST\n * have this mimetype.\n *\n * @constant\n * @type {String}\n */\nGuacamole.Object.STREAM_INDEX_MIMETYPE = 'application/vnd.glyptodon.guacamole.stream-index+json';\n\n\n/**\n * Dynamic on-screen keyboard. Given the layout object for an on-screen\n * keyboard, this object will construct a clickable on-screen keyboard with its\n * own key events.\n *\n * @constructor\n * @param {Guacamole.OnScreenKeyboard.Layout} layout\n * The layout of the on-screen keyboard to display.\n */\nGuacamole.OnScreenKeyboard = function(layout) {\n\n /**\n * Reference to this Guacamole.OnScreenKeyboard.\n *\n * @private\n * @type {Guacamole.OnScreenKeyboard}\n */\n var osk = this;\n\n /**\n * Map of currently-set modifiers to the keysym associated with their\n * original press. When the modifier is cleared, this keysym must be\n * released.\n *\n * @private\n * @type {Object.}\n */\n var modifierKeysyms = {};\n\n /**\n * Map of all key names to their current pressed states. If a key is not\n * pressed, it may not be in this map at all, but all pressed keys will\n * have a corresponding mapping to true.\n *\n * @private\n * @type {Object.}\n */\n var pressed = {};\n\n /**\n * All scalable elements which are part of the on-screen keyboard. Each\n * scalable element is carefully controlled to ensure the interface layout\n * and sizing remains constant, even on browsers that would otherwise\n * experience rounding error due to unit conversions.\n *\n * @private\n * @type {ScaledElement[]}\n */\n var scaledElements = [];\n\n /**\n * Adds a CSS class to an element.\n *\n * @private\n * @function\n * @param {Element} element\n * The element to add a class to.\n *\n * @param {String} classname\n * The name of the class to add.\n */\n var addClass = function addClass(element, classname) {\n\n // If classList supported, use that\n if (element.classList)\n element.classList.add(classname);\n\n // Otherwise, simply append the class\n else\n element.className += \" \" + classname;\n\n };\n\n /**\n * Removes a CSS class from an element.\n *\n * @private\n * @function\n * @param {Element} element\n * The element to remove a class from.\n *\n * @param {String} classname\n * The name of the class to remove.\n */\n var removeClass = function removeClass(element, classname) {\n\n // If classList supported, use that\n if (element.classList)\n element.classList.remove(classname);\n\n // Otherwise, manually filter out classes with given name\n else {\n element.className = element.className.replace(/([^ ]+)[ ]*/g,\n function removeMatchingClasses(match, testClassname) {\n\n // If same class, remove\n if (testClassname === classname)\n return \"\";\n\n // Otherwise, allow\n return match;\n\n }\n );\n }\n\n };\n\n /**\n * Counter of mouse events to ignore. This decremented by mousemove, and\n * while non-zero, mouse events will have no effect.\n *\n * @private\n * @type {Number}\n */\n var ignoreMouse = 0;\n\n /**\n * Ignores all pending mouse events when touch events are the apparent\n * source. Mouse events are ignored until at least touchMouseThreshold\n * mouse events occur without corresponding touch events.\n *\n * @private\n */\n var ignorePendingMouseEvents = function ignorePendingMouseEvents() {\n ignoreMouse = osk.touchMouseThreshold;\n };\n\n /**\n * An element whose dimensions are maintained according to an arbitrary\n * scale. The conversion factor for these arbitrary units to pixels is\n * provided later via a call to scale().\n *\n * @private\n * @constructor\n * @param {Element} element\n * The element whose scale should be maintained.\n *\n * @param {Number} width\n * The width of the element, in arbitrary units, relative to other\n * ScaledElements.\n *\n * @param {Number} height\n * The height of the element, in arbitrary units, relative to other\n * ScaledElements.\n *\n * @param {Boolean} [scaleFont=false]\n * Whether the line height and font size should be scaled as well.\n */\n var ScaledElement = function ScaledElement(element, width, height, scaleFont) {\n\n /**\n * The width of this ScaledElement, in arbitrary units, relative to\n * other ScaledElements.\n *\n * @type {Number}\n */\n this.width = width;\n\n /**\n * The height of this ScaledElement, in arbitrary units, relative to\n * other ScaledElements.\n *\n * @type {Number}\n */\n this.height = height;\n\n /**\n * Resizes the associated element, updating its dimensions according to\n * the given pixels per unit.\n *\n * @param {Number} pixels\n * The number of pixels to assign per arbitrary unit.\n */\n this.scale = function(pixels) {\n\n // Scale element width/height\n element.style.width = (width * pixels) + \"px\";\n element.style.height = (height * pixels) + \"px\";\n\n // Scale font, if requested\n if (scaleFont) {\n element.style.lineHeight = (height * pixels) + \"px\";\n element.style.fontSize = pixels + \"px\";\n }\n\n };\n\n };\n\n /**\n * Returns whether all modifiers having the given names are currently\n * active.\n *\n * @private\n * @param {String[]} names\n * The names of all modifiers to test.\n *\n * @returns {Boolean}\n * true if all specified modifiers are pressed, false otherwise.\n */\n var modifiersPressed = function modifiersPressed(names) {\n\n // If any required modifiers are not pressed, return false\n for (var i=0; i < names.length; i++) {\n\n // Test whether current modifier is pressed\n var name = names[i];\n if (!(name in modifierKeysyms))\n return false;\n\n }\n\n // Otherwise, all required modifiers are pressed\n return true;\n\n };\n\n /**\n * Returns the single matching Key object associated with the key of the\n * given name, where that Key object's requirements (such as pressed\n * modifiers) are all currently satisfied.\n *\n * @private\n * @param {String} keyName\n * The name of the key to retrieve.\n *\n * @returns {Guacamole.OnScreenKeyboard.Key}\n * The Key object associated with the given name, where that object's\n * requirements are all currently satisfied, or null if no such Key\n * can be found.\n */\n var getActiveKey = function getActiveKey(keyName) {\n\n // Get key array for given name\n var keys = osk.keys[keyName];\n if (!keys)\n return null;\n\n // Find last matching key\n for (var i = keys.length - 1; i >= 0; i--) {\n\n // Get candidate key\n var candidate = keys[i];\n\n // If all required modifiers are pressed, use that key\n if (modifiersPressed(candidate.requires))\n return candidate;\n\n }\n\n // No valid key\n return null;\n\n };\n\n /**\n * Presses the key having the given name, updating the associated key\n * element with the \"guac-keyboard-pressed\" CSS class. If the key is\n * already pressed, this function has no effect.\n *\n * @private\n * @param {String} keyName\n * The name of the key to press.\n *\n * @param {String} keyElement\n * The element associated with the given key.\n */\n var press = function press(keyName, keyElement) {\n\n // Press key if not yet pressed\n if (!pressed[keyName]) {\n\n addClass(keyElement, \"guac-keyboard-pressed\");\n\n // Get current key based on modifier state\n var key = getActiveKey(keyName);\n\n // Update modifier state\n if (key.modifier) {\n\n // Construct classname for modifier\n var modifierClass = \"guac-keyboard-modifier-\" + getCSSName(key.modifier);\n\n // Retrieve originally-pressed keysym, if modifier was already pressed\n var originalKeysym = modifierKeysyms[key.modifier];\n\n // Activate modifier if not pressed\n if (!originalKeysym) {\n\n addClass(keyboard, modifierClass);\n modifierKeysyms[key.modifier] = key.keysym;\n\n // Send key event\n if (osk.onkeydown)\n osk.onkeydown(key.keysym);\n\n }\n\n // Deactivate if not pressed\n else {\n\n removeClass(keyboard, modifierClass);\n delete modifierKeysyms[key.modifier];\n\n // Send key event\n if (osk.onkeyup)\n osk.onkeyup(originalKeysym);\n\n }\n\n }\n\n // If not modifier, send key event now\n else if (osk.onkeydown)\n osk.onkeydown(key.keysym);\n\n // Mark key as pressed\n pressed[keyName] = true;\n\n }\n\n };\n\n /**\n * Releases the key having the given name, removing the\n * \"guac-keyboard-pressed\" CSS class from the associated element. If the\n * key is already released, this function has no effect.\n *\n * @private\n * @param {String} keyName\n * The name of the key to release.\n *\n * @param {String} keyElement\n * The element associated with the given key.\n */\n var release = function release(keyName, keyElement) {\n\n // Release key if currently pressed\n if (pressed[keyName]) {\n\n removeClass(keyElement, \"guac-keyboard-pressed\");\n\n // Get current key based on modifier state\n var key = getActiveKey(keyName);\n\n // Send key event if not a modifier key\n if (!key.modifier && osk.onkeyup)\n osk.onkeyup(key.keysym);\n\n // Mark key as released\n pressed[keyName] = false;\n\n }\n\n };\n\n // Create keyboard\n var keyboard = document.createElement(\"div\");\n keyboard.className = \"guac-keyboard\";\n\n // Do not allow selection or mouse movement to propagate/register.\n keyboard.onselectstart =\n keyboard.onmousemove =\n keyboard.onmouseup =\n keyboard.onmousedown = function handleMouseEvents(e) {\n\n // If ignoring events, decrement counter\n if (ignoreMouse)\n ignoreMouse--;\n\n e.stopPropagation();\n return false;\n\n };\n\n /**\n * The number of mousemove events to require before re-enabling mouse\n * event handling after receiving a touch event.\n *\n * @type {Number}\n */\n this.touchMouseThreshold = 3;\n\n /**\n * Fired whenever the user presses a key on this Guacamole.OnScreenKeyboard.\n *\n * @event\n * @param {Number} keysym The keysym of the key being pressed.\n */\n this.onkeydown = null;\n\n /**\n * Fired whenever the user releases a key on this Guacamole.OnScreenKeyboard.\n *\n * @event\n * @param {Number} keysym The keysym of the key being released.\n */\n this.onkeyup = null;\n\n /**\n * The keyboard layout provided at time of construction.\n *\n * @type {Guacamole.OnScreenKeyboard.Layout}\n */\n this.layout = new Guacamole.OnScreenKeyboard.Layout(layout);\n\n /**\n * Returns the element containing the entire on-screen keyboard.\n * @returns {Element} The element containing the entire on-screen keyboard.\n */\n this.getElement = function() {\n return keyboard;\n };\n\n /**\n * Resizes all elements within this Guacamole.OnScreenKeyboard such that\n * the width is close to but does not exceed the specified width. The\n * height of the keyboard is determined based on the width.\n *\n * @param {Number} width The width to resize this Guacamole.OnScreenKeyboard\n * to, in pixels.\n */\n this.resize = function(width) {\n\n // Get pixel size of a unit\n var unit = Math.floor(width * 10 / osk.layout.width) / 10;\n\n // Resize all scaled elements\n for (var i=0; i} keys\n * A mapping of key name to key definition, where the key definition is\n * the title of the key (a string), the keysym (a number), a single\n * Key object, or an array of Key objects.\n *\n * @returns {Object.}\n * A more-predictable mapping of key name to key definition, where the\n * key definition is always simply an array of Key objects.\n */\n var getKeys = function getKeys(keys) {\n\n var keyArrays = {};\n\n // Coerce all keys into individual key arrays\n for (var name in layout.keys) {\n keyArrays[name] = asKeyArray(name, keys[name]);\n }\n\n return keyArrays;\n\n };\n\n /**\n * Map of all key names to their corresponding set of keys. Each key name\n * may correspond to multiple keys due to the effect of modifiers.\n *\n * @type {Object.}\n */\n this.keys = getKeys(layout.keys);\n\n /**\n * Given an arbitrary string representing the name of some component of the\n * on-screen keyboard, returns a string formatted for use as a CSS class\n * name. The result will be lowercase. Word boundaries previously denoted\n * by CamelCase will be replaced by individual hyphens, as will all\n * contiguous non-alphanumeric characters.\n *\n * @private\n * @param {String} name\n * An arbitrary string representing the name of some component of the\n * on-screen keyboard.\n *\n * @returns {String}\n * A string formatted for use as a CSS class name.\n */\n var getCSSName = function getCSSName(name) {\n\n // Convert name from possibly-CamelCase to hyphenated lowercase\n var cssName = name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[^A-Za-z0-9]+/g, '-')\n .toLowerCase();\n\n return cssName;\n\n };\n\n /**\n * Appends DOM elements to the given element as dictated by the layout\n * structure object provided. If a name is provided, an additional CSS\n * class, prepended with \"guac-keyboard-\", will be added to the top-level\n * element.\n *\n * If the layout structure object is an array, all elements within that\n * array will be recursively appended as children of a group, and the\n * top-level element will be given the CSS class \"guac-keyboard-group\".\n *\n * If the layout structure object is an object, all properties within that\n * object will be recursively appended as children of a group, and the\n * top-level element will be given the CSS class \"guac-keyboard-group\". The\n * name of each property will be applied as the name of each child object\n * for the sake of CSS. Each property will be added in sorted order.\n *\n * If the layout structure object is a string, the key having that name\n * will be appended. The key will be given the CSS class\n * \"guac-keyboard-key\" and \"guac-keyboard-key-NAME\", where NAME is the name\n * of the key. If the name of the key is a single character, this will\n * first be transformed into the C-style hexadecimal literal for the\n * Unicode codepoint of that character. For example, the key \"A\" would\n * become \"guac-keyboard-key-0x41\".\n *\n * If the layout structure object is a number, a gap of that size will be\n * inserted. The gap will be given the CSS class \"guac-keyboard-gap\", and\n * will be scaled according to the same size units as each key.\n *\n * @private\n * @param {Element} element\n * The element to append elements to.\n *\n * @param {Array|Object|String|Number} object\n * The layout structure object to use when constructing the elements to\n * append.\n *\n * @param {String} [name]\n * The name of the top-level element being appended, if any.\n */\n var appendElements = function appendElements(element, object, name) {\n\n var i;\n\n // Create div which will become the group or key\n var div = document.createElement('div');\n\n // Add class based on name, if name given\n if (name)\n addClass(div, 'guac-keyboard-' + getCSSName(name));\n\n // If an array, append each element\n if (object instanceof Array) {\n\n // Add group class\n addClass(div, 'guac-keyboard-group');\n\n // Append all elements of array\n for (i=0; i < object.length; i++)\n appendElements(div, object[i]);\n\n }\n\n // If an object, append each property value\n else if (object instanceof Object) {\n\n // Add group class\n addClass(div, 'guac-keyboard-group');\n\n // Append all children, sorted by name\n var names = Object.keys(object).sort();\n for (i=0; i < names.length; i++) {\n var name = names[i];\n appendElements(div, object[name], name);\n }\n\n }\n\n // If a number, create as a gap\n else if (typeof object === 'number') {\n\n // Add gap class\n addClass(div, 'guac-keyboard-gap');\n\n // Maintain scale\n scaledElements.push(new ScaledElement(div, object, object));\n\n }\n\n // If a string, create as a key\n else if (typeof object === 'string') {\n\n // If key name is only one character, use codepoint for name\n var keyName = object;\n if (keyName.length === 1)\n keyName = '0x' + keyName.charCodeAt(0).toString(16);\n\n // Add key container class\n addClass(div, 'guac-keyboard-key-container');\n\n // Create key element which will contain all possible caps\n var keyElement = document.createElement('div');\n keyElement.className = 'guac-keyboard-key '\n + 'guac-keyboard-key-' + getCSSName(keyName);\n\n // Add all associated keys as caps within DOM\n var keys = osk.keys[object];\n if (keys) {\n for (i=0; i < keys.length; i++) {\n\n // Get current key\n var key = keys[i];\n\n // Create cap element for key\n var capElement = document.createElement('div');\n capElement.className = 'guac-keyboard-cap';\n capElement.textContent = key.title;\n\n // Add classes for any requirements\n for (var j=0; j < key.requires.length; j++) {\n var requirement = key.requires[j];\n addClass(capElement, 'guac-keyboard-requires-' + getCSSName(requirement));\n addClass(keyElement, 'guac-keyboard-uses-' + getCSSName(requirement));\n }\n\n // Add cap to key within DOM\n keyElement.appendChild(capElement);\n\n }\n }\n\n // Add key to DOM, maintain scale\n div.appendChild(keyElement);\n scaledElements.push(new ScaledElement(div, osk.layout.keyWidths[object] || 1, 1, true));\n\n /**\n * Handles a touch event which results in the pressing of an OSK\n * key. Touch events will result in mouse events being ignored for\n * touchMouseThreshold events.\n *\n * @private\n * @param {TouchEvent} e\n * The touch event being handled.\n */\n var touchPress = function touchPress(e) {\n e.preventDefault();\n ignoreMouse = osk.touchMouseThreshold;\n press(object, keyElement);\n };\n\n /**\n * Handles a touch event which results in the release of an OSK\n * key. Touch events will result in mouse events being ignored for\n * touchMouseThreshold events.\n *\n * @private\n * @param {TouchEvent} e\n * The touch event being handled.\n */\n var touchRelease = function touchRelease(e) {\n e.preventDefault();\n ignoreMouse = osk.touchMouseThreshold;\n release(object, keyElement);\n };\n\n /**\n * Handles a mouse event which results in the pressing of an OSK\n * key. If mouse events are currently being ignored, this handler\n * does nothing.\n *\n * @private\n * @param {MouseEvent} e\n * The touch event being handled.\n */\n var mousePress = function mousePress(e) {\n e.preventDefault();\n if (ignoreMouse === 0)\n press(object, keyElement);\n };\n\n /**\n * Handles a mouse event which results in the release of an OSK\n * key. If mouse events are currently being ignored, this handler\n * does nothing.\n *\n * @private\n * @param {MouseEvent} e\n * The touch event being handled.\n */\n var mouseRelease = function mouseRelease(e) {\n e.preventDefault();\n if (ignoreMouse === 0)\n release(object, keyElement);\n };\n\n // Handle touch events on key\n keyElement.addEventListener(\"touchstart\", touchPress, true);\n keyElement.addEventListener(\"touchend\", touchRelease, true);\n\n // Handle mouse events on key\n keyElement.addEventListener(\"mousedown\", mousePress, true);\n keyElement.addEventListener(\"mouseup\", mouseRelease, true);\n keyElement.addEventListener(\"mouseout\", mouseRelease, true);\n\n } // end if object is key name\n\n // Add newly-created group/key\n element.appendChild(div);\n\n };\n\n // Create keyboard layout in DOM\n appendElements(keyboard, layout.layout);\n\n};\n\n/**\n * Represents an entire on-screen keyboard layout, including all available\n * keys, their behaviors, and their relative position and sizing.\n *\n * @constructor\n * @param {Guacamole.OnScreenKeyboard.Layout|Object} template\n * The object whose identically-named properties will be used to initialize\n * the properties of this layout.\n */\nGuacamole.OnScreenKeyboard.Layout = function(template) {\n\n /**\n * The language of keyboard layout, such as \"en_US\". This property is for\n * informational purposes only, but it is recommend to conform to the\n * [language code]_[country code] format.\n *\n * @type {String}\n */\n this.language = template.language;\n\n /**\n * The type of keyboard layout, such as \"qwerty\". This property is for\n * informational purposes only, and does not conform to any standard.\n *\n * @type {String}\n */\n this.type = template.type;\n\n /**\n * Map of key name to corresponding keysym, title, or key object. If only\n * the keysym or title is provided, the key object will be created\n * implicitly. In all cases, the name property of the key object will be\n * taken from the name given in the mapping.\n *\n * @type {Object.}\n */\n this.keys = template.keys;\n\n /**\n * Arbitrarily nested, arbitrarily grouped key names. The contents of the\n * layout will be traversed to produce an identically-nested grouping of\n * keys in the DOM tree. All strings will be transformed into their\n * corresponding sets of keys, while all objects and arrays will be\n * transformed into named groups and anonymous groups respectively. Any\n * numbers present will be transformed into gaps of that size, scaled\n * according to the same units as each key.\n *\n * @type {Object}\n */\n this.layout = template.layout;\n\n /**\n * The width of the entire keyboard, in arbitrary units. The width of each\n * key is relative to this width, as both width values are assumed to be in\n * the same units. The conversion factor between these units and pixels is\n * derived later via a call to resize() on the Guacamole.OnScreenKeyboard.\n *\n * @type {Number}\n */\n this.width = template.width;\n\n /**\n * The width of each key, in arbitrary units, relative to other keys in\n * this layout. The true pixel size of each key will be determined by the\n * overall size of the keyboard. If not defined here, the width of each\n * key will default to 1.\n *\n * @type {Object.}\n */\n this.keyWidths = template.keyWidths || {};\n\n};\n\n/**\n * Represents a single key, or a single possible behavior of a key. Each key\n * on the on-screen keyboard must have at least one associated\n * Guacamole.OnScreenKeyboard.Key, whether that key is explicitly defined or\n * implied, and may have multiple Guacamole.OnScreenKeyboard.Key if behavior\n * depends on modifier states.\n *\n * @constructor\n * @param {Guacamole.OnScreenKeyboard.Key|Object} template\n * The object whose identically-named properties will be used to initialize\n * the properties of this key.\n *\n * @param {String} [name]\n * The name to use instead of any name provided within the template, if\n * any. If omitted, the name within the template will be used, assuming the\n * template contains a name.\n */\nGuacamole.OnScreenKeyboard.Key = function(template, name) {\n\n /**\n * The unique name identifying this key within the keyboard layout.\n *\n * @type {String}\n */\n this.name = name || template.name;\n\n /**\n * The human-readable title that will be displayed to the user within the\n * key. If not provided, this will be derived from the key name.\n *\n * @type {String}\n */\n this.title = template.title || this.name;\n\n /**\n * The keysym to be pressed/released when this key is pressed/released. If\n * not provided, this will be derived from the title if the title is a\n * single character.\n *\n * @type {Number}\n */\n this.keysym = template.keysym || (function deriveKeysym(title) {\n\n // Do not derive keysym if title is not exactly one character\n if (!title || title.length !== 1)\n return null;\n\n // For characters between U+0000 and U+00FF, the keysym is the codepoint\n var charCode = title.charCodeAt(0);\n if (charCode >= 0x0000 && charCode <= 0x00FF)\n return charCode;\n\n // For characters between U+0100 and U+10FFFF, the keysym is the codepoint or'd with 0x01000000\n if (charCode >= 0x0100 && charCode <= 0x10FFFF)\n return 0x01000000 | charCode;\n\n // Unable to derive keysym\n return null;\n\n })(this.title);\n\n /**\n * The name of the modifier set when the key is pressed and cleared when\n * this key is released, if any. The names of modifiers are distinct from\n * the names of keys; both the \"RightShift\" and \"LeftShift\" keys may set\n * the \"shift\" modifier, for example. By default, the key will affect no\n * modifiers.\n *\n * @type {String}\n */\n this.modifier = template.modifier;\n\n /**\n * An array containing the names of each modifier required for this key to\n * have an effect. For example, a lowercase letter may require nothing,\n * while an uppercase letter would require \"shift\", assuming the Shift key\n * is named \"shift\" within the layout. By default, the key will require\n * no modifiers.\n *\n * @type {String[]}\n */\n this.requires = template.requires || [];\n\n};\n\n\n/**\n * Abstract stream which can receive data.\n *\n * @constructor\n * @param {Guacamole.Client} client The client owning this stream.\n * @param {Number} index The index of this stream.\n */\nGuacamole.OutputStream = function(client, index) {\n\n /**\n * Reference to this stream.\n * @private\n */\n var guac_stream = this;\n\n /**\n * The index of this stream.\n * @type {Number}\n */\n this.index = index;\n\n /**\n * Fired whenever an acknowledgement is received from the server, indicating\n * that a stream operation has completed, or an error has occurred.\n *\n * @event\n * @param {Guacamole.Status} status The status of the operation.\n */\n this.onack = null;\n\n /**\n * Writes the given base64-encoded data to this stream as a blob.\n *\n * @param {String} data The base64-encoded data to send.\n */\n this.sendBlob = function(data) {\n client.sendBlob(guac_stream.index, data);\n };\n\n /**\n * Closes this stream.\n */\n this.sendEnd = function() {\n client.endStream(guac_stream.index);\n };\n\n};\n\n\n/**\n * Simple Guacamole protocol parser that invokes an oninstruction event when\n * full instructions are available from data received via receive().\n *\n * @constructor\n */\nGuacamole.Parser = function() {\n\n /**\n * Reference to this parser.\n * @private\n */\n var parser = this;\n\n /**\n * Current buffer of received data. This buffer grows until a full\n * element is available. After a full element is available, that element\n * is flushed into the element buffer.\n *\n * @private\n */\n var buffer = \"\";\n\n /**\n * Buffer of all received, complete elements. After an entire instruction\n * is read, this buffer is flushed, and a new instruction begins.\n *\n * @private\n */\n var element_buffer = [];\n\n // The location of the last element's terminator\n var element_end = -1;\n\n // Where to start the next length search or the next element\n var start_index = 0;\n\n /**\n * Appends the given instruction data packet to the internal buffer of\n * this Guacamole.Parser, executing all completed instructions at\n * the beginning of this buffer, if any.\n *\n * @param {String} packet The instruction data to receive.\n */\n this.receive = function(packet) {\n\n // Truncate buffer as necessary\n if (start_index > 4096 && element_end >= start_index) {\n\n buffer = buffer.substring(start_index);\n\n // Reset parse relative to truncation\n element_end -= start_index;\n start_index = 0;\n\n }\n\n // Append data to buffer\n buffer += packet;\n\n // While search is within currently received data\n while (element_end < buffer.length) {\n\n // If we are waiting for element data\n if (element_end >= start_index) {\n\n // We now have enough data for the element. Parse.\n var element = buffer.substring(start_index, element_end);\n var terminator = buffer.substring(element_end, element_end+1);\n\n // Add element to array\n element_buffer.push(element);\n\n // If last element, handle instruction\n if (terminator == \";\") {\n\n // Get opcode\n var opcode = element_buffer.shift();\n\n // Call instruction handler.\n if (parser.oninstruction != null)\n parser.oninstruction(opcode, element_buffer);\n\n // Clear elements\n element_buffer.length = 0;\n\n }\n else if (terminator != ',')\n throw new Error(\"Illegal terminator.\");\n\n // Start searching for length at character after\n // element terminator\n start_index = element_end + 1;\n\n }\n\n // Search for end of length\n var length_end = buffer.indexOf(\".\", start_index);\n if (length_end != -1) {\n\n // Parse length\n var length = parseInt(buffer.substring(element_end+1, length_end));\n if (isNaN(length))\n throw new Error(\"Non-numeric character in element length.\");\n\n // Calculate start of element\n start_index = length_end + 1;\n\n // Calculate location of element terminator\n element_end = start_index + length;\n\n }\n\n // If no period yet, continue search when more data\n // is received\n else {\n start_index = buffer.length;\n break;\n }\n\n } // end parse loop\n\n };\n\n /**\n * Fired once for every complete Guacamole instruction received, in order.\n *\n * @event\n * @param {String} opcode The Guacamole instruction opcode.\n * @param {Array} parameters The parameters provided for the instruction,\n * if any.\n */\n this.oninstruction = null;\n\n};\n\n\n/**\n * A description of the format of raw PCM audio, such as that used by\n * Guacamole.RawAudioPlayer and Guacamole.RawAudioRecorder. This object\n * describes the number of bytes per sample, the number of channels, and the\n * overall sample rate.\n *\n * @constructor\n * @param {Guacamole.RawAudioFormat|Object} template\n * The object whose properties should be copied into the corresponding\n * properties of the new Guacamole.RawAudioFormat.\n */\nGuacamole.RawAudioFormat = function RawAudioFormat(template) {\n\n /**\n * The number of bytes in each sample of audio data. This value is\n * independent of the number of channels.\n *\n * @type {Number}\n */\n this.bytesPerSample = template.bytesPerSample;\n\n /**\n * The number of audio channels (ie: 1 for mono, 2 for stereo).\n *\n * @type {Number}\n */\n this.channels = template.channels;\n\n /**\n * The number of samples per second, per channel.\n *\n * @type {Number}\n */\n this.rate = template.rate;\n\n};\n\n/**\n * Parses the given mimetype, returning a new Guacamole.RawAudioFormat\n * which describes the type of raw audio data represented by that mimetype. If\n * the mimetype is not a supported raw audio data mimetype, null is returned.\n *\n * @param {String} mimetype\n * The audio mimetype to parse.\n *\n * @returns {Guacamole.RawAudioFormat}\n * A new Guacamole.RawAudioFormat which describes the type of raw\n * audio data represented by the given mimetype, or null if the given\n * mimetype is not supported.\n */\nGuacamole.RawAudioFormat.parse = function parseFormat(mimetype) {\n\n var bytesPerSample;\n\n // Rate is absolutely required - if null is still present later, the\n // mimetype must not be supported\n var rate = null;\n\n // Default for both \"audio/L8\" and \"audio/L16\" is one channel\n var channels = 1;\n\n // \"audio/L8\" has one byte per sample\n if (mimetype.substring(0, 9) === 'audio/L8;') {\n mimetype = mimetype.substring(9);\n bytesPerSample = 1;\n }\n\n // \"audio/L16\" has two bytes per sample\n else if (mimetype.substring(0, 10) === 'audio/L16;') {\n mimetype = mimetype.substring(10);\n bytesPerSample = 2;\n }\n\n // All other types are unsupported\n else\n return null;\n\n // Parse all parameters\n var parameters = mimetype.split(',');\n for (var i = 0; i < parameters.length; i++) {\n\n var parameter = parameters[i];\n\n // All parameters must have an equals sign separating name from value\n var equals = parameter.indexOf('=');\n if (equals === -1)\n return null;\n\n // Parse name and value from parameter string\n var name = parameter.substring(0, equals);\n var value = parameter.substring(equals+1);\n\n // Handle each supported parameter\n switch (name) {\n\n // Number of audio channels\n case 'channels':\n channels = parseInt(value);\n break;\n\n // Sample rate\n case 'rate':\n rate = parseInt(value);\n break;\n\n // All other parameters are unsupported\n default:\n return null;\n\n }\n\n };\n\n // The rate parameter is required\n if (rate === null)\n return null;\n\n // Return parsed format details\n return new Guacamole.RawAudioFormat({\n bytesPerSample : bytesPerSample,\n channels : channels,\n rate : rate\n });\n\n};\n\n\n/**\n * A recording of a Guacamole session. Given a {@link Guacamole.Tunnel}, the\n * Guacamole.SessionRecording automatically handles incoming Guacamole\n * instructions, storing them for playback. Playback of the recording may be\n * controlled through function calls to the Guacamole.SessionRecording, even\n * while the recording has not yet finished being created or downloaded.\n *\n * @constructor\n * @param {Guacamole.Tunnel} tunnel\n * The Guacamole.Tunnel from which the instructions of the recording should\n * be read.\n */\nGuacamole.SessionRecording = function SessionRecording(tunnel) {\n\n /**\n * Reference to this Guacamole.SessionRecording.\n *\n * @private\n * @type {Guacamole.SessionRecording}\n */\n var recording = this;\n\n /**\n * The minimum number of characters which must have been read between\n * keyframes.\n *\n * @private\n * @constant\n * @type {Number}\n */\n var KEYFRAME_CHAR_INTERVAL = 16384;\n\n /**\n * The minimum number of milliseconds which must elapse between keyframes.\n *\n * @private\n * @constant\n * @type {Number}\n */\n var KEYFRAME_TIME_INTERVAL = 5000;\n\n /**\n * All frames parsed from the provided tunnel.\n *\n * @private\n * @type {Guacamole.SessionRecording._Frame[]}\n */\n var frames = [];\n\n /**\n * All instructions which have been read since the last frame was added to\n * the frames array.\n *\n * @private\n * @type {Guacamole.SessionRecording._Frame.Instruction[]}\n */\n var instructions = [];\n\n /**\n * The approximate number of characters which have been read from the\n * provided tunnel since the last frame was flagged for use as a keyframe.\n *\n * @private\n * @type {Number}\n */\n var charactersSinceLastKeyframe = 0;\n\n /**\n * The timestamp of the last frame which was flagged for use as a keyframe.\n * If no timestamp has yet been flagged, this will be 0.\n *\n * @private\n * @type {Number}\n */\n var lastKeyframeTimestamp = 0;\n\n /**\n * Tunnel which feeds arbitrary instructions to the client used by this\n * Guacamole.SessionRecording for playback of the session recording.\n *\n * @private\n * @type {Guacamole.SessionRecording._PlaybackTunnel}\n */\n var playbackTunnel = new Guacamole.SessionRecording._PlaybackTunnel();\n\n /**\n * Guacamole.Client instance used for visible playback of the session\n * recording.\n *\n * @private\n * @type {Guacamole.Client}\n */\n var playbackClient = new Guacamole.Client(playbackTunnel);\n\n /**\n * The current frame rendered within the playback client. If no frame is\n * yet rendered, this will be -1.\n *\n * @private\n * @type {Number}\n */\n var currentFrame = -1;\n\n /**\n * The timestamp of the frame when playback began, in milliseconds. If\n * playback is not in progress, this will be null.\n *\n * @private\n * @type {Number}\n */\n var startVideoTimestamp = null;\n\n /**\n * The real-world timestamp when playback began, in milliseconds. If\n * playback is not in progress, this will be null.\n *\n * @private\n * @type {Number}\n */\n var startRealTimestamp = null;\n\n /**\n * The ID of the timeout which will play the next frame, if playback is in\n * progress. If playback is not in progress, the ID stored here (if any)\n * will not be valid.\n *\n * @private\n * @type {Number}\n */\n var playbackTimeout = null;\n\n // Start playback client connected\n playbackClient.connect();\n\n // Hide cursor unless mouse position is received\n playbackClient.getDisplay().showCursor(false);\n\n // Read instructions from provided tunnel, extracting each frame\n tunnel.oninstruction = function handleInstruction(opcode, args) {\n\n // Store opcode and arguments for received instruction\n var instruction = new Guacamole.SessionRecording._Frame.Instruction(opcode, args.slice());\n instructions.push(instruction);\n charactersSinceLastKeyframe += instruction.getSize();\n\n // Once a sync is received, store all instructions since the last\n // frame as a new frame\n if (opcode === 'sync') {\n\n // Parse frame timestamp from sync instruction\n var timestamp = parseInt(args[0]);\n\n // Add a new frame containing the instructions read since last frame\n var frame = new Guacamole.SessionRecording._Frame(timestamp, instructions);\n frames.push(frame);\n\n // This frame should eventually become a keyframe if enough data\n // has been processed and enough recording time has elapsed, or if\n // this is the absolute first frame\n if (frames.length === 1 || (charactersSinceLastKeyframe >= KEYFRAME_CHAR_INTERVAL\n && timestamp - lastKeyframeTimestamp >= KEYFRAME_TIME_INTERVAL)) {\n frame.keyframe = true;\n lastKeyframeTimestamp = timestamp;\n charactersSinceLastKeyframe = 0;\n }\n\n // Clear set of instructions in preparation for next frame\n instructions = [];\n\n // Notify that additional content is available\n if (recording.onprogress)\n recording.onprogress(recording.getDuration());\n\n }\n\n };\n\n /**\n * Converts the given absolute timestamp to a timestamp which is relative\n * to the first frame in the recording.\n *\n * @private\n * @param {Number} timestamp\n * The timestamp to convert to a relative timestamp.\n *\n * @returns {Number}\n * The difference in milliseconds between the given timestamp and the\n * first frame of the recording, or zero if no frames yet exist.\n */\n var toRelativeTimestamp = function toRelativeTimestamp(timestamp) {\n\n // If no frames yet exist, all timestamps are zero\n if (frames.length === 0)\n return 0;\n\n // Calculate timestamp relative to first frame\n return timestamp - frames[0].timestamp;\n\n };\n\n /**\n * Searches through the given region of frames for the frame having a\n * relative timestamp closest to the timestamp given.\n *\n * @private\n * @param {Number} minIndex\n * The index of the first frame in the region (the frame having the\n * smallest timestamp).\n *\n * @param {Number} maxIndex\n * The index of the last frame in the region (the frame having the\n * largest timestamp).\n *\n * @param {Number} timestamp\n * The relative timestamp to search for, where zero denotes the first\n * frame in the recording.\n *\n * @returns {Number}\n * The index of the frame having a relative timestamp closest to the\n * given value.\n */\n var findFrame = function findFrame(minIndex, maxIndex, timestamp) {\n\n // Do not search if the region contains only one element\n if (minIndex === maxIndex)\n return minIndex;\n\n // Split search region into two halves\n var midIndex = Math.floor((minIndex + maxIndex) / 2);\n var midTimestamp = toRelativeTimestamp(frames[midIndex].timestamp);\n\n // If timestamp is within lesser half, search again within that half\n if (timestamp < midTimestamp && midIndex > minIndex)\n return findFrame(minIndex, midIndex - 1, timestamp);\n\n // If timestamp is within greater half, search again within that half\n if (timestamp > midTimestamp && midIndex < maxIndex)\n return findFrame(midIndex + 1, maxIndex, timestamp);\n\n // Otherwise, we lucked out and found a frame with exactly the\n // desired timestamp\n return midIndex;\n\n };\n\n /**\n * Replays the instructions associated with the given frame, sending those\n * instructions to the playback client.\n *\n * @private\n * @param {Number} index\n * The index of the frame within the frames array which should be\n * replayed.\n */\n var replayFrame = function replayFrame(index) {\n\n var frame = frames[index];\n\n // Replay all instructions within the retrieved frame\n for (var i = 0; i < frame.instructions.length; i++) {\n var instruction = frame.instructions[i];\n playbackTunnel.receiveInstruction(instruction.opcode, instruction.args);\n }\n\n // Store client state if frame is flagged as a keyframe\n if (frame.keyframe && !frame.clientState) {\n playbackClient.exportState(function storeClientState(state) {\n frame.clientState = state;\n });\n }\n\n };\n\n /**\n * Moves the playback position to the given frame, resetting the state of\n * the playback client and replaying frames as necessary.\n *\n * @private\n * @param {Number} index\n * The index of the frame which should become the new playback\n * position.\n */\n var seekToFrame = function seekToFrame(index) {\n\n var startIndex;\n\n // Back up until startIndex represents current state\n for (startIndex = index; startIndex >= 0; startIndex--) {\n\n var frame = frames[startIndex];\n\n // If we've reached the current frame, startIndex represents\n // current state by definition\n if (startIndex === currentFrame)\n break;\n\n // If frame has associated absolute state, make that frame the\n // current state\n if (frame.clientState) {\n playbackClient.importState(frame.clientState);\n break;\n }\n\n }\n\n // Advance to frame index after current state\n startIndex++;\n\n // Replay any applicable incremental frames\n for (; startIndex <= index; startIndex++)\n replayFrame(startIndex);\n\n // Current frame is now at requested index\n currentFrame = index;\n\n // Notify of changes in position\n if (recording.onseek)\n recording.onseek(recording.getPosition());\n\n };\n\n /**\n * Advances playback to the next frame in the frames array and schedules\n * playback of the frame following that frame based on their associated\n * timestamps. If no frames exist after the next frame, playback is paused.\n *\n * @private\n */\n var continuePlayback = function continuePlayback() {\n\n // Advance to next frame\n seekToFrame(currentFrame + 1);\n\n // If frames remain after advancing, schedule next frame\n if (currentFrame + 1 < frames.length) {\n\n // Pull the upcoming frame\n var next = frames[currentFrame + 1];\n\n // Calculate the real timestamp corresponding to when the next\n // frame begins\n var nextRealTimestamp = next.timestamp - startVideoTimestamp + startRealTimestamp;\n\n // Calculate the relative delay between the current time and\n // the next frame start\n var delay = Math.max(nextRealTimestamp - new Date().getTime(), 0);\n\n // Advance to next frame after enough time has elapsed\n playbackTimeout = window.setTimeout(function frameDelayElapsed() {\n continuePlayback();\n }, delay);\n\n }\n\n // Otherwise stop playback\n else\n recording.pause();\n\n };\n\n /**\n * Fired when new frames have become available while the recording is\n * being downloaded.\n *\n * @event\n * @param {Number} duration\n * The new duration of the recording, in milliseconds.\n */\n this.onprogress = null;\n\n /**\n * Fired whenever playback of the recording has started.\n *\n * @event\n */\n this.onplay = null;\n\n /**\n * Fired whenever playback of the recording has been paused. This may\n * happen when playback is explicitly paused with a call to pause(), or\n * when playback is implicitly paused due to reaching the end of the\n * recording.\n *\n * @event\n */\n this.onpause = null;\n\n /**\n * Fired whenever the playback position within the recording changes.\n *\n * @event\n * @param {Number} position\n * The new position within the recording, in milliseconds.\n */\n this.onseek = null;\n\n /**\n * Connects the underlying tunnel, beginning download of the Guacamole\n * session. Playback of the Guacamole session cannot occur until at least\n * one frame worth of instructions has been downloaded.\n *\n * @param {String} data\n * The data to send to the tunnel when connecting.\n */\n this.connect = function connect(data) {\n tunnel.connect(data);\n };\n\n /**\n * Disconnects the underlying tunnel, stopping further download of the\n * Guacamole session.\n */\n this.disconnect = function disconnect() {\n tunnel.disconnect();\n };\n\n /**\n * Returns the underlying display of the Guacamole.Client used by this\n * Guacamole.SessionRecording for playback. The display contains an Element\n * which can be added to the DOM, causing the display (and thus playback of\n * the recording) to become visible.\n *\n * @return {Guacamole.Display}\n * The underlying display of the Guacamole.Client used by this\n * Guacamole.SessionRecording for playback.\n */\n this.getDisplay = function getDisplay() {\n return playbackClient.getDisplay();\n };\n\n /**\n * Returns whether playback is currently in progress.\n *\n * @returns {Boolean}\n * true if playback is currently in progress, false otherwise.\n */\n this.isPlaying = function isPlaying() {\n return !!startVideoTimestamp;\n };\n\n /**\n * Returns the current playback position within the recording, in\n * milliseconds, where zero is the start of the recording.\n *\n * @returns {Number}\n * The current playback position within the recording, in milliseconds.\n */\n this.getPosition = function getPosition() {\n\n // Position is simply zero if playback has not started at all\n if (currentFrame === -1)\n return 0;\n\n // Return current position as a millisecond timestamp relative to the\n // start of the recording\n return toRelativeTimestamp(frames[currentFrame].timestamp);\n\n };\n\n /**\n * Returns the duration of this recording, in milliseconds. If the\n * recording is still being downloaded, this value will gradually increase.\n *\n * @returns {Number}\n * The duration of this recording, in milliseconds.\n */\n this.getDuration = function getDuration() {\n\n // If no frames yet exist, duration is zero\n if (frames.length === 0)\n return 0;\n\n // Recording duration is simply the timestamp of the last frame\n return toRelativeTimestamp(frames[frames.length - 1].timestamp);\n\n };\n\n /**\n * Begins continuous playback of the recording downloaded thus far.\n * Playback of the recording will continue until pause() is invoked or\n * until no further frames exist. Playback is initially paused when a\n * Guacamole.SessionRecording is created, and must be explicitly started\n * through a call to this function. If playback is already in progress,\n * this function has no effect.\n */\n this.play = function play() {\n\n // If playback is not already in progress and frames remain,\n // begin playback\n if (!recording.isPlaying() && currentFrame + 1 < frames.length) {\n\n // Notify that playback is starting\n if (recording.onplay)\n recording.onplay();\n\n // Store timestamp of playback start for relative scheduling of\n // future frames\n var next = frames[currentFrame + 1];\n startVideoTimestamp = next.timestamp;\n startRealTimestamp = new Date().getTime();\n\n // Begin playback of video\n continuePlayback();\n\n }\n\n };\n\n /**\n * Seeks to the given position within the recording. If the recording is\n * currently being played back, playback will continue after the seek is\n * performed. If the recording is currently paused, playback will be\n * paused after the seek is performed.\n *\n * @param {Number} position\n * The position within the recording to seek to, in milliseconds.\n */\n this.seek = function seek(position) {\n\n // Do not seek if no frames exist\n if (frames.length === 0)\n return;\n\n // Pause playback, preserving playback state\n var originallyPlaying = recording.isPlaying();\n recording.pause();\n\n // Perform seek\n seekToFrame(findFrame(0, frames.length - 1, position));\n\n // Restore playback state\n if (originallyPlaying)\n recording.play();\n\n };\n\n /**\n * Pauses playback of the recording, if playback is currently in progress.\n * If playback is not in progress, this function has no effect. Playback is\n * initially paused when a Guacamole.SessionRecording is created, and must\n * be explicitly started through a call to play().\n */\n this.pause = function pause() {\n\n // Stop playback only if playback is in progress\n if (recording.isPlaying()) {\n\n // Notify that playback is stopping\n if (recording.onpause)\n recording.onpause();\n\n // Stop playback\n window.clearTimeout(playbackTimeout);\n startVideoTimestamp = null;\n startRealTimestamp = null;\n\n }\n\n };\n\n this.seekFrame = function seekFrame(offset) {\n if (currentFrame + offset < 0) {\n seekToFrame(0);\n } else if (currentFrame + offset >= frames.length) {\n seekToFrame(frames.length - 1);\n } else {\n seekToFrame(currentFrame + offset);\n }\n };\n};\n\n/**\n * A single frame of Guacamole session data. Each frame is made up of the set\n * of instructions used to generate that frame, and the timestamp as dictated\n * by the \"sync\" instruction terminating the frame. Optionally, a frame may\n * also be associated with a snapshot of Guacamole client state, such that the\n * frame can be rendered without replaying all previous frames.\n *\n * @private\n * @constructor\n * @param {Number} timestamp\n * The timestamp of this frame, as dictated by the \"sync\" instruction which\n * terminates the frame.\n *\n * @param {Guacamole.SessionRecording._Frame.Instruction[]} instructions\n * All instructions which are necessary to generate this frame relative to\n * the previous frame in the Guacamole session.\n */\nGuacamole.SessionRecording._Frame = function _Frame(timestamp, instructions) {\n\n /**\n * Whether this frame should be used as a keyframe if possible. This value\n * is purely advisory. The stored clientState must eventually be manually\n * set for the frame to be used as a keyframe. By default, frames are not\n * keyframes.\n *\n * @type {Boolean}\n * @default false\n */\n this.keyframe = false;\n\n /**\n * The timestamp of this frame, as dictated by the \"sync\" instruction which\n * terminates the frame.\n *\n * @type {Number}\n */\n this.timestamp = timestamp;\n\n /**\n * All instructions which are necessary to generate this frame relative to\n * the previous frame in the Guacamole session.\n *\n * @type {Guacamole.SessionRecording._Frame.Instruction[]}\n */\n this.instructions = instructions;\n\n /**\n * A snapshot of client state after this frame was rendered, as returned by\n * a call to exportState(). If no such snapshot has been taken, this will\n * be null.\n *\n * @type {Object}\n * @default null\n */\n this.clientState = null;\n\n};\n\n/**\n * A Guacamole protocol instruction. Each Guacamole protocol instruction is\n * made up of an opcode and set of arguments.\n *\n * @private\n * @constructor\n * @param {String} opcode\n * The opcode of this Guacamole instruction.\n *\n * @param {String[]} args\n * All arguments associated with this Guacamole instruction.\n */\nGuacamole.SessionRecording._Frame.Instruction = function Instruction(opcode, args) {\n\n /**\n * Reference to this Guacamole.SessionRecording._Frame.Instruction.\n *\n * @private\n * @type {Guacamole.SessionRecording._Frame.Instruction}\n */\n var instruction = this;\n\n /**\n * The opcode of this Guacamole instruction.\n *\n * @type {String}\n */\n this.opcode = opcode;\n\n /**\n * All arguments associated with this Guacamole instruction.\n *\n * @type {String[]}\n */\n this.args = args;\n\n /**\n * Returns the approximate number of characters which make up this\n * instruction. This value is only approximate as it excludes the length\n * prefixes and various delimiters used by the Guacamole protocol; only\n * the content of the opcode and each argument is taken into account.\n *\n * @returns {Number}\n * The approximate size of this instruction, in characters.\n */\n this.getSize = function getSize() {\n\n // Init with length of opcode\n var size = instruction.opcode.length;\n\n // Add length of all arguments\n for (var i = 0; i < instruction.args.length; i++)\n size += instruction.args[i].length;\n\n return size;\n\n };\n\n};\n\n/**\n * A read-only Guacamole.Tunnel implementation which streams instructions\n * received through explicit calls to its receiveInstruction() function.\n *\n * @private\n * @constructor\n * @augments {Guacamole.Tunnel}\n */\nGuacamole.SessionRecording._PlaybackTunnel = function _PlaybackTunnel() {\n\n /**\n * Reference to this Guacamole.SessionRecording._PlaybackTunnel.\n *\n * @private\n * @type {Guacamole.SessionRecording._PlaybackTunnel}\n */\n var tunnel = this;\n\n this.connect = function connect(data) {\n // Do nothing\n };\n\n this.sendMessage = function sendMessage(elements) {\n // Do nothing\n };\n\n this.disconnect = function disconnect() {\n // Do nothing\n };\n\n /**\n * Invokes this tunnel's oninstruction handler, notifying users of this\n * tunnel (such as a Guacamole.Client instance) that an instruction has\n * been received. If the oninstruction handler has not been set, this\n * function has no effect.\n *\n * @param {String} opcode\n * The opcode of the Guacamole instruction.\n *\n * @param {String[]} args\n * All arguments associated with this Guacamole instruction.\n */\n this.receiveInstruction = function receiveInstruction(opcode, args) {\n if (tunnel.oninstruction)\n tunnel.oninstruction(opcode, args);\n };\n\n};\n\n\n/**\n * A Guacamole status. Each Guacamole status consists of a status code, defined\n * by the protocol, and an optional human-readable message, usually only\n * included for debugging convenience.\n *\n * @constructor\n * @param {Number} code\n * The Guacamole status code, as defined by Guacamole.Status.Code.\n *\n * @param {String} [message]\n * An optional human-readable message.\n */\nGuacamole.Status = function(code, message) {\n\n /**\n * Reference to this Guacamole.Status.\n * @private\n */\n var guac_status = this;\n\n /**\n * The Guacamole status code.\n * @see Guacamole.Status.Code\n * @type {Number}\n */\n this.code = code;\n\n /**\n * An arbitrary human-readable message associated with this status, if any.\n * The human-readable message is not required, and is generally provided\n * for debugging purposes only. For user feedback, it is better to translate\n * the Guacamole status code into a message.\n *\n * @type {String}\n */\n this.message = message;\n\n /**\n * Returns whether this status represents an error.\n * @returns {Boolean} true if this status represents an error, false\n * otherwise.\n */\n this.isError = function() {\n return guac_status.code < 0 || guac_status.code > 0x00FF;\n };\n\n};\n\n/**\n * Enumeration of all Guacamole status codes.\n */\nGuacamole.Status.Code = {\n\n /**\n * The operation succeeded.\n *\n * @type {Number}\n */\n \"SUCCESS\": 0x0000,\n\n /**\n * The requested operation is unsupported.\n *\n * @type {Number}\n */\n \"UNSUPPORTED\": 0x0100,\n\n /**\n * The operation could not be performed due to an internal failure.\n *\n * @type {Number}\n */\n \"SERVER_ERROR\": 0x0200,\n\n /**\n * The operation could not be performed as the server is busy.\n *\n * @type {Number}\n */\n \"SERVER_BUSY\": 0x0201,\n\n /**\n * The operation could not be performed because the upstream server is not\n * responding.\n *\n * @type {Number}\n */\n \"UPSTREAM_TIMEOUT\": 0x0202,\n\n /**\n * The operation was unsuccessful due to an error or otherwise unexpected\n * condition of the upstream server.\n *\n * @type {Number}\n */\n \"UPSTREAM_ERROR\": 0x0203,\n\n /**\n * The operation could not be performed as the requested resource does not\n * exist.\n *\n * @type {Number}\n */\n \"RESOURCE_NOT_FOUND\": 0x0204,\n\n /**\n * The operation could not be performed as the requested resource is\n * already in use.\n *\n * @type {Number}\n */\n \"RESOURCE_CONFLICT\": 0x0205,\n\n /**\n * The operation could not be performed as the requested resource is now\n * closed.\n *\n * @type {Number}\n */\n \"RESOURCE_CLOSED\": 0x0206,\n\n /**\n * The operation could not be performed because the upstream server does\n * not appear to exist.\n *\n * @type {Number}\n */\n \"UPSTREAM_NOT_FOUND\": 0x0207,\n\n /**\n * The operation could not be performed because the upstream server is not\n * available to service the request.\n *\n * @type {Number}\n */\n \"UPSTREAM_UNAVAILABLE\": 0x0208,\n\n /**\n * The session within the upstream server has ended because it conflicted\n * with another session.\n *\n * @type {Number}\n */\n \"SESSION_CONFLICT\": 0x0209,\n\n /**\n * The session within the upstream server has ended because it appeared to\n * be inactive.\n *\n * @type {Number}\n */\n \"SESSION_TIMEOUT\": 0x020A,\n\n /**\n * The session within the upstream server has been forcibly terminated.\n *\n * @type {Number}\n */\n \"SESSION_CLOSED\": 0x020B,\n\n /**\n * The operation could not be performed because bad parameters were given.\n *\n * @type {Number}\n */\n \"CLIENT_BAD_REQUEST\": 0x0300,\n\n /**\n * Permission was denied to perform the operation, as the user is not yet\n * authorized (not yet logged in, for example).\n *\n * @type {Number}\n */\n \"CLIENT_UNAUTHORIZED\": 0x0301,\n\n /**\n * Permission was denied to perform the operation, and this permission will\n * not be granted even if the user is authorized.\n *\n * @type {Number}\n */\n \"CLIENT_FORBIDDEN\": 0x0303,\n\n /**\n * The client took too long to respond.\n *\n * @type {Number}\n */\n \"CLIENT_TIMEOUT\": 0x0308,\n\n /**\n * The client sent too much data.\n *\n * @type {Number}\n */\n \"CLIENT_OVERRUN\": 0x030D,\n\n /**\n * The client sent data of an unsupported or unexpected type.\n *\n * @type {Number}\n */\n \"CLIENT_BAD_TYPE\": 0x030F,\n\n /**\n * The operation failed because the current client is already using too\n * many resources.\n *\n * @type {Number}\n */\n \"CLIENT_TOO_MANY\": 0x031D\n\n};\n\n\n/**\n * A reader which automatically handles the given input stream, returning\n * strictly text data. Note that this object will overwrite any installed event\n * handlers on the given Guacamole.InputStream.\n *\n * @constructor\n * @param {Guacamole.InputStream} stream The stream that data will be read\n * from.\n */\nGuacamole.StringReader = function(stream) {\n\n /**\n * Reference to this Guacamole.InputStream.\n * @private\n */\n var guac_reader = this;\n\n /**\n * Wrapped Guacamole.ArrayBufferReader.\n * @private\n * @type {Guacamole.ArrayBufferReader}\n */\n var array_reader = new Guacamole.ArrayBufferReader(stream);\n\n /**\n * The number of bytes remaining for the current codepoint.\n *\n * @private\n * @type {Number}\n */\n var bytes_remaining = 0;\n\n /**\n * The current codepoint value, as calculated from bytes read so far.\n *\n * @private\n * @type {Number}\n */\n var codepoint = 0;\n\n /**\n * Decodes the given UTF-8 data into a Unicode string. The data may end in\n * the middle of a multibyte character.\n *\n * @private\n * @param {ArrayBuffer} buffer Arbitrary UTF-8 data.\n * @return {String} A decoded Unicode string.\n */\n function __decode_utf8(buffer) {\n\n var text = \"\";\n\n var bytes = new Uint8Array(buffer);\n for (var i=0; i= buffer.length) {\n var new_buffer = new Uint8Array((length+bytes)*2);\n new_buffer.set(buffer);\n buffer = new_buffer;\n }\n\n length += bytes;\n\n }\n\n /**\n * Appends a single Unicode character to the current buffer, resizing the\n * buffer if necessary. The character will be encoded as UTF-8.\n *\n * @private\n * @param {Number} codepoint The codepoint of the Unicode character to\n * append.\n */\n function __append_utf8(codepoint) {\n\n var mask;\n var bytes;\n\n // 1 byte\n if (codepoint <= 0x7F) {\n mask = 0x00;\n bytes = 1;\n }\n\n // 2 byte\n else if (codepoint <= 0x7FF) {\n mask = 0xC0;\n bytes = 2;\n }\n\n // 3 byte\n else if (codepoint <= 0xFFFF) {\n mask = 0xE0;\n bytes = 3;\n }\n\n // 4 byte\n else if (codepoint <= 0x1FFFFF) {\n mask = 0xF0;\n bytes = 4;\n }\n\n // If invalid codepoint, append replacement character\n else {\n __append_utf8(0xFFFD);\n return;\n }\n\n // Offset buffer by size\n __expand(bytes);\n var offset = length - 1;\n\n // Add trailing bytes, if any\n for (var i=1; i>= 6;\n }\n\n // Set initial byte\n buffer[offset] = mask | codepoint;\n\n }\n\n /**\n * Encodes the given string as UTF-8, returning an ArrayBuffer containing\n * the resulting bytes.\n *\n * @private\n * @param {String} text The string to encode as UTF-8.\n * @return {Uint8Array} The encoded UTF-8 data.\n */\n function __encode_utf8(text) {\n\n // Fill buffer with UTF-8\n for (var i=0; i 0) {\n var out_buffer = buffer.subarray(0, length);\n length = 0;\n return out_buffer;\n }\n\n }\n\n /**\n * Sends the given text.\n *\n * @param {String} text The text to send.\n */\n this.sendText = function(text) {\n if (text.length)\n array_writer.sendData(__encode_utf8(text));\n };\n\n /**\n * Signals that no further text will be sent, effectively closing the\n * stream.\n */\n this.sendEnd = function() {\n array_writer.sendEnd();\n };\n\n /**\n * Fired for received data, if acknowledged by the server.\n * @event\n * @param {Guacamole.Status} status The status of the operation.\n */\n this.onack = null;\n\n};\n\n/**\n * Core object providing abstract communication for Guacamole. This object\n * is a null implementation whose functions do nothing. Guacamole applications\n * should use {@link Guacamole.HTTPTunnel} instead, or implement their own tunnel based\n * on this one.\n *\n * @constructor\n * @see Guacamole.HTTPTunnel\n */\nGuacamole.Tunnel = function() {\n\n /**\n * Connect to the tunnel with the given optional data. This data is\n * typically used for authentication. The format of data accepted is\n * up to the tunnel implementation.\n *\n * @param {String} data The data to send to the tunnel when connecting.\n */\n this.connect = function(data) {};\n\n /**\n * Disconnect from the tunnel.\n */\n this.disconnect = function() {};\n\n /**\n * Send the given message through the tunnel to the service on the other\n * side. All messages are guaranteed to be received in the order sent.\n *\n * @param {...*} elements\n * The elements of the message to send to the service on the other side\n * of the tunnel.\n */\n this.sendMessage = function(elements) {};\n\n /**\n * The current state of this tunnel.\n *\n * @type {Number}\n */\n this.state = Guacamole.Tunnel.State.CONNECTING;\n\n /**\n * The maximum amount of time to wait for data to be received, in\n * milliseconds. If data is not received within this amount of time,\n * the tunnel is closed with an error. The default value is 15000.\n *\n * @type {Number}\n */\n this.receiveTimeout = 15000;\n\n /**\n * The UUID uniquely identifying this tunnel. If not yet known, this will\n * be null.\n *\n * @type {String}\n */\n this.uuid = null;\n\n /**\n * Fired whenever an error is encountered by the tunnel.\n *\n * @event\n * @param {Guacamole.Status} status A status object which describes the\n * error.\n */\n this.onerror = null;\n\n /**\n * Fired whenever the state of the tunnel changes.\n *\n * @event\n * @param {Number} state The new state of the client.\n */\n this.onstatechange = null;\n\n /**\n * Fired once for every complete Guacamole instruction received, in order.\n *\n * @event\n * @param {String} opcode The Guacamole instruction opcode.\n * @param {Array} parameters The parameters provided for the instruction,\n * if any.\n */\n this.oninstruction = null;\n\n};\n\n/**\n * The Guacamole protocol instruction opcode reserved for arbitrary internal\n * use by tunnel implementations. The value of this opcode is guaranteed to be\n * the empty string (\"\"). Tunnel implementations may use this opcode for any\n * purpose. It is currently used by the HTTP tunnel to mark the end of the HTTP\n * response, and by the WebSocket tunnel to transmit the tunnel UUID.\n *\n * @constant\n * @type {String}\n */\nGuacamole.Tunnel.INTERNAL_DATA_OPCODE = '';\n\n/**\n * All possible tunnel states.\n */\nGuacamole.Tunnel.State = {\n\n /**\n * A connection is in pending. It is not yet known whether connection was\n * successful.\n *\n * @type {Number}\n */\n \"CONNECTING\": 0,\n\n /**\n * Connection was successful, and data is being received.\n *\n * @type {Number}\n */\n \"OPEN\": 1,\n\n /**\n * The connection is closed. Connection may not have been successful, the\n * tunnel may have been explicitly closed by either side, or an error may\n * have occurred.\n *\n * @type {Number}\n */\n \"CLOSED\": 2\n\n};\n\n/**\n * Guacamole Tunnel implemented over WebSocket via XMLHttpRequest.\n *\n * @constructor\n * @augments Guacamole.Tunnel\n * @param {String} tunnelURL The URL of the WebSocket tunneling service.\n */\nGuacamole.WebSocketTunnel = function(tunnelURL) {\n\n /**\n * Reference to this WebSocket tunnel.\n * @private\n */\n var tunnel = this;\n\n /**\n * The WebSocket used by this tunnel.\n * @private\n */\n var socket = null;\n\n /**\n * The current receive timeout ID, if any.\n * @private\n */\n var receive_timeout = null;\n\n /**\n * The WebSocket protocol corresponding to the protocol used for the current\n * location.\n * @private\n */\n var ws_protocol = {\n \"http:\": \"ws:\",\n \"https:\": \"wss:\"\n };\n\n var message = '';\n\n // Transform current URL to WebSocket URL\n\n // If not already a websocket URL\n if ( tunnelURL.substring(0, 3) !== \"ws:\"\n && tunnelURL.substring(0, 4) !== \"wss:\") {\n\n var protocol = ws_protocol[window.location.protocol];\n\n // If absolute URL, convert to absolute WS URL\n if (tunnelURL.substring(0, 1) === \"/\")\n tunnelURL =\n protocol\n + \"//\" + window.location.host\n + tunnelURL;\n\n // Otherwise, construct absolute from relative URL\n else {\n\n // Get path from pathname\n var slash = window.location.pathname.lastIndexOf(\"/\");\n var path = window.location.pathname.substring(0, slash + 1);\n\n // Construct absolute URL\n tunnelURL =\n protocol\n + \"//\" + window.location.host\n + path\n + tunnelURL;\n\n }\n\n }\n\n /**\n * Initiates a timeout which, if data is not received, causes the tunnel\n * to close with an error.\n *\n * @private\n */\n function reset_timeout() {\n\n // Get rid of old timeout (if any)\n window.clearTimeout(receive_timeout);\n\n // Set new timeout\n receive_timeout = window.setTimeout(function () {\n close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_TIMEOUT, \"Server timeout.\"));\n }, tunnel.receiveTimeout);\n\n }\n\n /**\n * Closes this tunnel, signaling the given status and corresponding\n * message, which will be sent to the onerror handler if the status is\n * an error status.\n *\n * @private\n * @param {Guacamole.Status} status The status causing the connection to\n * close;\n */\n function close_tunnel(status) {\n // Ignore if already closed\n if (tunnel.state === Guacamole.Tunnel.State.CLOSED)\n return;\n\n // If connection closed abnormally, signal error.\n if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)\n tunnel.onerror(status);\n\n // Mark as closed\n tunnel.state = Guacamole.Tunnel.State.CLOSED;\n if (tunnel.onstatechange)\n tunnel.onstatechange(tunnel.state);\n\n socket.close();\n\n }\n\n this.sendMessage = function(elements) {\n\n // Do not attempt to send messages if not connected\n if (tunnel.state !== Guacamole.Tunnel.State.OPEN)\n return;\n\n // Do not attempt to send empty messages\n if (arguments.length === 0)\n return;\n\n /**\n * Converts the given value to a length/string pair for use as an\n * element in a Guacamole instruction.\n *\n * @private\n * @param value The value to convert.\n * @return {String} The converted value.\n */\n function getElement(value) {\n var string = new String(value);\n return string.length + \".\" + string;\n }\n\n // Initialized message with first element\n var message = getElement(arguments[0]);\n\n // Append remaining elements\n for (var i=1; i 200_000_000) {\n tunnel.onerror(new Guacamole.Status(9000, \"guac exceeded 200MB. terminating connection.\"));\n tunnel.disconnect();\n return;\n }\n // Parse only the portion of data which is newly received\n if (offset < length) {\n parser.receive(buffer.substring(offset));\n offset = length;\n }\n\n }\n\n // Clean up and close when done\n if (xhr.readyState === 4)\n tunnel.disconnect();\n\n };\n\n // Reset state and close upon error\n xhr.onerror = function httpError() {\n\n // Fail if file could not be downloaded via HTTP\n if (tunnel.onerror)\n tunnel.onerror(new Guacamole.Status(getGuacamoleStatusCode(xhr.status), xhr.statusText));\n\n tunnel.disconnect();\n };\n\n xhr.send(null);\n };\n\n this.disconnect = function disconnect() {\n\n // Abort and dispose of XHR if a request is in progress\n if (xhr) {\n xhr.abort();\n xhr = null;\n }\n\n // Connection is now closed\n setState(Guacamole.Tunnel.State.CLOSED);\n\n };\n\n};\n\nGuacamole.StaticHTTPTunnel.prototype = new Guacamole.Tunnel();\n\n\n/**\n * The unique ID of this version of the Guacamole JavaScript API. This ID will\n * be the version string of the guacamole-common-js Maven project, and can be\n * used in downstream applications as a sanity check that the proper version\n * of the APIs is being used (in case an older version is cached, for example).\n *\n * @type {String}\n */\nGuacamole.API_VERSION = \"0.9.13-incubating\";\n\n\n/**\n * Abstract video player which accepts, queues and plays back arbitrary video\n * data. It is up to implementations of this class to provide some means of\n * handling a provided Guacamole.InputStream and rendering the received data to\n * the provided Guacamole.Display.VisibleLayer. Data received along the\n * provided stream is to be played back immediately.\n *\n * @constructor\n */\nGuacamole.VideoPlayer = function VideoPlayer() {\n\n /**\n * Notifies this Guacamole.VideoPlayer that all video up to the current\n * point in time has been given via the underlying stream, and that any\n * difference in time between queued video data and the current time can be\n * considered latency.\n */\n this.sync = function sync() {\n // Default implementation - do nothing\n };\n\n};\n\n/**\n * Determines whether the given mimetype is supported by any built-in\n * implementation of Guacamole.VideoPlayer, and thus will be properly handled\n * by Guacamole.VideoPlayer.getInstance().\n *\n * @param {String} mimetype\n * The mimetype to check.\n *\n * @returns {Boolean}\n * true if the given mimetype is supported by any built-in\n * Guacamole.VideoPlayer, false otherwise.\n */\nGuacamole.VideoPlayer.isSupportedType = function isSupportedType(mimetype) {\n\n // There are currently no built-in video players (and therefore no\n // supported types)\n return false;\n\n};\n\n/**\n * Returns a list of all mimetypes supported by any built-in\n * Guacamole.VideoPlayer, in rough order of priority. Beware that only the core\n * mimetypes themselves will be listed. Any mimetype parameters, even required\n * ones, will not be included in the list.\n *\n * @returns {String[]}\n * A list of all mimetypes supported by any built-in Guacamole.VideoPlayer,\n * excluding any parameters.\n */\nGuacamole.VideoPlayer.getSupportedTypes = function getSupportedTypes() {\n\n // There are currently no built-in video players (and therefore no\n // supported types)\n return [];\n\n};\n\n/**\n * Returns an instance of Guacamole.VideoPlayer providing support for the given\n * video format. If support for the given video format is not available, null\n * is returned.\n *\n * @param {Guacamole.InputStream} stream\n * The Guacamole.InputStream to read video data from.\n *\n * @param {Guacamole.Display.VisibleLayer} layer\n * The destination layer in which this Guacamole.VideoPlayer should play\n * the received video data.\n *\n * @param {String} mimetype\n * The mimetype of the video data in the provided stream.\n *\n * @return {Guacamole.VideoPlayer}\n * A Guacamole.VideoPlayer instance supporting the given mimetype and\n * reading from the given stream, or null if support for the given mimetype\n * is absent.\n */\nGuacamole.VideoPlayer.getInstance = function getInstance(stream, layer, mimetype) {\n\n // There are currently no built-in video players\n return null;\n\n};\n"], "mappings": "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", "names": ["Guacamole", "stream", "guac_reader", "data", "binary", "arrayBuffer", "bufferView", "i", "guac_writer", "status", "__send_blob", "bytes", "offset", "AudioContext", "e", "mimetype", "format", "context", "nextPacketTime", "reader", "MIN_SPLIT_SIZE", "maxLatency", "SampleArray", "maxSampleValue", "packetQueue", "joinAudioPackets", "packets", "totalLength", "packet", "joined", "splitAudioPacket", "minValue", "optimalSplitLength", "samples", "minSplitSamples", "start", "totalValue", "channel", "pushAudioPacket", "shiftAudioPacket", "toAudioBuffer", "packetTime", "audioBuffer", "audioData", "source", "now", "recorder", "BUFFER_SIZE", "LANCZOS_WINDOW_SIZE", "getUserMedia", "writer", "readSamples", "writtenSamples", "mediaStream", "processor", "sinc", "x", "piX", "lanczos", "a", "interpolateSample", "t", "index", "end", "sum", "toSampleArray", "inSamples", "expectedWrittenSamples", "outSamples", "beginAudioCapture", "stopAudioCapture", "tracks", "length", "blob_builder", "blobs", "guacWriter", "arrayBufferWriter", "slice", "blob", "sliceImplementation", "sliceResult", "readNextChunk", "chunk", "tunnel", "guac_client", "STATE_IDLE", "STATE_CONNECTING", "STATE_WAITING", "STATE_CONNECTED", "STATE_DISCONNECTING", "STATE_DISCONNECTED", "currentState", "currentTimestamp", "pingInterval", "lineCap", "lineJoin", "display", "layers", "audioPlayers", "videoPlayers", "parsers", "streams", "objects", "stream_indices", "output_streams", "setState", "state", "isConnected", "callback", "layersSnapshot", "key", "layer", "canvas", "exportLayer", "getLayerIndex", "importLayer", "getLayer", "parent", "matrix", "width", "height", "pressed", "keysym", "mouseState", "buttonMask", "args", "filename", "name", "message", "code", "getParser", "parser", "layerPropertyHandlers", "value", "instructionHandlers", "parameters", "stream_index", "reason", "y", "radius", "startAngle", "endAngle", "negative", "audioPlayer", "objectIndex", "object", "streamIndex", "channelMask", "r", "g", "b", "srcL", "srcX", "srcY", "srcWidth", "srcHeight", "dstL", "dstX", "dstY", "cap", "join", "thickness", "cursorHotspotX", "cursorHotspotY", "cp1x", "cp1y", "cp2x", "cp2y", "layer_index", "c", "d", "f", "srcLayer", "parent_index", "z", "w", "h", "handler", "timestamp", "function_index", "videoPlayer", "opcode", "src", "dst", "uri", "guac_display", "displayWidth", "displayHeight", "displayScale", "default_layer", "cursor", "bounds", "tasks", "frames", "__flush_frames", "rendered_frames", "frame", "Frame", "Task", "taskHandler", "blocked", "task", "scheduleTask", "buffer", "hotspotX", "hotspotY", "srcx", "srcy", "srcw", "srch", "shown", "element", "image", "url", "duration", "video", "render_callback", "dstLayer", "transferFunction", "mask", "limit", "alpha", "scale", "get_children", "children", "diff", "a_element", "b_element", "position", "draw_layer", "initial_alpha", "child", "div", "__super_resize", "translate", "parent_element", "client", "guac_stream", "guac_pool", "pool", "integer", "guacReader", "stringReader", "json", "text", "guac_keyboard", "KeyEvent", "key_event", "KeydownEvent", "keyCode", "keyIdentifier", "location", "keysym_from_key_identifier", "keysym_from_keycode", "isPrintable", "key_identifier_sane", "prevent_alt", "prevent_ctrl", "recentKeysym", "KeypressEvent", "charCode", "keysym_from_charcode", "KeyupEvent", "eventLog", "keycodeKeysyms", "keyidentifier_keysym", "no_repeat", "last_keydown_result", "key_repeat_timeout", "key_repeat_interval", "get_keysym", "keysyms", "identifier", "shifted", "typedCharacter", "unicodePrefixLocation", "hex", "codepoint", "isControlCharacter", "result", "update_modifier_state", "interpret_events", "handled_event", "interpret_event", "last_event", "release_simulated_altgr", "first", "accepted_events", "defaultPrevented", "getEventLocation", "keydownEvent", "keypressEvent", "keyupEvent", "CANVAS_SIZE_FACTOR", "empty", "pathClosed", "stackSize", "compositeOperation", "resize", "newWidth", "newHeight", "canvasWidth", "canvasHeight", "oldData", "oldDataContext", "oldCompositeOperation", "fitRect", "opBoundX", "opBoundY", "resizeWidth", "resizeHeight", "srcCanvas", "src_pixel", "dst_pixel", "guac_mouse", "ignore_mouse", "scroll_delta", "cancelEvent", "target", "ignorePendingMouseEvents", "mousewheel_handler", "delta", "CSS3_CURSOR_SUPPORTED", "dataURL", "left", "middle", "right", "up", "down", "guac_state", "clientX", "clientY", "documentScrollLeft", "documentScrollTop", "guac_touchpad", "touch_count", "last_touch_x", "last_touch_y", "last_touch_time", "pixels_moved", "touch_buttons", "gesture_in_progress", "click_release_timeout", "time", "button", "starting_touch", "touch", "delta_x", "delta_y", "velocity", "guac_touchscreen", "gesture_start_x", "gesture_start_y", "long_press_timeout", "press_button", "release_button", "click_button", "move_mouse", "finger_moved", "begin_gesture", "end_gesture", "guacObject", "bodyCallbacks", "dequeueBodyCallback", "callbacks", "enqueueBodyCallback", "inputStream", "bodyCallback", "layout", "osk", "modifierKeysyms", "scaledElements", "addClass", "classname", "removeClass", "match", "testClassname", "ignoreMouse", "ScaledElement", "scaleFont", "pixels", "modifiersPressed", "names", "getActiveKey", "keyName", "keys", "candidate", "press", "keyElement", "modifierClass", "getCSSName", "originalKeysym", "keyboard", "release", "unit", "scaledElement", "asKeyArray", "getKeys", "keyArrays", "cssName", "appendElements", "capElement", "j", "requirement", "touchPress", "touchRelease", "mousePress", "mouseRelease", "template", "title", "element_buffer", "element_end", "start_index", "terminator", "length_end", "bytesPerSample", "rate", "channels", "parameter", "equals", "recording", "KEYFRAME_CHAR_INTERVAL", "KEYFRAME_TIME_INTERVAL", "instructions", "charactersSinceLastKeyframe", "lastKeyframeTimestamp", "playbackTunnel", "playbackClient", "currentFrame", "startVideoTimestamp", "startRealTimestamp", "playbackTimeout", "instruction", "toRelativeTimestamp", "findFrame", "minIndex", "maxIndex", "midIndex", "midTimestamp", "replayFrame", "seekToFrame", "startIndex", "continuePlayback", "next", "nextRealTimestamp", "delay", "originallyPlaying", "size", "elements", "guac_status", "array_reader", "bytes_remaining", "__decode_utf8", "array_writer", "__expand", "new_buffer", "__append_utf8", "__encode_utf8", "out_buffer", "tunnelURL", "socket", "receive_timeout", "ws_protocol", "protocol", "slash", "path", "reset_timeout", "close_tunnel", "getElement", "string", "event", "parsePartialMessage", "elementEnd", "lengthEnd", "tunnelChain", "chained_tunnel", "connect_data", "tunnels", "committedTunnel", "attach", "failTunnel", "next_tunnel", "commit_tunnel", "crossDomain", "xhr", "getGuacamoleStatusCode", "httpStatus"] }