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 window.RichFaces = window.RichFaces || {};
 28 RichFaces.jQuery = RichFaces.jQuery || window.jQuery;
 29 
 30 (function ($, rf, params) {
 31 
 32     rf.blankFunction = function () {
 33     }; //TODO: add it to global library
 34 
 35     /**
 36      * @class Base class for all components.
 37      * All RichFaces components should use this class as base or another RichFaces class which based on it.
 38      *
 39      <pre><code>
 40      //Inheritance example:
 41      (function ($, richfaces, params) {
 42 
 43      // Constructor definition
 44      richfaces.MyComponent = function(componentId, [options]) {
 45      // call constructor of parent class
 46      $super.constructor.call(this, componentId, [options]);
 47 
 48      <span style="color:red">
 49      // call this.attachToDom method to attach component to dom element
 50      // its required for the client side API calls and to clean up after ajax request or page unload:
 51      // destroy method will be called if component attached to dom
 52      this.attachToDom(componentId);
 53      </span>
 54      };
 55 
 56      // define private method
 57      var myPrivateMethod = function () {
 58      }
 59 
 60      // Extend component class and add protected methods from parent class to our container
 61      richfaces.BaseComponent.extend(richfaces.BaseComponent, richfaces.MyComponent);
 62 
 63      // define super class link
 64      var $super = richfaces.MyComponent.$super;
 65 
 66      // Add new properties and methods
 67      $.extend(richfaces.MyComponent.prototype, (function (params) {
 68      return {
 69      name:"MyComponent",
 70      f:function (){alert("hello"),
 71      // destroy method definition for clean up
 72      destroy: function () {
 73      // clean up code here
 74 
 75      // call parent's destroy method
 76      $super.destroy.call(this);
 77      }
 78      }
 79      };
 80      })(params));
 81      })(jQuery, RichFaces);
 82      </code></pre>
 83      *
 84      * @memberOf RichFaces
 85      * @name BaseComponent
 86      *
 87      * @constructor
 88      * @param {String} componentId - component id
 89      * */
 90     rf.BaseComponent = function(componentId) {
 91         this.id = componentId;
 92         this.options = this.options || {};
 93     };
 94 
 95     var $p = {};
 96 
 97     var extend = function (parent, child, h) {
 98         h = h || {};
 99         var F = rf.blankFunction;
100         F.prototype = parent.prototype;
101         child.prototype = new F();
102         child.prototype.constructor = child;
103         child.$super = parent.prototype;
104         if (child.$super == rf.BaseComponent.prototype) {
105             var r = jQuery.extend({}, $p, h || {});
106         }
107 
108         var _parent = child;
109 
110         // create wrapper with protected methods and variables
111         child.extend = function (_child, _h) {
112             _h = _h || {};
113             var _r = jQuery.extend({}, r || h || {}, _h || {});
114             return extend(_parent, _child, _r);
115         }
116         return r || h;
117     };
118 
119     /**
120      * Method extends child class prototype with parent prototype
121      * and return the object with parent's protected methods
122      *
123      * @function
124      * @name RichFaces.BaseComponent.extend
125      *
126      * @return {object}
127      * */
128     rf.BaseComponent.extend = function(child, h) {
129         return extend(rf.BaseComponent, child, h);
130     };
131 
132 
133     /**
134      * Easy way to create a subclass.
135      *
136      * Example:
137      *
138      * RichFaces.ui.MyClass = RichFaces.BaseComponent.extendClass({
139      *     // Class name
140      *     name: "MyClass",
141      *
142      *     // Constructor
143      *     init : function (...) {
144      *         // ...
145      *     },
146      *
147      *     // public api
148      *     publicFunction : function () {
149      *         // ...
150      *     },
151      *
152      *     // private api
153      *     // names of private methods should start with '__' (2 underscore symbols)
154      *     __privateFunction : function () {
155      *         // ...
156      *     },
157      *
158      *     __overrideMethod : function () {
159      *         // if you need to use method from parent class use link to parent prototype
160      *         // like in previous solution with extend method
161      *         $super.__overrideMethod.call(this, ...params...);
162      *
163      *         //...
164      *     }
165      *
166      * });
167      *
168      * RichFaces.ui.MySecondClass = RichFaces.ui.MyClass({
169      *     //
170      *     name : "MySecondClass",
171      *
172      *     // Constructor
173      *     init : function (...) {
174      *         // ...
175      *     }
176      *
177      * })
178      *
179      * */
180     rf.BaseComponent.extendClass = function (methods) {
181         var DerivedClass = methods.init || rf.blankFunction;
182         var SupperClass = this;
183 
184         SupperClass.extend(DerivedClass);
185 
186         DerivedClass.extendClass = SupperClass.extendClass;
187 
188         $.extend(DerivedClass.prototype, methods);
189 
190         return DerivedClass;
191     };
192 
193     $.extend(rf.BaseComponent.prototype, (function (params) {
194         return {
195             /**
196              * Component name.
197              *
198              * @name RichFaces.BaseComponent#name
199              * @type String
200              * */
201             name: "BaseComponent",
202 
203             /**
204              * Method for converting object to string
205              *
206              * @function
207              * @name RichFaces.BaseComponent#toString
208              *
209              * @return {String}
210              * */
211             toString: function() {
212                 var result = [];
213                 if (this.constructor.$super) {
214                     result[result.length] = this.constructor.$super.toString();
215                 }
216                 result[result.length] = this.name;
217                 return result.join(', ');
218             },
219 
220             /** TODO: add jsdocs and qunit tests
221              *
222              */
223             getValue: function() {
224                 return;
225             },
226 
227             /**
228              * Method returns element's id for event handlers binding.
229              * Event API calls this method when binding by component object as selector was used.
230              *
231              * @function
232              * @name RichFaces.BaseComponent#getEventElement
233              *
234              * @return {String}
235              * */
236             getEventElement: function() {
237                 return this.id;
238             },
239 
240             /**
241              * Attach component object to DOM element by component id, DOM element or jQuery object and returns the element
242              * Its required for the client side API calls and to clean up after ajax request or document unload by
243              * calling destroy method
244              *
245              * @function
246              * @name RichFaces.BaseComponent#attachToDom
247              * @param {string|DOMElement|jQuery} source - component id, DOM element or DOM elements wrapped by jQuery
248              *
249              * @return {DOMElement}
250              * */
251             attachToDom: function(source) {
252                 source = source || this.id;
253                 var element = rf.getDomElement(source);
254                 if (element) {
255                     var container = element[rf.RICH_CONTAINER] = element[rf.RICH_CONTAINER] || {};
256                     container.component = this;
257                 }
258                 return element;
259             },
260 
261             /**
262              * Detach component object from DOM element by component id, DOM element or jQuery object
263              *
264              * @function
265              * @name RichFaces.BaseComponent#detach
266              * @param {string|DOMElement|jQuery} source - component id, DOM element or DOM elements wrapped by jQuery
267              *
268              * */
269             detach: function(source) {
270                 source = source || this.id;
271                 var element = rf.getDomElement(source);
272                 element && element[rf.RICH_CONTAINER] && (element[rf.RICH_CONTAINER].component = null);
273             },
274 
275             /**
276              * Invokes event on on the DOM element
277              * @param eventType event type, e.g. "click"
278              * @param element DOM element object
279              * @param event jQuery Event
280              * @param data additional data used for event handler
281              * @return true if an event is successfully invoked
282              */
283             invokeEvent: function(eventType, element, event, data) {
284                 var handlerResult, result;
285                 var eventObj = $.extend({}, event, {type: eventType});
286 
287                 if (!eventObj) {
288                     if (document.createEventObject) {
289                         eventObj = document.createEventObject();
290                         eventObj.type = eventType;
291                     }
292                     else if (document.createEvent) {
293                         eventObj = document.createEvent('Events');
294                         eventObj.initEvent(eventType, true, false);
295                     }
296                 }
297                 eventObj[rf.RICH_CONTAINER] = {component:this, data: data};
298 
299                 var eventHandler = this.options['on' + eventType];
300 
301                 if (typeof eventHandler == "function") {
302                     handlerResult = eventHandler.call(element, eventObj);
303                 }
304 
305                 if (rf.Event) {
306                     result = rf.Event.callHandler(this, eventType, data);
307                 }
308 
309                 if (result != false && handlerResult != false) result = true;
310 
311                 return result;
312             },
313 
314             /**
315              * Destroy method. Will be called before remove component from the page
316              *
317              * @function
318              * @name RichFaces.BaseComponent#destroy
319              *
320              * */
321             destroy: function() {
322             }
323         };
324     })(params));
325 
326     rf.BaseNonVisualComponent = function(componentId) {
327         this.id = componentId;
328         this.options = this.options || {};
329     };
330 
331     rf.BaseNonVisualComponent.extend = function(child, h) {
332         return extend(rf.BaseNonVisualComponent, child, h);
333     };
334 
335     rf.BaseNonVisualComponent.extendClass = function (methods) {
336         var DerivedClass = methods.init || rf.blankFunction;
337         var SupperClass = this;
338 
339         SupperClass.extend(DerivedClass);
340 
341         DerivedClass.extendClass = SupperClass.extendClass;
342 
343         $.extend(DerivedClass.prototype, methods);
344 
345         return DerivedClass;
346     };
347 
348     $.extend(rf.BaseNonVisualComponent.prototype, (function (params) {
349         return {
350             name: "BaseNonVisualComponent",
351 
352             toString: function() {
353                 var result = [];
354                 if (this.constructor.$super) {
355                     result[result.length] = this.constructor.$super.toString();
356                 }
357                 result[result.length] = this.name;
358                 return result.join(', ');
359             },
360 
361             getValue: function() {
362                 return;
363             },
364             /**
365              * Attach component object to DOM element by component id, DOM element or jQuery object and returns the element
366              * Its required for the client side API calls and to clean up after ajax request or document unload by
367              * calling destroy method
368              *
369              * @function
370              * @name RichFaces.BaseNonVisualComponent#attachToDom
371              * @param {string|DOMElement|jQuery} source - component id, DOM element or DOM elements wrapped by jQuery
372              *
373              * @return {DOMElement}
374              * */
375             attachToDom: function(source) {
376                 source = source || this.id;
377                 var element = rf.getDomElement(source);
378                 if (element) {
379                     var container = element[rf.RICH_CONTAINER] = element[rf.RICH_CONTAINER] || {};
380                     if (container.attachedComponents) {
381                         container.attachedComponents[this.name] = this;
382                     } else {
383                         container.attachedComponents = {};
384                         container.attachedComponents[this.name] = this;
385                     }
386                 }
387                 return element;
388             },
389 
390             /**
391              * Detach component object from DOM element by component id, DOM element or jQuery object
392              *
393              * @function
394              * @name RichFaces.BaseNonVisualComponent#detach
395              * @param {string|DOMElement|jQuery} source - component id, DOM element or DOM elements wrapped by jQuery
396              *
397              * */
398             detach: function(source) {
399                 source = source || this.id;
400                 var element = rf.getDomElement(source);
401                 element && element[rf.RICH_CONTAINER] && (element[rf.RICH_CONTAINER].attachedComponents[this.name] = null);
402             },
403 
404             /**
405              * Destroy method. Will be called before remove component from the page
406              *
407              * @function
408              * @name RichFaces.BaseNonVisualComponent#destroy
409              *
410              * */
411             destroy: function() {
412             }
413         };
414     })(params));
415 
416 
417 })(jQuery, window.RichFaces || (window.RichFaces = {}));
418 
419 // RichFaces Base class for ui components
420 (function($, rf) {
421 
422     rf.ui = rf.ui || {};
423 
424     // Constructor definition
425     rf.ui.Base = function(componentId, options, defaultOptions) {
426         this.namespace = "." + rf.Event.createNamespace(this.name, componentId);
427         // call constructor of parent class
428         $super.constructor.call(this, componentId);
429         this.options = $.extend(this.options, defaultOptions, options);
430         this.attachToDom();
431         this.__bindEventHandlers();
432     };
433 
434     // Extend component class and add protected methods from parent class to our container
435     rf.BaseComponent.extend(rf.ui.Base);
436 
437     // define super class link
438     var $super = rf.ui.Base.$super;
439 
440     $.extend(rf.ui.Base.prototype, {
441             __bindEventHandlers: function () {
442             },
443             destroy: function () {
444                 rf.Event.unbindById(this.id, this.namespace);
445                 $super.destroy.call(this);
446             }
447         });
448 
449 })(RichFaces.jQuery, RichFaces);