Merge pull request #2 from MozillaSecurity/pyoor-dev

Major rework of text and numeric generators
This commit is contained in:
pyoor 2018-08-20 13:00:00 -04:00 committed by GitHub
commit 93b18cd32c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 486 additions and 206 deletions

View file

@ -6,13 +6,18 @@ const make = require('../make')
const random = require('../random') const random = require('../random')
class arrays extends make { class arrays extends make {
static filledArray (fn, limit) { /**
let array = [] * Returns an array containing random values generated by the supplied function
let size = limit || random.number(make.number.tiny()) + 1 * @param {Function} fn - Function used to generate values
* @param {number} limit - Length of the array
* @returns {Array}
*/
static filledArray (fn, limit = make.number.tiny()) {
const array = []
for (let i = 0; i < size; i++) { for (let i = 0; i < limit; i++) {
let value = random.pick(fn) const value = random.pick(fn)
if (value !== undefined) { if (value !== null) {
array.push(value) array.push(value)
} }
} }

View file

@ -141,7 +141,7 @@ class network extends make {
} }
static dtmf () { static dtmf () {
let count = make.number.range() let count = make.number.tiny()
const values = [] const values = []
while (count--) { while (count--) {
values.push(random.item(['*', '#', 'A', 'B', 'C', 'D', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])) values.push(random.item(['*', '#', 'A', 'B', 'C', 'D', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']))

View file

@ -6,40 +6,141 @@ const make = require('../make')
const random = require('../random') const random = require('../random')
class number extends make { class number extends make {
static bool () { /**
return random.bool() * Returns a number that is more likely to exceed the supplied boundary
* @param value {number}
* @private
*/
static _exceed (value) {
switch (random.number(4)) {
case 0:
// Divisions
return Math.ceil(value / random.range(2, 4))
case 1:
// Powers
const offset = Math.pow(2, random.range(1, 7))
return (value > 0) ? (value - offset) : value + offset
default:
// Slightly less than limit
return (value > 0) ? (value - random.number(3)) : value + random.number(3)
}
} }
/**
* Returns a int8 [-128, 127]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {number}
*/
static int8 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(random.choose([
[1, -128],
[10, 127]
]))
}
return random.range(-128, 127)
}
/**
* Returns a uint8 [0, 255]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {number}
*/
static uint8 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(255)
}
return random.range(0, 255)
}
/**
* Returns a int16 [-32768, 32767]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {number}
*/
static int16 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(random.choose([
[1, -32768],
[10, 32767]
]))
}
return random.range(-32768, 32767)
}
/**
* Returns a uint16 [0, 65535]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {*}
*/
static uint16 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(65535)
}
return random.range(-0, 65535)
}
/**
* Returns a int32 [-2147483648, 2147483647]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {number}
*/
static int32 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(random.choose([
[1, -2147483648],
[10, 2147483647]
]))
}
return random.range(-2147483648, 2147483647)
}
/**
* Returns a uint32 [0, 4294967295]
* @param bypass {boolean} - Determines if the range should be exceeded
* @returns {number}
*/
static uint32 (bypass = false) {
if (bypass || random.chance(50)) {
return number._exceed(4294967295)
}
return random.range(0, 4294967295)
}
/**
* Returns a random floating point number
* @returns {number}
*/
static float () { static float () {
let n /* if (random.chance(32)) {
if (random.chance(32)) {
switch (random.number(4)) { switch (random.number(4)) {
case 0: case 0:
n = random.range(Number.MIN_VALUE, Number.MAX_VALUE) return random.range(Number.MIN_VALUE, Number.MAX_VALUE)
break
case 1: case 1:
n = Math.pow(10, 1) / Math.pow(10, random.number(307)) return Math.pow(10, 1) / Math.pow(10, random.number(307))
break
case 2: case 2:
n = Math.pow(2, random.float() * random.float() * 64) return Math.pow(2, random.float() * random.float() * 64)
break
case 3: case 3:
n = Math.pow(10, random.range(1, 9)) / Math.pow(10, random.range(1, 9)) return Math.pow(10, random.range(1, 9)) / Math.pow(10, random.range(1, 9))
break
} }
return n } */
}
switch (random.number(6)) { return random.float()
default:
n = random.float()
}
return n
}
static range () {
return random.pick([1, 2, 3, 4, 6, 8, 16, 32, 64, number.tiny])
} }
/**
* Returns a float value within the supplied range
* @param {number} min - Start value
* @param {number} max - End value
* @param {number} precision
* @returns {number}
*/
static frange (min, max, precision) { static frange (min, max, precision) {
let x = Math.random() * (min - max) + max let x = Math.random() * (min - max) + max
if (precision) { if (precision) {
@ -49,36 +150,70 @@ class number extends make {
return x return x
} }
/**
* Returns a random power of 2 between 1 and 2048
* @returns {number}
*/
static tiny () { static tiny () {
return Math.pow(2, random.number(12)) // Calling random.number twice prefers lower values
} return Math.pow(2, random.number(random.number(13)))
static unsigned () {
if (random.chance(2)) {
return Math.abs(number.any())
}
return Math.pow(2, random.number(random.number(65))) + random.number(3) - 1
} }
/**
* Returns a random number adjacent to the supplied number
* @param {number} number
* @returns {number}
*/
static even (number) { static even (number) {
return number % 2 === 1 ? ++number : number return number % 2 === 1 ? ++number : number
} }
/**
* Returns a random number that may be interesting
* @returns {number}
*/
static interesting () { static interesting () {
return random.choose([ return random.choose([
[10, [-128, -1, 0, 1, 16, 32, 64, 100, 127]], [50, [-128, -1, 0, 1, 16, 32, 64, 100, 127]],
[7, [-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767]], [30, [-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767]],
[1, [-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647]] [1, [-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647]]
]) ])
} }
static any () { /**
let value = random.choose([ * Returns a random signed number
[1, number.float], * @returns {number}
[1, number.unsigned], */
[1, [number.range, number.tiny]] static signed () {
return random.choose([
[50, [number.int8]],
[30, [number.int16]],
[1, [number.int32]]
])
}
/**
* Returns a random unsigned number
* @returns {number}
*/
static unsigned () {
return random.choose([
[50, [number.uint8]],
[30, [number.uint16]],
[1, [number.uint32]]
])
}
/**
* Returns a random number using the type base number generators above
* @returns {number}
*/
static any () {
return random.choose([
[50, [number.int8, number.uint8]],
[30, [number.int16, number.uint16]],
[1, [number.int32, number.uint32]]
]) ])
return random.chance(10) ? -value : value
} }
} }

View file

@ -7,18 +7,66 @@ const make = require('../make')
const utils = require('../utils') const utils = require('../utils')
class text extends make { class text extends make {
static lineEnd () { /**
* Generate a random alphabetic character
*/
static alpha () {
return String.fromCharCode(random.range('A'.charCodeAt(0), 'z'.charCodeAt(0)))
}
/**
* Generate a random alphanumeric character
*/
static alphanum () {
return String.fromCharCode(random.range('0'.charCodeAt(0), 'z'.charCodeAt(0)))
}
/**
* Generate a random assignment operator
*/
static assignmentOperator () {
return random.pick([ return random.pick([
'\n', '\r', '\r\n', '\n\r' '=', '+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
]) ])
} }
/**
* Generate a random arithmetic operator
*/
static arithmeticOperator () {
return random.pick([
'%', '-', '+', '*', '/'
])
}
/**
* Generate a random control character
*/
static controlChar () { static controlChar () {
return random.pick([ return random.pick([
'\b', '\t', '\n', '\v', '\f', '\r', '\0', '\c', '\a', '\e' // eslint-disable-line no-useless-escape '\b', '\t', '\n', '\v', '\f', '\r', '\0', '\c', '\a', '\e' // eslint-disable-line no-useless-escape
]) ])
} }
/**
* Generate a random digit
*/
static digit () {
return String.fromCharCode(random.range('0'.charCodeAt(0), '9'.charCodeAt(0)))
}
/**
* Generate a random line ending
*/
static lineEnd () {
return random.pick([
'\n', '\r', '\r\n', '\n\r'
])
}
/**
* Generate a random token
*/
static token () { static token () {
return random.pick([ return random.pick([
'*', '+', '%', '-', '!', '^', ':', '|', '&', '<', '>', '.', '"', '*', '+', '%', '-', '!', '^', ':', '|', '&', '<', '>', '.', '"',
@ -35,9 +83,9 @@ class text extends make {
static language () { static language () {
return random.pick([ return random.pick([
// special casing for i, I, dotted/dotless variants // special casing for i, I, dotted/dotless variants
['tr', 'az'], ['tr', 'az', 'crh', 'tt', 'ba'],
// special casing rules: https://developer.mozilla.org/en/CSS/text-transform // special casing rules: https://developer.mozilla.org/en/CSS/text-transform
['nl', 'gr'], ['nl', 'el', 'ga'],
// special justification rules // special justification rules
['ja', 'zh'], ['ja', 'zh'],
// tend to be RTL // tend to be RTL
@ -49,155 +97,176 @@ class text extends make {
]) ])
} }
/**
* Generate a random character that may affect layout
*/
static layoutCharCodes () { static layoutCharCodes () {
return random.pick([ return String.fromCodePoint(
0, // null random.pick([
160, // non-breaking space 0, // null
0x005C, // backslash, but in some countries, represents local currency symbol (e.g. yen) 160, // non-breaking space
0x00AD, // soft hyphen 0x005C, // backslash, but in some countries, represents local currency symbol (e.g. yen)
0x0BCC, // a Tamil character that is displayed as three glyphs 0x00AD, // soft hyphen
// http://unicode.org/charts/PDF/U2000.pdf 0x0BCC, // a Tamil character that is displayed as three glyphs
0x200B, // zero-width space // http://unicode.org/charts/PDF/U2000.pdf
0x200C, // zero-width non-joiner 0x200B, // zero-width space
0x200D, // zero-width joiner 0x200C, // zero-width non-joiner
0x200E, // left-to-right mark 0x200D, // zero-width joiner
0x200F, // right-to-left mark 0x200E, // left-to-right mark
0x2011, // non-breaking hyphen 0x200F, // right-to-left mark
0x2027, // hyphenation point 0x2011, // non-breaking hyphen
0x2028, // line separator 0x2027, // hyphenation point
0x2029, // paragraph separator 0x2028, // line separator
0x202A, // left-to-right embedding 0x2029, // paragraph separator
0x202B, // right-to-left embedding 0x202A, // left-to-right embedding
0x202C, // pop directional formatting 0x202B, // right-to-left embedding
0x202D, // left-to-right override 0x202C, // pop directional formatting
0x202E, // right-to-left override 0x202D, // left-to-right override
0x202F, // narrow no-break space 0x202E, // right-to-left override
0x2060, // word joiner 0x202F, // narrow no-break space
0x2061, // function application (one of several invisible mathematical operators) 0x2060, // word joiner
// http://unicode.org/charts/PDF/U3000.pdf 0x2061, // function application (one of several invisible mathematical operators)
0x3000, // ideographic space (CJK) // http://unicode.org/charts/PDF/U3000.pdf
// http://unicode.org/charts/PDF/U0300.pdf 0x3000, // ideographic space (CJK)
0x0301, // combining acute accent (if it appears after "a", it turns into "a" with an accent) // http://unicode.org/charts/PDF/U0300.pdf
// Arabic has the interesting property that most letters connect to the next letter. 0x0301, // combining acute accent (if it appears after "a", it turns into "a" with an accent)
// Some code calls this "shaping". // Arabic has the interesting property that most letters connect to the next letter.
0x0643, // arabic letter kaf // Some code calls this "shaping".
0x0645, // arabic letter meem 0x0643, // arabic letter kaf
0x06CD, // arabic letter yeh with tail 0x0645, // arabic letter meem
0xFDDE, // invalid unicode? but somehow associated with arabic. 0x06CD, // arabic letter yeh with tail
// http://unicode.org/reports/tr36/tr36-7.html#Buffer_Overflows 0xFDDE, // invalid unicode? but somehow associated with arabic.
// Characters with especially high expansion factors when they go through various unicode "normalizations" // http://unicode.org/reports/tr36/tr36-7.html#Buffer_Overflows
0x1F82, // Characters with especially high expansion factors when they go through various unicode "normalizations"
0xFDFA, 0x1F82,
0xFB2C, 0xFDFA,
0x0390, 0xFB2C,
// 0x1D160, // hmm, need surrogates 0x0390,
// Characters with especially high expansion factors when lowercased or uppercased // 0x1D160, // hmm, need surrogates
0x023A, // Characters with especially high expansion factors when lowercased or uppercased
0x0041, 0x023A,
0xDC1D, // a low surrogate 0x0041,
0xDB00, // a high surrogate 0xDC1D, // a low surrogate
// UFFF0.pdf 0xDB00, // a high surrogate
0xFFF9, // interlinear annotation anchor // UFFF0.pdf
0xFFFA, // interlinear annotation seperator 0xFFF9, // interlinear annotation anchor
0xFFFB, // interlinear annotation terminator 0xFFFA, // interlinear annotation seperator
0xFFFC, // object replacement character 0xFFFB, // interlinear annotation terminator
0xFFFD, // replacement character 0xFFFC, // object replacement character
0xFEFF, // zero width no-break space 0xFFFD, // replacement character
0xFFFF, // not a character 0xFEFF, // zero width no-break space
0x00A0, // no-break space 0xFFFF, // not a character
0x2426, 0x00A0, // no-break space
0x003F, 0x2426,
0x00BF, 0x003F,
0xDC80, 0x00BF,
0xDCFF, 0xDC80,
// http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters 0xDCFF,
0x205F, // mathematical space // http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters
0x2061, // mathematical function application 0x205F, // mathematical space
0x2064, // mathematical invisible separator 0x2061, // mathematical function application
0x2044 // fraction slash character 0x2064, // mathematical invisible separator
]) 0x2044 // fraction slash character
])
)
} }
/**
* Generate a random character that affects bidi layout
*/
static bidiCharCodes () { static bidiCharCodes () {
return random.pick([ return String.fromCodePoint(
0x0660, // START_HINDI_DIGITS random.pick([
0x0669, // END_HINDI_DIGITS 0x0660, // START_HINDI_DIGITS
0x066A, // START_ARABIC_SEPARATOR 0x0669, // END_HINDI_DIGITS
0x066B, // END_ARABIC_SEPARATOR 0x066A, // START_ARABIC_SEPARATOR
0x0030, // START_ARABIC_DIGITS 0x066B, // END_ARABIC_SEPARATOR
0x0039, // END_ARABIC_DIGITS 0x0030, // START_ARABIC_DIGITS
0x06f0, // START_FARSI_DIGITS 0x0039, // END_ARABIC_DIGITS
0x06f9 // END_FARSI_DIGITS 0x06f0, // START_FARSI_DIGITS
]) 0x06f9 // END_FARSI_DIGITS
])
)
} }
// http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt /**
static unicodeCombiningCharacters () { * Generate a random unicode combining character
return random.item([ * http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt
[0x0300, 0x036F], // Combining Diacritical Marks */
[0x0483, 0x0489], static unicodeCombiningCharacter () {
[0x07EB, 0x07F3], return String.fromCodePoint(
[0x135D, 0x135F], random.range(
[0x1A7F, 0x1A7F], ...random.item([
[0x1B6B, 0x1B73], [0x0300, 0x036F], // Combining Diacritical Marks
[0x1DC0, 0x1DFF], // Combining Diacritical Marks Supplement [0x0483, 0x0489],
[0x20D0, 0x2DFF], [0x07EB, 0x07F3],
[0x3099, 0x309A], [0x135D, 0x135F],
[0xA66F, 0xA6F1], [0x1A7F, 0x1A7F],
[0xA8E0, 0xA8F1], [0x1B6B, 0x1B73],
[0xFE20, 0xFE26], // Combining Half Marks [0x1DC0, 0x1DFF], // Combining Diacritical Marks Supplement
[0x101FD, 0x101FD], [0x20D0, 0x2DFF],
[0x1D165, 0x1D169], [0x3099, 0x309A],
[0x1D16D, 0x1D172], [0xA66F, 0xA6F1],
[0x1D17B, 0x1D18B], [0xA8E0, 0xA8F1],
[0x1D1AA, 0x1D1AD], [0xFE20, 0xFE26], // Combining Half Marks
[0x1D242, 0x1D244] [0x101FD, 0x101FD],
]) [0x1D165, 0x1D169],
[0x1D16D, 0x1D172],
[0x1D17B, 0x1D18B],
[0x1D1AA, 0x1D1AD],
[0x1D242, 0x1D244]
])
)
)
} }
/**
* Generate a random basic multilingual plane character
*/
static unicodeBMP () { static unicodeBMP () {
return random.item([ return String.fromCodePoint(
// BMP = Basic Multilingual Plane random.range(0x0000, 0xFFFF)
[0x0000, 0xFFFF] )
])
} }
/**
* Generate a random supplementary multilingual plane character
*/
static unicodeSMP () { static unicodeSMP () {
return random.item([ return String.fromCodePoint(
// SMP = Supplementary Multilingual Plane random.range(
[0x10000, 0x13FFF], ...random.item([
[0x16000, 0x16FFF], [0x10000, 0x13FFF],
[0x1B000, 0x1BFFF], [0x16000, 0x16FFF],
[0x1D000, 0x1DFFF], [0x1B000, 0x1BFFF],
[0x1F000, 0x1FFFF] [0x1D000, 0x1DFFF],
]) [0x1F000, 0x1FFFF]
])
)
)
} }
/**
* Generate a random supplementary ideographic plane character
*/
static unicodeSIP () { static unicodeSIP () {
return random.item([ return String.fromCodePoint(
// SIP = Supplementary Ideographic Plane random.range(
[0x20000, 0x2BFFF], ...random.item([
[0x2F000, 0x2FFFF] [0x20000, 0x2BFFF],
]) [0x2F000, 0x2FFFF]
])
)
)
} }
/**
* Generate a random supplementary special-purpose plane character
*/
static unicodeSSP () { static unicodeSSP () {
return random.item([ return String.fromCodePoint(
// SSP = Supplementary Special-purpose Plane random.range(0xE0000, 0xE0FFF)
[0xE0000, 0xE0FFF] )
])
}
static assignmentOperator () {
return random.pick([
'=', '+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
])
}
static arithmeticOperator () {
return random.pick([
'%', '-', '+', '*', '/'
])
} }
static currency () { static currency () {
@ -222,26 +291,48 @@ class text extends make {
return utils.common.quote(text.any()) return utils.common.quote(text.any())
} }
static chars () { /**
return random.pick([ * Wrapper for all text generators
text.controlChar, * @returns {string}
text.token, */
text.assignmentOperator, static random () {
text.arithmeticOperator, return random.choose([
String.fromCharCode(text.layoutCharCodes()), [1, text.alpha],
String.fromCharCode(text.bidiCharCodes()) [1, text.alphanum],
[1, text.arithmeticOperator],
[1, text.assignmentOperator],
[1, text.controlChar],
[1, text.digit],
[1, text.lineEnd],
[1, text.token],
[3, text.layoutCharCodes],
[3, text.bidiCharCodes],
[3, text.unicodeCombiningCharacter],
[3, text.unicodeBMP],
[3, text.unicodeSMP],
[3, text.unicodeSIP],
[3, text.unicodeSSP]
]) ])
} }
/**
* Generate a single character
*/
static character () {
return text.random().charAt(0)
}
/**
* Generate string comprised of random generators
*/
static any () { static any () {
// Generate a string compromised of random individual characters
// This might be too slow to used for all 'texts' uses
let s = '' let s = ''
// TODO: Len calculation take from DOMFuzz - maybe we should revise this? let len = random.range(1, 126)
let len = random.pick([make.number.tiny, make.number.range])
for (let i = 0; i < len; i++) { while (len--) {
s += make.text.chars() s += make.text.random()
} }
return s return s
} }
} }

View file

@ -82,8 +82,7 @@ class typed extends make {
[1, [typed.short, typed.unsignedShort]], [1, [typed.short, typed.unsignedShort]],
[1, [typed.long, typed.unsignedLong]], [1, [typed.long, typed.unsignedLong]],
[1, [typed.float, typed.unrestrictedFloat]], [1, [typed.float, typed.unrestrictedFloat]],
[1, [typed.double, typed.unrestrictedDouble]], [1, [typed.double, typed.unrestrictedDouble]]
[1, [make.number.range, make.number.tiny]]
]) ])
} }

View file

@ -81,7 +81,7 @@ class random {
/** /**
* Returns a random index from a list * Returns a random index from a list
* @param {Array} list * @param {Array} list
* @returns list[n] * @returns {*}
*/ */
static item (list) { static item (list) {
if (!Array.isArray(list)) { if (!Array.isArray(list)) {

View file

@ -5,6 +5,11 @@ const jsesc = require('jsesc')
const utils = require('../utils') const utils = require('../utils')
class common extends utils { class common extends utils {
/**
* Return stringified object
* @param obj
* @returns {string}
*/
static objToString (obj) { static objToString (obj) {
try { try {
return `${obj}` return `${obj}`
@ -13,6 +18,11 @@ class common extends utils {
} }
} }
/**
* Return enumerable properties recursively
* @param obj
* @returns {Array}
*/
static getAllProperties (obj) { static getAllProperties (obj) {
let list = [] let list = []
while (obj) { while (obj) {
@ -22,6 +32,11 @@ class common extends utils {
return list return list
} }
/**
* Return all properties (non-recursive)
* @param obj
* @returns {Array}
*/
static getKeysFromHash (obj) { static getKeysFromHash (obj) {
let list = [] let list = []
for (let p in obj) { for (let p in obj) {
@ -30,17 +45,32 @@ class common extends utils {
return list return list
} }
static quote (s) { /**
* Escape and quote a string
* @param s - String to be quoted
* @param {boolean} html - Identifies whether the string must be HTML safe
* @returns {*}
*/
static quote (s, html = false) {
const options = {
minimal: true,
isScriptContext: html
}
if (typeof s === 'string') { if (typeof s === 'string') {
return `'${jsesc(s, {minimal: true})}'` return `'${jsesc(s, options)}'`
} else { } else {
return jsesc(s, {minimal: true}) return jsesc(s, options)
} }
} }
/**
* Unicode safe b64 encoding
* https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
* @param {string} str
* @returns {*}
*/
static b64encode (str) { static b64encode (str) {
// Unicode safe b64 encoding
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
if (process.browser) { if (process.browser) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
function toSolidBytes (match, p1) { function toSolidBytes (match, p1) {
@ -53,9 +83,13 @@ class common extends utils {
} }
} }
/**
* Unicode safe b64 decoding
* https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
* @param {string} str
* @returns {*}
*/
static b64decode (str) { static b64decode (str) {
// Unicode safe b64 decoding
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
if (process.browser) { if (process.browser) {
return decodeURIComponent(atob(str).split('').map(function (c) { return decodeURIComponent(atob(str).split('').map(function (c) {
return `%${('00' + c.charCodeAt(0).toString(16)).slice(-2)}` return `%${('00' + c.charCodeAt(0).toString(16)).slice(-2)}`
@ -65,6 +99,11 @@ class common extends utils {
} }
} }
/**
* Remove duplicate items from a list
* @param {Array} list
* @returns {Array}
*/
static uniqueList (list) { static uniqueList (list) {
let tmp = {} let tmp = {}
let r = [] let r = []
@ -77,6 +116,12 @@ class common extends utils {
return r return r
} }
/**
* Merge two objects recursively
* @param {Object} obj1
* @param {Object} obj2
* @returns {*}
*/
static mergeHash (obj1, obj2) { static mergeHash (obj1, obj2) {
for (let p in obj2) { for (let p in obj2) {
try { try {
@ -92,6 +137,11 @@ class common extends utils {
return obj1 return obj1
} }
/**
* Template string beautifier
* @param {Object} obj
* @returns {string}
*/
static mockup (obj) { static mockup (obj) {
return obj.split('\n').map((ln) => ln.trim()).join('') return obj.split('\n').map((ln) => ln.trim()).join('')
} }

View file

@ -43,7 +43,7 @@ class script extends utils {
} }
static makeLoop (s, max) { static makeLoop (s, max) {
return `for (let i = 0; i < ${max || make.number.range()}; i++) {${s}}` return `for (let i = 0; i < ${max || make.number.tiny()}; i++) {${s}}`
} }
static makeRandomOptions (baseObject) { static makeRandomOptions (baseObject) {