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