// Copyright © 2013 Adobe Systems Incorporated. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the “License”); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an “AS IS” BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ┌────────────────────────────────────────────────────────────┐ \ // │ Eve 0.5.0 - JavaScript Events Library │ \ // ├────────────────────────────────────────────────────────────┤ \ // │ Author Dmitry Baranovskiy (dmitry.baranovskiy.com/) │ \ // └────────────────────────────────────────────────────────────┘ \

(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.
\*/
    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];
    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]]);
                }
            } else {
                for (key in e) if (e[has](key)) {
                    splice.push(e[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;
        }
    }
};
/*\
 * 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);