text:
#!/usr/bin/env python

"""Train a model.

Argv:
    output-dir: A folder to store any output to
    kernel: Kernel type to be used in the algorithm
    penalty: Penalty parameter of the error term
"""
import argparse
import joblib
import matplotlib.pyplot as plt
import numpy as np
import os
import sys

from sklearn import datasets
from sklearn.metrics import classification_report
from sklearn.metrics import plot_confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import StratifiedKFold
from sklearn.svm import SVC

from amlrun import get_AMLRun

def train(output_dir='outputs', kernel='linear', penalty=1.0):
    # make sure output directory exist
    os.makedirs(output_dir, exist_ok=True)

    # Safely get the Azure ML run
    run = get_AMLRun()

    # loading the iris dataset
    iris = datasets.load_iris()

    # X -> features, y -> label
    X = iris.data
    y = iris.target
    class_names = iris.target_names

    # dividing X, y into train and test data. Random seed for reproducability
    X_train, X_test, y_train, y_test = \
        train_test_split(X, y, test_size=0.20, random_state=0)

    # create our model - a linear SVM classifier
    svm_model_linear = SVC(kernel=kernel, C=penalty)

    # evaluate each model in turn
    kfold = StratifiedKFold(n_splits=10, random_state=1)
    cv_results = cross_val_score(svm_model_linear, X_train, y_train,
                                 cv=kfold, scoring='accuracy')

    print('Cross Validation Mean: ', cv_results.mean())
    print('Cross Validation Std: ', cv_results.std())
    if run is not None:
        run.log_list('Cross Validation Accuracies', cv_results)
        run.log('Cross Validation Mean', cv_results.mean())
        run.log('Cross Validation Std', cv_results.std())

    # now training on the full dataset
    svm_model_linear.fit(X_train, y_train)
    y_pred = svm_model_linear.predict(X_test)

    # model accuracy for X_test
    accuracy = svm_model_linear.score(X_test, y_test)
    print('Accuracy of SVM classifier on test set: {:.2f}'.format(accuracy))
    if run is not None:
        run.log('Accuracy', np.float(accuracy))

    # Plot non-normalized confusion matrix
    title = 'Test confusion matrix'
    disp = plot_confusion_matrix(svm_model_linear, X_test, y_test,
                                 display_labels=class_names,
                                 cmap=plt.cm.Blues)
    disp.ax_.set_title(title)
    print(title)
    print(disp.confusion_matrix)

    if run is not None:
        run.log_image(title, plot=plt)
    else:
        plt.savefig(os.path.join(output_dir, 'confusion_matrix.png'))

    # Plot normalized confusion matrix
    title = 'Normalized test confusion matrix'
    disp = plot_confusion_matrix(svm_model_linear, X_test, y_test,
                                 display_labels=class_names,
                                 cmap=plt.cm.Blues,
                                 normalize='true')
    disp.ax_.set_title(title)
    print(title)
    print(disp.confusion_matrix)

    if run is not None:
        run.log_image(title,  plot=plt)
    else:
        plt.savefig(
            os.path.join(output_dir, 'confusion_matrix_normalised.png'))

    # Print classification report
    print(classification_report(y_test, y_pred))

    # files saved in the "outputs" folder are automatically uploaded into
    # Azure ML Service run history
    model_folder = os.path.join(output_dir, 'model')
    model_path = os.path.join(model_folder, '{{cookiecutter.mlops_name}}.joblib')
    os.makedirs(model_folder, exist_ok=True)
    joblib.dump(svm_model_linear, model_path)
    print('Output saved to', output_dir)

def main(arguments):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    # environment parameters
    # parser.add_argument(
    #     '--data-folder',
    #     help="local path to training data",
    #     required=True
    # )

    parser.add_argument(
        "--output-dir", type=str,
        default=os.path.join('..', '..', 'data', 'training', 'outputs'),
        help='location to write output'
    )

    # training specific parameters
    parser.add_argument('--kernel', type=str, default='linear',
                        help='Kernel type to be used in the algorithm')
    parser.add_argument('--penalty', type=float, default=1.0,
                        help='Penalty parameter of the error term')

    # parse the arguments
    args = parser.parse_args(arguments)

    # setup output directory
    # model_output_dir = os.path.join(
    #     os.path.dirname(os.path.realpath(__file__)),
    #     args.output_dir)
    # os.makedirs(args.output-dir, exist_ok=True)

    train(args.output_dir, args.kernel, args.penalty)

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))


text:
import os
import subprocess
import tempfile
import nbformat

def run_notebook(filename):
    """
    Execute the specified notebook via jupyter nbconvert and collect output.
    :returns (parsed nb object, execution errors)
    """
    os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

    # get temporary file ( and then close to avoid multiple write problems
    with tempfile.NamedTemporaryFile(suffix=".ipynb") as fout:
        temp_name = fout.name

    # run jupyter nbconvert
    args = ["jupyter", "nbconvert", "--to", "notebook", "--execute",
            "--ExecutePreprocessor.timeout=60", "--output", fout.name, filename]
    subprocess.check_call(args, shell=True)

    # read and parse notebook
    with open(temp_name, "r") as fout:
        fout.seek(0)
        nb = nbformat.read(fout, nbformat.current_nbformat)

    errors = [output for cell in nb.cells if "outputs" in cell
              for output in cell["outputs"]
              if output.output_type == "error"]

    return nb, errors

# Commented out for now pending an update to automatically set the conda environment.
# This will work, but only if all libraries are in your default python environment. As I am using miniconda and the
# example notebook uses numpy from within a separate environment this doesn't work for me.
# def test_notebook():
#    nb, errors = run_notebook('notebooks\example.ipynb')
#    assert errors == []

repo_name: equinor/data-science-template

repo_language: Jupyter Notebook


text:
import os
import sys

# Explicitly set path so don't need to run setup.py - if we have multiple copies of the code we would otherwise need
# to setup a separate environment for each to ensure the code pointers are correct.
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src')))  # noqa

# from pandas.util.testing import assert_frame_equal
from {{cookiecutter.package_name}} import examplemodule

def test_something():
    # print(os.getcwd())
    assert True, "A comment to show if the test fails"

# def test_that_fails():
#     assert False, "We expected this to fail"

def test_hello_world():
    assert examplemodule.hello_world() == "Hello World", "The Hello World strings should be the same"


text:
import os
import sys
import numpy as np
import pytest

# Explicitly set path so don't need to run setup.py - if we have multiple copies of the code we would otherwise need
# to setup a separate environment for each to ensure the code pointers are correct.
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src')))  # noqa

# from pandas.util.testing import assert_frame_equal
from {{cookiecutter.package_name}} import examplemodule

def test_add_value_to_numpy():
    array = np.array([1, 1, 1, 1, 1])
    expected_result = np.array([2, 2, 2, 2, 2])
    result_array = examplemodule.add_value_to_numpy(array, 1)
    assert np.array_equal(expected_result, result_array), "The Hello World strings should be the same"

def test_add_value_to_numpy_wrong_type():
    with pytest.raises(ValueError) as _:
        examplemodule.add_value_to_numpy([1, 1], 1)

def test_add_value_to_numpy_empty():
    with pytest.raises(ValueError) as _:
        examplemodule.add_value_to_numpy(None, 1)


text:
Windows Registry Editor Version 5.00
;
; This adds additional explorer menus. Verify any path's below and add / remove conda environments as needed
;

;
; Right click on folder commands
;

[HKEY_CLASSES_ROOT\Directory\Shell\Data Science]
"MUIVerb"="Data Science"
"SubCommands"=""
"Position"=-

[HKEY_CLASSES_ROOT\Directory\Shell\Data Science\Shell\Run Jupyter Here]
@="Run Jupyter Here"
"Icon"="C:\\appl\\Applications\\Anaconda3\\Menu\\jupyter.ico"

[HKEY_CLASSES_ROOT\Directory\Shell\Data Science\Shell\Run Jupyter Here\command]
@="cmd.exe /K jupyter notebook"

;
; Right click on background commands
;

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science]
"MUIVerb"="Data Science"
"SubCommands"=""
"Position"=-

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here]
@="Run Jupyter Here"
"Icon"="C:\\appl\\Applications\\Anaconda3\\Menu\\jupyter.ico"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here\command]
@="cmd.exe /K jupyter notebook"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here (deeplearning env)]
@="Run Jupyter Here (deeplearning env)"
"Icon"="C:\\appl\\Applications\\Anaconda3\\Menu\\jupyter.ico"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here (deeplearning env)\command]
@="cmd.exe /K activate deeplearning & jupyter notebook"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here (anaconda env)]
@="Run Jupyter Here (anaconda env)"
"Icon"="C:\\appl\\Applications\\Anaconda3\\Menu\\jupyter.ico"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Data Science\Shell\Run Jupyter Here (anaconda env)\command]
@="cmd.exe /K activate anaconda & jupyter notebook"


text:
# Some original code Copyright (c) Audrey Roy Greenfeld and individual contributors -
# see https://github.com/audreyr/cookiecutter-pypackage/blob/master/LICENSE

from contextlib import contextmanager
import shlex
import os
import subprocess
import datetime
from cookiecutter.utils import rmtree

@contextmanager
def inside_dir(dir_path):
    """
    Execute code from inside the given directory
    :param dir_path: String, path of the directory the command is being run.
    """
    old_path = os.getcwd()
    try:
        os.chdir(dir_path)
        yield
    finally:
        os.chdir(old_path)

@contextmanager
def bake_in_temp_dir(cookies, *args, **kwargs):
    """
    Delete the temporal directory that is created when executing the tests
    :param cookies: pytest_cookies.Cookies,
        cookie to be baked and its temporal files will be removed
    """
    result = cookies.bake(*args, **kwargs)
    try:
        yield result
    finally:
        rmtree(str(result.project))

def run_inside_dir(command, dir_path):
    """
    Run a command from inside a given directory, returning the exit status
    :param command: Command that will be executed
    :param dir_path: String, path of the directory the command is being run.
    """
    with inside_dir(dir_path):
        return subprocess.check_call(shlex.split(command))

def check_output_inside_dir(command, dir_path):
    """Run a command from inside a given directory, returning the command output"""
    with inside_dir(dir_path):
        return subprocess.check_output(shlex.split(command))

def test_bake_with_defaults(cookies):
    with bake_in_temp_dir(cookies) as result:
        assert result.project.isdir()
        assert result.exit_code == 0
        assert result.exception is None

        found_top_level_files = [f.basename for f in result.project.listdir()]
        assert '.gitignore' in found_top_level_files
        assert 'conda_env.yml' in found_top_level_files
        assert 'README.md' in found_top_level_files
        assert 'setup.py' in found_top_level_files

        assert os.path.isdir(os.path.join(result.project, 'src', 'project_name'))
        assert os.path.isdir(os.path.join(result.project, 'tests', 'project_name'))

def test_bake_and_run_tests(cookies):
    with bake_in_temp_dir(cookies) as result:
        assert result.project.isdir()
        assert run_inside_dir('python setup.py pytest', str(result.project)) == 0
        print("test_bake_and_run_tests path", str(result.project))

def test_bake_selecting_license(cookies):
    license_strings = {
        'MIT': ('MIT ', 'MIT License', True),
        'LGPL3': ('GNU LESSER GENERAL PUBLIC LICENSE', 'GNU Lesser General Public License v3 (LGPLv3)', False),
        'GPL3': ('GNU GENERAL PUBLIC LICENSE', 'GNU General Public License v3 (GPLv3)', False)
    }
    for project_license, (license_subtext, setup_subtext, should_contain_year) in license_strings.items():
        with bake_in_temp_dir(cookies, extra_context={'open_source_license': project_license}) as result:
            license_file_path = result.project.join('LICENSE')
            print(license_file_path)
            assert license_subtext in license_file_path.read()
            if should_contain_year:
                now = datetime.datetime.now()
                assert str(now.year) in license_file_path.read()
            assert setup_subtext in result.project.join('setup.py').read()

def test_bake_not_open_source(cookies):
    with bake_in_temp_dir(cookies, extra_context={'open_source_license': 'Not open source'}) as result:
        found_top_level_files = [f.basename for f in result.project.listdir()]
        assert 'setup.py' in found_top_level_files
        assert 'LICENSE' not in found_top_level_files

def test_bake_package_name(cookies):
    with bake_in_temp_dir(cookies, extra_context={'package_name': 'my_package'}) as result:
        with inside_dir(result.project):
            assert os.path.isdir(os.path.join('src', 'my_package'))
            assert os.path.isdir(os.path.join('tests', 'my_package'))

            'from my_package import examplemodule' in \
                open(os.path.join('tests', 'my_package', 'examplemodule', 'test_add_value_to_numpy.py')).read()
            'from my_package import examplemodule' in \
                open(os.path.join('tests', 'my_package', 'examplemodule', 'test_hello_world.py')).read()


text:
Creative Commons Legal Code

CC0 1.0 Universal

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    HEREUNDER.

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.

For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:

  i. the right to reproduce, adapt, distribute, perform, display,
     communicate, and translate a Work;
 ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
     likeness depicted in a Work;
 iv. rights protecting against unfair competition in regards to a Work,
     subject to the limitations in paragraph 4(a), below;
  v. rights protecting the extraction, dissemination, use and reuse of data
     in a Work;
 vi. database rights (such as those arising under Directive 96/9/EC of the
     European Parliament and of the Council of 11 March 1996 on the legal
     protection of databases, and under any national implementation
     thereof, including any amended or successor version of such
     directive); and
vii. other similar, equivalent or corresponding rights throughout the
     world based on applicable law or treaty, and any national
     implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.

4. Limitations and Disclaimers.

 a. No trademark or patent rights held by Affirmer are waived, abandoned,
    surrendered, licensed or otherwise affected by this document.
 b. Affirmer offers the Work as-is and makes no representations or
    warranties of any kind concerning the Work, express, implied,
    statutory or otherwise, including without limitation warranties of
    title, merchantability, fitness for a particular purpose, non
    infringement, or the absence of latent or other defects, accuracy, or
    the present or absence of errors, whether or not discoverable, all to
    the greatest extent permissible under applicable law.
 c. Affirmer disclaims responsibility for clearing rights of other persons
    that may apply to the Work or any use thereof, including without
    limitation any person's Copyright and Related Rights in the Work.
    Further, Affirmer disclaims responsibility for obtaining any necessary
    consents, permissions or other rights required for any use of the
    Work.
 d. Affirmer understands and acknowledges that Creative Commons is not a
    party to this document and has no duty or obligation with respect to
    this CC0 or use of the Work.

repo_name: thoughtspile/awesome-tiny-js

mime_type: text/plain


text:
# Awesome Tiny JS [![Awesome](https://awesome.re/badge-flat.svg)](https://awesome.re)

Tiny front-end libraries to put your bundle on a diet. Rules:

- Size is under 2 kB-ish, min + gzip, with all dependencies, except where noted.
- For multi-purpose libraries, the size of a useful subset must be under 2 kB-ish.
- Useful client-side. I haven't figured out participation rules for node-only libraries, and I'm not too worried about them.
- Second-level libraries only allowed for React, Vue, Angular, svelte.
- 100+ GitHub stars. Libraries with less are awesome, too, but stars indicate some community review.
- No zero-JS (CSS- or type-only) libraries. It's not awesome-css or something.

## Contents

- [UI Frameworks](#ui-frameworks)
- [Event Emitters](#event-emitters)
- [Reactive Programming](#reactive-programming)
- [State Managers](#state-managers)
- [Routers and URL Utils](#routers-and-url-utils)
- [API Layer](#api-layer)
- [I18N](#i18n)
- [Dates and Time](#dates-and-time)
- [Generic Utilities](#generic-utilities)
- [Unique ID Generation](#unique-id-generation)
- [Colors](#colors)
- [Touch Gestures](#touch-gestures)
- [Text Search](#text-search)

## UI Frameworks

UI frameworks (libraries?) provide declarative templates, event bindings, and observable state to update the view. I've been generous and expanded the size limit for this category to 4.5 kB (if you're boring, count them as 2 libraries), but also increased the star limit to 2K.

- [preact](https://github.com/preactjs/preact) - React-like API (pre-hooks). Cool ecosystem of similarly tiny tools and components. Highly recommended.

The following libraries are small and cool, but note they're about [500x less popular than preact.](https://npmtrends.com/preact-vs-fre-vs-hyperapp-vs-million-vs-redom) Kudos for deconstrucing the very essence of a "framework":

- [million](https://github.com/aidenybai/million) - Alternate runtime for React components,
- [fre](https://github.com/frejs/fre) - React-like library with hooks and concurrency,  or ~2 kB with tree-shaking.
- [hyperapp](https://github.com/jorgebucaran/hyperapp) - Declarative UI with pure JS syntax and immutable state,
- [redom](https://github.com/redom/redom) - Hyperapp-style templates with _imperative_ event listeners and updates,

And if being declarative is not your thing:

- [umbrella](https://github.com/franciscop/umbrella) - jQuery-style DOM manipulation library,

## Event Emitters

Event emitter pattern is fairly easy to implement yourself, but why bother when you have these cool tools? With an arms race to build the smallest one, the limit is 0.5 kB.

- [mitt](https://github.com/developit/mitt) - Plain event emitter that I use on most projects,
- [nanoevents](https://github.com/ai/nanoevents) - Nicer unsubscribe API, but no `*` event,
- [eev](https://github.com/chrisdavies/eev) - More of the same,
- [onfire.js](https://github.com/hustcc/onfire.js) - Also has `.once` method,

## Reactive Programming

A step up from a raw event emitter, reactive libraries can build chains of event transforms, filters, and side-effects. You can already use these to build UIs by manually updating DOM on state change:

- [flyd](https://github.com/paldepind/flyd) - Rx-styled event streams,
- [callbag-basics](https://github.com/staltz/callbag-basics) - Rx-style event streams,
- [hyperactiv](https://github.com/elbywan/hyperactiv) - 4 functions to make objects observable and listen to changes (mobx-style),
- [flimsy](https://github.com/fabiospampinato/flimsy) - Signals from Solid (it _almost_ fit into UI frameworks category itself). Author warning: _it's probably buggy._

Honorable mention: [oby](https://github.com/vobyjs/oby) _could_ make it _if_ it had tree-shaking, but otherwise is around 7 kB.

## State Managers

State managers combine observable state with actions and framework bindings, intended for app-wide state.

- [zustand](https://github.com/pmndrs/zustand) - Simple stores with pleasant actions and selectors. React  or vanilla
- [nanostores](https://github.com/nanostores/nanostores) - Modular store with good tree-shaking support, . Lots of framework connectors (e.g. React ).
- [exome](https://github.com/marcisbee/exome) - Atomic stores with lots of framework connectors,
- [reatom](https://github.com/artalar/reatom) - Atomic stores for React / svelte / vanilla,
- [storeon](https://github.com/storeon/storeon) - Minimal redux-styled store with lots of framework connectors,
- [unistore](https://github.com/developit/unistore) - Centralized store with actions for React / preact,
- [teaful](https://github.com/teafuljs/teaful) - Store with useState-like API for React / preact,

## Routers and URL Utils

Do stuff on URL / history changes, with path matching and parsing:

- [wouter](https://github.com/molefrog/wouter) - Declarative router for React / preact, , also available as a 400-byte hook.
- [@nanostores/router](https://github.com/nanostores/router) - Routes as a nanostores store (framework-agnostic),
- [navaid](https://github.com/lukeed/navaid) - History-based observable router,
- [routie](https://github.com/jgallen23/routie) - Hash-based observable router,

Just want to parse or match URL paths without observing them? Here you go:

- [matchit](https://github.com/lukeed/matchit) - Route parser and matcher in
- [regexparam](https://github.com/lukeed/regexparam) - Convert path to regexp in
- [qss](https://github.com/lukeed/qss) - Parse querystrings in . Not sure you need it, [URL API](https://developer.mozilla.org/en-US/docs/Web/API/URL) support is good.

## API Layer

`fetch` API has some boilerplate associated with it: serialize & parse data, reject on non-200 response, etc. These tiny packages handle it for you:

- [redaxios](https://github.com/developit/redaxios) - Drop-in axios replacement for modern browsers,
- [wretch](https://github.com/elbywan/wretch) - Chainable API with error processing and lots of extra plugins,
- [gretchen](https://github.com/truework/gretchen) - Chainable API with type-safe errors,

If for some reason you still need a fetch polyfill, try this one:

- [unfetch](https://github.com/developit/unfetch) - Loose fetch polyfill,

## I18N

A map of strings might seem enough to translate an app, but these tools also handle interpolation and some extra goodies:

- [@nanostores/i18n](https://github.com/nanostores/i18n) - Detect locale, load dictionaries, format dates / numbers,
- [eo-locale](https://github.com/ibitcy/eo-locale) - Interpolation and dates / numbers,  including react bindings.
- [rosetta](https://github.com/lukeed/rosetta) - Bare-bones template strings (`{{hello}}, {{username}}`) and custom functions for everyting else,
- [lingui](https://github.com/lingui/js-lingui) - Small core with template strings,  (uses build step). Optional react connector:

## Dates and Time

Date and time manipulation in pure JS is verbose. Luckily, two of the top date libraries have sensible size:

- [date-fns](https://github.com/date-fns/date-fns/) - Not tiny as a whole, but [most functions](https://bundlephobia.com/package/date-fns) are under 1 kB each (format and parse are quite heavy).
- [dayjs](https://github.com/iamkun/dayjs) - _Almost_ moment.js-compatible API, covers most use cases,

And some more packages that only do formatting:

- [tinytime](https://github.com/aweary/tinytime) - Simple date / time formatter: `{h}:{mm} -> 9:33`,
- [tinydate](https://github.com/lukeed/tinydate) - Date / time formatter, only supports padded numeric output (`September -> 09`),
- [time-stamp](https://github.com/jonschlinkert/time-stamp) - More of the same,
- [ms](https://github.com/vercel/ms) - Parse & format ms durations, e.g. `"1m" <-> 60000`,
- [timeago.js](https://github.com/hustcc/timeago.js) - Format dates into stuff like _X minutes ago_ or _in X hours,_
- [fromnow](https://github.com/lukeed/fromnow) - More of the same,

Note that the built-in [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) has decent support.

## Generic Utilities

Something you'd find in lodash or ramda, but smaller. Most are pretty similar and very small, with minor differences in package structure (single / package-per-helper) and tree shaking vs direct helper import.

- [remeda](https://github.com/remeda/remeda) - 90 tree-shakable helpers [(list).](https://bundlephobia.com/package/remeda)
- [rambda](https://github.com/selfrefactor/rambda) - 187 tree-shakable helpers [(list).](https://bundlephobia.com/package/rambda)
- [just](https://github.com/angus-c/just) - 82 helpers in separate packages [(list).](https://anguscroll.com/just/)
- [@tinkoff/utils](https://github.com/Tinkoff/utils.js) - 173 helpers, 1 import per helper. Conservative browser target.
- [@fxts/core](https://github.com/marpple/FxTS) - 96 tree-shakable helpers. Lazy evaluation support.

Honorable mention: [underscore,](https://github.com/jashkenas/underscore) contains many sub-1 kB helpers. It does not tree-shake as well as the libraries above due to codebase structure.

Note: lodash itself is not tree-shakable, but has made many attempts at modulaity with `lodash.method` packages, imports from `lodash/method`, and `lodash-es`, none of which work well in practice.

Also note that much of the original lodash functionality comes built-in with modern ES. Prefer native versions over libraries as your browser target allows.

## Unique ID Generation

Unique ID generation does not take a lot of code, but it's not someting I'd want to write myself. Limit is 500 bytes. Also note that the [native `crypto.randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) has [OK support.](https://caniuse.com/mdn-api_crypto_randomuuid)

- [@lukeed/uuid](https://github.com/lukeed/uuid) - Real UUIDs,
- [nanoid](https://github.com/ai/nanoid) - Random IDs with larger alphabet,
- [uid](https://github.com/lukeed/uid) - More of the same,
- [hexoid](https://github.com/lukeed/hexoid) - Hexadecimal IDs,

## Colors

Color manipulation is rare in pure UI development, but very helpful for data visualization, and uses [freaky math.](https://en.wikipedia.org/wiki/HSL_and_HSV#Color_conversion_formulae) Don't fry your brain, take these:

- [colord](https://github.com/omgovich/colord) - Manipulate colors and convert between spaces, . Extra features come as plugins, 150b to 1.5 kB each.
- [colr](https://github.com/stayradiated/colr) - More of the same,
- [polychrome](https://github.com/cdonohue/polychrome) - More of the same,
- [randomcolor](https://github.com/davidmerfield/randomColor) - Attractive random colors with configuration.

## Touch Gestures

Touch gestures like swipe, drag, pinch or doubletap are a staple of mobile UX, but recognizing a series of touchmove / pointer events as a gesture is tricky, and testing is painful. Here are two libraries that do the heavy lifting for you:

- [alloyfinger](https://github.com/AlloyTeam/AlloyFinger) - Pan, swipe, tap, doubletap, longpress, _and_ pinch / rotate. My personal favorite. .
- [tinygesture](https://github.com/sciactive/tinygesture) - Configurable pan, swipe, tap, doubletap, longpress. .

Even if you want to detect gestures yourself, juggling mouse, touch and pointer events is hard enough, and browser inconsistencies don't help. Here are two more libraries to assist with that:

- [pointer-tracker](https://github.com/GoogleChromeLabs/pointer-tracker) - Unified interface for mouse, touch and pointer events,
- [detect-it](https://github.com/rafgraph/detect-it) - Detect present and primary input method (touch / mouse) and supported events,

Honorable mentions: [any-touch](https://github.com/any86/any-touch) attempts a modular approach to gesture detection, but the core is around 2 kB without any gesture recognizers. [rc-gesture,](https://github.com/react-component/gesture) used in ant design system, could be the only react component on the list, but babel-runtime / corejs polyfills hard-wired into the build push the ~2.5 kB size to over 10 kB.

## Text Search

Text search is important for client-side filtering and autosuggests. Naive `option.includes(search)` has no sensible order on the results, and ignoring word boundaries gives unexpected matches like _spa -> newSPAper._ First, here are some libraries that prioritize word matches:

- [js-search](https://github.com/bvaughn/js-search) - Feature-rich and customizable: multi-field indices, stop words, custom stemmers and tokenizers.
- [ndx](https://github.com/localvoid/ndx) - Similar to js-search, differs in [ranking](https://kmwllc.com/index.php/2020/03/20/understanding-tf-idf-and-bm-25/) and is less strict for multi-word queries [(compare)](https://leeoniya.github.io/uFuzzy/demos/compare.html?libs=js-search,ndx,Wade&search=twilight%20sag). Supports field weights.
- [wade](https://github.com/kbrsh/wade) - Also similar, [(compare)](https://leeoniya.github.io/uFuzzy/demos/compare.html?libs=js-search,Wade,ndx&search=twilight%20sag)
- [libsearch](https://github.com/thesephist/libsearch) - Index-free search (slower, but easier to use) with sane ordering

One way to find sensible inexact matches is _stemming_ — converting words to a root form. _Walked_ will match _walking,_ etc. Here are a few [Porter stemmers](https://vijinimallawaarachchi.com/2017/05/09/porter-stemming-algorithm/) for English language:

- [stemmer](https://github.com/words/stemmer) -
- [porter-stemmer](https://github.com/jedp/porter-stemmer) -

For non-English words, I only have honorable mentions: [snowball-js](https://github.com/fortnightlabs/snowball-js) is 17 kB with 15 languages, [lunr-languages](https://github.com/MihaiValentin/lunr-languages) supports 30 languages but only works with [lunr,](https://github.com/olivernn/lunr.js) the most promising one is [natural](https://github.com/NaturalNode/natural/tree/master/lib/natural/stemmers) but it depends on Node.js.

### Fuzzy search

__Fuzzy search__ is another take on inexact matching — the words can be modified. First, we have libraries that only allow insertion: spacecat -> SPACECrAfT. Not perfect for general-purpose text search, but great for filename, command, or URL lookups.

- [fuzzy](https://github.com/mattyork/fuzzy) - Index-free, can highlight matches.
- [fuzzy-search](https://github.com/wouterrutgers/fuzzy-search) - With stateful index.
- [fzy.js](https://github.com/jhawthorn/fzy.js) - Matches one string at a time, tree-shakeable scores and match highlighting.  total, or ~150 bytes for `hasMatch` only.
- [fuzzysearch](https://github.com/bevacqua/fuzzysearch) -  One string at a time, does not compute score / rank.
- [liquidmetal](https://github.com/rmm5t/liquidmetal) - Quicksilver algorithm, prioritizes matches at start of word for command abbreviations (e.g. `gp` -> `git push`). One string at a time.
- [quick-score](https://github.com/fwextensions/quick-score) - Another quicksilver-based lib, tweaked for long strings. Built-in list filtering and sorting,  or 1.2 kB for single-string scoring.

Finally, one library is specifically built for spellchecking:

- [fuzzyset](https://github.com/Glench/fuzzyset.js) - Find misspellings, e.g. missipissi -> Missisipi,  Commercial usage costs $42.

## Contributing

Suggestions welcome! See [contributing.md](contributing.md), or drop an [issue](https://github.com/thoughtspile/awesome-tiny-js/issues).

## Footnotes

See [WIP](wip.md) for possibly awesome libraries I have found, but not yet analyzed deeply, and [incubate](incubate.md) for awesome libraries that don't meet popularity criteria yet.

Collected and reviewed by [Vladimir Klepov](https://blog.thoughtspile.tech) in 2023.


text:
## Waitlist

- Date / time:
  - https://github.com/masbagal/tempe


text:
https://github.com/kbrsh/moon

## Components

- https://github.com/vuejs-tips/v-money
- https://github.com/vuejs-tips/vue-the-mask
- https://github.com/realadvisor/rifm
- https://github.com/realadvisor/rgm
- https://github.com/nathancahill/split
- https://github.com/knadh/dragmove.js
- https://github.com/hustcc/placeholder.js
- https://github.com/ruyadorno/simple-slider
- https://github.com/glidejs/glide
- https://github.com/lukeed/calendarize
- https://github.com/joe223/tiny-swiper
- https://github.com/web-padawan/vanilla-colorful
- https://github.com/dabanlee/blurify
- https://github.com/egoist/snackbar
- https://github.com/dericcain/react-tiny-fab
- https://github.com/ghosh/Micromodal
- https://github.com/charlestati/amplify
- https://github.com/chrisdavies/tiny-date-picker

## CSS-in-JS

- https://github.com/cristianbote/goober
- https://github.com/morishitter/picostyle
- https://github.com/sadick254/scoped-style

## DOM helpers

- https://github.com/unjs/unhead
- https://github.com/marcoroth/current.js
- https://github.com/hustcc/size-sensor
- https://github.com/vaneenige/uos
- https://github.com/natemoo-re/micromorph
- https://github.com/ooade/react-click-away-listener
- https://github.com/fregante/image-promise
- https://github.com/NeXTs/Clusterize.js
- https://github.com/discord/focus-layers
- Sorting
  - https://github.com/Sjeiti/TinySort
  - https://github.com/tofsjonas/sortable
- [holmes](https://github.com/Haroenv/holmes) - Text search in DOM nodes

## Keyboard and Hotkeys

Keyboard controls make your app more accessible and accomodating to power users, but managing key combos can get messy, and platform differences stand in your way.

- [mousetrap](https://github.com/ccampbell/mousetrap) - Just a thin layer of listener management and key normalization,
- [keymage](https://github.com/piranha/keymage) - with scoping,

- [hotkeys-js](https://github.com/jaywcjlove/hotkeys-js) - more of the same,
- [keymaster](https://github.com/madrobby/keymaster) - more of the same,

- [whenipress](https://github.com/lukeraymonddowning/whenipress) - Declarative key sequences with event batching, element scoping, and doubletap / press-and-hold detection
- [@github/hotkey](https://github.com/github/hotkey) - Something different! Describe hotkeys in HTML using `data-hotkey` attributes and custom DOM events.

- https://github.com/ianstormtaylor/is-hotkey

## Diacritics

- https://github.com/andrewrk/node-diacritics
- https://github.com/dundalek/latinize

## Levenshtein distance

- https://github.com/ka-weihe/fastest-levenshtein
- https://github.com/sindresorhus/leven
- https://github.com/hiddentao/fast-levenshtein
- https://github.com/gustf/js-levenshtein
- https://github.com/gf3/Levenshtein

## Persistence

- https://github.com/unjs/unstorage
- https://github.com/willgm/web-crypto-storage

## Generic Utilities

- Async
  - https://github.com/blend/promise-utils
  - https://github.com/ai/nanodelay
  - https://github.com/lukeed/throttles
- Objects
  - https://github.com/developit/dlv
  - https://github.com/lukeed/dset
  - https://github.com/lukeed/klona
  - https://github.com/lukeed/dequal
  - https://github.com/lukeed/flattie
  - https://github.com/planttheidea/unchanged
  - https://github.com/mariocasciaro/object-path
  - https://github.com/unjs/defu
- Misc
  - https://github.com/planttheidea/micro-memoize

## Logging

- https://github.com/unjs/consola
- https://github.com/mperdeck/jsnlog.js
- https://github.com/debug-js/debug
- https://github.com/pimterry/loglevel

## Validation

- form
  - https://github.com/sha256/Pristine
  - https://github.com/lukeed/formee
  - https://github.com/rickharrison/validate.js
  - https://github.com/FrontLabsOfficial/vue-tiny-validate
  - https://github.com/chainlist/svelte-forms
  - https://github.com/jaywcjlove/validator.js
  - https://github.com/tjinauyeung/svelte-forms-lib
- data
  - https://github.com/neuledge/computed-types
  - https://github.com/ianstormtaylor/superstruct
  - https://github.com/5alidz/tiny-schema-validator
  - https://github.com/thoughtspile/banditypes
  - https://github.com/typeofweb/schema
  - https://github.com/antonioru/deep-waters
  - https://github.com/imbrn/v8n
- specific
  - https://github.com/tarunbatra/password-validator
  - https://github.com/ContaAzul/creditcard.js

## Misc

- https://github.com/mburakerman/hrjs
- https://github.com/d3/d3
- https://github.com/lukeed/clsx
- https://github.com/unjs/destr
- https://github.com/developit/snarkdown
- https://github.com/lukeed/flru
- https://github.com/lukeed/saturated
- https://github.com/lukeed/tmp-cache
- https://github.com/lukeed/semiver
- https://github.com/alexreardon/tiny-invariant
- https://github.com/GoogleChromeLabs/comlink
- https://github.com/lipka/piecon
- https://github.com/openexchangerates/accounting.js
- https://github.com/openexchangerates/money.js
- https://github.com/sindresorhus/crypto-hash
- https://github.com/TomerAberbach/grfn
- https://github.com/nemtsov/json-mask
- https://github.com/mourner/tinyqueue
- cookies
  - https://github.com/Alex1990/tiny-cookie
  - https://github.com/js-cookie/js-cookie
- https://github.com/nefe/number-precision
- https://github.com/mgechev/tiny-compiler
- https://github.com/komlev/tiny-use-media
- https://github.com/komlev/tiny-use-detect-horizontal-overflow
- https://github.com/willmcpo/body-scroll-lock

## Waitlist

- Date / time: https://github.com/masbagal/tempe


text:
## Contributing

So, you want to add a library to this list? Amazing, I'd love it! Here's how to progress:

1. Verify that the library fits the rules of this list:
  1. Size is under 2Kb-ish, min + gzip, with all deps, except for special categories.
  2. For tree-shakable libraries, the size of a useful subset must be under 2Kb-ish.
  3. Second-level libraries only allowed for React, Vue, Angular, svelte. The size of the framework is excluded from the size limit.
  4. Useful client-side — no node.js exclusive libraries and dev / build tooling.
  5. 100+ GitHub stars _or_ 100+ weekly npm installs for the last 4 weeks.
  6. The library _must_ contain _some_ JS.
2. Check if the library is not already in [WIP.](./WIP.md)
3. Open README.md in [GitHub editor](https://github.com/thoughtspile/awesome-tiny-js/edit/main/README.md) or by clicking the _pencil_ icon [on top of the file](https://github.com/thoughtspile/awesome-tiny-js/blob/main/README.md)
4. Add the proposed library in [GitHub flavord markdown](https://help.github.com/articles/github-flavored-markdown/)
5. Say what you're adding, and click "Propose changes"

Looking forward to your submissions!


text:
on:
  pull_request:
    branches: [main]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v3
      - run: npx awesome-lint readme.md


text:
# CHANGELOG

## 2.3.0 — 2018-09-08

### Added
- ES6 Imports.

repo_name: leofavre/canivete

repo_language: JavaScript

mime_type: text/x-java


text:
import multiEntry from "rollup-plugin-multi-entry";
import nodeResolve from "rollup-plugin-node-resolve";

export default {
	entry: "./test/*.js",
	dest: "./test/temp/allSpecs.js",
	plugins: [
		multiEntry(),
		nodeResolve({
			jsnext: true
		})
	],
	format: "es"
};


text:
const path = require('path');

process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = (config) => {
  config.set({
    browsers: [
      'ChromeHeadlessNoSandbox',
      'FirefoxHeadless'
    ],
    customLaunchers: {
      ChromeHeadlessNoSandbox: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      },
      FirefoxHeadless: {
        base: 'Firefox',
        flags: ['-headless']
      }
    },
    browserNoActivityTimeout: 60000,
    singleRun: true,
    frameworks: ['jasmine'],
    files: [
      {
        pattern: 'test/*.js',
        watched: false
      }
    ],
    preprocessors: {
      'test/*.js': ['webpack']
    },
    webpack: {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            use: {
              loader: 'istanbul-instrumenter-loader',
              options: {
                esModules: true
              }
            },
            include: path.resolve(__dirname, 'test'),
            exclude: /((node_modules|docs|dist)(\\|\/|$)|test\.js$)/
          }
        ]
      }
    },
    reporters: ['coverage-istanbul'],
    coverageIstanbulReporter: {
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true,
      skipFilesWithNoCoverage: true
    }
  });
};


text:
import nodeResolve from "rollup-plugin-node-resolve";
import babel from "rollup-plugin-babel";
// import uglify from "rollup-plugin-uglify";

export default {
	entry: "./docs/index.js",
	dest: "./docs/js/index.js",
	plugins: [
		babel({
			"presets": [
				[
					"es2015",
					{
						"modules": false
					}
				]
			],
			"plugins": [
				"external-helpers"
			]
		}),
		nodeResolve({
			jsnext: true
		}),
		// uglify()
	],
	format: "es",
	context: "this"
};


text:
![](https://canivete.leofavre.com/media/canivete.svg)

# Canivete ![](https://travis-ci.org/leofavre/canivete.svg?branch=master)

**[Documentation](https://canivete.leofavre.com/)**

---

Canivete (Brazilian Portuguese for swiss army knife) is an on-going personal project by [Leonardo Favre](https://leofavre.com/), a place to keep generic, multi-purpose Javascript functions.

Due to its multi-purpose nature, Canivete is not distributed as a single file, instead, one should import its functions as needed, using [ES6 module syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import).

[Babel](https://babeljs.io/) and [Babel Polyfill](https://babeljs.io/docs/usage/polyfill/) are recommended to make Canivete work in older browsers.

## Usage

Since ES6 modules have virtually no browser support at this time, a module bundler is needed to handle Canivete dependencies.

Here's how to use Canivete with [Rollup](https://github.com/leofavre/canivete-with-rollup) and [Webpack](https://github.com/leofavre/canivete-with-webpack).

## Credits

* **Canivete** Coding and documentation by [Leonardo Favre](https://leofavre.com/)
* **Website** Coding and layout by [Leonardo Favre](https://leofavre.com/) and symbol by [Andre Levy a.k.a. Zhion](http://www.zhion.com.br)
* **Typography** [Acropolis](https://www.typography.com/fonts/acropolis/overview/) and [Whitney](https://www.typography.com/fonts/whitney/overview/) by [Hoefler & Co.](https://www.typography.com/) and [Fira Code](https://github.com/tonsky/FiraCode) by [Nikita Prokopov](https://github.com/tonsky/)

Canivete is released under The Unlicense license.


text:
title: Canivete
email: leo@leofavre.com
description: >
  Canivete (portuguese for army knife) is a set of Javascript
  multi-purpose functions; a personal and on-going project
  where I keep functions that I find myself writing over and
  over again.
baseurl: "/canivete"
url: "https://leofavre.github.io"
github_username: leofavre

# Build settings
markdown: kramdown

exclude:
  - Gemfile
  - Gemfile.lock
  - index.js

gems:
  - jekyll-autoprefixer

autoprefixer:
  browsers:
  - last 4 versions


text:
source "https://rubygems.org"
ruby RUBY_VERSION

gem "jekyll", "3.4.0"
gem "jekyll-autoprefixer"


text:
import "./_includes/jquery.scrollTo.min";
import "./_includes/kerning.min";
import "./_includes/scrollingelement";
import getEventPath from "../dist/getEventPath";
import throttle from "lodash-es/throttle";
import debounce from "lodash-es/debounce";

const $window = $(window);
const logo = $("h1").find("span").get(0);
const navSwitch = $(".nav__switch").get(0);
const navDrawer = $(".nav__drawer").get(0);
const navButtons = $(".nav").find("a").get();
const navCollapsableButtons = $(navButtons).get().filter(domEl => $(domEl).parent().is("strong"));
const chapterAnchors = $(".content").find("h2, h3 > a").get().filter(domEl => !!getHash(domEl));

function getScrollTarget() {
	return useNativeScroll() ? getScrollingElement() : $(".page__inner").get(0);
}

function getCurrentlyReadableChapter() {
	let anchorsAboveTheFold = chapterAnchors.filter(isAboveTheFold);
	return getLastItem(anchorsAboveTheFold);
}

function getHash(domEl) {
	return domEl.href ? domEl.href.split("#")[1] : "" ||
		domEl.getAttribute("id") ||
		domEl.getAttribute("name");
}

function getAutoHeight(domEl) {
	$domEl = $(domEl);

	let memoCSS = $domEl.attr("style");

	$domEl.css({
		transition: "all 0s",
		height: "auto"
	});

	let memoHeight = $domEl.height();
	$domEl.attr("style", memoCSS || "");
	return memoHeight;
}

function getChapterByHash(hash) {
	return $(`[id="${hash}"], [name="${hash}"]`)[0];
}

function getNavButtonByHash(hash) {
	return $(navButtons).filter(`a[href$="#${hash}"]`)[0];
}

function getLastItem(arr) {
	return arr[arr.length - 1];
}

function getScrollingElement() {
	return document.scrollingElement;
};

function useNativeScroll() {
	return $("html").hasClass("mode--native-scroll");
}

function isAboveTheFold(domEl) {
	return domEl.getBoundingClientRect().top < $window.height() * 0.35;
}

function ignoreScrollNavigationEvents(...domEls) {
	$(domEls).off("scroll");
}

function listenForScrollNavigationEvents(...domEls) {
	$(domEls)
		.on("scroll", throttle(markNav, 100))
		.on("scroll", debounce(markBrowser, 350));
}

function updateScrollAnchorEvents() {
	$(logo).on("click", scrollToTop);
	$(`a[href^="#"]`).on("click", scrollToChapter);
}

function listenForLayoutChange() {
	$window
		.on("resize", debounce(updateScrollBehaviour, 300))
		.on("resize", debounce(markNav, 350))
		.on("resize", debounce(markBrowser, 350));
}

function removeNonBreakingSpacesFromTds() {
	$("td").each(function() {
		var $this = $(this);
		$this.html($this.html().replace(/ /g, ''));
	});
}

function updateScrollBehaviour() {
	ignoreScrollNavigationEvents(getScrollTarget(), window);
	listenForScrollNavigationEvents(getScrollTarget(), window);
}

function updateDrawerBehaviour() {
	let $scrollTarget = $(getScrollTarget());

	$(navSwitch).on("click", toggleDrawer);
	$scrollTarget.on("click", closeDrawerOnClickOutside);
}

function changeDrawer(func) {
	func();

	if (!isDrawerVisible()) {
		$(navDrawer).one("transitionend", moveDrawerScrollToTop);
	}
}

function toggleDrawer() {
	changeDrawer(() => {
		$(navDrawer).toggleClass("nav__drawer--off");
		$(navSwitch).toggleClass("nav__switch--off");
	});
}

function closeDrawer() {
	changeDrawer(() => {
		$(navDrawer).addClass("nav__drawer--off");
		$(navSwitch).addClass("nav__switch--off");
	});
}

function closeDrawerOnClickOutside(evt) {
	if (isEventOutsideDrawer(evt)) {
		closeDrawer();
	}
}

function isEventOutsideDrawer(evt) {
	let eventPath = getEventPath(evt);
	return !eventPath.includes(navSwitch) && !eventPath.includes(navDrawer);
}

function isDrawerVisible() {
	return !$(navDrawer).hasClass("nav__drawer--off");
}

function moveDrawerScrollToTop() {
	navDrawer.scrollTop = 0;
}

function scrollTo(position, evt) {
	let $scrollTarget = $(getScrollTarget());

	if (position != null) {
		evt.preventDefault();
		$scrollTarget.scrollTo(position, 600, {
			axis: "y"
		});
	}
}

function scrollToTop(evt) {
	scrollTo(0, evt);
}

function scrollToChapter(evt) {
	let chapter = getChapterByHash(getHash(evt.target));
	scrollTo(chapter, evt);
}

function markNav() {
	$(navButtons).removeClass("selected");

	let currentlyReadableChapter = getCurrentlyReadableChapter();

	if (currentlyReadableChapter) {
		let hash = getHash(currentlyReadableChapter);
		let markedNavButton = getNavButtonByHash(hash);
		$(markedNavButton).addClass("selected");
	}
}

function markBrowser() {
	let currentlyReadableChapter = getCurrentlyReadableChapter();

	if (history.replaceState) {
		if (currentlyReadableChapter) {
			let hash = `#${getHash(currentlyReadableChapter)}`;

			if (document.location.hash !== hash) {
				history.replaceState(null, null, hash);
			}
		}
		else {
			if (document.location.hash !== "") {
				history.replaceState(null, null, document.location.origin + document.location.pathname);
			}
		}
	}
}

updateDrawerBehaviour();
updateScrollAnchorEvents();
updateScrollBehaviour();
listenForLayoutChange();
removeNonBreakingSpacesFromTds();


text:
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square150x150logo src="/mstile-150x150.png"/>
            <TileColor>#32383e</TileColor>
        </tile>
    </msapplication>
</browserconfig>


text:
---
layout: default
title: Canivete
---

## *BEM*

>[formatBemClass](#formatbemclass), [modifyBemClass](#modifybemclass) & [modifyBemClassCompact](#modifybemclasscompact)

### `formatBemClass (block, [element], [modifier], [value], delimiters)`

Formats a CSS class according to the
[BEM methodology](https://en.bem.info/methodology/).
The function receives a block, an element, a modifier, a value
for the modifier and an array of BEM delimiters, e.g. `__`,
`--` and `-`.

#### Parameters

| Name | Default | Type | Description |
| --- | --- | --- | --- |
| `block` |  | String | The BEM block. |
| `element` |  | String | The BEM element. **optional** |
| `modifier` |  | String | The BEM modifier. **optional** |
| `value` | `true` | String
Number
Boolean | The BEM modifier value. **optional** |
| `delimiters` |  | Array.<String> | The BEM delimiters. |

#### Return

| Type | Description |
| --- | --- |
| String | The BEM CSS class. |

#### Examples

```javascript
let delimiters = ["__", "--", "-"];

formatBemClass("menu", delimiters);
// => "menu"

formatBemClass("menu", "item", delimiters);
// => "menu__item"

formatBemClass("menu", "item", "active", delimiters);
// => "menu__item--active"

formatBemClass("menu", "item", "active", false, delimiters);
// => "menu__item"

formatBemClass("menu", "item", "active", true, delimiters);
// => "menu__item--active"

formatBemClass("menu", "item", "level", 42, delimiters);
// => "menu__item--level-42"

formatBemClass("menu", "item", "level", "42", delimiters);
// => "menu__item--level-42"
```

```javascript
let delimiters = ["__", "--", "-"];

formatBemClass("button", null, "active", delimiters);
// => "button--active"

formatBemClass("button", null, "active", false, delimiters);
// => "button"

formatBemClass("button", null, "active", true, delimiters);
// => "button--active"

formatBemClass("button", null, "level", 42, delimiters);
// => "button--level-42"

formatBemClass("button", null, "level", "42", delimiters);
// => "button--level-42"
```

### `modifyBemClass (domEl, bemObj, delimiters)`

Modifies the CSS classes from a DOM element according
to the [BEM methodology](https://en.bem.info/methodology/).

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |
| `bemObj` | BemObject | The object describing BEM class changes (see table below). |
| `delimiters` | Array | The BEM delimiters. |

#### BemObject

| Name | Type | Description |
| --- | --- | --- |
| `name` | Object | An object which key is a BEM block or element, e.g. `menu` or `landing__area`. |
| `name.modifier` | Object | An object representing a modifiers and their values, e.g. `{active: false}` or `{level: 42}`. |

#### Example

```javascript
let domEl = document.createElement("div"),
	delimiters = ["__", "--", "-"];

modifyBemClass(domEl, {
	"swiper": {
		"slides": 5,
		"current": 2,
		"playing": true
	}
}, delimiters);

domEl.className;
// => "swiper swiper--slides-5 swiper--current-2 swiper--playing"

modifyBemClass(domEl, {
	"swiper": {
		"current": 3,
		"playing": false
	}
}, delimiters);

domEl.className;
// => "swiper swiper--slides-5 swiper--current-3"
```

### `modifyBemClassCompact (domEl, bemObj, delimiters)`

Modifies the CSS classes from a DOM element according
to the [BEM methodology](https://en.bem.info/methodology/).
Unlike [`modifyBemClass()`](#modifybemclass), it ommits the original block
or element CSS class if a modified version is output.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |
| `bemObj` | BemObject | The object describing BEM class changes (see table below). |
| `delimiters` | Array | The BEM delimiters. |

#### BemObject

| Name | Type | Description |
| --- | --- | --- |
| `name` | Object | An object which key is a BEM block or element, e.g. `menu` or `landing__area`. |
| `name.modifier` | Object | An object representing a modifiers and their values, e.g. `{active: false}` or `{level: 42}`. |

#### Example

```javascript
let domEl = document.createElement("div"),
	delimiters = ["__", "--", "-"];

modifyBemClassCompact(domEl, {
	"swiper": {
		"slides": 5,
		"current": 2,
		"playing": true
	}
}, delimiters);

domEl.className;
// => "swiper--slides-5 swiper--current-2 swiper--playing"

modifyBemClassCompact(domEl, {
	"swiper": {
		"current": 3,
		"playing": false
	}
}, delimiters);

domEl.className;
// => "swiper--slides-5 swiper--current-3"
```

## *ClassName*

>[addClass](#addclass), [hasClass](#hasclass) & [removeClass](#removeclass)

### `addClass (domEls, className)`

Adds a CSS class to one or more DOM elements.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `className` | String | The CSS class name. |

#### Examples

```javascript
let oneElement = document.querySelector("a");
addClass(oneElement, "link");

oneElement.className;
// => "link"
```

```javascript
let manyElements = document.querySelectorAll("a");
addClass(manyElements, "link");

manyElements[0].className;
// => "link"
```

### `hasClass (domEl, className)`

Verifies if a DOM element has a CSS class.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |
| `className` | String | The CSS class name. |

#### Return

| Type | Description |
| --- | --- |
| Boolean | Whether the element has the CSS class name. |

#### Example

```javascript
let oneElement = document.querySelector("a");
oneElement.className = "link reference";

hasClass(oneElement, "link");
// => true

hasClass(oneElement, "button");
// => false
```

### `removeClass (domEls, className)`

Removes a CSS class from one or more DOM elements.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `className` | String | The CSS class name. |

#### Examples

```javascript
let oneElement = document.querySelector(".link.base");
removeClass(oneElement, "link");

oneElement.className;
// => "base"
```

```javascript
let manyElements = document.querySelectorAll(".link.base");
removeClass(manyElements, "link");

manyElements[0].className;
// => "base"
```

## *Collection*

>[deepGroupBy](#deepgroupby)

### `deepGroupBy (collection, [...iteratees])`

Groups the contents of an array by one or more iteratees.
This function is similar to Lodash
[`groupBy()`](https://lodash.com/docs/4.17.4#groupBy),
except it can create nested groups but cannot receive
strings for iteratees.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `collection` | Array | The original array. |
| `...iteratees` | Function | The functions used to group the array of objects by their results. **optional** |

#### Return

| Type | Description |
| --- | --- |
| Object | The resulting object. |

#### Examples

```javascript
const getLength = str => str.length;
const getFirstLetter = str => str.slice(0, 1);

deepGroupBy(["one", "two", "three"], getLength, getFirstLetter);
// => {
// => 	"3": {"o": ["one"], "t": ["two"]},
// => 	"5": {"t": ["three"]}
// => }
```

```javascript
const getLength = str => str.length;
const getFirstLetter = str => str.slice(0, 1);

deepGroupBy(["one", "two", "three"], getFirstLetter, getLength);
// => {
// => 	"o": {"3": ["one"]},
// => 	"t": {"3": ["two"], "5": ["three"]}
// => }
```

```javascript
const stores = [{
	"name": "Iguatemi",
	"city": "Campinas",
	"state": "SP"
}, {
	"name": "Jardins",
	"city": "São Paulo",
	"state": "SP"
}, {
	"name": "Iguatemi",
	"city": "São Paulo",
	"state": "SP"
}, {
	"name": "Pedras",
	"city": "Búzios",
	"state": "RJ"
}, {
	"name": "Ipanema",
	"city": "Rio de Janeiro",
	"state": "RJ"
}, {
	"name": "Leblon",
	"city": "Rio de Janeiro",
	"state": "RJ"
}, {
	"name": "ParkShopping",
	"city": "Brasília",
	"state": "DF"
}];

const getStateName = item => item.state;
const getCityName = item => item.city;

deepGroupBy(stores, getStateName, getCityName);
// => {
// => 	"SP": { "Campinas": [...], "São Paulo": [...] },
// => 	"RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
// => 	"DF": { "Brasília": [...] }
// => }
```

## *DOM*

>[getAttr](#getattr), [parents](#parents), [removeAttr](#removeattr), [removeAttrs](#removeattrs), [selfAndParents](#selfandparents), [setAttr](#setattr) & [setAttrs](#setattrs)

### `getAttr (domEl, attrName)`

Gets a DOM element attribute using native
[`Element.getAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute),
except that the presence of an attribute without
a value will return `true` instead of an empty string,
and the absence will return `false`. The function also
deals with boolean values using old HTML4 syntax,
like `<option selected="selected">`.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |
| `attrName` | String | The attribute name. |

#### Return

| Type | Description |
| --- | --- |
| String
Boolean | The attribute value. |

#### Examples

```javascript
// HTML5 syntax
let inputElement = createDomElement('<input type="checkbox" checked>');

getAttr(inputElement, "checked");
// => true
```

```javascript
// HTML4 syntax
let inputElement = createDomElement('<input type="checkbox" checked="checked"/>');

getAttr(inputElement, "checked");
// => true
```

```javascript
let videoElement = createDomElement('<video src="video.mp4" controls>');

getAttr(videoElement, "src");
// => "video.mp4"

getAttr(videoElement, "controls");
// => true

getAttr(videoElement, "muted");
// => false
```

### `parents (domEl)`

Returns all parents of a DOM element,
from the closest to the most distant.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |

#### Return

| Type | Description |
| --- | --- |
| Array.<HTMLElement> | The DOM element parents. |

#### Example

```javascript
let domChild = document.createElement("div"),
	domParent = document.createElement("div"),
	domGrandparent = document.createElement("div"),
	body = document.body,
	html = document.querySelector("html");

domParent.appendChild(domChild);
domGrandparent.appendChild(domParent);
body.appendChild(domGrandparent);

parents(domChild);
// => [domParent, domGrandparent, body, html, document]
```

### `removeAttr (domEls, attrName)`

Removes an attribute from one or more DOM elements using native
[`Element.removeAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute).

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `attrName` | String | The attribute name. |

#### Examples

```javascript
let oneElement = createDomElement('<p data-level="42">Level 42</p>');

removeAttr(oneElement, "data-level");

oneElement.getAttribute("data-level");
// => null

oneElement.dataset.level;
// => undefined
```

```javascript
let oneElement = createDomElement('<a class="button" href="/news">News</a>');

removeAttr(oneElement, "class");

oneElement.getAttribute("class");
// => null

oneElement.className;
// => ""
```

```javascript
let listHtml = '<ul><li class="item">A</li><li class="item">B</li></ul>';
	listElement = createDomElement(listElement),
	manyElements = listElement.querySelectorAll("li");

removeAttr(manyElements, "class");

manyElements[0].className;
// => ""

manyElements[1].className;
// => ""
```

### `removeAttrs (domEls, attrArr)`

The same as [`removeAttr()`](#removeattr), except it takes an
array with attributes to be removed.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `attrArr` | Array.<String> | The array with attributes to be removed. |

#### Examples

```javascript
let oneElement = createDomElement('<p class="level" data-level="42">Level 42</p>');

removeAttrs(oneElement, ["data-level", "class"]);

oneElement.getAttribute("data-level");
// => null

oneElement.dataset.level;
// => undefined

oneElement.getAttribute("class");
// => null

oneElement.className;
// => ""
```

```javascript
let listHtml = '<ul><li class="item" data-level="1">A</li><li class="item" data-level="1">B</li></ul>',
	listElement = createDomElement(listHtml),
	manyElements = listElement.querySelectorAll("li");

removeAttrs(manyElements, ["data-level", "class"]);

manyElements[0].getAttribute("data-level");
// => null

manyElements[1].getAttribute("data-level");
// => null

manyElements[0].className;
// => ""

manyElements[1].className;
// => ""
```

### `selfAndParents (domEl)`

The same as [`parents()`](#parents), except it includes
the DOM element itself.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |

#### Return

| Type | Description |
| --- | --- |
| Array.<HTMLElement> | The DOM element and its parents. |

#### Example

```javascript
let domChild = document.createElement("div"),
	domParent = document.createElement("div"),
	domGrandparent = document.createElement("div"),
	body = document.body,
	html = document.querySelector("html");

domParent.appendChild(domChild);
domGrandparent.appendChild(domParent);
body.appendChild(domGrandparent);

selfAndParents(domChild);
// => [domChild, domParent, domGrandparent, body, html, document]
```

### `setAttr (domEls, attrName, value)`

Sets an attribute for one or more DOM elements using native
[`Element.setAttribute()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute).

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `attrName` | String | The attribute name. |
| `value` | String
Number
Boolean | The attribute value. |

#### Examples

```javascript
let oneElement = createDomElement('<p>Level 42</p>');

setAttr(oneElement, "data-level", 42);

oneElement.getAttribute("data-level");
// => "42"

oneElement.dataset.level;
// => "42"
```

```javascript
let oneElement = createDomElement('<a href="/news">News</a>');

setAttr(oneElement, "class", "button");

oneElement.getAttribute("class");
// => "button"

oneElement.className;
// => "button"
```

```javascript
let listElement = createDomElement('<ul><li>A</li><li>B</li><li>C</li></ul>'),
	manyElements = listElement.querySelectorAll("li");

setAttr(manyElements, "class", "item");

manyElements[0].className;
// => "item"

manyElements[1].className;
// => "item"
```

### `setAttrs (domEls, attrObj)`

The same as [`setAttr()`](#setattr), except it takes an object
with attribute name and value pairs to set one or
many attributes at once.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEls` | HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements. |
| `attrObj` | Object | The object with attribute name and value pairs, e.g. `{ "data-level": 42 }`. |

#### Examples

```javascript
let oneElement = createDomElement('<p>Level 42</p>');

setAttrs(oneElement, {
	"data-level": 42,
	"class": "level"
});

oneElement.getAttribute("data-level");
// => "42"

oneElement.dataset.level;
// => "42"

oneElement.getAttribute("class");
// => "level"

oneElement.className;
// => "level"
```

```javascript
let listElement = createDomElement('<ul><li>A</li><li>B</li><li>C</li></ul>'),
	manyElements = listElement.querySelectorAll("li");

setAttrs(manyElements, {
	"data-level": 42,
	"class": "item"
});

manyElements[0].getAttribute("data-level");
// => "42"

manyElements[1].getAttribute("data-level");
// => "42"

manyElements[0].className;
// => "item"

manyElements[1].className;
// => "item"
```

## *Event*

>[getEventPath](#geteventpath), [ignore](#ignore), [listen](#listen) & [trigger](#trigger)

### `getEventPath (evt)`

Returns an array with all DOM elements affected by an event.
The function serves as a polyfill for
[`Event.composedPath()`](https://dom.spec.whatwg.org/#dom-event-composedpath).

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `evt` | Event | The triggered event. |

#### Return

| Type | Description |
| --- | --- |
| Array.<HTMLElement> | The DOM elements affected by the event. |

#### Example

```javascript
let domChild = document.createElement("div"),
	domParent = document.createElement("div"),
	domGrandparent = document.createElement("div"),
	body = document.body,
	html = document.querySelector("html");

domParent.appendChild(domChild);
domGrandparent.appendChild(domParent);
body.appendChild(domGrandparent);

domChild.addEventListener("click", dealWithClick);
const dealWithClick = evt => getEventPath(evt);

// when domChild is clicked:
// => [domChild, domParent, domGrandparent, body, html, document, window]
```

### `ignore (domEls, eventStr, callback, [useCapture])`

Removes one or more event listeners from one or more DOM elements at once.
The function is a wrapper for
[`Element.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
that accepts a space-separated event names string and a group
of target DOM elements.

#### Parameters

| Name | Default | Type | Description |
| --- | --- | --- | --- |
| `domEls` |  | Window
HTMLDocument
HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements, including `document` and `window`. |
| `eventStr` |  | String | The event names string. |
| `callback` |  | Function | The function to be ignored. |
| `useCapture` | `false` | Boolean | The event phase being listened for. **optional** |

#### Example

```javascript
let oneElement = document.querySelector("a");
let manyElements = document.querySelectorAll("a");

ignore(oneElement, "click", reactToClick, true);
ignore(manyElements, "click", reactToClick);
ignore(window, "load resize", reactToLoadAndResize);
```

### `listen (domEls, eventStr, callback, [useCapture])`

Adds one or more event listeners to one or more DOM elements at once.
The function is a wrapper for
[`Element.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
that accepts a space-separated event names string and a group
of target DOM elements.

#### Parameters

| Name | Default | Type | Description |
| --- | --- | --- | --- |
| `domEls` |  | Window
HTMLDocument
HTMLElement
HTMLCollection
NodeList
Array.<HTMLElement>
Set.<HTMLElement> | One or more DOM elements, including `document` and `window`. |
| `eventStr` |  | String | The event names string. |
| `callback` |  | Function | The function to be exectuted when the event is dispatched. |
| `useCapture` | `false` | Boolean | The event phase to be listened for. **optional** |

#### Example

```javascript
let oneElement = document.querySelector("a");
let manyElements = document.querySelectorAll("a");

listen(oneElement, "click", reactToClick, true);
listen(manyElements, "click", reactToClick);
listen(window, "load resize", reactToLoadAndResize);
```

### `trigger (domEl, evtName, [bubbles], [cancelable], [detail])`

Triggers a custom DOM event.

#### Parameters

| Name | Default | Type | Description |
| --- | --- | --- | --- |
| `domEl` |  | Window
HTMLDocument
HTMLElement | The DOM element, including `document` and `window`. |
| `evtName` |  | String | The event name. |
| `bubbles` | `false` | Boolean | Whether the event bubbles. **optional** |
| `cancelable` | `false` | Boolean | Whether the event can be canceled. **optional** |
| `detail` |  | All | Any information passed along. **optional** |

#### Example

```javascript
let popupButton = document.querySelector(".popup__button"),
	popupLayer = document.querySelector(".popup__layer");

popupButton.addEventListener("click", evt => {
	trigger(popupLayer, "open");
});
```

## *Geometry*

>[getClippingInfo](#getclippinginfo) & [getDistanceBetweenCoords](#getdistancebetweencoords)

### `getClippingInfo (domEl, [maskDef])`

Given a DOM element, returns an object with position
and clipping information relative to a mask, defined
by the second parameter, or to the viewport, if the
second parameter is omitted.

The mask can be either a DOM element or an object
containing numeric values for "top", "bottom",
"left" and "right" properties, like a
[`DOMRect`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect).

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `domEl` | HTMLElement | The DOM element. |
| `maskDef` | HTMLElement
Object | The mask definition. **optional** |

#### Return

| Type | Description |
| --- | --- |
| ClippingObject | Position and clipping information relative to a mask (see table below). |

#### ClippingObject

| Name | Type | Description |
| --- | --- | --- |
| `isOffTop` | Boolean | Above and off the mask. |
| `isOffBottom` | Boolean | Below and off the mask. |
| `isOffLeft` | Boolean | On the left and off the mask. |
| `isOffRight` | Boolean | On the right and off the mask. |
| `isOff` | Boolean | Off the mask. |
| `isClippedTop` | Boolean | Above and intersecting with the mask. |
| `isClippedBottom` | Boolean | Below and intersecting with the mask. |
| `isClippedLeft` | Boolean | On the left and intersecting with the mask. |
| `isClippedRight` | Boolean | On the right and intersecting with the mask. |
| `isClipped` | Boolean | Intersecting with the mask. |
| `isFullyVisible` | Boolean | Fully visible inside the mask. |
| `isPartiallyVisible` | Boolean | Alias for `isClipped`. |
| `isInvisible` | Boolean | Alias for `isOff`. |
| `isAsVisibleAsPossible` | Boolean | As visible as possible (the element is taller or wider than the mask). |
| `isNotAsVisibleAsPossible` | Boolean | Not as visible as possible (the element is taller or wider than the mask). |

#### Example

```javascript
let domEl = document.createElement("div");

domEl.style.position = "fixed";
domEl.style.top = "-50px";
domEl.style.left = "-50px";
domEl.style.width = "200px";
domEl.style.height = "200px";

document.body.appendChild(domEl);

let info = getClippingInfo(domEl);

info.isClippedTop;
// => true

info.isClippedLeft;
// => true

info.isFullyVisible;
// => false

info.isPartiallyVisible;
// => true

info.isInvisible;
// => false
```

### `getDistanceBetweenCoords (coordA, coordB)`

Calculates and returns the distance between two points,
given their cartesian coordinates, represented, each one,
by an array of numbers.

For example, the point in a plane A(x, y) should be passed
to the function as `[x, y]`. Likewise, the point in
3D space A(x, y, z) should be passed as `[x, y, z]`.

The function deals with cartesian coordinates in
[n-dimensional spaces](https://en.wikipedia.org/wiki/Euclidean_distance#n_dimensions).

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `coordA` | Array.<Number> | An array representing a cartesian coordinate. |
| `coordB` | Array.<Number> | An array representing a cartesian coordinate. |

#### Return

| Type | Description |
| --- | --- |
| Number | The distance between the two cartesian coordinates. |

#### Example

```javascript
getDistanceBetweenCoords([0, 0], [3, 4]);
// => 5

getDistanceBetweenCoords([2, 1], [5, 5]);
// => 5

getDistanceBetweenCoords([2, 1, 8], [5, 5, 0]);
// => 9.433981132056603

getDistanceBetweenCoords([2], [5]);
// => 3
```

## *Promise*

>[eventAsPromise](#eventaspromise) & [waitInPromise](#waitinpromise)

### `eventAsPromise (domEl, evtName, [happened])`

Transforms a DOM event into a promise.

The functions takes as parameters: a DOM element,
the name of the event to be listened for
and a function that verifies if the event has already
happened, which receives the DOM element as parameter.

Like all promises in Javascript, the function will
only fulfill once, either if the verification function
returns true or when the event occurs for the first time.

Note that the function throws an error if the first two
parameters are not a DOM element and a string.

#### Parameters

| Name | Default | Type | Description |
| --- | --- | --- | --- |
| `domEl` |  | Window
HTMLDocument
HTMLElement | The DOM element, including `document` and `window`. |
| `evtName` |  | String | The event to be listened for. |
| `happened` | `domEl => false` | Function | The verification function. **optional** |

#### Return

| Type | Description |
| --- | --- |
| Promise | When fulfilled, returns the DOM element. |

#### Examples

```javascript
let checkbox = document.createElement("input");
checkbox.type = "checkbox";
document.body.appendChild(checkbox);

eventAsPromise(checkbox, "change")
	.then(doSomethingAfterChange);
```

```javascript
let imageEl = document.createElement("img");
imageEl.src = "img.jpg";
document.body.appendChild(imageEl);

eventAsPromise(imageEl, "load", imageEl => imageEl.complete)
	.then(doSomethingAfterImageLoaded);
```

### `waitInPromise (delay)`

Delays the chaining of a promise by a specified
time in milliseconds.

The function is curried so as to be used inside
the `.then()` method, passing along the resolved
value from the previous promise step to the next.

Note that if a non-numeric parameter is passed,
the promise resolves without delay, skipping the
internal `setTimeout()`.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `delay` | Number | The delay in milliseconds. |

#### Return

| Type | Description |
| --- | --- |
| Promise | When fulfilled, returns the resolved value from the previous step. |

#### Example

```javascript
Promise.resolve("waiting")
	.then(waitInPromise(1000))
	.then(doSomethingAfterOneSecond);
```

## *Random*

>[oneOutOf](#oneoutof)

### `oneOutOf (num)`

Returns `true` approximately one out of `num` times,
randomly.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `num` | Number | A number greater than zero. |

#### Return

| Type | Description |
| --- | --- |
| Boolean | Returns `true` approximately one out of `num` times. |

#### Example

```javascript
oneOutOf(2);
// => true

oneOutOf(2);
// => false
```

## *Reduce*

>[toAverage](#toaverage), [toAverageProp](#toaverageprop), [toClosest](#toclosest), [toClosestProp](#toclosestprop), [toLargestProp](#tolargestprop), [toSmallestProp](#tosmallestprop) & [toSum](#tosum)

### `toAverage ()`

When used with `[].reduce()`, returns
the average of the values in an array.

Note that reducing arrays with non-numeric values
using `toAverage()` can lead to unexpected results.
Also, note that the parentheses can be ommited.

#### Parameter

|  |
|  |

#### Return

| Type | Description |
| --- | --- |
| Number | The average of the values in an array. |

#### Example

```javascript
[3, 5, 7, 9].reduce(toAverage());
// => 6

[3, 5, 7, 9].reduce(toAverage);
// => 6
```

### `toAverageProp (path)`

When used with `[].reduce()`, returns
the object in an array in which a specific property,
passed as parameter, has the closest value to the average.

If two or more results are found, the first one
is returned.

Note that the function expects the reduced array to be
formed by objects with the same set of properties.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `path` | String | The path to the property of an object. |

#### Return

| Type | Description |
| --- | --- |
| Object | The object in which an specific property has the closest value to the average. |

#### Example

```javascript
let cities = [{
	"city": "Rio de Janeiro",
	"temperature": 96,
	"demographics": {
		"population": 6.32
	}
}, {
	"city": "São Paulo",
	"temperature": 82.5,
	"demographics": {
		"population": 12.04
	}
}, {
	"city": "Curitiba",
	"temperature": 70,
	"demographics": {
		"population": 1.752
	}
}, {
	"city": "Florianópolis",
	"temperature": 86,
	"demographics": {
		"population": 0.249
	}
}];

// average "temperature": 83.625
// average "population": 5.09025

cities.reduce(toAverageProp("temperature"));
// => { "city": "São Paulo", [...] }

cities.reduce(toAverageProp("demographics.population"));
// => { "city": "Rio de Janeiro", [...] }
```

### `toClosest (num)`

When used with `[].reduce()`, returns
the closest value to the one passed as parameter.

If two or more results are found, the first one
is returned.

Note that reducing arrays with non-numeric values
using `toClosest()` can lead to unexpected results.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `num` | Number | The base value. |

#### Return

| Type | Description |
| --- | --- |
| Number | The value, from an array, closest to the base value. |

#### Example

```javascript
[3, 5, 7, 9].reduce(toClosest(6));
// => 5

[3, 5, 7, 9].reduce(toClosest(-2));
// => 3
```

### `toClosestProp (path, num)`

When used with `[].reduce()`, returns
the object in an array in which a specific property,
passed as parameter, has the closest value to another,
also passed as parameter.

If two or more results are found, the first one
is returned.

Note that the function expects the reduced array to be
formed by objects with the same set of properties.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `path` | String | The path to the property of an object. |
| `num` | String | The base value. |

#### Return

| Type | Description |
| --- | --- |
| Object | The object in which an specific property has the closest value to the base value. |

#### Example

```javascript
let cities = [{
	"city": "Rio de Janeiro",
	"temperature": 96,
	"demographics": {
		"population": 6.32
	}
}, {
	"city": "São Paulo",
	"temperature": 82.5,
	"demographics": {
		"population": 12.04
	}
}, {
	"city": "Curitiba",
	"temperature": 70,
	"demographics": {
		"population": 1.752
	}
}, {
	"city": "Florianópolis",
	"temperature": 86,
	"demographics": {
		"population": 0.249
	}
}];

cities.reduce(toClosestProp("temperature", 75));
// => { "city": "Curitiba", [...] }

cities.reduce(toClosestProp("demographics.population", 5));
// => { "city": "Rio de Janeiro", [...] }
```

### `toLargestProp (path)`

When used with `[].reduce()`, returns
the object in an array in which a specific property
has the largest value.

If two or more results are found, the first one
is returned.

Note that the function expects the reduced array to be
formed by objects with the same set of properties.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `path` | String | The path to the property of an object. |

#### Return

| Type | Description |
| --- | --- |
| Object | The object in which a specific property has the largest value. |

#### Example

```javascript
let cities = [{
	"city": "Rio de Janeiro",
	"temperature": 96,
	"demographics": {
		"population": 6.32
	}
}, {
	"city": "São Paulo",
	"temperature": 82.5,
	"demographics": {
		"population": 12.04
	}
}, {
	"city": "Curitiba",
	"temperature": 70,
	"demographics": {
		"population": 1.752
	}
}, {
	"city": "Florianópolis",
	"temperature": 86,
	"demographics": {
		"population": 0.249
	}
}];

cities.reduce(toLargestProp("temperature"));
// => { "city": "Rio de Janeiro", [...] }

cities.reduce(toLargestProp("demographics.population"));
// => { "city": "São Paulo", [...] }
```

### `toSmallestProp (path)`

When used with `[].reduce()`, returns
the object in an array in which a specific property
has the smallest property.

If two or more results are found, the first one
is returned.

Note that the function expects the reduced array to be
formed by objects with the same set of properties.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `path` | String | The path to the property of an object. |

#### Return

| Type | Description |
| --- | --- |
| Object | The object in which a specific property has the smallest value. |

#### Example

```javascript
let cities = [{
	"city": "Rio de Janeiro",
	"temperature": 96,
	"demographics": {
		"population": 6.32
	}
}, {
	"city": "São Paulo",
	"temperature": 82.5,
	"demographics": {
		"population": 12.04
	}
}, {
	"city": "Curitiba",
	"temperature": 70,
	"demographics": {
		"population": 1.752
	}
}, {
	"city": "Florianópolis",
	"temperature": 86,
	"demographics": {
		"population": 0.249
	}
}];

cities.reduce(toSmallestProp("temperature"));
// => { "city": "Curitiba", [...] }

cities.reduce(toSmallestProp("demographics.population"));
// => { "city": "Florianópolis", [...] }
```

### `toSum ()`

When used with `[].reduce()`, returns
the sum of the values in an array.

Note that reducing arrays with non-numeric values
using `toSum()` can lead to unexpected results.
Also, note that the parentheses can be ommited.

#### Parameter

|  |
|  |

#### Return

| Type | Description |
| --- | --- |
| Number | The sum of the values in an array. |

#### Example

```javascript
[3, 5, 7, 9].reduce(toSum());
// => 24

[3, 5, 7, 9].reduce(toSum);
// => 24
```

## *Sort*

>[byAlphabeticalOrder](#byalphabeticalorder) & [byProps](#byprops)

### `byAlphabeticalOrder ()`

When used with `[].sort()`, sorts
the array in ascending alphabetical order.

Note that the parentheses can be ommited.

#### Parameter

|  |
|  |

#### Return

| Type | Description |
| --- | --- |
| Array | The array in ascending alphabetical order. |

#### Example

```javascript
let musqueteers = ["Athos", "Porthos", "Aramis"];

musqueteers.sort(byAlphabeticalOrder());
// => ["Aramis", "Athos", "Porthos"]

musqueteers.sort(byAlphabeticalOrder);
// => ["Aramis", "Athos", "Porthos"]
```

### `byProps (...fields)`

When used with `[].sort()`, returns an array of
objects sorted by one or more criteria, passed as
parameters.

Each parameter can be eitheir a path to an object
property, passed as a string, or an object containing
a path to an object property, a boolean value indicating
if the result should be reversed, and a function to
process each value before sorting.

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `...fields` | String
SortField | The criteria used to sort the array of objects. |

#### SortField

| Name | Type | Description |
| --- | --- | --- |
| `path` | String | The path to the property of an object. |
| `primer` | Function | The function used to process each value before sorting. **optional** |
| `reverse` | Boolean | Whether the result should be reversed. **optional** |

#### Return

| Type | Description |
| --- | --- |
| Array.<Object> | The resulting array. |

#### Examples

```javascript
let places = [{
	"name": "Ipanema",
	"location": {
		"city": "Rio de Janeiro",
		"state": "RJ"
	}
}, {
	"name": "Pedras",
	"location": {
		"city": "Búzios",
		"state": "RJ"
	}
}, {
	"name": "Morumbi",
	"location": {
		"city": "São Paulo",
		"state": "SP"
	}
}];

places.sort(byProps("name"));
// Sorts places by name
// => [
// =>	{ "name": "Ipanema", [...] },
// =>	{ "name": "Morumbi", [...] },
// =>	{ "name": "Pedras", [...] }
// => ]

places.sort(byProps({ "path": "name", "reverse": true });
// Sorts places by name in reversed order
// => [
// =>	{ "name": "Pedras", [...] },
// =>	{ "name": "Morumbi", [...] },
// =>	{ "name": "Ipanema", [...] }
// => ]

places.sort(byProps("location.state", "location.city", "name"));
// Sorts places by state, city and name
// => [
// =>	{ "name": "Pedras", [...] },
// =>	{ "name": "Ipanema", [...] },
// =>	{ "name": "Morumbi", [...] }
// => ]

places.sort(byProps({ "path": "location.state", "reverse": true }, "location.city", "name"));
// Sorts places by state (in reversed order), city and name
// => [
// =>	{ "name": "Morumbi", [...] },
// =>	{ "name": "Pedras", [...] },
// =>	{ "name": "Ipanema", [...] }
// => ]
```

```javascript
let numbers = [{
	"value": 35
}, {
	"value": -20
}, {
	"value": 3
}, {
	"value": 0.8
}];

numbers.sort(byProps("value"));
// Sorts numbers by value in ascending order
// => [{ "value": -20 }, { "value": 0.8 }, { "value": 3 }, { "value": 35 }]

numbers.sort(byProps({ "path": "value", "primer": Math.abs }));
// Sorts numbers by value in ascending order but ignoring the minus sign
// => [{ "value": 0.8 }, { "value": 3 }, { "value": -20 }, { "value": 35 }]
```

## *String*

>[afterFirst](#afterfirst), [afterLast](#afterlast), [beforeFirst](#beforefirst) & [beforeLast](#beforelast)

### `afterFirst (str, delimiter)`

Returns the string formed by the characters **after
the first occurrence** of the delimiter in a base string.
If the delimiter is not found, the function returns `undefined`.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `str` | String | The base string. |
| `delimiter` | String | The delimiter string. |

#### Return

| Type |
| --- |
| String |

#### Example

```javascript
afterFirst("parallelepiped", "le");
// => "lepiped"
```

### `afterLast (str, delimiter)`

Returns the string formed by the characters **after
the last occurrence** of the delimiter in a base string.
If the delimiter is not found, the function returns `undefined`.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `str` | String | The base string. |
| `delimiter` | String | The delimiter string. |

#### Return

| Type |
| --- |
| String |

#### Example

```javascript
afterLast("parallelepiped", "le");
// => "piped"
```

### `beforeFirst (str, delimiter)`

Returns the string formed by the characters **before
the first occurrence** of the delimiter in a base string.
If the delimiter is not found, the function returns `undefined`.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `str` | String | The base string. |
| `delimiter` | String | The delimiter string. |

#### Return

| Type |
| --- |
| String |

#### Example

```javascript
beforeFirst("parallelepiped", "le");
// => "paral"
```

### `beforeLast (str, delimiter)`

Returns the string formed by the characters **before
the last occurrence** of the delimiter in a base string.
If the delimiter is not found, the function returns `undefined`.

#### Parameters

| Name | Type | Description |
| --- | --- | --- |
| `str` | String | The base string. |
| `delimiter` | String | The delimiter string. |

#### Return

| Type |
| --- |
| String |

#### Example

```javascript
beforeLast("parallelepiped", "le");
// => "paralle"
```

## *Time*

>[timeSince](#timesince)

### `timeSince (timestamp)`

Returns the time passed since a timestamp, in milliseconds;

#### Parameter

| Name | Type | Description |
| --- | --- | --- |
| `timestamp` | Number | The time stamp. |

#### Return

| Type | Description |
| --- | --- |
| Number | Time passed since the timestamp, in milliseconds. |

#### Example

```javascript
let timestamp = +new Date(),
	result = 0;

setTimeout(() => {
	result = timeSince(timestamp);
}, 150);

result;
// => 150
// This value is approximate and may vary.
```


text:
<!DOCTYPE html>

{% include head.html %}

Canivete

				<svg class="nav__switch nav__switch--off" xmlns="http://www.w3.org/2000/svg" width="21" height="16" viewBox="0 0 21 16"><rect width="21" height="2"/><rect y="7" width="21" height="2"/><rect y="14" width="21" height="2"/></svg>

					{% include pagenav.html %}
					{% include menu.html %}

					{% include about.html %}
					{% include usage.html %}
					{% include canivete-with-rollup.html %}
					{% include canivete-with-webpack.html %}
					{% include credits.html %}

					{{ content }}

	{% include footer.html %}


text:
/* about */

#about {
	-letter-kern: 0.01em -0.01em -0.02em 0em 0;
}

/* usage */

#usage {
	-letter-kern: 0.03em -0.02em -0.01em -0.015em 0;
}

/* credits */

#credits {
	-letter-kern: 0 -0.02em -0.03em 0em -0.02em 0 0;
}

/* bem */

#bem {
	-letter-kern: -0.01em 0 0;
}

/* classname */

#classname {
	-letter-kern: -0.05em -0.01em 0.015em -0.01em 0.015em -0.01em -0.015em -0.025em 0;
}

/* collection */

#collection {
	-letter-kern: -0.04em -0.04em 0 -0.03em -0.035em -0.035em 0.035em -0.035em -0.02em 0;
}

/* dom */

#dom {
	-letter-kern: -0.02em -0.02em 0;
}

/* event */

#event {
	-letter-kern: -0.01em -0.03em 0 -0.02em 0;
}

/* geometry */

#geometry {
	-letter-kern: -0.015em -0.035em -0.02em -0.025em -0.03em 0.035em 0 0;
}

#geometry .char4 {
	margin-left: 0.42em;
}

#geometry .char6 {
	margin-left: -0.09em;
}

/* promise */

#promise {
	-letter-kern: 0 -0.03em -0.02em -0.02em 0.01em -0.015em 0;
}

#promise .char4 {
	margin-left: 0.32em;
}

/* random */

#random {
	-letter-kern: -0.03em -0.01em 0 -0.02em -0.02em 0;
}

#random .char4 {
	margin-left: -0.07em;
}

/* reduce */

#reduce {
	-letter-kern: -0.03em -0.03em 0 0 -0.04em 0;
}

/* sort */

#sort {
	-letter-kern: -0.01em -0.025em 0.01em 0;
}

/* string */

#string {
	-letter-kern: 0 0.035em 0 0 0 0;
}

/* time */

#time {
	-letter-kern: 0.04em -0.02em -0.025em 0;
}


text:
---
---
@import "variables";

@import "typography";
@import "html";
@import "page";
@import "grid";

@import "nav";
@import "content";

@import "monokai";


text:
@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Light.eot");
    src: url("../fonts/eot/FiraCode-Light.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Light.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Light.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Light.ttf") format("truetype");
    font-weight: 300;
    font-style: normal;
}

@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Regular.eot");
    src: url("../fonts/eot/FiraCode-Regular.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Regular.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Regular.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Regular.ttf") format("truetype");
    font-weight: 400;
    font-style: normal;
}

@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Medium.eot");
    src: url("../fonts/eot/FiraCode-Medium.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Medium.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Medium.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Medium.ttf") format("truetype");
    font-weight: 500;
    font-style: normal;
}

@font-face{
    font-family: "Fira Code";
    src: url("../fonts/eot/FiraCode-Bold.eot");
    src: url("../fonts/eot/FiraCode-Bold.eot") format("embedded-opentype"),
         url("../fonts/woff2/FiraCode-Bold.woff2") format("woff2"),
         url("../fonts/woff/FiraCode-Bold.woff") format("woff"),
         url("../fonts/ttf/FiraCode-Bold.ttf") format("truetype");
    font-weight: 700;
    font-style: normal;
}


text:
<!DOCTYPE html>

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Fira Code Specimen</title>

  <link rel="stylesheet" href="fira_code.css">

# Fira Code Light

take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] ++ take n-1, xs

last3 = reverse >> take 3 >> reverse

# Fira Code Regular

take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] ++ take n-1, xs

last3 = reverse >> take 3 >> reverse

# Fira Code Medium

take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] ++ take n-1, xs

last3 = reverse >> take 3 >> reverse

# Fira Code Bold

take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] ++ take n-1, xs

last3 = reverse >> take 3 >> reverse


text:
/*
	Copyright (C) 2011-2018 Hoefler & Co.
	This software is the property of Hoefler & Co. (H&Co).
	Your right to access and use this software is subject to the
	applicable License Agreement, or Terms of Service, that exists
	between you and H&Co. If no such agreement exists, you may not
	access or use this software for any purpose.
	This software may only be hosted at the locations specified in
	the applicable License Agreement or Terms of Service, and only
	for the purposes expressly set forth therein. You may not copy,
	modify, convert, create derivative works from or distribute this
	software in any way, or make it accessible to any third party,
	without first obtaining the written permission of H&Co.
	For more information, please visit us at http://typography.com.
	316729-117071-20180907
*/