
A collection of useful JavaScript helpers.
Demos
Modules
Module: deep_equal
Helper that verifies deeply equality of two arguments of any type.
Usage example from unit tests:
import { deep_equal } from '../src/deep_equal.js'
import { it } from '../src/it.js'
it('should work', () => {
deep_equal(true, true).should.be.true
deep_equal(true, false).should.be.false
deep_equal(false, true).should.be.false
deep_equal(false, false).should.be.true
deep_equal(null, null).should.be.true
deep_equal(null, undefined).should.be.false
deep_equal(undefined, null).should.be.false
deep_equal(undefined, undefined).should.be.true
deep_equal(42, 42).should.be.true
deep_equal(42, '42').should.be.false
deep_equal('42', 42).should.be.false
deep_equal('42', '42').should.be.true
deep_equal([1, { a: 2, b: 3 }, 4], [1, { b: 3, a: 2 }, 4]).should.be.true
deep_equal([1, { a: 2, b: 3 }, 4], [1, { b: 3, a: '2' }, 4]).should.be.false
deep_equal([1, { a: 2, b: 3 }, 4], [1, { b: 3, a: 2 }, '4']).should.be.false
deep_equal(Object.create(null), {}).should.be.true
deep_equal(() => { }, () => { }).should.be.false
const keys = 'abcdefghilklmnopqrstuvwxyz0123456789_$'.split('')
const a = [{}, {}, {}]
const t = [a[0], a[1], a[2]]
for (let i = 0; i < t.length; ++i) {
for (let k = 0; k < 10000; ++k) {
t[i] = t[i][keys[k % keys.length]] = {}
}
t[i].deepest = !i
}
deep_equal(a[0], a[1]).should.be.false
deep_equal(a[0], a[2]).should.be.false
deep_equal(a[1], a[2]).should.be.true
})
deep_equal
Helper that verifies deeply equality of two arguments of any type.
An iterative implementation that does not cause a stack overflow exception.
🔴 Parameter: actual
any
🔴 Parameter: expected
any
🟢 Returns
boolean
Module: download
Helper for handling client-side (web browser) generated downloads.
Usage example from unit tests:
import { it } from '../src/it.js'
import { download } from '../src/download.js'
it('should work', () => {
download('Some content', 'name.txt', 'plain/text')
})
download
Helper for handling client-side (web browser) generated downloads.
🔴 Parameter: blobPart
BlobPart[]=
🔴 Parameter: download
string=
🔴 Parameter: type
string=
Module: elvis
A set of helpers for working with objects and arrays.
Usage example from unit tests:
import { arr, get, obj, set } from '../src/elvis.js'
import { it } from '../src/it.js'
it('should create array of proper values', () => {
const actual = arr` ${'The answer'} to life the universe and everything:
${42}${true} `
const expected = [
'The answer', 'to', 'life', 'the', 'universe', 'and', 'everything:',
42, true
]
actual.should.be.deep.equal(expected)
})
it('should get existing non-nested property', () => {
const actual_1 = { a: 42 }
get(actual_1, 'a').should.be.equal(42)
})
it('should get existing nested property and get undefined for non existing nested property', () => {
const actual_2 = {
The: {
answer: {
to: {
life: {
the: {
universe: {
and: {
everything: 42
}
}
}
}
}
}
}
}
get(actual_2, ...arr`The answer to life the universe and everything`).should.be.equal(42)
expect(get(actual_2, ...arr`The answer to life the Universe and everything`)).to.be.undefined
})
it('should set not existing non-nested property', () => {
const actual_1 = {}
set(actual_1).a = 42
actual_1.should.be.deep.equal({ a: 42 })
})
it('shoud set not existing nested property', () => {
const actual_2 = {}
set(actual_2, ...arr`E l v i`).s = 42
actual_2.should.be.deep.equal({ E: { l: { v: { i: { s: 42 } } } } })
})
it('should create object of undefined values', () => {
const actual = obj` The answer to life the universe and everything:
${42} is greater than ${17} `
const expected = {
The: undefined,
answer: undefined,
to: undefined,
life: undefined,
the: undefined,
universe: undefined,
and: undefined,
'everything:': undefined,
42: undefined,
is: undefined,
greater: undefined,
than: undefined,
17: undefined
}
expect(actual).to.be.deep.equal(expected)
})
arr
Array from tagged template literal.
🔴 Parameter: template
TemplateStringsArray
🔴 Parameter: substitutions
any[]
🟢 Returns
any[]
obj
Object from tagged template literal.
🔴 Parameter: params
[TemplateStringsArray, ...any[]]
🟢 Returns
{
[key: string]: undefined;
}
get
Helper similar to ? operator (Elvis operator) for easy accessing nested object values.
🔴 Parameter: ref
object
🔴 Parameter: keys
string[]
🟢 Returns
object=
set
Helper similar to ? operator (Elvis operator) for easy assigning values to nested objects.
🔴 Parameter: ref
object
🔴 Parameter: keys
string[]
🟢 Returns
object=
Module: fix_typography
Helper implementing typographic corrections appropriate for Polish typography.
Usage example from unit tests:
import { j } from '../src/j.js'
import { fix_typography } from '../src/fix_typography.js'
import { it } from '../src/it.js'
it('should work', () => {
const p = j({
t: 'p',
k: {
innerHTML: 'a b c d e f g h i j k l m n o p q r s t u v w x y z ' +
'https://example.com ' +
'<a href="https://example.com">https://example.com</a>'
}
}).e
fix_typography(p)
p.outerHTML.replace(/\u200B/g, '​').should.be.equal(
'<p>a b c d e f g h ' +
'<span class="nbsp">i </span>j k l m n ' +
'<span class="nbsp">o </span>p q r s t ' +
'<span class="nbsp">u </span>v ' +
'<span class="nbsp">w </span>x y ' +
'<span class="nbsp">z </span>' +
'https:/​/​example.​com ' +
'<a href="https://example.com">' +
'https:/​/​example.​com' +
'</a></p>'
)
})
fix_typography
Helper implementing typographic corrections appropriate for Polish typography.
An iterative implementation that does not cause a stack overflow exception.
🔴 Parameter: htmlElement
HTMLElement
Module: fts
A set of helpers for Full-Text Search.
Usage example from unit tests:
import { substitution_costs_default, fts_index, fts_rank, levenshtein_distance, substitution_costs_pl } from '../src/fts.js'
import { it } from '../src/it.js'
it('should work', () => {
const count_word_id = {}
const count_word = {}
const count_id = {}
const index = fts_index.bind(0, count_word_id, count_word, count_id)
const rank = fts_rank.bind(0, count_word_id, count_word, count_id)
index('one', 1)
index('one', 2)
index('two', 2)
index('two', 3)
rank('one').should.be.deep.equal({ 1: 0.5, 2: 0.25 })
rank('two').should.be.deep.equal({ 2: 0.25, 3: 0.5 })
})
it('should correctly calculate the Levenshtein distance', () => {
const levenshtein = levenshtein_distance.bind(null, 1, 1, substitution_costs_default)
levenshtein('kot', 'kat').should.be.equal(1)
levenshtein('koty', 'kat').should.be.equal(2)
levenshtein('levenshtein', 'lewensztejn').should.be.equal(3)
levenshtein('kitten', 'sitting').should.be.equal(3)
levenshtein('kat', 'kąt').should.be.equal(1)
const levenshtein_pl = levenshtein_distance.bind(null, 1, 1, substitution_costs_pl)
levenshtein_pl('kat', 'kąt').should.be.equal(0.3)
})
fts_index
Helper that indexes the specified word within the specified id.
🔴 Parameter: count_word_id
{
[word: string]: {
[id: string]: number;
};
}
🔴 Parameter: count_word
{
[word: string]: number;
}
🔴 Parameter: count_id
{
[id: string]: number;
}
🔴 Parameter: word
string
🔴 Parameter: id
string
🔴 Parameter: rank
number=
fts_rank
Helper that searches for the given word among indexed words.
Returns a map of non-zero relevance coefficients for registered identifiers.
🔴 Parameter: count_word_id
{
[word: string]: {
[id: string]: number;
};
}
🔴 Parameter: count_word
{
[word: string]: number;
}
🔴 Parameter: count_id
{
[id: string]: number;
}
🔴 Parameter: word
string
🔴 Parameter: result
{
[id: string]: number;
}=
🟢 Returns
{
[id: string]: number;
}
fts_init_counters
Helper that creates count_word and count_id maps based on the count_word_id map.
🔴 Parameter: count_word_id
{
[word: string]: {
[id: string]: number;
};
}
🟢 Returns
[{
[word: string]: number;
}, {
[id: string]: number;
}]
Type definition: SUBSTITUTION_COST
The substitution cost function used by levenshtein_distance.
🔴 Parameter: letter_1
string
🔴 Parameter: letter_2
string
🟢 Returns
number
substitution_costs_default
Default substitution costs function to use with levenshtein_distance.
🟠 Type
SUBSTITUTION_COST
substitution_costs_pl
Substitution costs function for the Polish language (to use with levenshtein_distance).
🟠 Type
SUBSTITUTION_COST
levenshtein_distance
Helper for calculating Levenshtein distances.
🔴 Parameter: deletion_cost
number
🔴 Parameter: insertion_cost
number
🔴 Parameter: substitution_cost
SUBSTITUTION_COST
🔴 Parameter: word_1
string
🔴 Parameter: word_2
string
🟢 Returns
number
Module: is_in
Replacement for the in operator (not to be confused with the for-in loop) that works properly.
Usage example from unit tests:
import { it } from '../src/it.js'
import { is_in } from '../src/is_in.js'
it('should work', () => {
const ob = { key: 'K', null: 'N' }
expect('key' in ob).to.be.true
is_in('key', ob).should.be.true
expect('null' in ob).to.be.true
is_in('null', ob).should.be.true
expect(null in ob).to.be.true
is_in(null, ob).should.be.false
expect('toString' in ob).to.be.true
is_in('toString', ob).should.be.false
is_in('key', null).should.be.false
})
is_in
Replacement for the in operator (not to be confused with the for-in loop) that works properly.
🔴 Parameter: key
any
🔴 Parameter: map
any
🟢 Returns
boolean
Module: it
A simple helper for various types of tests (especially unit tests).
it
A simple helper for various types of tests (especially unit tests).
🔴 Parameter: message
string
🔴 Parameter: callback
function
summary
Prints a summary of the tests.
🟢 Returns
number
Module: j
Lightweight helper for creating and modifying DOM elements.
Usage example from unit tests:
import { it } from '../src/it.js'
import { j, j_style, j_svg, j_svg_use, j_symbol, toe } from '../src/j.js'
it('should create <b><i> with class attribute and textContent', () => {
j({
t: 'b',
i: [{
t: 'i', k: { className: 'some class', textContent: 'text' }
}]
}).e.outerHTML.should.be.equal('<b><i class="some class">text</i></b>')
})
const b = j({ t: 'b' })
it('should create empty <b>', () => {
b.e.outerHTML.should.be.equal('<b></b>')
})
const i = j({
t: 'i', i: [{ e: 'text' }], p: b.e
})
it('should create <i> with previous <b> as a child', () => {
i.e.outerHTML.should.be.equal('<i>text</i>')
b.e.outerHTML.should.be.equal('<b><i>text</i></b>')
})
j({
e: i.e, k: { className: 'some class' }
})
it('should add some class to previous <i> and previous <b>', () => {
i.e.outerHTML.should.be.equal('<i class="some class">text</i>')
b.e.outerHTML.should.be.equal('<b><i class="some class">text</i></b>')
})
it('should set textContent by child node', () => {
j({
t: 'span', k: { className: 'some class' }, i: [{ e: 0 }]
}).e.outerHTML.should.be.equal('<span class="some class">0</span>')
})
it('should set textContent by textContent property', () => {
j({
t: 'span', k: { className: 'some class', textContent: 'one' }
}).e.outerHTML.should.be.equal('<span class="some class">one</span>')
})
it('should set style by style properties', () => {
j({
t: 'div',
k: {
style: { margin: 0 }
}
}).e.outerHTML.should.be.equal('<div style="margin: 0px;"></div>')
})
it('should create <div> with child nodes', () => {
j({
t: 'div',
k: { className: 'some class' },
i: [{
t: 'b', i: [{ e: 'bold 1' }]
}, {
t: 'b', i: [{ e: 'bold 2' }]
}, {
t: 'i', i: [{ e: 'italic' }]
}]
}).e.outerHTML.should.be.equal(
'<div class="some class">' +
'<b>bold 1</b><b>bold 2</b><i>italic</i>' +
'</div>')
})
const input_1 = j({
t: 'input', k: { value: 42 }
})
const input_2 = j({
t: 'input', a: { value: 42 }
})
it('should set <input> value set by value property', () => {
input_1.e.value.should.be.equal('42')
})
it('should set <input> value set by value attribute', () => {
input_2.e.value.should.be.equal('42')
})
it('should not expose <input> value attribute set by value property', () => {
input_1.e.outerHTML.should.be.equal('<input>')
})
it('should expose <input> value attribute set by value attribute', () => {
input_2.e.outerHTML.should.be.equal('<input value="42">')
})
it('should wrap the argument to an object with a single property ‘e’', () => {
toe(42).should.be.deep.equal({ e: 42 })
})
it('should handle boolean attributes in a special way', () => {
j({
t: 'input', a: { type: 'checkbox', checked: true }
}).e.outerHTML.should.be.equal('<input type="checkbox" checked="">')
j({
t: 'input', a: { type: 'checkbox', checked: false }
}).e.outerHTML.should.be.equal('<input type="checkbox">')
j({
t: 'input', a: { type: 'checkbox', 'xhtml:checked': true }
}).e.outerHTML.should.be.equal('<input type="checkbox" checked="">')
j({
t: 'input', a: { type: 'checkbox', 'xhtml:checked': false }
}).e.outerHTML.should.be.equal('<input type="checkbox">')
})
it('should assign the property by reference if assignment by subkeys is not possible', () => {
j({
t: 'div', k: { key: { key_2: 42 } }
}).e.key.should.be.deep.equal({ key_2: 42 })
})
it('should work', () => {
j_symbol({}).e.outerHTML.should.be.equal('<symbol></symbol>')
j_symbol({ t: 'non-symbol' }).e.outerHTML.should.be.equal('<symbol></symbol>')
j_symbol({ a: { id: 'id' } }).e.outerHTML.should.be.equal('<symbol id="id"></symbol>')
})
it('should work', () => {
j_svg([{
a: { viewBox: '0 1 2 3', id: 'id' },
i: [{ t: 'some-tag' }]
}]).e.outerHTML.should.be.equal(
'<svg style="display: none;">' +
'<symbol viewBox="0 1 2 3" id="id"><some-tag></some-tag></symbol>' +
'</svg>')
})
it('should handle id', () => {
j(j_svg_use('id')).e.outerHTML.replace('xlink:', '').should.be.equal(
'<svg><use href="#id"></use></svg>')
})
it('should handle extra attributes', () => {
j({
...j_svg_use('id'),
a: { fill: '#000', stroke: '#000', width: 42, height: 42 }
}).e.outerHTML.replace('xlink:', '').should.be.equal(
'<svg fill="#000" stroke="#000" width="42" height="42">' +
'<use href="#id"></use>' +
'</svg>')
})
it('should work', () => {
const js_style = {
a: { b$$1: 1, b$$2: 2 }
}
j_style(js_style).e.outerHTML.should.be.equal(
'<style>a{b:1;b:2}</style>')
j_style(js_style, '$$$').e.outerHTML.should.be.equal(
'<style>a{b$$1:1;b$$2:2}</style>')
})
Type definition: J_CONFIG
The j helper configuration.
🟡 Property: a
{
[attribute: string]: any;
}?
attributes of the created or modified element set by setAttribute or setAttributeNS
🟡 Property: e
(Element | Text | string | number)?
modified element
🟡 Property: i
J_CONFIG[]?
an array of subelements (items) of the created or modified element
🟡 Property: k
{
[key: string]: any;
}?
properties (keys) to set in the created or modified element
🟡 Property: n
string?
namespace for createElementNS, setAttributeNS and removeAttributeNS methods
🟡 Property: p
Element?
reference to the parent element for the created or modified element
🟡 Property: t
string?
tag of the created element
🔵 Template parameter: T
Type definition: E
Result type of the j helper.
🟡 Property: e
T
j
Lightweight helper for creating and modifying DOM elements.
🔴 Parameter: config
J_CONFIG
🟢 Returns
E<Element | HTMLElement | Text>
toe
🔵 Template parameter: T
🔴 Parameter: e
T
🟢 Returns
E<T>
j_symbol
Helper for creating <symbol> elements.
🔴 Parameter: config
J_CONFIG
🟢 Returns
E<SVGSymbolElement>
j_svg
Helper for creating <svg> container elements.
🔴 Parameter: configs
Array<J_CONFIG>
🟢 Returns
E<SVGSVGElement>
j_svg_use
Helper for creating <svg><use> elements.
🔴 Parameter: id
string
🟢 Returns
J_CONFIG
j_style
Helper for creating <style> elements.
🔴 Parameter: js_style
JSS
🔴 Parameter: splitter
string=
🟢 Returns
E<HTMLStyleElement>
Module: jcmp
Jackens’ Components.
Jackens’ Components configuration format
Jackens’ Components configuration format (JCMP_CONFIG) is based on three main keys:
w (wrapper): J_CONFIG format configuration of the component wrapper (HTMLDivElement element)
c (control): J_CONFIG format configuration of the component control
l (label): J_CONFIG format configuration of the component label (HTMLLabelElement element; applies to input controls of type checkbox, radio and file)
All other keys are just an alternative, more convenient form for configuration using the main keys.
The sample file selection button component shown below

has the following HTML representation:
<body>
<div class="jcmp" label="Label">
<input type="file" id="jcmp-1" />
<div>
<label for="jcmp-1">Text</label>
</div>
</div>
<script type="module">
import { j, j_style } from '../../src/j.js'
import { jcmp_jss } from '../../src/jcmp.js'
window.onload = () => j({
e: document.body,
i: [j_style(jcmp_jss())]
})
</script>
</body>
The file selection button component shown above can be generated with the following code:
<script type="module">
import { j, j_style } from '../../src/j.js'
import { jcmp, jcmp_jss } from '../../src/jcmp.js'
window.onload = () => j({
e: document.body,
i: [
j_style(jcmp_jss()),
jcmp({ type: 'file', label: 'Label', text: 'Text' })
]
})
</script>
The JCMP_CONFIG format has the following default values:
{ […], t: 'input', class: 'jcmp', […] }
The icon and text keys are handled in a special way: they allow you to conveniently specify the icon and text of the component you are defining.
Jackens’ Components layout system
Jackens’ Components layout system contains only two types of classes:
.w-«w»-«sw»: width of «w» slots when the screen is wide enough for «sw» slots.
.h-«h»-«sw»: height of «h» slots when the screen is wide enough for «sw» slots.
The minimum slot width is 200 pixels.
Example. Components defined as follows:
<script type="module">
import { j, j_style } from '../../src/j.js'
import { jcmp, jcmp_jss } from '../../src/jcmp.js'
window.onload = () => j({
e: document.body,
i: [
j_style(jcmp_jss()),
jcmp({
label: 'Component #1',
class: 'jcmp w-1-3 w-1-2',
text: 'jcmp w-1-3 w-1-2'
}), jcmp({
label: 'Component #2',
class: 'jcmp w-1-3 w-1-2',
text: 'jcmp w-1-3 w-1-2'
}), jcmp({
label: 'Component #3',
class: 'jcmp w-1-3',
text: 'jcmp w-1-3'
})
]
})
</script>
on a screen at least 600 pixels wide, will be arranged on a single line:

on a screen less than 600 pixels wide, but not less than 400 pixels wide, will be arranged in two lines:

while on a screen less than 400 pixels wide, they will be arranged in three rows:

Type definition: JCMP_CONFIG
Jackens’ Components configuration.
🟡 Property: w
J_CONFIG?
wrapper configuration
🟡 Property: c
J_CONFIG?
control configuration
🟡 Property: l
J_CONFIG?
label configuration
🟡 Property: class
string?
w.a.class configuration
🟡 Property: label
string?
w.a.label configuration
🟡 Property: p
HTMLElement?
w.p configuration
🟡 Property: onchange
any?
c.k.onchange configuration
🟡 Property: onclick
any?
c.k.onclick configuration
🟡 Property: oninput
any?
c.k.oninput configuration
🟡 Property: onkeyup
any?
c.k.onkeyup configuration
🟡 Property: style
{
[key: string]: any;
}?
c.k.style configuration
🟡 Property: t
string?
c.t configuration
🟡 Property: icon
string?
icon
🟡 Property: text
string?
text
jcmp
Convenient converter from JCMP_CONFIG format to J_CONFIG format.
import { it } from '../src/it.js'
import { j_svg_use } from '../src/j.js'
import { jcmp, jcmp_jss } from '../src/jcmp.js'
const svg_use_config = {
a: { fill: '#fff', stroke: '#fff', width: 17, height: 17 }
}
const button_1 = jcmp({
t: 'button',
w: {
a: { class: 'jcmp' }
},
c: {
a: { 'aria-label': 'button' },
i: [{
...j_svg_use('icon-id'), ...svg_use_config
}, {
t: 'div'
}, {
e: 'Button'
}],
k: {
style: { marginLeft: 42, marginRight: 17 },
onclick: console.log
}
}
})
const button_2 = jcmp({
t: 'button',
style: { marginLeft: 42, marginRight: 17 },
onclick: console.log,
text: 'Button',
icon: 'icon-id',
'aria-label': 'button'
})
it('should handle keys ‘style’, ‘onclick’ and ‘aria-label’', () => {
button_2.should.be.deep.equal(button_1)
})
const file_1 = jcmp({
t: 'input',
type: 'file',
w: {
a: { class: 'jcmp' }
},
l: {
i: [{ e: 'Choose file…' }]
}
})
const file_2 = jcmp({
t: 'input', type: 'file', text: 'Choose file…'
})
it('should add class ‘jcmp’ automatically and handle key ‘text’', () => {
file_2.i[0].a.id = file_2.i[1].i[0].a.for = 'jcmp-1'
file_2.should.be.deep.equal(file_1)
})
const css = jcmp_jss()
it('should generate proper CSS', () => {
css.should.be.string
})
🔴 Parameter: config
JCMP_CONFIG
🟢 Returns
J_CONFIG
jcmp_jss
Jackens’ Components CSS rules in JSS format.
🔴 Parameter: config
{
font_family?: string;
focus_color?: string;
main_color?: string;
}
🟢 Returns
JSS
Module: js_on_parse
JSON.parse with “JavaScript turned on”.
Usage example from unit tests:
import { it } from '../src/it.js'
import { js_on_parse } from '../src/js_on_parse.js'
const handlers = {
join: (...params) => params.join(' '),
outer: text => `Hello ${text}!`,
inner: text => text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(),
question: text => ({ d: 'Yes!', t: 42 }[text[0]])
}
it('should use ‘join’ handler', () => {
js_on_parse(`{
"join": ["Hello", "World!"]
}`, handlers).should.be.deep.equal('Hello World!')
})
it('should ‘join’ and ‘to_much’ handlers', () => {
js_on_parse(`{
"join": ["Hello", "World!"], "to_much": "whatever"
}`, handlers).should.be.deep.equal({
join: ['Hello', 'World!'], to_much: 'whatever'
})
})
it('should use ‘inner’ and ‘outer’ handlers', () => {
js_on_parse(`{
"outer":{ "inner": "wORld" }
}`, handlers).should.be.deep.equal('Hello World!')
})
it('should use ‘question’ handler', () => {
js_on_parse(`[{
"question": "does it really works?!?"
}, {
"question": "the answer to life the universe and everything"
}, {
"Question": null
}]`, handlers).should.be.deep.equal(['Yes!', 42, { Question: null }])
})
it('should process nested objects', () => {
js_on_parse('{"H":{"e":{"l":{"l":{"o":["World!"]}}}}}', handlers)
.should.be.deep.equal({ H: { e: { l: { l: { o: ['World!'] } } } } })
})
Objects having exactly one «handlerName» property present in the handlers map, i.e. objects of form:
{ "«handlerName»": «param» }
and
{ "«handlerName»": [«params»] }
are replaced by the result of call
handlers['«handlerName»'](«param»)
and
handlers['«handlerName»'](...«params»)
Remark. To pass to handlers['«handlerName»'] a single argument that is an array, use the following form:
{ "«handlerName»": [[«elements»]] }
which will force a call
handlers['«handlerName»']([«elements»])
js_on_parse
JSON.parse with “JavaScript turned on”.
🔴 Parameter: text
string
🔴 Parameter: handlers
{
[handler_name: string]: function;
}
🟢 Returns
any
Module: jss
CSS-in-JS helper based on the JSS format.
The JSS format provides a hierarchical description of CSS rules.
- Keys of sub-objects whose values are NOT objects are treated as CSS attribute, and values are treated as values of those CSS attributes; the concatenation of keys of all parent objects is a CSS rule.
- All keys ignore the part starting with a splitter (default: '$$') sign until the end of the key (e.g.
src$$1 → src, @font-face$$1 → @font-face).
- In keys specifying CSS attribute, all uppercase letters are replaced by lowercase letters with an additional
- character preceding them (e.g. fontFamily → font-family).
- Commas in keys that makes a CSS rule cause it to “split” and create separate rules for each part (e.g.
{div:{margin:1,'.a,.b,.c':{margin:2}}} → div{margin:1}div.a,div.b,div.c{margin:2}).
- Top-level keys that begin with
@ are not concatenated with sub-object keys.
Usage example from unit tests:
import { it } from '../src/it.js'
import { jss } from '../src/jss.js'
it('should handle nested definitions (level 1)', () => {
const actual_1 = {
a: {
color: 'red',
margin: 1,
'.c': {
margin: 2,
padding: 2
},
padding: 1
}
}
const expected_1 =
'a{color:red;margin:1}' +
'a.c{margin:2;padding:2}' +
'a{padding:1}'
jss(actual_1).should.be.equal(expected_1)
})
it('should handle nested definitions (level 2)', () => {
const actual_2 = {
a: {
'.b': {
color: 'red',
margin: 1,
'.c': {
margin: 2,
padding: 2
},
padding: 1
}
}
}
const expected_2 =
'a.b{color:red;margin:1}' +
'a.b.c{margin:2;padding:2}' +
'a.b{padding:1}'
jss(actual_2).should.be.equal(expected_2)
})
it('should handle ‘@’ prefixes and ‘$$suffix’ suffixes', () => {
const actual_3 = {
'@font-face$$1': {
fontFamily: 'Jackens',
src$$1: 'url(fonts/jackens.otf)',
src$$2: "url(fonts/jackens.otf) format('opentype')," +
"url(fonts/jackens.svg) format('svg')",
fontWeight: 'normal',
fontStyle: 'normal'
},
'@font-face$$2': {
fontFamily: 'C64',
src: 'url(fonts/C64_Pro_Mono-STYLE.woff)'
},
'@keyframes spin': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
},
div: {
border: 'solid red 1px',
'.c1': { 'background-color': '#000' },
' .c1': { backgroundColor: 'black' },
'.c2': { backgroundColor: 'rgb(0,0,0)' }
},
'@media(min-width:200px)': {
div: { margin: 0, padding: 0 },
span: { color: '#000' }
}
}
const expected_3 = '@font-face{font-family:Jackens;src:url(fonts/jackens.otf);' +
"src:url(fonts/jackens.otf) format('opentype')," +
"url(fonts/jackens.svg) format('svg');" +
'font-weight:normal;font-style:normal}' +
'@font-face{font-family:C64;src:url(fonts/C64_Pro_Mono-STYLE.woff)}' +
'@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}' +
'div{border:solid red 1px}' +
'div.c1{background-color:#000}' +
'div .c1{background-color:black}' +
'div.c2{background-color:rgb(0,0,0)}' +
'@media(min-width:200px){div{margin:0;padding:0}span{color:#000}}'
jss(actual_3).should.be.equal(expected_3)
})
it('should handle comma separation (level 2)', () => {
const actual_4 = {
a: {
'.b,.c': {
margin: 1,
'.d': {
margin: 2
}
}
}
}
const expected_4 = 'a.b,a.c{margin:1}a.b.d,a.c.d{margin:2}'
jss(actual_4).should.be.equal(expected_4)
})
it('should handle comma separation (level 1)', () => {
const actual_5 = {
'.b,.c': {
margin: 1,
'.d': {
margin: 2
}
}
}
const expected_5 = '.b,.c{margin:1}.b.d,.c.d{margin:2}'
jss(actual_5).should.be.equal(expected_5)
})
it('should handle multiple comma separations', () => {
const actual_6 = {
'.a,.b': {
margin: 1,
'.c,.d': {
margin: 2
}
}
}
const expected_6 = '.a,.b{margin:1}' +
'.a.c,.a.d,.b.c,.b.d{margin:2}'
jss(actual_6).should.be.equal(expected_6)
})
Type definition: JSS
{
[rule_or_attribute: string]: string | number | JSS;
}
jss
CSS-in-JS helper based on the JSS format.
An iterative implementation that does not cause a stack overflow exception.
🔴 Parameter: style
JSS
🔴 Parameter: splitter
string=
🟢 Returns
string
Module: mdoc
Converter from JSDoc to Markdown.
Usage example from unit tests (used to generate this documentation):
import { js_on_parse } from '../src/js_on_parse.js'
import { mdoc, read_file, write_file } from '../src/mdoc.js'
const config = {
write_file: {
lines: [
{ read_file: 'header.md' },
'\n# Modules\n',
{ mdoc: {} },
'\n# License\n',
{ read_file: 'LICENSE' }
],
path: 'readme.md'
}
}
js_on_parse(JSON.stringify(config), { mdoc, read_file, write_file })
read_file
Handler that reads the contents of the specified file.
🔴 Parameter: path
string
🟢 Returns
string
mdoc
Converter from JSDoc to Markdown.
🔴 Parameter: config
{
src?: string[];
extra_ids?: boolean;
}
🟢 Returns
string
write_file
Helper that writes an array of lines to the specified file.
🔴 Parameter: config
{
lines: string[];
path: string;
}
🟢 Returns
string
Module: num_to_key
Helper for converting integers to map-friendly string identifiers.
Usage example from unit tests:
import { it } from '../src/it.js'
import { num_to_key } from '../src/num_to_key.js'
const ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$'
const _1_TO_9 = '123456789'
const _0_TO_9 = `0${_1_TO_9}`
const ALPHA_NUM = `${_0_TO_9}${ALPHA}`
const KEYS = []
for (let a = 0; a < ALPHA_NUM.length; ++a) {
KEYS.push(ALPHA_NUM[a])
}
for (let a = 0; a < _1_TO_9.length; ++a) {
for (let b = 0; b < _0_TO_9.length; ++b) {
KEYS.push(`${_1_TO_9[a]}${_0_TO_9[b]}`)
}
}
for (let a = 0; a < ALPHA.length; ++a) {
for (let b = 0; b < ALPHA_NUM.length; ++b) {
KEYS.push(`${ALPHA[a]}${ALPHA_NUM[b]}`)
}
}
for (let a = 0; a < _1_TO_9.length; ++a) {
for (let b = 0; b < _0_TO_9.length; ++b) {
for (let c = 0; c < _0_TO_9.length; ++c) {
KEYS.push(`${_1_TO_9[a]}${_0_TO_9[b]}${_0_TO_9[c]}`)
}
}
}
for (let a = 0; a < ALPHA.length; ++a) {
for (let b = 0; b < ALPHA_NUM.length; ++b) {
for (let c = 0; c < ALPHA_NUM.length; ++c) {
KEYS.push(`${ALPHA[a]}${ALPHA_NUM[b]}${ALPHA_NUM[c]}`)
}
}
}
it('should work', () => {
for (let num = 0; num < KEYS.length; ++num) {
num_to_key(num).should.be.equal(KEYS[num])
}
})
num_to_key
Helper for converting integers to map-friendly string identifiers.
🔴 Parameter: num
number
🟢 Returns
string
Module: pl_ural
Helper for choosing the correct singular and plural.
Usage example from unit tests:
import { it } from '../src/it.js'
import { pl_ural } from '../src/pl_ural.js'
it('should handle 0', () => {
pl_ural(0, 'car', 'cars', 'cars').should.be.equal('0 cars')
pl_ural(0, 'auto', 'auta', 'aut').should.be.equal('0 aut')
})
it('should handle 1', () => {
pl_ural(1, 'car', 'cars', 'cars').should.be.equal('1 car')
pl_ural(1, 'auto', 'auta', 'aut').should.be.equal('1 auto')
})
it('should handle 5', () => {
pl_ural(5, 'car', 'cars', 'cars').should.be.equal('5 cars')
pl_ural(5, 'auto', 'auta', 'aut').should.be.equal('5 aut')
})
it('should handle 2', () => {
pl_ural(42, 'car', 'cars', 'cars').should.be.equal('42 cars')
pl_ural(42, 'auto', 'auta', 'aut').should.be.equal('42 auta')
})
pl_ural
Helper for choosing the correct singular and plural.
🔴 Parameter: value
number
🔴 Parameter: singular
string
🔴 Parameter: plural_2
string
🔴 Parameter: plural_5
string
🔴 Parameter: no_value_1
string=
🔴 Parameter: no_value
string=
🟢 Returns
string
Module: preloader
Animated gear preloader.
preloader
Animated gear preloader.
🟠 Type
E<SVGSVGElement>
preloader_jss
Animated gear preloader CSS rules in JSS format.
🟠 Type
JSS
Module: scan_dirs
Helper to scan files in the specified directories.
Unit tests:
import { it } from '../src/it.js'
import { scan_dirs } from '../src/scan_dirs.js'
it('should work', async () => {
const src = scan_dirs('src')
const test = scan_dirs('test')
const src_and_test = scan_dirs('src', 'test')
src.length.should.be.greaterThan(20)
test.length.should.be.greaterThan(30)
src_and_test.length.should.be.equal(src.length + test.length)
})
scan_dirs
Helper to scan files in the specified directories.
🔴 Parameter: dirs
...string
🟢 Returns
string[]
Module: sql
A set of helpers for creating safe SQL queries.
Usage example from unit tests:
import { it } from '../src/it.js'
import { mssql_name, mysql_name, sql, sql_name } from '../src/sql.js'
it('should work', () => {
const actual = sql`
SELECT *
FROM ${mysql_name('table`name')}
WHERE
${sql_name('column"name')} = ${42} OR
${mssql_name('[column][name]')} = ${"'"} OR
column_name = ${true} OR
column_name = ${false} OR
column_name = ${new Date('1980-03-31T04:30:00.000Z')} OR
column_name IN (${[42, "'", true, false, new Date('1980-03-31T04:30:00.000Z')]})`
const expected = `
SELECT *
FROM \`table\`\`name\`
WHERE
"column""name" = 42 OR
[[column]][name]]] = '''' OR
column_name = b'1' OR
column_name = b'0' OR
column_name = '1980-03-31 04:30:00' OR
column_name IN (42,'''',b'1',b'0','1980-03-31 04:30:00')`
actual.should.be.equal(expected)
})
Type definition: SQL_NAME
{
[Symbol.toStringTag]: string;
toString: () => string;
}
sql_name
Helper for escaping SQL column, table and schema names.
🔴 Parameter: name
string
🟢 Returns
SQL_NAME
mysql_name
Helper for escaping MySQL/MariaDB column, table and schema names.
🔴 Parameter: name
string
🟢 Returns
SQL_NAME
mssql_name
Helper for escaping MS SQL column, table and schema names.
🔴 Parameter: name
string
🟢 Returns
SQL_NAME
sql_value
Helper for escaping values.
🔴 Parameter: value
any
🟢 Returns
string
sql
Tagged Template Literal helper for creating secure SQL queries.
🔴 Parameter: template
TemplateStringsArray
🔴 Parameter: substitutions
any[]
🟢 Returns
string
sql_type
Methods that implement value escape for particular types.
🟠 Type
{
[type: string]: (value: any) => string;
}
Module: stream_to_buffer
Helper for converting streams to buffers.
Usage example from unit tests:
import { createReadStream } from 'fs'
import { it } from '../src/it.js'
import { stream_to_buffer } from '../src/stream_to_buffer.js'
it('should work', async () => {
const stream = createReadStream('src/stream_to_buffer.js')
const buffer = await stream_to_buffer(stream)
buffer.toString('utf8').includes('export const stream_to_buffer =')
.should.be.true
})
stream_to_buffer
Helper for converting streams to buffers.
🔴 Parameter: stream
Stream
🟢 Returns
Promise<Buffer>
Module: translate
Language translations helper.
Usage example from unit tests:
import { it } from '../src/it.js'
import { translate } from '../src/translate.js'
const locales = {
pl: {
Password: 'Hasło',
button: { Login: 'Zaloguj' }
}
}
const _ = translate.bind(0, locales, 'pl')
it('should handle defined texts', () => {
_('Login').should.be.equal('Login')
_('Password').should.be.equal('Hasło')
})
it('should handle undefined text', () => {
_('Undefined text').should.be.equal('Undefined text')
})
it('should handle defined version', () => {
_('Login', 'button').should.be.equal('Zaloguj')
})
it('should handle undefined version', () => {
_('Password', 'undefined_version').should.be.equal('Hasło')
_('Undefined text', 'undefined_version').should.be.equal('Undefined text')
})
it('should have empty prototype', () => {
_('toString').should.be.equal('toString')
_('toString', 'undefined_version').should.be.equal('toString')
})
translate
Language translations helper.
🔴 Parameter: locales
{
[lang: string]: {
[text_or_version: string]: string | {
[text: string]: string;
};
};
}
🔴 Parameter: language
string
🔴 Parameter: text
string
🔴 Parameter: version
string=
🟢 Returns
string
get_navigator_language
🔴 Parameter: locales
{
[lang: string]: {
[text_or_version: string]: string | {
[text: string]: string;
};
};
}
🔴 Parameter: default_language
string
Module: type_of
A collection of type testing helpers.
Usage example from unit tests:
import { it } from '../src/it.js'
import {
is_Array,
is_AsyncFunction,
is_Boolean,
is_Date,
is_Element,
is_Function,
is_GeneratorFunction,
is_HTMLButtonElement,
is_HTMLInputElement,
is_HTMLTextAreaElement,
is_Number,
is_Object,
is_Promise,
is_RegExp,
is_String,
is_Text,
type_of
} from '../src/type_of.js'
it('should work', () => {
expect(typeof []).to.be.equal('object')
type_of([]).should.be.equal('Array')
is_Array([]).should.be.true
expect(typeof (async () => { })).to.be.equal('function')
type_of(async () => { }).should.be.equal('AsyncFunction')
is_AsyncFunction(async () => { }).should.be.true
expect(typeof true).to.be.equal('boolean')
type_of(true).should.be.equal('Boolean')
is_Boolean(true).should.be.true
expect(typeof new Date()).to.be.equal('object')
type_of(new Date()).should.be.equal('Date')
is_Date(new Date()).should.be.true
expect(typeof document.createElement('DIV')).to.be.equal('object')
type_of(document.createElement('DIV')).should.be.equal('HTMLDivElement')
is_Element(document.createElement('DIV')).should.be.true
expect(typeof function * () {}).to.be.equal('function')
type_of(function * () {}).should.be.equal('GeneratorFunction')
is_GeneratorFunction(function * () {}).should.be.true
expect(typeof type_of).to.be.equal('function')
type_of(type_of).should.be.equal('Function')
is_Function(type_of).should.be.true
is_HTMLButtonElement(document.createElement('BUTTON')).should.be.true
is_HTMLInputElement(document.createElement('INPUT')).should.be.true
is_HTMLTextAreaElement(document.createElement('TEXTAREA')).should.be.true
expect(typeof 42).to.be.equal('number')
type_of(42).should.be.equal('Number')
is_Number(42).should.be.true
expect(typeof NaN).to.be.equal('number')
type_of(NaN).should.be.equal('Number')
is_Number(NaN).should.be.true
expect(typeof Infinity).to.be.equal('number')
type_of(Infinity).should.be.equal('Number')
is_Number(Infinity).should.be.true
expect(typeof {}).to.be.equal('object')
type_of({}).should.be.equal('Object')
is_Object({}).should.be.true
expect(typeof Promise.resolve()).to.be.equal('object')
type_of(Promise.resolve()).should.be.equal('Promise')
is_Promise(Promise.resolve()).should.be.true
expect(typeof /^(Reg)(Exp)$/).to.be.equal('object')
type_of(/^(Reg)(Exp)$/).should.be.equal('RegExp')
is_RegExp(/^(Reg)(Exp)$/).should.be.true
expect(typeof 'Jackens').to.be.equal('string')
type_of('Jackens').should.be.equal('String')
is_String('Jackens').should.be.true
expect(typeof String('Jackens')).to.be.equal('string')
type_of(String('Jackens')).should.be.equal('String')
is_String(String('Jackens')).should.be.true
expect(typeof new String('Jackens')).to.be.equal('object')
type_of(new String('Jackens')).should.be.equal('String')
is_String(new String('Jackens')).should.be.true
is_Text(document.createTextNode('')).should.be.true
})
type_of
Replacement for the typeof operator that works properly.
🔴 Parameter: arg
any
🟢 Returns
string
is_Array
Helper that checks if the arg is of type Array.
🔴 Parameter: arg
any
🟢 Returns
arg is Array
is_AsyncFunction
Helper that checks if the arg is of type AsyncFunction.
🔴 Parameter: arg
any
🟢 Returns
boolean
is_Boolean
Helper that checks if the arg is of type Boolean.
🔴 Parameter: arg
any
🟢 Returns
arg is Boolean
is_Date
Helper that checks if the arg is of type Date.
🔴 Parameter: arg
any
🟢 Returns
arg is Date
is_Element
Helper that checks if the arg is of type Element.
🔴 Parameter: arg
any
🟢 Returns
arg is Element
is_Function
Helper that checks if the arg is of type Function.
🔴 Parameter: arg
any
🟢 Returns
arg is Function
is_GeneratorFunction
Helper that checks if the arg is of type GeneratorFunction.
🔴 Parameter: arg
any
🟢 Returns
arg is GeneratorFunction
is_HTMLButtonElement
Helper that checks if the arg is of type HTMLButtonElement.
🔴 Parameter: arg
any
🟢 Returns
arg is HTMLButtonElement
is_HTMLInputElement
Helper that checks if the arg is of type HTMLInputElement.
🔴 Parameter: arg
any
🟢 Returns
arg is HTMLInputElement
is_HTMLTextAreaElement
Helper that checks if the arg is of type HTMLTextAreaElement.
🔴 Parameter: arg
any
🟢 Returns
arg is HTMLTextAreaElement
is_Number
Helper that checks if the arg is of type Number.
🔴 Parameter: arg
any
🟢 Returns
arg is Number
is_Object
Helper that checks if the arg is of type Object.
🔴 Parameter: arg
any
🟢 Returns
arg is Object
is_Promise
Helper that checks if the arg is of type Promise.
🔴 Parameter: arg
any
🟢 Returns
arg is Promise
is_RegExp
Helper that checks if the arg is of type RegExp.
🔴 Parameter: arg
any
🟢 Returns
arg is RegExp
is_String
Helper that checks if the arg is of type String.
🔴 Parameter: arg
any
🟢 Returns
arg is String
is_Text
Helper that checks if the arg is of type Text.
🔴 Parameter: arg
any
🟢 Returns
arg is Text
Module: uuid_v1
A collection of UUID v1 helpers.
Usage example from unit tests:
import { it } from '../src/it.js'
import { uuid_v1, uuid_v1_decode } from '../src/uuid_v1.js'
it('should contain a global counter', () => {
for (let i = 0; i < 4200; ++i) {
const counter = uuid_v1().split('-')[3]
if (i === 0) {
counter.should.be.equal('8001')
} else if (i === 4094) {
counter.should.be.equal('8fff')
} else if (i === 4095) {
counter.should.be.equal('8000')
} else if (i === 4096) {
counter.should.be.equal('8001')
}
}
})
it('should match regexp pattern', () => {
uuid_v1().should.match(
/^[\da-f]{8}-[\da-f]{4}-1[\da-f]{3}-8[\da-f]{3}-[\da-f]{12}$/)
})
it('should support the specified date and value', () => {
const uuid_1 = uuid_v1(new Date(323325e6), 205163983024656)
uuid_1.startsWith('c1399400-9a71-11bd').should.be.true
uuid_1.endsWith('-ba9876543210').should.be.true
const uuid_2 = uuid_v1(new Date(323325e6), 486638959735312)
uuid_2.startsWith('c1399400-9a71-11bd').should.be.true
uuid_2.endsWith('-ba9876543210').should.be.true
})
it('should decode date', () => {
const date_1 = new Date()
const uuid = uuid_v1(date_1)
const date_2 = uuid_v1_decode(uuid)
expect(+date_2).to.be.equal(+date_1)
})
uuid_v1
UUID v1 identifier (containing creation timestamp) generator.
🔴 Parameter: date
Date=
🔴 Parameter: value
number=
🟢 Returns
string
uuid_v1_decode
Date extractor from UUID v1 identifier.
🔴 Parameter: uuid
string
🟢 Returns
Date
Module: wsc
Generic JSON RPC 2.0 WebSocket Client.
Usage examples:
<script type="module">
import { wsc } from '../../src/wsc.js'
window.onload = async () => {
const fs = await wsc('ws://localhost:12345/')
const list = await fs('list', { path: 'src' })
const file = await fs('read', { path: 'src/wsc.js' })
console.log(list)
console.log(file)
}
</script>
<script type="module">
import { wsc } from '../../src/wsc.js'
window.onload = async () => {
const mariadb = await wsc('ws://localhost:13306/')
const status = await mariadb('connect', {
config: { host: 'localhost', user: 'root', password: 'qwerty' }
})
const rows = await mariadb('query', { query: 'SELECT CURRENT_TIMESTAMP' })
console.log(status)
console.log(rows)
}
</script>
Type definition: WSC_HANDLERS
{
[id: string]: [function, function];
}
Type definition: WSC_SEND
🔴 Parameter: method
string
🔴 Parameter: params
{
[key: string]: any;
}
🟢 Returns
Promise<any>
wsc
Generic JSON RPC 2.0 WebSocket Client.
🔴 Parameter: url
string
🔴 Parameter: handlers
WSC_HANDLERS=
🟢 Returns
Promise<WSC_SEND>
Module: wss
Generic JSON RPC 2.0 WebSocket Server.
Type definition: WSS_METHOD
🔴 Parameter: param0
{
clients: WebSocket[];
client: WebSocket;
params: {
[param_name: string]: any;
};
}
🟢 Returns
Promise<any>
wss
Generic JSON RPC 2.0 WebSocket Server.
🔴 Parameter: methods
{
[method_name: string]: WSS_METHOD;
}
🔴 Parameter: port
number
Module: wss_fs
File System JSON RPC 2.0 WebSocket Server.
Usage example:
import { wss_fs } from '../../src/wss_fs.js'
wss_fs(12345)
wss_fs
File System JSON RPC 2.0 WebSocket Server.
🟠 Type
(port: number) => void
Module: wss_mariadb
MariaDB JSON RPC 2.0 WebSocket Server.
Usage example:
import { wss_mariadb } from '../../src/wss_mariadb.js'
wss_mariadb(13306)
wss_mariadb
MariaDB JSON RPC 2.0 WebSocket Server.
🟠 Type
(port: number) => void
License
MIT License
Copyright (c) 2016+ Jackens
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.