1 /*
  2  * Copyright (C) 2013 Glyptodon LLC
  3  *
  4  * Permission is hereby granted, free of charge, to any person obtaining a copy
  5  * of this software and associated documentation files (the "Software"), to deal
  6  * in the Software without restriction, including without limitation the rights
  7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8  * copies of the Software, and to permit persons to whom the Software is
  9  * furnished to do so, subject to the following conditions:
 10  *
 11  * The above copyright notice and this permission notice shall be included in
 12  * all copies or substantial portions of the Software.
 13  *
 14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 20  * THE SOFTWARE.
 21  */
 22 
 23 var Guacamole = Guacamole || {};
 24 
 25 /**
 26  * An object used by the Guacamole client to house arbitrarily-many named
 27  * input and output streams.
 28  * 
 29  * @constructor
 30  * @param {Guacamole.Client} client
 31  *     The client owning this object.
 32  *
 33  * @param {Number} index
 34  *     The index of this object.
 35  */
 36 Guacamole.Object = function guacamoleObject(client, index) {
 37 
 38     /**
 39      * Reference to this Guacamole.Object.
 40      *
 41      * @private
 42      * @type Guacamole.Object
 43      */
 44     var guacObject = this;
 45 
 46     /**
 47      * The callbacks associated with all pending input stream requests, if the
 48      * default onbody handling is in use.
 49      *
 50      * @private
 51      * @type Object.<String, Function[]>
 52      *     Map of stream name to corresponding queue of callbacks. The queue of
 53      *     callbacks is guaranteed to be in order of request.
 54      */
 55     var bodyCallbacks = {};
 56 
 57     /**
 58      * Removes and returns the callback at the head of the callback queue for
 59      * the stream having the given name. If no such callbacks exist, null is
 60      * returned.
 61      *
 62      * @private
 63      * @param {String} name
 64      *     The name of the stream to retrieve a callback for.
 65      *
 66      * @returns {Function}
 67      *     The next callback associated with the stream having the given name,
 68      *     or null if no such callback exists.
 69      */
 70     var dequeueBodyCallback = function dequeueBodyCallback(name) {
 71 
 72         // If no callbacks defined, simply return null
 73         var callbacks = bodyCallbacks[name];
 74         if (!callbacks)
 75             return null;
 76 
 77         // Otherwise, pull off first callback, deleting the queue if empty
 78         var callback = callbacks.shift();
 79         if (callbacks.length === 0)
 80             delete bodyCallbacks[name];
 81 
 82         // Return found callback
 83         return callback;
 84 
 85     };
 86 
 87     /**
 88      * Adds the given callback to the tail of the callback queue for the stream
 89      * having the given name.
 90      *
 91      * @private
 92      * @param {String} name
 93      *     The name of the stream to associate with the given callback.
 94      *
 95      * @param {Function} callback
 96      *     The callback to add to the queue of the stream with the given name.
 97      */
 98     var enqueueBodyCallback = function enqueueBodyCallback(name, callback) {
 99 
100         // Get callback queue by name, creating first if necessary
101         var callbacks = bodyCallbacks[name];
102         if (!callbacks) {
103             callbacks = [];
104             bodyCallbacks[name] = callbacks;
105         }
106 
107         // Add callback to end of queue
108         callbacks.push(callback);
109 
110     };
111 
112     /**
113      * The index of this object.
114      *
115      * @type Number
116      */
117     this.index = index;
118 
119     /**
120      * Called when this object receives the body of a requested input stream.
121      * By default, all objects will invoke the callbacks provided to their
122      * requestInputStream() functions based on the name of the stream
123      * requested. This behavior can be overridden by specifying a different
124      * handler here.
125      *
126      * @event
127      * @param {Guacamole.InputStream} inputStream
128      *     The input stream of the received body.
129      *
130      * @param {String} mimetype
131      *     The mimetype of the data being received.
132      *
133      * @param {String} name
134      *     The name of the stream whose body has been received.
135      */
136     this.onbody = function defaultBodyHandler(inputStream, mimetype, name) {
137 
138         // Call queued callback for the received body, if any
139         var callback = dequeueBodyCallback(name);
140         if (callback)
141             callback(inputStream, mimetype);
142 
143     };
144 
145     /**
146      * Called when this object is being undefined. Once undefined, no further
147      * communication involving this object may occur.
148      * 
149      * @event
150      */
151     this.onundefine = null;
152 
153     /**
154      * Requests read access to the input stream having the given name. If
155      * successful, a new input stream will be created.
156      *
157      * @param {String} name
158      *     The name of the input stream to request.
159      *
160      * @param {Function} [bodyCallback]
161      *     The callback to invoke when the body of the requested input stream
162      *     is received. This callback will be provided a Guacamole.InputStream
163      *     and its mimetype as its two only arguments. If the onbody handler of
164      *     this object is overridden, this callback will not be invoked.
165      */
166     this.requestInputStream = function requestInputStream(name, bodyCallback) {
167 
168         // Queue body callback if provided
169         if (bodyCallback)
170             enqueueBodyCallback(name, bodyCallback);
171 
172         // Send request for input stream
173         client.requestObjectInputStream(guacObject.index, name);
174 
175     };
176 
177     /**
178      * Creates a new output stream associated with this object and having the
179      * given mimetype and name. The legality of a mimetype and name is dictated
180      * by the object itself.
181      *
182      * @param {String} mimetype
183      *     The mimetype of the data which will be sent to the output stream.
184      *
185      * @param {String} name
186      *     The defined name of an output stream within this object.
187      *
188      * @returns {Guacamole.OutputStream}
189      *     An output stream which will write blobs to the named output stream
190      *     of this object.
191      */
192     this.createOutputStream = function createOutputStream(mimetype, name) {
193         return client.createObjectOutputStream(guacObject.index, mimetype, name);
194     };
195 
196 };
197 
198 /**
199  * The reserved name denoting the root stream of any object. The contents of
200  * the root stream MUST be a JSON map of stream name to mimetype.
201  *
202  * @constant
203  * @type String
204  */
205 Guacamole.Object.ROOT_STREAM = '/';
206 
207 /**
208  * The mimetype of a stream containing JSON which maps available stream names
209  * to their corresponding mimetype. The root stream of a Guacamole.Object MUST
210  * have this mimetype.
211  *
212  * @constant
213  * @type String
214  */
215 Guacamole.Object.STREAM_INDEX_MIMETYPE = 'application/vnd.glyptodon.guacamole.stream-index+json';
216