mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
1560 lines
52 KiB
JavaScript
1560 lines
52 KiB
JavaScript
/*
|
|
* 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 || {};
|
|
|
|
/**
|
|
* The Guacamole display. The display does not deal with the Guacamole
|
|
* protocol, and instead implements a set of graphical operations which
|
|
* embody the set of operations present in the protocol. The order operations
|
|
* are executed is guaranteed to be in the same order as their corresponding
|
|
* functions are called.
|
|
*
|
|
* @constructor
|
|
*/
|
|
Guacamole.Display = function() {
|
|
|
|
/**
|
|
* Reference to this Guacamole.Display.
|
|
* @private
|
|
*/
|
|
var guac_display = this;
|
|
|
|
var displayWidth = 0;
|
|
var displayHeight = 0;
|
|
var displayScale = 1;
|
|
|
|
// Create display
|
|
var display = document.createElement("div");
|
|
display.style.position = "relative";
|
|
display.style.width = displayWidth + "px";
|
|
display.style.height = displayHeight + "px";
|
|
|
|
// Ensure transformations on display originate at 0,0
|
|
display.style.transformOrigin =
|
|
display.style.webkitTransformOrigin =
|
|
display.style.MozTransformOrigin =
|
|
display.style.OTransformOrigin =
|
|
display.style.msTransformOrigin =
|
|
"0 0";
|
|
|
|
// Create default layer
|
|
var default_layer = new Guacamole.Display.VisibleLayer(displayWidth, displayHeight);
|
|
|
|
// Create cursor layer
|
|
var cursor = new Guacamole.Display.VisibleLayer(0, 0);
|
|
cursor.setChannelMask(Guacamole.Layer.SRC);
|
|
|
|
// Add default layer and cursor to display
|
|
display.appendChild(default_layer.getElement());
|
|
display.appendChild(cursor.getElement());
|
|
|
|
// Create bounding div
|
|
var bounds = document.createElement("div");
|
|
bounds.style.position = "relative";
|
|
bounds.style.width = (displayWidth*displayScale) + "px";
|
|
bounds.style.height = (displayHeight*displayScale) + "px";
|
|
|
|
// Add display to bounds
|
|
bounds.appendChild(display);
|
|
|
|
/**
|
|
* The X coordinate of the hotspot of the mouse cursor. The hotspot is
|
|
* the relative location within the image of the mouse cursor at which
|
|
* each click occurs.
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
this.cursorHotspotX = 0;
|
|
|
|
/**
|
|
* The Y coordinate of the hotspot of the mouse cursor. The hotspot is
|
|
* the relative location within the image of the mouse cursor at which
|
|
* each click occurs.
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
this.cursorHotspotY = 0;
|
|
|
|
/**
|
|
* The current X coordinate of the local mouse cursor. This is not
|
|
* necessarily the location of the actual mouse - it refers only to
|
|
* the location of the cursor image within the Guacamole display, as
|
|
* last set by moveCursor().
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
this.cursorX = 0;
|
|
|
|
/**
|
|
* The current X coordinate of the local mouse cursor. This is not
|
|
* necessarily the location of the actual mouse - it refers only to
|
|
* the location of the cursor image within the Guacamole display, as
|
|
* last set by moveCursor().
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
this.cursorY = 0;
|
|
|
|
/**
|
|
* Fired when the default layer (and thus the entire Guacamole display)
|
|
* is resized.
|
|
*
|
|
* @event
|
|
* @param {Number} width The new width of the Guacamole display.
|
|
* @param {Number} height The new height of the Guacamole display.
|
|
*/
|
|
this.onresize = null;
|
|
|
|
/**
|
|
* Fired whenever the local cursor image is changed. This can be used to
|
|
* implement special handling of the client-side cursor, or to override
|
|
* the default use of a software cursor layer.
|
|
*
|
|
* @event
|
|
* @param {HTMLCanvasElement} canvas The cursor image.
|
|
* @param {Number} x The X-coordinate of the cursor hotspot.
|
|
* @param {Number} y The Y-coordinate of the cursor hotspot.
|
|
*/
|
|
this.oncursor = null;
|
|
|
|
/**
|
|
* The queue of all pending Tasks. Tasks will be run in order, with new
|
|
* tasks added at the end of the queue and old tasks removed from the
|
|
* front of the queue (FIFO). These tasks will eventually be grouped
|
|
* into a Frame.
|
|
* @private
|
|
* @type {Task[]}
|
|
*/
|
|
var tasks = [];
|
|
|
|
/**
|
|
* The queue of all frames. Each frame is a pairing of an array of tasks
|
|
* and a callback which must be called when the frame is rendered.
|
|
* @private
|
|
* @type {Frame[]}
|
|
*/
|
|
var frames = [];
|
|
|
|
/**
|
|
* Flushes all pending frames.
|
|
* @private
|
|
*/
|
|
function __flush_frames() {
|
|
|
|
var rendered_frames = 0;
|
|
|
|
// Draw all pending frames, if ready
|
|
while (rendered_frames < frames.length) {
|
|
|
|
var frame = frames[rendered_frames];
|
|
if (!frame.isReady())
|
|
break;
|
|
|
|
frame.flush();
|
|
rendered_frames++;
|
|
|
|
}
|
|
|
|
// Remove rendered frames from array
|
|
frames.splice(0, rendered_frames);
|
|
|
|
}
|
|
|
|
/**
|
|
* An ordered list of tasks which must be executed atomically. Once
|
|
* executed, an associated (and optional) callback will be called.
|
|
*
|
|
* @private
|
|
* @constructor
|
|
* @param {function} callback The function to call when this frame is
|
|
* rendered.
|
|
* @param {Task[]} tasks The set of tasks which must be executed to render
|
|
* this frame.
|
|
*/
|
|
function Frame(callback, tasks) {
|
|
|
|
/**
|
|
* Returns whether this frame is ready to be rendered. This function
|
|
* returns true if and only if ALL underlying tasks are unblocked.
|
|
*
|
|
* @returns {boolean} true if all underlying tasks are unblocked,
|
|
* false otherwise.
|
|
*/
|
|
this.isReady = function() {
|
|
|
|
// Search for blocked tasks
|
|
for (var i=0; i < tasks.length; i++) {
|
|
if (tasks[i].blocked)
|
|
return false;
|
|
}
|
|
|
|
// If no blocked tasks, the frame is ready
|
|
return true;
|
|
|
|
};
|
|
|
|
/**
|
|
* Renders this frame, calling the associated callback, if any, after
|
|
* the frame is complete. This function MUST only be called when no
|
|
* blocked tasks exist. Calling this function with blocked tasks
|
|
* will result in undefined behavior.
|
|
*/
|
|
this.flush = function() {
|
|
|
|
// Draw all pending tasks.
|
|
for (var i=0; i < tasks.length; i++)
|
|
tasks[i].execute();
|
|
|
|
// Call callback
|
|
if (callback) callback();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* A container for an task handler. Each operation which must be ordered
|
|
* is associated with a Task that goes into a task queue. Tasks in this
|
|
* queue are executed in order once their handlers are set, while Tasks
|
|
* without handlers block themselves and any following Tasks from running.
|
|
*
|
|
* @constructor
|
|
* @private
|
|
* @param {function} taskHandler The function to call when this task
|
|
* runs, if any.
|
|
* @param {boolean} blocked Whether this task should start blocked.
|
|
*/
|
|
function Task(taskHandler, blocked) {
|
|
|
|
var task = this;
|
|
|
|
/**
|
|
* Whether this Task is blocked.
|
|
*
|
|
* @type {boolean}
|
|
*/
|
|
this.blocked = blocked;
|
|
|
|
/**
|
|
* Unblocks this Task, allowing it to run.
|
|
*/
|
|
this.unblock = function() {
|
|
if (task.blocked) {
|
|
task.blocked = false;
|
|
__flush_frames();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Calls the handler associated with this task IMMEDIATELY. This
|
|
* function does not track whether this task is marked as blocked.
|
|
* Enforcing the blocked status of tasks is up to the caller.
|
|
*/
|
|
this.execute = function() {
|
|
if (taskHandler) taskHandler();
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* Schedules a task for future execution. The given handler will execute
|
|
* immediately after all previous tasks upon frame flush, unless this
|
|
* task is blocked. If any tasks is blocked, the entire frame will not
|
|
* render (and no tasks within will execute) until all tasks are unblocked.
|
|
*
|
|
* @private
|
|
* @param {function} handler The function to call when possible, if any.
|
|
* @param {boolean} blocked Whether the task should start blocked.
|
|
* @returns {!Task} The Task created and added to the queue for future
|
|
* running.
|
|
*/
|
|
function scheduleTask(handler, blocked) {
|
|
var task = new Task(handler, blocked);
|
|
tasks.push(task);
|
|
return task;
|
|
}
|
|
|
|
/**
|
|
* Returns the element which contains the Guacamole display.
|
|
*
|
|
* @return {!Element} The element containing the Guacamole display.
|
|
*/
|
|
this.getElement = function() {
|
|
return bounds;
|
|
};
|
|
|
|
/**
|
|
* Returns the width of this display.
|
|
*
|
|
* @return {Number} The width of this display;
|
|
*/
|
|
this.getWidth = function() {
|
|
return displayWidth;
|
|
};
|
|
|
|
/**
|
|
* Returns the height of this display.
|
|
*
|
|
* @return {Number} The height of this display;
|
|
*/
|
|
this.getHeight = function() {
|
|
return displayHeight;
|
|
};
|
|
|
|
/**
|
|
* Returns the default layer of this display. Each Guacamole display always
|
|
* has at least one layer. Other layers can optionally be created within
|
|
* this layer, but the default layer cannot be removed and is the absolute
|
|
* ancestor of all other layers.
|
|
*
|
|
* @return {!Guacamole.Display.VisibleLayer} The default layer.
|
|
*/
|
|
this.getDefaultLayer = function() {
|
|
return default_layer;
|
|
};
|
|
|
|
/**
|
|
* Returns the cursor layer of this display. Each Guacamole display contains
|
|
* a layer for the image of the mouse cursor. This layer is a special case
|
|
* and exists above all other layers, similar to the hardware mouse cursor.
|
|
*
|
|
* @return {!Guacamole.Display.VisibleLayer} The cursor layer.
|
|
*/
|
|
this.getCursorLayer = function() {
|
|
return cursor;
|
|
};
|
|
|
|
/**
|
|
* Creates a new layer. The new layer will be a direct child of the default
|
|
* layer, but can be moved to be a child of any other layer. Layers returned
|
|
* by this function are visible.
|
|
*
|
|
* @return {!Guacamole.Display.VisibleLayer} The newly-created layer.
|
|
*/
|
|
this.createLayer = function() {
|
|
var layer = new Guacamole.Display.VisibleLayer(displayWidth, displayHeight);
|
|
layer.move(default_layer, 0, 0, 0);
|
|
return layer;
|
|
};
|
|
|
|
/**
|
|
* Creates a new buffer. Buffers are invisible, off-screen surfaces. They
|
|
* are implemented in the same manner as layers, but do not provide the
|
|
* same nesting semantics.
|
|
*
|
|
* @return {!Guacamole.Layer} The newly-created buffer.
|
|
*/
|
|
this.createBuffer = function() {
|
|
var buffer = new Guacamole.Layer(0, 0);
|
|
buffer.autosize = 1;
|
|
return buffer;
|
|
};
|
|
|
|
/**
|
|
* Flush all pending draw tasks, if possible, as a new frame. If the entire
|
|
* frame is not ready, the flush will wait until all required tasks are
|
|
* unblocked.
|
|
*
|
|
* @param {function} callback The function to call when this frame is
|
|
* flushed. This may happen immediately, or
|
|
* later when blocked tasks become unblocked.
|
|
*/
|
|
this.flush = function(callback) {
|
|
|
|
// Add frame, reset tasks
|
|
frames.push(new Frame(callback, tasks));
|
|
tasks = [];
|
|
|
|
// Attempt flush
|
|
__flush_frames();
|
|
|
|
};
|
|
|
|
/**
|
|
* Sets the hotspot and image of the mouse cursor displayed within the
|
|
* Guacamole display.
|
|
*
|
|
* @param {Number} hotspotX The X coordinate of the cursor hotspot.
|
|
* @param {Number} hotspotY The Y coordinate of the cursor hotspot.
|
|
* @param {Guacamole.Layer} layer The source layer containing the data which
|
|
* should be used as the mouse cursor image.
|
|
* @param {Number} srcx The X coordinate of the upper-left corner of the
|
|
* rectangle within the source layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcy The Y coordinate of the upper-left corner of the
|
|
* rectangle within the source layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcw The width of the rectangle within the source layer's
|
|
* coordinate space to copy data from.
|
|
* @param {Number} srch The height of the rectangle within the source
|
|
* layer's coordinate space to copy data from.
|
|
|
|
*/
|
|
this.setCursor = function(hotspotX, hotspotY, layer, srcx, srcy, srcw, srch) {
|
|
scheduleTask(function __display_set_cursor() {
|
|
|
|
// Set hotspot
|
|
guac_display.cursorHotspotX = hotspotX;
|
|
guac_display.cursorHotspotY = hotspotY;
|
|
|
|
// Reset cursor size
|
|
cursor.resize(srcw, srch);
|
|
|
|
// Draw cursor to cursor layer
|
|
cursor.copy(layer, srcx, srcy, srcw, srch, 0, 0);
|
|
guac_display.moveCursor(guac_display.cursorX, guac_display.cursorY);
|
|
|
|
// Fire cursor change event
|
|
if (guac_display.oncursor)
|
|
guac_display.oncursor(cursor.toCanvas(), hotspotX, hotspotY);
|
|
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets whether the software-rendered cursor is shown. This cursor differs
|
|
* from the hardware cursor in that it is built into the Guacamole.Display,
|
|
* and relies on its own Guacamole layer to render.
|
|
*
|
|
* @param {Boolean} [shown=true] Whether to show the software cursor.
|
|
*/
|
|
this.showCursor = function(shown) {
|
|
|
|
var element = cursor.getElement();
|
|
var parent = element.parentNode;
|
|
|
|
// Remove from DOM if hidden
|
|
if (shown === false) {
|
|
if (parent)
|
|
parent.removeChild(element);
|
|
}
|
|
|
|
// Otherwise, ensure cursor is child of display
|
|
else if (parent !== display)
|
|
display.appendChild(element);
|
|
|
|
};
|
|
|
|
/**
|
|
* Sets the location of the local cursor to the given coordinates. For the
|
|
* sake of responsiveness, this function performs its action immediately.
|
|
* Cursor motion is not maintained within atomic frames.
|
|
*
|
|
* @param {Number} x The X coordinate to move the cursor to.
|
|
* @param {Number} y The Y coordinate to move the cursor to.
|
|
*/
|
|
this.moveCursor = function(x, y) {
|
|
|
|
// Move cursor layer
|
|
cursor.translate(x - guac_display.cursorHotspotX,
|
|
y - guac_display.cursorHotspotY);
|
|
|
|
// Update stored position
|
|
guac_display.cursorX = x;
|
|
guac_display.cursorY = y;
|
|
|
|
};
|
|
|
|
/**
|
|
* Changes the size of the given Layer to the given width and height.
|
|
* Resizing is only attempted if the new size provided is actually different
|
|
* from the current size.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to resize.
|
|
* @param {Number} width The new width.
|
|
* @param {Number} height The new height.
|
|
*/
|
|
this.resize = function(layer, width, height) {
|
|
scheduleTask(function __display_resize() {
|
|
|
|
layer.resize(width, height);
|
|
|
|
// Resize display if default layer is resized
|
|
if (layer === default_layer) {
|
|
|
|
// Update (set) display size
|
|
displayWidth = width;
|
|
displayHeight = height;
|
|
display.style.width = displayWidth + "px";
|
|
display.style.height = displayHeight + "px";
|
|
|
|
// Update bounds size
|
|
bounds.style.width = (displayWidth*displayScale) + "px";
|
|
bounds.style.height = (displayHeight*displayScale) + "px";
|
|
|
|
// Notify of resize
|
|
if (guac_display.onresize)
|
|
guac_display.onresize(width, height);
|
|
|
|
}
|
|
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Draws the specified image at the given coordinates. The image specified
|
|
* must already be loaded.
|
|
*
|
|
* @param {Guacamole.Layer} layer
|
|
* The layer to draw upon.
|
|
*
|
|
* @param {Number} x
|
|
* The destination X coordinate.
|
|
*
|
|
* @param {Number} y
|
|
* The destination Y coordinate.
|
|
*
|
|
* @param {CanvasImageSource} image
|
|
* The image to draw. Note that this not a URL.
|
|
*/
|
|
this.drawImage = function(layer, x, y, image) {
|
|
scheduleTask(function __display_drawImage() {
|
|
layer.drawImage(x, y, image);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Draws the image contained within the specified Blob at the given
|
|
* coordinates. The Blob specified must already be populated with image
|
|
* data.
|
|
*
|
|
* @param {Guacamole.Layer} layer
|
|
* The layer to draw upon.
|
|
*
|
|
* @param {Number} x
|
|
* The destination X coordinate.
|
|
*
|
|
* @param {Number} y
|
|
* The destination Y coordinate.
|
|
*
|
|
* @param {Blob} blob
|
|
* The Blob containing the image data to draw.
|
|
*/
|
|
this.drawBlob = function(layer, x, y, blob) {
|
|
|
|
var task;
|
|
|
|
// Prefer createImageBitmap() over blob URLs if available
|
|
if (window.createImageBitmap) {
|
|
|
|
var bitmap;
|
|
|
|
// Draw image once loaded
|
|
task = scheduleTask(function drawImageBitmap() {
|
|
layer.drawImage(x, y, bitmap);
|
|
}, true);
|
|
|
|
// Load image from provided blob
|
|
window.createImageBitmap(blob).then(function bitmapLoaded(decoded) {
|
|
bitmap = decoded;
|
|
task.unblock();
|
|
});
|
|
|
|
}
|
|
|
|
// Use blob URLs and the Image object if createImageBitmap() is
|
|
// unavailable
|
|
else {
|
|
|
|
// Create URL for blob
|
|
var url = URL.createObjectURL(blob);
|
|
|
|
// Draw and free blob URL when ready
|
|
task = scheduleTask(function __display_drawBlob() {
|
|
|
|
// Draw the image only if it loaded without errors
|
|
if (image.width && image.height)
|
|
layer.drawImage(x, y, image);
|
|
|
|
// Blob URL no longer needed
|
|
URL.revokeObjectURL(url);
|
|
|
|
}, true);
|
|
|
|
// Load image from URL
|
|
var image = new Image();
|
|
image.onload = task.unblock;
|
|
image.onerror = task.unblock;
|
|
image.src = url;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Draws the image within the given stream at the given coordinates. The
|
|
* image will be loaded automatically, and this and any future operations
|
|
* will wait for the image to finish loading. This function will
|
|
* automatically choose an approriate method for reading and decoding the
|
|
* given image stream, and should be preferred for received streams except
|
|
* where manual decoding of the stream is unavoidable.
|
|
*
|
|
* @param {Guacamole.Layer} layer
|
|
* The layer to draw upon.
|
|
*
|
|
* @param {Number} x
|
|
* The destination X coordinate.
|
|
*
|
|
* @param {Number} y
|
|
* The destination Y coordinate.
|
|
*
|
|
* @param {Guacamole.InputStream} stream
|
|
* The stream along which image data will be received.
|
|
*
|
|
* @param {String} mimetype
|
|
* The mimetype of the image within the stream.
|
|
*/
|
|
this.drawStream = function drawStream(layer, x, y, stream, mimetype) {
|
|
|
|
// If createImageBitmap() is available, load the image as a blob so
|
|
// that function can be used
|
|
if (window.createImageBitmap) {
|
|
var reader = new Guacamole.BlobReader(stream, mimetype);
|
|
reader.onend = function drawImageBlob() {
|
|
guac_display.drawBlob(layer, x, y, reader.getBlob());
|
|
};
|
|
}
|
|
|
|
// Lacking createImageBitmap(), fall back to data URIs and the Image
|
|
// object
|
|
else {
|
|
var reader = new Guacamole.DataURIReader(stream, mimetype);
|
|
reader.onend = function drawImageDataURI() {
|
|
guac_display.draw(layer, x, y, reader.getURI());
|
|
};
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Draws the image at the specified URL at the given coordinates. The image
|
|
* will be loaded automatically, and this and any future operations will
|
|
* wait for the image to finish loading.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} x The destination X coordinate.
|
|
* @param {Number} y The destination Y coordinate.
|
|
* @param {String} url The URL of the image to draw.
|
|
*/
|
|
this.draw = function(layer, x, y, url) {
|
|
|
|
var task = scheduleTask(function __display_draw() {
|
|
|
|
// Draw the image only if it loaded without errors
|
|
if (image.width && image.height)
|
|
layer.drawImage(x, y, image);
|
|
|
|
}, true);
|
|
|
|
var image = new Image();
|
|
image.onload = task.unblock;
|
|
image.onerror = task.unblock;
|
|
image.src = url;
|
|
|
|
};
|
|
|
|
/**
|
|
* Plays the video at the specified URL within this layer. The video
|
|
* will be loaded automatically, and this and any future operations will
|
|
* wait for the video to finish loading. Future operations will not be
|
|
* executed until the video finishes playing.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {String} mimetype The mimetype of the video to play.
|
|
* @param {Number} duration The duration of the video in milliseconds.
|
|
* @param {String} url The URL of the video to play.
|
|
*/
|
|
this.play = function(layer, mimetype, duration, url) {
|
|
|
|
// Start loading the video
|
|
var video = document.createElement("video");
|
|
video.type = mimetype;
|
|
video.src = url;
|
|
|
|
// Start copying frames when playing
|
|
video.addEventListener("play", function() {
|
|
|
|
function render_callback() {
|
|
layer.drawImage(0, 0, video);
|
|
if (!video.ended)
|
|
window.setTimeout(render_callback, 20);
|
|
}
|
|
|
|
render_callback();
|
|
|
|
}, false);
|
|
|
|
scheduleTask(video.play);
|
|
|
|
};
|
|
|
|
/**
|
|
* Transfer a rectangle of image data from one Layer to this Layer using the
|
|
* specified transfer function.
|
|
*
|
|
* @param {Guacamole.Layer} srcLayer The Layer to copy image data from.
|
|
* @param {Number} srcx The X coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcy The Y coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcw The width of the rectangle within the source Layer's
|
|
* coordinate space to copy data from.
|
|
* @param {Number} srch The height of the rectangle within the source
|
|
* Layer's coordinate space to copy data from.
|
|
* @param {Guacamole.Layer} dstLayer The layer to draw upon.
|
|
* @param {Number} x The destination X coordinate.
|
|
* @param {Number} y The destination Y coordinate.
|
|
* @param {Function} transferFunction The transfer function to use to
|
|
* transfer data from source to
|
|
* destination.
|
|
*/
|
|
this.transfer = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y, transferFunction) {
|
|
scheduleTask(function __display_transfer() {
|
|
dstLayer.transfer(srcLayer, srcx, srcy, srcw, srch, x, y, transferFunction);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Put a rectangle of image data from one Layer to this Layer directly
|
|
* without performing any alpha blending. Simply copy the data.
|
|
*
|
|
* @param {Guacamole.Layer} srcLayer The Layer to copy image data from.
|
|
* @param {Number} srcx The X coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcy The Y coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcw The width of the rectangle within the source Layer's
|
|
* coordinate space to copy data from.
|
|
* @param {Number} srch The height of the rectangle within the source
|
|
* Layer's coordinate space to copy data from.
|
|
* @param {Guacamole.Layer} dstLayer The layer to draw upon.
|
|
* @param {Number} x The destination X coordinate.
|
|
* @param {Number} y The destination Y coordinate.
|
|
*/
|
|
this.put = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y) {
|
|
scheduleTask(function __display_put() {
|
|
dstLayer.put(srcLayer, srcx, srcy, srcw, srch, x, y);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Copy a rectangle of image data from one Layer to this Layer. This
|
|
* operation will copy exactly the image data that will be drawn once all
|
|
* operations of the source Layer that were pending at the time this
|
|
* function was called are complete. This operation will not alter the
|
|
* size of the source Layer even if its autosize property is set to true.
|
|
*
|
|
* @param {Guacamole.Layer} srcLayer The Layer to copy image data from.
|
|
* @param {Number} srcx The X coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcy The Y coordinate of the upper-left corner of the
|
|
* rectangle within the source Layer's coordinate
|
|
* space to copy data from.
|
|
* @param {Number} srcw The width of the rectangle within the source Layer's
|
|
* coordinate space to copy data from.
|
|
* @param {Number} srch The height of the rectangle within the source
|
|
* Layer's coordinate space to copy data from.
|
|
* @param {Guacamole.Layer} dstLayer The layer to draw upon.
|
|
* @param {Number} x The destination X coordinate.
|
|
* @param {Number} y The destination Y coordinate.
|
|
*/
|
|
this.copy = function(srcLayer, srcx, srcy, srcw, srch, dstLayer, x, y) {
|
|
scheduleTask(function __display_copy() {
|
|
dstLayer.copy(srcLayer, srcx, srcy, srcw, srch, x, y);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Starts a new path at the specified point.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} x The X coordinate of the point to draw.
|
|
* @param {Number} y The Y coordinate of the point to draw.
|
|
*/
|
|
this.moveTo = function(layer, x, y) {
|
|
scheduleTask(function __display_moveTo() {
|
|
layer.moveTo(x, y);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Add the specified line to the current path.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} x The X coordinate of the endpoint of the line to draw.
|
|
* @param {Number} y The Y coordinate of the endpoint of the line to draw.
|
|
*/
|
|
this.lineTo = function(layer, x, y) {
|
|
scheduleTask(function __display_lineTo() {
|
|
layer.lineTo(x, y);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Add the specified arc to the current path.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} x The X coordinate of the center of the circle which
|
|
* will contain the arc.
|
|
* @param {Number} y The Y coordinate of the center of the circle which
|
|
* will contain the arc.
|
|
* @param {Number} radius The radius of the circle.
|
|
* @param {Number} startAngle The starting angle of the arc, in radians.
|
|
* @param {Number} endAngle The ending angle of the arc, in radians.
|
|
* @param {Boolean} negative Whether the arc should be drawn in order of
|
|
* decreasing angle.
|
|
*/
|
|
this.arc = function(layer, x, y, radius, startAngle, endAngle, negative) {
|
|
scheduleTask(function __display_arc() {
|
|
layer.arc(x, y, radius, startAngle, endAngle, negative);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Starts a new path at the specified point.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} cp1x The X coordinate of the first control point.
|
|
* @param {Number} cp1y The Y coordinate of the first control point.
|
|
* @param {Number} cp2x The X coordinate of the second control point.
|
|
* @param {Number} cp2y The Y coordinate of the second control point.
|
|
* @param {Number} x The X coordinate of the endpoint of the curve.
|
|
* @param {Number} y The Y coordinate of the endpoint of the curve.
|
|
*/
|
|
this.curveTo = function(layer, cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
scheduleTask(function __display_curveTo() {
|
|
layer.curveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Closes the current path by connecting the end point with the start
|
|
* point (if any) with a straight line.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
*/
|
|
this.close = function(layer) {
|
|
scheduleTask(function __display_close() {
|
|
layer.close();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Add the specified rectangle to the current path.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} x The X coordinate of the upper-left corner of the
|
|
* rectangle to draw.
|
|
* @param {Number} y The Y coordinate of the upper-left corner of the
|
|
* rectangle to draw.
|
|
* @param {Number} w The width of the rectangle to draw.
|
|
* @param {Number} h The height of the rectangle to draw.
|
|
*/
|
|
this.rect = function(layer, x, y, w, h) {
|
|
scheduleTask(function __display_rect() {
|
|
layer.rect(x, y, w, h);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Clip all future drawing operations by the current path. The current path
|
|
* is implicitly closed. The current path can continue to be reused
|
|
* for other operations (such as fillColor()) but a new path will be started
|
|
* once a path drawing operation (path() or rect()) is used.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to affect.
|
|
*/
|
|
this.clip = function(layer) {
|
|
scheduleTask(function __display_clip() {
|
|
layer.clip();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Stroke the current path with the specified color. The current path
|
|
* is implicitly closed. The current path can continue to be reused
|
|
* for other operations (such as clip()) but a new path will be started
|
|
* once a path drawing operation (path() or rect()) is used.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {String} cap The line cap style. Can be "round", "square",
|
|
* or "butt".
|
|
* @param {String} join The line join style. Can be "round", "bevel",
|
|
* or "miter".
|
|
* @param {Number} thickness The line thickness in pixels.
|
|
* @param {Number} r The red component of the color to fill.
|
|
* @param {Number} g The green component of the color to fill.
|
|
* @param {Number} b The blue component of the color to fill.
|
|
* @param {Number} a The alpha component of the color to fill.
|
|
*/
|
|
this.strokeColor = function(layer, cap, join, thickness, r, g, b, a) {
|
|
scheduleTask(function __display_strokeColor() {
|
|
layer.strokeColor(cap, join, thickness, r, g, b, a);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Fills the current path with the specified color. The current path
|
|
* is implicitly closed. The current path can continue to be reused
|
|
* for other operations (such as clip()) but a new path will be started
|
|
* once a path drawing operation (path() or rect()) is used.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Number} r The red component of the color to fill.
|
|
* @param {Number} g The green component of the color to fill.
|
|
* @param {Number} b The blue component of the color to fill.
|
|
* @param {Number} a The alpha component of the color to fill.
|
|
*/
|
|
this.fillColor = function(layer, r, g, b, a) {
|
|
scheduleTask(function __display_fillColor() {
|
|
layer.fillColor(r, g, b, a);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Stroke the current path with the image within the specified layer. The
|
|
* image data will be tiled infinitely within the stroke. The current path
|
|
* is implicitly closed. The current path can continue to be reused
|
|
* for other operations (such as clip()) but a new path will be started
|
|
* once a path drawing operation (path() or rect()) is used.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {String} cap The line cap style. Can be "round", "square",
|
|
* or "butt".
|
|
* @param {String} join The line join style. Can be "round", "bevel",
|
|
* or "miter".
|
|
* @param {Number} thickness The line thickness in pixels.
|
|
* @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern
|
|
* within the stroke.
|
|
*/
|
|
this.strokeLayer = function(layer, cap, join, thickness, srcLayer) {
|
|
scheduleTask(function __display_strokeLayer() {
|
|
layer.strokeLayer(cap, join, thickness, srcLayer);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Fills the current path with the image within the specified layer. The
|
|
* image data will be tiled infinitely within the stroke. The current path
|
|
* is implicitly closed. The current path can continue to be reused
|
|
* for other operations (such as clip()) but a new path will be started
|
|
* once a path drawing operation (path() or rect()) is used.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
* @param {Guacamole.Layer} srcLayer The layer to use as a repeating pattern
|
|
* within the fill.
|
|
*/
|
|
this.fillLayer = function(layer, srcLayer) {
|
|
scheduleTask(function __display_fillLayer() {
|
|
layer.fillLayer(srcLayer);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Push current layer state onto stack.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
*/
|
|
this.push = function(layer) {
|
|
scheduleTask(function __display_push() {
|
|
layer.push();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Pop layer state off stack.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
*/
|
|
this.pop = function(layer) {
|
|
scheduleTask(function __display_pop() {
|
|
layer.pop();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Reset the layer, clearing the stack, the current path, and any transform
|
|
* matrix.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to draw upon.
|
|
*/
|
|
this.reset = function(layer) {
|
|
scheduleTask(function __display_reset() {
|
|
layer.reset();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets the given affine transform (defined with six values from the
|
|
* transform's matrix).
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to modify.
|
|
* @param {Number} a The first value in the affine transform's matrix.
|
|
* @param {Number} b The second value in the affine transform's matrix.
|
|
* @param {Number} c The third value in the affine transform's matrix.
|
|
* @param {Number} d The fourth value in the affine transform's matrix.
|
|
* @param {Number} e The fifth value in the affine transform's matrix.
|
|
* @param {Number} f The sixth value in the affine transform's matrix.
|
|
*/
|
|
this.setTransform = function(layer, a, b, c, d, e, f) {
|
|
scheduleTask(function __display_setTransform() {
|
|
layer.setTransform(a, b, c, d, e, f);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Applies the given affine transform (defined with six values from the
|
|
* transform's matrix).
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to modify.
|
|
* @param {Number} a The first value in the affine transform's matrix.
|
|
* @param {Number} b The second value in the affine transform's matrix.
|
|
* @param {Number} c The third value in the affine transform's matrix.
|
|
* @param {Number} d The fourth value in the affine transform's matrix.
|
|
* @param {Number} e The fifth value in the affine transform's matrix.
|
|
* @param {Number} f The sixth value in the affine transform's matrix.
|
|
*/
|
|
this.transform = function(layer, a, b, c, d, e, f) {
|
|
scheduleTask(function __display_transform() {
|
|
layer.transform(a, b, c, d, e, f);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets the channel mask for future operations on this Layer.
|
|
*
|
|
* The channel mask is a Guacamole-specific compositing operation identifier
|
|
* with a single bit representing each of four channels (in order): source
|
|
* image where destination transparent, source where destination opaque,
|
|
* destination where source transparent, and destination where source
|
|
* opaque.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to modify.
|
|
* @param {Number} mask The channel mask for future operations on this
|
|
* Layer.
|
|
*/
|
|
this.setChannelMask = function(layer, mask) {
|
|
scheduleTask(function __display_setChannelMask() {
|
|
layer.setChannelMask(mask);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets the miter limit for stroke operations using the miter join. This
|
|
* limit is the maximum ratio of the size of the miter join to the stroke
|
|
* width. If this ratio is exceeded, the miter will not be drawn for that
|
|
* joint of the path.
|
|
*
|
|
* @param {Guacamole.Layer} layer The layer to modify.
|
|
* @param {Number} limit The miter limit for stroke operations using the
|
|
* miter join.
|
|
*/
|
|
this.setMiterLimit = function(layer, limit) {
|
|
scheduleTask(function __display_setMiterLimit() {
|
|
layer.setMiterLimit(limit);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Removes the given layer container entirely, such that it is no longer
|
|
* contained within its parent layer, if any.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} layer
|
|
* The layer being removed from its parent.
|
|
*/
|
|
this.dispose = function dispose(layer) {
|
|
scheduleTask(function disposeLayer() {
|
|
layer.dispose();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Applies the given affine transform (defined with six values from the
|
|
* transform's matrix) to the given layer.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} layer
|
|
* The layer being distorted.
|
|
*
|
|
* @param {Number} a
|
|
* The first value in the affine transform's matrix.
|
|
*
|
|
* @param {Number} b
|
|
* The second value in the affine transform's matrix.
|
|
*
|
|
* @param {Number} c
|
|
* The third value in the affine transform's matrix.
|
|
*
|
|
* @param {Number} d
|
|
* The fourth value in the affine transform's matrix.
|
|
*
|
|
* @param {Number} e
|
|
* The fifth value in the affine transform's matrix.
|
|
*
|
|
* @param {Number} f
|
|
* The sixth value in the affine transform's matrix.
|
|
*/
|
|
this.distort = function distort(layer, a, b, c, d, e, f) {
|
|
scheduleTask(function distortLayer() {
|
|
layer.distort(a, b, c, d, e, f);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Moves the upper-left corner of the given layer to the given X and Y
|
|
* coordinate, sets the Z stacking order, and reparents the layer
|
|
* to the given parent layer.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} layer
|
|
* The layer being moved.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} parent
|
|
* The parent to set.
|
|
*
|
|
* @param {Number} x
|
|
* The X coordinate to move to.
|
|
*
|
|
* @param {Number} y
|
|
* The Y coordinate to move to.
|
|
*
|
|
* @param {Number} z
|
|
* The Z coordinate to move to.
|
|
*/
|
|
this.move = function move(layer, parent, x, y, z) {
|
|
scheduleTask(function moveLayer() {
|
|
layer.move(parent, x, y, z);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets the opacity of the given layer to the given value, where 255 is
|
|
* fully opaque and 0 is fully transparent.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} layer
|
|
* The layer whose opacity should be set.
|
|
*
|
|
* @param {Number} alpha
|
|
* The opacity to set.
|
|
*/
|
|
this.shade = function shade(layer, alpha) {
|
|
scheduleTask(function shadeLayer() {
|
|
layer.shade(alpha);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Sets the scale of the client display element such that it renders at
|
|
* a relatively smaller or larger size, without affecting the true
|
|
* resolution of the display.
|
|
*
|
|
* @param {Number} scale The scale to resize to, where 1.0 is normal
|
|
* size (1:1 scale).
|
|
*/
|
|
this.scale = function(scale) {
|
|
|
|
display.style.transform =
|
|
display.style.WebkitTransform =
|
|
display.style.MozTransform =
|
|
display.style.OTransform =
|
|
display.style.msTransform =
|
|
|
|
"scale(" + scale + "," + scale + ")";
|
|
|
|
displayScale = scale;
|
|
|
|
// Update bounds size
|
|
bounds.style.width = (displayWidth*displayScale) + "px";
|
|
bounds.style.height = (displayHeight*displayScale) + "px";
|
|
|
|
};
|
|
|
|
/**
|
|
* Returns the scale of the display.
|
|
*
|
|
* @return {Number} The scale of the display.
|
|
*/
|
|
this.getScale = function() {
|
|
return displayScale;
|
|
};
|
|
|
|
/**
|
|
* Returns a canvas element containing the entire display, with all child
|
|
* layers composited within.
|
|
*
|
|
* @return {!HTMLCanvasElement} A new canvas element containing a copy of
|
|
* the display.
|
|
*/
|
|
this.flatten = function() {
|
|
|
|
// Get destination canvas
|
|
var canvas = document.createElement("canvas");
|
|
canvas.width = default_layer.width;
|
|
canvas.height = default_layer.height;
|
|
|
|
var context = canvas.getContext("2d");
|
|
|
|
// Returns sorted array of children
|
|
function get_children(layer) {
|
|
|
|
// Build array of children
|
|
var children = [];
|
|
for (var index in layer.children)
|
|
children.push(layer.children[index]);
|
|
|
|
// Sort
|
|
children.sort(function children_comparator(a, b) {
|
|
|
|
// Compare based on Z order
|
|
var diff = a.z - b.z;
|
|
if (diff !== 0)
|
|
return diff;
|
|
|
|
// If Z order identical, use document order
|
|
var a_element = a.getElement();
|
|
var b_element = b.getElement();
|
|
var position = b_element.compareDocumentPosition(a_element);
|
|
|
|
if (position & Node.DOCUMENT_POSITION_PRECEDING) return -1;
|
|
if (position & Node.DOCUMENT_POSITION_FOLLOWING) return 1;
|
|
|
|
// Otherwise, assume same
|
|
return 0;
|
|
|
|
});
|
|
|
|
// Done
|
|
return children;
|
|
|
|
}
|
|
|
|
// Draws the contents of the given layer at the given coordinates
|
|
function draw_layer(layer, x, y) {
|
|
|
|
// Draw layer
|
|
if (layer.width > 0 && layer.height > 0) {
|
|
|
|
// Save and update alpha
|
|
var initial_alpha = context.globalAlpha;
|
|
context.globalAlpha *= layer.alpha / 255.0;
|
|
|
|
// Copy data
|
|
context.drawImage(layer.getCanvas(), x, y);
|
|
|
|
// Draw all children
|
|
var children = get_children(layer);
|
|
for (var i=0; i<children.length; i++) {
|
|
var child = children[i];
|
|
draw_layer(child, x + child.x, y + child.y);
|
|
}
|
|
|
|
// Restore alpha
|
|
context.globalAlpha = initial_alpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Draw default layer and all children
|
|
draw_layer(default_layer, 0, 0);
|
|
|
|
// Return new canvas copy
|
|
return canvas;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
/**
|
|
* Simple container for Guacamole.Layer, allowing layers to be easily
|
|
* repositioned and nested. This allows certain operations to be accelerated
|
|
* through DOM manipulation, rather than raster operations.
|
|
*
|
|
* @constructor
|
|
* @augments Guacamole.Layer
|
|
* @param {Number} width The width of the Layer, in pixels. The canvas element
|
|
* backing this Layer will be given this width.
|
|
* @param {Number} height The height of the Layer, in pixels. The canvas element
|
|
* backing this Layer will be given this height.
|
|
*/
|
|
Guacamole.Display.VisibleLayer = function(width, height) {
|
|
|
|
Guacamole.Layer.apply(this, [width, height]);
|
|
|
|
/**
|
|
* Reference to this layer.
|
|
* @private
|
|
*/
|
|
var layer = this;
|
|
|
|
/**
|
|
* Identifier which uniquely identifies this layer. This is COMPLETELY
|
|
* UNRELATED to the index of the underlying layer, which is specific
|
|
* to the Guacamole protocol, and not relevant at this level.
|
|
*
|
|
* @private
|
|
* @type {Number}
|
|
*/
|
|
this.__unique_id = Guacamole.Display.VisibleLayer.__next_id++;
|
|
|
|
/**
|
|
* The opacity of the layer container, where 255 is fully opaque and 0 is
|
|
* fully transparent.
|
|
*/
|
|
this.alpha = 0xFF;
|
|
|
|
/**
|
|
* X coordinate of the upper-left corner of this layer container within
|
|
* its parent, in pixels.
|
|
* @type {Number}
|
|
*/
|
|
this.x = 0;
|
|
|
|
/**
|
|
* Y coordinate of the upper-left corner of this layer container within
|
|
* its parent, in pixels.
|
|
* @type {Number}
|
|
*/
|
|
this.y = 0;
|
|
|
|
/**
|
|
* Z stacking order of this layer relative to other sibling layers.
|
|
* @type {Number}
|
|
*/
|
|
this.z = 0;
|
|
|
|
/**
|
|
* The affine transformation applied to this layer container. Each element
|
|
* corresponds to a value from the transformation matrix, with the first
|
|
* three values being the first row, and the last three values being the
|
|
* second row. There are six values total.
|
|
*
|
|
* @type {Number[]}
|
|
*/
|
|
this.matrix = [1, 0, 0, 1, 0, 0];
|
|
|
|
/**
|
|
* The parent layer container of this layer, if any.
|
|
* @type {Guacamole.Display.VisibleLayer}
|
|
*/
|
|
this.parent = null;
|
|
|
|
/**
|
|
* Set of all children of this layer, indexed by layer index. This object
|
|
* will have one property per child.
|
|
*/
|
|
this.children = {};
|
|
|
|
// Set layer position
|
|
var canvas = layer.getCanvas();
|
|
canvas.style.position = "absolute";
|
|
canvas.style.left = "0px";
|
|
canvas.style.top = "0px";
|
|
|
|
// Create div with given size
|
|
var div = document.createElement("div");
|
|
div.appendChild(canvas);
|
|
div.style.width = width + "px";
|
|
div.style.height = height + "px";
|
|
div.style.position = "absolute";
|
|
div.style.left = "0px";
|
|
div.style.top = "0px";
|
|
div.style.overflow = "hidden";
|
|
|
|
/**
|
|
* Superclass resize() function.
|
|
* @private
|
|
*/
|
|
var __super_resize = this.resize;
|
|
|
|
this.resize = function(width, height) {
|
|
|
|
// Resize containing div
|
|
div.style.width = width + "px";
|
|
div.style.height = height + "px";
|
|
|
|
__super_resize(width, height);
|
|
|
|
};
|
|
|
|
/**
|
|
* Returns the element containing the canvas and any other elements
|
|
* associated with this layer.
|
|
* @returns {!Element} The element containing this layer's canvas.
|
|
*/
|
|
this.getElement = function() {
|
|
return div;
|
|
};
|
|
|
|
/**
|
|
* The translation component of this layer's transform.
|
|
* @private
|
|
*/
|
|
var translate = "translate(0px, 0px)"; // (0, 0)
|
|
|
|
/**
|
|
* The arbitrary matrix component of this layer's transform.
|
|
* @private
|
|
*/
|
|
var matrix = "matrix(1, 0, 0, 1, 0, 0)"; // Identity
|
|
|
|
/**
|
|
* Moves the upper-left corner of this layer to the given X and Y
|
|
* coordinate.
|
|
*
|
|
* @param {Number} x The X coordinate to move to.
|
|
* @param {Number} y The Y coordinate to move to.
|
|
*/
|
|
this.translate = function(x, y) {
|
|
|
|
layer.x = x;
|
|
layer.y = y;
|
|
|
|
// Generate translation
|
|
translate = "translate("
|
|
+ x + "px,"
|
|
+ y + "px)";
|
|
|
|
// Set layer transform
|
|
div.style.transform =
|
|
div.style.WebkitTransform =
|
|
div.style.MozTransform =
|
|
div.style.OTransform =
|
|
div.style.msTransform =
|
|
|
|
translate + " " + matrix;
|
|
|
|
};
|
|
|
|
/**
|
|
* Moves the upper-left corner of this VisibleLayer to the given X and Y
|
|
* coordinate, sets the Z stacking order, and reparents this VisibleLayer
|
|
* to the given VisibleLayer.
|
|
*
|
|
* @param {Guacamole.Display.VisibleLayer} parent The parent to set.
|
|
* @param {Number} x The X coordinate to move to.
|
|
* @param {Number} y The Y coordinate to move to.
|
|
* @param {Number} z The Z coordinate to move to.
|
|
*/
|
|
this.move = function(parent, x, y, z) {
|
|
|
|
// Set parent if necessary
|
|
if (layer.parent !== parent) {
|
|
|
|
// Maintain relationship
|
|
if (layer.parent)
|
|
delete layer.parent.children[layer.__unique_id];
|
|
layer.parent = parent;
|
|
parent.children[layer.__unique_id] = layer;
|
|
|
|
// Reparent element
|
|
var parent_element = parent.getElement();
|
|
parent_element.appendChild(div);
|
|
|
|
}
|
|
|
|
// Set location
|
|
layer.translate(x, y);
|
|
layer.z = z;
|
|
div.style.zIndex = z;
|
|
|
|
};
|
|
|
|
/**
|
|
* Sets the opacity of this layer to the given value, where 255 is fully
|
|
* opaque and 0 is fully transparent.
|
|
*
|
|
* @param {Number} a The opacity to set.
|
|
*/
|
|
this.shade = function(a) {
|
|
layer.alpha = a;
|
|
div.style.opacity = a/255.0;
|
|
};
|
|
|
|
/**
|
|
* Removes this layer container entirely, such that it is no longer
|
|
* contained within its parent layer, if any.
|
|
*/
|
|
this.dispose = function() {
|
|
|
|
// Remove from parent container
|
|
if (layer.parent) {
|
|
delete layer.parent.children[layer.__unique_id];
|
|
layer.parent = null;
|
|
}
|
|
|
|
// Remove from parent element
|
|
if (div.parentNode)
|
|
div.parentNode.removeChild(div);
|
|
|
|
};
|
|
|
|
/**
|
|
* Applies the given affine transform (defined with six values from the
|
|
* transform's matrix).
|
|
*
|
|
* @param {Number} a The first value in the affine transform's matrix.
|
|
* @param {Number} b The second value in the affine transform's matrix.
|
|
* @param {Number} c The third value in the affine transform's matrix.
|
|
* @param {Number} d The fourth value in the affine transform's matrix.
|
|
* @param {Number} e The fifth value in the affine transform's matrix.
|
|
* @param {Number} f The sixth value in the affine transform's matrix.
|
|
*/
|
|
this.distort = function(a, b, c, d, e, f) {
|
|
|
|
// Store matrix
|
|
layer.matrix = [a, b, c, d, e, f];
|
|
|
|
// Generate matrix transformation
|
|
matrix =
|
|
|
|
/* a c e
|
|
* b d f
|
|
* 0 0 1
|
|
*/
|
|
|
|
"matrix(" + a + "," + b + "," + c + "," + d + "," + e + "," + f + ")";
|
|
|
|
// Set layer transform
|
|
div.style.transform =
|
|
div.style.WebkitTransform =
|
|
div.style.MozTransform =
|
|
div.style.OTransform =
|
|
div.style.msTransform =
|
|
|
|
translate + " " + matrix;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
/**
|
|
* The next identifier to be assigned to the layer container. This identifier
|
|
* uniquely identifies each VisibleLayer, but is unrelated to the index of
|
|
* the layer, which exists at the protocol/client level only.
|
|
*
|
|
* @private
|
|
* @type {Number}
|
|
*/
|
|
Guacamole.Display.VisibleLayer.__next_id = 0;
|