epiphany/node_modules/bcp-47-match/index.js
2023-12-09 22:19:03 -08:00

142 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict'
// See https://tools.ietf.org/html/rfc4647#section-3.1
// for more information on the algorithms.
exports.basicFilter = factory(basic, true)
exports.extendedFilter = factory(extended, true)
exports.lookup = factory(lookup)
// Basic Filtering (Section 3.3.1) matches a language priority list consisting
// of basic language ranges (Section 2.1) to sets of language tags.
function basic(tag, range) {
return range === '*' || tag === range || tag.indexOf(range + '-') > -1
}
// Extended Filtering (Section 3.3.2) matches a language priority list
// consisting of extended language ranges (Section 2.2) to sets of language
// tags.
function extended(tag, range) {
// 3.3.2.1
var left = tag.split('-')
var right = range.split('-')
var leftIndex = 0
var rightIndex = 0
// 3.3.2.2
if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) {
return false
}
leftIndex++
rightIndex++
// 3.3.2.3
while (rightIndex < right.length) {
// 3.3.2.3.A
if (right[rightIndex] === '*') {
rightIndex++
continue
}
// 3.3.2.3.B
if (!left[leftIndex]) return false
// 3.3.2.3.C
if (left[leftIndex] === right[rightIndex]) {
leftIndex++
rightIndex++
continue
}
// 3.3.2.3.D
if (left[leftIndex].length === 1) return false
// 3.3.2.3.E
leftIndex++
}
// 3.3.2.4
return true
}
// Lookup (Section 3.4) matches a language priority list consisting of basic
// language ranges to sets of language tags to find the one exact language tag
// that best matches the range.
function lookup(tag, range) {
var right = range
var index
/* eslint-disable-next-line no-constant-condition */
while (true) {
if (right === '*' || tag === right) return true
index = right.lastIndexOf('-')
if (index < 0) return false
if (right.charAt(index - 2) === '-') index -= 2
right = right.slice(0, index)
}
}
// Factory to perform a filter or a lookup.
// This factory creates a function that accepts a list of tags and a list of
// ranges, and contains logic to exit early for lookups.
// `check` just has to deal with one tag and one range.
// This match function iterates over ranges, and for each range,
// iterates over tags. That way, earlier ranges matching any tag have
// precedence over later ranges.
function factory(check, filter) {
return match
function match(tags, ranges) {
var left = cast(tags, 'tag')
var right = cast(ranges == null ? '*' : ranges, 'range')
var matches = []
var rightIndex = -1
var range
var leftIndex
var next
while (++rightIndex < right.length) {
range = right[rightIndex].toLowerCase()
// Ignore wildcards in lookup mode.
if (!filter && range === '*') continue
leftIndex = -1
next = []
while (++leftIndex < left.length) {
if (check(left[leftIndex].toLowerCase(), range)) {
// Exit if this is a lookup and we have a match.
if (!filter) return left[leftIndex]
matches.push(left[leftIndex])
} else {
next.push(left[leftIndex])
}
}
left = next
}
// If this is a filter, return the list. If its a lookup, we didnt find
// a match, so return `undefined`.
return filter ? matches : undefined
}
}
// Validate tags or ranges, and cast them to arrays.
function cast(values, name) {
var value = values && typeof values === 'string' ? [values] : values
if (!value || typeof value !== 'object' || !('length' in value)) {
throw new Error(
'Invalid ' + name + ' `' + value + '`, expected non-empty string'
)
}
return value
}