npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2024 – Pkg Stats / Ryan Hefner

eventi

v1.3.8

Published

Powerful application events and event handling, made easy.

Downloads

65

Readme

Eventi

Powerful application events and event handling, made easy.

Getting Started

Download the full, minified, or server versions. Build Status
Bower: bower install eventi
NPM: npm install eventi
Component: component install esha/Eventi

Eventi API

Eventi.fire([target, ]eventi[, data,...])

Creates and dispatches the specified event(s) on the specified target(s), attaching any specified data.

Arguments | Required | Type(s) | Default | Description --------- | -------- | ------- | ------- | ----------- target | No | Object, Array | global/window | A single target or array of them. eventi | Yes | String, Event, Array | | One or more space-delimited Eventi definitions, an Event instance, or an array of either (in which case you must specify a target argument). data | No | | | Any number of arguments that will be put in an Array and attached to all created events as the data property.

*When passing events in an array, you must specify a target argument.

Any attached data will be fed to the event listeners as additional arguments after the event itself and any data registered along with the listener itself.

Eventi.fire(user, 'account:close', accountId);

Eventi.on([target, ]eventi, fn[, data,...])

Registers the specified function as an event listener for the specified event(s) on the specified target(s).

Arguments | Required | Type(s) | Default | Description --------- | -------- | ------- | ------- | ----------- target | No | Object, Array | global/window | A single target or array of them. eventi | Yes | String, Array | | One or more space-delimited Eventi definitions or an array of them (in which case you must specify a target argument). fn | Yes | Function | | The function to be called when a matching event hits the target(s). data | No | * | | Any number of arguments that will be passed directly to the listener function as arguments (after the event argument).

The function argument will be called with the target as its context, the received event as the first argument, and data arguments registered with the listener as the next arguments, and any data arguments attached to the event itself after those.

Eventi.on(user, 'account:open', function(e) {
   console.log('Event: type='+e.type+' category='+e.category, this/* will be user */); 
});

Eventi.on([targets, ]{ eventi:fn,... })

Overloading of on() for convenient registration of multiple listeners at once. Iterates over the object argument, registering key/value pairs as eventi/fn pairs.

Eventi.on(user, {
    'account:open', function(e) {
        console.log('Event: type='+e.type+' category='+e.category, this/* will be user */); 
    },
    'account:close', function(e, accountId) {
        console.log('User: '+this.id+' is closing account '+accountId);
    }
});

eventi="eventi ..."

Registers event listeners on the element that search between an Event's target and the listening element for an element with a handler attribute (e.g. data-click="...") telling the listener how to handle the event.

Attributes | Required | Content | Description --------- | -------- | ------- | ------- | ----------- eventi, data-eventi | For all events except click | Space-delimited Eventi definitions | Defines listeners for an element. {alias}, data-{alias} | No | Element function, global function, Eventi definition | Either a reference to the function to be called, an Eventi definition to be fired on the element, or nothing, in which case the alias is used as the content.

<div id="admin" data-eventi="/beforeunload=>save keyup[delete]<.account>=>delete">
    <ul class="accounts">
        <li class="account" tabindex="0" delete="account:close">Savings</li>
        ...
    </ul>
    <button click="account:open">New Account</button>
</div>

Eventi.off([targets, ][eventi, ][fn])

Unregisters event listeners that match the specified event(s) and/or function on the specified target(s), or the global/window object, if none is specified.

Arguments | Required | Type(s) | Default | Description --------- | -------- | ------- | ------- | ----------- target | No | Object, Array | global/window | A single target or array of them. eventi | No | String, Array | | One or more space-delimited Eventi definitions (or portions thereof) or an array of them (in which case you must specify a target argument) fn | No | Function | | The specific listener function to be removed.

Eventi.off(user, 'account:');

Eventi.alias([context, ]eventi,...)

Defines aliases for the events specified. It creates sub-functions for on(), off(), and fire() under the alias (or simple type if no alias is defined).

Arguments | Required | Type | Default | Description --------- | -------- | ---- | ------- | ----------- context | No | Object | Eventi | An object that has been run through Eventi.fy() whose methods you want the aliases set for. eventi | Yes | String | | One or more event space-delimited Eventi definitions.

Eventi.alias('account:request#freeze=>hold');
Eventi.on.hold(user, function(e, admin) {
  console.log(admin.id+' is requesting freeze for '+user.id+' accounts');  
});
//...
Eventi.fire.hold(user, admin);

Eventi.fy(target)

Convenience tool that simply defines on(), off(), and fire(), making the specified target object the context and target for those functions.

Arguments | Required | Type | Default | Description --------- | -------- | ---- | ------- | ----------- context | Yes | Object | | A target object you would like to call fire/on/off upon directly.

Eventi.fy(user);
user.on('account:open', function(e) {
   console.log('Open new account for '+this/* will be user */); 
});
Eventi.fy(Element.prototype);// lets you call Eventi functions on all DOM elements

Eventi Syntax

Core - Defining What's What

Type

Example definition: open

This is the central, "action" portion in an Eventi definition. It is not optional in most definitions, with the exception of calls to off(). It is what you have left after you strip off all the other event and handler properties described below here. It is an Event instance's type property. This should typically be a simple verb, in present or past tense.

var e = new Eventi('open');
console.log(e.type);// outputs 'open'

Category:

Example definition: account:open

This is the "subject" option in an Eventi definition. The category always precedes the type and is delimited from it by a :. It is an Eventi instance's category property. This should typically be a simple noun, and represent the subject or owner of a set of events. It is good practice to provide a category for your events unless you specifically mean them to be broadly applied.

var e = Eventi.fire('account:open');
console.log(e.category);// outputs 'account'

#Tags

Example definition: move#up#left

These are the "adverb" or "adjective" portions of Eventi definitions. They always follow the type and are delimited from it and each other by #. When creating an Event, they are set together in an array on the instance as the tags property and each individual tag is set on the instance as a property with a value of true. Use these to distinguish related sub-types of events or provide simple annotations for the subject or object of an event.

Eventi.on('open#new', function(e) {
  if (e.unverified) {
    console.log(e.tags.join(' & '));// outputs 'new & unverified'
  }
}).fire('open#new#unverified');

(Detail)

Example definition: account:label(["verified","gold"])

This is the "object" portion of an Eventi definition. It follows the type and is delimited by ( and ). The contents may be either JSON (which will get parsed), a reference to a global value (which will be resolved), or an arbitrary string. The result is an instance's detail property. Use this to declaratively attach contextual information for an event at the moment it is created.

window.user = { name: "jdoe123" };
var e = Eventi.fire('like(user.name)');
console.log(e.detail);// outputs 'jdoe123'

Browser - Environment Awareness

<.Delegate>

Example definition: cancel<.transaction>

This specifies a selector for event delegation and is delimited by < and >. It instructs a listener to only react to events happening within the bounds of elements matching the specified selector and to use the matching element as the context for the handling function.

Eventi.on('change<[type=checkbox]>', function(e) {
  console.log(this.type);// always outputs 'checkbox'
});

[Key]

Example definition: keyup[delete]

This specifies a key or combination thereof, by either keyCode or description, and is delimited by [ and ]. It instructs a listener to only react when the event has the appropriate keyCode/ctrlKey/metaKey/shiftKey/altKey properties. Particular keyCodes may be specified explicitly (e.g. keydown[33]), but many have been given English or symbolic equivalents for the sake of readable code (e.g. keypress[z]). These are stored in the Eventi._.codes object and are keyup-based values, so keydown and keypress definitions should be wary of those outside ASCII. The other key-related event properties are set simply by giving them - as a suffix (e.g. [shift-13] -> e.shiftKey = true, [meta-] -> e.metaKey = true, etc).

The [Key] syntax is also special in that it will provide keyup as the type for event listeners (for on() only) that have a [Key] specified, but no type. Typically, event listeners with no type are never executed.

Eventi.on(field, '[shift-enter]', function(e) {
  console.log(e.shiftKey, e.keyCode);// outputs 'true 13'
});

@Location

Example definition: keypress[escape]@/edit

This specifies a url pattern that is matched against the current window.location to filter event listener execution.

There are three types of @ location values:

@/vanilla#string - Will be directly matched against the URL string retrieved from window.location.
@?mini={template} - Turns the sections in braces into wildcard matches (breaking on \,?, and #) whose values are gathered into a key/value object, passed as the 2nd argument to the event handler.
@`reg[ular]+Exp?` - For more complicated matching, use a regular expression. Use ` to delimit it. Handlers will receive the full match as the 2nd argument (after the event itself) and matching groups as subsequent arguments. Any handler or event data args will follow the location matching arguments.

Like [Key], @Location enables shorthand definitions by setting 'location' as the type for event listeners (for on() only) that have a location property, but no type property.

Eventi.on('save@#file={filename}', function(e, vals, folder) {
  console.log('Saved '+vals.filename+' in '+folder);// outputs 'Saved image.png in /pics'
});
window.location.hash = 'file=image.png';
Eventi.fire('save', '/pics');
location@Location

Eventi also provides a unified location event that is dispatched on the window whenever the current location changes, whether via hashchange and popstate events or calls to history.pushState (which gets its own pushstate event as well). When registering a listener for a location event, the location is promptly tested (as if a location event was fired), allowing immediate execution of handlers for currently matching locations. Also, when a location type event is dispatched (e.g. fire('location@/path')), the @Location will be used to update window.location via history.pushState.

// assume URL is http://esha.github.io/ to start
var s = '';
Eventi.on('location', function(e) {
  s += ' '+e.location;
});
window.location.hash = 'hash';
history.pushState(null,'push it', '?push');
history.go(-2);// popstate
console.log(s);// outputs ' / /#hash /?push /#hash /'
Eventi.on('@/login', function(e, match) {// will listen for 'location' type
  console.log(e.type, match);// outputs 'location login'
});
Eventi.fire('location@/login');

@Location's mini-templates can work in reverse when updating the location via event. Just pass a key/value object back as the first data argument (either at listener registration or event firing).

Eventi.fire('location@?page={page}', {page:2});
window.location.search === '?page=2'; // true

Just to spell it out, the sum of these flexible and easy location features is a powerful event-based application router.

Control - For Special Handling

_NoBubbles

Example definition: _hide

This simply tells the dispatching code not to let an event propogate beyond the immediate target. Include a _ in the control characters at the start of an event definition to set the bubbles property to false.

Eventi.on('test', function(e) {
  console.log('Element events will never get here if they do not bubble.');
});
Eventi.fire(document.getElementById('hideme'), '_test');

/Global

Example definition: /login

This registers the listener on the global/window object but executes handler functions in the context for which they are registered. Include a / in the control characters at the start of an event definition to assign the listener globally.

var buttons = document.querySelector('button,[type=submit]');
Eventi.on(buttons, '/ajaxStart', function(e) {
  this.disabled = true;
});

^Singleton

Example definition: ^ready

Including ^ in an event definition's control characters identifies it as a "singleton". The simplest way to explain these is that they are the event-equivalent of jQuery's ready() function. Once a singleton event is dispatched or received, it is remembered so that registered listeners execute no more than once and listeners registered after a singleton event is dispatched will be immediately executed with the remembered event. It's a once-for-all kind of deal. Events that go through fire() marked as singletons are automatically remembered for every target hit. When registering a listener for a singleton event that has not yet been fired, any matching event (marked singleton or not) will be remembered on the listener's target alone, for subsequent registrations. And yes, Eventi automatically watches for DOMContentLoaded and fires a ^ready event on the document element.

Eventi.on({
  '^ready': function() {
    loadAsync('globalResource').then(function(resource) {
      Eventi.fire('^resource:loaded', resource);
    });
  },
  '^resource:loaded': function(e, resource) {
    // use resource here
  }
});

$End

Example definition: login$1

Not all event listeners are meant to last forever. You may declare a listener's end when registering it by appending a $ and a condition to the event definition. The condition may be either a number (indicating the number of executions allowed), a reference to a value (either a property of the context or global/window), or a reference to a function that will return such a value. In the case of a value, you may also prefix it with ! to reverse the condition. The "end declaration" follows all features of a definition except an alias. The most common is, of course, $1 for single-use listeners.

Eventi.on(player, 'death$!player.livesLeft', player.respawn);
Eventi.on('load$1', Plugins.init);

!Important

Example definition: !location

A ! control character in the front of an event definition is only relevant when you try to off() it. Listeners defined as "important" may only be unregistered by a fully-matching definition given to off(), including the !. This exists mostly to protect "internal" listeners (both Eventi's own and extensions) from being errantly unregistered.

Eventi.on('!demo', function() {
  console.log('still here!');
});
Eventi.off('demo');
Eventi.fire('demo');// will output 'still here!'

=>Alias

Example definition: account:notify#SMS(balance)=>textme

Aliases are used by either alias() or data-eventi definitions to provide a simple alias for safely and conveniently referencing event definitions (particularly complicated ones). These are strongly recommended for any complex definitions that are repeated in your JavaScript. And, of course, for data-eventi declarations, they are unavoidable.

Eventi.alias('/user:logout=>shutdown');
Eventi.on.shutdown(function() {
  // look ma, no typos!
});

Multiplicity - Beyond Definition Boundaries

The following syntax options are uber-syntax for working with multiple, separate definitions. No syntax features can be shared across definitions conjoined by spaces, commas, or plus symbols.

"Multiple Events"

Example definition: keyup[enter] blur

Allows you to dispatch, register, unregister, or alias multiple event definitions in the same call.

Eventi.on(editor, 'keyup[ctrl-s] blur submit', function() {
  Eventi.fire('service:send local:save');
});

"Event,Sequences"

Example definition: validate,save

Allows you to dispatch or listen for a connected, ordered sequence of events. This is another feature adapted from trigger (read this!). The concepts are the same, only the delimiter (now ,) and the API given to sequenced events have changed:

event.index - The index of this Event in the sequence.
event.previousEvent - The preceding Event instance (if any).
event.sequence - The array of Eventi definitions in the sequence.
event.pauseSequence([promise]) - Pauses the firing of the sequence. If a Promise (or other "then-able" object) is given as argument, it will automatically resume upon successful resolution of the promise. This makes async event sequences very straightforward!
event.isSequencePaused() - Returns a Boolean indicating if the sequence has been paused.
event.resumeSequence([index]) - Resumes a paused sequence at either the specified index or the next index in the sequence.

Eventi.on('validate', function(e) {
  var promise = asyncValidate($(this).closest('form'));
  e.pauseSequence(promise);
});
<form>
...
  <button click="validate,save,location@/home">Save and Quit</button>
</form>

Unlike trigger, Eventi also allows you to register listeners for event sequences as well. Such sequences can be fired as sequences (as above) or separately. You may also specify a time in milliseconds as the first data argument, in order to restrict the timeframe for sequence completion.

Eventi.on(editor, 'keyup[a],keyup[s],keyup[d],keyup[f]', function() {
  console.log('Fake typing!');
}, 500);

"Combo+Events"

Example definition: scroll+click

Allows you to listen for an unordered group of related events before executing the handler function. This is exactly like registering a listener for a sequence of events, except that the order in which the events are received is ignored (or irrelevant).

Eventi.on(editor, 'click+click+click', function() {
  Eventi.fire(this, 'tripleclick');
}, 200);

And yes, you can mix combos and sequences. When doing so, sequences events will be sub-events of combos, not vice versa.

Release History

  • 2014-02-11 v0.5.0 (alpha)
  • 2014-04-03 v1.0.0 (beta)
  • 2014-04-04 v1.0.1 (beta - IE fixes)
  • 2014-04-09 v1.0.2 (beta - toString and location fix)
  • 2014-04-17 v1.1.0 (beta - restructure artifacts, small improvements)
  • 2014-04-21 v1.2.0 (beta - docs, space-delimited alias arguments, artifact changes, optional data- prefixes, combo fix)
  • 2014-04-22 v1.2.1 (beta - docs, shorthand type for [key] and @location)
  • 2014-04-24 v1.3.0 (beta - server fixes, nodeunit tests, dual Eventi ctor)
  • 2014-04-29 v1.3.1 (beta - jquery integration aid)
  • 2014-08-15 v1.3.2 (beta - swap alias/delegate parser order)
  • 2014-08-18 v1.3.3 (beta - fix click/enter handling in declare)
  • 2014-08-19 v1.3.4 (beta - properly handle form elements as targets)
  • 2014-10-15 v1.3.5 (beta - location event fixes)
  • 2014-10-20 v1.3.6 (beta - location event fix, use sourcemap friendly main file for bower package)
  • 2014-10-20 v1.3.7 (beta - add main entry to package.json)