Suite of AT Protocol TypeScript libraries built on web standards
21
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 75 lines 2.8 kB view raw
1export const utf8Len = (str: string): number => { 2 return new TextEncoder().encode(str).byteLength; 3}; 4 5export const graphemeLen = (str: string): number => { 6 if (typeof Intl !== "undefined" && "Segmenter" in Intl) { 7 const segmenter = new Intl.Segmenter(undefined, { 8 granularity: "grapheme", 9 }); 10 return Array.from(segmenter.segment(str)).length; 11 } 12 13 return Array.from(str).length; 14}; 15 16export const utf8ToB64Url = (utf8: string): string => { 17 const encoder = new TextEncoder(); 18 const bytes = encoder.encode(utf8); 19 return btoa(String.fromCharCode(...bytes)) 20 .replace(/\+/g, "-") 21 .replace(/\//g, "_") 22 .replace(/=/g, ""); 23}; 24 25export const b64UrlToUtf8 = (b64: string): string => { 26 const base64 = b64.replace(/-/g, "+").replace(/_/g, "/"); 27 const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4); 28 29 const binaryString = atob(padded); 30 const bytes = new Uint8Array(binaryString.length); 31 for (let i = 0; i < binaryString.length; i++) { 32 bytes[i] = binaryString.charCodeAt(i); 33 } 34 35 const decoder = new TextDecoder(); 36 return decoder.decode(bytes); 37}; 38 39export const parseLanguage = (langTag: string): LanguageTag | null => { 40 const parsed = langTag.match(bcp47Regexp); 41 if (!parsed?.groups) return null; 42 const parts = parsed.groups; 43 const result: LanguageTag = {}; 44 45 if (parts.grandfathered) result.grandfathered = parts.grandfathered; 46 if (parts.language) result.language = parts.language; 47 if (parts.extlang) result.extlang = parts.extlang; 48 if (parts.script) result.script = parts.script; 49 if (parts.region) result.region = parts.region; 50 if (parts.variant) result.variant = parts.variant; 51 if (parts.extension) result.extension = parts.extension; 52 if (parts.privateUseA || parts.privateUseB) { 53 result.privateUse = parts.privateUseA || parts.privateUseB; 54 } 55 56 return result; 57}; 58 59export const validateLanguage = (langTag: string): boolean => { 60 return bcp47Regexp.test(langTag); 61}; 62 63export type LanguageTag = { 64 grandfathered?: string; 65 language?: string; 66 extlang?: string; 67 script?: string; 68 region?: string; 69 variant?: string; 70 extension?: string; 71 privateUse?: string; 72}; 73 74const bcp47Regexp = 75 /^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUseA>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUseB>x(-[A-Za-z0-9]{1,8})+))$/;