white-space: nowrap;
However, the above does not work on Safari. In Safari, use this instead:
white-space: pre; overflow-x: auto; word-wrap: normal;
https://stackoverflow.com/questions/657795/how-to-remove-word-wrap-from-textarea/25556711#25556711
white-space: nowrap;
white-space: pre; overflow-x: auto; word-wrap: normal;
function escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
Lookbehind:
String.prototype.splitKeep = function (tokens) {
const escaped = escapeRegExp(tokens);
return this.split(new RegExp(`(?=[${escaped}])|(?<=[${escaped}])`, "g"));
};
Match. Safari does not support lookbehind, use match approach:
String.prototype.splitKeep = function (tokens) {
const tokensEscaped = tokens
.split("")
.map((s) => escapeRegExp(s))
.join("|");
const wordMatch = `[^${escapeRegExp(tokens)}]+`;
return this.match(new RegExp(tokensEscaped + "|" + wordMatch, "g"));
};
Loop-based approach:
String.prototype.splitKeep = function (tokens) {
let toPush = "";
const splitList = [];
for (const c of this) {
if (tokens.includes(c)) {
if (toPush.length > 0) {
splitList.push(toPush);
}
splitList.push(c);
toPush = "";
continue;
}
toPush += c;
}
if (toPush.length > 0) {
splitList.push(toPush);
}
return splitList;
};
Benchmark code:
console.time("test");
for (let i = 0; i < 10000; ++i) {
"pin1yin1".splitKeep("12345 ");
}
console.timeEnd("test");
Results:
% node splitkeep-lookbehind.js test: 19.35ms % node splitkeep-lookbehind.js test: 18.951ms % node splitkeep-match.js test: 54.635ms % node splitkeep-match.js test: 51.998ms % node splitkeep-loop.js test: 14.647ms % node splitkeep-loop.js test: 13.035ms
Instead of this:
const VARIANT_CHAR = /variant of \p{Script=Han}/u;
// 'variant of chineseCharacterHere' sorts last
sort((a, b) =>
VARIANT_CHAR.test(a.definitions.join(' '))
&& VARIANT_CHAR.test(b.definitions.join(' ')) ?
0
: VARIANT_CHAR.test(a.definitions.join(' ')) ?
1
: VARIANT_CHAR.test(b.definitions.join(' ')) ?
-1
:
0
)
Just do this:
const VARIANT_CHAR = /variant of \p{Script=Han}/u;
// 'variant of chineseCharacterHere' sorts last
sort((a, b) =>
VARIANT_CHAR.test(a.definitions.join(' '))
-
VARIANT_CHAR.test(b.definitions.join(' '))
)
$ xcrun safari-web-extension-converter xcrun: error: unable to find utility "safari-web-extension-converter", not a developer tool or in PATHDo this first:
sudo xcode-select -s /Applications/Xcode.app
. . . newHzl[hanzi].pinyin = newHzl[hanzi].pinyin.filter(eachPinyin => pinyinEnglish[eachPinyin]); } // for loopThat yields this error:
error: TS2532 [ERROR]: Object is possibly 'undefined'.
newHzl[hanzi].pinyin = newHzl[hanzi].pinyin.filter(
Got a similar error code on following code, and removed the error by checking if a variable has nothing, if nothing then skip(continue)
for (const [hanzi, { pinyinEnglish }] of Object.entries(hzl)) {
if (!pinyinEnglish) {
continue;
}
for (const pinyin of Object.keys(pinyinEnglish)) {
if (pinyinEnglish[pinyin].length === 0) {
delete pinyinEnglish[pinyin];
}
}
.
.
.
I tried to do the same solution on the post's first code, but it still yields a compile error of Object is possibly 'undefined'
.
.
.
if (!newHzl[hanzi].pinyin) {
continue;
}
newHzl[hanzi].pinyin = newHzl[hanzi].pinyin.filter(eachPinyin => pinyinEnglish[eachPinyin]);
} // for loop
The workaround is to introduce a variable so the compiler will not have a hard time inferring the code's control flow:
.
.
.
const hanziPinyin = newHzl[hanzi].pinyin;
if (!hanziPinyin) {
continue;
}
newHzl[hanzi].pinyin = hanziPinyin.filter(eachPinyin => pinyinEnglish[eachPinyin]);
} // for loop
The problem went away
String.prototype.splitKeep = function (tokens) {
const escaped = escapeRegExp(tokens);
return this.split(new RegExp(`(?=[${escaped}])|(?<=[${escaped}])`, 'g'));
};
// Not built-in yet https://github.com/tc39/proposal-regex-escaping
// Use a good one for the meantime https://stackoverflow.com/questions/3115150/how-to-escape-regular-expression-special-characters-using-javascript
function escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
Add this when using TypeScript:
declare global {
interface String {
splitKeep(tokens: string): string[];
}
}
November 14
For browsers that the regex component don't have lookbehind capability yet, use match method:
String.prototype.splitKeep = function (tokens) {
const tokensEscaped = tokens
.split('')
.map((s) => escapeRegExp(s))
.join('|');
const wordMatch = `[^${escapeRegExp(tokens)}]+`;
return this.match(new RegExp(tokensEscaped + '|' + wordMatch, 'g'));
};
<div id="something">Click or double click me</div>
<hr/>
<div id="good">Click or double click me</div>
<style>
#something, #good {
background-color: lemonchiffon;
}
</style>
JS:
addGlobalEventListener(
'click',
'#something',
debounceSingleClickOnly(sayHello)
);
addGlobalEventListener(
'dblclick',
'#something',
sayWorld
);
addGlobalEventListener(
'click',
'#good',
debounceSingleClickOnly(sayHello)
);
addGlobalEventListener(
'dblclick',
'#good',
sayWorld
);
let counter = 0;
function sayHello({target: {id}}) {
++counter;
console.log(`${counter}. clicked ${id}`);
}
function sayWorld({target: {id}}) {
++counter;
console.log(`${counter}. double-clicked ${id}`);
}
function addGlobalEventListener(type, selector, callback) {
document.addEventListener(type, (e) => {
if (e.target.matches(selector)) {
callback(e);
}
});
}
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this,
args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
function debounceSingleClickOnly(func, timeout = 500) {
function eventHandler(event) {
const { detail } = event;
if (detail > 1) {
return;
}
func.apply(this, arguments);
}
return debounce(eventHandler, timeout);
}