2 Copyright (c) 2011, OpenERP S.A.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * (Almost) unmodified John Resig's inheritance
33 /* Simple JavaScript Inheritance
34 * By John Resig http://ejohn.org/
37 // Inspired by base2 and Prototype
39 var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
40 // The base Class implementation (does nothing)
41 this.Class = function(){};
43 // Create a new Class that inherits from this class
44 this.Class.extend = function(prop) {
45 var _super = this.prototype;
47 // Instantiate a base class (but only create the instance,
48 // don't run the init constructor)
50 var prototype = new this();
53 // Copy the properties over onto the new prototype
54 for (var name in prop) {
55 // Check if we're overwriting an existing function
56 prototype[name] = typeof prop[name] == "function" &&
57 typeof _super[name] == "function" && fnTest.test(prop[name]) ?
60 var tmp = this._super;
62 // Add a new ._super() method that is the same method
63 // but on the super-class
64 this._super = _super[name];
66 // The method only need to be bound temporarily, so we
67 // remove it when we're done executing
68 var ret = fn.apply(this, arguments);
73 })(name, prop[name]) :
77 // The dummy class constructor
79 // All construction is actually done in the init method
80 if ( !initializing && this.init )
81 this.init.apply(this, arguments);
84 // Populate our constructed prototype object
85 Class.prototype = prototype;
87 // Enforce the constructor to be what we expect
88 Class.prototype.constructor = Class;
90 // And make this class extendable
98 // end of John Resig's code
100 lib.DestroyableMixin = {
102 this.__destroyableDestroyed = false;
104 isDestroyed : function() {
105 return this.__destroyableDestroyed;
107 destroy : function() {
108 this.__destroyableDestroyed = true;
112 lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, {
113 __parentedMixin : true,
115 lib.DestroyableMixin.init.call(this);
116 this.__parentedChildren = [];
117 this.__parentedParent = null;
119 setParent : function(parent) {
120 if (this.getParent()) {
121 if (this.getParent().__parentedMixin) {
122 this.getParent().__parentedChildren = _.without(this
123 .getParent().getChildren(), this);
126 this.__parentedParent = parent;
127 if (parent && parent.__parentedMixin) {
128 parent.__parentedChildren.push(this);
131 getParent : function() {
132 return this.__parentedParent;
134 getChildren : function() {
135 return _.clone(this.__parentedChildren);
137 destroy : function() {
138 _.each(this.getChildren(), function(el) {
141 this.setParent(undefined);
142 lib.DestroyableMixin.destroy.call(this);
147 * Yes, we steal Backbone's events :)
149 * This class just handle the dispatching of events, it is not meant to be extended,
150 * nor used directly. All integration with parenting and automatic unregistration of
151 * events is done in EventDispatcherMixin.
153 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
154 // Backbone may be freely distributed under the MIT license.
155 // For all details and documentation:
156 // http://backbonejs.org
157 lib.internal.Events = lib.Class.extend({
159 on : function(events, callback, context) {
161 events = events.split(/\s+/);
162 var calls = this._callbacks || (this._callbacks = {});
163 while (ev = events.shift()) {
164 var list = calls[ev] || (calls[ev] = {});
165 var tail = list.tail || (list.tail = list.next = {});
166 tail.callback = callback;
167 tail.context = context;
168 list.tail = tail.next = {};
173 off : function(events, callback, context) {
176 delete this._callbacks;
177 } else if (calls = this._callbacks) {
178 events = events.split(/\s+/);
179 while (ev = events.shift()) {
182 if (!callback || !node)
184 while ((node = node.next) && node.next) {
185 if (node.callback === callback
186 && (!context || node.context === context))
188 this.on(ev, node.callback, node.context);
195 trigger : function(events) {
196 var event, node, calls, tail, args, all, rest;
197 if (!(calls = this._callbacks))
200 (events = events.split(/\s+/)).push(null);
201 // Save references to the current heads & tails.
202 while (event = events.shift()) {
209 if (!(node = calls[event]))
216 rest = Array.prototype.slice.call(arguments, 1);
217 while (node = events.pop()) {
219 args = node.event ? [ node.event ].concat(rest) : rest;
220 while ((node = node.next) !== tail) {
221 node.callback.apply(node.context || this, args);
227 // end of Backbone's events class
229 lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, {
230 __eventDispatcherMixin: true,
232 lib.ParentedMixin.init.call(this);
233 this.__edispatcherEvents = new lib.internal.Events();
234 this.__edispatcherRegisteredEvents = [];
236 on: function(events, dest, func) {
238 events = events.split(/\s+/);
239 _.each(events, function(eventName) {
240 self.__edispatcherEvents.on(eventName, func, dest);
241 if (dest && dest.__eventDispatcherMixin) {
242 dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
247 off: function(events, dest, func) {
249 events = events.split(/\s+/);
250 _.each(events, function(eventName) {
251 self.__edispatcherEvents.off(eventName, func, dest);
252 if (dest && dest.__eventDispatcherMixin) {
253 dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
254 return !(el.name === eventName && el.func === func && el.source === self);
260 trigger: function(events) {
261 this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
264 destroy: function() {
266 _.each(this.__edispatcherRegisteredEvents, function(event) {
267 event.source.__edispatcherEvents.off(event.name, event.func, self);
269 this.__edispatcherRegisteredEvents = [];
270 this.__edispatcherEvents.off();
271 lib.ParentedMixin.destroy.call(this);
275 lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, {
277 lib.EventDispatcherMixin.init.call(this);
278 this.__getterSetterInternalMap = {};
283 _.each(map, function(val, key) {
284 var tmp = self.__getterSetterInternalMap[key];
288 self.__getterSetterInternalMap[key] = val;
289 self.trigger("change:" + key, self, {
295 self.trigger("change", self);
298 return this.__getterSetterInternalMap[key];
302 lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, {
304 * Tag name when creating a default $element.
309 * Constructs the widget and sets its parent if a parent is given.
311 * @constructs openerp.web.Widget
312 * @extends openerp.web.CallbackEnabled
314 * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
315 * When that widget is destroyed by calling destroy(), the current instance will be
316 * destroyed too. Can be null.
317 * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
318 * to bind the current Widget to an already existing part of the DOM, which is not compatible
319 * with the DOM insertion methods provided by the current implementation of Widget. So
320 * for new components this argument should not be provided any more.
322 init: function(parent) {
323 lib.GetterSetterMixin.init.call(this);
324 this.$element = $(document.createElement(this.tagName));
326 this.setParent(parent);
329 * Destroys the current widget, also destroys all its children before destroying itself.
331 destroy: function() {
332 _.each(this.getChildren(), function(el) {
335 if(this.$element != null) {
336 this.$element.remove();
338 lib.GetterSetterMixin.destroy.call(this);
341 * Renders the current widget and appends it to the given jQuery object or Widget.
343 * @param target A jQuery object or a Widget instance.
345 appendTo: function(target) {
347 return this.__widgetRenderAndInsert(function(t) {
348 self.$element.appendTo(t);
352 * Renders the current widget and prepends it to the given jQuery object or Widget.
354 * @param target A jQuery object or a Widget instance.
356 prependTo: function(target) {
358 return this.__widgetRenderAndInsert(function(t) {
359 self.$element.prependTo(t);
363 * Renders the current widget and inserts it after to the given jQuery object or Widget.
365 * @param target A jQuery object or a Widget instance.
367 insertAfter: function(target) {
369 return this.__widgetRenderAndInsert(function(t) {
370 self.$element.insertAfter(t);
374 * Renders the current widget and inserts it before to the given jQuery object or Widget.
376 * @param target A jQuery object or a Widget instance.
378 insertBefore: function(target) {
380 return this.__widgetRenderAndInsert(function(t) {
381 self.$element.insertBefore(t);
385 * Renders the current widget and replaces the given jQuery object.
387 * @param target A jQuery object or a Widget instance.
389 replace: function(target) {
390 return this.__widgetRenderAndInsert(_.bind(function(t) {
391 this.$element.replaceAll(t);
394 __widgetRenderAndInsert: function(insertion, target) {
395 this.renderElement();
400 * This is the method to implement to render the Widget.
402 renderElement: function() {},
404 * Method called after rendering. Mostly used to bind actions, perform asynchronous
407 * By convention, the method should return a promise to inform the caller when
408 * this widget has been initialized.
410 * @returns {jQuery.Deferred}