| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| var Guacamole = Guacamole || {}; |
| |
| /** |
| * A writer which automatically writes to the given output stream with the |
| * contents of provided Blob objects. |
| * |
| * @constructor |
| * @param {Guacamole.OutputStream} stream |
| * The stream that data will be written to. |
| */ |
| Guacamole.BlobWriter = function BlobWriter(stream) { |
| |
| /** |
| * Reference to this Guacamole.BlobWriter. |
| * |
| * @private |
| * @type {Guacamole.BlobWriter} |
| */ |
| var guacWriter = this; |
| |
| /** |
| * Wrapped Guacamole.ArrayBufferWriter which will be used to send any |
| * provided file data. |
| * |
| * @private |
| * @type {Guacamole.ArrayBufferWriter} |
| */ |
| var arrayBufferWriter = new Guacamole.ArrayBufferWriter(stream); |
| |
| // Initially, simply call onack for acknowledgements |
| arrayBufferWriter.onack = function(status) { |
| if (guacWriter.onack) |
| guacWriter.onack(status); |
| }; |
| |
| /** |
| * Browser-independent implementation of Blob.slice() which uses an end |
| * offset to determine the span of the resulting slice, rather than a |
| * length. |
| * |
| * @private |
| * @param {Blob} blob |
| * The Blob to slice. |
| * |
| * @param {Number} start |
| * The starting offset of the slice, in bytes, inclusive. |
| * |
| * @param {Number} end |
| * The ending offset of the slice, in bytes, exclusive. |
| * |
| * @returns {Blob} |
| * A Blob containing the data within the given Blob starting at |
| * <code>start</code> and ending at <code>end - 1</code>. |
| */ |
| var slice = function slice(blob, start, end) { |
| |
| // Use prefixed implementations if necessary |
| var sliceImplementation = ( |
| blob.slice |
| || blob.webkitSlice |
| || blob.mozSlice |
| ).bind(blob); |
| |
| var length = end - start; |
| |
| // The old Blob.slice() was length-based (not end-based). Try the |
| // length version first, if the two calls are not equivalent. |
| if (length !== end) { |
| |
| // If the result of the slice() call matches the expected length, |
| // trust that result. It must be correct. |
| var sliceResult = sliceImplementation(start, length); |
| if (sliceResult.size === length) |
| return sliceResult; |
| |
| } |
| |
| // Otherwise, use the most-recent standard: end-based slice() |
| return sliceImplementation(start, end); |
| |
| }; |
| |
| /** |
| * Sends the contents of the given blob over the underlying stream. |
| * |
| * @param {Blob} blob |
| * The blob to send. |
| */ |
| this.sendBlob = function sendBlob(blob) { |
| |
| var offset = 0; |
| var reader = new FileReader(); |
| |
| /** |
| * Reads the next chunk of the blob provided to |
| * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The chunk itself |
| * is read asynchronously, and will not be available until |
| * reader.onload fires. |
| * |
| * @private |
| */ |
| var readNextChunk = function readNextChunk() { |
| |
| // If no further chunks remain, inform of completion and stop |
| if (offset >= blob.size) { |
| |
| // Fire completion event for completed blob |
| if (guacWriter.oncomplete) |
| guacWriter.oncomplete(blob); |
| |
| // No further chunks to read |
| return; |
| |
| } |
| |
| // Obtain reference to next chunk as a new blob |
| var chunk = slice(blob, offset, offset + arrayBufferWriter.blobLength); |
| offset += arrayBufferWriter.blobLength; |
| |
| // Attempt to read the blob contents represented by the blob into |
| // a new array buffer |
| reader.readAsArrayBuffer(chunk); |
| |
| }; |
| |
| // Send each chunk over the stream, continue reading the next chunk |
| reader.onload = function chunkLoadComplete() { |
| |
| // Send the successfully-read chunk |
| arrayBufferWriter.sendData(reader.result); |
| |
| // Continue sending more chunks after the latest chunk is |
| // acknowledged |
| arrayBufferWriter.onack = function sendMoreChunks(status) { |
| |
| if (guacWriter.onack) |
| guacWriter.onack(status); |
| |
| // Abort transfer if an error occurs |
| if (status.isError()) |
| return; |
| |
| // Inform of blob upload progress via progress events |
| if (guacWriter.onprogress) |
| guacWriter.onprogress(blob, offset - arrayBufferWriter.blobLength); |
| |
| // Queue the next chunk for reading |
| readNextChunk(); |
| |
| }; |
| |
| }; |
| |
| // If an error prevents further reading, inform of error and stop |
| reader.onerror = function chunkLoadFailed() { |
| |
| // Fire error event, including the context of the error |
| if (guacWriter.onerror) |
| guacWriter.onerror(blob, offset, reader.error); |
| |
| }; |
| |
| // Begin reading the first chunk |
| readNextChunk(); |
| |
| }; |
| |
| /** |
| * Signals that no further text will be sent, effectively closing the |
| * stream. |
| */ |
| this.sendEnd = function sendEnd() { |
| arrayBufferWriter.sendEnd(); |
| }; |
| |
| /** |
| * Fired for received data, if acknowledged by the server. |
| * |
| * @event |
| * @param {Guacamole.Status} status |
| * The status of the operation. |
| */ |
| this.onack = null; |
| |
| /** |
| * Fired when an error occurs reading a blob passed to |
| * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. The transfer for the |
| * the given blob will cease, but the stream will remain open. |
| * |
| * @event |
| * @param {Blob} blob |
| * The blob that was being read when the error occurred. |
| * |
| * @param {Number} offset |
| * The offset of the failed read attempt within the blob, in bytes. |
| * |
| * @param {DOMError} error |
| * The error that occurred. |
| */ |
| this.onerror = null; |
| |
| /** |
| * Fired for each successfully-read chunk of data as a blob is being sent |
| * via [sendBlob()]{@link Guacamole.BlobWriter#sendBlob}. |
| * |
| * @event |
| * @param {Blob} blob |
| * The blob that is being read. |
| * |
| * @param {Number} offset |
| * The offset of the read that just succeeded. |
| */ |
| this.onprogress = null; |
| |
| /** |
| * Fired when a blob passed to |
| * [sendBlob()]{@link Guacamole.BlobWriter#sendBlob} has finished being |
| * sent. |
| * |
| * @event |
| * @param {Blob} blob |
| * The blob that was sent. |
| */ |
| this.oncomplete = null; |
| |
| }; |