102 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { endUTF32Pair, minUTF32, startUTF32Pair1, startUTF32Pair2 } from "./data.js";
 | |
| 
 | |
| /**
 | |
| * Convert string to number
 | |
| */
 | |
| function getEmojiCodePoint(code) {
 | |
| 	return parseInt(code, 16);
 | |
| }
 | |
| /**
 | |
| * First part of UTF-32 to UTF-16
 | |
| */
 | |
| function utf32FirstNum(code) {
 | |
| 	return (code - minUTF32 >> 10 | 0) + startUTF32Pair1;
 | |
| }
 | |
| /**
 | |
| * First part of UTF-32 to UTF-16
 | |
| */
 | |
| function utf32SecondNum(code) {
 | |
| 	return (code - minUTF32 & 1023) + startUTF32Pair2;
 | |
| }
 | |
| /**
 | |
| * Get UTF-32 as UTF-16 sequence
 | |
| */
 | |
| function splitUTF32Number(code) {
 | |
| 	if (code >= minUTF32) return [utf32FirstNum(code), utf32SecondNum(code)];
 | |
| }
 | |
| /**
 | |
| * Check if number is UTF-32 split as UTF-16
 | |
| *
 | |
| * Returns:
 | |
| * - 1 if number fits first number in sequence
 | |
| * - 2 if number fits second number in sequence
 | |
| * - false on failure
 | |
| */
 | |
| function isUTF32SplitNumber(value) {
 | |
| 	if (value >= startUTF32Pair1) {
 | |
| 		if (value < startUTF32Pair2) return 1;
 | |
| 		if (value < endUTF32Pair) return 2;
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| /**
 | |
| * Get UTF-16 sequence as UTF-32
 | |
| */
 | |
| function mergeUTF32Numbers(part1, part2) {
 | |
| 	if (part1 < startUTF32Pair1 || part1 >= startUTF32Pair2 || part2 < startUTF32Pair2 || part2 >= endUTF32Pair) return;
 | |
| 	return (part1 - startUTF32Pair1 << 10) + (part2 - startUTF32Pair2) + minUTF32;
 | |
| }
 | |
| /**
 | |
| * Convert hexadecimal string or number to unicode
 | |
| */
 | |
| function getEmojiUnicode(code) {
 | |
| 	return String.fromCodePoint(typeof code === "number" ? code : getEmojiCodePoint(code));
 | |
| }
 | |
| /**
 | |
| * Convert sequence to UTF-16
 | |
| */
 | |
| function convertEmojiSequenceToUTF16(numbers) {
 | |
| 	const results = [];
 | |
| 	for (let i = 0; i < numbers.length; i++) {
 | |
| 		const code = numbers[i];
 | |
| 		if (code >= minUTF32) {
 | |
| 			results.push(utf32FirstNum(code));
 | |
| 			results.push(utf32SecondNum(code));
 | |
| 		} else results.push(code);
 | |
| 	}
 | |
| 	return results;
 | |
| }
 | |
| /**
 | |
| * Convert sequence to UTF-32
 | |
| */
 | |
| function convertEmojiSequenceToUTF32(numbers, throwOnError = true) {
 | |
| 	const results = [];
 | |
| 	for (let i = 0; i < numbers.length; i++) {
 | |
| 		const code = numbers[i];
 | |
| 		if (code >= minUTF32) {
 | |
| 			results.push(code);
 | |
| 			continue;
 | |
| 		}
 | |
| 		const part = isUTF32SplitNumber(code);
 | |
| 		if (!part) {
 | |
| 			results.push(code);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (part === 1 && numbers.length > i + 1) {
 | |
| 			const merged = mergeUTF32Numbers(code, numbers[i + 1]);
 | |
| 			if (merged) {
 | |
| 				i++;
 | |
| 				results.push(merged);
 | |
| 				continue;
 | |
| 			}
 | |
| 		}
 | |
| 		if (throwOnError) {
 | |
| 			const nextCode = numbers[i + 1];
 | |
| 			throw new Error(`Invalid UTF-16 sequence: ${code.toString(16)}-${nextCode ? nextCode.toString(16) : "undefined"}`);
 | |
| 		}
 | |
| 		results.push(code);
 | |
| 	}
 | |
| 	return results;
 | |
| }
 | |
| 
 | |
| export { convertEmojiSequenceToUTF16, convertEmojiSequenceToUTF32, getEmojiCodePoint, getEmojiUnicode, isUTF32SplitNumber, mergeUTF32Numbers, splitUTF32Number }; | 
