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 2019/01/01 00:29:36 UTC

[30/51] [partial] guacamole-website git commit: Deploy updated/draft documentation for 1.0.0.

http://git-wip-us.apache.org/repos/asf/guacamole-website/blob/8c2fe14e/content/doc/1.0.0/guacamole-common-js/Parser.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/Parser.js.html b/content/doc/1.0.0/guacamole-common-js/Parser.js.html
new file mode 100644
index 0000000..13719a1
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/Parser.js.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>JSDoc: Source: Parser.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: Parser.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 || {};
+
+/**
+ * Simple Guacamole protocol parser that invokes an oninstruction event when
+ * full instructions are available from data received via receive().
+ * 
+ * @constructor
+ */
+Guacamole.Parser = function() {
+
+    /**
+     * Reference to this parser.
+     * @private
+     */
+    var parser = this;
+
+    /**
+     * Current buffer of received data. This buffer grows until a full
+     * element is available. After a full element is available, that element
+     * is flushed into the element buffer.
+     * 
+     * @private
+     */
+    var buffer = "";
+
+    /**
+     * Buffer of all received, complete elements. After an entire instruction
+     * is read, this buffer is flushed, and a new instruction begins.
+     * 
+     * @private
+     */
+    var element_buffer = [];
+
+    // The location of the last element's terminator
+    var element_end = -1;
+
+    // Where to start the next length search or the next element
+    var start_index = 0;
+
+    /**
+     * Appends the given instruction data packet to the internal buffer of
+     * this Guacamole.Parser, executing all completed instructions at
+     * the beginning of this buffer, if any.
+     *
+     * @param {String} packet The instruction data to receive.
+     */
+    this.receive = function(packet) {
+
+        // Truncate buffer as necessary
+        if (start_index > 4096 &amp;&amp; element_end >= start_index) {
+
+            buffer = buffer.substring(start_index);
+
+            // Reset parse relative to truncation
+            element_end -= start_index;
+            start_index = 0;
+
+        }
+
+        // Append data to buffer
+        buffer += packet;
+
+        // While search is within currently received data
+        while (element_end &lt; buffer.length) {
+
+            // If we are waiting for element data
+            if (element_end >= start_index) {
+
+                // We now have enough data for the element. Parse.
+                var element = buffer.substring(start_index, element_end);
+                var terminator = buffer.substring(element_end, element_end+1);
+
+                // Add element to array
+                element_buffer.push(element);
+
+                // If last element, handle instruction
+                if (terminator == ";") {
+
+                    // Get opcode
+                    var opcode = element_buffer.shift();
+
+                    // Call instruction handler.
+                    if (parser.oninstruction != null)
+                        parser.oninstruction(opcode, element_buffer);
+
+                    // Clear elements
+                    element_buffer.length = 0;
+
+                }
+                else if (terminator != ',')
+                    throw new Error("Illegal terminator.");
+
+                // Start searching for length at character after
+                // element terminator
+                start_index = element_end + 1;
+
+            }
+
+            // Search for end of length
+            var length_end = buffer.indexOf(".", start_index);
+            if (length_end != -1) {
+
+                // Parse length
+                var length = parseInt(buffer.substring(element_end+1, length_end));
+                if (isNaN(length))
+                    throw new Error("Non-numeric character in element length.");
+
+                // Calculate start of element
+                start_index = length_end + 1;
+
+                // Calculate location of element terminator
+                element_end = start_index + length;
+
+            }
+            
+            // If no period yet, continue search when more data
+            // is received
+            else {
+                start_index = buffer.length;
+                break;
+            }
+
+        } // end parse loop
+
+    };
+
+    /**
+     * Fired once for every complete Guacamole instruction received, in order.
+     * 
+     * @event
+     * @param {String} opcode The Guacamole instruction opcode.
+     * @param {Array} parameters The parameters provided for the instruction,
+     *                           if any.
+     */
+    this.oninstruction = 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.InputSink.html">InputSink</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><l
 i><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.RawAudioRecorder.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 hre
 f="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">onfilesys
 tem</a></li><li><a href="Guacamole.Client.html#event:onname">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">onprogress</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 h
 ref="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:o
 ninstruction">oninstruction</a></li><li><a href="Guacamole.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 Fri Dec 21 2018 13:47:10 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/8c2fe14e/content/doc/1.0.0/guacamole-common-js/RawAudioFormat.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/RawAudioFormat.js.html b/content/doc/1.0.0/guacamole-common-js/RawAudioFormat.js.html
new file mode 100644
index 0000000..f097f8a
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/RawAudioFormat.js.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>JSDoc: Source: RawAudioFormat.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: RawAudioFormat.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 description of the format of raw PCM audio, such as that used by
+ * Guacamole.RawAudioPlayer and Guacamole.RawAudioRecorder. This object
+ * describes the number of bytes per sample, the number of channels, and the
+ * overall sample rate.
+ *
+ * @constructor
+ * @param {Guacamole.RawAudioFormat|Object} template
+ *     The object whose properties should be copied into the corresponding
+ *     properties of the new Guacamole.RawAudioFormat.
+ */
+Guacamole.RawAudioFormat = function RawAudioFormat(template) {
+
+    /**
+     * The number of bytes in each sample of audio data. This value is
+     * independent of the number of channels.
+     *
+     * @type {Number}
+     */
+    this.bytesPerSample = template.bytesPerSample;
+
+    /**
+     * The number of audio channels (ie: 1 for mono, 2 for stereo).
+     *
+     * @type {Number}
+     */
+    this.channels = template.channels;
+
+    /**
+     * The number of samples per second, per channel.
+     *
+     * @type {Number}
+     */
+    this.rate = template.rate;
+
+};
+
+/**
+ * Parses the given mimetype, returning a new Guacamole.RawAudioFormat
+ * which describes the type of raw audio data represented by that mimetype. If
+ * the mimetype is not a supported raw audio data mimetype, null is returned.
+ *
+ * @param {String} mimetype
+ *     The audio mimetype to parse.
+ *
+ * @returns {Guacamole.RawAudioFormat}
+ *     A new Guacamole.RawAudioFormat which describes the type of raw
+ *     audio data represented by the given mimetype, or null if the given
+ *     mimetype is not supported.
+ */
+Guacamole.RawAudioFormat.parse = function parseFormat(mimetype) {
+
+    var bytesPerSample;
+
+    // Rate is absolutely required - if null is still present later, the
+    // mimetype must not be supported
+    var rate = null;
+
+    // Default for both "audio/L8" and "audio/L16" is one channel
+    var channels = 1;
+
+    // "audio/L8" has one byte per sample
+    if (mimetype.substring(0, 9) === 'audio/L8;') {
+        mimetype = mimetype.substring(9);
+        bytesPerSample = 1;
+    }
+
+    // "audio/L16" has two bytes per sample
+    else if (mimetype.substring(0, 10) === 'audio/L16;') {
+        mimetype = mimetype.substring(10);
+        bytesPerSample = 2;
+    }
+
+    // All other types are unsupported
+    else
+        return null;
+
+    // Parse all parameters
+    var parameters = mimetype.split(',');
+    for (var i = 0; i &lt; parameters.length; i++) {
+
+        var parameter = parameters[i];
+
+        // All parameters must have an equals sign separating name from value
+        var equals = parameter.indexOf('=');
+        if (equals === -1)
+            return null;
+
+        // Parse name and value from parameter string
+        var name  = parameter.substring(0, equals);
+        var value = parameter.substring(equals+1);
+
+        // Handle each supported parameter
+        switch (name) {
+
+            // Number of audio channels
+            case 'channels':
+                channels = parseInt(value);
+                break;
+
+            // Sample rate
+            case 'rate':
+                rate = parseInt(value);
+                break;
+
+            // All other parameters are unsupported
+            default:
+                return null;
+
+        }
+
+    };
+
+    // The rate parameter is required
+    if (rate === null)
+        return null;
+
+    // Return parsed format details
+    return new Guacamole.RawAudioFormat({
+        bytesPerSample : bytesPerSample,
+        channels       : channels,
+        rate           : rate
+    });
+
+};
+</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.InputSink.html">InputSink</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><l
 i><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.RawAudioRecorder.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 hre
 f="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">onfilesys
 tem</a></li><li><a href="Guacamole.Client.html#event:onname">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">onprogress</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 h
 ref="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:o
 ninstruction">oninstruction</a></li><li><a href="Guacamole.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 Fri Dec 21 2018 13:47:10 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/8c2fe14e/content/doc/1.0.0/guacamole-common-js/SessionRecording.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/SessionRecording.js.html b/content/doc/1.0.0/guacamole-common-js/SessionRecording.js.html
new file mode 100644
index 0000000..fc235ef
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/SessionRecording.js.html
@@ -0,0 +1,880 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>JSDoc: Source: SessionRecording.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: SessionRecording.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 recording of a Guacamole session. Given a {@link Guacamole.Tunnel}, the
+ * Guacamole.SessionRecording automatically handles incoming Guacamole
+ * instructions, storing them for playback. Playback of the recording may be
+ * controlled through function calls to the Guacamole.SessionRecording, even
+ * while the recording has not yet finished being created or downloaded.
+ *
+ * @constructor
+ * @param {Guacamole.Tunnel} tunnel
+ *     The Guacamole.Tunnel from which the instructions of the recording should
+ *     be read.
+ */
+Guacamole.SessionRecording = function SessionRecording(tunnel) {
+
+    /**
+     * Reference to this Guacamole.SessionRecording.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording}
+     */
+    var recording = this;
+
+    /**
+     * The minimum number of characters which must have been read between
+     * keyframes.
+     *
+     * @private
+     * @constant
+     * @type {Number}
+     */
+    var KEYFRAME_CHAR_INTERVAL = 16384;
+
+    /**
+     * The minimum number of milliseconds which must elapse between keyframes.
+     *
+     * @private
+     * @constant
+     * @type {Number}
+     */
+    var KEYFRAME_TIME_INTERVAL = 5000;
+
+    /**
+     * The maximum amount of time to spend in any particular seek operation
+     * before returning control to the main thread, in milliseconds. Seek
+     * operations exceeding this amount of time will proceed asynchronously.
+     *
+     * @private
+     * @constant
+     * @type {Number}
+     */
+    var MAXIMUM_SEEK_TIME = 5;
+
+    /**
+     * All frames parsed from the provided tunnel.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._Frame[]}
+     */
+    var frames = [];
+
+    /**
+     * All instructions which have been read since the last frame was added to
+     * the frames array.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._Frame.Instruction[]}
+     */
+    var instructions = [];
+
+    /**
+     * The approximate number of characters which have been read from the
+     * provided tunnel since the last frame was flagged for use as a keyframe.
+     *
+     * @private
+     * @type {Number}
+     */
+    var charactersSinceLastKeyframe = 0;
+
+    /**
+     * The timestamp of the last frame which was flagged for use as a keyframe.
+     * If no timestamp has yet been flagged, this will be 0.
+     *
+     * @private
+     * @type {Number}
+     */
+    var lastKeyframeTimestamp = 0;
+
+    /**
+     * Tunnel which feeds arbitrary instructions to the client used by this
+     * Guacamole.SessionRecording for playback of the session recording.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._PlaybackTunnel}
+     */
+    var playbackTunnel = new Guacamole.SessionRecording._PlaybackTunnel();
+
+    /**
+     * Guacamole.Client instance used for visible playback of the session
+     * recording.
+     *
+     * @private
+     * @type {Guacamole.Client}
+     */
+    var playbackClient = new Guacamole.Client(playbackTunnel);
+
+    /**
+     * The current frame rendered within the playback client. If no frame is
+     * yet rendered, this will be -1.
+     *
+     * @private
+     * @type {Number}
+     */
+    var currentFrame = -1;
+
+    /**
+     * The timestamp of the frame when playback began, in milliseconds. If
+     * playback is not in progress, this will be null.
+     *
+     * @private
+     * @type {Number}
+     */
+    var startVideoTimestamp = null;
+
+    /**
+     * The real-world timestamp when playback began, in milliseconds. If
+     * playback is not in progress, this will be null.
+     *
+     * @private
+     * @type {Number}
+     */
+    var startRealTimestamp = null;
+
+    /**
+     * The ID of the timeout which will continue the in-progress seek
+     * operation. If no seek operation is in progress, the ID stored here (if
+     * any) will not be valid.
+     *
+     * @private
+     * @type {Number}
+     */
+    var seekTimeout = null;
+
+    // Start playback client connected
+    playbackClient.connect();
+
+    // Hide cursor unless mouse position is received
+    playbackClient.getDisplay().showCursor(false);
+
+    // Read instructions from provided tunnel, extracting each frame
+    tunnel.oninstruction = function handleInstruction(opcode, args) {
+
+        // Store opcode and arguments for received instruction
+        var instruction = new Guacamole.SessionRecording._Frame.Instruction(opcode, args.slice());
+        instructions.push(instruction);
+        charactersSinceLastKeyframe += instruction.getSize();
+
+        // Once a sync is received, store all instructions since the last
+        // frame as a new frame
+        if (opcode === 'sync') {
+
+            // Parse frame timestamp from sync instruction
+            var timestamp = parseInt(args[0]);
+
+            // Add a new frame containing the instructions read since last frame
+            var frame = new Guacamole.SessionRecording._Frame(timestamp, instructions);
+            frames.push(frame);
+
+            // This frame should eventually become a keyframe if enough data
+            // has been processed and enough recording time has elapsed, or if
+            // this is the absolute first frame
+            if (frames.length === 1 || (charactersSinceLastKeyframe >= KEYFRAME_CHAR_INTERVAL
+                    &amp;&amp; timestamp - lastKeyframeTimestamp >= KEYFRAME_TIME_INTERVAL)) {
+                frame.keyframe = true;
+                lastKeyframeTimestamp = timestamp;
+                charactersSinceLastKeyframe = 0;
+            }
+
+            // Clear set of instructions in preparation for next frame
+            instructions = [];
+
+            // Notify that additional content is available
+            if (recording.onprogress)
+                recording.onprogress(recording.getDuration());
+
+        }
+
+    };
+
+    /**
+     * Converts the given absolute timestamp to a timestamp which is relative
+     * to the first frame in the recording.
+     *
+     * @private
+     * @param {Number} timestamp
+     *     The timestamp to convert to a relative timestamp.
+     *
+     * @returns {Number}
+     *     The difference in milliseconds between the given timestamp and the
+     *     first frame of the recording, or zero if no frames yet exist.
+     */
+    var toRelativeTimestamp = function toRelativeTimestamp(timestamp) {
+
+        // If no frames yet exist, all timestamps are zero
+        if (frames.length === 0)
+            return 0;
+
+        // Calculate timestamp relative to first frame
+        return timestamp - frames[0].timestamp;
+
+    };
+
+    /**
+     * Searches through the given region of frames for the frame having a
+     * relative timestamp closest to the timestamp given.
+     *
+     * @private
+     * @param {Number} minIndex
+     *     The index of the first frame in the region (the frame having the
+     *     smallest timestamp).
+     *
+     * @param {Number} maxIndex
+     *     The index of the last frame in the region (the frame having the
+     *     largest timestamp).
+     *
+     * @param {Number} timestamp
+     *     The relative timestamp to search for, where zero denotes the first
+     *     frame in the recording.
+     *
+     * @returns {Number}
+     *     The index of the frame having a relative timestamp closest to the
+     *     given value.
+     */
+    var findFrame = function findFrame(minIndex, maxIndex, timestamp) {
+
+        // Do not search if the region contains only one element
+        if (minIndex === maxIndex)
+            return minIndex;
+
+        // Split search region into two halves
+        var midIndex = Math.floor((minIndex + maxIndex) / 2);
+        var midTimestamp = toRelativeTimestamp(frames[midIndex].timestamp);
+
+        // If timestamp is within lesser half, search again within that half
+        if (timestamp &lt; midTimestamp &amp;&amp; midIndex > minIndex)
+            return findFrame(minIndex, midIndex - 1, timestamp);
+
+        // If timestamp is within greater half, search again within that half
+        if (timestamp > midTimestamp &amp;&amp; midIndex &lt; maxIndex)
+            return findFrame(midIndex + 1, maxIndex, timestamp);
+
+        // Otherwise, we lucked out and found a frame with exactly the
+        // desired timestamp
+        return midIndex;
+
+    };
+
+    /**
+     * Replays the instructions associated with the given frame, sending those
+     * instructions to the playback client.
+     *
+     * @private
+     * @param {Number} index
+     *     The index of the frame within the frames array which should be
+     *     replayed.
+     */
+    var replayFrame = function replayFrame(index) {
+
+        var frame = frames[index];
+
+        // Replay all instructions within the retrieved frame
+        for (var i = 0; i &lt; frame.instructions.length; i++) {
+            var instruction = frame.instructions[i];
+            playbackTunnel.receiveInstruction(instruction.opcode, instruction.args);
+        }
+
+        // Store client state if frame is flagged as a keyframe
+        if (frame.keyframe &amp;&amp; !frame.clientState) {
+            playbackClient.exportState(function storeClientState(state) {
+                frame.clientState = state;
+            });
+        }
+
+    };
+
+    /**
+     * Moves the playback position to the given frame, resetting the state of
+     * the playback client and replaying frames as necessary. The seek
+     * operation will proceed asynchronously. If a seek operation is already in
+     * progress, that seek is first aborted. The progress of the seek operation
+     * can be observed through the onseek handler and the provided callback.
+     *
+     * @private
+     * @param {Number} index
+     *     The index of the frame which should become the new playback
+     *     position.
+     *
+     * @param {function} callback
+     *     The callback to invoke once the seek operation has completed.
+     *
+     * @param {Number} [delay=0]
+     *     The number of milliseconds that the seek operation should be
+     *     scheduled to take.
+     */
+    var seekToFrame = function seekToFrame(index, callback, delay) {
+
+        // Abort any in-progress seek
+        abortSeek();
+
+        // Replay frames asynchronously
+        seekTimeout = window.setTimeout(function continueSeek() {
+
+            var startIndex;
+
+            // Back up until startIndex represents current state
+            for (startIndex = index; startIndex >= 0; startIndex--) {
+
+                var frame = frames[startIndex];
+
+                // If we've reached the current frame, startIndex represents
+                // current state by definition
+                if (startIndex === currentFrame)
+                    break;
+
+                // If frame has associated absolute state, make that frame the
+                // current state
+                if (frame.clientState) {
+                    playbackClient.importState(frame.clientState);
+                    break;
+                }
+
+            }
+
+            // Advance to frame index after current state
+            startIndex++;
+
+            var startTime = new Date().getTime();
+
+            // Replay any applicable incremental frames
+            for (; startIndex &lt;= index; startIndex++) {
+
+                // Stop seeking if the operation is taking too long
+                var currentTime = new Date().getTime();
+                if (currentTime - startTime >= MAXIMUM_SEEK_TIME)
+                    break;
+
+                replayFrame(startIndex);
+            }
+
+            // Current frame is now at requested index
+            currentFrame = startIndex - 1;
+
+            // Notify of changes in position
+            if (recording.onseek)
+                recording.onseek(recording.getPosition());
+
+            // If the seek operation has not yet completed, schedule continuation
+            if (currentFrame !== index)
+                seekToFrame(index, callback,
+                    Math.max(delay - (new Date().getTime() - startTime), 0));
+
+            // Notify that the requested seek has completed
+            else
+                callback();
+
+        }, delay || 0);
+
+    };
+
+    /**
+     * Aborts the seek operation currently in progress, if any. If no seek
+     * operation is in progress, this function has no effect.
+     *
+     * @private
+     */
+    var abortSeek = function abortSeek() {
+        window.clearTimeout(seekTimeout);
+    };
+
+    /**
+     * Advances playback to the next frame in the frames array and schedules
+     * playback of the frame following that frame based on their associated
+     * timestamps. If no frames exist after the next frame, playback is paused.
+     *
+     * @private
+     */
+    var continuePlayback = function continuePlayback() {
+
+        // If frames remain after advancing, schedule next frame
+        if (currentFrame + 1 &lt; frames.length) {
+
+            // Pull the upcoming frame
+            var next = frames[currentFrame + 1];
+
+            // Calculate the real timestamp corresponding to when the next
+            // frame begins
+            var nextRealTimestamp = next.timestamp - startVideoTimestamp + startRealTimestamp;
+
+            // Calculate the relative delay between the current time and
+            // the next frame start
+            var delay = Math.max(nextRealTimestamp - new Date().getTime(), 0);
+
+            // Advance to next frame after enough time has elapsed
+            seekToFrame(currentFrame + 1, function frameDelayElapsed() {
+                continuePlayback();
+            }, delay);
+
+        }
+
+        // Otherwise stop playback
+        else
+            recording.pause();
+
+    };
+
+    /**
+     * Fired when new frames have become available while the recording is
+     * being downloaded.
+     *
+     * @event
+     * @param {Number} duration
+     *     The new duration of the recording, in milliseconds.
+     */
+    this.onprogress = null;
+
+    /**
+     * Fired whenever playback of the recording has started.
+     *
+     * @event
+     */
+    this.onplay = null;
+
+    /**
+     * Fired whenever playback of the recording has been paused. This may
+     * happen when playback is explicitly paused with a call to pause(), or
+     * when playback is implicitly paused due to reaching the end of the
+     * recording.
+     *
+     * @event
+     */
+    this.onpause = null;
+
+    /**
+     * Fired whenever the playback position within the recording changes.
+     *
+     * @event
+     * @param {Number} position
+     *     The new position within the recording, in milliseconds.
+     */
+    this.onseek = null;
+
+    /**
+     * Connects the underlying tunnel, beginning download of the Guacamole
+     * session. Playback of the Guacamole session cannot occur until at least
+     * one frame worth of instructions has been downloaded.
+     *
+     * @param {String} data
+     *     The data to send to the tunnel when connecting.
+     */
+    this.connect = function connect(data) {
+        tunnel.connect(data);
+    };
+
+    /**
+     * Disconnects the underlying tunnel, stopping further download of the
+     * Guacamole session.
+     */
+    this.disconnect = function disconnect() {
+        tunnel.disconnect();
+    };
+
+    /**
+     * Returns the underlying display of the Guacamole.Client used by this
+     * Guacamole.SessionRecording for playback. The display contains an Element
+     * which can be added to the DOM, causing the display (and thus playback of
+     * the recording) to become visible.
+     *
+     * @return {Guacamole.Display}
+     *     The underlying display of the Guacamole.Client used by this
+     *     Guacamole.SessionRecording for playback.
+     */
+    this.getDisplay = function getDisplay() {
+        return playbackClient.getDisplay();
+    };
+
+    /**
+     * Returns whether playback is currently in progress.
+     *
+     * @returns {Boolean}
+     *     true if playback is currently in progress, false otherwise.
+     */
+    this.isPlaying = function isPlaying() {
+        return !!startVideoTimestamp;
+    };
+
+    /**
+     * Returns the current playback position within the recording, in
+     * milliseconds, where zero is the start of the recording.
+     *
+     * @returns {Number}
+     *     The current playback position within the recording, in milliseconds.
+     */
+    this.getPosition = function getPosition() {
+
+        // Position is simply zero if playback has not started at all
+        if (currentFrame === -1)
+            return 0;
+
+        // Return current position as a millisecond timestamp relative to the
+        // start of the recording
+        return toRelativeTimestamp(frames[currentFrame].timestamp);
+
+    };
+
+    /**
+     * Returns the duration of this recording, in milliseconds. If the
+     * recording is still being downloaded, this value will gradually increase.
+     *
+     * @returns {Number}
+     *     The duration of this recording, in milliseconds.
+     */
+    this.getDuration = function getDuration() {
+
+        // If no frames yet exist, duration is zero
+        if (frames.length === 0)
+            return 0;
+
+        // Recording duration is simply the timestamp of the last frame
+        return toRelativeTimestamp(frames[frames.length - 1].timestamp);
+
+    };
+
+    /**
+     * Begins continuous playback of the recording downloaded thus far.
+     * Playback of the recording will continue until pause() is invoked or
+     * until no further frames exist. Playback is initially paused when a
+     * Guacamole.SessionRecording is created, and must be explicitly started
+     * through a call to this function. If playback is already in progress,
+     * this function has no effect. If a seek operation is in progress,
+     * playback resumes at the current position, and the seek is aborted as if
+     * completed.
+     */
+    this.play = function play() {
+
+        // If playback is not already in progress and frames remain,
+        // begin playback
+        if (!recording.isPlaying() &amp;&amp; currentFrame + 1 &lt; frames.length) {
+
+            // Notify that playback is starting
+            if (recording.onplay)
+                recording.onplay();
+
+            // Store timestamp of playback start for relative scheduling of
+            // future frames
+            var next = frames[currentFrame + 1];
+            startVideoTimestamp = next.timestamp;
+            startRealTimestamp = new Date().getTime();
+
+            // Begin playback of video
+            continuePlayback();
+
+        }
+
+    };
+
+    /**
+     * Seeks to the given position within the recording. If the recording is
+     * currently being played back, playback will continue after the seek is
+     * performed. If the recording is currently paused, playback will be
+     * paused after the seek is performed. If a seek operation is already in
+     * progress, that seek is first aborted. The seek operation will proceed
+     * asynchronously.
+     *
+     * @param {Number} position
+     *     The position within the recording to seek to, in milliseconds.
+     *
+     * @param {function} [callback]
+     *     The callback to invoke once the seek operation has completed.
+     */
+    this.seek = function seek(position, callback) {
+
+        // Do not seek if no frames exist
+        if (frames.length === 0)
+            return;
+
+        // Pause playback, preserving playback state
+        var originallyPlaying = recording.isPlaying();
+        recording.pause();
+
+        // Perform seek
+        seekToFrame(findFrame(0, frames.length - 1, position), function restorePlaybackState() {
+
+            // Restore playback state
+            if (originallyPlaying)
+                recording.play();
+
+            // Notify that seek has completed
+            if (callback)
+                callback();
+
+        });
+
+    };
+
+    /**
+     * Pauses playback of the recording, if playback is currently in progress.
+     * If playback is not in progress, this function has no effect. If a seek
+     * operation is in progress, the seek is aborted. Playback is initially
+     * paused when a Guacamole.SessionRecording is created, and must be
+     * explicitly started through a call to play().
+     */
+    this.pause = function pause() {
+
+        // Abort any in-progress seek / playback
+        abortSeek();
+
+        // Stop playback only if playback is in progress
+        if (recording.isPlaying()) {
+
+            // Notify that playback is stopping
+            if (recording.onpause)
+                recording.onpause();
+
+            // Playback is stopped
+            startVideoTimestamp = null;
+            startRealTimestamp = null;
+
+        }
+
+    };
+
+};
+
+/**
+ * A single frame of Guacamole session data. Each frame is made up of the set
+ * of instructions used to generate that frame, and the timestamp as dictated
+ * by the "sync" instruction terminating the frame. Optionally, a frame may
+ * also be associated with a snapshot of Guacamole client state, such that the
+ * frame can be rendered without replaying all previous frames.
+ *
+ * @private
+ * @constructor
+ * @param {Number} timestamp
+ *     The timestamp of this frame, as dictated by the "sync" instruction which
+ *     terminates the frame.
+ *
+ * @param {Guacamole.SessionRecording._Frame.Instruction[]} instructions
+ *     All instructions which are necessary to generate this frame relative to
+ *     the previous frame in the Guacamole session.
+ */
+Guacamole.SessionRecording._Frame = function _Frame(timestamp, instructions) {
+
+    /**
+     * Whether this frame should be used as a keyframe if possible. This value
+     * is purely advisory. The stored clientState must eventually be manually
+     * set for the frame to be used as a keyframe. By default, frames are not
+     * keyframes.
+     *
+     * @type {Boolean}
+     * @default false
+     */
+    this.keyframe = false;
+
+    /**
+     * The timestamp of this frame, as dictated by the "sync" instruction which
+     * terminates the frame.
+     *
+     * @type {Number}
+     */
+    this.timestamp = timestamp;
+
+    /**
+     * All instructions which are necessary to generate this frame relative to
+     * the previous frame in the Guacamole session.
+     *
+     * @type {Guacamole.SessionRecording._Frame.Instruction[]}
+     */
+    this.instructions = instructions;
+
+    /**
+     * A snapshot of client state after this frame was rendered, as returned by
+     * a call to exportState(). If no such snapshot has been taken, this will
+     * be null.
+     *
+     * @type {Object}
+     * @default null
+     */
+    this.clientState = null;
+
+};
+
+/**
+ * A Guacamole protocol instruction. Each Guacamole protocol instruction is
+ * made up of an opcode and set of arguments.
+ *
+ * @private
+ * @constructor
+ * @param {String} opcode
+ *     The opcode of this Guacamole instruction.
+ *
+ * @param {String[]} args
+ *     All arguments associated with this Guacamole instruction.
+ */
+Guacamole.SessionRecording._Frame.Instruction = function Instruction(opcode, args) {
+
+    /**
+     * Reference to this Guacamole.SessionRecording._Frame.Instruction.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._Frame.Instruction}
+     */
+    var instruction = this;
+
+    /**
+     * The opcode of this Guacamole instruction.
+     *
+     * @type {String}
+     */
+    this.opcode = opcode;
+
+    /**
+     * All arguments associated with this Guacamole instruction.
+     *
+     * @type {String[]}
+     */
+    this.args = args;
+
+    /**
+     * Returns the approximate number of characters which make up this
+     * instruction. This value is only approximate as it excludes the length
+     * prefixes and various delimiters used by the Guacamole protocol; only
+     * the content of the opcode and each argument is taken into account.
+     *
+     * @returns {Number}
+     *     The approximate size of this instruction, in characters.
+     */
+    this.getSize = function getSize() {
+
+        // Init with length of opcode
+        var size = instruction.opcode.length;
+
+        // Add length of all arguments
+        for (var i = 0; i &lt; instruction.args.length; i++)
+            size += instruction.args[i].length;
+
+        return size;
+
+    };
+
+};
+
+/**
+ * A read-only Guacamole.Tunnel implementation which streams instructions
+ * received through explicit calls to its receiveInstruction() function.
+ *
+ * @private
+ * @constructor
+ * @augments {Guacamole.Tunnel}
+ */
+Guacamole.SessionRecording._PlaybackTunnel = function _PlaybackTunnel() {
+
+    /**
+     * Reference to this Guacamole.SessionRecording._PlaybackTunnel.
+     *
+     * @private
+     * @type {Guacamole.SessionRecording._PlaybackTunnel}
+     */
+    var tunnel = this;
+
+    this.connect = function connect(data) {
+        // Do nothing
+    };
+
+    this.sendMessage = function sendMessage(elements) {
+        // Do nothing
+    };
+
+    this.disconnect = function disconnect() {
+        // Do nothing
+    };
+
+    /**
+     * Invokes this tunnel's oninstruction handler, notifying users of this
+     * tunnel (such as a Guacamole.Client instance) that an instruction has
+     * been received. If the oninstruction handler has not been set, this
+     * function has no effect.
+     *
+     * @param {String} opcode
+     *     The opcode of the Guacamole instruction.
+     *
+     * @param {String[]} args
+     *     All arguments associated with this Guacamole instruction.
+     */
+    this.receiveInstruction = function receiveInstruction(opcode, args) {
+        if (tunnel.oninstruction)
+            tunnel.oninstruction(opcode, args);
+    };
+
+};
+</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.InputSink.html">InputSink</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><l
 i><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.RawAudioRecorder.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 hre
 f="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">onfilesys
 tem</a></li><li><a href="Guacamole.Client.html#event:onname">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">onprogress</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 h
 ref="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:o
 ninstruction">oninstruction</a></li><li><a href="Guacamole.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 Fri Dec 21 2018 13:47:10 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/8c2fe14e/content/doc/1.0.0/guacamole-common-js/Status.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/Status.js.html b/content/doc/1.0.0/guacamole-common-js/Status.js.html
new file mode 100644
index 0000000..1fa9df6
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/Status.js.html
@@ -0,0 +1,379 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>JSDoc: Source: Status.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: Status.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 Guacamole status. Each Guacamole status consists of a status code, defined
+ * by the protocol, and an optional human-readable message, usually only
+ * included for debugging convenience.
+ *
+ * @constructor
+ * @param {Number} code
+ *     The Guacamole status code, as defined by Guacamole.Status.Code.
+ *
+ * @param {String} [message]
+ *     An optional human-readable message.
+ */
+Guacamole.Status = function(code, message) {
+
+    /**
+     * Reference to this Guacamole.Status.
+     * @private
+     */
+    var guac_status = this;
+
+    /**
+     * The Guacamole status code.
+     * @see Guacamole.Status.Code
+     * @type {Number}
+     */
+    this.code = code;
+
+    /**
+     * An arbitrary human-readable message associated with this status, if any.
+     * The human-readable message is not required, and is generally provided
+     * for debugging purposes only. For user feedback, it is better to translate
+     * the Guacamole status code into a message.
+     * 
+     * @type {String}
+     */
+    this.message = message;
+
+    /**
+     * Returns whether this status represents an error.
+     * @returns {Boolean} true if this status represents an error, false
+     *                    otherwise.
+     */
+    this.isError = function() {
+        return guac_status.code &lt; 0 || guac_status.code > 0x00FF;
+    };
+
+};
+
+/**
+ * Enumeration of all Guacamole status codes.
+ */
+Guacamole.Status.Code = {
+
+    /**
+     * The operation succeeded.
+     *
+     * @type {Number}
+     */
+    "SUCCESS": 0x0000,
+
+    /**
+     * The requested operation is unsupported.
+     *
+     * @type {Number}
+     */
+    "UNSUPPORTED": 0x0100,
+
+    /**
+     * The operation could not be performed due to an internal failure.
+     *
+     * @type {Number}
+     */
+    "SERVER_ERROR": 0x0200,
+
+    /**
+     * The operation could not be performed as the server is busy.
+     *
+     * @type {Number}
+     */
+    "SERVER_BUSY": 0x0201,
+
+    /**
+     * The operation could not be performed because the upstream server is not
+     * responding.
+     *
+     * @type {Number}
+     */
+    "UPSTREAM_TIMEOUT": 0x0202,
+
+    /**
+     * The operation was unsuccessful due to an error or otherwise unexpected
+     * condition of the upstream server.
+     *
+     * @type {Number}
+     */
+    "UPSTREAM_ERROR": 0x0203,
+
+    /**
+     * The operation could not be performed as the requested resource does not
+     * exist.
+     *
+     * @type {Number}
+     */
+    "RESOURCE_NOT_FOUND": 0x0204,
+
+    /**
+     * The operation could not be performed as the requested resource is
+     * already in use.
+     *
+     * @type {Number}
+     */
+    "RESOURCE_CONFLICT": 0x0205,
+
+    /**
+     * The operation could not be performed as the requested resource is now
+     * closed.
+     *
+     * @type {Number}
+     */
+    "RESOURCE_CLOSED": 0x0206,
+
+    /**
+     * The operation could not be performed because the upstream server does
+     * not appear to exist.
+     *
+     * @type {Number}
+     */
+    "UPSTREAM_NOT_FOUND": 0x0207,
+
+    /**
+     * The operation could not be performed because the upstream server is not
+     * available to service the request.
+     *
+     * @type {Number}
+     */
+    "UPSTREAM_UNAVAILABLE": 0x0208,
+
+    /**
+     * The session within the upstream server has ended because it conflicted
+     * with another session.
+     *
+     * @type {Number}
+     */
+    "SESSION_CONFLICT": 0x0209,
+
+    /**
+     * The session within the upstream server has ended because it appeared to
+     * be inactive.
+     *
+     * @type {Number}
+     */
+    "SESSION_TIMEOUT": 0x020A,
+
+    /**
+     * The session within the upstream server has been forcibly terminated.
+     *
+     * @type {Number}
+     */
+    "SESSION_CLOSED": 0x020B,
+
+    /**
+     * The operation could not be performed because bad parameters were given.
+     *
+     * @type {Number}
+     */
+    "CLIENT_BAD_REQUEST": 0x0300,
+
+    /**
+     * Permission was denied to perform the operation, as the user is not yet
+     * authorized (not yet logged in, for example).
+     *
+     * @type {Number}
+     */
+    "CLIENT_UNAUTHORIZED": 0x0301,
+
+    /**
+     * Permission was denied to perform the operation, and this permission will
+     * not be granted even if the user is authorized.
+     *
+     * @type {Number}
+     */
+    "CLIENT_FORBIDDEN": 0x0303,
+
+    /**
+     * The client took too long to respond.
+     *
+     * @type {Number}
+     */
+    "CLIENT_TIMEOUT": 0x0308,
+
+    /**
+     * The client sent too much data.
+     *
+     * @type {Number}
+     */
+    "CLIENT_OVERRUN": 0x030D,
+
+    /**
+     * The client sent data of an unsupported or unexpected type.
+     *
+     * @type {Number}
+     */
+    "CLIENT_BAD_TYPE": 0x030F,
+
+    /**
+     * The operation failed because the current client is already using too
+     * many resources.
+     *
+     * @type {Number}
+     */
+    "CLIENT_TOO_MANY": 0x031D
+
+};
+
+/**
+ * Returns the Guacamole protocol status code which most closely
+ * represents the given HTTP status code.
+ *
+ * @param {Number} status
+ *     The HTTP status code to translate into a Guacamole protocol status
+ *     code.
+ *
+ * @returns {Number}
+ *     The Guacamole protocol status code which most closely represents the
+ *     given HTTP status code.
+ */
+Guacamole.Status.Code.fromHTTPCode = function fromHTTPCode(status) {
+
+    // Translate status codes with known equivalents
+    switch (status) {
+
+        // HTTP 400 - Bad request
+        case 400:
+            return Guacamole.Status.Code.CLIENT_BAD_REQUEST;
+
+        // HTTP 403 - Forbidden
+        case 403:
+            return Guacamole.Status.Code.CLIENT_FORBIDDEN;
+
+        // HTTP 404 - Resource not found
+        case 404:
+            return Guacamole.Status.Code.RESOURCE_NOT_FOUND;
+
+        // HTTP 429 - Too many requests
+        case 429:
+            return Guacamole.Status.Code.CLIENT_TOO_MANY;
+
+        // HTTP 503 - Server unavailable
+        case 503:
+            return Guacamole.Status.Code.SERVER_BUSY;
+
+    }
+
+    // Default all other codes to generic internal error
+    return Guacamole.Status.Code.SERVER_ERROR;
+
+};
+
+/**
+ * Returns the Guacamole protocol status code which most closely
+ * represents the given WebSocket status code.
+ *
+ * @param {Number} code
+ *     The WebSocket status code to translate into a Guacamole protocol
+ *     status code.
+ *
+ * @returns {Number}
+ *     The Guacamole protocol status code which most closely represents the
+ *     given WebSocket status code.
+ */
+Guacamole.Status.Code.fromWebSocketCode = function fromWebSocketCode(code) {
+
+    // Translate status codes with known equivalents
+    switch (code) {
+
+        // Successful disconnect (no error)
+        case 1000: // Normal Closure
+            return Guacamole.Status.Code.SUCCESS;
+
+        // Codes which indicate the server is not reachable
+        case 1006: // Abnormal Closure (also signalled by JavaScript when the connection cannot be opened in the first place)
+        case 1015: // TLS Handshake
+            return Guacamole.Status.Code.UPSTREAM_NOT_FOUND;
+
+        // Codes which indicate the server is reachable but busy/unavailable
+        case 1001: // Going Away
+        case 1012: // Service Restart
+        case 1013: // Try Again Later
+        case 1014: // Bad Gateway
+            return Guacamole.Status.Code.UPSTREAM_UNAVAILABLE;
+
+    }
+
+    // Default all other codes to generic internal error
+    return Guacamole.Status.Code.SERVER_ERROR;
+
+};
+</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.InputSink.html">InputSink</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><l
 i><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.RawAudioRecorder.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 hre
 f="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">onfilesys
 tem</a></li><li><a href="Guacamole.Client.html#event:onname">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">onprogress</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 h
 ref="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:o
 ninstruction">oninstruction</a></li><li><a href="Guacamole.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 Fri Dec 21 2018 13:47:10 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>