You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/10/01 01:10:52 UTC
[3/7] guacamole-website git commit: Pull guacamole-common-js modules
from git for tests. Allow git commit to be overridden.
http://git-wip-us.apache.org/repos/asf/guacamole-website/blob/2de22be5/pub/tests/guac/modules/Keyboard.js
----------------------------------------------------------------------
diff --git a/pub/tests/guac/modules/Keyboard.js b/pub/tests/guac/modules/Keyboard.js
deleted file mode 100644
index ce9f016..0000000
--- a/pub/tests/guac/modules/Keyboard.js
+++ /dev/null
@@ -1,1162 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-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 (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];
- };
-
- 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;
-
-};
http://git-wip-us.apache.org/repos/asf/guacamole-website/blob/2de22be5/pub/tests/guac/modules/Layer.js
----------------------------------------------------------------------
diff --git a/pub/tests/guac/modules/Layer.js b/pub/tests/guac/modules/Layer.js
deleted file mode 100644
index d9cf7f2..0000000
--- a/pub/tests/guac/modules/Layer.js
+++ /dev/null
@@ -1,904 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-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;
-
-};