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