spiegel-keyman/web/source/kmwstring.ts

466 lines
15 KiB
TypeScript

/***
KeymanWeb 10.0
Copyright 2017 SIL International
***/
interface StringConstructor {
kmwFromCharCode(cp0: number): string,
_kmwFromCharCode(cp0: number): string,
kmwEnableSupplementaryPlane(bEnable: boolean)
}
interface String {
kmwCharCodeAt(codePointIndex: number): number,
kmwCharAt(codePointIndex: number) : string,
kmwIndexOf(searchValue: string, fromIndex?: number) : number,
kmwLastIndexOf(searchValue: string, fromIndex?: number) : number,
kmwSlice(beginSlice: number, endSlice: number) : string,
kmwSubstring(start: number, length: number) : string,
kmwSubstr(start: number, length: number) : string,
kmwBMPSubstr(start: number, length: number) : string,
kmwLength(): number,
kmwBMPLength(): number,
kmwNextChar(codeUnitIndex: number): number,
kmwBMPNextChar(codeUnitIndex: number): number,
kmwPrevChar(codeUnitIndex: number): number,
kmwBMPPrevChar(codeUnitIndex: number): number,
kmwCodePointToCodeUnit(codePointIndex: number) : number,
kmwBMPCodePointToCodeUnit(codePointIndex: number) : number,
kmwCodeUnitToCodePoint(codeUnitIndex: number) : number,
kmwBMPCodeUnitToCodePoint(codeUnitIndex: number) : number,
_kmwCharCodeAt(codePointIndex: number): number,
_kmwCharAt(codePointIndex: number) : string,
_kmwIndexOf(searchValue: string, fromIndex?: number) : number,
_kmwLastIndexOf(searchValue: string, fromIndex?: number) : number,
_kmwSlice(beginSlice: number, endSlice: number) : string,
_kmwSubstring(start: number, length?: number) : string,
_kmwSubstr(start: number, length?: number) : string,
_kmwLength(): number,
_kmwNextChar(codeUnitIndex: number): number,
_kmwPrevChar(codeUnitIndex: number): number,
_kmwCodePointToCodeUnit(codePointIndex: number) : number,
_kmwCodeUnitToCodePoint(codeUnitIndex: number) : number,
}
/**
* Constructs a string from one or more Unicode character codepoint values
* passed as integer parameters.
*
* @param {number} cp0,... 1 or more Unicode codepoints, e.g. 0x0065, 0x10000
* @return {string|null} The new String object.
*/
String.kmwFromCharCode = function(cp0) {
var chars = [], i;
for (i = 0; i < arguments.length; i++) {
var c = Number(arguments[i]);
if (!isFinite(c) || c < 0 || c > 0x10FFFF || Math.floor(c) !== c) {
throw new RangeError("Invalid code point " + c);
}
if (c < 0x10000) {
chars.push(c);
} else {
c -= 0x10000;
chars.push((c >> 10) + 0xD800);
chars.push((c % 0x400) + 0xDC00);
}
}
return String.fromCharCode.apply(undefined, chars);
}
/**
* Returns a number indicating the Unicode value of the character at the given
* code point index, with support for supplementary plane characters.
*
* @param {number} codePointIndex The code point index into the string (not
the code unit index) to return
* @return {number} The Unicode character value
*/
String.prototype.kmwCharCodeAt = function(codePointIndex) {
var str = String(this);
var codeUnitIndex = 0;
if (codePointIndex < 0 || codePointIndex >= str.length) {
return NaN;
}
for(var i = 0; i < codePointIndex; i++) {
codeUnitIndex = str.kmwNextChar(codeUnitIndex);
if(codeUnitIndex === null) return NaN;
}
var first = str.charCodeAt(codeUnitIndex);
if (first >= 0xD800 && first <= 0xDBFF && str.length > codeUnitIndex + 1) {
var second = str.charCodeAt(codeUnitIndex + 1);
if (second >= 0xDC00 && second <= 0xDFFF) {
return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000;
}
}
return first;
}
/**
* Returns the code point index within the calling String object of the first occurrence
* of the specified value, or -1 if not found.
*
* @param {string} searchValue The value to search for
* @param {number} [fromIndex] Optional code point index to start searching from
* @return {number} The code point index of the specified search value
*/
String.prototype.kmwIndexOf = function(searchValue, fromIndex) {
var str = String(this);
var codeUnitIndex = str.indexOf(searchValue, fromIndex);
if(codeUnitIndex < 0) {
return codeUnitIndex;
}
var codePointIndex = 0;
for(var i = 0; i !== null && i < codeUnitIndex; i = str.kmwNextChar(i)) codePointIndex++;
return codePointIndex;
}
/**
* Returns the code point index within the calling String object of the last occurrence
* of the specified value, or -1 if not found.
*
* @param {string} searchValue The value to search for
* @param {number} fromIndex Optional code point index to start searching from
* @return {number} The code point index of the specified search value
*/
String.prototype.kmwLastIndexOf = function(searchValue, fromIndex)
{
var str = String(this);
var codeUnitIndex = str.lastIndexOf(searchValue, fromIndex);
if(codeUnitIndex < 0) {
return codeUnitIndex;
}
var codePointIndex = 0;
for(var i = 0; i !== null && i < codeUnitIndex; i = str.kmwNextChar(i)) codePointIndex++;
return codePointIndex;
}
/**
* Returns the length of the string in code points, as opposed to code units.
*
* @return {number} The length of the string in code points
*/
String.prototype.kmwLength = function() {
var str = String(this);
if(str.length == 0) return 0;
for(var i = 0, codeUnitIndex = 0; codeUnitIndex !== null; i++)
codeUnitIndex = str.kmwNextChar(codeUnitIndex);
return i;
}
/**
* Extracts a section of a string and returns a new string.
*
* @param {number} beginSlice The start code point index in the string to
* extract from
* @param {number} endSlice Optional end code point index in the string
* to extract to
* @return {string} The substring as selected by beginSlice and
* endSlice
*/
String.prototype.kmwSlice = function(beginSlice, endSlice) {
var str = String(this);
var beginSliceCodeUnit = str.kmwCodePointToCodeUnit(beginSlice);
var endSliceCodeUnit = str.kmwCodePointToCodeUnit(endSlice);
if(beginSliceCodeUnit === null || endSliceCodeUnit === null)
return '';
else
return str.slice(beginSliceCodeUnit, endSliceCodeUnit);
}
/**
* Returns the characters in a string beginning at the specified location through
* the specified number of characters.
*
* @param {number} start The start code point index in the string to
* extract from
* @param {number} length Optional length to extract
* @return {string} The substring as selected by start and length
*/
String.prototype.kmwSubstr = function(start, length)
{
var str = String(this);
if(start < 0)
{
start = str.kmwLength() + start;
}
if(start < 0) start = 0;
var startCodeUnit = str.kmwCodePointToCodeUnit(start);
var endCodeUnit = startCodeUnit;
if(startCodeUnit === null) return '';
if(arguments.length < 2) {
endCodeUnit = str.length;
} else {
for(var i = 0; i < length; i++) endCodeUnit = str.kmwNextChar(endCodeUnit);
}
if(endCodeUnit === null)
return str.substring(startCodeUnit);
else
return str.substring(startCodeUnit, endCodeUnit);
}
/**
* Returns the characters in a string between two indexes into the string.
*
* @param {number} indexA The start code point index in the string to
* extract from
* @param {number} indexB The end code point index in the string to
* extract to
* @return {string} The substring as selected by indexA and indexB
*/
String.prototype.kmwSubstring = function(indexA, indexB)
{
var str = String(this),indexACodeUnit,indexBCodeUnit;
if(typeof(indexB) == 'undefined')
{
indexACodeUnit = str.kmwCodePointToCodeUnit(indexA);
indexBCodeUnit = str.length;
}
else
{
if(indexA > indexB) { var c = indexA; indexA = indexB; indexB = c; }
indexACodeUnit = str.kmwCodePointToCodeUnit(indexA);
indexBCodeUnit = str.kmwCodePointToCodeUnit(indexB);
}
if(isNaN(indexACodeUnit) || indexACodeUnit === null) indexACodeUnit = 0;
if(isNaN(indexBCodeUnit) || indexBCodeUnit === null) indexBCodeUnit = str.length;
return str.substring(indexACodeUnit, indexBCodeUnit);
}
/*
Helper functions
*/
/**
* Returns the code unit index for the next code point in the string, accounting for
* supplementary pairs
*
* @param {number|null} codeUnitIndex The code unit position to increment
* @return {number|null} The index of the next code point in the string,
* in code units
*/
String.prototype.kmwNextChar = function(codeUnitIndex) {
var str = String(this);
if(codeUnitIndex === null || codeUnitIndex < 0 || codeUnitIndex >= str.length - 1) {
return null;
}
var first = str.charCodeAt(codeUnitIndex);
if (first >= 0xD800 && first <= 0xDBFF && str.length > codeUnitIndex + 1) {
var second = str.charCodeAt(codeUnitIndex + 1);
if (second >= 0xDC00 && second <= 0xDFFF) {
if(codeUnitIndex == str.length - 2) {
return null;
}
return codeUnitIndex + 2;
}
}
return codeUnitIndex + 1;
}
/**
* Returns the code unit index for the previous code point in the string, accounting
* for supplementary pairs
*
* @param {number|null} codeUnitIndex The code unit position to decrement
* @return {number|null} The index of the previous code point in the
* string, in code units
*/
String.prototype.kmwPrevChar = function(codeUnitIndex) {
var str = String(this);
if(codeUnitIndex == null || codeUnitIndex <= 0 || codeUnitIndex > str.length) {
return null;
}
var second = str.charCodeAt(codeUnitIndex - 1);
if (second >= 0xDC00 && second <= 0xDFFF && codeUnitIndex > 1) {
var first = str.charCodeAt(codeUnitIndex - 2);
if(first >= 0xD800 && first <= 0xDBFF) {
return codeUnitIndex - 2;
}
}
return codeUnitIndex - 1;
}
/**
* Returns the corresponding code unit index to the code point index passed
*
* @param {number|null} codePointIndex A code point index in the string
* @return {number|null} The corresponding code unit index
*/
String.prototype.kmwCodePointToCodeUnit = function(codePointIndex) {
if(codePointIndex === null) return null;
var str = String(this);
var codeUnitIndex = 0;
if(codePointIndex < 0) {
codeUnitIndex = str.length;
for(var i = 0; i > codePointIndex; i--)
codeUnitIndex = str.kmwPrevChar(codeUnitIndex);
return codeUnitIndex;
}
if(codePointIndex == str.kmwLength()) return str.length;
for(var i = 0; i < codePointIndex; i++)
codeUnitIndex = str.kmwNextChar(codeUnitIndex);
return codeUnitIndex;
}
/**
* Returns the corresponding code point index to the code unit index passed
*
* @param {number|null} codeUnitIndex A code unit index in the string
* @return {number|null} The corresponding code point index
*/
String.prototype.kmwCodeUnitToCodePoint = function(codeUnitIndex) {
var str = String(this);
if(codeUnitIndex === null)
return null;
else if(codeUnitIndex == 0)
return 0;
else if(codeUnitIndex < 0)
return str.substr(codeUnitIndex).kmwLength();
else
return str.substr(0,codeUnitIndex).kmwLength();
}
/**
* Returns the character at a the code point index passed
*
* @param {number} codePointIndex A code point index in the string
* @return {string} The corresponding character
*/
String.prototype.kmwCharAt = function(codePointIndex) {
var str = String(this);
if(codePointIndex >= 0) return str.kmwSubstr(codePointIndex,1); else return '';
}
/**
* String prototype library extensions for basic plane characters,
* to simplify enabling or disabling supplementary plane functionality (I3319)
*/
/**
* Returns the code unit index for the next code point in the string
*
* @param {number} codeUnitIndex A code point index in the string
* @return {number|null} The corresponding character
*/
String.prototype.kmwBMPNextChar = function(codeUnitIndex)
{
var str = String(this);
if(codeUnitIndex < 0 || codeUnitIndex >= str.length - 1) {
return null;
}
return codeUnitIndex + 1;
}
/**
* Returns the code unit index for the previous code point in the string
*
* @param {number} codeUnitIndex A code unit index in the string
* @return {number|null} The corresponding character
*/
String.prototype.kmwBMPPrevChar = function(codeUnitIndex)
{
var str = String(this);
if(codeUnitIndex <= 0 || codeUnitIndex > str.length) {
return null;
}
return codeUnitIndex - 1;
}
/**
* Returns the code unit index for a code point index
*
* @param {number} codePointIndex A code point index in the string
* @return {number} The corresponding character
*/
String.prototype.kmwBMPCodePointToCodeUnit = function(codePointIndex)
{
return codePointIndex;
}
/**
* Returns the code point index for a code unit index
*
* @param {number} codeUnitIndex A code point index in the string
* @return {number} The corresponding character
*/
String.prototype.kmwBMPCodeUnitToCodePoint = function(codeUnitIndex)
{
return codeUnitIndex;
}
/**
* Returns the length of a BMP string
*
* @return {number} The length in code points
*/
String.prototype.kmwBMPLength = function()
{
var str = String(this);
return str.length;
}
/**
* Returns a substring
*
* @param {number} n
* @param {number} ln
* @return {string}
*/
String.prototype.kmwBMPSubstr = function(n,ln)
{
var str=String(this);
if(n > -1)
return str.substr(n,ln);
else
return str.substr(str.length+n,-n);
}
/**
* Enable or disable supplementary plane string handling
*
* @param {boolean} bEnable
*/
String.kmwEnableSupplementaryPlane = function(bEnable)
{
var p=String.prototype;
String._kmwFromCharCode = bEnable ? String.kmwFromCharCode : String.fromCharCode;
p._kmwCharAt = bEnable ? p.kmwCharAt : p.charAt;
p._kmwCharCodeAt = bEnable ? p.kmwCharCodeAt : p.charCodeAt;
p._kmwIndexOf = bEnable ? p.kmwIndexOf :p.indexOf;
p._kmwLastIndexOf = bEnable ? p.kmwLastIndexOf : p.lastIndexOf ;
p._kmwSlice = bEnable ? p.kmwSlice : p.slice;
p._kmwSubstring = bEnable ? p.kmwSubstring : p.substring;
p._kmwSubstr = bEnable ? p.kmwSubstr : p.kmwBMPSubstr;
p._kmwLength = bEnable ? p.kmwLength : p.kmwBMPLength;
p._kmwNextChar = bEnable ? p.kmwNextChar : p.kmwBMPNextChar;
p._kmwPrevChar = bEnable ? p.kmwPrevChar : p.kmwBMPPrevChar;
p._kmwCodePointToCodeUnit = bEnable ? p.kmwCodePointToCodeUnit : p.kmwBMPCodePointToCodeUnit;
p._kmwCodeUnitToCodePoint = bEnable ? p.kmwCodeUnitToCodePoint : p.kmwBMPCodeUnitToCodePoint;
}