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 2017/07/06 02:29:39 UTC
[50/51] [partial] incubator-guacamole-website git commit: Deploy
draft documentation for 0.9.13-incubating.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/AudioPlayer.js.html
----------------------------------------------------------------------
diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/AudioPlayer.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/AudioPlayer.js.html
new file mode 100644
index 0000000..a550cd0
--- /dev/null
+++ b/content/doc/0.9.13-incubating/guacamole-common-js/AudioPlayer.js.html
@@ -0,0 +1,566 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: AudioPlayer.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: AudioPlayer.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 || {};
+
+/**
+ * Abstract audio player which accepts, queues and plays back arbitrary audio
+ * data. It is up to implementations of this class to provide some means of
+ * handling a provided Guacamole.InputStream. Data received along the provided
+ * stream is to be played back immediately.
+ *
+ * @constructor
+ */
+Guacamole.AudioPlayer = function AudioPlayer() {
+
+ /**
+ * Notifies this Guacamole.AudioPlayer that all audio up to the current
+ * point in time has been given via the underlying stream, and that any
+ * difference in time between queued audio data and the current time can be
+ * considered latency.
+ */
+ this.sync = function sync() {
+ // Default implementation - do nothing
+ };
+
+};
+
+/**
+ * Determines whether the given mimetype is supported by any built-in
+ * implementation of Guacamole.AudioPlayer, and thus will be properly handled
+ * by Guacamole.AudioPlayer.getInstance().
+ *
+ * @param {String} mimetype
+ * The mimetype to check.
+ *
+ * @returns {Boolean}
+ * true if the given mimetype is supported by any built-in
+ * Guacamole.AudioPlayer, false otherwise.
+ */
+Guacamole.AudioPlayer.isSupportedType = function isSupportedType(mimetype) {
+
+ return Guacamole.RawAudioPlayer.isSupportedType(mimetype);
+
+};
+
+/**
+ * Returns a list of all mimetypes supported by any built-in
+ * Guacamole.AudioPlayer, in rough order of priority. Beware that only the core
+ * mimetypes themselves will be listed. Any mimetype parameters, even required
+ * ones, will not be included in the list. For example, "audio/L8" is a
+ * supported raw audio mimetype that is supported, but it is invalid without
+ * additional parameters. Something like "audio/L8;rate=44100" would be valid,
+ * however (see https://tools.ietf.org/html/rfc4856).
+ *
+ * @returns {String[]}
+ * A list of all mimetypes supported by any built-in Guacamole.AudioPlayer,
+ * excluding any parameters.
+ */
+Guacamole.AudioPlayer.getSupportedTypes = function getSupportedTypes() {
+
+ return Guacamole.RawAudioPlayer.getSupportedTypes();
+
+};
+
+/**
+ * Returns an instance of Guacamole.AudioPlayer providing support for the given
+ * audio format. If support for the given audio format is not available, null
+ * is returned.
+ *
+ * @param {Guacamole.InputStream} stream
+ * The Guacamole.InputStream to read audio data from.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data in the provided stream.
+ *
+ * @return {Guacamole.AudioPlayer}
+ * A Guacamole.AudioPlayer instance supporting the given mimetype and
+ * reading from the given stream, or null if support for the given mimetype
+ * is absent.
+ */
+Guacamole.AudioPlayer.getInstance = function getInstance(stream, mimetype) {
+
+ // Use raw audio player if possible
+ if (Guacamole.RawAudioPlayer.isSupportedType(mimetype))
+ return new Guacamole.RawAudioPlayer(stream, mimetype);
+
+ // No support for given mimetype
+ return null;
+
+};
+
+/**
+ * Implementation of Guacamole.AudioPlayer providing support for raw PCM format
+ * audio. This player relies only on the Web Audio API and does not require any
+ * browser-level support for its audio formats.
+ *
+ * @constructor
+ * @augments Guacamole.AudioPlayer
+ * @param {Guacamole.InputStream} stream
+ * The Guacamole.InputStream to read audio data from.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data in the provided stream, which must be a
+ * "audio/L8" or "audio/L16" mimetype with necessary parameters, such as:
+ * "audio/L16;rate=44100,channels=2".
+ */
+Guacamole.RawAudioPlayer = function RawAudioPlayer(stream, mimetype) {
+
+ /**
+ * The format of audio this player will decode.
+ *
+ * @private
+ * @type {Guacamole.RawAudioFormat}
+ */
+ var format = Guacamole.RawAudioFormat.parse(mimetype);
+
+ /**
+ * An instance of a Web Audio API AudioContext object, or null if the
+ * Web Audio API is not supported.
+ *
+ * @private
+ * @type {AudioContext}
+ */
+ var context = Guacamole.AudioContextFactory.getAudioContext();
+
+ /**
+ * The earliest possible time that the next packet could play without
+ * overlapping an already-playing packet, in seconds. Note that while this
+ * value is in seconds, it is not an integer value and has microsecond
+ * resolution.
+ *
+ * @private
+ * @type {Number}
+ */
+ var nextPacketTime = context.currentTime;
+
+ /**
+ * Guacamole.ArrayBufferReader wrapped around the audio input stream
+ * provided with this Guacamole.RawAudioPlayer was created.
+ *
+ * @private
+ * @type {Guacamole.ArrayBufferReader}
+ */
+ var reader = new Guacamole.ArrayBufferReader(stream);
+
+ /**
+ * The minimum size of an audio packet split by splitAudioPacket(), in
+ * seconds. Audio packets smaller than this will not be split, nor will the
+ * split result of a larger packet ever be smaller in size than this
+ * minimum.
+ *
+ * @private
+ * @constant
+ * @type {Number}
+ */
+ var MIN_SPLIT_SIZE = 0.02;
+
+ /**
+ * The maximum amount of latency to allow between the buffered data stream
+ * and the playback position, in seconds. Initially, this is set to
+ * roughly one third of a second.
+ *
+ * @private
+ * @type {Number}
+ */
+ var maxLatency = 0.3;
+
+ /**
+ * The type of typed array that will be used to represent each audio packet
+ * internally. This will be either Int8Array or Int16Array, depending on
+ * whether the raw audio format is 8-bit or 16-bit.
+ *
+ * @private
+ * @constructor
+ */
+ var SampleArray = (format.bytesPerSample === 1) ? window.Int8Array : window.Int16Array;
+
+ /**
+ * The maximum absolute value of any sample within a raw audio packet
+ * received by this audio player. This depends only on the size of each
+ * sample, and will be 128 for 8-bit audio and 32768 for 16-bit audio.
+ *
+ * @private
+ * @type {Number}
+ */
+ var maxSampleValue = (format.bytesPerSample === 1) ? 128 : 32768;
+
+ /**
+ * The queue of all pending audio packets, as an array of sample arrays.
+ * Audio packets which are pending playback will be added to this queue for
+ * further manipulation prior to scheduling via the Web Audio API. Once an
+ * audio packet leaves this queue and is scheduled via the Web Audio API,
+ * no further modifications can be made to that packet.
+ *
+ * @private
+ * @type {SampleArray[]}
+ */
+ var packetQueue = [];
+
+ /**
+ * Given an array of audio packets, returns a single audio packet
+ * containing the concatenation of those packets.
+ *
+ * @private
+ * @param {SampleArray[]} packets
+ * The array of audio packets to concatenate.
+ *
+ * @returns {SampleArray}
+ * A single audio packet containing the concatenation of all given
+ * audio packets. If no packets are provided, this will be undefined.
+ */
+ var joinAudioPackets = function joinAudioPackets(packets) {
+
+ // Do not bother joining if one or fewer packets are in the queue
+ if (packets.length <= 1)
+ return packets[0];
+
+ // Determine total sample length of the entire queue
+ var totalLength = 0;
+ packets.forEach(function addPacketLengths(packet) {
+ totalLength += packet.length;
+ });
+
+ // Append each packet within queue
+ var offset = 0;
+ var joined = new SampleArray(totalLength);
+ packets.forEach(function appendPacket(packet) {
+ joined.set(packet, offset);
+ offset += packet.length;
+ });
+
+ return joined;
+
+ };
+
+ /**
+ * Given a single packet of audio data, splits off an arbitrary length of
+ * audio data from the beginning of that packet, returning the split result
+ * as an array of two packets. The split location is determined through an
+ * algorithm intended to minimize the liklihood of audible clicking between
+ * packets. If no such split location is possible, an array containing only
+ * the originally-provided audio packet is returned.
+ *
+ * @private
+ * @param {SampleArray} data
+ * The audio packet to split.
+ *
+ * @returns {SampleArray[]}
+ * An array of audio packets containing the result of splitting the
+ * provided audio packet. If splitting is possible, this array will
+ * contain two packets. If splitting is not possible, this array will
+ * contain only the originally-provided packet.
+ */
+ var splitAudioPacket = function splitAudioPacket(data) {
+
+ var minValue = Number.MAX_VALUE;
+ var optimalSplitLength = data.length;
+
+ // Calculate number of whole samples in the provided audio packet AND
+ // in the minimum possible split packet
+ var samples = Math.floor(data.length / format.channels);
+ var minSplitSamples = Math.floor(format.rate * MIN_SPLIT_SIZE);
+
+ // Calculate the beginning of the "end" of the audio packet
+ var start = Math.max(
+ format.channels * minSplitSamples,
+ format.channels * (samples - minSplitSamples)
+ );
+
+ // For all samples at the end of the given packet, find a point where
+ // the perceptible volume across all channels is lowest (and thus is
+ // the optimal point to split)
+ for (var offset = start; offset < data.length; offset += format.channels) {
+
+ // Calculate the sum of all values across all channels (the result
+ // will be proportional to the average volume of a sample)
+ var totalValue = 0;
+ for (var channel = 0; channel < format.channels; channel++) {
+ totalValue += Math.abs(data[offset + channel]);
+ }
+
+ // If this is the smallest average value thus far, set the split
+ // length such that the first packet ends with the current sample
+ if (totalValue <= minValue) {
+ optimalSplitLength = offset + format.channels;
+ minValue = totalValue;
+ }
+
+ }
+
+ // If packet is not split, return the supplied packet untouched
+ if (optimalSplitLength === data.length)
+ return [data];
+
+ // Otherwise, split the packet into two new packets according to the
+ // calculated optimal split length
+ return [
+ new SampleArray(data.buffer.slice(0, optimalSplitLength * format.bytesPerSample)),
+ new SampleArray(data.buffer.slice(optimalSplitLength * format.bytesPerSample))
+ ];
+
+ };
+
+ /**
+ * Pushes the given packet of audio data onto the playback queue. Unlike
+ * other private functions within Guacamole.RawAudioPlayer, the type of the
+ * ArrayBuffer packet of audio data here need not be specific to the type
+ * of audio (as with SampleArray). The ArrayBuffer type provided by a
+ * Guacamole.ArrayBufferReader, for example, is sufficient. Any necessary
+ * conversions will be performed automatically internally.
+ *
+ * @private
+ * @param {ArrayBuffer} data
+ * A raw packet of audio data that should be pushed onto the audio
+ * playback queue.
+ */
+ var pushAudioPacket = function pushAudioPacket(data) {
+ packetQueue.push(new SampleArray(data));
+ };
+
+ /**
+ * Shifts off and returns a packet of audio data from the beginning of the
+ * playback queue. The length of this audio packet is determined
+ * dynamically according to the click-reduction algorithm implemented by
+ * splitAudioPacket().
+ *
+ * @private
+ * @returns {SampleArray}
+ * A packet of audio data pulled from the beginning of the playback
+ * queue.
+ */
+ var shiftAudioPacket = function shiftAudioPacket() {
+
+ // Flatten data in packet queue
+ var data = joinAudioPackets(packetQueue);
+ if (!data)
+ return null;
+
+ // Pull an appropriate amount of data from the front of the queue
+ packetQueue = splitAudioPacket(data);
+ data = packetQueue.shift();
+
+ return data;
+
+ };
+
+ /**
+ * Converts the given audio packet into an AudioBuffer, ready for playback
+ * by the Web Audio API. Unlike the raw audio packets received by this
+ * audio player, AudioBuffers require floating point samples and are split
+ * into isolated planes of channel-specific data.
+ *
+ * @private
+ * @param {SampleArray} data
+ * The raw audio packet that should be converted into a Web Audio API
+ * AudioBuffer.
+ *
+ * @returns {AudioBuffer}
+ * A new Web Audio API AudioBuffer containing the provided audio data,
+ * converted to the format used by the Web Audio API.
+ */
+ var toAudioBuffer = function toAudioBuffer(data) {
+
+ // Calculate total number of samples
+ var samples = data.length / format.channels;
+
+ // Determine exactly when packet CAN play
+ var packetTime = context.currentTime;
+ if (nextPacketTime < packetTime)
+ nextPacketTime = packetTime;
+
+ // Get audio buffer for specified format
+ var audioBuffer = context.createBuffer(format.channels, samples, format.rate);
+
+ // Convert each channel
+ for (var channel = 0; channel < format.channels; channel++) {
+
+ var audioData = audioBuffer.getChannelData(channel);
+
+ // Fill audio buffer with data for channel
+ var offset = channel;
+ for (var i = 0; i < samples; i++) {
+ audioData[i] = data[offset] / maxSampleValue;
+ offset += format.channels;
+ }
+
+ }
+
+ return audioBuffer;
+
+ };
+
+ // Defer playback of received audio packets slightly
+ reader.ondata = function playReceivedAudio(data) {
+
+ // Push received samples onto queue
+ pushAudioPacket(new SampleArray(data));
+
+ // Shift off an arbitrary packet of audio data from the queue (this may
+ // be different in size from the packet just pushed)
+ var packet = shiftAudioPacket();
+ if (!packet)
+ return;
+
+ // Determine exactly when packet CAN play
+ var packetTime = context.currentTime;
+ if (nextPacketTime < packetTime)
+ nextPacketTime = packetTime;
+
+ // Set up buffer source
+ var source = context.createBufferSource();
+ source.connect(context.destination);
+
+ // Use noteOn() instead of start() if necessary
+ if (!source.start)
+ source.start = source.noteOn;
+
+ // Schedule packet
+ source.buffer = toAudioBuffer(packet);
+ source.start(nextPacketTime);
+
+ // Update timeline by duration of scheduled packet
+ nextPacketTime += packet.length / format.channels / format.rate;
+
+ };
+
+ /** @override */
+ this.sync = function sync() {
+
+ // Calculate elapsed time since last sync
+ var now = context.currentTime;
+
+ // Reschedule future playback time such that playback latency is
+ // bounded within a reasonable latency threshold
+ nextPacketTime = Math.min(nextPacketTime, now + maxLatency);
+
+ };
+
+};
+
+Guacamole.RawAudioPlayer.prototype = new Guacamole.AudioPlayer();
+
+/**
+ * Determines whether the given mimetype is supported by
+ * Guacamole.RawAudioPlayer.
+ *
+ * @param {String} mimetype
+ * The mimetype to check.
+ *
+ * @returns {Boolean}
+ * true if the given mimetype is supported by Guacamole.RawAudioPlayer,
+ * false otherwise.
+ */
+Guacamole.RawAudioPlayer.isSupportedType = function isSupportedType(mimetype) {
+
+ // No supported types if no Web Audio API
+ if (!Guacamole.AudioContextFactory.getAudioContext())
+ return false;
+
+ return Guacamole.RawAudioFormat.parse(mimetype) !== null;
+
+};
+
+/**
+ * Returns a list of all mimetypes supported by Guacamole.RawAudioPlayer. Only
+ * the core mimetypes themselves will be listed. Any mimetype parameters, even
+ * required ones, will not be included in the list. For example, "audio/L8" is
+ * a raw audio mimetype that may be supported, but it is invalid without
+ * additional parameters. Something like "audio/L8;rate=44100" would be valid,
+ * however (see https://tools.ietf.org/html/rfc4856).
+ *
+ * @returns {String[]}
+ * A list of all mimetypes supported by Guacamole.RawAudioPlayer, excluding
+ * any parameters. If the necessary JavaScript APIs for playing raw audio
+ * are absent, this list will be empty.
+ */
+Guacamole.RawAudioPlayer.getSupportedTypes = function getSupportedTypes() {
+
+ // No supported types if no Web Audio API
+ if (!Guacamole.AudioContextFactory.getAudioContext())
+ return [];
+
+ // We support 8-bit and 16-bit raw PCM
+ return [
+ 'audio/L8',
+ 'audio/L16'
+ ];
+
+};
+</code></pre>
+ </article>
+ </section>
+
+
+
+
+</div>
+
+<nav>
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li>
<li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR
ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li><
li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna
me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog
ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li><
li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole
.SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol
e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT)
+</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/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/AudioRecorder.js.html
----------------------------------------------------------------------
diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/AudioRecorder.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/AudioRecorder.js.html
new file mode 100644
index 0000000..c48bae8
--- /dev/null
+++ b/content/doc/0.9.13-incubating/guacamole-common-js/AudioRecorder.js.html
@@ -0,0 +1,628 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: AudioRecorder.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: AudioRecorder.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 || {};
+
+/**
+ * Abstract audio recorder which streams arbitrary audio data to an underlying
+ * Guacamole.OutputStream. It is up to implementations of this class to provide
+ * some means of handling this Guacamole.OutputStream. Data produced by the
+ * recorder is to be sent along the provided stream immediately.
+ *
+ * @constructor
+ */
+Guacamole.AudioRecorder = function AudioRecorder() {
+
+ /**
+ * Callback which is invoked when the audio recording process has stopped
+ * and the underlying Guacamole stream has been closed normally. Audio will
+ * only resume recording if a new Guacamole.AudioRecorder is started. This
+ * Guacamole.AudioRecorder instance MAY NOT be reused.
+ *
+ * @event
+ */
+ this.onclose = null;
+
+ /**
+ * Callback which is invoked when the audio recording process cannot
+ * continue due to an error, if it has started at all. The underlying
+ * Guacamole stream is automatically closed. Future attempts to record
+ * audio should not be made, and this Guacamole.AudioRecorder instance
+ * MAY NOT be reused.
+ *
+ * @event
+ */
+ this.onerror = null;
+
+};
+
+/**
+ * Determines whether the given mimetype is supported by any built-in
+ * implementation of Guacamole.AudioRecorder, and thus will be properly handled
+ * by Guacamole.AudioRecorder.getInstance().
+ *
+ * @param {String} mimetype
+ * The mimetype to check.
+ *
+ * @returns {Boolean}
+ * true if the given mimetype is supported by any built-in
+ * Guacamole.AudioRecorder, false otherwise.
+ */
+Guacamole.AudioRecorder.isSupportedType = function isSupportedType(mimetype) {
+
+ return Guacamole.RawAudioRecorder.isSupportedType(mimetype);
+
+};
+
+/**
+ * Returns a list of all mimetypes supported by any built-in
+ * Guacamole.AudioRecorder, in rough order of priority. Beware that only the
+ * core mimetypes themselves will be listed. Any mimetype parameters, even
+ * required ones, will not be included in the list. For example, "audio/L8" is
+ * a supported raw audio mimetype that is supported, but it is invalid without
+ * additional parameters. Something like "audio/L8;rate=44100" would be valid,
+ * however (see https://tools.ietf.org/html/rfc4856).
+ *
+ * @returns {String[]}
+ * A list of all mimetypes supported by any built-in
+ * Guacamole.AudioRecorder, excluding any parameters.
+ */
+Guacamole.AudioRecorder.getSupportedTypes = function getSupportedTypes() {
+
+ return Guacamole.RawAudioRecorder.getSupportedTypes();
+
+};
+
+/**
+ * Returns an instance of Guacamole.AudioRecorder providing support for the
+ * given audio format. If support for the given audio format is not available,
+ * null is returned.
+ *
+ * @param {Guacamole.OutputStream} stream
+ * The Guacamole.OutputStream to send audio data through.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data to be sent along the provided stream.
+ *
+ * @return {Guacamole.AudioRecorder}
+ * A Guacamole.AudioRecorder instance supporting the given mimetype and
+ * writing to the given stream, or null if support for the given mimetype
+ * is absent.
+ */
+Guacamole.AudioRecorder.getInstance = function getInstance(stream, mimetype) {
+
+ // Use raw audio recorder if possible
+ if (Guacamole.RawAudioRecorder.isSupportedType(mimetype))
+ return new Guacamole.RawAudioRecorder(stream, mimetype);
+
+ // No support for given mimetype
+ return null;
+
+};
+
+/**
+ * Implementation of Guacamole.AudioRecorder providing support for raw PCM
+ * format audio. This recorder relies only on the Web Audio API and does not
+ * require any browser-level support for its audio formats.
+ *
+ * @constructor
+ * @augments Guacamole.AudioRecorder
+ * @param {Guacamole.OutputStream} stream
+ * The Guacamole.OutputStream to write audio data to.
+ *
+ * @param {String} mimetype
+ * The mimetype of the audio data to send along the provided stream, which
+ * must be a "audio/L8" or "audio/L16" mimetype with necessary parameters,
+ * such as: "audio/L16;rate=44100,channels=2".
+ */
+Guacamole.RawAudioRecorder = function RawAudioRecorder(stream, mimetype) {
+
+ /**
+ * Reference to this RawAudioRecorder.
+ *
+ * @private
+ * @type {Guacamole.RawAudioRecorder}
+ */
+ var recorder = this;
+
+ /**
+ * The size of audio buffer to request from the Web Audio API when
+ * recording or processing audio, in sample-frames. This must be a power of
+ * two between 256 and 16384 inclusive, as required by
+ * AudioContext.createScriptProcessor().
+ *
+ * @private
+ * @constant
+ * @type {Number}
+ */
+ var BUFFER_SIZE = 2048;
+
+ /**
+ * The window size to use when applying Lanczos interpolation, commonly
+ * denoted by the variable "a".
+ * See: https://en.wikipedia.org/wiki/Lanczos_resampling
+ *
+ * @private
+ * @contant
+ * @type Number
+ */
+ var LANCZOS_WINDOW_SIZE = 3;
+
+ /**
+ * The format of audio this recorder will encode.
+ *
+ * @private
+ * @type {Guacamole.RawAudioFormat}
+ */
+ var format = Guacamole.RawAudioFormat.parse(mimetype);
+
+ /**
+ * An instance of a Web Audio API AudioContext object, or null if the
+ * Web Audio API is not supported.
+ *
+ * @private
+ * @type {AudioContext}
+ */
+ var context = Guacamole.AudioContextFactory.getAudioContext();
+
+ /**
+ * A function which directly invokes the browser's implementation of
+ * navigator.getUserMedia() with all provided parameters.
+ *
+ * @type Function
+ */
+ var getUserMedia = (navigator.getUserMedia
+ || navigator.webkitGetUserMedia
+ || navigator.mozGetUserMedia
+ || navigator.msGetUserMedia).bind(navigator);
+
+ /**
+ * Guacamole.ArrayBufferWriter wrapped around the audio output stream
+ * provided when this Guacamole.RawAudioRecorder was created.
+ *
+ * @private
+ * @type {Guacamole.ArrayBufferWriter}
+ */
+ var writer = new Guacamole.ArrayBufferWriter(stream);
+
+ /**
+ * The type of typed array that will be used to represent each audio packet
+ * internally. This will be either Int8Array or Int16Array, depending on
+ * whether the raw audio format is 8-bit or 16-bit.
+ *
+ * @private
+ * @constructor
+ */
+ var SampleArray = (format.bytesPerSample === 1) ? window.Int8Array : window.Int16Array;
+
+ /**
+ * The maximum absolute value of any sample within a raw audio packet sent
+ * by this audio recorder. This depends only on the size of each sample,
+ * and will be 128 for 8-bit audio and 32768 for 16-bit audio.
+ *
+ * @private
+ * @type {Number}
+ */
+ var maxSampleValue = (format.bytesPerSample === 1) ? 128 : 32768;
+
+ /**
+ * The total number of audio samples read from the local audio input device
+ * over the life of this audio recorder.
+ *
+ * @private
+ * @type {Number}
+ */
+ var readSamples = 0;
+
+ /**
+ * The total number of audio samples written to the underlying Guacamole
+ * connection over the life of this audio recorder.
+ *
+ * @private
+ * @type {Number}
+ */
+ var writtenSamples = 0;
+
+ /**
+ * The audio stream provided by the browser, if allowed. If no stream has
+ * yet been received, this will be null.
+ *
+ * @type MediaStream
+ */
+ var mediaStream = null;
+
+ /**
+ * The source node providing access to the local audio input device.
+ *
+ * @private
+ * @type {MediaStreamAudioSourceNode}
+ */
+ var source = null;
+
+ /**
+ * The script processing node which receives audio input from the media
+ * stream source node as individual audio buffers.
+ *
+ * @private
+ * @type {ScriptProcessorNode}
+ */
+ var processor = null;
+
+ /**
+ * The normalized sinc function. The normalized sinc function is defined as
+ * 1 for x=0 and sin(PI * x) / (PI * x) for all other values of x.
+ *
+ * See: https://en.wikipedia.org/wiki/Sinc_function
+ *
+ * @private
+ * @param {Number} x
+ * The point at which the normalized sinc function should be computed.
+ *
+ * @returns {Number}
+ * The value of the normalized sinc function at x.
+ */
+ var sinc = function sinc(x) {
+
+ // The value of sinc(0) is defined as 1
+ if (x === 0)
+ return 1;
+
+ // Otherwise, normlized sinc(x) is sin(PI * x) / (PI * x)
+ var piX = Math.PI * x;
+ return Math.sin(piX) / piX;
+
+ };
+
+ /**
+ * Calculates the value of the Lanczos kernal at point x for a given window
+ * size. See: https://en.wikipedia.org/wiki/Lanczos_resampling
+ *
+ * @private
+ * @param {Number} x
+ * The point at which the value of the Lanczos kernel should be
+ * computed.
+ *
+ * @param {Number} a
+ * The window size to use for the Lanczos kernel.
+ *
+ * @returns {Number}
+ * The value of the Lanczos kernel at the given point for the given
+ * window size.
+ */
+ var lanczos = function lanczos(x, a) {
+
+ // Lanczos is sinc(x) * sinc(x / a) for -a < x < a ...
+ if (-a < x && x < a)
+ return sinc(x) * sinc(x / a);
+
+ // ... and 0 otherwise
+ return 0;
+
+ };
+
+ /**
+ * Determines the value of the waveform represented by the audio data at
+ * the given location. If the value cannot be determined exactly as it does
+ * not correspond to an exact sample within the audio data, the value will
+ * be derived through interpolating nearby samples.
+ *
+ * @private
+ * @param {Float32Array} audioData
+ * An array of audio data, as returned by AudioBuffer.getChannelData().
+ *
+ * @param {Number} t
+ * The relative location within the waveform from which the value
+ * should be retrieved, represented as a floating point number between
+ * 0 and 1 inclusive, where 0 represents the earliest point in time and
+ * 1 represents the latest.
+ *
+ * @returns {Number}
+ * The value of the waveform at the given location.
+ */
+ var interpolateSample = function getValueAt(audioData, t) {
+
+ // Convert [0, 1] range to [0, audioData.length - 1]
+ var index = (audioData.length - 1) * t;
+
+ // Determine the start and end points for the summation used by the
+ // Lanczos interpolation algorithm (see: https://en.wikipedia.org/wiki/Lanczos_resampling)
+ var start = Math.floor(index) - LANCZOS_WINDOW_SIZE + 1;
+ var end = Math.floor(index) + LANCZOS_WINDOW_SIZE;
+
+ // Calculate the value of the Lanczos interpolation function for the
+ // required range
+ var sum = 0;
+ for (var i = start; i <= end; i++) {
+ sum += (audioData[i] || 0) * lanczos(index - i, LANCZOS_WINDOW_SIZE);
+ }
+
+ return sum;
+
+ };
+
+ /**
+ * Converts the given AudioBuffer into an audio packet, ready for streaming
+ * along the underlying output stream. Unlike the raw audio packets used by
+ * this audio recorder, AudioBuffers require floating point samples and are
+ * split into isolated planes of channel-specific data.
+ *
+ * @private
+ * @param {AudioBuffer} audioBuffer
+ * The Web Audio API AudioBuffer that should be converted to a raw
+ * audio packet.
+ *
+ * @returns {SampleArray}
+ * A new raw audio packet containing the audio data from the provided
+ * AudioBuffer.
+ */
+ var toSampleArray = function toSampleArray(audioBuffer) {
+
+ // Track overall amount of data read
+ var inSamples = audioBuffer.length;
+ readSamples += inSamples;
+
+ // Calculate the total number of samples that should be written as of
+ // the audio data just received and adjust the size of the output
+ // packet accordingly
+ var expectedWrittenSamples = Math.round(readSamples * format.rate / audioBuffer.sampleRate);
+ var outSamples = expectedWrittenSamples - writtenSamples;
+
+ // Update number of samples written
+ writtenSamples += outSamples;
+
+ // Get array for raw PCM storage
+ var data = new SampleArray(outSamples * format.channels);
+
+ // Convert each channel
+ for (var channel = 0; channel < format.channels; channel++) {
+
+ var audioData = audioBuffer.getChannelData(channel);
+
+ // Fill array with data from audio buffer channel
+ var offset = channel;
+ for (var i = 0; i < outSamples; i++) {
+ data[offset] = interpolateSample(audioData, i / (outSamples - 1)) * maxSampleValue;
+ offset += format.channels;
+ }
+
+ }
+
+ return data;
+
+ };
+
+ /**
+ * Requests access to the user's microphone and begins capturing audio. All
+ * received audio data is resampled as necessary and forwarded to the
+ * Guacamole stream underlying this Guacamole.RawAudioRecorder. This
+ * function must be invoked ONLY ONCE per instance of
+ * Guacamole.RawAudioRecorder.
+ *
+ * @private
+ */
+ var beginAudioCapture = function beginAudioCapture() {
+
+ // Attempt to retrieve an audio input stream from the browser
+ getUserMedia({ 'audio' : true }, function streamReceived(stream) {
+
+ // Create processing node which receives appropriately-sized audio buffers
+ processor = context.createScriptProcessor(BUFFER_SIZE, format.channels, format.channels);
+ processor.connect(context.destination);
+
+ // Send blobs when audio buffers are received
+ processor.onaudioprocess = function processAudio(e) {
+ writer.sendData(toSampleArray(e.inputBuffer).buffer);
+ };
+
+ // Connect processing node to user's audio input source
+ source = context.createMediaStreamSource(stream);
+ source.connect(processor);
+
+ // Save stream for later cleanup
+ mediaStream = stream;
+
+ }, function streamDenied() {
+
+ // Simply end stream if audio access is not allowed
+ writer.sendEnd();
+
+ // Notify of closure
+ if (recorder.onerror)
+ recorder.onerror();
+
+ });
+
+ };
+
+ /**
+ * Stops capturing audio, if the capture has started, freeing all associated
+ * resources. If the capture has not started, this function simply ends the
+ * underlying Guacamole stream.
+ *
+ * @private
+ */
+ var stopAudioCapture = function stopAudioCapture() {
+
+ // Disconnect media source node from script processor
+ if (source)
+ source.disconnect();
+
+ // Disconnect associated script processor node
+ if (processor)
+ processor.disconnect();
+
+ // Stop capture
+ if (mediaStream) {
+ var tracks = mediaStream.getTracks();
+ for (var i = 0; i < tracks.length; i++)
+ tracks[i].stop();
+ }
+
+ // Remove references to now-unneeded components
+ processor = null;
+ source = null;
+ mediaStream = null;
+
+ // End stream
+ writer.sendEnd();
+
+ };
+
+ // Once audio stream is successfully open, request and begin reading audio
+ writer.onack = function audioStreamAcknowledged(status) {
+
+ // Begin capture if successful response and not yet started
+ if (status.code === Guacamole.Status.Code.SUCCESS && !mediaStream)
+ beginAudioCapture();
+
+ // Otherwise stop capture and cease handling any further acks
+ else {
+
+ // Stop capturing audio
+ stopAudioCapture();
+ writer.onack = null;
+
+ // Notify if stream has closed normally
+ if (status.code === Guacamole.Status.Code.RESOURCE_CLOSED) {
+ if (recorder.onclose)
+ recorder.onclose();
+ }
+
+ // Otherwise notify of closure due to error
+ else {
+ if (recorder.onerror)
+ recorder.onerror();
+ }
+
+ }
+
+ };
+
+};
+
+Guacamole.RawAudioRecorder.prototype = new Guacamole.AudioRecorder();
+
+/**
+ * Determines whether the given mimetype is supported by
+ * Guacamole.RawAudioRecorder.
+ *
+ * @param {String} mimetype
+ * The mimetype to check.
+ *
+ * @returns {Boolean}
+ * true if the given mimetype is supported by Guacamole.RawAudioRecorder,
+ * false otherwise.
+ */
+Guacamole.RawAudioRecorder.isSupportedType = function isSupportedType(mimetype) {
+
+ // No supported types if no Web Audio API
+ if (!Guacamole.AudioContextFactory.getAudioContext())
+ return false;
+
+ return Guacamole.RawAudioFormat.parse(mimetype) !== null;
+
+};
+
+/**
+ * Returns a list of all mimetypes supported by Guacamole.RawAudioRecorder. Only
+ * the core mimetypes themselves will be listed. Any mimetype parameters, even
+ * required ones, will not be included in the list. For example, "audio/L8" is
+ * a raw audio mimetype that may be supported, but it is invalid without
+ * additional parameters. Something like "audio/L8;rate=44100" would be valid,
+ * however (see https://tools.ietf.org/html/rfc4856).
+ *
+ * @returns {String[]}
+ * A list of all mimetypes supported by Guacamole.RawAudioRecorder,
+ * excluding any parameters. If the necessary JavaScript APIs for recording
+ * raw audio are absent, this list will be empty.
+ */
+Guacamole.RawAudioRecorder.getSupportedTypes = function getSupportedTypes() {
+
+ // No supported types if no Web Audio API
+ if (!Guacamole.AudioContextFactory.getAudioContext())
+ return [];
+
+ // We support 8-bit and 16-bit raw PCM
+ return [
+ 'audio/L8',
+ 'audio/L16'
+ ];
+
+};
+</code></pre>
+ </article>
+ </section>
+
+
+
+
+</div>
+
+<nav>
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li>
<li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR
ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li><
li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna
me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog
ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li><
li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole
.SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol
e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT)
+</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/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/BlobReader.js.html
----------------------------------------------------------------------
diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/BlobReader.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/BlobReader.js.html
new file mode 100644
index 0000000..387b68b
--- /dev/null
+++ b/content/doc/0.9.13-incubating/guacamole-common-js/BlobReader.js.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>JSDoc: Source: BlobReader.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: BlobReader.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, assembling all
+ * received blobs into a single blob by appending them to each other in order.
+ * 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.
+ * @param {String} mimetype The mimetype of the blob being built.
+ */
+Guacamole.BlobReader = function(stream, mimetype) {
+
+ /**
+ * Reference to this Guacamole.InputStream.
+ * @private
+ */
+ var guac_reader = this;
+
+ /**
+ * The length of this Guacamole.InputStream in bytes.
+ * @private
+ */
+ var length = 0;
+
+ // Get blob builder
+ var blob_builder;
+ if (window.BlobBuilder) blob_builder = new BlobBuilder();
+ else if (window.WebKitBlobBuilder) blob_builder = new WebKitBlobBuilder();
+ else if (window.MozBlobBuilder) blob_builder = new MozBlobBuilder();
+ else
+ blob_builder = new (function() {
+
+ var blobs = [];
+
+ /** @ignore */
+ this.append = function(data) {
+ blobs.push(new Blob([data], {"type": mimetype}));
+ };
+
+ /** @ignore */
+ this.getBlob = function() {
+ return new Blob(blobs, {"type": mimetype});
+ };
+
+ })();
+
+ // Append received blobs
+ stream.onblob = function(data) {
+
+ // Convert to ArrayBuffer
+ var binary = window.atob(data);
+ var arrayBuffer = new ArrayBuffer(binary.length);
+ var bufferView = new Uint8Array(arrayBuffer);
+
+ for (var i=0; i<binary.length; i++)
+ bufferView[i] = binary.charCodeAt(i);
+
+ blob_builder.append(arrayBuffer);
+ length += arrayBuffer.byteLength;
+
+ // Call handler, if present
+ if (guac_reader.onprogress)
+ guac_reader.onprogress(arrayBuffer.byteLength);
+
+ // Send success response
+ stream.sendAck("OK", 0x0000);
+
+ };
+
+ // Simply call onend when end received
+ stream.onend = function() {
+ if (guac_reader.onend)
+ guac_reader.onend();
+ };
+
+ /**
+ * Returns the current length of this Guacamole.InputStream, in bytes.
+ * @return {Number} The current length of this Guacamole.InputStream.
+ */
+ this.getLength = function() {
+ return length;
+ };
+
+ /**
+ * Returns the contents of this Guacamole.BlobReader as a Blob.
+ * @return {Blob} The contents of this Guacamole.BlobReader.
+ */
+ this.getBlob = function() {
+ return blob_builder.getBlob();
+ };
+
+ /**
+ * Fired once for every blob of data received.
+ *
+ * @event
+ * @param {Number} length The number of bytes received.
+ */
+ this.onprogress = 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.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li>
<li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR
ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li><
li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna
me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog
ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li><
li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole
.SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol
e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT)
+</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>