http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/06386843/content/doc/0.9.10-incubating/guacamole-common-js/Keyboard.js.html ---------------------------------------------------------------------- diff --git a/content/doc/0.9.10-incubating/guacamole-common-js/Keyboard.js.html b/content/doc/0.9.10-incubating/guacamole-common-js/Keyboard.js.html new file mode 100644 index 0000000..8f51742 --- /dev/null +++ b/content/doc/0.9.10-incubating/guacamole-common-js/Keyboard.js.html @@ -0,0 +1,1230 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: Keyboard.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: Keyboard.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 || {}; + +/** + * Provides cross-browser and cross-keyboard keyboard for a specific element. + * Browser and keyboard layout variation is abstracted away, providing events + * which represent keys as their corresponding X11 keysym. + * + * @constructor + * @param {Element} element The Element to use to provide keyboard events. + */ +Guacamole.Keyboard = function(element) { + + /** + * Reference to this Guacamole.Keyboard. + * @private + */ + var guac_keyboard = this; + + /** + * Fired whenever the user presses a key with the element associated + * with this Guacamole.Keyboard in focus. + * + * @event + * @param {Number} keysym The keysym of the key being pressed. + * @return {Boolean} true if the key event should be allowed through to the + * browser, false otherwise. + */ + this.onkeydown = null; + + /** + * Fired whenever the user releases a key with the element associated + * with this Guacamole.Keyboard in focus. + * + * @event + * @param {Number} keysym The keysym of the key being released. + */ + this.onkeyup = null; + + /** + * A key event having a corresponding timestamp. This event is non-specific. + * Its subclasses should be used instead when recording specific key + * events. + * + * @private + * @constructor + */ + var KeyEvent = function() { + + /** + * Reference to this key event. + */ + var key_event = this; + + /** + * An arbitrary timestamp in milliseconds, indicating this event's + * position in time relative to other events. + * + * @type {Number} + */ + this.timestamp = new Date().getTime(); + + /** + * Whether the default action of this key event should be prevented. + * + * @type {Boolean} + */ + this.defaultPrevented = false; + + /** + * The keysym of the key associated with this key event, as determined + * by a best-effort guess using available event properties and keyboard + * state. + * + * @type {Number} + */ + this.keysym = null; + + /** + * Whether the keysym value of this key event is known to be reliable. + * If false, the keysym may still be valid, but it's only a best guess, + * and future key events may be a better source of information. + * + * @type {Boolean} + */ + this.reliable = false; + + /** + * Returns the number of milliseconds elapsed since this event was + * received. + * + * @return {Number} The number of milliseconds elapsed since this + * event was received. + */ + this.getAge = function() { + return new Date().getTime() - key_event.timestamp; + }; + + }; + + /** + * Information related to the pressing of a key, which need not be a key + * associated with a printable character. The presence or absence of any + * information within this object is browser-dependent. + * + * @private + * @constructor + * @augments Guacamole.Keyboard.KeyEvent + * @param {Number} keyCode The JavaScript key code of the key pressed. + * @param {String} keyIdentifier The legacy DOM3 "keyIdentifier" of the key + * pressed, as defined at: + * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent + * @param {String} key The standard name of the key pressed, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * @param {Number} location The location on the keyboard corresponding to + * the key pressed, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + */ + var KeydownEvent = function(keyCode, keyIdentifier, key, location) { + + // We extend KeyEvent + KeyEvent.apply(this); + + /** + * The JavaScript key code of the key pressed. + * + * @type {Number} + */ + this.keyCode = keyCode; + + /** + * The legacy DOM3 "keyIdentifier" of the key pressed, as defined at: + * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent + * + * @type {String} + */ + this.keyIdentifier = keyIdentifier; + + /** + * The standard name of the key pressed, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * + * @type {String} + */ + this.key = key; + + /** + * The location on the keyboard corresponding to the key pressed, as + * defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * + * @type {Number} + */ + this.location = location; + + // If key is known from keyCode or DOM3 alone, use that + this.keysym = keysym_from_key_identifier(key, location) + || keysym_from_keycode(keyCode, location); + + // DOM3 and keyCode are reliable sources if the corresponding key is + // not a printable key + if (this.keysym && !isPrintable(this.keysym)) + this.reliable = true; + + // Use legacy keyIdentifier as a last resort, if it looks sane + if (!this.keysym && key_identifier_sane(keyCode, keyIdentifier)) + this.keysym = keysym_from_key_identifier(keyIdentifier, location, guac_keyboard.modifiers.shift); + + // Determine whether default action for Alt+combinations must be prevented + var prevent_alt = !guac_keyboard.modifiers.ctrl + && !(navigator && navigator.platform && navigator.platform.match(/^mac/i)); + + // Determine whether default action for Ctrl+combinations must be prevented + var prevent_ctrl = !guac_keyboard.modifiers.alt; + + // We must rely on the (potentially buggy) keyIdentifier if preventing + // the default action is important + if ((prevent_ctrl && guac_keyboard.modifiers.ctrl) + || (prevent_alt && guac_keyboard.modifiers.alt) + || guac_keyboard.modifiers.meta + || guac_keyboard.modifiers.hyper) + this.reliable = true; + + // Record most recently known keysym by associated key code + recentKeysym[keyCode] = this.keysym; + + }; + + KeydownEvent.prototype = new KeyEvent(); + + /** + * Information related to the pressing of a key, which MUST be + * associated with a printable character. The presence or absence of any + * information within this object is browser-dependent. + * + * @private + * @constructor + * @augments Guacamole.Keyboard.KeyEvent + * @param {Number} charCode The Unicode codepoint of the character that + * would be typed by the key pressed. + */ + var KeypressEvent = function(charCode) { + + // We extend KeyEvent + KeyEvent.apply(this); + + /** + * The Unicode codepoint of the character that would be typed by the + * key pressed. + * + * @type {Number} + */ + this.charCode = charCode; + + // Pull keysym from char code + this.keysym = keysym_from_charcode(charCode); + + // Keypress is always reliable + this.reliable = true; + + }; + + KeypressEvent.prototype = new KeyEvent(); + + /** + * Information related to the pressing of a key, which need not be a key + * associated with a printable character. The presence or absence of any + * information within this object is browser-dependent. + * + * @private + * @constructor + * @augments Guacamole.Keyboard.KeyEvent + * @param {Number} keyCode The JavaScript key code of the key released. + * @param {String} keyIdentifier The legacy DOM3 "keyIdentifier" of the key + * released, as defined at: + * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent + * @param {String} key The standard name of the key released, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * @param {Number} location The location on the keyboard corresponding to + * the key released, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + */ + var KeyupEvent = function(keyCode, keyIdentifier, key, location) { + + // We extend KeyEvent + KeyEvent.apply(this); + + /** + * The JavaScript key code of the key released. + * + * @type {Number} + */ + this.keyCode = keyCode; + + /** + * The legacy DOM3 "keyIdentifier" of the key released, as defined at: + * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent + * + * @type {String} + */ + this.keyIdentifier = keyIdentifier; + + /** + * The standard name of the key released, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * + * @type {String} + */ + this.key = key; + + /** + * The location on the keyboard corresponding to the key released, as + * defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + * + * @type {Number} + */ + this.location = location; + + // If key is known from keyCode or DOM3 alone, use that + this.keysym = recentKeysym[keyCode] + || keysym_from_keycode(keyCode, location) + || keysym_from_key_identifier(key, location); // keyCode is still more reliable for keyup when dead keys are in use + + // Keyup is as reliable as it will ever be + this.reliable = true; + + }; + + KeyupEvent.prototype = new KeyEvent(); + + /** + * An array of recorded events, which can be instances of the private + * KeydownEvent, KeypressEvent, and KeyupEvent classes. + * + * @private + * @type {KeyEvent[]} + */ + var eventLog = []; + + /** + * Map of known JavaScript keycodes which do not map to typable characters + * to their X11 keysym equivalents. + * @private + */ + var keycodeKeysyms = { + 8: [0xFF08], // backspace + 9: [0xFF09], // tab + 12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5 + 13: [0xFF0D], // enter + 16: [0xFFE1, 0xFFE1, 0xFFE2], // shift + 17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl + 18: [0xFFE9, 0xFFE9, 0xFE03], // alt + 19: [0xFF13], // pause/break + 20: [0xFFE5], // caps lock + 27: [0xFF1B], // escape + 32: [0x0020], // space + 33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9 + 34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3 + 35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1 + 36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7 + 37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4 + 38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8 + 39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6 + 40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2 + 45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0 + 46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal + 91: [0xFFEB], // left window key (hyper_l) + 92: [0xFF67], // right window key (menu key?) + 93: null, // select key + 96: [0xFFB0], // KP 0 + 97: [0xFFB1], // KP 1 + 98: [0xFFB2], // KP 2 + 99: [0xFFB3], // KP 3 + 100: [0xFFB4], // KP 4 + 101: [0xFFB5], // KP 5 + 102: [0xFFB6], // KP 6 + 103: [0xFFB7], // KP 7 + 104: [0xFFB8], // KP 8 + 105: [0xFFB9], // KP 9 + 106: [0xFFAA], // KP multiply + 107: [0xFFAB], // KP add + 109: [0xFFAD], // KP subtract + 110: [0xFFAE], // KP decimal + 111: [0xFFAF], // KP divide + 112: [0xFFBE], // f1 + 113: [0xFFBF], // f2 + 114: [0xFFC0], // f3 + 115: [0xFFC1], // f4 + 116: [0xFFC2], // f5 + 117: [0xFFC3], // f6 + 118: [0xFFC4], // f7 + 119: [0xFFC5], // f8 + 120: [0xFFC6], // f9 + 121: [0xFFC7], // f10 + 122: [0xFFC8], // f11 + 123: [0xFFC9], // f12 + 144: [0xFF7F], // num lock + 145: [0xFF14], // scroll lock + 225: [0xFE03] // altgraph (iso_level3_shift) + }; + + /** + * Map of known JavaScript keyidentifiers which do not map to typable + * characters to their unshifted X11 keysym equivalents. + * @private + */ + var keyidentifier_keysym = { + "Again": [0xFF66], + "AllCandidates": [0xFF3D], + "Alphanumeric": [0xFF30], + "Alt": [0xFFE9, 0xFFE9, 0xFE03], + "Attn": [0xFD0E], + "AltGraph": [0xFE03], + "ArrowDown": [0xFF54], + "ArrowLeft": [0xFF51], + "ArrowRight": [0xFF53], + "ArrowUp": [0xFF52], + "Backspace": [0xFF08], + "CapsLock": [0xFFE5], + "Cancel": [0xFF69], + "Clear": [0xFF0B], + "Convert": [0xFF21], + "Copy": [0xFD15], + "Crsel": [0xFD1C], + "CrSel": [0xFD1C], + "CodeInput": [0xFF37], + "Compose": [0xFF20], + "Control": [0xFFE3, 0xFFE3, 0xFFE4], + "ContextMenu": [0xFF67], + "DeadGrave": [0xFE50], + "DeadAcute": [0xFE51], + "DeadCircumflex": [0xFE52], + "DeadTilde": [0xFE53], + "DeadMacron": [0xFE54], + "DeadBreve": [0xFE55], + "DeadAboveDot": [0xFE56], + "DeadUmlaut": [0xFE57], + "DeadAboveRing": [0xFE58], + "DeadDoubleacute": [0xFE59], + "DeadCaron": [0xFE5A], + "DeadCedilla": [0xFE5B], + "DeadOgonek": [0xFE5C], + "DeadIota": [0xFE5D], + "DeadVoicedSound": [0xFE5E], + "DeadSemivoicedSound": [0xFE5F], + "Delete": [0xFFFF], + "Down": [0xFF54], + "End": [0xFF57], + "Enter": [0xFF0D], + "EraseEof": [0xFD06], + "Escape": [0xFF1B], + "Execute": [0xFF62], + "Exsel": [0xFD1D], + "ExSel": [0xFD1D], + "F1": [0xFFBE], + "F2": [0xFFBF], + "F3": [0xFFC0], + "F4": [0xFFC1], + "F5": [0xFFC2], + "F6": [0xFFC3], + "F7": [0xFFC4], + "F8": [0xFFC5], + "F9": [0xFFC6], + "F10": [0xFFC7], + "F11": [0xFFC8], + "F12": [0xFFC9], + "F13": [0xFFCA], + "F14": [0xFFCB], + "F15": [0xFFCC], + "F16": [0xFFCD], + "F17": [0xFFCE], + "F18": [0xFFCF], + "F19": [0xFFD0], + "F20": [0xFFD1], + "F21": [0xFFD2], + "F22": [0xFFD3], + "F23": [0xFFD4], + "F24": [0xFFD5], + "Find": [0xFF68], + "GroupFirst": [0xFE0C], + "GroupLast": [0xFE0E], + "GroupNext": [0xFE08], + "GroupPrevious": [0xFE0A], + "FullWidth": null, + "HalfWidth": null, + "HangulMode": [0xFF31], + "Hankaku": [0xFF29], + "HanjaMode": [0xFF34], + "Help": [0xFF6A], + "Hiragana": [0xFF25], + "HiraganaKatakana": [0xFF27], + "Home": [0xFF50], + "Hyper": [0xFFED, 0xFFED, 0xFFEE], + "Insert": [0xFF63], + "JapaneseHiragana": [0xFF25], + "JapaneseKatakana": [0xFF26], + "JapaneseRomaji": [0xFF24], + "JunjaMode": [0xFF38], + "KanaMode": [0xFF2D], + "KanjiMode": [0xFF21], + "Katakana": [0xFF26], + "Left": [0xFF51], + "Meta": [0xFFE7, 0xFFE7, 0xFFE8], + "ModeChange": [0xFF7E], + "NumLock": [0xFF7F], + "PageDown": [0xFF56], + "PageUp": [0xFF55], + "Pause": [0xFF13], + "Play": [0xFD16], + "PreviousCandidate": [0xFF3E], + "PrintScreen": [0xFD1D], + "Redo": [0xFF66], + "Right": [0xFF53], + "RomanCharacters": null, + "Scroll": [0xFF14], + "Select": [0xFF60], + "Separator": [0xFFAC], + "Shift": [0xFFE1, 0xFFE1, 0xFFE2], + "SingleCandidate": [0xFF3C], + "Super": [0xFFEB, 0xFFEB, 0xFFEC], + "Tab": [0xFF09], + "Up": [0xFF52], + "Undo": [0xFF65], + "Win": [0xFFEB], + "Zenkaku": [0xFF28], + "ZenkakuHankaku": [0xFF2A] + }; + + /** + * All keysyms which should not repeat when held down. + * @private + */ + var no_repeat = { + 0xFE03: true, // ISO Level 3 Shift (AltGr) + 0xFFE1: true, // Left shift + 0xFFE2: true, // Right shift + 0xFFE3: true, // Left ctrl + 0xFFE4: true, // Right ctrl + 0xFFE7: true, // Left meta + 0xFFE8: true, // Right meta + 0xFFE9: true, // Left alt + 0xFFEA: true, // Right alt + 0xFFEB: true, // Left hyper + 0xFFEC: true // Right hyper + }; + + /** + * All modifiers and their states. + */ + this.modifiers = new Guacamole.Keyboard.ModifierState(); + + /** + * The state of every key, indexed by keysym. If a particular key is + * pressed, the value of pressed for that keysym will be true. If a key + * is not currently pressed, it will not be defined. + */ + this.pressed = {}; + + /** + * The last result of calling the onkeydown handler for each key, indexed + * by keysym. This is used to prevent/allow default actions for key events, + * even when the onkeydown handler cannot be called again because the key + * is (theoretically) still pressed. + * + * @private + */ + var last_keydown_result = {}; + + /** + * The keysym most recently associated with a given keycode when keydown + * fired. This object maps keycodes to keysyms. + * + * @private + * @type {Object.<Number, Number>} + */ + var recentKeysym = {}; + + /** + * Timeout before key repeat starts. + * @private + */ + var key_repeat_timeout = null; + + /** + * Interval which presses and releases the last key pressed while that + * key is still being held down. + * @private + */ + var key_repeat_interval = null; + + /** + * Given an array of keysyms indexed by location, returns the keysym + * for the given location, or the keysym for the standard location if + * undefined. + * + * @private + * @param {Number[]} keysyms + * An array of keysyms, where the index of the keysym in the array is + * the location value. + * + * @param {Number} location + * The location on the keyboard corresponding to the key pressed, as + * defined at: http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + */ + var get_keysym = function get_keysym(keysyms, location) { + + if (!keysyms) + return null; + + return keysyms[location] || keysyms[0]; + }; + + /** + * Returns true if the given keysym corresponds to a printable character, + * false otherwise. + * + * @param {Number} keysym + * The keysym to check. + * + * @returns {Boolean} + * true if the given keysym corresponds to a printable character, + * false otherwise. + */ + var isPrintable = function isPrintable(keysym) { + + // Keysyms with Unicode equivalents are printable + return (keysym >= 0x00 && keysym <= 0xFF) + || (keysym & 0xFFFF0000) === 0x01000000; + + }; + + function keysym_from_key_identifier(identifier, location, shifted) { + + if (!identifier) + return null; + + var typedCharacter; + + // If identifier is U+xxxx, decode Unicode character + var unicodePrefixLocation = identifier.indexOf("U+"); + if (unicodePrefixLocation >= 0) { + var hex = identifier.substring(unicodePrefixLocation+2); + typedCharacter = String.fromCharCode(parseInt(hex, 16)); + } + + // If single character and not keypad, use that as typed character + else if (identifier.length === 1 && location !== 3) + typedCharacter = identifier; + + // Otherwise, look up corresponding keysym + else + return get_keysym(keyidentifier_keysym[identifier], location); + + // Alter case if necessary + if (shifted === true) + typedCharacter = typedCharacter.toUpperCase(); + else if (shifted === false) + typedCharacter = typedCharacter.toLowerCase(); + + // Get codepoint + var codepoint = typedCharacter.charCodeAt(0); + return keysym_from_charcode(codepoint); + + } + + function isControlCharacter(codepoint) { + return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F); + } + + function keysym_from_charcode(codepoint) { + + // Keysyms for control characters + if (isControlCharacter(codepoint)) return 0xFF00 | codepoint; + + // Keysyms for ASCII chars + if (codepoint >= 0x0000 && codepoint <= 0x00FF) + return codepoint; + + // Keysyms for Unicode + if (codepoint >= 0x0100 && codepoint <= 0x10FFFF) + return 0x01000000 | codepoint; + + return null; + + } + + function keysym_from_keycode(keyCode, location) { + return get_keysym(keycodeKeysyms[keyCode], location); + } + + /** + * Heuristically detects if the legacy keyIdentifier property of + * a keydown/keyup event looks incorrectly derived. Chrome, and + * presumably others, will produce the keyIdentifier by assuming + * the keyCode is the Unicode codepoint for that key. This is not + * correct in all cases. + * + * @private + * @param {Number} keyCode + * The keyCode from a browser keydown/keyup event. + * + * @param {String} keyIdentifier + * The legacy keyIdentifier from a browser keydown/keyup event. + * + * @returns {Boolean} + * true if the keyIdentifier looks sane, false if the keyIdentifier + * appears incorrectly derived or is missing entirely. + */ + var key_identifier_sane = function key_identifier_sane(keyCode, keyIdentifier) { + + // Missing identifier is not sane + if (!keyIdentifier) + return false; + + // Assume non-Unicode keyIdentifier values are sane + var unicodePrefixLocation = keyIdentifier.indexOf("U+"); + if (unicodePrefixLocation === -1) + return true; + + // If the Unicode codepoint isn't identical to the keyCode, + // then the identifier is likely correct + var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16); + if (keyCode !== codepoint) + return true; + + // The keyCodes for A-Z and 0-9 are actually identical to their + // Unicode codepoints + if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57)) + return true; + + // The keyIdentifier does NOT appear sane + return false; + + }; + + /** + * Marks a key as pressed, firing the keydown event if registered. Key + * repeat for the pressed key will start after a delay if that key is + * not a modifier. The return value of this function depends on the + * return value of the keydown event handler, if any. + * + * @param {Number} keysym The keysym of the key to press. + * @return {Boolean} true if event should NOT be canceled, false otherwise. + */ + this.press = function(keysym) { + + // Don't bother with pressing the key if the key is unknown + if (keysym === null) return; + + // Only press if released + if (!guac_keyboard.pressed[keysym]) { + + // Mark key as pressed + guac_keyboard.pressed[keysym] = true; + + // Send key event + if (guac_keyboard.onkeydown) { + var result = guac_keyboard.onkeydown(keysym); + last_keydown_result[keysym] = result; + + // Stop any current repeat + window.clearTimeout(key_repeat_timeout); + window.clearInterval(key_repeat_interval); + + // Repeat after a delay as long as pressed + if (!no_repeat[keysym]) + key_repeat_timeout = window.setTimeout(function() { + key_repeat_interval = window.setInterval(function() { + guac_keyboard.onkeyup(keysym); + guac_keyboard.onkeydown(keysym); + }, 50); + }, 500); + + return result; + } + } + + // Return the last keydown result by default, resort to false if unknown + return last_keydown_result[keysym] || false; + + }; + + /** + * Marks a key as released, firing the keyup event if registered. + * + * @param {Number} keysym The keysym of the key to release. + */ + this.release = function(keysym) { + + // Only release if pressed + if (guac_keyboard.pressed[keysym]) { + + // Mark key as released + delete guac_keyboard.pressed[keysym]; + + // Stop repeat + window.clearTimeout(key_repeat_timeout); + window.clearInterval(key_repeat_interval); + + // Send key event + if (keysym !== null && guac_keyboard.onkeyup) + guac_keyboard.onkeyup(keysym); + + } + + }; + + /** + * Resets the state of this keyboard, releasing all keys, and firing keyup + * events for each released key. + */ + this.reset = function() { + + // Release all pressed keys + for (var keysym in guac_keyboard.pressed) + guac_keyboard.release(parseInt(keysym)); + + // Clear event log + eventLog = []; + + }; + + /** + * Given a keyboard event, updates the local modifier state and remote + * key state based on the modifier flags within the event. This function + * pays no attention to keycodes. + * + * @private + * @param {KeyboardEvent} e + * The keyboard event containing the flags to update. + */ + var update_modifier_state = function update_modifier_state(e) { + + // Get state + var state = Guacamole.Keyboard.ModifierState.fromKeyboardEvent(e); + + // Release alt if implicitly released + if (guac_keyboard.modifiers.alt && state.alt === false) { + guac_keyboard.release(0xFFE9); // Left alt + guac_keyboard.release(0xFFEA); // Right alt + guac_keyboard.release(0xFE03); // AltGr + } + + // Release shift if implicitly released + if (guac_keyboard.modifiers.shift && state.shift === false) { + guac_keyboard.release(0xFFE1); // Left shift + guac_keyboard.release(0xFFE2); // Right shift + } + + // Release ctrl if implicitly released + if (guac_keyboard.modifiers.ctrl && state.ctrl === false) { + guac_keyboard.release(0xFFE3); // Left ctrl + guac_keyboard.release(0xFFE4); // Right ctrl + } + + // Release meta if implicitly released + if (guac_keyboard.modifiers.meta && state.meta === false) { + guac_keyboard.release(0xFFE7); // Left meta + guac_keyboard.release(0xFFE8); // Right meta + } + + // Release hyper if implicitly released + if (guac_keyboard.modifiers.hyper && state.hyper === false) { + guac_keyboard.release(0xFFEB); // Left hyper + guac_keyboard.release(0xFFEC); // Right hyper + } + + // Update state + guac_keyboard.modifiers = state; + + }; + + /** + * Reads through the event log, removing events from the head of the log + * when the corresponding true key presses are known (or as known as they + * can be). + * + * @private + * @return {Boolean} Whether the default action of the latest event should + * be prevented. + */ + function interpret_events() { + + // Do not prevent default if no event could be interpreted + var handled_event = interpret_event(); + if (!handled_event) + return false; + + // Interpret as much as possible + var last_event; + do { + last_event = handled_event; + handled_event = interpret_event(); + } while (handled_event !== null); + + return last_event.defaultPrevented; + + } + + /** + * Releases Ctrl+Alt, if both are currently pressed and the given keysym + * looks like a key that may require AltGr. + * + * @private + * @param {Number} keysym The key that was just pressed. + */ + var release_simulated_altgr = function release_simulated_altgr(keysym) { + + // Both Ctrl+Alt must be pressed if simulated AltGr is in use + if (!guac_keyboard.modifiers.ctrl || !guac_keyboard.modifiers.alt) + return; + + // Assume [A-Z] never require AltGr + if (keysym >= 0x0041 && keysym <= 0x005A) + return; + + // Assume [a-z] never require AltGr + if (keysym >= 0x0061 && keysym <= 0x007A) + return; + + // Release Ctrl+Alt if the keysym is printable + if (keysym <= 0xFF || (keysym & 0xFF000000) === 0x01000000) { + guac_keyboard.release(0xFFE3); // Left ctrl + guac_keyboard.release(0xFFE4); // Right ctrl + guac_keyboard.release(0xFFE9); // Left alt + guac_keyboard.release(0xFFEA); // Right alt + } + + }; + + /** + * Reads through the event log, interpreting the first event, if possible, + * and returning that event. If no events can be interpreted, due to a + * total lack of events or the need for more events, null is returned. Any + * interpreted events are automatically removed from the log. + * + * @private + * @return {KeyEvent} + * The first key event in the log, if it can be interpreted, or null + * otherwise. + */ + var interpret_event = function interpret_event() { + + // Peek at first event in log + var first = eventLog[0]; + if (!first) + return null; + + // Keydown event + if (first instanceof KeydownEvent) { + + var keysym = null; + var accepted_events = []; + + // If event itself is reliable, no need to wait for other events + if (first.reliable) { + keysym = first.keysym; + accepted_events = eventLog.splice(0, 1); + } + + // If keydown is immediately followed by a keypress, use the indicated character + else if (eventLog[1] instanceof KeypressEvent) { + keysym = eventLog[1].keysym; + accepted_events = eventLog.splice(0, 2); + } + + // If keydown is immediately followed by anything else, then no + // keypress can possibly occur to clarify this event, and we must + // handle it now + else if (eventLog[1]) { + keysym = first.keysym; + accepted_events = eventLog.splice(0, 1); + } + + // Fire a key press if valid events were found + if (accepted_events.length > 0) { + + if (keysym) { + + // Fire event + release_simulated_altgr(keysym); + var defaultPrevented = !guac_keyboard.press(keysym); + recentKeysym[first.keyCode] = keysym; + + // If a key is pressed while meta is held down, the keyup will + // never be sent in Chrome, so send it now. (bug #108404) + if (guac_keyboard.modifiers.meta && keysym !== 0xFFE7 && keysym !== 0xFFE8) + guac_keyboard.release(keysym); + + // Record whether default was prevented + for (var i=0; i<accepted_events.length; i++) + accepted_events[i].defaultPrevented = defaultPrevented; + + } + + return first; + + } + + } // end if keydown + + // Keyup event + else if (first instanceof KeyupEvent) { + + // Release specific key if known + var keysym = first.keysym; + if (keysym) { + guac_keyboard.release(keysym); + first.defaultPrevented = true; + } + + // Otherwise, fall back to releasing all keys + else { + guac_keyboard.reset(); + return first; + } + + return eventLog.shift(); + + } // end if keyup + + // Ignore any other type of event (keypress by itself is invalid) + else + return eventLog.shift(); + + // No event interpreted + return null; + + }; + + /** + * Returns the keyboard location of the key associated with the given + * keyboard event. The location differentiates key events which otherwise + * have the same keycode, such as left shift vs. right shift. + * + * @private + * @param {KeyboardEvent} e + * A JavaScript keyboard event, as received through the DOM via a + * "keydown", "keyup", or "keypress" handler. + * + * @returns {Number} + * The location of the key event on the keyboard, as defined at: + * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent + */ + var getEventLocation = function getEventLocation(e) { + + // Use standard location, if possible + if ('location' in e) + return e.location; + + // Failing that, attempt to use deprecated keyLocation + if ('keyLocation' in e) + return e.keyLocation; + + // If no location is available, assume left side + return 0; + + }; + + // When key pressed + element.addEventListener("keydown", function(e) { + + // Only intercept if handler set + if (!guac_keyboard.onkeydown) return; + + var keyCode; + if (window.event) keyCode = window.event.keyCode; + else if (e.which) keyCode = e.which; + + // Fix modifier states + update_modifier_state(e); + + // Ignore (but do not prevent) the "composition" keycode sent by some + // browsers when an IME is in use (see: http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html) + if (keyCode === 229) + return; + + // Log event + var keydownEvent = new KeydownEvent(keyCode, e.keyIdentifier, e.key, getEventLocation(e)); + eventLog.push(keydownEvent); + + // Interpret as many events as possible, prevent default if indicated + if (interpret_events()) + e.preventDefault(); + + }, true); + + // When key pressed + element.addEventListener("keypress", function(e) { + + // Only intercept if handler set + if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return; + + var charCode; + if (window.event) charCode = window.event.keyCode; + else if (e.which) charCode = e.which; + + // Fix modifier states + update_modifier_state(e); + + // Log event + var keypressEvent = new KeypressEvent(charCode); + eventLog.push(keypressEvent); + + // Interpret as many events as possible, prevent default if indicated + if (interpret_events()) + e.preventDefault(); + + }, true); + + // When key released + element.addEventListener("keyup", function(e) { + + // Only intercept if handler set + if (!guac_keyboard.onkeyup) return; + + e.preventDefault(); + + var keyCode; + if (window.event) keyCode = window.event.keyCode; + else if (e.which) keyCode = e.which; + + // Fix modifier states + update_modifier_state(e); + + // Log event, call for interpretation + var keyupEvent = new KeyupEvent(keyCode, e.keyIdentifier, e.key, getEventLocation(e)); + eventLog.push(keyupEvent); + interpret_events(); + + }, true); + +}; + +/** + * The state of all supported keyboard modifiers. + * @constructor + */ +Guacamole.Keyboard.ModifierState = function() { + + /** + * Whether shift is currently pressed. + * @type {Boolean} + */ + this.shift = false; + + /** + * Whether ctrl is currently pressed. + * @type {Boolean} + */ + this.ctrl = false; + + /** + * Whether alt is currently pressed. + * @type {Boolean} + */ + this.alt = false; + + /** + * Whether meta (apple key) is currently pressed. + * @type {Boolean} + */ + this.meta = false; + + /** + * Whether hyper (windows key) is currently pressed. + * @type {Boolean} + */ + this.hyper = false; + +}; + +/** + * Returns the modifier state applicable to the keyboard event given. + * + * @param {KeyboardEvent} e The keyboard event to read. + * @returns {Guacamole.Keyboard.ModifierState} The current state of keyboard + * modifiers. + */ +Guacamole.Keyboard.ModifierState.fromKeyboardEvent = function(e) { + + var state = new Guacamole.Keyboard.ModifierState(); + + // Assign states from old flags + state.shift = e.shiftKey; + state.ctrl = e.ctrlKey; + state.alt = e.altKey; + state.meta = e.metaKey; + + // Use DOM3 getModifierState() for others + if (e.getModifierState) { + state.hyper = e.getModifierState("OS") + || e.getModifierState("Super") + || e.getModifierState("Hyper") + || e.getModifierState("Win"); + } + + return state; + +}; +</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.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:onname">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">ons tatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkey up</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.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="Guacamole.W ebSocketTunnel.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 Mon Oct 31 2016 20:22:21 GMT-0700 (PDT) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> +</body> +</html>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/06386843/content/doc/0.9.10-incubating/guacamole-common-js/Layer.js.html ---------------------------------------------------------------------- diff --git a/content/doc/0.9.10-incubating/guacamole-common-js/Layer.js.html b/content/doc/0.9.10-incubating/guacamole-common-js/Layer.js.html new file mode 100644 index 0000000..ecc5530 --- /dev/null +++ b/content/doc/0.9.10-incubating/guacamole-common-js/Layer.js.html @@ -0,0 +1,952 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: Layer.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: Layer.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 ordered drawing surface. Each Layer contains a canvas element and + * provides simple drawing instructions for drawing to that canvas element, + * however unlike the canvas element itself, drawing operations on a Layer are + * guaranteed to run in order, even if such an operation must wait for an image + * to load before completing. + * + * @constructor + * + * @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.Layer = function(width, height) { + + /** + * Reference to this Layer. + * @private + */ + var layer = this; + + /** + * The canvas element backing this Layer. + * @private + */ + var canvas = document.createElement("canvas"); + + /** + * The 2D display context of the canvas element backing this Layer. + * @private + */ + var context = canvas.getContext("2d"); + context.save(); + + /** + * Whether a new path should be started with the next path drawing + * operations. + * @private + */ + var pathClosed = true; + + /** + * The number of states on the state stack. + * + * Note that there will ALWAYS be one element on the stack, but that + * element is not exposed. It is only used to reset the layer to its + * initial state. + * + * @private + */ + var stackSize = 0; + + /** + * Map of all Guacamole channel masks to HTML5 canvas composite operation + * names. Not all channel mask combinations are currently implemented. + * @private + */ + var compositeOperation = { + /* 0x0 NOT IMPLEMENTED */ + 0x1: "destination-in", + 0x2: "destination-out", + /* 0x3 NOT IMPLEMENTED */ + 0x4: "source-in", + /* 0x5 NOT IMPLEMENTED */ + 0x6: "source-atop", + /* 0x7 NOT IMPLEMENTED */ + 0x8: "source-out", + 0x9: "destination-atop", + 0xA: "xor", + 0xB: "destination-over", + 0xC: "copy", + /* 0xD NOT IMPLEMENTED */ + 0xE: "source-over", + 0xF: "lighter" + }; + + /** + * Resizes the canvas element backing this Layer without testing the + * new size. This function should only be used internally. + * + * @private + * @param {Number} newWidth The new width to assign to this Layer. + * @param {Number} newHeight The new height to assign to this Layer. + */ + function resize(newWidth, newHeight) { + + // Only preserve old data if width/height are both non-zero + var oldData = null; + if (layer.width !== 0 && layer.height !== 0) { + + // Create canvas and context for holding old data + oldData = document.createElement("canvas"); + oldData.width = layer.width; + oldData.height = layer.height; + + var oldDataContext = oldData.getContext("2d"); + + // Copy image data from current + oldDataContext.drawImage(canvas, + 0, 0, layer.width, layer.height, + 0, 0, layer.width, layer.height); + + } + + // Preserve composite operation + var oldCompositeOperation = context.globalCompositeOperation; + + // Resize canvas + canvas.width = newWidth; + canvas.height = newHeight; + + // Redraw old data, if any + if (oldData) + context.drawImage(oldData, + 0, 0, layer.width, layer.height, + 0, 0, layer.width, layer.height); + + // Restore composite operation + context.globalCompositeOperation = oldCompositeOperation; + + layer.width = newWidth; + layer.height = newHeight; + + // Acknowledge reset of stack (happens on resize of canvas) + stackSize = 0; + context.save(); + + } + + /** + * Given the X and Y coordinates of the upper-left corner of a rectangle + * and the rectangle's width and height, resize the backing canvas element + * as necessary to ensure that the rectangle fits within the canvas + * element's coordinate space. This function will only make the canvas + * larger. If the rectangle already fits within the canvas element's + * coordinate space, the canvas is left unchanged. + * + * @private + * @param {Number} x The X coordinate of the upper-left corner of the + * rectangle to fit. + * @param {Number} y The Y coordinate of the upper-left corner of the + * rectangle to fit. + * @param {Number} w The width of the the rectangle to fit. + * @param {Number} h The height of the the rectangle to fit. + */ + function fitRect(x, y, w, h) { + + // Calculate bounds + var opBoundX = w + x; + var opBoundY = h + y; + + // Determine max width + var resizeWidth; + if (opBoundX > layer.width) + resizeWidth = opBoundX; + else + resizeWidth = layer.width; + + // Determine max height + var resizeHeight; + if (opBoundY > layer.height) + resizeHeight = opBoundY; + else + resizeHeight = layer.height; + + // Resize if necessary + layer.resize(resizeWidth, resizeHeight); + + } + + /** + * Set to true if this Layer should resize itself to accomodate the + * dimensions of any drawing operation, and false (the default) otherwise. + * + * Note that setting this property takes effect immediately, and thus may + * take effect on operations that were started in the past but have not + * yet completed. If you wish the setting of this flag to only modify + * future operations, you will need to make the setting of this flag an + * operation with sync(). + * + * @example + * // Set autosize to true for all future operations + * layer.sync(function() { + * layer.autosize = true; + * }); + * + * @type {Boolean} + * @default false + */ + this.autosize = false; + + /** + * The current width of this layer. + * @type {Number} + */ + this.width = width; + + /** + * The current height of this layer. + * @type {Number} + */ + this.height = height; + + /** + * Returns the canvas element backing this Layer. + * @returns {Element} The canvas element backing this Layer. + */ + this.getCanvas = function() { + return canvas; + }; + + /** + * Changes the size of this Layer to the given width and height. Resizing + * is only attempted if the new size provided is actually different from + * the current size. + * + * @param {Number} newWidth The new width to assign to this Layer. + * @param {Number} newHeight The new height to assign to this Layer. + */ + this.resize = function(newWidth, newHeight) { + if (newWidth !== layer.width || newHeight !== layer.height) + resize(newWidth, newHeight); + }; + + /** + * Draws the specified image at the given coordinates. The image specified + * must already be loaded. + * + * @param {Number} x The destination X coordinate. + * @param {Number} y The destination Y coordinate. + * @param {Image} image The image to draw. Note that this is an Image + * object - not a URL. + */ + this.drawImage = function(x, y, image) { + if (layer.autosize) fitRect(x, y, image.width, image.height); + context.drawImage(image, x, y); + }; + + /** + * 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 {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, x, y, transferFunction) { + + var srcCanvas = srcLayer.getCanvas(); + + // If entire rectangle outside source canvas, stop + if (srcx >= srcCanvas.width || srcy >= srcCanvas.height) return; + + // Otherwise, clip rectangle to area + if (srcx + srcw > srcCanvas.width) + srcw = srcCanvas.width - srcx; + + if (srcy + srch > srcCanvas.height) + srch = srcCanvas.height - srcy; + + // Stop if nothing to draw. + if (srcw === 0 || srch === 0) return; + + if (layer.autosize) fitRect(x, y, srcw, srch); + + // Get image data from src and dst + var src = srcLayer.getCanvas().getContext("2d").getImageData(srcx, srcy, srcw, srch); + var dst = context.getImageData(x , y, srcw, srch); + + // Apply transfer for each pixel + for (var i=0; i<srcw*srch*4; i+=4) { + + // Get source pixel environment + var src_pixel = new Guacamole.Layer.Pixel( + src.data[i], + src.data[i+1], + src.data[i+2], + src.data[i+3] + ); + + // Get destination pixel environment + var dst_pixel = new Guacamole.Layer.Pixel( + dst.data[i], + dst.data[i+1], + dst.data[i+2], + dst.data[i+3] + ); + + // Apply transfer function + transferFunction(src_pixel, dst_pixel); + + // Save pixel data + dst.data[i ] = dst_pixel.red; + dst.data[i+1] = dst_pixel.green; + dst.data[i+2] = dst_pixel.blue; + dst.data[i+3] = dst_pixel.alpha; + + } + + // Draw image data + context.putImageData(dst, x, y); + + }; + + /** + * 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 {Number} x The destination X coordinate. + * @param {Number} y The destination Y coordinate. + */ + this.put = function(srcLayer, srcx, srcy, srcw, srch, x, y) { + + var srcCanvas = srcLayer.getCanvas(); + + // If entire rectangle outside source canvas, stop + if (srcx >= srcCanvas.width || srcy >= srcCanvas.height) return; + + // Otherwise, clip rectangle to area + if (srcx + srcw > srcCanvas.width) + srcw = srcCanvas.width - srcx; + + if (srcy + srch > srcCanvas.height) + srch = srcCanvas.height - srcy; + + // Stop if nothing to draw. + if (srcw === 0 || srch === 0) return; + + if (layer.autosize) fitRect(x, y, srcw, srch); + + // Get image data from src and dst + var src = srcLayer.getCanvas().getContext("2d").getImageData(srcx, srcy, srcw, srch); + context.putImageData(src, 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 {Number} x The destination X coordinate. + * @param {Number} y The destination Y coordinate. + */ + this.copy = function(srcLayer, srcx, srcy, srcw, srch, x, y) { + + var srcCanvas = srcLayer.getCanvas(); + + // If entire rectangle outside source canvas, stop + if (srcx >= srcCanvas.width || srcy >= srcCanvas.height) return; + + // Otherwise, clip rectangle to area + if (srcx + srcw > srcCanvas.width) + srcw = srcCanvas.width - srcx; + + if (srcy + srch > srcCanvas.height) + srch = srcCanvas.height - srcy; + + // Stop if nothing to draw. + if (srcw === 0 || srch === 0) return; + + if (layer.autosize) fitRect(x, y, srcw, srch); + context.drawImage(srcCanvas, srcx, srcy, srcw, srch, x, y, srcw, srch); + + }; + + /** + * Starts a new path at the specified point. + * + * @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(x, y) { + + // Start a new path if current path is closed + if (pathClosed) { + context.beginPath(); + pathClosed = false; + } + + if (layer.autosize) fitRect(x, y, 0, 0); + context.moveTo(x, y); + + }; + + /** + * Add the specified line to the current path. + * + * @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(x, y) { + + // Start a new path if current path is closed + if (pathClosed) { + context.beginPath(); + pathClosed = false; + } + + if (layer.autosize) fitRect(x, y, 0, 0); + context.lineTo(x, y); + + }; + + /** + * Add the specified arc to the current path. + * + * @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(x, y, radius, startAngle, endAngle, negative) { + + // Start a new path if current path is closed + if (pathClosed) { + context.beginPath(); + pathClosed = false; + } + + if (layer.autosize) fitRect(x, y, 0, 0); + context.arc(x, y, radius, startAngle, endAngle, negative); + + }; + + /** + * Starts a new path at the specified point. + * + * @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(cp1x, cp1y, cp2x, cp2y, x, y) { + + // Start a new path if current path is closed + if (pathClosed) { + context.beginPath(); + pathClosed = false; + } + + if (layer.autosize) fitRect(x, y, 0, 0); + context.bezierCurveTo(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. + */ + this.close = function() { + context.closePath(); + pathClosed = true; + }; + + /** + * Add the specified rectangle to the current path. + * + * @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(x, y, w, h) { + + // Start a new path if current path is closed + if (pathClosed) { + context.beginPath(); + pathClosed = false; + } + + if (layer.autosize) fitRect(x, y, w, h); + context.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. + */ + this.clip = function() { + + // Set new clipping region + context.clip(); + + // Path now implicitly closed + pathClosed = true; + + }; + + /** + * 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 {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(cap, join, thickness, r, g, b, a) { + + // Stroke with color + context.lineCap = cap; + context.lineJoin = join; + context.lineWidth = thickness; + context.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; + context.stroke(); + + // Path now implicitly closed + pathClosed = true; + + }; + + /** + * 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 {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(r, g, b, a) { + + // Fill with color + context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a/255.0 + ")"; + context.fill(); + + // Path now implicitly closed + pathClosed = true; + + }; + + /** + * 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 {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(cap, join, thickness, srcLayer) { + + // Stroke with image data + context.lineCap = cap; + context.lineJoin = join; + context.lineWidth = thickness; + context.strokeStyle = context.createPattern( + srcLayer.getCanvas(), + "repeat" + ); + context.stroke(); + + // Path now implicitly closed + pathClosed = true; + + }; + + /** + * 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} srcLayer The layer to use as a repeating pattern + * within the fill. + */ + this.fillLayer = function(srcLayer) { + + // Fill with image data + context.fillStyle = context.createPattern( + srcLayer.getCanvas(), + "repeat" + ); + context.fill(); + + // Path now implicitly closed + pathClosed = true; + + }; + + /** + * Push current layer state onto stack. + */ + this.push = function() { + + // Save current state onto stack + context.save(); + stackSize++; + + }; + + /** + * Pop layer state off stack. + */ + this.pop = function() { + + // Restore current state from stack + if (stackSize > 0) { + context.restore(); + stackSize--; + } + + }; + + /** + * Reset the layer, clearing the stack, the current path, and any transform + * matrix. + */ + this.reset = function() { + + // Clear stack + while (stackSize > 0) { + context.restore(); + stackSize--; + } + + // Restore to initial state + context.restore(); + context.save(); + + // Clear path + context.beginPath(); + pathClosed = false; + + }; + + /** + * Sets 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.setTransform = function(a, b, c, d, e, f) { + context.setTransform( + a, b, c, + d, e, f + /*0, 0, 1*/ + ); + }; + + /** + * 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.transform = function(a, b, c, d, e, f) { + context.transform( + a, b, c, + d, e, f + /*0, 0, 1*/ + ); + }; + + /** + * 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 {Number} mask The channel mask for future operations on this + * Layer. + */ + this.setChannelMask = function(mask) { + context.globalCompositeOperation = compositeOperation[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 {Number} limit The miter limit for stroke operations using the + * miter join. + */ + this.setMiterLimit = function(limit) { + context.miterLimit = limit; + }; + + // Initialize canvas dimensions + canvas.width = width; + canvas.height = height; + + // Explicitly render canvas below other elements in the layer (such as + // child layers). Chrome and others may fail to render layers properly + // without this. + canvas.style.zIndex = -1; + +}; + +/** + * Channel mask for the composite operation "rout". + */ +Guacamole.Layer.ROUT = 0x2; + +/** + * Channel mask for the composite operation "atop". + */ +Guacamole.Layer.ATOP = 0x6; + +/** + * Channel mask for the composite operation "xor". + */ +Guacamole.Layer.XOR = 0xA; + +/** + * Channel mask for the composite operation "rover". + */ +Guacamole.Layer.ROVER = 0xB; + +/** + * Channel mask for the composite operation "over". + */ +Guacamole.Layer.OVER = 0xE; + +/** + * Channel mask for the composite operation "plus". + */ +Guacamole.Layer.PLUS = 0xF; + +/** + * Channel mask for the composite operation "rin". + * Beware that WebKit-based browsers may leave the contents of the destionation + * layer where the source layer is transparent, despite the definition of this + * operation. + */ +Guacamole.Layer.RIN = 0x1; + +/** + * Channel mask for the composite operation "in". + * Beware that WebKit-based browsers may leave the contents of the destionation + * layer where the source layer is transparent, despite the definition of this + * operation. + */ +Guacamole.Layer.IN = 0x4; + +/** + * Channel mask for the composite operation "out". + * Beware that WebKit-based browsers may leave the contents of the destionation + * layer where the source layer is transparent, despite the definition of this + * operation. + */ +Guacamole.Layer.OUT = 0x8; + +/** + * Channel mask for the composite operation "ratop". + * Beware that WebKit-based browsers may leave the contents of the destionation + * layer where the source layer is transparent, despite the definition of this + * operation. + */ +Guacamole.Layer.RATOP = 0x9; + +/** + * Channel mask for the composite operation "src". + * Beware that WebKit-based browsers may leave the contents of the destionation + * layer where the source layer is transparent, despite the definition of this + * operation. + */ +Guacamole.Layer.SRC = 0xC; + +/** + * Represents a single pixel of image data. All components have a minimum value + * of 0 and a maximum value of 255. + * + * @constructor + * + * @param {Number} r The red component of this pixel. + * @param {Number} g The green component of this pixel. + * @param {Number} b The blue component of this pixel. + * @param {Number} a The alpha component of this pixel. + */ +Guacamole.Layer.Pixel = function(r, g, b, a) { + + /** + * The red component of this pixel, where 0 is the minimum value, + * and 255 is the maximum. + */ + this.red = r; + + /** + * The green component of this pixel, where 0 is the minimum value, + * and 255 is the maximum. + */ + this.green = g; + + /** + * The blue component of this pixel, where 0 is the minimum value, + * and 255 is the maximum. + */ + this.blue = b; + + /** + * The alpha component of this pixel, where 0 is the minimum value, + * and 255 is the maximum. + */ + this.alpha = a; + +}; +</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.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:onname">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">ons tatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkey up</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.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="Guacamole.W ebSocketTunnel.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 Mon Oct 31 2016 20:22:21 GMT-0700 (PDT) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> +</body> +</html>
