You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by mj...@apache.org on 2018/01/14 21:51:06 UTC
[49/51] [partial] guacamole-website git commit: Deploy updated/draft
documentation for 0.9.14.
http://git-wip-us.apache.org/repos/asf/guacamole-website/blob/ee520931/content/doc/0.9.14/guacamole-common-js/BlobWriter.js.html
----------------------------------------------------------------------
diff --git a/content/doc/0.9.14/guacamole-common-js/BlobWriter.js.html b/content/doc/0.9.14/guacamole-common-js/BlobWriter.js.html
new file mode 100644
index 0000000..05f26d5
--- /dev/null
+++ b/content/doc/0.9.14/guacamole-common-js/BlobWriter.js.html
@@ -0,0 +1,306 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: BlobWriter.js</title>
+
+ <script src="scripts/prettify/prettify.js"> </script>
+ <script src="scripts/prettify/lang-css.js"> </script>
+ <!--[if lt IE 9]>
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
+</head>
+
+<body>
+
+<div id="main">
+
+ <h1 class="page-title">Source: BlobWriter.js</h1>
+
+
+
+
+
+
+ <section>
+ <article>
+ <pre class="prettyprint source linenums"><code>/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var Guacamole = Guacamole || {};
+
+/**
+ * A writer which automatically writes to the given output stream with the
+ * contents of provided Blob objects.
+ *
+ * @constructor
+ * @param {Guacamole.OutputStream} stream
+ * The stream that data will be written to.
+ */
+Guacamole.BlobWriter = function BlobWriter(stream) {
+
+ /**
+ * Reference to this Guacamole.BlobWriter.
+ *
+ * @private
+ * @type {Guacamole.BlobWriter}
+ */
+ var guacWriter = this;
+
+ /**
+ * Wrapped Guacamole.ArrayBufferWriter which will be used to send any
+ * provided file data.
+ *
+ * @private
+ * @type {Guacamole.ArrayBufferWriter}
+ */
+ var arrayBufferWriter = new Guacamole.ArrayBufferWriter(stream);
+
+ // Initially, simply call onack for acknowledgements
+ arrayBufferWriter.onack = function(status) {
+ if (guacWriter.onack)
+ guacWriter.onack(status);
+ };
+
+ /**
+ * Browser-independent implementation of Blob.slice() which uses an end
+ * offset to determine the span of the resulting slice, rather than a
+ * length.
+ *
+ * @private
+ * @param {Blob} blob
+ * The Blob to slice.
+ *
+ * @param {Number} start
+ * The starting offset of the slice, in bytes, inclusive.
+ *
+ * @param {Number} end
+ * The ending offset of the slice, in bytes, exclusive.
+ *
+ * @returns {Blob}
+ * A Blob containing the data within the given Blob starting at
+ * <code>start</code> and ending at <code>end - 1</code>.
+ */
+ var slice = function slice(blob, start, end) {
+
+ // Use prefixed implementations if necessary
+ var sliceImplementation = (
+ blob.slice
+ || blob.webkitSlice
+ || blob.mozSlice
+ ).bind(blob);
+
+ var length = end - start;
+
+ // The old Blob.slice() was length-based (not end-based). Try the
+ // length version first, if the two calls are not equivalent.
+ if (length !== end) {
+
+ // If the result of the slice() call matches the expected length,
+ // trust that result. It must be correct.
+ var sliceResult = sliceImplementation(start, length);
+ if (sliceResult.size === length)
+ return sliceResult;
+
+ }
+
+ // Otherwise, use the most-recent standard: end-based slice()
+ return sliceImplementation(start, end);
+
+ };
+
+ /**
+ * Sends the contents of the given blob over the underlying stream.
+ *
+ * @param {Blob} blob
+ * The blob to send.
+ */
+ this.sendBlob = function sendBlob(blob) {
+
+ var offset = 0;
+ var reader = new FileReader();
+
+ /**
+ * Reads the next chunk of the blob provided to
+ * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The chunk itself
+ * is read asynchronously, and will not be available until
+ * reader.onload fires.
+ *
+ * @private
+ */
+ var readNextChunk = function readNextChunk() {
+
+ // If no further chunks remain, inform of completion and stop
+ if (offset >= blob.size) {
+
+ // Fire completion event for completed blob
+ if (guacWriter.oncomplete)
+ guacWriter.oncomplete(blob);
+
+ // No further chunks to read
+ return;
+
+ }
+
+ // Obtain reference to next chunk as a new blob
+ var chunk = slice(blob, offset, offset + arrayBufferWriter.blobLength);
+ offset += arrayBufferWriter.blobLength;
+
+ // Attempt to read the blob contents represented by the blob into
+ // a new array buffer
+ reader.readAsArrayBuffer(chunk);
+
+ };
+
+ // Send each chunk over the stream, continue reading the next chunk
+ reader.onload = function chunkLoadComplete() {
+
+ // Send the successfully-read chunk
+ arrayBufferWriter.sendData(reader.result);
+
+ // Continue sending more chunks after the latest chunk is
+ // acknowledged
+ arrayBufferWriter.onack = function sendMoreChunks(status) {
+
+ if (guacWriter.onack)
+ guacWriter.onack(status);
+
+ // Abort transfer if an error occurs
+ if (status.isError())
+ return;
+
+ // Inform of blob upload progress via progress events
+ if (guacWriter.onprogress)
+ guacWriter.onprogress(blob, offset - arrayBufferWriter.blobLength);
+
+ // Queue the next chunk for reading
+ readNextChunk();
+
+ };
+
+ };
+
+ // If an error prevents further reading, inform of error and stop
+ reader.onerror = function chunkLoadFailed() {
+
+ // Fire error event, including the context of the error
+ if (guacWriter.onerror)
+ guacWriter.onerror(blob, offset, reader.error);
+
+ };
+
+ // Begin reading the first chunk
+ readNextChunk();
+
+ };
+
+ /**
+ * Signals that no further text will be sent, effectively closing the
+ * stream.
+ */
+ this.sendEnd = function sendEnd() {
+ arrayBufferWriter.sendEnd();
+ };
+
+ /**
+ * Fired for received data, if acknowledged by the server.
+ *
+ * @event
+ * @param {Guacamole.Status} status
+ * The status of the operation.
+ */
+ this.onack = null;
+
+ /**
+ * Fired when an error occurs reading a blob passed to
+ * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The transfer for the
+ * the given blob will cease, but the stream will remain open.
+ *
+ * @event
+ * @param {Blob} blob
+ * The blob that was being read when the error occurred.
+ *
+ * @param {Number} offset
+ * The offset of the failed read attempt within the blob, in bytes.
+ *
+ * @param {DOMError} error
+ * The error that occurred.
+ */
+ this.onerror = null;
+
+ /**
+ * Fired for each successfully-read chunk of data as a blob is being sent
+ * via [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}.
+ *
+ * @event
+ * @param {Blob} blob
+ * The blob that is being read.
+ *
+ * @param {Number} offset
+ * The offset of the read that just succeeded.
+ */
+ this.onprogress = null;
+
+ /**
+ * Fired when a blob passed to
+ * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob} has finished being
+ * sent.
+ *
+ * @event
+ * @param {Blob} blob
+ * The blob that was sent.
+ */
+ this.oncomplete = null;
+
+};
+</code></pre>
+ </article>
+ </section>
+
+
+
+
+</div>
+
+<nav>
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li>
<li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR
ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li><
li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna
me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog
ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li><
li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole
.SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol
e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Tue Jan 09 2018 15:51:08 GMT-0800 (PST)
+</footer>
+
+<script> prettyPrint(); </script>
+<script src="scripts/linenumber.js"> </script>
+ <!-- Google Analytics -->
+ <script type="text/javascript">
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-75289145-1', 'auto');
+ ga('send', 'pageview');
+ </script>
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/guacamole-website/blob/ee520931/content/doc/0.9.14/guacamole-common-js/Client.js.html
----------------------------------------------------------------------
diff --git a/content/doc/0.9.14/guacamole-common-js/Client.js.html b/content/doc/0.9.14/guacamole-common-js/Client.js.html
new file mode 100644
index 0000000..338e83b
--- /dev/null
+++ b/content/doc/0.9.14/guacamole-common-js/Client.js.html
@@ -0,0 +1,1716 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: Client.js</title>
+
+ <script src="scripts/prettify/prettify.js"> </script>
+ <script src="scripts/prettify/lang-css.js"> </script>
+ <!--[if lt IE 9]>
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
+</head>
+
+<body>
+
+<div id="main">
+
+ <h1 class="page-title">Source: Client.js</h1>
+
+
+
+
+
+
+ <section>
+ <article>
+ <pre class="prettyprint source linenums"><code>/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var Guacamole = Guacamole || {};
+
+/**
+ * Guacamole protocol client. Given a {@link Guacamole.Tunnel},
+ * automatically handles incoming and outgoing Guacamole instructions via the
+ * provided tunnel, updating its display using one or more canvas elements.
+ *
+ * @constructor
+ * @param {Guacamole.Tunnel} tunnel The tunnel to use to send and receive
+ * Guacamole instructions.
+ */
+Guacamole.Client = function(tunnel) {
+
+ var guac_client = this;
+
+ var STATE_IDLE = 0;
+ var STATE_CONNECTING = 1;
+ var STATE_WAITING = 2;
+ var STATE_CONNECTED = 3;
+ var STATE_DISCONNECTING = 4;
+ var STATE_DISCONNECTED = 5;
+
+ var currentState = STATE_IDLE;
+
+ var currentTimestamp = 0;
+ var pingInterval = null;
+
+ /**
+ * Translation from Guacamole protocol line caps to Layer line caps.
+ * @private
+ */
+ var lineCap = {
+ 0: "butt",
+ 1: "round",
+ 2: "square"
+ };
+
+ /**
+ * Translation from Guacamole protocol line caps to Layer line caps.
+ * @private
+ */
+ var lineJoin = {
+ 0: "bevel",
+ 1: "miter",
+ 2: "round"
+ };
+
+ /**
+ * The underlying Guacamole display.
+ *
+ * @private
+ * @type {Guacamole.Display}
+ */
+ var display = new Guacamole.Display();
+
+ /**
+ * All available layers and buffers
+ *
+ * @private
+ * @type {Object.<Number, (Guacamole.Display.VisibleLayer|Guacamole.Layer)>}
+ */
+ var layers = {};
+
+ /**
+ * All audio players currently in use by the client. Initially, this will
+ * be empty, but audio players may be allocated by the server upon request.
+ *
+ * @private
+ * @type {Object.<Number, Guacamole.AudioPlayer>}
+ */
+ var audioPlayers = {};
+
+ /**
+ * All video players currently in use by the client. Initially, this will
+ * be empty, but video players may be allocated by the server upon request.
+ *
+ * @private
+ * @type {Object.<Number, Guacamole.VideoPlayer>}
+ */
+ var videoPlayers = {};
+
+ // No initial parsers
+ var parsers = [];
+
+ // No initial streams
+ var streams = [];
+
+ /**
+ * All current objects. The index of each object is dictated by the
+ * Guacamole server.
+ *
+ * @private
+ * @type {Guacamole.Object[]}
+ */
+ var objects = [];
+
+ // Pool of available stream indices
+ var stream_indices = new Guacamole.IntegerPool();
+
+ // Array of allocated output streams by index
+ var output_streams = [];
+
+ function setState(state) {
+ if (state != currentState) {
+ currentState = state;
+ if (guac_client.onstatechange)
+ guac_client.onstatechange(currentState);
+ }
+ }
+
+ function isConnected() {
+ return currentState == STATE_CONNECTED
+ || currentState == STATE_WAITING;
+ }
+
+ /**
+ * Produces an opaque representation of Guacamole.Client state which can be
+ * later imported through a call to importState(). This object is
+ * effectively an independent, compressed snapshot of protocol and display
+ * state. Invoking this function implicitly flushes the display.
+ *
+ * @param {function} callback
+ * Callback which should be invoked once the state object is ready. The
+ * state object will be passed to the callback as the sole parameter.
+ * This callback may be invoked immediately, or later as the display
+ * finishes rendering and becomes ready.
+ */
+ this.exportState = function exportState(callback) {
+
+ // Start with empty state
+ var state = {
+ 'currentState' : currentState,
+ 'currentTimestamp' : currentTimestamp,
+ 'layers' : {}
+ };
+
+ var layersSnapshot = {};
+
+ // Make a copy of all current layers (protocol state)
+ for (var key in layers) {
+ layersSnapshot[key] = layers[key];
+ }
+
+ // Populate layers once data is available (display state, requires flush)
+ display.flush(function populateLayers() {
+
+ // Export each defined layer/buffer
+ for (var key in layersSnapshot) {
+
+ var index = parseInt(key);
+ var layer = layersSnapshot[key];
+ var canvas = layer.toCanvas();
+
+ // Store layer/buffer dimensions
+ var exportLayer = {
+ 'width' : layer.width,
+ 'height' : layer.height
+ };
+
+ // Store layer/buffer image data, if it can be generated
+ if (layer.width && layer.height)
+ exportLayer.url = canvas.toDataURL('image/png');
+
+ // Add layer properties if not a buffer nor the default layer
+ if (index > 0) {
+ exportLayer.x = layer.x;
+ exportLayer.y = layer.y;
+ exportLayer.z = layer.z;
+ exportLayer.alpha = layer.alpha;
+ exportLayer.matrix = layer.matrix;
+ exportLayer.parent = getLayerIndex(layer.parent);
+ }
+
+ // Store exported layer
+ state.layers[key] = exportLayer;
+
+ }
+
+ // Invoke callback now that the state is ready
+ callback(state);
+
+ });
+
+ };
+
+ /**
+ * Restores Guacamole.Client protocol and display state based on an opaque
+ * object from a prior call to exportState(). The Guacamole.Client instance
+ * used to export that state need not be the same as this instance.
+ *
+ * @param {Object} state
+ * An opaque representation of Guacamole.Client state from a prior call
+ * to exportState().
+ *
+ * @param {function} [callback]
+ * The function to invoke when state has finished being imported. This
+ * may happen immediately, or later as images within the provided state
+ * object are loaded.
+ */
+ this.importState = function importState(state, callback) {
+
+ var key;
+ var index;
+
+ currentState = state.currentState;
+ currentTimestamp = state.currentTimestamp;
+
+ // Dispose of all layers
+ for (key in layers) {
+ index = parseInt(key);
+ if (index > 0)
+ display.dispose(layers[key]);
+ }
+
+ layers = {};
+
+ // Import state of each layer/buffer
+ for (key in state.layers) {
+
+ index = parseInt(key);
+
+ var importLayer = state.layers[key];
+ var layer = getLayer(index);
+
+ // Reset layer size
+ display.resize(layer, importLayer.width, importLayer.height);
+
+ // Initialize new layer if it has associated data
+ if (importLayer.url) {
+ display.setChannelMask(layer, Guacamole.Layer.SRC);
+ display.draw(layer, 0, 0, importLayer.url);
+ }
+
+ // Set layer-specific properties if not a buffer nor the default layer
+ if (index > 0 && importLayer.parent >= 0) {
+
+ // Apply layer position and set parent
+ var parent = getLayer(importLayer.parent);
+ display.move(layer, parent, importLayer.x, importLayer.y, importLayer.z);
+
+ // Set layer transparency
+ display.shade(layer, importLayer.alpha);
+
+ // Apply matrix transform
+ var matrix = importLayer.matrix;
+ display.distort(layer,
+ matrix[0], matrix[1], matrix[2],
+ matrix[3], matrix[4], matrix[5]);
+
+ }
+
+ }
+
+ // Flush changes to display
+ display.flush(callback);
+
+ };
+
+ /**
+ * Returns the underlying display of this Guacamole.Client. The display
+ * contains an Element which can be added to the DOM, causing the
+ * display to become visible.
+ *
+ * @return {Guacamole.Display} The underlying display of this
+ * Guacamole.Client.
+ */
+ this.getDisplay = function() {
+ return display;
+ };
+
+ /**
+ * Sends the current size of the screen.
+ *
+ * @param {Number} width The width of the screen.
+ * @param {Number} height The height of the screen.
+ */
+ this.sendSize = function(width, height) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ tunnel.sendMessage("size", width, height);
+
+ };
+
+ /**
+ * Sends a key event having the given properties as if the user
+ * pressed or released a key.
+ *
+ * @param {Boolean} pressed Whether the key is pressed (true) or released
+ * (false).
+ * @param {Number} keysym The keysym of the key being pressed or released.
+ */
+ this.sendKeyEvent = function(pressed, keysym) {
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ tunnel.sendMessage("key", keysym, pressed);
+ };
+
+ /**
+ * Sends a mouse event having the properties provided by the given mouse
+ * state.
+ *
+ * @param {Guacamole.Mouse.State} mouseState The state of the mouse to send
+ * in the mouse event.
+ */
+ this.sendMouseState = function(mouseState) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ // Update client-side cursor
+ display.moveCursor(
+ Math.floor(mouseState.x),
+ Math.floor(mouseState.y)
+ );
+
+ // Build mask
+ var buttonMask = 0;
+ if (mouseState.left) buttonMask |= 1;
+ if (mouseState.middle) buttonMask |= 2;
+ if (mouseState.right) buttonMask |= 4;
+ if (mouseState.up) buttonMask |= 8;
+ if (mouseState.down) buttonMask |= 16;
+
+ // Send message
+ tunnel.sendMessage("mouse", Math.floor(mouseState.x), Math.floor(mouseState.y), buttonMask);
+ };
+
+ /**
+ * Sets the clipboard of the remote client to the given text data.
+ *
+ * @deprecated Use createClipboardStream() instead.
+ * @param {String} data The data to send as the clipboard contents.
+ */
+ this.setClipboard = function(data) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ // Open stream
+ var stream = guac_client.createClipboardStream("text/plain");
+ var writer = new Guacamole.StringWriter(stream);
+
+ // Send text chunks
+ for (var i=0; i<data.length; i += 4096)
+ writer.sendText(data.substring(i, i+4096));
+
+ // Close stream
+ writer.sendEnd();
+
+ };
+
+ /**
+ * Allocates an available stream index and creates a new
+ * Guacamole.OutputStream using that index, associating the resulting
+ * stream with this Guacamole.Client. Note that this stream will not yet
+ * exist as far as the other end of the Guacamole connection is concerned.
+ * Streams exist within the Guacamole protocol only when referenced by an
+ * instruction which creates the stream, such as a "clipboard", "file", or
+ * "pipe" instruction.
+ *
+ * @returns {Guacamole.OutputStream}
+ * A new Guacamole.OutputStream with a newly-allocated index and
+ * associated with this Guacamole.Client.
+ */
+ this.createOutputStream = function createOutputStream() {
+
+ // Allocate index
+ var index = stream_indices.next();
+
+ // Return new stream
+ var stream = output_streams[index] = new Guacamole.OutputStream(guac_client, index);
+ return stream;
+
+ };
+
+ /**
+ * Opens a new audio stream for writing, where audio data having the give
+ * mimetype will be sent along the returned stream. The instruction
+ * necessary to create this stream will automatically be sent.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data that will be sent along the returned
+ * stream.
+ *
+ * @return {Guacamole.OutputStream}
+ * The created audio stream.
+ */
+ this.createAudioStream = function(mimetype) {
+
+ // Allocate and associate stream with audio metadata
+ var stream = guac_client.createOutputStream();
+ tunnel.sendMessage("audio", stream.index, mimetype);
+ return stream;
+
+ };
+
+ /**
+ * Opens a new file for writing, having the given index, mimetype and
+ * filename. The instruction necessary to create this stream will
+ * automatically be sent.
+ *
+ * @param {String} mimetype The mimetype of the file being sent.
+ * @param {String} filename The filename of the file being sent.
+ * @return {Guacamole.OutputStream} The created file stream.
+ */
+ this.createFileStream = function(mimetype, filename) {
+
+ // Allocate and associate stream with file metadata
+ var stream = guac_client.createOutputStream();
+ tunnel.sendMessage("file", stream.index, mimetype, filename);
+ return stream;
+
+ };
+
+ /**
+ * Opens a new pipe for writing, having the given name and mimetype. The
+ * instruction necessary to create this stream will automatically be sent.
+ *
+ * @param {String} mimetype The mimetype of the data being sent.
+ * @param {String} name The name of the pipe.
+ * @return {Guacamole.OutputStream} The created file stream.
+ */
+ this.createPipeStream = function(mimetype, name) {
+
+ // Allocate and associate stream with pipe metadata
+ var stream = guac_client.createOutputStream();
+ tunnel.sendMessage("pipe", stream.index, mimetype, name);
+ return stream;
+
+ };
+
+ /**
+ * Opens a new clipboard object for writing, having the given mimetype. The
+ * instruction necessary to create this stream will automatically be sent.
+ *
+ * @param {String} mimetype The mimetype of the data being sent.
+ * @param {String} name The name of the pipe.
+ * @return {Guacamole.OutputStream} The created file stream.
+ */
+ this.createClipboardStream = function(mimetype) {
+
+ // Allocate and associate stream with clipboard metadata
+ var stream = guac_client.createOutputStream();
+ tunnel.sendMessage("clipboard", stream.index, mimetype);
+ return stream;
+
+ };
+
+ /**
+ * Creates a new output stream associated with the given object and having
+ * the given mimetype and name. The legality of a mimetype and name is
+ * dictated by the object itself. The instruction necessary to create this
+ * stream will automatically be sent.
+ *
+ * @param {Number} index
+ * The index of the object for which the output stream is being
+ * created.
+ *
+ * @param {String} mimetype
+ * The mimetype of the data which will be sent to the output stream.
+ *
+ * @param {String} name
+ * The defined name of an output stream within the given object.
+ *
+ * @returns {Guacamole.OutputStream}
+ * An output stream which will write blobs to the named output stream
+ * of the given object.
+ */
+ this.createObjectOutputStream = function createObjectOutputStream(index, mimetype, name) {
+
+ // Allocate and ssociate stream with object metadata
+ var stream = guac_client.createOutputStream();
+ tunnel.sendMessage("put", index, stream.index, mimetype, name);
+ return stream;
+
+ };
+
+ /**
+ * Requests read access to the input stream having the given name. If
+ * successful, a new input stream will be created.
+ *
+ * @param {Number} index
+ * The index of the object from which the input stream is being
+ * requested.
+ *
+ * @param {String} name
+ * The name of the input stream to request.
+ */
+ this.requestObjectInputStream = function requestObjectInputStream(index, name) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ tunnel.sendMessage("get", index, name);
+ };
+
+ /**
+ * Acknowledge receipt of a blob on the stream with the given index.
+ *
+ * @param {Number} index The index of the stream associated with the
+ * received blob.
+ * @param {String} message A human-readable message describing the error
+ * or status.
+ * @param {Number} code The error code, if any, or 0 for success.
+ */
+ this.sendAck = function(index, message, code) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ tunnel.sendMessage("ack", index, message, code);
+ };
+
+ /**
+ * Given the index of a file, writes a blob of data to that file.
+ *
+ * @param {Number} index The index of the file to write to.
+ * @param {String} data Base64-encoded data to write to the file.
+ */
+ this.sendBlob = function(index, data) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ tunnel.sendMessage("blob", index, data);
+ };
+
+ /**
+ * Marks a currently-open stream as complete. The other end of the
+ * Guacamole connection will be notified via an "end" instruction that the
+ * stream is closed, and the index will be made available for reuse in
+ * future streams.
+ *
+ * @param {Number} index
+ * The index of the stream to end.
+ */
+ this.endStream = function(index) {
+
+ // Do not send requests if not connected
+ if (!isConnected())
+ return;
+
+ // Explicitly close stream by sending "end" instruction
+ tunnel.sendMessage("end", index);
+
+ // Free associated index and stream if they exist
+ if (output_streams[index]) {
+ stream_indices.free(index);
+ delete output_streams[index];
+ }
+
+ };
+
+ /**
+ * Fired whenever the state of this Guacamole.Client changes.
+ *
+ * @event
+ * @param {Number} state The new state of the client.
+ */
+ this.onstatechange = null;
+
+ /**
+ * Fired when the remote client sends a name update.
+ *
+ * @event
+ * @param {String} name The new name of this client.
+ */
+ this.onname = null;
+
+ /**
+ * Fired when an error is reported by the remote client, and the connection
+ * is being closed.
+ *
+ * @event
+ * @param {Guacamole.Status} status A status object which describes the
+ * error.
+ */
+ this.onerror = null;
+
+ /**
+ * Fired when a audio stream is created. The stream provided to this event
+ * handler will contain its own event handlers for received data.
+ *
+ * @event
+ * @param {Guacamole.InputStream} stream
+ * The stream that will receive audio data from the server.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data which will be received.
+ *
+ * @return {Guacamole.AudioPlayer}
+ * An object which implements the Guacamole.AudioPlayer interface and
+ * has been initialied to play the data in the provided stream, or null
+ * if the built-in audio players of the Guacamole client should be
+ * used.
+ */
+ this.onaudio = null;
+
+ /**
+ * Fired when a video stream is created. The stream provided to this event
+ * handler will contain its own event handlers for received data.
+ *
+ * @event
+ * @param {Guacamole.InputStream} stream
+ * The stream that will receive video data from the server.
+ *
+ * @param {Guacamole.Display.VisibleLayer} layer
+ * The destination layer on which the received video data should be
+ * played. It is the responsibility of the Guacamole.VideoPlayer
+ * implementation to play the received data within this layer.
+ *
+ * @param {String} mimetype
+ * The mimetype of the video data which will be received.
+ *
+ * @return {Guacamole.VideoPlayer}
+ * An object which implements the Guacamole.VideoPlayer interface and
+ * has been initialied to play the data in the provided stream, or null
+ * if the built-in video players of the Guacamole client should be
+ * used.
+ */
+ this.onvideo = null;
+
+ /**
+ * Fired when the clipboard of the remote client is changing.
+ *
+ * @event
+ * @param {Guacamole.InputStream} stream The stream that will receive
+ * clipboard data from the server.
+ * @param {String} mimetype The mimetype of the data which will be received.
+ */
+ this.onclipboard = null;
+
+ /**
+ * Fired when a file stream is created. The stream provided to this event
+ * handler will contain its own event handlers for received data.
+ *
+ * @event
+ * @param {Guacamole.InputStream} stream The stream that will receive data
+ * from the server.
+ * @param {String} mimetype The mimetype of the file received.
+ * @param {String} filename The name of the file received.
+ */
+ this.onfile = null;
+
+ /**
+ * Fired when a filesystem object is created. The object provided to this
+ * event handler will contain its own event handlers and functions for
+ * requesting and handling data.
+ *
+ * @event
+ * @param {Guacamole.Object} object
+ * The created filesystem object.
+ *
+ * @param {String} name
+ * The name of the filesystem.
+ */
+ this.onfilesystem = null;
+
+ /**
+ * Fired when a pipe stream is created. The stream provided to this event
+ * handler will contain its own event handlers for received data;
+ *
+ * @event
+ * @param {Guacamole.InputStream} stream The stream that will receive data
+ * from the server.
+ * @param {String} mimetype The mimetype of the data which will be received.
+ * @param {String} name The name of the pipe.
+ */
+ this.onpipe = null;
+
+ /**
+ * Fired whenever a sync instruction is received from the server, indicating
+ * that the server is finished processing any input from the client and
+ * has sent any results.
+ *
+ * @event
+ * @param {Number} timestamp The timestamp associated with the sync
+ * instruction.
+ */
+ this.onsync = null;
+
+ /**
+ * Returns the layer with the given index, creating it if necessary.
+ * Positive indices refer to visible layers, an index of zero refers to
+ * the default layer, and negative indices refer to buffers.
+ *
+ * @private
+ * @param {Number} index
+ * The index of the layer to retrieve.
+ *
+ * @return {Guacamole.Display.VisibleLayer|Guacamole.Layer}
+ * The layer having the given index.
+ */
+ var getLayer = function getLayer(index) {
+
+ // Get layer, create if necessary
+ var layer = layers[index];
+ if (!layer) {
+
+ // Create layer based on index
+ if (index === 0)
+ layer = display.getDefaultLayer();
+ else if (index > 0)
+ layer = display.createLayer();
+ else
+ layer = display.createBuffer();
+
+ // Add new layer
+ layers[index] = layer;
+
+ }
+
+ return layer;
+
+ };
+
+ /**
+ * Returns the index passed to getLayer() when the given layer was created.
+ * Positive indices refer to visible layers, an index of zero refers to the
+ * default layer, and negative indices refer to buffers.
+ *
+ * @param {Guacamole.Display.VisibleLayer|Guacamole.Layer} layer
+ * The layer whose index should be determined.
+ *
+ * @returns {Number}
+ * The index of the given layer, or null if no such layer is associated
+ * with this client.
+ */
+ var getLayerIndex = function getLayerIndex(layer) {
+
+ // Avoid searching if there clearly is no such layer
+ if (!layer)
+ return null;
+
+ // Search through each layer, returning the index of the given layer
+ // once found
+ for (var key in layers) {
+ if (layer === layers[key])
+ return parseInt(key);
+ }
+
+ // Otherwise, no such index
+ return null;
+
+ };
+
+ function getParser(index) {
+
+ var parser = parsers[index];
+
+ // If parser not yet created, create it, and tie to the
+ // oninstruction handler of the tunnel.
+ if (parser == null) {
+ parser = parsers[index] = new Guacamole.Parser();
+ parser.oninstruction = tunnel.oninstruction;
+ }
+
+ return parser;
+
+ }
+
+ /**
+ * Handlers for all defined layer properties.
+ * @private
+ */
+ var layerPropertyHandlers = {
+
+ "miter-limit": function(layer, value) {
+ display.setMiterLimit(layer, parseFloat(value));
+ }
+
+ };
+
+ /**
+ * Handlers for all instruction opcodes receivable by a Guacamole protocol
+ * client.
+ * @private
+ */
+ var instructionHandlers = {
+
+ "ack": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var reason = parameters[1];
+ var code = parseInt(parameters[2]);
+
+ // Get stream
+ var stream = output_streams[stream_index];
+ if (stream) {
+
+ // Signal ack if handler defined
+ if (stream.onack)
+ stream.onack(new Guacamole.Status(code, reason));
+
+ // If code is an error, invalidate stream if not already
+ // invalidated by onack handler
+ if (code >= 0x0100 && output_streams[stream_index] === stream) {
+ stream_indices.free(stream_index);
+ delete output_streams[stream_index];
+ }
+
+ }
+
+ },
+
+ "arc": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var x = parseInt(parameters[1]);
+ var y = parseInt(parameters[2]);
+ var radius = parseInt(parameters[3]);
+ var startAngle = parseFloat(parameters[4]);
+ var endAngle = parseFloat(parameters[5]);
+ var negative = parseInt(parameters[6]);
+
+ display.arc(layer, x, y, radius, startAngle, endAngle, negative != 0);
+
+ },
+
+ "audio": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var mimetype = parameters[1];
+
+ // Create stream
+ var stream = streams[stream_index] =
+ new Guacamole.InputStream(guac_client, stream_index);
+
+ // Get player instance via callback
+ var audioPlayer = null;
+ if (guac_client.onaudio)
+ audioPlayer = guac_client.onaudio(stream, mimetype);
+
+ // If unsuccessful, try to use a default implementation
+ if (!audioPlayer)
+ audioPlayer = Guacamole.AudioPlayer.getInstance(stream, mimetype);
+
+ // If we have successfully retrieved an audio player, send success response
+ if (audioPlayer) {
+ audioPlayers[stream_index] = audioPlayer;
+ guac_client.sendAck(stream_index, "OK", 0x0000);
+ }
+
+ // Otherwise, mimetype must be unsupported
+ else
+ guac_client.sendAck(stream_index, "BAD TYPE", 0x030F);
+
+ },
+
+ "blob": function(parameters) {
+
+ // Get stream
+ var stream_index = parseInt(parameters[0]);
+ var data = parameters[1];
+ var stream = streams[stream_index];
+
+ // Write data
+ if (stream && stream.onblob)
+ stream.onblob(data);
+
+ },
+
+ "body" : function handleBody(parameters) {
+
+ // Get object
+ var objectIndex = parseInt(parameters[0]);
+ var object = objects[objectIndex];
+
+ var streamIndex = parseInt(parameters[1]);
+ var mimetype = parameters[2];
+ var name = parameters[3];
+
+ // Create stream if handler defined
+ if (object && object.onbody) {
+ var stream = streams[streamIndex] = new Guacamole.InputStream(guac_client, streamIndex);
+ object.onbody(stream, mimetype, name);
+ }
+
+ // Otherwise, unsupported
+ else
+ guac_client.sendAck(streamIndex, "Receipt of body unsupported", 0x0100);
+
+ },
+
+ "cfill": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var r = parseInt(parameters[2]);
+ var g = parseInt(parameters[3]);
+ var b = parseInt(parameters[4]);
+ var a = parseInt(parameters[5]);
+
+ display.setChannelMask(layer, channelMask);
+ display.fillColor(layer, r, g, b, a);
+
+ },
+
+ "clip": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.clip(layer);
+
+ },
+
+ "clipboard": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var mimetype = parameters[1];
+
+ // Create stream
+ if (guac_client.onclipboard) {
+ var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
+ guac_client.onclipboard(stream, mimetype);
+ }
+
+ // Otherwise, unsupported
+ else
+ guac_client.sendAck(stream_index, "Clipboard unsupported", 0x0100);
+
+ },
+
+ "close": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.close(layer);
+
+ },
+
+ "copy": function(parameters) {
+
+ var srcL = getLayer(parseInt(parameters[0]));
+ var srcX = parseInt(parameters[1]);
+ var srcY = parseInt(parameters[2]);
+ var srcWidth = parseInt(parameters[3]);
+ var srcHeight = parseInt(parameters[4]);
+ var channelMask = parseInt(parameters[5]);
+ var dstL = getLayer(parseInt(parameters[6]));
+ var dstX = parseInt(parameters[7]);
+ var dstY = parseInt(parameters[8]);
+
+ display.setChannelMask(dstL, channelMask);
+ display.copy(srcL, srcX, srcY, srcWidth, srcHeight,
+ dstL, dstX, dstY);
+
+ },
+
+ "cstroke": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var cap = lineCap[parseInt(parameters[2])];
+ var join = lineJoin[parseInt(parameters[3])];
+ var thickness = parseInt(parameters[4]);
+ var r = parseInt(parameters[5]);
+ var g = parseInt(parameters[6]);
+ var b = parseInt(parameters[7]);
+ var a = parseInt(parameters[8]);
+
+ display.setChannelMask(layer, channelMask);
+ display.strokeColor(layer, cap, join, thickness, r, g, b, a);
+
+ },
+
+ "cursor": function(parameters) {
+
+ var cursorHotspotX = parseInt(parameters[0]);
+ var cursorHotspotY = parseInt(parameters[1]);
+ var srcL = getLayer(parseInt(parameters[2]));
+ var srcX = parseInt(parameters[3]);
+ var srcY = parseInt(parameters[4]);
+ var srcWidth = parseInt(parameters[5]);
+ var srcHeight = parseInt(parameters[6]);
+
+ display.setCursor(cursorHotspotX, cursorHotspotY,
+ srcL, srcX, srcY, srcWidth, srcHeight);
+
+ },
+
+ "curve": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var cp1x = parseInt(parameters[1]);
+ var cp1y = parseInt(parameters[2]);
+ var cp2x = parseInt(parameters[3]);
+ var cp2y = parseInt(parameters[4]);
+ var x = parseInt(parameters[5]);
+ var y = parseInt(parameters[6]);
+
+ display.curveTo(layer, cp1x, cp1y, cp2x, cp2y, x, y);
+
+ },
+
+ "disconnect" : function handleDisconnect(parameters) {
+
+ // Explicitly tear down connection
+ guac_client.disconnect();
+
+ },
+
+ "dispose": function(parameters) {
+
+ var layer_index = parseInt(parameters[0]);
+
+ // If visible layer, remove from parent
+ if (layer_index > 0) {
+
+ // Remove from parent
+ var layer = getLayer(layer_index);
+ display.dispose(layer);
+
+ // Delete reference
+ delete layers[layer_index];
+
+ }
+
+ // If buffer, just delete reference
+ else if (layer_index < 0)
+ delete layers[layer_index];
+
+ // Attempting to dispose the root layer currently has no effect.
+
+ },
+
+ "distort": function(parameters) {
+
+ var layer_index = parseInt(parameters[0]);
+ var a = parseFloat(parameters[1]);
+ var b = parseFloat(parameters[2]);
+ var c = parseFloat(parameters[3]);
+ var d = parseFloat(parameters[4]);
+ var e = parseFloat(parameters[5]);
+ var f = parseFloat(parameters[6]);
+
+ // Only valid for visible layers (not buffers)
+ if (layer_index >= 0) {
+ var layer = getLayer(layer_index);
+ display.distort(layer, a, b, c, d, e, f);
+ }
+
+ },
+
+ "error": function(parameters) {
+
+ var reason = parameters[0];
+ var code = parseInt(parameters[1]);
+
+ // Call handler if defined
+ if (guac_client.onerror)
+ guac_client.onerror(new Guacamole.Status(code, reason));
+
+ guac_client.disconnect();
+
+ },
+
+ "end": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+
+ // Get stream
+ var stream = streams[stream_index];
+ if (stream) {
+
+ // Signal end of stream if handler defined
+ if (stream.onend)
+ stream.onend();
+
+ // Invalidate stream
+ delete streams[stream_index];
+
+ }
+
+ },
+
+ "file": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var mimetype = parameters[1];
+ var filename = parameters[2];
+
+ // Create stream
+ if (guac_client.onfile) {
+ var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
+ guac_client.onfile(stream, mimetype, filename);
+ }
+
+ // Otherwise, unsupported
+ else
+ guac_client.sendAck(stream_index, "File transfer unsupported", 0x0100);
+
+ },
+
+ "filesystem" : function handleFilesystem(parameters) {
+
+ var objectIndex = parseInt(parameters[0]);
+ var name = parameters[1];
+
+ // Create object, if supported
+ if (guac_client.onfilesystem) {
+ var object = objects[objectIndex] = new Guacamole.Object(guac_client, objectIndex);
+ guac_client.onfilesystem(object, name);
+ }
+
+ // If unsupported, simply ignore the availability of the filesystem
+
+ },
+
+ "identity": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.setTransform(layer, 1, 0, 0, 1, 0, 0);
+
+ },
+
+ "img": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var channelMask = parseInt(parameters[1]);
+ var layer = getLayer(parseInt(parameters[2]));
+ var mimetype = parameters[3];
+ var x = parseInt(parameters[4]);
+ var y = parseInt(parameters[5]);
+
+ // Create stream
+ var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
+ var reader = new Guacamole.DataURIReader(stream, mimetype);
+
+ // Draw image when stream is complete
+ reader.onend = function drawImageBlob() {
+ display.setChannelMask(layer, channelMask);
+ display.draw(layer, x, y, reader.getURI());
+ };
+
+ },
+
+ "jpeg": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var x = parseInt(parameters[2]);
+ var y = parseInt(parameters[3]);
+ var data = parameters[4];
+
+ display.setChannelMask(layer, channelMask);
+ display.draw(layer, x, y, "data:image/jpeg;base64," + data);
+
+ },
+
+ "lfill": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var srcLayer = getLayer(parseInt(parameters[2]));
+
+ display.setChannelMask(layer, channelMask);
+ display.fillLayer(layer, srcLayer);
+
+ },
+
+ "line": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var x = parseInt(parameters[1]);
+ var y = parseInt(parameters[2]);
+
+ display.lineTo(layer, x, y);
+
+ },
+
+ "lstroke": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var srcLayer = getLayer(parseInt(parameters[2]));
+
+ display.setChannelMask(layer, channelMask);
+ display.strokeLayer(layer, srcLayer);
+
+ },
+
+ "mouse" : function handleMouse(parameters) {
+
+ var x = parseInt(parameters[0]);
+ var y = parseInt(parameters[1]);
+
+ // Display and move software cursor to received coordinates
+ display.showCursor(true);
+ display.moveCursor(x, y);
+
+ },
+
+ "move": function(parameters) {
+
+ var layer_index = parseInt(parameters[0]);
+ var parent_index = parseInt(parameters[1]);
+ var x = parseInt(parameters[2]);
+ var y = parseInt(parameters[3]);
+ var z = parseInt(parameters[4]);
+
+ // Only valid for non-default layers
+ if (layer_index > 0 && parent_index >= 0) {
+ var layer = getLayer(layer_index);
+ var parent = getLayer(parent_index);
+ display.move(layer, parent, x, y, z);
+ }
+
+ },
+
+ "name": function(parameters) {
+ if (guac_client.onname) guac_client.onname(parameters[0]);
+ },
+
+ "nest": function(parameters) {
+ var parser = getParser(parseInt(parameters[0]));
+ parser.receive(parameters[1]);
+ },
+
+ "pipe": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var mimetype = parameters[1];
+ var name = parameters[2];
+
+ // Create stream
+ if (guac_client.onpipe) {
+ var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
+ guac_client.onpipe(stream, mimetype, name);
+ }
+
+ // Otherwise, unsupported
+ else
+ guac_client.sendAck(stream_index, "Named pipes unsupported", 0x0100);
+
+ },
+
+ "png": function(parameters) {
+
+ var channelMask = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var x = parseInt(parameters[2]);
+ var y = parseInt(parameters[3]);
+ var data = parameters[4];
+
+ display.setChannelMask(layer, channelMask);
+ display.draw(layer, x, y, "data:image/png;base64," + data);
+
+ },
+
+ "pop": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.pop(layer);
+
+ },
+
+ "push": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.push(layer);
+
+ },
+
+ "rect": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var x = parseInt(parameters[1]);
+ var y = parseInt(parameters[2]);
+ var w = parseInt(parameters[3]);
+ var h = parseInt(parameters[4]);
+
+ display.rect(layer, x, y, w, h);
+
+ },
+
+ "reset": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+
+ display.reset(layer);
+
+ },
+
+ "set": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var name = parameters[1];
+ var value = parameters[2];
+
+ // Call property handler if defined
+ var handler = layerPropertyHandlers[name];
+ if (handler)
+ handler(layer, value);
+
+ },
+
+ "shade": function(parameters) {
+
+ var layer_index = parseInt(parameters[0]);
+ var a = parseInt(parameters[1]);
+
+ // Only valid for visible layers (not buffers)
+ if (layer_index >= 0) {
+ var layer = getLayer(layer_index);
+ display.shade(layer, a);
+ }
+
+ },
+
+ "size": function(parameters) {
+
+ var layer_index = parseInt(parameters[0]);
+ var layer = getLayer(layer_index);
+ var width = parseInt(parameters[1]);
+ var height = parseInt(parameters[2]);
+
+ display.resize(layer, width, height);
+
+ },
+
+ "start": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var x = parseInt(parameters[1]);
+ var y = parseInt(parameters[2]);
+
+ display.moveTo(layer, x, y);
+
+ },
+
+ "sync": function(parameters) {
+
+ var timestamp = parseInt(parameters[0]);
+
+ // Flush display, send sync when done
+ display.flush(function displaySyncComplete() {
+
+ // Synchronize all audio players
+ for (var index in audioPlayers) {
+ var audioPlayer = audioPlayers[index];
+ if (audioPlayer)
+ audioPlayer.sync();
+ }
+
+ // Send sync response to server
+ if (timestamp !== currentTimestamp) {
+ tunnel.sendMessage("sync", timestamp);
+ currentTimestamp = timestamp;
+ }
+
+ });
+
+ // If received first update, no longer waiting.
+ if (currentState === STATE_WAITING)
+ setState(STATE_CONNECTED);
+
+ // Call sync handler if defined
+ if (guac_client.onsync)
+ guac_client.onsync(timestamp);
+
+ },
+
+ "transfer": function(parameters) {
+
+ var srcL = getLayer(parseInt(parameters[0]));
+ var srcX = parseInt(parameters[1]);
+ var srcY = parseInt(parameters[2]);
+ var srcWidth = parseInt(parameters[3]);
+ var srcHeight = parseInt(parameters[4]);
+ var function_index = parseInt(parameters[5]);
+ var dstL = getLayer(parseInt(parameters[6]));
+ var dstX = parseInt(parameters[7]);
+ var dstY = parseInt(parameters[8]);
+
+ /* SRC */
+ if (function_index === 0x3)
+ display.put(srcL, srcX, srcY, srcWidth, srcHeight,
+ dstL, dstX, dstY);
+
+ /* Anything else that isn't a NO-OP */
+ else if (function_index !== 0x5)
+ display.transfer(srcL, srcX, srcY, srcWidth, srcHeight,
+ dstL, dstX, dstY, Guacamole.Client.DefaultTransferFunction[function_index]);
+
+ },
+
+ "transform": function(parameters) {
+
+ var layer = getLayer(parseInt(parameters[0]));
+ var a = parseFloat(parameters[1]);
+ var b = parseFloat(parameters[2]);
+ var c = parseFloat(parameters[3]);
+ var d = parseFloat(parameters[4]);
+ var e = parseFloat(parameters[5]);
+ var f = parseFloat(parameters[6]);
+
+ display.transform(layer, a, b, c, d, e, f);
+
+ },
+
+ "undefine" : function handleUndefine(parameters) {
+
+ // Get object
+ var objectIndex = parseInt(parameters[0]);
+ var object = objects[objectIndex];
+
+ // Signal end of object definition
+ if (object && object.onundefine)
+ object.onundefine();
+
+ },
+
+ "video": function(parameters) {
+
+ var stream_index = parseInt(parameters[0]);
+ var layer = getLayer(parseInt(parameters[1]));
+ var mimetype = parameters[2];
+
+ // Create stream
+ var stream = streams[stream_index] =
+ new Guacamole.InputStream(guac_client, stream_index);
+
+ // Get player instance via callback
+ var videoPlayer = null;
+ if (guac_client.onvideo)
+ videoPlayer = guac_client.onvideo(stream, layer, mimetype);
+
+ // If unsuccessful, try to use a default implementation
+ if (!videoPlayer)
+ videoPlayer = Guacamole.VideoPlayer.getInstance(stream, layer, mimetype);
+
+ // If we have successfully retrieved an video player, send success response
+ if (videoPlayer) {
+ videoPlayers[stream_index] = videoPlayer;
+ guac_client.sendAck(stream_index, "OK", 0x0000);
+ }
+
+ // Otherwise, mimetype must be unsupported
+ else
+ guac_client.sendAck(stream_index, "BAD TYPE", 0x030F);
+
+ }
+
+ };
+
+ tunnel.oninstruction = function(opcode, parameters) {
+
+ var handler = instructionHandlers[opcode];
+ if (handler)
+ handler(parameters);
+
+ };
+
+ /**
+ * Sends a disconnect instruction to the server and closes the tunnel.
+ */
+ this.disconnect = function() {
+
+ // Only attempt disconnection not disconnected.
+ if (currentState != STATE_DISCONNECTED
+ && currentState != STATE_DISCONNECTING) {
+
+ setState(STATE_DISCONNECTING);
+
+ // Stop ping
+ if (pingInterval)
+ window.clearInterval(pingInterval);
+
+ // Send disconnect message and disconnect
+ tunnel.sendMessage("disconnect");
+ tunnel.disconnect();
+ setState(STATE_DISCONNECTED);
+
+ }
+
+ };
+
+ /**
+ * Connects the underlying tunnel of this Guacamole.Client, passing the
+ * given arbitrary data to the tunnel during the connection process.
+ *
+ * @param data Arbitrary connection data to be sent to the underlying
+ * tunnel during the connection process.
+ * @throws {Guacamole.Status} If an error occurs during connection.
+ */
+ this.connect = function(data) {
+
+ setState(STATE_CONNECTING);
+
+ try {
+ tunnel.connect(data);
+ }
+ catch (status) {
+ setState(STATE_IDLE);
+ throw status;
+ }
+
+ // Ping every 5 seconds (ensure connection alive)
+ pingInterval = window.setInterval(function() {
+ tunnel.sendMessage("nop");
+ }, 5000);
+
+ setState(STATE_WAITING);
+ };
+
+};
+
+/**
+ * Map of all Guacamole binary raster operations to transfer functions.
+ * @private
+ */
+Guacamole.Client.DefaultTransferFunction = {
+
+ /* BLACK */
+ 0x0: function (src, dst) {
+ dst.red = dst.green = dst.blue = 0x00;
+ },
+
+ /* WHITE */
+ 0xF: function (src, dst) {
+ dst.red = dst.green = dst.blue = 0xFF;
+ },
+
+ /* SRC */
+ 0x3: function (src, dst) {
+ dst.red = src.red;
+ dst.green = src.green;
+ dst.blue = src.blue;
+ dst.alpha = src.alpha;
+ },
+
+ /* DEST (no-op) */
+ 0x5: function (src, dst) {
+ // Do nothing
+ },
+
+ /* Invert SRC */
+ 0xC: function (src, dst) {
+ dst.red = 0xFF & ~src.red;
+ dst.green = 0xFF & ~src.green;
+ dst.blue = 0xFF & ~src.blue;
+ dst.alpha = src.alpha;
+ },
+
+ /* Invert DEST */
+ 0xA: function (src, dst) {
+ dst.red = 0xFF & ~dst.red;
+ dst.green = 0xFF & ~dst.green;
+ dst.blue = 0xFF & ~dst.blue;
+ },
+
+ /* AND */
+ 0x1: function (src, dst) {
+ dst.red = ( src.red & dst.red);
+ dst.green = ( src.green & dst.green);
+ dst.blue = ( src.blue & dst.blue);
+ },
+
+ /* NAND */
+ 0xE: function (src, dst) {
+ dst.red = 0xFF & ~( src.red & dst.red);
+ dst.green = 0xFF & ~( src.green & dst.green);
+ dst.blue = 0xFF & ~( src.blue & dst.blue);
+ },
+
+ /* OR */
+ 0x7: function (src, dst) {
+ dst.red = ( src.red | dst.red);
+ dst.green = ( src.green | dst.green);
+ dst.blue = ( src.blue | dst.blue);
+ },
+
+ /* NOR */
+ 0x8: function (src, dst) {
+ dst.red = 0xFF & ~( src.red | dst.red);
+ dst.green = 0xFF & ~( src.green | dst.green);
+ dst.blue = 0xFF & ~( src.blue | dst.blue);
+ },
+
+ /* XOR */
+ 0x6: function (src, dst) {
+ dst.red = ( src.red ^ dst.red);
+ dst.green = ( src.green ^ dst.green);
+ dst.blue = ( src.blue ^ dst.blue);
+ },
+
+ /* XNOR */
+ 0x9: function (src, dst) {
+ dst.red = 0xFF & ~( src.red ^ dst.red);
+ dst.green = 0xFF & ~( src.green ^ dst.green);
+ dst.blue = 0xFF & ~( src.blue ^ dst.blue);
+ },
+
+ /* AND inverted source */
+ 0x4: function (src, dst) {
+ dst.red = 0xFF & (~src.red & dst.red);
+ dst.green = 0xFF & (~src.green & dst.green);
+ dst.blue = 0xFF & (~src.blue & dst.blue);
+ },
+
+ /* OR inverted source */
+ 0xD: function (src, dst) {
+ dst.red = 0xFF & (~src.red | dst.red);
+ dst.green = 0xFF & (~src.green | dst.green);
+ dst.blue = 0xFF & (~src.blue | dst.blue);
+ },
+
+ /* AND inverted destination */
+ 0x2: function (src, dst) {
+ dst.red = 0xFF & ( src.red & ~dst.red);
+ dst.green = 0xFF & ( src.green & ~dst.green);
+ dst.blue = 0xFF & ( src.blue & ~dst.blue);
+ },
+
+ /* OR inverted destination */
+ 0xB: function (src, dst) {
+ dst.red = 0xFF & ( src.red | ~dst.red);
+ dst.green = 0xFF & ( src.green | ~dst.green);
+ dst.blue = 0xFF & ( src.blue | ~dst.blue);
+ }
+
+};
+</code></pre>
+ </article>
+ </section>
+
+
+
+
+</div>
+
+<nav>
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li>
<li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR
ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li><
li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna
me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog
ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li><
li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole
.SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol
e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Tue Jan 09 2018 15:51:08 GMT-0800 (PST)
+</footer>
+
+<script> prettyPrint(); </script>
+<script src="scripts/linenumber.js"> </script>
+ <!-- Google Analytics -->
+ <script type="text/javascript">
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-75289145-1', 'auto');
+ ga('send', 'pageview');
+ </script>
+</body>
+</html>