JavaScript 65.5%
Python 17.8%
CSS 13%
HTML 3.7%
1"use strict";23Object.defineProperty(exports, "__esModule", {4 value: true5});6exports["default"] = void 0;7function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }8function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }9function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }10function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }11function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }12function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }13/*14 * noVNC: HTML5 VNC client15 * Copyright (C) 2020 The noVNC authors16 * Licensed under MPL 2.0 (see LICENSE.txt)17 *18 * See README.md for usage and integration instructions.19 *20 */2122var GH_NOGESTURE = 0;23var GH_ONETAP = 1;24var GH_TWOTAP = 2;25var GH_THREETAP = 4;26var GH_DRAG = 8;27var GH_LONGPRESS = 16;28var GH_TWODRAG = 32;29var GH_PINCH = 64;30var GH_INITSTATE = 127;31var GH_MOVE_THRESHOLD = 50;32var GH_ANGLE_THRESHOLD = 90; // Degrees3334// Timeout when waiting for gestures (ms)35var GH_MULTITOUCH_TIMEOUT = 250;3637// Maximum time between press and release for a tap (ms)38var GH_TAP_TIMEOUT = 1000;3940// Timeout when waiting for longpress (ms)41var GH_LONGPRESS_TIMEOUT = 1000;4243// Timeout when waiting to decide between PINCH and TWODRAG (ms)44var GH_TWOTOUCH_TIMEOUT = 50;45var GestureHandler = exports["default"] = /*#__PURE__*/function () {46 function GestureHandler() {47 _classCallCheck(this, GestureHandler);48 this._target = null;49 this._state = GH_INITSTATE;50 this._tracked = [];51 this._ignored = [];52 this._waitingRelease = false;53 this._releaseStart = 0.0;54 this._longpressTimeoutId = null;55 this._twoTouchTimeoutId = null;56 this._boundEventHandler = this._eventHandler.bind(this);57 }58 return _createClass(GestureHandler, [{59 key: "attach",60 value: function attach(target) {61 this.detach();62 this._target = target;63 this._target.addEventListener('touchstart', this._boundEventHandler);64 this._target.addEventListener('touchmove', this._boundEventHandler);65 this._target.addEventListener('touchend', this._boundEventHandler);66 this._target.addEventListener('touchcancel', this._boundEventHandler);67 }68 }, {69 key: "detach",70 value: function detach() {71 if (!this._target) {72 return;73 }74 this._stopLongpressTimeout();75 this._stopTwoTouchTimeout();76 this._target.removeEventListener('touchstart', this._boundEventHandler);77 this._target.removeEventListener('touchmove', this._boundEventHandler);78 this._target.removeEventListener('touchend', this._boundEventHandler);79 this._target.removeEventListener('touchcancel', this._boundEventHandler);80 this._target = null;81 }82 }, {83 key: "_eventHandler",84 value: function _eventHandler(e) {85 var fn;86 e.stopPropagation();87 e.preventDefault();88 switch (e.type) {89 case 'touchstart':90 fn = this._touchStart;91 break;92 case 'touchmove':93 fn = this._touchMove;94 break;95 case 'touchend':96 case 'touchcancel':97 fn = this._touchEnd;98 break;99 }100 for (var i = 0; i < e.changedTouches.length; i++) {101 var touch = e.changedTouches[i];102 fn.call(this, touch.identifier, touch.clientX, touch.clientY);103 }104 }105 }, {106 key: "_touchStart",107 value: function _touchStart(id, x, y) {108 // Ignore any new touches if there is already an active gesture,109 // or we're in a cleanup state110 if (this._hasDetectedGesture() || this._state === GH_NOGESTURE) {111 this._ignored.push(id);112 return;113 }114115 // Did it take too long between touches that we should no longer116 // consider this a single gesture?117 if (this._tracked.length > 0 && Date.now() - this._tracked[0].started > GH_MULTITOUCH_TIMEOUT) {118 this._state = GH_NOGESTURE;119 this._ignored.push(id);120 return;121 }122123 // If we're waiting for fingers to release then we should no longer124 // recognize new touches125 if (this._waitingRelease) {126 this._state = GH_NOGESTURE;127 this._ignored.push(id);128 return;129 }130 this._tracked.push({131 id: id,132 started: Date.now(),133 active: true,134 firstX: x,135 firstY: y,136 lastX: x,137 lastY: y,138 angle: 0139 });140 switch (this._tracked.length) {141 case 1:142 this._startLongpressTimeout();143 break;144 case 2:145 this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);146 this._stopLongpressTimeout();147 break;148 case 3:149 this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);150 break;151 default:152 this._state = GH_NOGESTURE;153 }154 }155 }, {156 key: "_touchMove",157 value: function _touchMove(id, x, y) {158 var touch = this._tracked.find(function (t) {159 return t.id === id;160 });161162 // If this is an update for a touch we're not tracking, ignore it163 if (touch === undefined) {164 return;165 }166167 // Update the touches last position with the event coordinates168 touch.lastX = x;169 touch.lastY = y;170 var deltaX = x - touch.firstX;171 var deltaY = y - touch.firstY;172173 // Update angle when the touch has moved174 if (touch.firstX !== touch.lastX || touch.firstY !== touch.lastY) {175 touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;176 }177 if (!this._hasDetectedGesture()) {178 // Ignore moves smaller than the minimum threshold179 if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {180 return;181 }182183 // Can't be a tap or long press as we've seen movement184 this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);185 this._stopLongpressTimeout();186 if (this._tracked.length !== 1) {187 this._state &= ~GH_DRAG;188 }189 if (this._tracked.length !== 2) {190 this._state &= ~(GH_TWODRAG | GH_PINCH);191 }192193 // We need to figure out which of our different two touch gestures194 // this might be195 if (this._tracked.length === 2) {196 // The other touch is the one where the id doesn't match197 var prevTouch = this._tracked.find(function (t) {198 return t.id !== id;199 });200201 // How far the previous touch point has moved since start202 var prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX, prevTouch.firstY - prevTouch.lastY);203204 // We know that the current touch moved far enough,205 // but unless both touches moved further than their206 // threshold we don't want to disqualify any gestures207 if (prevDeltaMove > GH_MOVE_THRESHOLD) {208 // The angle difference between the direction of the touch points209 var deltaAngle = Math.abs(touch.angle - prevTouch.angle);210 deltaAngle = Math.abs((deltaAngle + 180) % 360 - 180);211212 // PINCH or TWODRAG can be eliminated depending on the angle213 if (deltaAngle > GH_ANGLE_THRESHOLD) {214 this._state &= ~GH_TWODRAG;215 } else {216 this._state &= ~GH_PINCH;217 }218 if (this._isTwoTouchTimeoutRunning()) {219 this._stopTwoTouchTimeout();220 }221 } else if (!this._isTwoTouchTimeoutRunning()) {222 // We can't determine the gesture right now, let's223 // wait and see if more events are on their way224 this._startTwoTouchTimeout();225 }226 }227 if (!this._hasDetectedGesture()) {228 return;229 }230 this._pushEvent('gesturestart');231 }232 this._pushEvent('gesturemove');233 }234 }, {235 key: "_touchEnd",236 value: function _touchEnd(id, x, y) {237 // Check if this is an ignored touch238 if (this._ignored.indexOf(id) !== -1) {239 // Remove this touch from ignored240 this._ignored.splice(this._ignored.indexOf(id), 1);241242 // And reset the state if there are no more touches243 if (this._ignored.length === 0 && this._tracked.length === 0) {244 this._state = GH_INITSTATE;245 this._waitingRelease = false;246 }247 return;248 }249250 // We got a touchend before the timer triggered,251 // this cannot result in a gesture anymore.252 if (!this._hasDetectedGesture() && this._isTwoTouchTimeoutRunning()) {253 this._stopTwoTouchTimeout();254 this._state = GH_NOGESTURE;255 }256257 // Some gestures don't trigger until a touch is released258 if (!this._hasDetectedGesture()) {259 // Can't be a gesture that relies on movement260 this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);261 // Or something that relies on more time262 this._state &= ~GH_LONGPRESS;263 this._stopLongpressTimeout();264 if (!this._waitingRelease) {265 this._releaseStart = Date.now();266 this._waitingRelease = true;267268 // Can't be a tap that requires more touches than we current have269 switch (this._tracked.length) {270 case 1:271 this._state &= ~(GH_TWOTAP | GH_THREETAP);272 break;273 case 2:274 this._state &= ~(GH_ONETAP | GH_THREETAP);275 break;276 }277 }278 }279280 // Waiting for all touches to release? (i.e. some tap)281 if (this._waitingRelease) {282 // Were all touches released at roughly the same time?283 if (Date.now() - this._releaseStart > GH_MULTITOUCH_TIMEOUT) {284 this._state = GH_NOGESTURE;285 }286287 // Did too long time pass between press and release?288 if (this._tracked.some(function (t) {289 return Date.now() - t.started > GH_TAP_TIMEOUT;290 })) {291 this._state = GH_NOGESTURE;292 }293 var touch = this._tracked.find(function (t) {294 return t.id === id;295 });296 touch.active = false;297298 // Are we still waiting for more releases?299 if (this._hasDetectedGesture()) {300 this._pushEvent('gesturestart');301 } else {302 // Have we reached a dead end?303 if (this._state !== GH_NOGESTURE) {304 return;305 }306 }307 }308 if (this._hasDetectedGesture()) {309 this._pushEvent('gestureend');310 }311312 // Ignore any remaining touches until they are ended313 for (var i = 0; i < this._tracked.length; i++) {314 if (this._tracked[i].active) {315 this._ignored.push(this._tracked[i].id);316 }317 }318 this._tracked = [];319 this._state = GH_NOGESTURE;320321 // Remove this touch from ignored if it's in there322 if (this._ignored.indexOf(id) !== -1) {323 this._ignored.splice(this._ignored.indexOf(id), 1);324 }325326 // We reset the state if ignored is empty327 if (this._ignored.length === 0) {328 this._state = GH_INITSTATE;329 this._waitingRelease = false;330 }331 }332 }, {333 key: "_hasDetectedGesture",334 value: function _hasDetectedGesture() {335 if (this._state === GH_NOGESTURE) {336 return false;337 }338 // Check to see if the bitmask value is a power of 2339 // (i.e. only one bit set). If it is, we have a state.340 if (this._state & this._state - 1) {341 return false;342 }343344 // For taps we also need to have all touches released345 // before we've fully detected the gesture346 if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {347 if (this._tracked.some(function (t) {348 return t.active;349 })) {350 return false;351 }352 }353 return true;354 }355 }, {356 key: "_startLongpressTimeout",357 value: function _startLongpressTimeout() {358 var _this = this;359 this._stopLongpressTimeout();360 this._longpressTimeoutId = setTimeout(function () {361 return _this._longpressTimeout();362 }, GH_LONGPRESS_TIMEOUT);363 }364 }, {365 key: "_stopLongpressTimeout",366 value: function _stopLongpressTimeout() {367 clearTimeout(this._longpressTimeoutId);368 this._longpressTimeoutId = null;369 }370 }, {371 key: "_longpressTimeout",372 value: function _longpressTimeout() {373 if (this._hasDetectedGesture()) {374 throw new Error("A longpress gesture failed, conflict with a different gesture");375 }376 this._state = GH_LONGPRESS;377 this._pushEvent('gesturestart');378 }379 }, {380 key: "_startTwoTouchTimeout",381 value: function _startTwoTouchTimeout() {382 var _this2 = this;383 this._stopTwoTouchTimeout();384 this._twoTouchTimeoutId = setTimeout(function () {385 return _this2._twoTouchTimeout();386 }, GH_TWOTOUCH_TIMEOUT);387 }388 }, {389 key: "_stopTwoTouchTimeout",390 value: function _stopTwoTouchTimeout() {391 clearTimeout(this._twoTouchTimeoutId);392 this._twoTouchTimeoutId = null;393 }394 }, {395 key: "_isTwoTouchTimeoutRunning",396 value: function _isTwoTouchTimeoutRunning() {397 return this._twoTouchTimeoutId !== null;398 }399 }, {400 key: "_twoTouchTimeout",401 value: function _twoTouchTimeout() {402 if (this._tracked.length === 0) {403 throw new Error("A pinch or two drag gesture failed, no tracked touches");404 }405406 // How far each touch point has moved since start407 var avgM = this._getAverageMovement();408 var avgMoveH = Math.abs(avgM.x);409 var avgMoveV = Math.abs(avgM.y);410411 // The difference in the distance between where412 // the touch points started and where they are now413 var avgD = this._getAverageDistance();414 var deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) - Math.hypot(avgD.last.x, avgD.last.y));415 if (avgMoveV < deltaTouchDistance && avgMoveH < deltaTouchDistance) {416 this._state = GH_PINCH;417 } else {418 this._state = GH_TWODRAG;419 }420 this._pushEvent('gesturestart');421 this._pushEvent('gesturemove');422 }423 }, {424 key: "_pushEvent",425 value: function _pushEvent(type) {426 var detail = {427 type: this._stateToGesture(this._state)428 };429430 // For most gesture events the current (average) position is the431 // most useful432 var avg = this._getPosition();433 var pos = avg.last;434435 // However we have a slight distance to detect gestures, so for the436 // first gesture event we want to use the first positions we saw437 if (type === 'gesturestart') {438 pos = avg.first;439 }440441 // For these gestures, we always want the event coordinates442 // to be where the gesture began, not the current touch location.443 switch (this._state) {444 case GH_TWODRAG:445 case GH_PINCH:446 pos = avg.first;447 break;448 }449 detail['clientX'] = pos.x;450 detail['clientY'] = pos.y;451452 // FIXME: other coordinates?453454 // Some gestures also have a magnitude455 if (this._state === GH_PINCH) {456 var distance = this._getAverageDistance();457 if (type === 'gesturestart') {458 detail['magnitudeX'] = distance.first.x;459 detail['magnitudeY'] = distance.first.y;460 } else {461 detail['magnitudeX'] = distance.last.x;462 detail['magnitudeY'] = distance.last.y;463 }464 } else if (this._state === GH_TWODRAG) {465 if (type === 'gesturestart') {466 detail['magnitudeX'] = 0.0;467 detail['magnitudeY'] = 0.0;468 } else {469 var movement = this._getAverageMovement();470 detail['magnitudeX'] = movement.x;471 detail['magnitudeY'] = movement.y;472 }473 }474 var gev = new CustomEvent(type, {475 detail: detail476 });477 this._target.dispatchEvent(gev);478 }479 }, {480 key: "_stateToGesture",481 value: function _stateToGesture(state) {482 switch (state) {483 case GH_ONETAP:484 return 'onetap';485 case GH_TWOTAP:486 return 'twotap';487 case GH_THREETAP:488 return 'threetap';489 case GH_DRAG:490 return 'drag';491 case GH_LONGPRESS:492 return 'longpress';493 case GH_TWODRAG:494 return 'twodrag';495 case GH_PINCH:496 return 'pinch';497 }498 throw new Error("Unknown gesture state: " + state);499 }500 }, {501 key: "_getPosition",502 value: function _getPosition() {503 if (this._tracked.length === 0) {504 throw new Error("Failed to get gesture position, no tracked touches");505 }506 var size = this._tracked.length;507 var fx = 0,508 fy = 0,509 lx = 0,510 ly = 0;511 for (var i = 0; i < this._tracked.length; i++) {512 fx += this._tracked[i].firstX;513 fy += this._tracked[i].firstY;514 lx += this._tracked[i].lastX;515 ly += this._tracked[i].lastY;516 }517 return {518 first: {519 x: fx / size,520 y: fy / size521 },522 last: {523 x: lx / size,524 y: ly / size525 }526 };527 }528 }, {529 key: "_getAverageMovement",530 value: function _getAverageMovement() {531 if (this._tracked.length === 0) {532 throw new Error("Failed to get gesture movement, no tracked touches");533 }534 var totalH, totalV;535 totalH = totalV = 0;536 var size = this._tracked.length;537 for (var i = 0; i < this._tracked.length; i++) {538 totalH += this._tracked[i].lastX - this._tracked[i].firstX;539 totalV += this._tracked[i].lastY - this._tracked[i].firstY;540 }541 return {542 x: totalH / size,543 y: totalV / size544 };545 }546 }, {547 key: "_getAverageDistance",548 value: function _getAverageDistance() {549 if (this._tracked.length === 0) {550 throw new Error("Failed to get gesture distance, no tracked touches");551 }552553 // Distance between the first and last tracked touches554555 var first = this._tracked[0];556 var last = this._tracked[this._tracked.length - 1];557 var fdx = Math.abs(last.firstX - first.firstX);558 var fdy = Math.abs(last.firstY - first.firstY);559 var ldx = Math.abs(last.lastX - first.lastX);560 var ldy = Math.abs(last.lastY - first.lastY);561 return {562 first: {563 x: fdx,564 y: fdy565 },566 last: {567 x: ldx,568 y: ldy569 }570 };571 }572 }]);573}();