react-saasify-chrisvxd
Version:
React components for Saasify web clients.
150 lines (112 loc) • 3.82 kB
text/coffeescript
{CR,LF,Control,Extend,Regional_Indicator,SpacingMark,L,V,T,LV,LVT} = require './classes.json'
UnicodeTrie = require 'unicode-trie'
fs = require 'fs'
classTrie = new UnicodeTrie fs.readFileSync __dirname + '/classes.trie'
# Gets a code point from a UTF-16 string
# handling surrogate pairs appropriately
codePointAt = (str, idx) ->
idx = idx or 0
code = str.charCodeAt(idx)
# High surrogate
if 0xD800 <= code <= 0xDBFF
hi = code
low = str.charCodeAt(idx + 1)
if 0xDC00 <= low <= 0xDFFF
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
return hi
# Low surrogate
if 0xDC00 <= code <= 0xDFFF
hi = str.charCodeAt(idx - 1)
low = code
if 0xD800 <= hi <= 0xDBFF
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
return low
return code
# Returns whether a break is allowed between the
# two given grapheme breaking classes
shouldBreak = (previous, current) ->
# GB3. CR X LF
if previous is CR and current is LF
return false
# GB4. (Control|CR|LF) ÷
else if previous in [Control, CR, LF]
return true
# GB5. ÷ (Control|CR|LF)
else if current in [Control, CR, LF]
return true
# GB6. L X (L|V|LV|LVT)
else if previous is L and current in [L, V, LV, LVT]
return false
# GB7. (LV|V) X (V|T)
else if previous in [LV, V] and current in [V, T]
return false
# GB8. (LVT|T) X (T)
else if previous in [LVT, T] and current is T
return false
# GB8a. Regional_Indicator X Regional_Indicator
else if previous is Regional_Indicator and current is Regional_Indicator
return false
# GB9. X Extend
else if current is Extend
return false
# GB9a. X SpacingMark
else if current is SpacingMark
return false
# GB9b. Prepend X (there are currently no characters with this class)
# else if previous is Prepend
# return false
# GB10. Any ÷ Any
return true
# Returns the next grapheme break in the string after the given index
exports.nextBreak = (string, index = 0) ->
if index < 0
return 0
if index >= string.length - 1
return string.length
prev = classTrie.get codePointAt(string, index)
for i in [index + 1...string.length] by 1
# check for already processed low surrogates
continue if 0xd800 <= string.charCodeAt(i - 1) <= 0xdbff and
0xdc00 <= string.charCodeAt(i) <= 0xdfff
next = classTrie.get codePointAt(string, i)
if shouldBreak prev, next
return i
prev = next
return string.length
# Returns the next grapheme break in the string before the given index
exports.previousBreak = (string, index = string.length) ->
if index > string.length
return string.length
if index <= 1
return 0
index--
next = classTrie.get codePointAt(string, index)
for i in [index - 1..0] by -1
# check for already processed high surrogates
continue if 0xd800 <= string.charCodeAt(i) <= 0xdbff and
0xdc00 <= string.charCodeAt(i + 1) <= 0xdfff
prev = classTrie.get codePointAt(string, i)
if shouldBreak prev, next
return i + 1
next = prev
return 0
# Breaks the given string into an array of grapheme cluster strings
exports.break = (str) ->
res = []
index = 0
while (brk = exports.nextBreak(str, index)) < str.length
res.push str.slice(index, brk)
index = brk
if index < str.length
res.push str.slice(index)
return res
# Returns the number of grapheme clusters there are in the given string
exports.countBreaks = (str) ->
count = 0
index = 0
while (brk = exports.nextBreak(str, index)) < str.length
index = brk
count++
if index < str.length
count++
return count