mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-18 09:53:05 +08:00
66 lines
2.9 KiB
Plaintext
66 lines
2.9 KiB
Plaintext
export interface AnnouncementSpan { text: string; url: string; }
|
|
|
|
// Same restricted plain-text/link grammar as Android AnnouncementText.java.
|
|
export function isAnnouncementURL(address: string): boolean {
|
|
return !/[\x00-\x20\x7f\\<>\"]/.test(address) &&
|
|
/^https?:\/\/(?:[^\/?#]*@)?(?:\[[0-9a-f:.]+\]|[^\/?#:@\[\]]+)(?::[0-9]+)?(?:[\/?#]|$)/i.test(address);
|
|
}
|
|
function hasScheme(text: string, start: number): boolean {
|
|
return /^https?:\/\//i.test(text.substring(start, start + 8));
|
|
}
|
|
function urlEnd(text: string, start: number): number {
|
|
let parentheses = 0, brackets = 0, end = start;
|
|
for (; end < text.length; end++) {
|
|
const c = text.charAt(end);
|
|
if (c.charCodeAt(0) <= 0x20 || c.charCodeAt(0) === 0x7f || c === '\\' || c === '`' ||
|
|
'<>\"\'{},。;:!?、()【】《》「」『』“”‘’'.includes(c)) break;
|
|
if (c === '(') parentheses++;
|
|
if (c === ')') { if (parentheses === 0) break; parentheses--; }
|
|
if (c === '[') brackets++;
|
|
if (c === ']') { if (brackets === 0) break; brackets--; }
|
|
}
|
|
return end;
|
|
}
|
|
export function announcementSpans(body: string): AnnouncementSpan[] {
|
|
const text = body.replace(/\r\n/g, '\n'), result: AnnouncementSpan[] = [];
|
|
let plain = '';
|
|
for (let position = 0; position < text.length;) {
|
|
let label = '', address = '', next = position;
|
|
if (text.charAt(position) === '[') {
|
|
const labelEnd = text.indexOf(']', position + 1), start = labelEnd + 2;
|
|
if (labelEnd > position + 1 && start < text.length && text.charAt(labelEnd + 1) === '(' &&
|
|
!/[\[\]\r\n]/.test(text.substring(position + 1, labelEnd)) && hasScheme(text, start)) {
|
|
const end = urlEnd(text, start), url = text.substring(start, end);
|
|
if (text.charAt(end) === ')' && isAnnouncementURL(url)) {
|
|
label = text.substring(position + 1, labelEnd); address = url; next = end + 1;
|
|
}
|
|
}
|
|
}
|
|
if (!address && hasScheme(text, position)) {
|
|
let end = urlEnd(text, position);
|
|
while (end > position && '.,;:!?'.includes(text.charAt(end - 1))) end--;
|
|
const url = text.substring(position, end);
|
|
if (isAnnouncementURL(url)) { label = address = url; next = end; }
|
|
}
|
|
if (address) {
|
|
if (plain) { result.push({ text: plain, url: '' }); plain = ''; }
|
|
result.push({ text: label, url: address }); position = next;
|
|
} else plain += text.charAt(position++);
|
|
}
|
|
if (plain) result.push({ text: plain, url: '' });
|
|
return result;
|
|
}
|
|
|
|
export function announcementByteLength(text: string): number {
|
|
let bytes = 0;
|
|
for (let i = 0; i < text.length; i++) {
|
|
const c = text.charCodeAt(i);
|
|
if (c < 0x80) bytes++;
|
|
else if (c < 0x800) bytes += 2;
|
|
else if (c >= 0xd800 && c <= 0xdbff && i + 1 < text.length &&
|
|
text.charCodeAt(i + 1) >= 0xdc00 && text.charCodeAt(i + 1) <= 0xdfff) { bytes += 4; i++; }
|
|
else bytes += c >= 0xd800 && c <= 0xdfff ? 1 : 3; // Java UTF-8 replaces lone surrogates with '?'.
|
|
}
|
|
return bytes;
|
|
}
|