Sindbad~EG File Manager
'use strict';
const Hoek = require('@hapi/hoek');
const Boom = require('@hapi/boom');
const internals = {};
exports.selection = function (header, preferences, options) {
const selections = exports.selections(header, preferences, options);
return selections.length ? selections[0] : '';
};
exports.selections = function (header, preferences, options) {
Hoek.assert(!preferences || Array.isArray(preferences), 'Preferences must be an array');
return internals.parse(header || '', preferences, options);
};
// RFC 7231 Section 5.3.3 (https://tools.ietf.org/html/rfc7231#section-5.3.3)
//
// Accept-Charset = *( "," OWS ) ( ( charset / "*" ) [ weight ] ) *( OWS "," [ OWS ( ( charset / "*" ) [ weight ] ) ] )
// charset = token
//
// Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
// RFC 7231 Section 5.3.4 (https://tools.ietf.org/html/rfc7231#section-5.3.4)
//
// Accept-Encoding = [ ( "," / ( codings [ weight ] ) ) *( OWS "," [ OWS ( codings [ weight ] ) ] ) ]
// codings = content-coding / "identity" / "*"
// content-coding = token
//
// Accept-Encoding: compress, gzip
// Accept-Encoding:
// Accept-Encoding: *
// Accept-Encoding: compress;q=0.5, gzip;q=1.0
// Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
// RFC 7231 Section 5.3.5 (https://tools.ietf.org/html/rfc7231#section-5.3.5)
//
// Accept-Language = *( "," OWS ) ( language-range [ weight ] ) *( OWS "," [ OWS ( language-range [ weight ] ) ] )
// language-range = ( 1*8ALPHA *( "-" 1*8alphanum ) ) / "*" ; [RFC4647], Section 2.1
// alphanum = ALPHA / DIGIT
//
// Accept-Language: da, en-gb;q=0.8, en;q=0.7
// token = 1*tchar
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
// / DIGIT / ALPHA
// ; any VCHAR, except delimiters
// OWS = *( SP / HTAB )
// RFC 7231 Section 5.3.1 (https://tools.ietf.org/html/rfc7231#section-5.3.1)
//
// The weight is normalized to a real number in the range 0 through 1,
// where 0.001 is the least preferred and 1 is the most preferred; a
// value of 0 means "not acceptable". If no "q" parameter is present,
// the default weight is 1.
//
// weight = OWS ";" OWS "q=" qvalue
// qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
internals.parse = function (raw, preferences, options) {
// Normalize header (remove spaces and tabs)
const header = raw.replace(/[ \t]/g, '');
// Normalize preferences
const lowers = new Map();
if (preferences) {
let pos = 0;
for (const preference of preferences) {
const lower = preference.toLowerCase();
lowers.set(lower, { orig: preference, pos: pos++ });
if (options.prefixMatch) {
const parts = lower.split('-');
while (parts.pop(), parts.length > 0) {
const joined = parts.join('-');
if (!lowers.has(joined)) {
lowers.set(joined, { orig: preference, pos: pos++ });
}
}
}
}
}
// Parse selections
const parts = header.split(',');
const selections = [];
const map = new Set();
for (let i = 0; i < parts.length; ++i) {
const part = parts[i];
if (!part) { // Ignore empty parts or leading commas
continue;
}
// Parse parameters
const params = part.split(';');
if (params.length > 2) {
throw Boom.badRequest(`Invalid ${options.type} header`);
}
let token = params[0].toLowerCase();
if (!token) {
throw Boom.badRequest(`Invalid ${options.type} header`);
}
if (options.equivalents?.has(token)) {
token = options.equivalents.get(token);
}
const selection = {
token,
pos: i,
q: 1
};
if (preferences &&
lowers.has(token)) {
selection.pref = lowers.get(token).pos;
}
map.add(selection.token);
// Parse q=value
if (params.length === 2) {
const q = params[1];
const [key, value] = q.split('=');
if (!value ||
key !== 'q' && key !== 'Q') {
throw Boom.badRequest(`Invalid ${options.type} header`);
}
const score = parseFloat(value);
if (score === 0) {
continue;
}
if (Number.isFinite(score) &&
score <= 1 &&
score >= 0.001) {
selection.q = score;
}
}
selections.push(selection); // Only add allowed selections (q !== 0)
}
// Sort selection based on q and then position in header
selections.sort(internals.sort);
// Extract tokens
const values = selections.map((selection) => selection.token);
if (options.default &&
!map.has(options.default)) {
values.push(options.default);
}
if (!preferences?.length) {
return values;
}
const preferred = [];
for (const selection of values) {
if (selection === '*') {
for (const [preference, value] of lowers) {
if (!map.has(preference)) {
preferred.push(value.orig);
}
}
}
else {
const lower = selection.toLowerCase();
if (lowers.has(lower)) {
preferred.push(lowers.get(lower).orig);
}
}
}
return preferred;
};
internals.sort = function (a, b) {
const aFirst = -1;
const bFirst = 1;
if (b.q !== a.q) {
return b.q - a.q;
}
if (b.pref !== a.pref) {
if (a.pref === undefined) {
return bFirst;
}
if (b.pref === undefined) {
return aFirst;
}
return a.pref - b.pref;
}
return a.pos - b.pos;
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists