mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-07 05:13:40 +01:00
602270d178
* MIT license headers * various tweaks - Tweak jsdoc headers a bit - Use an old-school format with SPDX-License-Identifier for SCSS - Add headers to tgui dmcode * Simplify the license section * Rebuild tgui, small tweaks Co-authored-by: ZeWaka <zewakagamer@gmail.com> About The Pull Request All relevant source code now contains copyright headers, that explicitly assert copyright and license for every file. This has been done to prepare TGUI for wider adoption. Goon station devs are interested in using TGUI, and hopefully this will result in a nice collaboration and improvements to both codebases. The following files were relicensed under MIT: code/controllers/subsystem/tgui.dm code/modules/tgui/*.dm tgui/**/*.js tgui/**/*.scss The following files were kept untouched: tgui/packages/tgui/interfaces/**/*.js tgui/packages/tgui/styles/interfaces/**/*.scss Project is still basically AGPL-3.0 under /tg/station's parent license (with added MIT texts), but allows importing MIT code into MIT-compatible codebases.
163 lines
4.0 KiB
JavaScript
163 lines
4.0 KiB
JavaScript
/**
|
|
* @file
|
|
* @copyright 2020 Aleksej Komarov
|
|
* @license MIT
|
|
*/
|
|
|
|
/**
|
|
* Removes excess whitespace and indentation from the string.
|
|
*/
|
|
export const multiline = str => {
|
|
if (Array.isArray(str)) {
|
|
// Small stub to allow usage as a template tag
|
|
return multiline(str.join(''));
|
|
}
|
|
const lines = str.split('\n');
|
|
// Determine base indentation
|
|
let minIndent;
|
|
for (let line of lines) {
|
|
for (let indent = 0; indent < line.length; indent++) {
|
|
const char = line[indent];
|
|
if (char !== ' ') {
|
|
if (minIndent === undefined || indent < minIndent) {
|
|
minIndent = indent;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!minIndent) {
|
|
minIndent = 0;
|
|
}
|
|
// Remove this base indentation and trim the resulting string
|
|
// from both ends.
|
|
return lines
|
|
.map(line => line.substr(minIndent).trimRight())
|
|
.join('\n')
|
|
.trim();
|
|
};
|
|
|
|
/**
|
|
* Creates a glob pattern matcher.
|
|
*
|
|
* Matches strings with wildcards.
|
|
*
|
|
* Example: createGlobPattern('*@domain')('user@domain') === true
|
|
*/
|
|
export const createGlobPattern = pattern => {
|
|
const escapeString = str => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
|
|
const regex = new RegExp('^'
|
|
+ pattern.split(/\*+/).map(escapeString).join('.*')
|
|
+ '$');
|
|
return str => regex.test(str);
|
|
};
|
|
|
|
/**
|
|
* Creates a search terms matcher.
|
|
*
|
|
* Returns true if given string matches the search text.
|
|
*
|
|
* @template T
|
|
* @param {string} searchText
|
|
* @param {(obj: T) => string} stringifier
|
|
* @returns {(obj: T) => boolean}
|
|
*/
|
|
export const createSearch = (searchText, stringifier) => {
|
|
const preparedSearchText = searchText.toLowerCase().trim();
|
|
return obj => {
|
|
if (!preparedSearchText) {
|
|
return true;
|
|
}
|
|
const str = stringifier ? stringifier(obj) : obj;
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
return str
|
|
.toLowerCase()
|
|
.includes(preparedSearchText);
|
|
};
|
|
};
|
|
|
|
export const capitalize = str => {
|
|
// Handle array
|
|
if (Array.isArray(str)) {
|
|
return str.map(capitalize);
|
|
}
|
|
// Handle string
|
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
};
|
|
|
|
export const toTitleCase = str => {
|
|
// Handle array
|
|
if (Array.isArray(str)) {
|
|
return str.map(toTitleCase);
|
|
}
|
|
// Pass non-string
|
|
if (typeof str !== 'string') {
|
|
return str;
|
|
}
|
|
// Handle string
|
|
const WORDS_UPPER = ['Id', 'Tv'];
|
|
const WORDS_LOWER = [
|
|
'A', 'An', 'And', 'As', 'At', 'But', 'By', 'For', 'For', 'From', 'In',
|
|
'Into', 'Near', 'Nor', 'Of', 'On', 'Onto', 'Or', 'The', 'To', 'With',
|
|
];
|
|
let currentStr = str.replace(/([^\W_]+[^\s-]*) */g, str => {
|
|
return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
|
|
});
|
|
for (let word of WORDS_LOWER) {
|
|
const regex = new RegExp('\\s' + word + '\\s', 'g');
|
|
currentStr = currentStr.replace(regex, str => str.toLowerCase());
|
|
}
|
|
for (let word of WORDS_UPPER) {
|
|
const regex = new RegExp('\\b' + word + '\\b', 'g');
|
|
currentStr = currentStr.replace(regex, str => str.toLowerCase());
|
|
}
|
|
return currentStr;
|
|
};
|
|
|
|
/**
|
|
* Decodes HTML entities, and removes unnecessary HTML tags.
|
|
*
|
|
* @param {String} str Encoded HTML string
|
|
* @return {String} Decoded HTML string
|
|
*/
|
|
export const decodeHtmlEntities = str => {
|
|
if (!str) {
|
|
return str;
|
|
}
|
|
const translate_re = /&(nbsp|amp|quot|lt|gt|apos);/g;
|
|
const translate = {
|
|
nbsp: ' ',
|
|
amp: '&',
|
|
quot: '"',
|
|
lt: '<',
|
|
gt: '>',
|
|
apos: '\'',
|
|
};
|
|
return str
|
|
// Newline tags
|
|
.replace(/<br>/gi, '\n')
|
|
.replace(/<\/?[a-z0-9-_]+[^>]*>/gi, '')
|
|
// Basic entities
|
|
.replace(translate_re, (match, entity) => translate[entity])
|
|
// Decimal entities
|
|
.replace(/&#?([0-9]+);/gi, (match, numStr) => {
|
|
const num = parseInt(numStr, 10);
|
|
return String.fromCharCode(num);
|
|
})
|
|
// Hex entities
|
|
.replace(/&#x?([0-9a-f]+);/gi, (match, numStr) => {
|
|
const num = parseInt(numStr, 16);
|
|
return String.fromCharCode(num);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Converts an object into a query string,
|
|
*/
|
|
export const buildQueryString = obj => Object.keys(obj)
|
|
.map(key => encodeURIComponent(key)
|
|
+ '=' + encodeURIComponent(obj[key]))
|
|
.join('&');
|