npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hlp

v3.4.6

Published

[![build status](https://github.com/vielhuber/hlp/actions/workflows/ci.yml/badge.svg)](https://github.com/vielhuber/hlp/actions)

Downloads

172

Readme

build status

motivation

tired of writing

if( Object.keys(obj).length === 0 && obj.constructor === Object )
{

}

or

if (typeof arr !== 'undefined' && arr.length > 0)
{

}

or

for(const [obj__key, obj__value] of Object.entries(obj))
{

}

?

installation

npm init -y
npm install hlp

now use it as a module:

import hlp from 'hlp';

or embed it directly:

<script src="hlp.js"></script>

usage

existence

// check existence
if( hlp.x(vrbl) )
{

}

// check non-existence
if( hlp.nx(vrbl) )
{

}

equality

// js has a lot of pitfalls, when comparing loosely
if( '' == [] ) // true
if( '' == [''] ) // true
if( '' == 0 ) // true
if( ' ' == false ) // true
if( [0] == false ) // true
if( [0] == '0' ) // true
if( [] == false ) // true
if( [''] == false ) // true
if( 0 == false ) // true
if( 0 == [] ) // true
if( 0 == [''] ) // true
if( [0] == false ) // true
if( [0] == 0 ) // true

// also don't forget those delicacies
0 === -0 // true
NaN === NaN // false
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]] === 'fail' // true

// this non-strict equality is symmetric, but not transitive
a = ''; b = 0; c = [0];
a == b; // true
b == c; // true
c == a; // false

// to overcome this issue, we...

// ...use strict comparison when possible
if( vrbl === 'foo' )
{

}

// ...use loose comparison when appropriate
if( hlp.getParam('number') == 1337 )
{

}

// ...check for truthness / falsiness with these helper methods
if( hlp.true(vrbl) )
{

}

if( hlp.false(vrbl) )
{

}

// be aware, that hlp.true is not always the logic negation of hlp.false
hlp.true(null) // false
hlp.false(null) // false

value

// get variable if exists, otherwise ''
hlp.v( vrbl )

// get variable if exists, otherwise 'default'
hlp.v( vrbl, 'default' )

// get first variable that exists, otherwise ''
hlp.v( vrbl1, vrbl2, vrbl3 )

loop

// loop over arrays/objects only if possible
hlp.loop(['foo','bar','baz'], (vrbl__value) =>
{

});
hlp.loop(['foo','bar','baz'], (vrbl__value, vrbl__key) =>
{

});
hlp.loop({bar: 'foo', baz: 'bar', foo: 'baz'}, (vrbl__value, vrbl__key) =>
{

});
hlp.loop([], (vrbl__value, vrbl__key) => { }) // does nothing
hlp.loop({}, (vrbl__value, vrbl__key) => { }) // does nothing
hlp.loop(null, (vrbl__value, vrbl__key) => { }) // does nothing

try

// if you are unsure, if a variable is even set before checking its existence,
// simply put it inside this helper function
if( hlp.x(() => vrbl) )
if( hlp.nx(() => vrbl)  )
if( hlp.true(() => vrbl) )
if( hlp.false(() => vrbl)  )
if( hlp.v(() => vrbl) === 'foo' )
if( hlp.v(() => vrbl) == 1337 )
echo hlp.v(() => vrbl)
hlp.loop((() => vrbl), (vrbl__value, vrbl__key) => { })

that works because javascript only evaluates the content of the inner callback (or closure) when it is actually executed.

helpers

there are also some other neat little helpers available:

// capitalize
hlp.capitalize('foo') // Foo

// check if object
hlp.isObject({}) // true
hlp.isObject({foo: 'bar'}) // true
hlp.isObject(null) // false
hlp.isObject([]) // false (be aware: an array in js is scrictly an object, but this function returns false)

// check if array
hlp.isArray([]) // true
hlp.isArray(['foo','bar']) // true
hlp.isArray(null) // false

// check if string
hlp.isString('foo'); // true
hlp.isString(42); // false
hlp.isString(null); // false

// check if date
hlp.isDate('2018-01-01') // true
hlp.isDate('2018-02-29') // false
hlp.isDate('1700-01-01') // true
hlp.isDate(42) // false

// format date
hlp.formatDate('d.m.Y', '2018-01-01') // 01.01.2018
hlp.formatDate('Y-m-d H:i:s', new Date()) // 01.01.2018

// get week number from date
hlp.dateToWeek(new Date('2021-02-22')) // 8
hlp.dateToWeek() // hlp.dateToWeek(new Date())

// get date (of monday) from week number
hlp.weekToDate(42, 2018) // new Date('2018-10-14')
hlp.weekToDate(17, 2023) // new Date('2023-04-23')

// add days to date
hlp.addDays(new Date('2018-01-01'), 7) // new Date('2018-01-08')
hlp.addDays(new Date('2018-02-22'), 658) // new Date('2019-12-12')
hlp.addDays(new Date('2018-02-22'), 1) // new Date('2018-02-21')

// compare dates
let d1 = new Date();
let d2 = new Date();
hlp.compareDates(d1, d2) // 0
hlp.addDays(d1, -1);
hlp.compareDates(d1, d2) // -1
hlp.addDays(d1, 2);
hlp.compareDates(d1, d2) // 1
hlp.compareDates('2020-01-01', '2020-01-17 17:42:19') // -1

// spaceship operator
hlp.spaceship(5,7) // -1
hlp.spaceship(9,7) // 1
hlp.spaceship(7,7) // 0
hlp.spaceship('foo','bar') // 1
hlp.spaceship('bar','foo') // -1

// check if objects are equal
hlp.objectsAreEqual({}, {}) // true
hlp.objectsAreEqual({ foo: 'bar' }, { foo: 'bar'}) // true
hlp.objectsAreEqual({ foo: 'bar' }, { bar: 'baz'}) // false

// check if object is inside an array/object
hlp.containsObject({ foo: 'bar' }, []) // false
hlp.containsObject({ foo: 'bar' }, [{ foo: 'bar' }, { bar: 'baz' }]) // true
hlp.containsObject({ foo: 'bar' }, { foo: { foo: 'bar' } }) // true

// recursively search key/value in nested object and return dotprop array
hlp.findRecursiveInObject({ foo: { id: 42 }, bar: { foo: { id: 7 } }, baz: { id1: 42, id2: 7 } }, 'id'); // ['foo', 'bar.foo']
hlp.findRecursiveInObject({ foo: { id: 42 }, bar: { foo: { id: 7 } }, baz: { id1: 42, id2: 7 } }, 'id', 42); // ['foo']
hlp.findRecursiveInObject({ foo: { id: 42 }, bar: { foo: { id: 7 } }, baz: { id1: 42, id2: 7 } }, null, 7); // ['bar.foo', 'baz']

// deep clone reference types (object/array/date/regex)
hlp.deepCopy({ foo: 'bar' })
hlp.deepCopy(['foo','bar'])
hlp.deepCopy(new Date())
hlp.deepCopy(new Date('2018-01-01'))
hlp.deepCopy(new RegExp('ab+c', 'i'))

// generate uuid/guid v4
hlp.uuid() // e86e393c-9788-857b-27c2-f80c8ca1a302
hlp.uuid() // 8b25a8f8-9525-bd73-4679-3539321db93b

// replace all occurences
hlp.replaceAll('foo bar baz', 'a', 'b') // 'foo bbr bbz'

// replace last occurence
hlp.replaceLast('foo bar baz', 'a', 'b') // 'foo bar bbz'

// replace last occurence
hlp.replaceFirst('foo bar baz', 'a', 'b') // 'foo bbr baz'

// case insensitive search
hlp.indexOfCaseInsensitive('foo', 'this is a FOO') // 10
hlp.indexOfCaseInsensitive('foo', 'this is a FOO and a foobar', 15) // 20

// count occurences in string
hlp.countAllOccurences('foo', 'this is a foo and a foobar') // 2
hlp.countAllOccurencesCaseInsensitive('FoO', 'this is a FOO and a foobar') // 2

// find all positions in string
hlp.findAllPositions('foo', 'this is a foo and a foobar') // [10,20]
hlp.findAllPositionsCaseInsensitive('FoO', 'this is a FOO and a foobar') // [10,20]

// highlight strings
hlp.highlight('that is a search string', 'is') // that <strong class="highlight">is</strong> a search string
hlp.highlight('abc def geh ijk lmn opq rst abc def geh ijk lmn opq rst', 'ijk', true, 5) // '... geh <strong class="highlight">ijk</strong> lmn ... geh <strong class="highlight">ijk</strong> lmn ...'

// remove empty elements from array
hlp.removeEmpty(['foo',null,[],'','bar']) // ['foo','bar']

// return unique array (remove duplicate values, order-safe)
hlp.uniqueArray(['foo','bar','foo','baz']) // ['foo','bar','baz']

// powerset of array (all subsets of a set)
hlp.powerset([1,2,3]) // [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

// shuffle array
hlp.shuffle(['foo','bar']) // ['foo','bar']
hlp.shuffle(['foo','bar']) // ['foo','bar']
hlp.shuffle(['foo','bar']) // ['foo','bar'] (yikes)
hlp.shuffle(['foo','bar']) // ['bar','foo']

// char helpers
hlp.charToInt('D') // 4
hlp.intToChar(4) // 'D'
hlp.incChar('D') // 'E'
hlp.incChar('Z') // 'AA'
hlp.incChar('A',2) // 'C'
hlp.decChar('U') // 'T'

// slugify / sanitize string
hlp.slugify('That röcks!') // that-roecks

// range
hlp.range('A','Z') // ['A','B',...,'Z']
hlp.range(1,42) // [1,2,...,42]
hlp.range('C','A') // ['C','B','A']

// get last item of object/array
hlp.last(['foo', 'bar', 'baz']) // 'baz'
hlp.last({ foo: 'bar', bar: 'baz'}) // 'baz'

// get first item of object/array
hlp.first(['foo', 'bar', 'baz']) // 'foo'
hlp.first({ foo: 'bar', bar: 'baz'}) // 'bar'

// get random element from object/array
hlp.rand(['foo', 'bar', 'baz']) // 'bar'

// generate a random string
hlp.random_string() // edPhi34d
hlp.random_string(10) // abCa321aC6
hlp.random_string(16, 'idkfa') // idifafafifaifafk

// generate a random integer
hlp.random_int() // 42
hlp.random_int(7,42) // 17

// proper rounding to n digits
hlp.round(1.005, 2) // 1.01
hlp.round(1.005) // 1

// check if variable is integer
hlp.isInteger('foo') // false
hlp.isInteger(42) // true
hlp.isInteger('42') // true
hlp.isInteger(4e2) // true
hlp.isInteger('4e2') // true
hlp.isInteger(' 1 ') // true
hlp.isInteger('') // false
hlp.isInteger('  ') // false
hlp.isInteger(42.1) // false
hlp.isInteger('1a') // false
hlp.isInteger('4e2a') // false
hlp.isInteger(null) // false
hlp.isInteger(undefined) // false
hlp.isInteger(NaN) // false

// check if variable is numeric
hlp.isNumeric(1337) // true
hlp.isNumeric('42') // true
hlp.isNumeric('42.7') // true
hlp.isNumeric('a') // false

// json parsing
hlp.jsonStringToObject('["foo","bar","baz"]') // ['foo','bar','baz']
hlp.jsonStringToObject('["foo","bar","baz",]') // null
hlp.jsonObjectToString(['foo','bar','baz']) // '["foo","bar","baz"]'
hlp.isJsonString('["foo","bar","baz",]') // false
hlp.isJsonString('["foo","bar","baz"]') // true

// map for objects
hlp.map({ foo: 'bar', bar: 'baz' }, (obj__key, obj__value) => obj__value += '!'); // { foo: 'bar!', bar: 'baz!' }

// fun with blobs
hlp.stringtoblob(string)
hlp.stringtoblob(string, 'image/png')
hlp.blobtostring(blob).then((string) => { })
hlp.blobtobase64(blob).then((base64) => { })
hlp.base64toblob(base64)
hlp.base64toblob(base64, 'image/png')
hlp.filetobase64(file).then((base64) => { })
hlp.blobtofile(blob)
hlp.blobtofile(blob, 'filename.png')
hlp.filetoblob(file)
hlp.base64tofile(base64)
hlp.base64tofile(base64, 'image/png')
hlp.base64tofile(base64, 'image/png', 'filename.png')
hlp.base64tostring(base64)
hlp.stringtobase64(string)
hlp.blobtourl(blob)
hlp.stringtourl(string)
hlp.base64tourl(base64)
hlp.filetourl(file)

// fix exif image orientation
hlp.fixImageOrientation(base64).then((base64) => { });
hlp.getImageOrientation(base64).then((orientation) => { });

// html entity encode/decode
hlp.htmlEncode('&<>"`\'') // &amp;&lt;&gt;&quot;&#96;&#x27;
hlp.htmlDecode('&amp;&lt;&gt;&quot;&#96;&#x27;') // &<>"`'

// line break conversion
hlp.nl2br('foo\nbar') // foo<br/>bar
hlp.br2nl('foo<br/>bar') // foo\nbar

// floating point math made easy
hlp.fmath('*', 0.1, 0.2) // 0.02
hlp.fmath('+', 0.1, 0.2) // 0.3
hlp.fmath('-', 0.1, 0.2) // -0.1
hlp.fmath('/', 0.2, 0.1) // 2
hlp.fmath('/', 0.39, 100, 12) // 0.0039 (precision of 12)

// trim helpers
hlp.trim('  foo ') // 'foo'
hlp.trim('xxfoox', 'x') // 'foo'
hlp.ltrim('  foo ') // 'foo '
hlp.ltrim('xxfoox', 'x') // 'foox'
hlp.rtrim('  foo ') // '  foo'
hlp.rtrim('xxfoox', 'x') // 'xxfoo'

// truncate/trim long strings
hlp.truncate_string('Lorem ipsum dolor sit amet, consectetuer.', 20); // Lorem ipsum dolor ...
hlp.truncate_string('Lorem ipsum dolor sit amet, consectetuer.', 20, '…'); // Lorem ipsum dolor …

// fun with emojis
let str = 'This❤️😀👩‍⚖️ is a text full of 🧗‍♀️emojis👩🏼‍❤️‍💋‍👩🏽.';
str.match(hlp.emojiRegex()) // ['❤️', '😀', '👩‍⚖️', '🧗‍♀️', '👩🏼‍❤️‍💋‍👩🏽']
str.replaceAll(hlp.emojiRegex(), '') // This is a text full of emojis.
str.replace(hlp.emojiRegex(false), '') // This😀👩‍⚖️ is a text full of 🧗‍♀️emojis👩🏼‍❤️‍💋‍👩🏽.
hlp.emojiRegex().test(str) // true

// create lexicographically ordered string ids like in firebase
hlp.pushId() // -LDiDooGs9PyGHmghk5i
hlp.pushId() // -LDiDooGs9PyGHmghk5j

// access object properties with dotprop notation
// today it is better to use optional chaining in combination with nullish coalescing ({}?.c?.a?.a??'default')
hlp.getProp({
    a: 1,
    b: { a: 3, b: 3 },
    c: { a: { a: 7 } }
}, 'c.a.a') // 7

check out also the following helpers for the frontend:

// cookies
hlp.cookieSet('foo', 'bar', 7)
hlp.cookieSet('foo', 'bar', 7, false) // only for current domain (no subdomains)
hlp.cookieGet('foo') // bar
hlp.cookieDelete('foo')
hlp.cookieDelete('foo', false) // only for current domain (no subdomains)
hlp.cookieExists('foo') // true|false

// localstorage (with expiration time and object support)
hlp.localStorageSet('foo', {some: 'data'}, 7)
hlp.localStorageGet('foo') // {'some': 'data'}
hlp.localStorageDelete('foo')
hlp.localStorageExists('foo') // true|false

// get parameter (example url: https://tld.com/?foo=bar&bar=baz)
hlp.getParam('foo') // foo
hlp.getParam('bar') // baz
hlp.getParam('baz') // null

// device detection helpers
hlp.getDevice() // ['phone','tablet','desktop']
hlp.isPhone()
hlp.isTablet()
hlp.isDesktop()
hlp.isMobile()
hlp.isTouch()

// os detection helpers
hlp.getOs() // ['windows','mac','linux','unknown']
hlp.isWindows()
hlp.isMac()
hlp.isLinux()

// browser detection helpers
hlp.getBrowser() // ['ie','edge','firefox','chrome','safari','opera','unknown']

// smooth scroll to position / element
hlp.scrollTo( 0, 1000 ).then(() => { console.log('done'); });
hlp.scrollTo( document.querySelector('.foo'), 1000 ).then(() => { console.log('done'); });
hlp.scrollTo( 0, 1000, document.querySelector('.bar') ).then(() => { console.log('done'); }); // scoll inside .bar
hlp.scrollTo( document.querySelector('.foo'), 1000, null, -200 ).then(() => { console.log('done'); }); // apply offset
hlp.scrollTo( document.querySelector('.foo'), 1000, null, document.querySelector('.header') ).then(() => { console.log('done'); }); // apply offset (height of dom element only if it exists and is fixed!)
hlp.scrollTo( document.querySelector('.foo'), 1000, null, [document.querySelector('.header'), -200] ).then(() => { console.log('done'); }); // you can also provide multiple values (of different type)
hlp.scrollTo( document.querySelector('.foo'), 1000, null, -200, true ).then(() => { console.log('done'); }); // only scroll up (never down)

// get top/left scroll position
hlp.scrollTop()
hlp.scrollLeft()

// get closest vertical scrollable element (including oneself)
hlp.closestScrollable( document.querySelector('.foo') )

// get offset of element (excluding margin)
hlp.offsetTop( document.querySelector('.foo') )
hlp.offsetLeft( document.querySelector('.foo') )
hlp.offsetRight( document.querySelector('.foo') )
hlp.offsetBottom( document.querySelector('.foo') )

// get offset of element (including margin)
hlp.offsetTopWithMargin( document.querySelector('.foo') )
hlp.offsetLeftWithMargin( document.querySelector('.foo') )
hlp.offsetRightWithMargin( document.querySelector('.foo') )
hlp.offsetBottomWithMargin( document.querySelector('.foo') )

// get document size
hlp.documentWidth()
hlp.documentHeight()

// get window size
hlp.windowWidth()
hlp.windowHeight()
hlp.windowWidthWithoutScrollbar()
hlp.windowHeightWithoutScrollbar()

// get width with margin
hlp.outerWidthWithMargin( document.querySelector('.foo') )
hlp.outerHeightWithMargin( document.querySelector('.foo') )

// get cursor position (without mouse events)
hlp.cursorPosition().then(pos => { console.log(pos); /* pos: { x: ..., y: ... } */ });

// polyfills for ie11
hlp.closest( document.querySelector('.children'), '.parent' )
hlp.matches( document.querySelector('.parent'), '.parent' ) // true
hlp.remove( document.querySelector('.foo') ) // also works if .foo does not exist

// dom traversal
hlp.prevAll( document.querySelector('.foo') )
hlp.prevAll( document.querySelector('.foo'), '.bar' )
hlp.nextAll( document.querySelector('.foo') )
hlp.nextAll( document.querySelector('.foo'), '.bar' )
hlp.siblings( document.querySelector('.foo') )
hlp.siblings( document.querySelector('.foo'), '.bar' )
hlp.parents( document.querySelector('.foo') )
hlp.parents( document.querySelector('.foo'), '.bar' )
hlp.prevUntil( document.querySelector('.foo'), '.bar' ) // prev until selector not including
hlp.nextUntil( document.querySelector('.foo'), '.bar' ) // next until selector not including
hlp.prev( document.querySelector('.foo') )
hlp.prev( document.querySelector('.foo'), '.bar' )
hlp.next( document.querySelector('.foo') )
hlp.next( document.querySelector('.foo'), '.bar' )

// wrap element
hlp.wrap( document.querySelector('.foo'), '<div class="wrapper"></div>' )

// wrap all text nodes with new node
hlp.wrapTextNodes( document.querySelector('.foo'), 'p' )

// html string to dom (also supports ie11 and td nodes that cannot be root nodes)
hlp.html2dom('<p>foo</p>')
hlp.html2dom('<td>bar</td>')

// get all styles of a dom element (extracted from both inline styling and external styling through stylesheets)
hlp.css( document.querySelector('.foo') )

// visually focus element on page
hlp.focus('.foo')
hlp.focus(document.querySelector('.foo'))
hlp.unfocus()

// on delegate
hlp.on('click', '.selector', (e, el) => { });
hlp.on('click', '.selector', '.scope', (e, el) => { });

// classic debounce
window.addEventListener('resize', hlp.debounce(() => { console.log('debounce at resize') }, 1000));
document.querySelector('.container').addEventListener('input', hlp.debounce((e) => { console.log('debounce at '+e.target.value); }, 1000));
let debounce = hlp.debounce((e) => { console.log('debounce at '+e.target.value); }, 1000); // conditional debounce
document.querySelector('.container').addEventListener('input', e => { debounce(e); });

// classic throttle
window.addEventListener('resize', hlp.throttle(() => { console.log('throttle at resize') }, 1000));
document.querySelector('.container').addEventListener('input', hlp.throttle((e) => { console.log('throttle at '+e.target.value); }, 1000));
let throttle = hlp.throttle((e) => { console.log('throttle at '+e.target.value); }, 1000); // conditional throttle
document.querySelector('.container').addEventListener('input', e => { throttle(e); });

// get current url
hlp.url() // https://github.com/vielhuber/hlp
hlp.urlWithHash() // https://github.com/vielhuber/hlp#foo
hlp.fullUrl() // https://github.com/vielhuber/hlp?foo=bar#foo
hlp.urlWithArgs() // https://github.com/vielhuber/hlp?foo=bar
hlp.baseUrl() // https://github.com
hlp.urlHost(); // github.com
hlp.urlHostTopLevel(); // github.com
hlp.urlPath(); // /vielhuber/hlp
hlp.urlHash(); // #foo
hlp.urlArgs(); // ?foo=bar

// get url of current running script
hlp.urlOfScript(); // https://tld.com/wp-content/themes/dummy/script.js
hlp.pathOfScript(); // https://tld.com/wp-content/themes/dummy

// set 100vh for a dom element (respecting the visibility of the address bar)
hlp.real100vh('.foo') // 100vh
hlp.real100vh('.foo', 60) // 60vh
hlp.real100vh() // sets up a css variable which can be used with e.g. calc(100 * var(--vh, 1vh)); to mimic 100vh

// remove hover states on ios to prevent double clicks (see https://stackoverflow.com/questions/47802530/a-click-in-ios-safari-triggers-a-hover-state-on-element-underneath-where-you-t);
hlp.iOsRemoveHover();

// fade in/out dom element
hlp.fadeIn( document.querySelector('.foo'), 1000 ).then(() => { console.log('done'); });
hlp.fadeOut( document.querySelector('.foo'), 1000 ).then(() => { console.log('done'); });

// check if dom element is generally visible
hlp.isVisible( document.querySelector('.foo') )
// check if dom element is visible inside viewport
hlp.isVisibleInViewport( document.querySelector('.foo') )

// wait until a dom element has a certain css property
// this is quite useful when working with async loaded stylesheets like loadCSS
// .beacon is an element below the fold populated by the stylesheet
hlp.waitUntil('.beacon').then(() => { });
hlp.waitUntil('.beacon','position').then(() => { });
hlp.waitUntil('.beacon','position','relative').then(() => { });

// wait until a variable is set or has a specific value
hlp.waitUntilVar('globalVar').then(() => { });
hlp.waitUntilVar(obj, 'objectVar').then(() => { });
hlp.waitUntilVar(obj, 'objectVar', true).then(() => { });

// run a function for every dom element, even it is added dynamically later on
hlp.runForEl('.beacon', el => { el.style.backgroundColor = 'red'; });

// automatically change height of all textareas based on content
hlp.textareaAutoHeight()
hlp.textareaAutoHeight('.special')
hlp.textareaSetHeight( document.querySelector('.special') )

// load external js file in dom with promise
hlp.loadJs('https://apis.google.com/js/api.js').then(() => { console.log('done'); });
hlp.loadJs([
    'https://www.tld.com/1.js',
    'https://www.tld.com/2.js',
    'https://www.tld.com/3.js'
]).then(() => { console.log('done'); });
hlp.loadJsSequentially([
    'https://www.tld.com/1.js',
    'https://www.tld.com/2.js',
    'https://www.tld.com/3.js'
]).then(() => { console.log('done'); });

// run event after all images are loaded inside container
// works even after dynamic changes
// runs more than once (after each change)
// run this outside of window load / document ready events
hlp.triggerAfterAllImagesLoaded('.container', '.container__image', () => {});

// proper document read/load events, that are guaranteed to be run (also if the script is embedded via async)
hlp.ready().then(() => {});
hlp.load().then(() => {});

// easy ajax requests (without the fetch api; also works in ie11)
hlp.get('https://httpbin.org/anything').then((response) => { }).catch((error) => { }) // { "method": "GET", ... }
hlp.get('/relpath').then((response) => { }).catch((error) => { }) // if a full url is omitted, the call is done on the baseurl
hlp.get('https://httpbin.org/anything', { throttle: 1000 }).then((response) => { }).catch((error) => { }) // same but with a throttle of 1 second
hlp.get('https://httpbin.org/status/404', { allow_errors: false }).then((response) => { }) // deny 404 and other status codes as a response (inside catch())
hlp.post('https://httpbin.org/anything', { data: { foo: 'bar' } }).then((response) => { }).catch((error) => { }) // { "method": "POST", "data": {"foo": "bar"}, ... }
hlp.post('https://httpbin.org/anything', { data: { foo: 'bar' }, headers: { Bar: 'baz' } }).then((response) => { }).catch((error) => { }) // { "method": "POST", "headers" = { "Bar": "baz", ... }, ... }
let formData = new FormData(); formData.append('foo', 'bar'); hlp.post('https://httpbin.org/anything', { data: formData }) // this also works with FormData

// on resize vertically/horizontally
window.addEventListener('resize', () => {}) // inaccurate, triggers too often (especially when scrolling on android/iphone)
hlp.onResizeHorizontal(() => {}) // only triggers when viewport width changes; also triggers on first run
hlp.onResizeVertical(() => {}) // only triggers when viewport height changes; also triggers on first run

// add event listener once
hlp.addEventListenerOnce(
    document.getElementById('foo'),
    'click',
    (event) => { alert('this gets called only once'); }
);
hlp.addEventListenerOnce(
    document.getElementById('foo'),
    'click',
    (event) => { if(1==1) { return false; } } // if you return false, the event listener is not removed
);

// simple animations (via css transitions)
hlp.animate(
    document.getElementById('.single'),
    'transform: translateX(0)',
    'transform: translateX(-100%)',
    'ease-in-out',
    1000
).then(() => { console.log('done'); });
hlp.animate(
    document.querySelectorAll('.multiple'),
    'opacity: 1; pointer-events:auto',
    'opacity: 0; pointer-events:none',
    'linear',
    5000
).then(() => { console.log('done'); });

php implementation

there is also a php implemenation stringhelper with similiar functions available.

appendix

existence matrix

| | hlp.x() | hlp.true() | hlp.false() | !== null | != null | !== false | != false | === true | == true | typeof !== 'undefined' | != undefined | !== undefined | if/else | ternary | length > 0 | != '' | !== '' | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | undefined | false | true | false | true | false | true | true | false | false | false | false | false | false | false | error | true | true | | null | false | false | false | false | false | true | true | false | false | true | false | true | false | false | error | true | true | | false | false | false | true | true | true | false | false | false | false | true | true | true | false | false | false | false | true | | true | true | true | false | true | true | true | true | true | true | true | true | true | true | true | false | true | true | | [] | false | false | false | true | true | true | false | false | false | true | true | true | true | true | false | false | true | | [''] | false | false | false | true | true | true | false | false | false | true | true | true | true | true | true | false | true | | 0 | true | false | true | true | true | true | false | false | false | true | true | true | false | false | false | false | true | | 1 | true | true | false | true | true | true | true | false | true | true | true | true | true | true | false | true | true | | -1 | true | true | false | true | true | true | true | false | false | true | true | true | true | true | false | true | true | | '0' | true | false | true | true | true | true | false | false | false | true | true | true | true | true | true | true | true | | '1' | true | true | false | true | true | true | true | false | true | true | true | true | true | true | true | true | true | | '-1' | true | true | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | '' | false | false | false | true | true | true | false | false | false | true | true | true | false | false | false | false | false | | ' ' | false | false | false | true | true | true | false | false | false | true | true | true | true | true | true | true | true | | 'null' | true | false | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | 'false' | true | false | true | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | 'true' | true | true | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | 'str' | true | true | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | [0,1] | true | true | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true | | [0] | true | true | false | true | true | true | false | false | false | true | true | true | true | true | true | true | true | | {} | false | false | false | true | true | true | true | false | false | true | true | true | true | true | false | true | true | | un.known.property | error | error | error | error | error | error | error | error | error | error | false | false | error | error | error | error | error | | (()=>un.known.property) | true | true | false | true | true | true | true | false | false | true | true | true | true | true | true | true | true |

loose comparison matrix

| == | undefined | null | false | true | [] | [''] | 0 | 1 | -1 | '0' | '1' | '-1' | '' | ' ' | 'null' | 'false' | 'true' | 'str' | [0,1] | [0] | {} | un.known.property | (()=>un.known.property) | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | undefined | true | true | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | error | false | | null | true | true | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | error | false | | false | false | false | true | false | true | true | true | false | false | true | false | false | true | true | false | false | false | false | false | true | false | error | false | | true | false | false | false | true | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | false | error | false | | [] | false | false | true | false | true | false | true | false | false | false | false | false | true | false | false | false | false | false | false | false | false | error | false | | [''] | false | false | true | false | false | true | true | false | false | false | false | false | true | false | false | false | false | false | false | false | false | error | false | | 0 | false | false | true | false | true | true | true | false | false | true | false | false | true | true | false | false | false | false | false | true | false | error | false | | 1 | false | false | false | true | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | false | error | false | | -1 | false | false | false | false | false | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | error | false | | '0' | false | false | true | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | true | false | error | false | | '1' | false | false | false | true | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | false | error | false | | '-1' | false | false | false | false | false | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | error | false | | '' | false | false | true | false | true | true | true | false | false | false | false | false | true | false | false | false | false | false | false | false | false | error | false | | ' ' | false | false | true | false | false | false | true | false | false | false | false | false | false | true | false | false | false | false | false | false | false | error | false | | 'null' | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | false | false | false | false | false | false | error | false | | 'false' | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | false | false | false | false | false | error | false | | 'true' | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | false | false | false | false | error | false | | 'str' | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | false | false | false | error | false | | [0,1] | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | false | false | error | false | | [0] | false | false | true | false | false | false | true | false | false | true | false | false | false | false | false | false | false | false | false | true | false | error | false | | {} | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | true | error | false | | un.known.property | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | error | | (()=>un.known.property) | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | false | error | true |