Saturday, June 11, 2022

Multiple nots is knotty to read

Simplifying multiple nots:
const isVariant = e.match(VARIANT_TESTER);
const matchingFirstHanzi = isVariant?.[2];
const matchingSecondHanzi = isVariant?.[4];

// knotty read:
// const needToShow
//     = !isVariant || (matchingFirstHanzi !== hanzi && matchingSecondHanzi !== hanzi);

// De-Morgan'd, easy to read:
// const needToHide = isVariant && (matchingFirstHanzi === hanzi || matchingSecondHanzi === hanzi);

const needToHide = isVariant && [matchingFirstHanzi, matchingSecondHanzi].includes(hanzi);
const needToShow = !needToHide;

Thursday, November 18, 2021

Horizontal scrollbar on textarea when text exceeds textarea's width

This works on Chrome:
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

Sunday, November 14, 2021

Split keep function. Lookbehind, match, loop

Common code:
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

Saturday, October 2, 2021

Magic of 1s and 0s

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(' '))
)

Tuesday, June 8, 2021

safari-web-extension-converter error

$ xcrun safari-web-extension-converter
xcrun: error: unable to find utility "safari-web-extension-converter", not a developer tool or in PATH
Do this first:
sudo xcode-select -s /Applications/Xcode.app

Saturday, May 29, 2021

TypeScript Object is possibly undefined

   .
   .
   .
   
   newHzl[hanzi].pinyin = newHzl[hanzi].pinyin.filter(eachPinyin => pinyinEnglish[eachPinyin]);
} // for loop   
That 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

Tuesday, May 18, 2021

JavaScript splitKeep

Helper function for split keep. Reference: https://medium.com/@shemar.gordon32/how-to-split-and-keep-the-delimiter-s-d433fb697c65
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'));
};