      1 /*
      2  * JBoss, Home of Professional Open Source
      3  * Copyright 2013, Red Hat, Inc. and individual contributors
      4  * by the @authors tag. See the copyright.txt in the distribution for a
      5  * full listing of individual contributors.
      6  *
      7  * This is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU Lesser General Public License as
      9  * published by the Free Software Foundation; either version 2.1 of
     10  * the License, or (at your option) any later version.
     11  *
     12  * This software is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this software; if not, write to the Free
     19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
     20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
     21  */
     22
     23 /**
     24  * @author Pavel Yaschenko
     25  */
     26
     27 // TODO: add support to bind multiple events using type param as an object with eventType,function pairs // see bindById method
     28 // TODO: update js docs
     29
     30 window.RichFaces = window.RichFaces || {};
     31 RichFaces.jQuery = RichFaces.jQuery || window.jQuery;
     32
     33 (function($, rf) {
     34
     35     /**
     36      * RichFaces Event API container
     37      * @class
     38      * @memberOf RichFaces
     39      * @static
     40      * @name Event
     41      * */
     42     rf.Event = rf.Event || {};
     43
     44     var getEventElement = function (selector) {
     45         if (!selector) {
     46             throw "RichFaces.Event: empty selector";
     47         }
     48         var element;
     49         if (rf.BaseComponent && selector instanceof rf.BaseComponent) {
     50             element = $(rf.getDomElement(selector.getEventElement()));
     51         } else {
     52             element = $(selector);
     53         }
     54
     55         return element;
     56     }
     57
     58     var getHandlerWrapper = function (component, fn) {
     59         return function (e, d) {
     60             if (!e[rf.RICH_CONTAINER]) {
     61                 e[rf.RICH_CONTAINER] = {data: d};
     62             }
     63             return fn.call(component || this, e, this, d);
     64         };
     65     }
     66
     67     var getMultipleHandlerWrapper = function (object, component) {
     68         var result = {};
     69         for (var type in object) {
     70             result[type] = getHandlerWrapper(component, object[type]);
     71         }
     72         return result;
     73     }
     74
     75     $.extend(rf.Event, {
     76             /**
     77              * @constant
     78              * @name RichFaces.Event.RICH_NAMESPACE
     79              * @type string
     80              * */
     81             RICH_NAMESPACE : "RICH",
     82
     83             /**
     84              * @constant
     85              * @name RichFaces.Event.EVENT_NAMESPACE_SEPARATOR
     86              * @type string
     87              * */
     88             EVENT_NAMESPACE_SEPARATOR : ".",
     89
     90             MESSAGE_EVENT_TYPE: "onmessage",
     91
     92             /**
     93              * Attach an event handler to execute when the DOM is fully loaded.
     94              *
     95              * @function
     96              * @name RichFaces.Event.ready
     97              * @param {function} fn - event handler
     98              * @return {jQuery} document element wrapped by jQuery
     99              * */
    100             ready : function(fn) {
    101                 // TODO: not completed yet
    102                 return $(document).ready(fn);
    103                 /*
    104                  function callback(jQueryReference) {
    105                  this; // document
    106                  }
    107                  */
    108             },
    109
    110             /**
    111              * Attach a handler to an event for the elements.
    112              * @function
    113              * @name RichFaces.Event.bind
    114              *
    115              * @param {string|DOMElement|jQuery} selector - jQuery elements selector
    116              * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
    117              * @param {function} fn - event handler
    118              * @param {Object} [data] - component or object with additional data
    119              * It is a context for an event handler
    120              * @return {function} function that binded to the element's event
    121              * */
    122             bind : function(selector, eventType, fn, component, data) {
    123                 // eventType: namespace can be used, like onclick.rf.conponentName
    124                 if (typeof eventType == "object") {
    125                     // in this case fn == component object
    126                     getEventElement(selector).bind(getMultipleHandlerWrapper(eventType, fn), data);
    127                 } else {
    128                     var f = getHandlerWrapper(component, fn);
    129                     getEventElement(selector).bind(eventType, data, f);
    130                     return f;
    131                 }
    132             },
    133
    134             /**
    135              * Attach a handler to an event for the element by element id.
    136              * @function
    137              * @name RichFaces.Event.bindById
    138              *
    139              * @param {string} id - DOM element id
    140              * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
    141              * @param {function} fn - event handler
    142              * @param {Object} [data] - component or object with additional data
    143              * It is a context for an event handler
    144              * @return {function} function that binded to the element's event
    145              * */
    146             bindById : function(id, eventType, fn, component, data) {
    147                 // eventType: namespace can be used, like onclick.rf.conponentName
    148                 if (typeof eventType == "object") {
    149                     // in this case fn == component object
    150                     $(document.getElementById(id)).bind(getMultipleHandlerWrapper(eventType, fn), data);
    151                 } else {
    152                     var f = getHandlerWrapper(component, fn);
    153                     $(document.getElementById(id)).bind(eventType, data, f);
    154                 }
    155                 return f;
    156             },
    157
    158             /**
    159              * Attach a handler to an event for the elements.
    160              * The handler will be called only once when event happened.
    161              * @function
    162              * @name RichFaces.Event.bindOne
    163              *
    164              * @param {string|DOMElement|jQuery} selector - jQuery elements selector
    165              * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
    166              * @param {function} fn - event handler
    167              * @param {Object} [data] - component or object with additional data
    168              * It is a context for an event handler
    169              * @return {function} function that binded to the element's event
    170              * */
    171             bindOne: function(selector, eventType, fn, component, data) {
    172                 // eventType: namespace can be used, like onclick.rf.conponentName
    173                 var f = getHandlerWrapper(component, fn);
    174                 getEventElement(selector).one(eventType, data, f);
    175                 return f;
    176             },
    177
    178             /**
    179              * Attach a handler to an event for the element by element id.
    180              * The handler will be called only once when event happened.
    181              * @function
    182              * @name RichFaces.Event.bindOneById
    183              *
    184              * @param {string} id - DOM element id
    185              * @param {string} eventType - one or more JavaScript event types, such as "click" or "submit," or custom event names
    186              * @param {function} fn - event handler
    187              * @param {Object} [data] - component or object with additional data
    188              * It is a context for an event handler
    189              * @return {function} function that binded to the element's event
    190              * */
    191             bindOneById: function(id, eventType, fn, component, data) {
    192                 // eventType: namespace can be used, like onclick.rf.conponentName
    193                 var f = getHandlerWrapper(component, fn);
    194                 $(document.getElementById(id)).one(eventType, data, f);
    195                 return f;
    196             },
    197
    198             /**
    199              * Remove a previously-attached event handler from the elements.
    200              * @function
    201              * @name RichFaces.Event.unbind
    202              *
    203              * @param {string|DOMElement|jQuery} selector - jQuery elements selector
    204              * @param {string} [eventType] - one or more JavaScript event types, such as "click" or "submit," or custom event names
    205              * @param {function} [fn] - event handler
    206              * @return {jQuery} element wrapped by jQuery
    207              * */
    208             unbind : function(selector, eventType, fn) {
    209                 // eventType: namespace can be used, like onclick.rf.conponentName
    210                 return getEventElement(selector).unbind(eventType, fn);
    211             },
    212
    213             /**
    214              * Remove a previously-attached event handler from the elements by element id.
    215              * The handler will be called only once when event happened.
    216              * @function
    217              * @name RichFaces.Event.unbindById
    218              *
    219              * @param {string} id - DOM element id
    220              * @param {string} [eventType] - one or more JavaScript event types, such as "click" or "submit," or custom event names
    221              * @param {function} [fn] - event handler
    222              * @return {jQuery} element wrapped by jQuery
    223              * */
    224             unbindById : function(id, eventType, fn) {
    225                 // eventType: namespace can be used, like onclick.rf.conponentName
    226                 return $(document.getElementById(id)).unbind(eventType, fn);
    227             },
    228
    229             // TODO add jsdocs and qunits
    230             bindScrollEventHandlers: function(element, handler, component) {
    231                 var elements = [];
    232                 element = rf.getDomElement(element).parentNode;
    233                 while (element && element != window.document.body) {
    234                     if (element.offsetWidth != element.scrollWidth || element.offsetHeight != element.scrollHeight) {
    235                         elements.push(element);
    236                         rf.Event.bind(element, "scroll" + component.getNamespace(), handler, component);
    237                     }
    238                     element = element.parentNode;
    239                 }
    240                 return elements;
    241             },
    242             unbindScrollEventHandlers: function(elements, component) {
    243                 rf.Event.unbind(elements, "scroll" + component.getNamespace());
    244             },
    245
    246             /**
    247              * Execute all handlers and behaviors attached to the matched elements for the given event type.
    248              * @function
    249              * @name RichFaces.Event.fire
    250              *
    251              * @param {string|DOMElement|jQuery} selector - jQuery elements selector
    252              * @param {string} eventType - event type
    253              * @param {Object} [data] - a object of additional parameters to pass to the event handler
    254              * @return {jQuery} element wrapped by jQuery
    255              * */
    256             fire : function(selector, eventType, data) {
    257                 var event = $.Event(eventType);
    258                 getEventElement(selector).trigger(event, [data]);
    259                 return !event.isDefaultPrevented();
    260             },
    261
    262             /**
    263              * The same as the fire method, but selects element by id.
    264              * @function
    265              * @name RichFaces.Event.fireById
    266              *
    267              * @param {string} id - DOM element id
    268              * @param {string} eventType - event type
    269              * @param {Object} [data] - a object of additional parameters to pass to the event handler
    270              * @return {jQuery} element wrapped by jQuery
    271              * */
    272             fireById : function(id, eventType, data) {
    273                 var event = $.Event(eventType);
    274                 $(document.getElementById(id)).trigger(event, [data]);
    275                 return !event.isDefaultPrevented();
    276             },
    277
    278             /**
    279              * The same as the fire method, but:
    280              *  - does not cause the default behavior of an event to occur
    281              *  - does not bubble up event
    282              *  - call handler only for the first founded element
    283              *  - returns whatever value that was returned by the handler
    284              * @function
    285              * @name RichFaces.Event.callHandler
    286              *
    287              * @param {string|DOMElement|jQuery} selector - jQuery elements selector
    288              * @param {string} eventType - event type
    289              * @param {Object} [data] - a object of additional parameters to pass to the event handler
    290              * @return value that was returned by the handler
    291              * */
    292             callHandler : function(selector, eventType, data) {
    293                 return getEventElement(selector).triggerHandler(eventType, [data]);
    294             },
    295
    296             /**
    297              * The same as the callHandler method, but selects element by id.
    298              * @function
    299              * @name RichFaces.Event.callHandlerById
    300              *
    301              * @param {string} id - DOM element id
    302              * @param {string} eventType - event type
    303              * @param {Object} [data] - a object of additional parameters to pass to the event handler
    304              * @return value that was returned by the handler
    305              *  */
    306             callHandlerById : function(id, eventType, data) {
    307                 return $(document.getElementById(id)).triggerHandler(eventType, [data]);
    308             },
    309
    310             /**
    311              * Create an event namespace for the components.
    312              * @function
    313              * @name RichFaces.Event.createNamespace
    314              *
    315              * @param {string} [componentName] - component name
    316              * @param {string} [id] - element id
    317              * @param {string} [prefix=RichFaces.Event.RICH_NAMESPACE] - namespace prefix
    318              * @return {string} namespace string
    319              *  */
    320                 // TODO: rename argument names
    321             createNamespace : function(componentName, id, prefix) {
    322                 var a = [];
    323                 a.push(prefix || rf.Event.RICH_NAMESPACE);
    324                 if (componentName) {
    325                     a.push(componentName);
    326                 }
    327                 if (id) {
    328                     a.push(id);
    329                 }
    330                 return a.join(rf.Event.EVENT_NAMESPACE_SEPARATOR);
    331             }
    332         });
    333
    334 })(RichFaces.jQuery, RichFaces);
    335
    336 /*
    337  fn : function (eventObject, element) {
    338  this; // object passed as data to bind function or dom element if no data
    339  element; // dom element
    340
    341  }
    342  */
    343
    344 // 	API usage example:
    345 // 		RichFaces.Event.bind(selector, type, fn, data);
    346