# Source: richfaces-event.js

    /*
     * JBoss, Home of Professional Open Source
     * Copyright 2013, Red Hat, Inc. and individual contributors
     * by the @authors tag. See the copyright.txt in the distribution for a
     * full listing of individual contributors.
     *
     * This is free software; you can redistribute it and/or modify it
     * under the terms of the GNU Lesser General Public License as
     * published by the Free Software Foundation; either version 2.1 of
     * the License, or (at your option) any later version.
     *
     * This software is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
     * License along with this software; if not, write to the Free
     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
     */

    /**
     * @author Pavel Yaschenko
     */

    // TODO: add support to bind multiple events using type param as an object with eventType,function pairs // see bindById method
    // TODO: update js docs

    window.RichFaces = window.RichFaces || {};
    RichFaces.jQuery = RichFaces.jQuery || window.jQuery;

    (function($, rf) {

        /**
         * RichFaces Event API container
         * @class
         * @memberOf! RichFaces
         * @static
         * @name Event
         * */
        rf.Event = rf.Event || {};

        var getEventElement = function (selector) {
            if (!selector) {
                throw "RichFaces.Event: empty selector";
            }
            var element;
            if (rf.BaseComponent && selector instanceof rf.BaseComponent) {
                element = $(rf.getDomElement(selector.getEventElement()));
            } else {
                element = $(selector);
            }

            return element;
        }

        var getHandlerWrapper = function (component, fn) {
            return function (e, d) {
                if (!e[rf.RICH_CONTAINER]) {
                    e[rf.RICH_CONTAINER] = {data: d};
                }
                return fn.call(component || this, e, this, d);
            };
        }

        var getMultipleHandlerWrapper = function (object, component) {
            var result = {};
            for (var type in object) {
                if (object.hasOwnProperty(type)) {
                    result[type] = getHandlerWrapper(component, object[type]);
                }
            }
            return result;
        }

        $.extend(rf.Event, {
                /**
                 * @constant
                 * @name RichFaces.Event.RICH_NAMESPACE
                 * @type {string}
                 * */
                RICH_NAMESPACE : "RICH",

                /**
                 * @constant
                 * @name RichFaces.Event.EVENT_NAMESPACE_SEPARATOR
                 * @type {string}
                 * */
                EVENT_NAMESPACE_SEPARATOR : ".",

                MESSAGE_EVENT_TYPE: "onmessage",

                /**
                 * Attach an event handler to execute when the DOM is fully loaded.
                 *
                 * @function
                 * @name RichFaces.Event.ready
                 * @param {function} fn - event handler
                 * @return {jQuery} document element wrapped by jQuery
                 * */
                ready : function(fn) {
                    // TODO: not completed yet
                    return $(document).ready(fn);
                    /*
                     function callback(jQueryReference) {
                     this; // document
                     }
                     */
                },

                /**
                 * Attach a handler to an event for the elements.
                 * @function
                 * @name RichFaces.Event.bind
                 *
                 * @param {string|DOMElement|jQuery} selector - jQuery elements selector
                 * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} fn - event handler
                 * @param {Object} [data] - component or object with additional data
                 * It is a context for an event handler
                 * @return {function} function that binded to the element's event
                 * */
                bind : function(selector, eventType, fn, component, data) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    if (typeof eventType == "object") {
                        // in this case fn == component object
                        getEventElement(selector).bind(getMultipleHandlerWrapper(eventType, fn), data);
                    } else {
                        var f = getHandlerWrapper(component, fn);
                        getEventElement(selector).bind(eventType, data, f);
                        return f;
                    }
                },

                /**
                 * Attach a handler to an event for the element by element id.
                 * @function
                 * @name RichFaces.Event.bindById
                 *
                 * @param {string} id - DOM element id
                 * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} fn - event handler
                 * @param {Object} [data] - component or object with additional data
                 * It is a context for an event handler
                 * @return {function} function that binded to the element's event
                 * */
                bindById : function(id, eventType, fn, component, data) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    if (typeof eventType == "object") {
                        // in this case fn == component object
                        $(document.getElementById(id)).bind(getMultipleHandlerWrapper(eventType, fn), data);
                    } else {
                        var f = getHandlerWrapper(component, fn);
                        $(document.getElementById(id)).bind(eventType, data, f);
                    }
                    return f;
                },

                /**
                 * Attach a handler to an event for the elements.
                 * The handler will be called only once when event happened.
                 * @function
                 * @name RichFaces.Event.bindOne
                 *
                 * @param {string|DOMElement|jQuery} selector - jQuery elements selector
                 * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} fn - event handler
                 * @param {Object} [data] - component or object with additional data
                 * It is a context for an event handler
                 * @return {function} function that binded to the element's event
                 * */
                bindOne: function(selector, eventType, fn, component, data) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    var f = getHandlerWrapper(component, fn);
                    getEventElement(selector).one(eventType, data, f);
                    return f;
                },

                /**
                 * Attach a handler to an event for the element by element id.
                 * The handler will be called only once when event happened.
                 * @function
                 * @name RichFaces.Event.bindOneById
                 *
                 * @param {string} id - DOM element id
                 * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} fn - event handler
                 * @param {Object} [data] - component or object with additional data
                 * It is a context for an event handler
                 * @return {function} function that binded to the element's event
                 * */
                bindOneById: function(id, eventType, fn, component, data) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    var f = getHandlerWrapper(component, fn);
                    $(document.getElementById(id)).one(eventType, data, f);
                    return f;
                },

                /**
                 * Remove a previously-attached event handler from the elements.
                 * @function
                 * @name RichFaces.Event.unbind
                 *
                 * @param {string|DOMElement|jQuery} selector - jQuery elements selector
                 * @param {string} [eventType] - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} [fn] - event handler
                 * @return {jQuery} element wrapped by jQuery
                 * */
                unbind : function(selector, eventType, fn) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    return getEventElement(selector).unbind(eventType, fn);
                },

                /**
                 * Remove a previously-attached event handler from the elements by element id.
                 * The handler will be called only once when event happened.
                 * @function
                 * @name RichFaces.Event.unbindById
                 *
                 * @param {string} id - DOM element id
                 * @param {string} [eventType] - one or more JavaScript event types, such as "click" or "submit," or custom event names
                 * @param {function} [fn] - event handler
                 * @return {jQuery} element wrapped by jQuery
                 * */
                unbindById : function(id, eventType, fn) {
                    // eventType: namespace can be used, like onclick.rf.conponentName
                    return $(document.getElementById(id)).unbind(eventType, fn);
                },

                // TODO add jsdocs and qunits
                bindScrollEventHandlers: function(element, handler, component) {
                    var elements = [];
                    element = rf.getDomElement(element).parentNode;
                    while (element && element != window.document.body) {
                        if (element.offsetWidth != element.scrollWidth || element.offsetHeight != element.scrollHeight) {
                            elements.push(element);
                            rf.Event.bind(element, "scroll" + component.getNamespace(), handler, component);
                        }
                        element = element.parentNode;
                    }
                    return elements;
                },
                unbindScrollEventHandlers: function(elements, component) {
                    rf.Event.unbind(elements, "scroll" + component.getNamespace());
                },

                /**
                 * Execute all handlers and behaviors attached to the matched elements for the given event type.
                 * @function
                 * @name RichFaces.Event.fire
                 *
                 * @param {string|DOMElement|jQuery} selector - jQuery elements selector
                 * @param {string} eventType - event type
                 * @param {Object} [data] - a object of additional parameters to pass to the event handler
                 * @return {jQuery} element wrapped by jQuery
                 * */
                fire : function(selector, eventType, data) {
                    var event = $.Event(eventType);
                    getEventElement(selector).trigger(event, [data]);
                    return !event.isDefaultPrevented();
                },

                /**
                 * The same as the fire method, but selects element by id.
                 * @function
                 * @name RichFaces.Event.fireById
                 *
                 * @param {string} id - DOM element id
                 * @param {string} eventType - event type
                 * @param {Object} [data] - a object of additional parameters to pass to the event handler
                 * @return {jQuery} element wrapped by jQuery
                 * */
                fireById : function(id, eventType, data) {
                    var event = $.Event(eventType);
                    $(document.getElementById(id)).trigger(event, [data]);
                    return !event.isDefaultPrevented();
                },

                /**
                 * The same as the fire method, but:
                 *  - does not cause the default behavior of an event to occur
                 *  - does not bubble up event
                 *  - call handler only for the first founded element
                 *  - returns whatever value that was returned by the handler
                 * @function
                 * @name RichFaces.Event.callHandler
                 *
                 * @param {string|DOMElement|jQuery} selector - jQuery elements selector
                 * @param {string} eventType - event type
                 * @param {Object} [data] - a object of additional parameters to pass to the event handler
                 * @return value that was returned by the handler
                 * */
                callHandler : function(selector, eventType, data) {
                    return getEventElement(selector).triggerHandler(eventType, [data]);
                },

                /**
                 * The same as the callHandler method, but selects element by id.
                 * @function
                 * @name RichFaces.Event.callHandlerById
                 *
                 * @param {string} id - DOM element id
                 * @param {string} eventType - event type
                 * @param {Object} [data] - a object of additional parameters to pass to the event handler
                 * @return value that was returned by the handler
                 *  */
                callHandlerById : function(id, eventType, data) {
                    return $(document.getElementById(id)).triggerHandler(eventType, [data]);
                },

                /**
                 * Create an event namespace for the components.
                 * @function
                 * @name RichFaces.Event.createNamespace
                 *
                 * @param {string} [componentName] - component name
                 * @param {string} [id] - element id
                 * @param {string} [prefix=RichFaces.Event.RICH_NAMESPACE] - namespace prefix
                 * @return {string} namespace string
                 *  */
                    // TODO: rename argument names
                createNamespace : function(componentName, id, prefix) {
                    var a = [];
                    a.push(prefix || rf.Event.RICH_NAMESPACE);
                    if (componentName) {
                        a.push(componentName);
                    }
                    if (id) {
                        a.push(id);
                    }
                    return a.join(rf.Event.EVENT_NAMESPACE_SEPARATOR);
                }
            });

    })(RichFaces.jQuery, RichFaces);

    /*
     fn : function (eventObject, element) {
     this; // object passed as data to bind function or dom element if no data
     element; // dom element

     }
     */

    // 	API usage example:
    // 		RichFaces.Event.bind(selector, type, fn, data);