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:35 UTC
[29/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/StringReader.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/StringReader.js.html b/content/doc/1.0.0/guacamole-common-js/StringReader.js.html
new file mode 100644
index 0000000..3e26f7d
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/StringReader.js.html
@@ -0,0 +1,227 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: StringReader.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: StringReader.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 reader which automatically handles the given input stream, returning
+ * strictly text data. Note that this object will overwrite any installed event
+ * handlers on the given Guacamole.InputStream.
+ *
+ * @constructor
+ * @param {Guacamole.InputStream} stream The stream that data will be read
+ * from.
+ */
+Guacamole.StringReader = function(stream) {
+
+ /**
+ * Reference to this Guacamole.InputStream.
+ * @private
+ */
+ var guac_reader = this;
+
+ /**
+ * Wrapped Guacamole.ArrayBufferReader.
+ * @private
+ * @type {Guacamole.ArrayBufferReader}
+ */
+ var array_reader = new Guacamole.ArrayBufferReader(stream);
+
+ /**
+ * The number of bytes remaining for the current codepoint.
+ *
+ * @private
+ * @type {Number}
+ */
+ var bytes_remaining = 0;
+
+ /**
+ * The current codepoint value, as calculated from bytes read so far.
+ *
+ * @private
+ * @type {Number}
+ */
+ var codepoint = 0;
+
+ /**
+ * Decodes the given UTF-8 data into a Unicode string. The data may end in
+ * the middle of a multibyte character.
+ *
+ * @private
+ * @param {ArrayBuffer} buffer Arbitrary UTF-8 data.
+ * @return {String} A decoded Unicode string.
+ */
+ function __decode_utf8(buffer) {
+
+ var text = "";
+
+ var bytes = new Uint8Array(buffer);
+ for (var i=0; i<bytes.length; i++) {
+
+ // Get current byte
+ var value = bytes[i];
+
+ // Start new codepoint if nothing yet read
+ if (bytes_remaining === 0) {
+
+ // 1 byte (0xxxxxxx)
+ if ((value | 0x7F) === 0x7F)
+ text += String.fromCharCode(value);
+
+ // 2 byte (110xxxxx)
+ else if ((value | 0x1F) === 0xDF) {
+ codepoint = value & 0x1F;
+ bytes_remaining = 1;
+ }
+
+ // 3 byte (1110xxxx)
+ else if ((value | 0x0F )=== 0xEF) {
+ codepoint = value & 0x0F;
+ bytes_remaining = 2;
+ }
+
+ // 4 byte (11110xxx)
+ else if ((value | 0x07) === 0xF7) {
+ codepoint = value & 0x07;
+ bytes_remaining = 3;
+ }
+
+ // Invalid byte
+ else
+ text += "\uFFFD";
+
+ }
+
+ // Continue existing codepoint (10xxxxxx)
+ else if ((value | 0x3F) === 0xBF) {
+
+ codepoint = (codepoint << 6) | (value & 0x3F);
+ bytes_remaining--;
+
+ // Write codepoint if finished
+ if (bytes_remaining === 0)
+ text += String.fromCharCode(codepoint);
+
+ }
+
+ // Invalid byte
+ else {
+ bytes_remaining = 0;
+ text += "\uFFFD";
+ }
+
+ }
+
+ return text;
+
+ }
+
+ // Receive blobs as strings
+ array_reader.ondata = function(buffer) {
+
+ // Decode UTF-8
+ var text = __decode_utf8(buffer);
+
+ // Call handler, if present
+ if (guac_reader.ontext)
+ guac_reader.ontext(text);
+
+ };
+
+ // Simply call onend when end received
+ array_reader.onend = function() {
+ if (guac_reader.onend)
+ guac_reader.onend();
+ };
+
+ /**
+ * Fired once for every blob of text data received.
+ *
+ * @event
+ * @param {String} text The data packet received.
+ */
+ this.ontext = null;
+
+ /**
+ * Fired once this stream is finished and no further data will be written.
+ * @event
+ */
+ this.onend = 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/StringWriter.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/StringWriter.js.html b/content/doc/1.0.0/guacamole-common-js/StringWriter.js.html
new file mode 100644
index 0000000..b2b9df5
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/StringWriter.js.html
@@ -0,0 +1,252 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: StringWriter.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: StringWriter.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 text
+ * data.
+ *
+ * @constructor
+ * @param {Guacamole.OutputStream} stream The stream that data will be written
+ * to.
+ */
+Guacamole.StringWriter = function(stream) {
+
+ /**
+ * Reference to this Guacamole.StringWriter.
+ * @private
+ */
+ var guac_writer = this;
+
+ /**
+ * Wrapped Guacamole.ArrayBufferWriter.
+ * @private
+ * @type {Guacamole.ArrayBufferWriter}
+ */
+ var array_writer = new Guacamole.ArrayBufferWriter(stream);
+
+ /**
+ * Internal buffer for UTF-8 output.
+ * @private
+ */
+ var buffer = new Uint8Array(8192);
+
+ /**
+ * The number of bytes currently in the buffer.
+ * @private
+ */
+ var length = 0;
+
+ // Simply call onack for acknowledgements
+ array_writer.onack = function(status) {
+ if (guac_writer.onack)
+ guac_writer.onack(status);
+ };
+
+ /**
+ * Expands the size of the underlying buffer by the given number of bytes,
+ * updating the length appropriately.
+ *
+ * @private
+ * @param {Number} bytes The number of bytes to add to the underlying
+ * buffer.
+ */
+ function __expand(bytes) {
+
+ // Resize buffer if more space needed
+ if (length+bytes >= buffer.length) {
+ var new_buffer = new Uint8Array((length+bytes)*2);
+ new_buffer.set(buffer);
+ buffer = new_buffer;
+ }
+
+ length += bytes;
+
+ }
+
+ /**
+ * Appends a single Unicode character to the current buffer, resizing the
+ * buffer if necessary. The character will be encoded as UTF-8.
+ *
+ * @private
+ * @param {Number} codepoint The codepoint of the Unicode character to
+ * append.
+ */
+ function __append_utf8(codepoint) {
+
+ var mask;
+ var bytes;
+
+ // 1 byte
+ if (codepoint <= 0x7F) {
+ mask = 0x00;
+ bytes = 1;
+ }
+
+ // 2 byte
+ else if (codepoint <= 0x7FF) {
+ mask = 0xC0;
+ bytes = 2;
+ }
+
+ // 3 byte
+ else if (codepoint <= 0xFFFF) {
+ mask = 0xE0;
+ bytes = 3;
+ }
+
+ // 4 byte
+ else if (codepoint <= 0x1FFFFF) {
+ mask = 0xF0;
+ bytes = 4;
+ }
+
+ // If invalid codepoint, append replacement character
+ else {
+ __append_utf8(0xFFFD);
+ return;
+ }
+
+ // Offset buffer by size
+ __expand(bytes);
+ var offset = length - 1;
+
+ // Add trailing bytes, if any
+ for (var i=1; i<bytes; i++) {
+ buffer[offset--] = 0x80 | (codepoint & 0x3F);
+ codepoint >>= 6;
+ }
+
+ // Set initial byte
+ buffer[offset] = mask | codepoint;
+
+ }
+
+ /**
+ * Encodes the given string as UTF-8, returning an ArrayBuffer containing
+ * the resulting bytes.
+ *
+ * @private
+ * @param {String} text The string to encode as UTF-8.
+ * @return {Uint8Array} The encoded UTF-8 data.
+ */
+ function __encode_utf8(text) {
+
+ // Fill buffer with UTF-8
+ for (var i=0; i<text.length; i++) {
+ var codepoint = text.charCodeAt(i);
+ __append_utf8(codepoint);
+ }
+
+ // Flush buffer
+ if (length > 0) {
+ var out_buffer = buffer.subarray(0, length);
+ length = 0;
+ return out_buffer;
+ }
+
+ }
+
+ /**
+ * Sends the given text.
+ *
+ * @param {String} text The text to send.
+ */
+ this.sendText = function(text) {
+ if (text.length)
+ array_writer.sendData(__encode_utf8(text));
+ };
+
+ /**
+ * Signals that no further text will be sent, effectively closing the
+ * stream.
+ */
+ this.sendEnd = function() {
+ array_writer.sendEnd();
+ };
+
+ /**
+ * Fired for received data, if acknowledged by the server.
+ * @event
+ * @param {Guacamole.Status} status The status of the operation.
+ */
+ this.onack = 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/Tunnel.js.html
----------------------------------------------------------------------
diff --git a/content/doc/1.0.0/guacamole-common-js/Tunnel.js.html b/content/doc/1.0.0/guacamole-common-js/Tunnel.js.html
new file mode 100644
index 0000000..f219192
--- /dev/null
+++ b/content/doc/1.0.0/guacamole-common-js/Tunnel.js.html
@@ -0,0 +1,1445 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: Tunnel.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: Tunnel.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 || {};
+
+/**
+ * Core object providing abstract communication for Guacamole. This object
+ * is a null implementation whose functions do nothing. Guacamole applications
+ * should use {@link Guacamole.HTTPTunnel} instead, or implement their own tunnel based
+ * on this one.
+ *
+ * @constructor
+ * @see Guacamole.HTTPTunnel
+ */
+Guacamole.Tunnel = function() {
+
+ /**
+ * Connect to the tunnel with the given optional data. This data is
+ * typically used for authentication. The format of data accepted is
+ * up to the tunnel implementation.
+ *
+ * @param {String} data The data to send to the tunnel when connecting.
+ */
+ this.connect = function(data) {};
+
+ /**
+ * Disconnect from the tunnel.
+ */
+ this.disconnect = function() {};
+
+ /**
+ * Send the given message through the tunnel to the service on the other
+ * side. All messages are guaranteed to be received in the order sent.
+ *
+ * @param {...*} elements
+ * The elements of the message to send to the service on the other side
+ * of the tunnel.
+ */
+ this.sendMessage = function(elements) {};
+
+ /**
+ * Changes the stored numeric state of this tunnel, firing the onstatechange
+ * event if the new state is different and a handler has been defined.
+ *
+ * @private
+ * @param {Number} state
+ * The new state of this tunnel.
+ */
+ this.setState = function(state) {
+
+ // Notify only if state changes
+ if (state !== this.state) {
+ this.state = state;
+ if (this.onstatechange)
+ this.onstatechange(state);
+ }
+
+ };
+
+ /**
+ * Returns whether this tunnel is currently connected.
+ *
+ * @returns {Boolean}
+ * true if this tunnel is currently connected, false otherwise.
+ */
+ this.isConnected = function isConnected() {
+ return this.state === Guacamole.Tunnel.State.OPEN
+ || this.state === Guacamole.Tunnel.State.UNSTABLE;
+ };
+
+ /**
+ * The current state of this tunnel.
+ *
+ * @type {Number}
+ */
+ this.state = Guacamole.Tunnel.State.CONNECTING;
+
+ /**
+ * The maximum amount of time to wait for data to be received, in
+ * milliseconds. If data is not received within this amount of time,
+ * the tunnel is closed with an error. The default value is 15000.
+ *
+ * @type {Number}
+ */
+ this.receiveTimeout = 15000;
+
+ /**
+ * The amount of time to wait for data to be received before considering
+ * the connection to be unstable, in milliseconds. If data is not received
+ * within this amount of time, the tunnel status is updated to warn that
+ * the connection appears unresponsive and may close. The default value is
+ * 1500.
+ *
+ * @type {Number}
+ */
+ this.unstableThreshold = 1500;
+
+ /**
+ * The UUID uniquely identifying this tunnel. If not yet known, this will
+ * be null.
+ *
+ * @type {String}
+ */
+ this.uuid = null;
+
+ /**
+ * Fired whenever an error is encountered by the tunnel.
+ *
+ * @event
+ * @param {Guacamole.Status} status A status object which describes the
+ * error.
+ */
+ this.onerror = null;
+
+ /**
+ * Fired whenever the state of the tunnel changes.
+ *
+ * @event
+ * @param {Number} state The new state of the client.
+ */
+ this.onstatechange = null;
+
+ /**
+ * 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;
+
+};
+
+/**
+ * The Guacamole protocol instruction opcode reserved for arbitrary internal
+ * use by tunnel implementations. The value of this opcode is guaranteed to be
+ * the empty string (""). Tunnel implementations may use this opcode for any
+ * purpose. It is currently used by the HTTP tunnel to mark the end of the HTTP
+ * response, and by the WebSocket tunnel to transmit the tunnel UUID and send
+ * connection stability test pings/responses.
+ *
+ * @constant
+ * @type {String}
+ */
+Guacamole.Tunnel.INTERNAL_DATA_OPCODE = '';
+
+/**
+ * All possible tunnel states.
+ */
+Guacamole.Tunnel.State = {
+
+ /**
+ * A connection is in pending. It is not yet known whether connection was
+ * successful.
+ *
+ * @type {Number}
+ */
+ "CONNECTING": 0,
+
+ /**
+ * Connection was successful, and data is being received.
+ *
+ * @type {Number}
+ */
+ "OPEN": 1,
+
+ /**
+ * The connection is closed. Connection may not have been successful, the
+ * tunnel may have been explicitly closed by either side, or an error may
+ * have occurred.
+ *
+ * @type {Number}
+ */
+ "CLOSED": 2,
+
+ /**
+ * The connection is open, but communication through the tunnel appears to
+ * be disrupted, and the connection may close as a result.
+ *
+ * @type {Number}
+ */
+ "UNSTABLE" : 3
+
+};
+
+/**
+ * Guacamole Tunnel implemented over HTTP via XMLHttpRequest.
+ *
+ * @constructor
+ * @augments Guacamole.Tunnel
+ *
+ * @param {String} tunnelURL
+ * The URL of the HTTP tunneling service.
+ *
+ * @param {Boolean} [crossDomain=false]
+ * Whether tunnel requests will be cross-domain, and thus must use CORS
+ * mechanisms and headers. By default, it is assumed that tunnel requests
+ * will be made to the same domain.
+ *
+ * @param {Object} [extraTunnelHeaders={}]
+ * Key value pairs containing the header names and values of any additional
+ * headers to be sent in tunnel requests. By default, no extra headers will
+ * be added.
+ */
+Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
+
+ /**
+ * Reference to this HTTP tunnel.
+ * @private
+ */
+ var tunnel = this;
+
+ var TUNNEL_CONNECT = tunnelURL + "?connect";
+ var TUNNEL_READ = tunnelURL + "?read:";
+ var TUNNEL_WRITE = tunnelURL + "?write:";
+
+ var POLLING_ENABLED = 1;
+ var POLLING_DISABLED = 0;
+
+ // Default to polling - will be turned off automatically if not needed
+ var pollingMode = POLLING_ENABLED;
+
+ var sendingMessages = false;
+ var outputMessageBuffer = "";
+
+ // If requests are expected to be cross-domain, the cookie that the HTTP
+ // tunnel depends on will only be sent if withCredentials is true
+ var withCredentials = !!crossDomain;
+
+ /**
+ * The current receive timeout ID, if any.
+ * @private
+ */
+ var receive_timeout = null;
+
+ /**
+ * The current connection stability timeout ID, if any.
+ *
+ * @private
+ * @type {Number}
+ */
+ var unstableTimeout = null;
+
+ /**
+ * The current connection stability test ping interval ID, if any. This
+ * will only be set upon successful connection.
+ *
+ * @private
+ * @type {Number}
+ */
+ var pingInterval = null;
+
+ /**
+ * The number of milliseconds to wait between connection stability test
+ * pings.
+ *
+ * @private
+ * @constant
+ * @type {Number}
+ */
+ var PING_FREQUENCY = 500;
+
+ /**
+ * Additional headers to be sent in tunnel requests. This dictionary can be
+ * populated with key/value header pairs to pass information such as authentication
+ * tokens, etc.
+ *
+ * @private
+ */
+ var extraHeaders = extraTunnelHeaders || {};
+
+ /**
+ * Adds the configured additional headers to the given request.
+ *
+ * @param {XMLHttpRequest} request
+ * The request where the configured extra headers will be added.
+ *
+ * @param {Object} headers
+ * The headers to be added to the request.
+ *
+ * @private
+ */
+ function addExtraHeaders(request, headers) {
+ for (var name in headers) {
+ request.setRequestHeader(name, headers[name]);
+ }
+ }
+
+ /**
+ * Initiates a timeout which, if data is not received, causes the tunnel
+ * to close with an error.
+ *
+ * @private
+ */
+ function reset_timeout() {
+
+ // Get rid of old timeouts (if any)
+ window.clearTimeout(receive_timeout);
+ window.clearTimeout(unstableTimeout);
+
+ // Clear unstable status
+ if (tunnel.state === Guacamole.Tunnel.State.UNSTABLE)
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
+
+ // Set new timeout for tracking overall connection timeout
+ receive_timeout = window.setTimeout(function () {
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_TIMEOUT, "Server timeout."));
+ }, tunnel.receiveTimeout);
+
+ // Set new timeout for tracking suspected connection instability
+ unstableTimeout = window.setTimeout(function() {
+ tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
+ }, tunnel.unstableThreshold);
+
+ }
+
+ /**
+ * Closes this tunnel, signaling the given status and corresponding
+ * message, which will be sent to the onerror handler if the status is
+ * an error status.
+ *
+ * @private
+ * @param {Guacamole.Status} status The status causing the connection to
+ * close;
+ */
+ function close_tunnel(status) {
+
+ // Get rid of old timeouts (if any)
+ window.clearTimeout(receive_timeout);
+ window.clearTimeout(unstableTimeout);
+
+ // Cease connection test pings
+ window.clearInterval(pingInterval);
+
+ // Ignore if already closed
+ if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
+ return;
+
+ // If connection closed abnormally, signal error.
+ if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror) {
+
+ // Ignore RESOURCE_NOT_FOUND if we've already connected, as that
+ // only signals end-of-stream for the HTTP tunnel.
+ if (tunnel.state === Guacamole.Tunnel.State.CONNECTING
+ || status.code !== Guacamole.Status.Code.RESOURCE_NOT_FOUND)
+ tunnel.onerror(status);
+
+ }
+
+ // Reset output message buffer
+ sendingMessages = false;
+
+ // Mark as closed
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
+
+ }
+
+
+ this.sendMessage = function() {
+
+ // Do not attempt to send messages if not connected
+ if (!tunnel.isConnected())
+ return;
+
+ // Do not attempt to send empty messages
+ if (arguments.length === 0)
+ return;
+
+ /**
+ * Converts the given value to a length/string pair for use as an
+ * element in a Guacamole instruction.
+ *
+ * @private
+ * @param value The value to convert.
+ * @return {String} The converted value.
+ */
+ function getElement(value) {
+ var string = new String(value);
+ return string.length + "." + string;
+ }
+
+ // Initialized message with first element
+ var message = getElement(arguments[0]);
+
+ // Append remaining elements
+ for (var i=1; i<arguments.length; i++)
+ message += "," + getElement(arguments[i]);
+
+ // Final terminator
+ message += ";";
+
+ // Add message to buffer
+ outputMessageBuffer += message;
+
+ // Send if not currently sending
+ if (!sendingMessages)
+ sendPendingMessages();
+
+ };
+
+ function sendPendingMessages() {
+
+ // Do not attempt to send messages if not connected
+ if (!tunnel.isConnected())
+ return;
+
+ if (outputMessageBuffer.length > 0) {
+
+ sendingMessages = true;
+
+ var message_xmlhttprequest = new XMLHttpRequest();
+ message_xmlhttprequest.open("POST", TUNNEL_WRITE + tunnel.uuid);
+ message_xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(message_xmlhttprequest, extraHeaders);
+ message_xmlhttprequest.setRequestHeader("Content-type", "application/octet-stream");
+
+ // Once response received, send next queued event.
+ message_xmlhttprequest.onreadystatechange = function() {
+ if (message_xmlhttprequest.readyState === 4) {
+
+ reset_timeout();
+
+ // If an error occurs during send, handle it
+ if (message_xmlhttprequest.status !== 200)
+ handleHTTPTunnelError(message_xmlhttprequest);
+
+ // Otherwise, continue the send loop
+ else
+ sendPendingMessages();
+
+ }
+ };
+
+ message_xmlhttprequest.send(outputMessageBuffer);
+ outputMessageBuffer = ""; // Clear buffer
+
+ }
+ else
+ sendingMessages = false;
+
+ }
+
+ function handleHTTPTunnelError(xmlhttprequest) {
+
+ // Pull status code directly from headers provided by Guacamole
+ var code = parseInt(xmlhttprequest.getResponseHeader("Guacamole-Status-Code"));
+ if (code) {
+ var message = xmlhttprequest.getResponseHeader("Guacamole-Error-Message");
+ close_tunnel(new Guacamole.Status(code, message));
+ }
+
+ // Failing that, derive a Guacamole status code from the HTTP status
+ // code provided by the browser
+ else if (xmlhttprequest.status)
+ close_tunnel(new Guacamole.Status(
+ Guacamole.Status.Code.fromHTTPCode(xmlhttprequest.status),
+ xmlhttprequest.statusText));
+
+ // Otherwise, assume server is unreachable
+ else
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_NOT_FOUND));
+
+ }
+
+ function handleResponse(xmlhttprequest) {
+
+ var interval = null;
+ var nextRequest = null;
+
+ var dataUpdateEvents = 0;
+
+ // The location of the last element's terminator
+ var elementEnd = -1;
+
+ // Where to start the next length search or the next element
+ var startIndex = 0;
+
+ // Parsed elements
+ var elements = new Array();
+
+ function parseResponse() {
+
+ // Do not handle responses if not connected
+ if (!tunnel.isConnected()) {
+
+ // Clean up interval if polling
+ if (interval !== null)
+ clearInterval(interval);
+
+ return;
+ }
+
+ // Do not parse response yet if not ready
+ if (xmlhttprequest.readyState < 2) return;
+
+ // Attempt to read status
+ var status;
+ try { status = xmlhttprequest.status; }
+
+ // If status could not be read, assume successful.
+ catch (e) { status = 200; }
+
+ // Start next request as soon as possible IF request was successful
+ if (!nextRequest && status === 200)
+ nextRequest = makeRequest();
+
+ // Parse stream when data is received and when complete.
+ if (xmlhttprequest.readyState === 3 ||
+ xmlhttprequest.readyState === 4) {
+
+ reset_timeout();
+
+ // Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data)
+ if (pollingMode === POLLING_ENABLED) {
+ if (xmlhttprequest.readyState === 3 && !interval)
+ interval = setInterval(parseResponse, 30);
+ else if (xmlhttprequest.readyState === 4 && interval)
+ clearInterval(interval);
+ }
+
+ // If canceled, stop transfer
+ if (xmlhttprequest.status === 0) {
+ tunnel.disconnect();
+ return;
+ }
+
+ // Halt on error during request
+ else if (xmlhttprequest.status !== 200) {
+ handleHTTPTunnelError(xmlhttprequest);
+ return;
+ }
+
+ // Attempt to read in-progress data
+ var current;
+ try { current = xmlhttprequest.responseText; }
+
+ // Do not attempt to parse if data could not be read
+ catch (e) { return; }
+
+ // While search is within currently received data
+ while (elementEnd < current.length) {
+
+ // If we are waiting for element data
+ if (elementEnd >= startIndex) {
+
+ // We now have enough data for the element. Parse.
+ var element = current.substring(startIndex, elementEnd);
+ var terminator = current.substring(elementEnd, elementEnd+1);
+
+ // Add element to array
+ elements.push(element);
+
+ // If last element, handle instruction
+ if (terminator === ";") {
+
+ // Get opcode
+ var opcode = elements.shift();
+
+ // Call instruction handler.
+ if (tunnel.oninstruction)
+ tunnel.oninstruction(opcode, elements);
+
+ // Clear elements
+ elements.length = 0;
+
+ }
+
+ // Start searching for length at character after
+ // element terminator
+ startIndex = elementEnd + 1;
+
+ }
+
+ // Search for end of length
+ var lengthEnd = current.indexOf(".", startIndex);
+ if (lengthEnd !== -1) {
+
+ // Parse length
+ var length = parseInt(current.substring(elementEnd+1, lengthEnd));
+
+ // If we're done parsing, handle the next response.
+ if (length === 0) {
+
+ // Clean up interval if polling
+ if (interval)
+ clearInterval(interval);
+
+ // Clean up object
+ xmlhttprequest.onreadystatechange = null;
+ xmlhttprequest.abort();
+
+ // Start handling next request
+ if (nextRequest)
+ handleResponse(nextRequest);
+
+ // Done parsing
+ break;
+
+ }
+
+ // Calculate start of element
+ startIndex = lengthEnd + 1;
+
+ // Calculate location of element terminator
+ elementEnd = startIndex + length;
+
+ }
+
+ // If no period yet, continue search when more data
+ // is received
+ else {
+ startIndex = current.length;
+ break;
+ }
+
+ } // end parse loop
+
+ }
+
+ }
+
+ // If response polling enabled, attempt to detect if still
+ // necessary (via wrapping parseResponse())
+ if (pollingMode === POLLING_ENABLED) {
+ xmlhttprequest.onreadystatechange = function() {
+
+ // If we receive two or more readyState==3 events,
+ // there is no need to poll.
+ if (xmlhttprequest.readyState === 3) {
+ dataUpdateEvents++;
+ if (dataUpdateEvents >= 2) {
+ pollingMode = POLLING_DISABLED;
+ xmlhttprequest.onreadystatechange = parseResponse;
+ }
+ }
+
+ parseResponse();
+ };
+ }
+
+ // Otherwise, just parse
+ else
+ xmlhttprequest.onreadystatechange = parseResponse;
+
+ parseResponse();
+
+ }
+
+ /**
+ * Arbitrary integer, unique for each tunnel read request.
+ * @private
+ */
+ var request_id = 0;
+
+ function makeRequest() {
+
+ // Make request, increment request ID
+ var xmlhttprequest = new XMLHttpRequest();
+ xmlhttprequest.open("GET", TUNNEL_READ + tunnel.uuid + ":" + (request_id++));
+ xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(xmlhttprequest, extraHeaders);
+ xmlhttprequest.send(null);
+
+ return xmlhttprequest;
+
+ }
+
+ this.connect = function(data) {
+
+ // Start waiting for connect
+ reset_timeout();
+
+ // Mark the tunnel as connecting
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
+
+ // Start tunnel and connect
+ var connect_xmlhttprequest = new XMLHttpRequest();
+ connect_xmlhttprequest.onreadystatechange = function() {
+
+ if (connect_xmlhttprequest.readyState !== 4)
+ return;
+
+ // If failure, throw error
+ if (connect_xmlhttprequest.status !== 200) {
+ handleHTTPTunnelError(connect_xmlhttprequest);
+ return;
+ }
+
+ reset_timeout();
+
+ // Get UUID from response
+ tunnel.uuid = connect_xmlhttprequest.responseText;
+
+ // Mark as open
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
+
+ // Ping tunnel endpoint regularly to test connection stability
+ pingInterval = setInterval(function sendPing() {
+ tunnel.sendMessage("nop");
+ }, PING_FREQUENCY);
+
+ // Start reading data
+ handleResponse(makeRequest());
+
+ };
+
+ connect_xmlhttprequest.open("POST", TUNNEL_CONNECT, true);
+ connect_xmlhttprequest.withCredentials = withCredentials;
+ addExtraHeaders(connect_xmlhttprequest, extraHeaders);
+ connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
+ connect_xmlhttprequest.send(data);
+
+ };
+
+ this.disconnect = function() {
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SUCCESS, "Manually closed."));
+ };
+
+};
+
+Guacamole.HTTPTunnel.prototype = new Guacamole.Tunnel();
+
+/**
+ * Guacamole Tunnel implemented over WebSocket via XMLHttpRequest.
+ *
+ * @constructor
+ * @augments Guacamole.Tunnel
+ * @param {String} tunnelURL The URL of the WebSocket tunneling service.
+ */
+Guacamole.WebSocketTunnel = function(tunnelURL) {
+
+ /**
+ * Reference to this WebSocket tunnel.
+ * @private
+ */
+ var tunnel = this;
+
+ /**
+ * The WebSocket used by this tunnel.
+ * @private
+ */
+ var socket = null;
+
+ /**
+ * The current receive timeout ID, if any.
+ * @private
+ */
+ var receive_timeout = null;
+
+ /**
+ * The current connection stability timeout ID, if any.
+ *
+ * @private
+ * @type {Number}
+ */
+ var unstableTimeout = null;
+
+ /**
+ * The current connection stability test ping interval ID, if any. This
+ * will only be set upon successful connection.
+ *
+ * @private
+ * @type {Number}
+ */
+ var pingInterval = null;
+
+ /**
+ * The WebSocket protocol corresponding to the protocol used for the current
+ * location.
+ * @private
+ */
+ var ws_protocol = {
+ "http:": "ws:",
+ "https:": "wss:"
+ };
+
+ /**
+ * The number of milliseconds to wait between connection stability test
+ * pings.
+ *
+ * @private
+ * @constant
+ * @type {Number}
+ */
+ var PING_FREQUENCY = 500;
+
+ // Transform current URL to WebSocket URL
+
+ // If not already a websocket URL
+ if ( tunnelURL.substring(0, 3) !== "ws:"
+ && tunnelURL.substring(0, 4) !== "wss:") {
+
+ var protocol = ws_protocol[window.location.protocol];
+
+ // If absolute URL, convert to absolute WS URL
+ if (tunnelURL.substring(0, 1) === "/")
+ tunnelURL =
+ protocol
+ + "//" + window.location.host
+ + tunnelURL;
+
+ // Otherwise, construct absolute from relative URL
+ else {
+
+ // Get path from pathname
+ var slash = window.location.pathname.lastIndexOf("/");
+ var path = window.location.pathname.substring(0, slash + 1);
+
+ // Construct absolute URL
+ tunnelURL =
+ protocol
+ + "//" + window.location.host
+ + path
+ + tunnelURL;
+
+ }
+
+ }
+
+ /**
+ * Initiates a timeout which, if data is not received, causes the tunnel
+ * to close with an error.
+ *
+ * @private
+ */
+ function reset_timeout() {
+
+ // Get rid of old timeouts (if any)
+ window.clearTimeout(receive_timeout);
+ window.clearTimeout(unstableTimeout);
+
+ // Clear unstable status
+ if (tunnel.state === Guacamole.Tunnel.State.UNSTABLE)
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
+
+ // Set new timeout for tracking overall connection timeout
+ receive_timeout = window.setTimeout(function () {
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_TIMEOUT, "Server timeout."));
+ }, tunnel.receiveTimeout);
+
+ // Set new timeout for tracking suspected connection instability
+ unstableTimeout = window.setTimeout(function() {
+ tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
+ }, tunnel.unstableThreshold);
+
+ }
+
+ /**
+ * Closes this tunnel, signaling the given status and corresponding
+ * message, which will be sent to the onerror handler if the status is
+ * an error status.
+ *
+ * @private
+ * @param {Guacamole.Status} status The status causing the connection to
+ * close;
+ */
+ function close_tunnel(status) {
+
+ // Get rid of old timeouts (if any)
+ window.clearTimeout(receive_timeout);
+ window.clearTimeout(unstableTimeout);
+
+ // Cease connection test pings
+ window.clearInterval(pingInterval);
+
+ // Ignore if already closed
+ if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
+ return;
+
+ // If connection closed abnormally, signal error.
+ if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)
+ tunnel.onerror(status);
+
+ // Mark as closed
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
+
+ socket.close();
+
+ }
+
+ this.sendMessage = function(elements) {
+
+ // Do not attempt to send messages if not connected
+ if (!tunnel.isConnected())
+ return;
+
+ // Do not attempt to send empty messages
+ if (arguments.length === 0)
+ return;
+
+ /**
+ * Converts the given value to a length/string pair for use as an
+ * element in a Guacamole instruction.
+ *
+ * @private
+ * @param value The value to convert.
+ * @return {String} The converted value.
+ */
+ function getElement(value) {
+ var string = new String(value);
+ return string.length + "." + string;
+ }
+
+ // Initialized message with first element
+ var message = getElement(arguments[0]);
+
+ // Append remaining elements
+ for (var i=1; i<arguments.length; i++)
+ message += "," + getElement(arguments[i]);
+
+ // Final terminator
+ message += ";";
+
+ socket.send(message);
+
+ };
+
+ this.connect = function(data) {
+
+ reset_timeout();
+
+ // Mark the tunnel as connecting
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
+
+ // Connect socket
+ socket = new WebSocket(tunnelURL + "?" + data, "guacamole");
+
+ socket.onopen = function(event) {
+ reset_timeout();
+
+ // Ping tunnel endpoint regularly to test connection stability
+ pingInterval = setInterval(function sendPing() {
+ tunnel.sendMessage(Guacamole.Tunnel.INTERNAL_DATA_OPCODE,
+ "ping", new Date().getTime());
+ }, PING_FREQUENCY);
+
+ };
+
+ socket.onclose = function(event) {
+
+ // Pull status code directly from closure reason provided by Guacamole
+ if (event.reason)
+ close_tunnel(new Guacamole.Status(parseInt(event.reason), event.reason));
+
+ // Failing that, derive a Guacamole status code from the WebSocket
+ // status code provided by the browser
+ else if (event.code)
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.fromWebSocketCode(event.code)));
+
+ // Otherwise, assume server is unreachable
+ else
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_NOT_FOUND));
+
+ };
+
+ socket.onmessage = function(event) {
+
+ reset_timeout();
+
+ var message = event.data;
+ var startIndex = 0;
+ var elementEnd;
+
+ var elements = [];
+
+ do {
+
+ // Search for end of length
+ var lengthEnd = message.indexOf(".", startIndex);
+ if (lengthEnd !== -1) {
+
+ // Parse length
+ var length = parseInt(message.substring(elementEnd+1, lengthEnd));
+
+ // Calculate start of element
+ startIndex = lengthEnd + 1;
+
+ // Calculate location of element terminator
+ elementEnd = startIndex + length;
+
+ }
+
+ // If no period, incomplete instruction.
+ else
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SERVER_ERROR, "Incomplete instruction."));
+
+ // We now have enough data for the element. Parse.
+ var element = message.substring(startIndex, elementEnd);
+ var terminator = message.substring(elementEnd, elementEnd+1);
+
+ // Add element to array
+ elements.push(element);
+
+ // If last element, handle instruction
+ if (terminator === ";") {
+
+ // Get opcode
+ var opcode = elements.shift();
+
+ // Update state and UUID when first instruction received
+ if (tunnel.state === Guacamole.Tunnel.State.CONNECTING) {
+
+ // Associate tunnel UUID if received
+ if (opcode === Guacamole.Tunnel.INTERNAL_DATA_OPCODE)
+ tunnel.uuid = elements[0];
+
+ // Tunnel is now open and UUID is available
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
+
+ }
+
+ // Call instruction handler.
+ if (opcode !== Guacamole.Tunnel.INTERNAL_DATA_OPCODE && tunnel.oninstruction)
+ tunnel.oninstruction(opcode, elements);
+
+ // Clear elements
+ elements.length = 0;
+
+ }
+
+ // Start searching for length at character after
+ // element terminator
+ startIndex = elementEnd + 1;
+
+ } while (startIndex < message.length);
+
+ };
+
+ };
+
+ this.disconnect = function() {
+ close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SUCCESS, "Manually closed."));
+ };
+
+};
+
+Guacamole.WebSocketTunnel.prototype = new Guacamole.Tunnel();
+
+/**
+ * Guacamole Tunnel which cycles between all specified tunnels until
+ * no tunnels are left. Another tunnel is used if an error occurs but
+ * no instructions have been received. If an instruction has been
+ * received, or no tunnels remain, the error is passed directly out
+ * through the onerror handler (if defined).
+ *
+ * @constructor
+ * @augments Guacamole.Tunnel
+ * @param {...*} tunnelChain
+ * The tunnels to use, in order of priority.
+ */
+Guacamole.ChainedTunnel = function(tunnelChain) {
+
+ /**
+ * Reference to this chained tunnel.
+ * @private
+ */
+ var chained_tunnel = this;
+
+ /**
+ * Data passed in via connect(), to be used for
+ * wrapped calls to other tunnels' connect() functions.
+ * @private
+ */
+ var connect_data;
+
+ /**
+ * Array of all tunnels passed to this ChainedTunnel through the
+ * constructor arguments.
+ * @private
+ */
+ var tunnels = [];
+
+ /**
+ * The tunnel committed via commit_tunnel(), if any, or null if no tunnel
+ * has yet been committed.
+ *
+ * @private
+ * @type {Guacamole.Tunnel}
+ */
+ var committedTunnel = null;
+
+ // Load all tunnels into array
+ for (var i=0; i<arguments.length; i++)
+ tunnels.push(arguments[i]);
+
+ /**
+ * Sets the current tunnel.
+ *
+ * @private
+ * @param {Guacamole.Tunnel} tunnel The tunnel to set as the current tunnel.
+ */
+ function attach(tunnel) {
+
+ // Set own functions to tunnel's functions
+ chained_tunnel.disconnect = tunnel.disconnect;
+ chained_tunnel.sendMessage = tunnel.sendMessage;
+
+ /**
+ * Fails the currently-attached tunnel, attaching a new tunnel if
+ * possible.
+ *
+ * @private
+ * @param {Guacamole.Status} [status]
+ * An object representing the failure that occured in the
+ * currently-attached tunnel, if known.
+ *
+ * @return {Guacamole.Tunnel}
+ * The next tunnel, or null if there are no more tunnels to try or
+ * if no more tunnels should be tried.
+ */
+ var failTunnel = function failTunnel(status) {
+
+ // Do not attempt to continue using next tunnel on server timeout
+ if (status && status.code === Guacamole.Status.Code.UPSTREAM_TIMEOUT) {
+ tunnels = [];
+ return null;
+ }
+
+ // Get next tunnel
+ var next_tunnel = tunnels.shift();
+
+ // If there IS a next tunnel, try using it.
+ if (next_tunnel) {
+ tunnel.onerror = null;
+ tunnel.oninstruction = null;
+ tunnel.onstatechange = null;
+ attach(next_tunnel);
+ }
+
+ return next_tunnel;
+
+ };
+
+ /**
+ * Use the current tunnel from this point forward. Do not try any more
+ * tunnels, even if the current tunnel fails.
+ *
+ * @private
+ */
+ function commit_tunnel() {
+ tunnel.onstatechange = chained_tunnel.onstatechange;
+ tunnel.oninstruction = chained_tunnel.oninstruction;
+ tunnel.onerror = chained_tunnel.onerror;
+ chained_tunnel.uuid = tunnel.uuid;
+ committedTunnel = tunnel;
+ }
+
+ // Wrap own onstatechange within current tunnel
+ tunnel.onstatechange = function(state) {
+
+ switch (state) {
+
+ // If open, use this tunnel from this point forward.
+ case Guacamole.Tunnel.State.OPEN:
+ commit_tunnel();
+ if (chained_tunnel.onstatechange)
+ chained_tunnel.onstatechange(state);
+ break;
+
+ // If closed, mark failure, attempt next tunnel
+ case Guacamole.Tunnel.State.CLOSED:
+ if (!failTunnel() && chained_tunnel.onstatechange)
+ chained_tunnel.onstatechange(state);
+ break;
+
+ }
+
+ };
+
+ // Wrap own oninstruction within current tunnel
+ tunnel.oninstruction = function(opcode, elements) {
+
+ // Accept current tunnel
+ commit_tunnel();
+
+ // Invoke handler
+ if (chained_tunnel.oninstruction)
+ chained_tunnel.oninstruction(opcode, elements);
+
+ };
+
+ // Attach next tunnel on error
+ tunnel.onerror = function(status) {
+
+ // Mark failure, attempt next tunnel
+ if (!failTunnel(status) && chained_tunnel.onerror)
+ chained_tunnel.onerror(status);
+
+ };
+
+ // Attempt connection
+ tunnel.connect(connect_data);
+
+ }
+
+ this.connect = function(data) {
+
+ // Remember connect data
+ connect_data = data;
+
+ // Get committed tunnel if exists or the first tunnel on the list
+ var next_tunnel = committedTunnel ? committedTunnel : tunnels.shift();
+
+ // Attach first tunnel
+ if (next_tunnel)
+ attach(next_tunnel);
+
+ // If there IS no first tunnel, error
+ else if (chained_tunnel.onerror)
+ chained_tunnel.onerror(Guacamole.Status.Code.SERVER_ERROR, "No tunnels to try.");
+
+ };
+
+};
+
+Guacamole.ChainedTunnel.prototype = new Guacamole.Tunnel();
+
+/**
+ * Guacamole Tunnel which replays a Guacamole protocol dump from a static file
+ * received via HTTP. Instructions within the file are parsed and handled as
+ * quickly as possible, while the file is being downloaded.
+ *
+ * @constructor
+ * @augments Guacamole.Tunnel
+ * @param {String} url
+ * The URL of a Guacamole protocol dump.
+ *
+ * @param {Boolean} [crossDomain=false]
+ * Whether tunnel requests will be cross-domain, and thus must use CORS
+ * mechanisms and headers. By default, it is assumed that tunnel requests
+ * will be made to the same domain.
+ *
+ * @param {Object} [extraTunnelHeaders={}]
+ * Key value pairs containing the header names and values of any additional
+ * headers to be sent in tunnel requests. By default, no extra headers will
+ * be added.
+ */
+Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTunnelHeaders) {
+
+ /**
+ * Reference to this Guacamole.StaticHTTPTunnel.
+ *
+ * @private
+ */
+ var tunnel = this;
+
+ /**
+ * The current, in-progress HTTP request. If no request is currently in
+ * progress, this will be null.
+ *
+ * @private
+ * @type {XMLHttpRequest}
+ */
+ var xhr = null;
+
+ /**
+ * Additional headers to be sent in tunnel requests. This dictionary can be
+ * populated with key/value header pairs to pass information such as authentication
+ * tokens, etc.
+ *
+ * @private
+ */
+ var extraHeaders = extraTunnelHeaders || {};
+
+ /**
+ * Adds the configured additional headers to the given request.
+ *
+ * @param {XMLHttpRequest} request
+ * The request where the configured extra headers will be added.
+ *
+ * @param {Object} headers
+ * The headers to be added to the request.
+ *
+ * @private
+ */
+ function addExtraHeaders(request, headers) {
+ for (var name in headers) {
+ request.setRequestHeader(name, headers[name]);
+ }
+ }
+
+ this.sendMessage = function sendMessage(elements) {
+ // Do nothing
+ };
+
+ this.connect = function connect(data) {
+
+ // Ensure any existing connection is killed
+ tunnel.disconnect();
+
+ // Connection is now starting
+ tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
+
+ // Start a new connection
+ xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.withCredentials = !!crossDomain;
+ addExtraHeaders(xhr, extraHeaders);
+ xhr.responseType = 'text';
+ xhr.send(null);
+
+ var offset = 0;
+
+ // Create Guacamole protocol parser specifically for this connection
+ var parser = new Guacamole.Parser();
+
+ // Invoke tunnel's oninstruction handler for each parsed instruction
+ parser.oninstruction = function instructionReceived(opcode, args) {
+ if (tunnel.oninstruction)
+ tunnel.oninstruction(opcode, args);
+ };
+
+ // Continuously parse received data
+ xhr.onreadystatechange = function readyStateChanged() {
+
+ // Parse while data is being received
+ if (xhr.readyState === 3 || xhr.readyState === 4) {
+
+ // Connection is open
+ tunnel.setState(Guacamole.Tunnel.State.OPEN);
+
+ var buffer = xhr.responseText;
+ var length = buffer.length;
+
+ // Parse only the portion of data which is newly received
+ if (offset < length) {
+ parser.receive(buffer.substring(offset));
+ offset = length;
+ }
+
+ }
+
+ // Clean up and close when done
+ if (xhr.readyState === 4)
+ tunnel.disconnect();
+
+ };
+
+ // Reset state and close upon error
+ xhr.onerror = function httpError() {
+
+ // Fail if file could not be downloaded via HTTP
+ if (tunnel.onerror)
+ tunnel.onerror(new Guacamole.Status(
+ Guacamole.Status.Code.fromHTTPCode(xhr.status), xhr.statusText));
+
+ tunnel.disconnect();
+ };
+
+ };
+
+ this.disconnect = function disconnect() {
+
+ // Abort and dispose of XHR if a request is in progress
+ if (xhr) {
+ xhr.abort();
+ xhr = null;
+ }
+
+ // Connection is now closed
+ tunnel.setState(Guacamole.Tunnel.State.CLOSED);
+
+ };
+
+};
+
+Guacamole.StaticHTTPTunnel.prototype = new Guacamole.Tunnel();
+</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>