(function (glob) {
  var version = "0.5.0",
      has = "hasOwnProperty",
      separator = /[\.\/]/,
      comaseparator = /\s*,\s*/,
      wildcard = "*",
      fun = function () {},
      numsort = function (a, b) {
          return a - b;
      },
      current_event,
      stop,
      events = {n: {}},
      firstDefined = function () {
          for (var i = 0, ii = this.length; i < ii; i++) {
              if (typeof this[i] != "undefined") {
                  return this[i];
              }
          }
      },
      lastDefined = function () {
          var i = this.length;
          while (--i) {
              if (typeof this[i] != "undefined") {
                  return this[i];
              }
          }
      },
      objtos = Object.prototype.toString,
      Str = String,
      isArray = Array.isArray || function (ar) {
          return ar instanceof Array || objtos.call(ar) == "[object Array]";
      };
  /*\
   * eve
   [ method ]

   * Fires event with given `name`, given scope and other parameters.

   > Arguments

   - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
   - scope (object) context for the event handlers
   - varargs (...) the rest of arguments will be sent to event handlers

   = (object) array of returned values from the listeners. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value.
  \*/
      var eve = function (name, scope) {
          var e = events,
              oldstop = stop,
              args = Array.prototype.slice.call(arguments, 2),
              listeners = eve.listeners(name),
              z = 0,
              f = false,
              l,
              indexed = [],
              queue = {},
              out = [],
              ce = current_event,
              errors = [];
          out.firstDefined = firstDefined;
          out.lastDefined = lastDefined;
          current_event = name;
          stop = 0;
          for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
              indexed.push(listeners[i].zIndex);
              if (listeners[i].zIndex < 0) {
                  queue[listeners[i].zIndex] = listeners[i];
              }
          }
          indexed.sort(numsort);
          while (indexed[z] < 0) {
              l = queue[indexed[z++]];
              out.push(l.apply(scope, args));
              if (stop) {
                  stop = oldstop;
                  return out;
              }
          }
          for (i = 0; i < ii; i++) {
              l = listeners[i];
              if ("zIndex" in l) {
                  if (l.zIndex == indexed[z]) {
                      out.push(l.apply(scope, args));
                      if (stop) {
                          break;
                      }
                      do {
                          z++;
                          l = queue[indexed[z]];
                          l && out.push(l.apply(scope, args));
                          if (stop) {
                              break;
                          }
                      } while (l)
                  } else {
                      queue[l.zIndex] = l;
                  }
              } else {
                  out.push(l.apply(scope, args));
                  if (stop) {
                      break;
                  }
              }
          }
          stop = oldstop;
          current_event = ce;
          return out;
      };
      // Undocumented. Debug only.
      eve._events = events;
  /*\
   * eve.listeners
   [ method ]

   * Internal method which gives you array of all event handlers that will be triggered by the given `name`.

   > Arguments

   - name (string) name of the event, dot (`.`) or slash (`/`) separated

   = (array) array of event handlers
  \*/
  eve.listeners = function (name) {
      var names = isArray(name) ? name : name.split(separator),
          e = events,
          item,
          items,
          k,
          i,
          ii,
          j,
          jj,
          nes,
          es = [e],
          out = [];
      for (i = 0, ii = names.length; i < ii; i++) {
          nes = [];
          for (j = 0, jj = es.length; j < jj; j++) {
              e = es[j].n;
              items = [e[names[i]], e[wildcard]];
              k = 2;
              while (k--) {
                  item = items[k];
                  if (item) {
                      nes.push(item);
                      out = out.concat(item.f || []);
                  }
              }
          }
          es = nes;
      }
      return out;
  };
  /*\
   * eve.separator
   [ method ]

   * If for some reasons you don’t like default separators (`.` or `/`) you can specify yours
   * here. Be aware that if you pass a string longer than one character it will be treated as
   * a list of characters.

   - separator (string) new separator. Empty string resets to default: `.` or `/`.
  \*/
  eve.separator = function (sep) {
      if (sep) {
          sep = Str(sep).replace(/(?=[\.\^\]\[\-])/g, "\\");
          sep = "[" + sep + "]";
          separator = new RegExp(sep);
      } else {
          separator = /[\.\/]/;
      }
  };
  /*\
   * eve.on
   [ method ]
   **
   * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
   | eve.on("*.under.*", f);
   | eve("mouse.under.floor"); // triggers f
   * Use @eve to trigger the listener.
   **
   - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
   - f (function) event handler function
   **
   - name (array) if you don’t want to use separators, you can use array of strings
   - f (function) event handler function
   **
   = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. 
   > Example:
   | eve.on("mouse", eatIt)(2);
   | eve.on("mouse", scream);
   | eve.on("mouse", catchIt)(1);
   * This will ensure that `catchIt` function will be called before `eatIt`.
   *
   * If you want to put your handler before non-indexed handlers, specify a negative value.
   * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
  \*/
  eve.on = function (name, f) {
      if (typeof f != "function") {
          return function () {};
      }
      var names = isArray(name) ? (isArray(name[0]) ? name : [name]) : Str(name).split(comaseparator);
      for (var i = 0, ii = names.length; i < ii; i++) {
          (function (name) {
              var names = isArray(name) ? name : Str(name).split(separator),
                  e = events,
                  exist;
              for (var i = 0, ii = names.length; i < ii; i++) {
                  e = e.n;
                  e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
              }
              e.f = e.f || [];
              for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
                  exist = true;
                  break;
              }
              !exist && e.f.push(f);
          }(names[i]));
      }
      return function (zIndex) {
          if (+zIndex == +zIndex) {
              f.zIndex = +zIndex;
          }
      };
  };
  /*\
   * eve.f
   [ method ]
   **
   * Returns function that will fire given event with optional arguments.
   * Arguments that will be passed to the result function will be also
   * concated to the list of final arguments.
   | el.onclick = eve.f("click", 1, 2);
   | eve.on("click", function (a, b, c) {
   |     console.log(a, b, c); // 1, 2, [event object]
   | });
   > Arguments
   - event (string) event name
   - varargs (…) and any other arguments
   = (function) possible event handler function
  \*/
  eve.f = function (event) {
      var attrs = [].slice.call(arguments, 1);
      return function () {
          eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
      };
  };
  /*\
   * eve.stop
   [ method ]
   **
   * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
  \*/
  eve.stop = function () {
      stop = 1;
  };
  /*\
   * eve.nt
   [ method ]
   **
   * Could be used inside event handler to figure out actual name of the event.
   **
   > Arguments
   **
   - subname (string) #optional subname of the event
   **
   = (string) name of the event, if `subname` is not specified
   * or
   = (boolean) `true`, if current event’s name contains `subname`
  \*/
  eve.nt = function (subname) {
      var cur = isArray(current_event) ? current_event.join(".") : current_event;
      if (subname) {
          return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(cur);
      }
      return cur;
  };
  /*\
   * eve.nts
   [ method ]
   **
   * Could be used inside event handler to figure out actual name of the event.
   **
   **
   = (array) names of the event
  \*/
  eve.nts = function () {
      return isArray(current_event) ? current_event : current_event.split(separator);
  };
  /*\
   * eve.off
   [ method ]
   **
   * Removes given function from the list of event listeners assigned to given name.
   * If no arguments specified all the events will be cleared.
   **
   > Arguments
   **
   - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
   - f (function) event handler function
  \*/
  /*\
   * eve.unbind
   [ method ]
   **
   * See @eve.off
  \*/
  eve.off = eve.unbind = function (name, f) {
      if (!name) {
          eve._events = events = {n: {}};
          return;
      }
      var names = isArray(name) ? (isArray(name[0]) ? name : [name]) : Str(name).split(comaseparator);
      if (names.length > 1) {
          for (var i = 0, ii = names.length; i < ii; i++) {
              eve.off(names[i], f);
          }
          return;
      }
      names = isArray(name) ? name : Str(name).split(separator);
      var e,
          key,
          splice,
          i, ii, j, jj,
          cur = [events],
          inodes = [];
      for (i = 0, ii = names.length; i < ii; i++) {
          for (j = 0; j < cur.length; j += splice.length - 2) {
              splice = [j, 1];
              e = cur[j].n;
              if (names[i] != wildcard) {
                  if (e[names[i]]) {
                      splice.push(e[names[i]]);
                      inodes.unshift({
                          n: e,
                          name: names[i]
                      });
                  }
              } else {
                  for (key in e) if (e[has](key)) {
                      splice.push(e[key]);
                      inodes.unshift({
                          n: e,
                          name: key
                      });
                  }
              }
              cur.splice.apply(cur, splice);
          }
      }
      for (i = 0, ii = cur.length; i < ii; i++) {
          e = cur[i];
          while (e.n) {
              if (f) {
                  if (e.f) {
                      for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
                          e.f.splice(j, 1);
                          break;
                      }
                      !e.f.length && delete e.f;
                  }
                  for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                      var funcs = e.n[key].f;
                      for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
                          funcs.splice(j, 1);
                          break;
                      }
                      !funcs.length && delete e.n[key].f;
                  }
              } else {
                  delete e.f;
                  for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                      delete e.n[key].f;
                  }
              }
              e = e.n;
          }
      }
      // prune inner nodes in path
      prune: for (i = 0, ii = inodes.length; i < ii; i++) {
          e = inodes[i];
          for (key in e.n[e.name].f) {
              // not empty (has listeners)
              continue prune;
          }
          for (key in e.n[e.name].n) {
              // not empty (has children)
              continue prune;
          }
          // is empty
          delete e.n[e.name];
      }
  };
  /*\
   * eve.once
   [ method ]
   **
   * Binds given event handler with a given name to only run once then unbind itself.
   | eve.once("login", f);
   | eve("login"); // triggers f
   | eve("login"); // no listeners
   * Use @eve to trigger the listener.
   **
   > Arguments
   **
   - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
   - f (function) event handler function
   **
   = (function) same return function as @eve.on
  \*/
  eve.once = function (name, f) {
      var f2 = function () {
          eve.off(name, f2);
          return f.apply(this, arguments);
      };
      return eve.on(name, f2);
  };
  /*\
   * eve.version
   [ property (string) ]
   **
   * Current version of the library.
  \*/
  eve.version = version;
  eve.toString = function () {
      return "You are running Eve " + version;
  };
  (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define === "function" && define.amd ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
})(this);