UNPKG

@area2-ai/a2-node-keystroke-package

Version:

## Description

929 lines (904 loc) 31.7 kB
import emojiRegex from 'emoji-regex'; import { v4 } from 'uuid'; function _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); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _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); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } var defaultCountriesAndLayoutByLanguage = { en: { country: "US", keyboardLayout: "QWERTY" }, zh: { country: "CN", keyboardLayout: "QWERTY" }, es: { country: "ES", keyboardLayout: "QWERTY (Spanish)" }, fr: { country: "FR", keyboardLayout: "AZERTY" }, ar: { country: "SA", keyboardLayout: "Arabic (Standard)" }, pt: { country: "PT", keyboardLayout: "QWERTY (Portuguese)" }, ru: { country: "RU", keyboardLayout: "ЙЦУКЕН" }, ja: { country: "JP", keyboardLayout: "QWERTY (JIS)" }, de: { country: "DE", keyboardLayout: "QWERTZ" }, it: { country: "IT", keyboardLayout: "QWERTY (Italian)" } }; var keyZones = { 1: /[12qwaszx]/i, 2: /[34erdfcv]/i, 3: /[56tyghbn]/i, 4: /[78uijkm,]/i, 5: /[90opl;./]/i, 6: /[-=']/i }; var specialKeys = { Backspace: /Backspace/i, Meta: /(Shift|Alt|Control|CapsLock)/i, Enter: /Enter/i, Space: / /i }; var keyTypes = { alphanumeric: /^[a-z0-9]$/i, punctuation: /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/, modifierKeys: /^(Shift|Alt|Control|CapsLock|Meta|ContextMenu|NumLock|ScrollLock)$/i, space: / /, backspace: /Backspace/i, emoji: /*#__PURE__*/emojiRegex(), enterKey: /Enter/i //* Regex to match "Enter" key }; var seasons = { 1: 'Winter', 2: "Winter", 3: "Spring", 4: "Spring", 5: "Spring", 6: "Summer", 7: "Summer", 8: "Summer", 9: "Fall", 10: "Fall", 11: "Fall", 12: "Winter" }; var specialKeyMap = { 'Backspace': 8, 'Tab': 9, 'Enter': 13, 'Shift': 16, 'Ctrl': 17, 'Alt': 18, 'Pause': 19, 'CapsLock': 20, 'Esc': 27, 'Space': 32, 'PageUp': 33, 'PageDown': 34, 'End': 35, 'Home': 36, 'LeftArrow': 37, 'UpArrow': 38, 'RightArrow': 39, 'DownArrow': 40, 'Insert': 45, 'Delete': 46, 'Meta': 91, 'ContextMenu': 93, 'NumLock': 144, 'ScrollLock': 145, 'SemiColon': 186, 'EqualSign': 187, 'Comma': 188, 'Dash': 189, 'Period': 190, 'ForwardSlash': 191, 'GraveAccent': 192, 'OpenBracket': 219, 'BackSlash': 220, 'CloseBracket': 221, 'SingleQuote': 222 }; var checkIsEmoji = function checkIsEmoji(character) { var regex = emojiRegex(); return regex.test(character); }; /** * Formats the user's language and country code based on the browser's language settings. * * @returns {string} A formatted language code in the form of `language-country` (e.g., `en-US`). * * ### Functionality: * - Retrieves the user's browser language using `navigator.language`. * - Splits the language string into a language code (e.g., `en`) and a country code (e.g., `US`). * - Checks if the country code is valid (i.e., not numeric): * - If valid, returns the original `language-country` format. * - If invalid or missing, assigns a default country code based on the language. * - Returns the final formatted language string. */ var formatLanguage = function formatLanguage() { var _defaultCountriesAndL; var lang = navigator.language.split('-'); var userLanguageCode = lang[0]; var userCountryCode = lang[1]; var countryCodeToNumber = Number(userCountryCode); if (userCountryCode && isNaN(countryCodeToNumber)) { return lang.join('-'); } userCountryCode = ((_defaultCountriesAndL = defaultCountriesAndLayoutByLanguage[userLanguageCode]) == null ? void 0 : _defaultCountriesAndL.country) || 'not-found'; if (userCountryCode === 'not-found') { return 'en-US'; } return userLanguageCode + "-" + userCountryCode; }; // Function to generate a UUID var generateUUID = function generateUUID() { return v4(); }; /** * Returns the day of the week as a string based on a given date. * @param date - The date object to get the day of the week from. * @returns The corresponding day of the week as a string. */ var getDayOfWeek = function getDayOfWeek(date) { var daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; return daysOfWeek[date.getDay()]; }; /** * Compares two strings and returns the words present in the first string but not in the second. * The comparison is case-insensitive and ignores leading/trailing spaces. * * @param {string} string1 - The first string to compare. * @param {string} string2 - The second string to compare against. * @returns {string} - A single string containing the words from `string1` that are not found in `string2`, separated by spaces. * * @example * const string1 = "Hello world from TypeScript"; * const string2 = "Hello from JavaScript"; * const result = getDifferenceBetweenWords(string1, string2); * console.log(result); // Output: "world TypeScript" */ var getDifferenceBetweenWords = function getDifferenceBetweenWords(string1, string2) { //? Splits both strings into words and removes extra spaces var words1 = string1.toLowerCase().trim().split(/\s+/); var words2 = string2.toLowerCase().trim().split(/\s+/); //? Filters out words in string1 that are not in string2 var differences = words1.filter(function (word) { return !words2.includes(word); }); return differences.join(' '); }; /** * Converts an emoji character to its Unicode code point representation. * * @param {string} emoji - The emoji character to convert. * @param {boolean} [isAndroid=false] - Determines if the emoji should be normalized for Android devices. * - `true`: Normalizes the emoji using NFC (Normalization Form Canonical Composition) before converting. * - `false`: Directly retrieves the Unicode code point without normalization. * @returns {string} The Unicode code point of the emoji in uppercase hexadecimal format. * * @example * getEmojiUnicode('😀'); * // Returns: "1F600" * * getEmojiUnicode('😀', true); * // Returns: "1F600" */ var getEmojiUnicode = function getEmojiUnicode(emoji, isAndroid) { if (isAndroid === void 0) { isAndroid = false; } if (isAndroid) { var normalizedCodePoint = emoji.normalize('NFC').codePointAt(0).toString(16).toUpperCase(); return normalizedCodePoint; } // Get the code in UTF-32 format var utf32CodePoint = emoji.codePointAt(0).toString(16).toUpperCase(); return utf32CodePoint; }; var getInputType = function getInputType(target) { var hasAutocorrect = target.getAttribute('autocorrect') ? true : false; var isSpellcheckEnabled = target.spellcheck; if (target.tagName !== 'INPUT' && target.tagName !== 'TEXTAREA') { return 'Text_ACOFF'; } if (hasAutocorrect || isSpellcheckEnabled) { return 'Text'; } return 'Text_ACOFF'; }; /** * Calculates the area occupied by the on-screen keyboard on mobile devices. * * This function determines the dimensions of the keyboard area based on the * difference between the total screen height and the current viewport height. * If the difference is below a certain threshold, it assumes that the keyboard * is not visible. * * @returns {KeyboardArea | undefined} - An object representing the keyboard's position and dimensions: * - `x`: The x-coordinate of the keyboard's starting position (always `0` for full-width keyboards). * - `y`: The y-coordinate of the top edge of the keyboard, which matches the viewport's bottom edge. * - `width`: The width of the keyboard (matches the viewport's width). * - `height`: The height of the keyboard. * * Returns `undefined` if the keyboard is not visible or if the function is called outside a browser environment. * * @example * const keyboardArea = getKeyboardArea(); * if (keyboardArea) { * console.log(`Keyboard height: ${keyboardArea.height}`); * } */ var getKeyboardArea = function getKeyboardArea() { var viewportHeight = window.innerHeight; var fullHeight = window.screen.height; // Total screen height //? If the difference between the height of the window and the screen is significant, there is a visible keyboard. var keyboardHeight = fullHeight - viewportHeight; if (keyboardHeight <= 100) { return; } // Arbitrary threshold for considering the keypad to be active return { x: 0, y: viewportHeight, width: window.innerWidth, height: keyboardHeight }; }; var getKeyboardLayout = function getKeyboardLayout() { var _defaultCountriesAndL; var userLanguageCode = navigator.language.split('-')[0]; return ((_defaultCountriesAndL = defaultCountriesAndLayoutByLanguage[userLanguageCode]) == null ? void 0 : _defaultCountriesAndL.keyboardLayout) || 'QWERTY'; }; /** * Calculates the screen size of the device in the specified unit ('px' or 'mm'). * * @param {('px' | 'mm')} unit - The unit of measurement for the screen size. * 'px' returns the dimensions in pixels, * while 'mm' returns the dimensions in millimeters (estimated). * @returns {ScreenSize} An object containing the width and height of the screen in the selected unit. * - If 'px', dimensions are returned as integers. * - If 'mm', dimensions are returned as numbers rounded to two decimal places. * * @example * // Get screen size in pixels * const sizeInPixels = getScreenDeviceSize('px'); * console.log(sizeInPixels); // { width: 1920, height: 1080 } * * @example * // Get screen size in millimeters * const sizeInMillimeters = getScreenDeviceSize('mm'); * console.log(sizeInMillimeters); // { width: 508.02, height: 285.75 } * * @note The conversion to millimeters is an estimation based on the device's pixel density (DPI). * The accuracy may vary depending on the device and browser. */ var getScreenDeviceSize = function getScreenDeviceSize(unit) { var screenWidthPixels = screen.width; var screenHeightPixels = screen.height; if (unit === 'px') { return { width: screenWidthPixels, height: screenHeightPixels }; } var dpi = window.devicePixelRatio * 96; // Estimation based on browser pixel rate var inchToMm = 25.4; // Conversion from inches to mm var screenWidthMm = screen.width / dpi * inchToMm; var screenHeightMm = screen.height / dpi * inchToMm; return { width: Number(screenWidthMm.toFixed(2)), height: Number(screenHeightMm.toFixed(2)) }; }; /** * Returns the season for a given month. * @param month - The month as a number (1 for January, 12 for December). * @returns The corresponding season as a string. */ var getSeason = function getSeason(month) { if (month < 1 || month > 12) throw new Error('The month must be a number between 1 and 12'); return seasons[month]; }; /** * Returns the time of day based on the hour. * @param hour - The hour as a number (0-23). * @returns The corresponding time of day as a string. */ var getTimeOfDay = function getTimeOfDay(hour) { if (hour < 0 || hour >= 24) throw new Error('The time must be a number between 0 and 23'); if (hour >= 6 && hour < 12) return 'Morning'; if (hour >= 12 && hour < 18) return 'Afternoon'; if (hour >= 18 && hour <= 23) return 'Evening'; return 'Night'; }; var getKeyCode = function getKeyCode(character) { if (checkIsEmoji(character)) { return 900; } //* Special code for emojis if (character.length === 1) { return character.charCodeAt(0); } return specialKeyMap[character] || 0; //* Default to 0 if no match is found }; var getKeyArea = function getKeyArea(character) { //? Determine the key zone for the given character for (var _i = 0, _Object$entries = Object.entries(keyZones); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _Object$entries[_i], zone = _Object$entries$_i[0], regex = _Object$entries$_i[1]; if (regex.test(character)) { return parseInt(zone); } } //? Check if the character matches a special key if (specialKeys.Backspace.test(character) || specialKeys.Meta.test(character) || specialKeys.Enter.test(character) || specialKeys.Space.test(character)) { return 0; } return 0; //* Default to 0 if no match is found }; var getKeyType = function getKeyType(character) { for (var _i2 = 0, _Object$entries2 = Object.entries(keyTypes); _i2 < _Object$entries2.length; _i2++) { var _Object$entries2$_i = _Object$entries2[_i2], type = _Object$entries2$_i[0], regex = _Object$entries2$_i[1]; if (regex.test(character)) { switch (type) { case 'alphanumeric': return 'an'; case 'punctuation': return 'pt'; case 'modifierKeys': return 'md'; case 'space': return 'sp'; case 'backspace': return 'bk'; case 'emoji': return 'em'; case 'enterKey': return 'en'; } } } return 'ukn'; //* Returns 'ukn' if the type is not found }; var getInitialMobileTypingData = function getInitialMobileTypingData() { return { autocorrectLengths: [], autocorrectTimes: [], emojis: [], keyArea: [], keyTypes: [], language: formatLanguage(), layout: getKeyboardLayout(), pasteLengths: [], pasteTimes: [], performance: [], predictionLengths: [], predictionTimes: [], pressTimes: [], qualityCheck: [], releaseTimes: [], screenSizeMm: getScreenDeviceSize('mm'), screenSizePx: getScreenDeviceSize('px'), season: getSeason(new Date().getMonth() + 1), sessionID: generateUUID(), startUnixTime: null, textField: "Text_ACOFF", time: getTimeOfDay(new Date().getHours()), timeZone: new Date().getTimezoneOffset() / -60, weekday: getDayOfWeek(new Date()) }; }; var getInitialTypingData = function getInitialTypingData() { return { // startTimestamp: getFormattedTimestamp(), keyArea: [], keyTypes: [], length: 0, pressTimes: [], qualityCheck: [], releaseTimes: [], season: getSeason(new Date().getMonth() + 1), sessionID: generateUUID(), setting: "ah", source: "mech", startUnixTime: null, study: "demo", task: "free", textStructure: [], time: getTimeOfDay(new Date().getHours()), timeZone: new Date().getTimezoneOffset() / -60, weekday: getDayOfWeek(new Date()) }; }; /** * Manages keystroke data for a typing session on Android devices. */ var AndroidKeystrokeManager = /*#__PURE__*/function () { function AndroidKeystrokeManager() { this.isTypingSessionActive = false; this.typingData = getInitialMobileTypingData(); this.openKeyEntry = undefined; this.textWasPasted = false; this.previousText = undefined; this.backspaceWasPressed = false; this.performanceStart = 0; this.lastKeyPressed = undefined; this.prevContentLength = 0; } /** * Returns whether a typing session is active. */ var _proto = AndroidKeystrokeManager.prototype; /** * Processes the input content before it changes. * @param currentValue - Current value of the input. * @param inputContentValue - Previous value of the input. */ _proto.processBeforeInput = function processBeforeInput(currentValue, inputContentValue) { this.prevContentLength = currentValue.length; this.previousText = inputContentValue; // Before it changes } /** * Processes the paste event. * @param pastedText - Text that was pasted. */; _proto.processPaste = function processPaste(pastedText) { this.textWasPasted = true; if (!this.isTypingSessionActive) { return; } var diff = Date.now() / 1000 - this.typingData.startUnixTime; //* Seconds this.typingData = _extends({}, this.typingData, { pasteTimes: [].concat(this.typingData.pasteTimes, [Math.trunc(diff * 1000)]), pasteLengths: [].concat(this.typingData.pasteLengths, [pastedText.length]) }); } /** * Processes the autocorrection event. * @param keyPressed - Key that was pressed. * @param newValue - New value of the input. */; _proto.processAutocorrection = function processAutocorrection(keyPressed, newValue) { if (this.textWasPasted) { this.textWasPasted = false; return; } if (this.backspaceWasPressed) { this.backspaceWasPressed = false; return; } if (checkIsEmoji(keyPressed)) { return; } if (keyPressed.length <= 1) { return; } var diff = Date.now() / 1000 - this.typingData.startUnixTime; //* Seconds var autocorrectedWord; //? If they are the same, it was already a correction if (keyPressed === newValue) { autocorrectedWord = keyPressed.trim().toLowerCase(); } autocorrectedWord = getDifferenceBetweenWords(newValue, this.previousText); if (autocorrectedWord.length === 0) { return; } this.previousText = undefined; this.typingData = _extends({}, this.typingData, { autocorrectLengths: [].concat(this.typingData.autocorrectLengths, [autocorrectedWord.length]), autocorrectTimes: [].concat(this.typingData.autocorrectTimes, [Math.trunc(diff * 1000)]) }); } /** * Processes the key input event. * @param inputContent - Content of the input. */; _proto.processKeyInput = function processKeyInput(inputContent) { var _this$openKeyEntry; var diff = inputContent.length - this.prevContentLength; var keyPressed = inputContent.slice(-diff); if (diff < 1) { this.backspaceWasPressed = true; keyPressed = 'Backspace'; } this.lastKeyPressed = keyPressed; this.processAutocorrection(keyPressed, inputContent); //? Ensure a key press event is not duplicated if (((_this$openKeyEntry = this.openKeyEntry) == null ? void 0 : _this$openKeyEntry.keyValue) === keyPressed) { return; } this.openKeyEntry = { keyValue: keyPressed }; } /** * Processes a keydown event and updates typing data. * @param target - The target input element. */; _proto.processKeydown = function processKeydown(target) { this.performanceStart = performance.now(); var temporalStartUnixTime = Date.now() / 1000; //* Seconds var pressTime; if (!this.isTypingSessionActive) { this.isTypingSessionActive = true; pressTime = Date.now() / 1000 - temporalStartUnixTime; //* Compute relative time in seconds this.typingData = _extends({}, this.typingData, { textField: getInputType(target), keyboardArea: getKeyboardArea(), startUnixTime: temporalStartUnixTime, pressTimes: [].concat(this.typingData.pressTimes, [Math.trunc(pressTime * 1000000)]) }); return; } pressTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in seconds this.typingData = _extends({}, this.typingData, { pressTimes: [].concat(this.typingData.pressTimes, [Math.trunc(pressTime * 1000000)]) }); } /** * Processes a keyup event and updates typing data. */; _proto.processKeyup = function processKeyup() { var emojiUnicode; var keyCode = getKeyCode(this.lastKeyPressed); var keyArea = getKeyArea(this.lastKeyPressed); var keyType = getKeyType(this.lastKeyPressed); //? If is emoji if (keyCode === 900) { emojiUnicode = getEmojiUnicode(this.lastKeyPressed, true); } var releaseTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in Seconds var performanceEnd = performance.now(); this.typingData = _extends({}, this.typingData, { releaseTimes: [].concat(this.typingData.releaseTimes, [Math.trunc(releaseTime * 1000000)]), performance: [].concat(this.typingData.performance, [performanceEnd - this.performanceStart]), keyArea: [].concat(this.typingData.keyArea, [keyArea]), keyTypes: [].concat(this.typingData.keyTypes, [keyType]), emojis: [].concat(this.typingData.emojis, emojiUnicode ? [emojiUnicode] : []) }); this.openKeyEntry = undefined; } /** * Ends the current typing session and returns the collected typing data. * @returns The collected typing data. */; _proto.endTypingSession = function endTypingSession() { this.isTypingSessionActive = false; return this.typingData; } /** * Resets the typing data to its initial state. */; _proto.resetTypingData = function resetTypingData() { this.typingData = getInitialMobileTypingData(); this.performanceStart = 0; }; return _createClass(AndroidKeystrokeManager, [{ key: "getIsTypingSessionActive", get: function get() { return this.isTypingSessionActive; } }]); }(); /** * Manages keystroke data for a typing session on iOS devices. */ var IosKeystrokeManager = /*#__PURE__*/function () { function IosKeystrokeManager() { this.isTypingSessionActive = false; this.typingData = getInitialMobileTypingData(); this.openKeyEntry = {}; this.textWasPasted = false; this.previousText = undefined; this.backspaceWasPressed = false; this.performanceStart = 0; this.prevContentLength = 0; } /** * Returns whether a typing session is active. */ var _proto = IosKeystrokeManager.prototype; /** * Processes the paste event. * @param pastedText - Text that was pasted. */ _proto.processPaste = function processPaste(pastedText) { this.textWasPasted = true; if (!this.isTypingSessionActive) { return; } var diff = Date.now() / 1000 - this.typingData.startUnixTime; //* Seconds this.typingData = _extends({}, this.typingData, { pasteTimes: [].concat(this.typingData.pasteTimes, [Math.trunc(diff * 1000)]), pasteLengths: [].concat(this.typingData.pasteLengths, [pastedText.length]) }); } /** * Processes the autocorrection event. * @param textInputValue - New value of the input. */; _proto.processAutocorrection = function processAutocorrection(textInputValue) { if (!this.previousText) { return; } if (textInputValue.startsWith(this.previousText)) { return; } var diff = Date.now() / 1000 - this.typingData.startUnixTime; //* Seconds var autocorrectedWord = getDifferenceBetweenWords(textInputValue, this.previousText); this.typingData = _extends({}, this.typingData, { autocorrectTimes: [].concat(this.typingData.autocorrectTimes, [Math.trunc(diff * 1000)]), autocorrectLengths: [].concat(this.typingData.autocorrectLengths, [autocorrectedWord.length]) }); this.previousText = undefined; } /** * Processes the prediction event. * @param newValue - New value of the input. * @param textSnapshot - Previous value of the input. */; _proto.processPrediction = function processPrediction(newValue, textSnapshot) { if (this.textWasPasted) { this.textWasPasted = false; return; } if (this.backspaceWasPressed) { this.backspaceWasPressed = false; return; } //? Checks if the last character contains an emoji var lastCharLength = newValue.length - this.prevContentLength; var lastChar = newValue.slice(-lastCharLength); // May be a composite emoji if (checkIsEmoji(lastChar)) { return; } if (newValue.length - textSnapshot.length === 1 || newValue === textSnapshot) { return; } var diff = Date.now() / 1000 - this.typingData.startUnixTime; //* Seconds var predictedWord = getDifferenceBetweenWords(newValue, textSnapshot); this.typingData = _extends({}, this.typingData, { predictionTimes: [].concat(this.typingData.predictionTimes, [Math.trunc(diff * 1000)]), predictionLengths: [].concat(this.typingData.predictionLengths, [predictedWord.length]) }); } /** * Handles the autocorrection trigger. * @param key - The key that was pressed. * @param text - The current text in the input. */; _proto.handleAutocorrectionTrigger = function handleAutocorrectionTrigger(key, text) { if (key !== ' ' && key !== 'Enter') { return; } this.previousText = text; } /** * Processes a keydown event and updates typing data. * @param keyPressed - The key that was pressed. * @param target - The target input element. */; _proto.processKeydown = function processKeydown(keyPressed, target) { var _extends2; this.performanceStart = performance.now(); var temporalStartUnitTime = Date.now() / 1000; //* Seconds if (keyPressed === 'Backspace') { this.backspaceWasPressed = true; } if (!this.isTypingSessionActive) { var timeZone = new Date().getTimezoneOffset() / -60; this.isTypingSessionActive = true; this.typingData = _extends({}, this.typingData, { timeZone: timeZone, textField: getInputType(target), keyboardArea: getKeyboardArea(), startUnixTime: temporalStartUnitTime }); } //? Ensure a key press event is not duplicated if (keyPressed in this.openKeyEntry) { return; } var pressTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in seconds this.handleAutocorrectionTrigger(keyPressed, target.value); this.openKeyEntry = _extends({}, this.openKeyEntry, (_extends2 = {}, _extends2[keyPressed] = { pressTime: Math.trunc(pressTime * 1000000), keyCode: getKeyCode(keyPressed), keyArea: getKeyArea(keyPressed), keyType: getKeyType(keyPressed) }, _extends2)); } /** * Processes a keyup event and updates typing data. * @param keyPressed - The key that was released. */; _proto.processKeyup = function processKeyup(keyPressed) { var emojiUnicode; if (!(keyPressed in this.openKeyEntry)) { return; } var keyEntry = this.openKeyEntry[keyPressed]; //? If is emoji if (keyEntry.keyCode === 900) { emojiUnicode = getEmojiUnicode(keyPressed); } var releaseTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in Seconds var performanceEnd = performance.now(); this.typingData = _extends({}, this.typingData, { pressTimes: [].concat(this.typingData.pressTimes, [keyEntry.pressTime]), releaseTimes: [].concat(this.typingData.releaseTimes, [Math.trunc(releaseTime * 1000000)]), keyArea: [].concat(this.typingData.keyArea, [keyEntry.keyArea]), keyTypes: [].concat(this.typingData.keyTypes, [keyEntry.keyType]), emojis: [].concat(this.typingData.emojis, emojiUnicode ? [emojiUnicode] : []), performance: [].concat(this.typingData.performance, [performanceEnd - this.performanceStart]) }); delete this.openKeyEntry[keyPressed]; } /** * Ends the current typing session and returns the collected typing data. * @returns The collected typing data. */; _proto.endTypingSession = function endTypingSession() { this.isTypingSessionActive = false; return this.typingData; } /** * Resets the typing data to its initial state. */; _proto.resetTypingData = function resetTypingData() { this.openKeyEntry = {}; this.typingData = getInitialMobileTypingData(); this.performanceStart = 0; }; return _createClass(IosKeystrokeManager, [{ key: "getIsTypingSessionActive", get: function get() { return this.isTypingSessionActive; } /** * Sets the previous content length. */ }, { key: "setPrevContentLength", set: function set(value) { this.prevContentLength = value; } }]); }(); /** * Manages keystroke data for a typing session. */ var KeystrokeManager = /*#__PURE__*/function () { function KeystrokeManager() { this.isTypingSessionActive = false; this.typingData = getInitialTypingData(); this.openKeyEntries = {}; } /** * Returns whether a typing session is active. */ var _proto = KeystrokeManager.prototype; /** * Processes input changes and updates typing data length. * @param newValue - The new input value. */ _proto.processInputChange = function processInputChange(newValue) { this.typingData = _extends({}, this.typingData, { length: newValue.length }); } /** * Processes a keydown event and updates typing data. * @param keyPressed - The key that was pressed. */; _proto.processKeydown = function processKeydown(keyPressed) { var temporalStartUnixTime = Date.now() / 1000; //* Seconds if (!this.isTypingSessionActive) { this.isTypingSessionActive = true; this.typingData = _extends({}, this.typingData, { startUnixTime: temporalStartUnixTime }); } //? Ensure a key press event is not duplicated if (keyPressed in this.openKeyEntries) return; var pressTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in seconds this.openKeyEntries[keyPressed] = { pressTime: Math.trunc(pressTime * 1000000), keyCode: getKeyCode(keyPressed), keyArea: getKeyArea(keyPressed), keyType: getKeyType(keyPressed) }; } /** * Processes a keyup event and updates typing data. * @param keyPressed - The key that was released. */; _proto.processKeyup = function processKeyup(keyPressed) { if (!(keyPressed in this.openKeyEntries)) return; var keyEntry = this.openKeyEntries[keyPressed]; var releaseTime = Date.now() / 1000 - this.typingData.startUnixTime; //* Compute relative time in Seconds this.typingData = _extends({}, this.typingData, { pressTimes: [].concat(this.typingData.pressTimes, [keyEntry.pressTime]), releaseTimes: [].concat(this.typingData.releaseTimes, [Math.trunc(releaseTime * 1000000)]), keyArea: [].concat(this.typingData.keyArea, [keyEntry.keyArea]), keyTypes: [].concat(this.typingData.keyTypes, [keyEntry.keyType]) }); delete this.openKeyEntries[keyPressed]; } /** * Ends the current typing session and returns the collected typing data. * @returns The collected typing data. */; _proto.endTypingSession = function endTypingSession() { this.isTypingSessionActive = false; return this.typingData; } /** * Resets the typing data to its initial state. */; _proto.resetTypingData = function resetTypingData() { this.typingData = getInitialTypingData(); this.openKeyEntries = {}; }; return _createClass(KeystrokeManager, [{ key: "getIsTypingSessionActive", get: function get() { return this.isTypingSessionActive; } }]); }(); export { AndroidKeystrokeManager, IosKeystrokeManager, KeystrokeManager }; //# sourceMappingURL=a2-node-keystroke-package.esm.js.map