Files
2026-03-21 18:57:10 +08:00

124 lines
3.6 KiB
JavaScript

/* global module */
const TOUCH_SHIFT_MODE_OFF = "off";
const TOUCH_SHIFT_MODE_ONCE = "once";
const TOUCH_SHIFT_MODE_LOCK = "lock";
const TOUCH_SHIFT_DOUBLE_TAP_MS = 320;
function normalizeTouchShiftMode(value) {
const normalized = String(value || "")
.trim()
.toLowerCase();
if (normalized === TOUCH_SHIFT_MODE_ONCE) return TOUCH_SHIFT_MODE_ONCE;
if (normalized === TOUCH_SHIFT_MODE_LOCK) return TOUCH_SHIFT_MODE_LOCK;
return TOUCH_SHIFT_MODE_OFF;
}
function isTouchShiftActive(mode) {
const normalized = normalizeTouchShiftMode(mode);
return normalized === TOUCH_SHIFT_MODE_ONCE || normalized === TOUCH_SHIFT_MODE_LOCK;
}
function isAsciiLetter(value) {
return /[A-Za-z]/.test(String(value || ""));
}
function resolveTouchShiftModeOnTap(currentMode, lastTapAt, now, doubleTapWindowMs) {
const normalizedMode = normalizeTouchShiftMode(currentMode);
const currentTime = Number.isFinite(Number(now)) ? Number(now) : Date.now();
const previousTapAt = Number(lastTapAt);
const windowMs = Number.isFinite(Number(doubleTapWindowMs))
? Math.max(0, Number(doubleTapWindowMs))
: TOUCH_SHIFT_DOUBLE_TAP_MS;
if (normalizedMode === TOUCH_SHIFT_MODE_LOCK) {
return TOUCH_SHIFT_MODE_OFF;
}
if (
normalizedMode === TOUCH_SHIFT_MODE_ONCE &&
Number.isFinite(previousTapAt) &&
currentTime >= previousTapAt &&
currentTime - previousTapAt <= windowMs
) {
return TOUCH_SHIFT_MODE_LOCK;
}
return TOUCH_SHIFT_MODE_ONCE;
}
function findCommonPrefixLength(previousValue, nextValue) {
const max = Math.min(previousValue.length, nextValue.length);
let index = 0;
while (index < max && previousValue[index] === nextValue[index]) {
index += 1;
}
return index;
}
function findCommonSuffixLength(previousValue, nextValue, prefixLength) {
const previousRemain = previousValue.length - prefixLength;
const nextRemain = nextValue.length - prefixLength;
const max = Math.min(previousRemain, nextRemain);
let index = 0;
while (
index < max &&
previousValue[previousValue.length - 1 - index] === nextValue[nextValue.length - 1 - index]
) {
index += 1;
}
return index;
}
function applyTouchShiftToValue(previousValue, nextValue, mode) {
const previousText = String(previousValue || "");
const nextText = String(nextValue || "");
const normalizedMode = normalizeTouchShiftMode(mode);
if (!isTouchShiftActive(normalizedMode) || !nextText) {
return {
value: nextText,
consumedOnce: false,
touchedLetter: false,
transformed: false
};
}
const prefixLength = findCommonPrefixLength(previousText, nextText);
const suffixLength = findCommonSuffixLength(previousText, nextText, prefixLength);
const insertedEnd = nextText.length - suffixLength;
const insertedText = nextText.slice(prefixLength, insertedEnd);
if (!insertedText) {
return {
value: nextText,
consumedOnce: false,
touchedLetter: false,
transformed: false
};
}
const touchedLetter = isAsciiLetter(insertedText);
const transformedInserted = insertedText.replace(/[a-z]/g, (match) => match.toUpperCase());
const transformed = transformedInserted !== insertedText;
const value = transformed
? `${nextText.slice(0, prefixLength)}${transformedInserted}${nextText.slice(insertedEnd)}`
: nextText;
return {
value,
consumedOnce: normalizedMode === TOUCH_SHIFT_MODE_ONCE && touchedLetter,
touchedLetter,
transformed
};
}
module.exports = {
TOUCH_SHIFT_DOUBLE_TAP_MS,
TOUCH_SHIFT_MODE_LOCK,
TOUCH_SHIFT_MODE_OFF,
TOUCH_SHIFT_MODE_ONCE,
applyTouchShiftToValue,
isTouchShiftActive,
normalizeTouchShiftMode,
resolveTouchShiftModeOnTap
};