      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);