diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 85a7c9f25e3..39ddd0e1ecd 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1102,11 +1102,11 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.$table = $(QWeb.render("Interface", {})); self.$element.append(self.$table); self.header = new openerp.web.Header(self); - self.header.on_logout.add(self.on_logout); - self.header.on_action.add(self.on_menu_action); + self.header.on_logout.add(this.proxy('on_logout')); + self.header.on_action.add(this.proxy('on_menu_action')); self.header.appendTo($("#oe_header")); self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu"); - self.menu.on_action.add(self.on_menu_action); + self.menu.on_action.add(this.proxy('on_menu_action')); self.menu.start(); }, show_common: function() { diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index a194c34f287..d5c096d5a2a 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -348,6 +348,31 @@ openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.Ca } } } + }, + /** + * Proxies a method of the object, in order to keep the right ``this`` on + * method invocations. + * + * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and + * even more so to ``jQuery.proxy`` with a fundamental difference: its + * resolution of the method being called is lazy, meaning it will use the + * method as it is when the proxy is called, not when the proxy is created. + * + * Other methods will fix the bound method to what it is when creating the + * binding/proxy, which is fine in most javascript code but problematic in + * OpenERP Web where developers may want to replace existing callbacks with + * theirs. + * + * The semantics of this precisely replace closing over the method call. + * + * @param {String} method_name name of the method to invoke + * @returns {Function} proxied method + */ + proxy: function (method_name) { + var self = this; + return function () { + return self[method_name].apply(self, arguments); + } } }); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 63f9dfd6909..e654b773605 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -217,11 +217,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# }); this.$element.find('.oe-list-add') - .click(this.do_add_record) + .click(this.proxy('do_add_record')) .attr('disabled', grouped && this.options.editable); this.$element.find('.oe-list-delete') .attr('disabled', true) - .click(this.do_delete_selected); + .click(this.proxy('do_delete_selected')); this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) { e.stopPropagation(); @@ -512,7 +512,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.no_leaf = !!context['group_by_no_leaf']; this.reload_view(!!group_by, context).then( - $.proxy(this, 'reload_content')); + this.proxy('reload_content')); }, /** * Handles the signal to delete lines from the records list @@ -795,7 +795,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis $row.remove(); self.refresh_zebra(index); }, - 'reset': $.proxy(this, 'on_records_reset'), + 'reset': function () { return self.on_records_reset(); }, 'change': function (event, record) { var $row = self.$current.find('[data-id=' + record.get('id') + ']'); $row.replaceWith(self.render_record(record)); @@ -917,13 +917,15 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis }); }, render: function () { + var self = this; if (this.$current) { this.$current.remove(); } this.$current = this.$_element.clone(true); this.$current.empty().append( QWeb.render('ListView.rows', _.extend({ - render_cell: $.proxy(this, 'render_cell')}, this))); + render_cell: function () { return self.render_cell(); } + }, this))); this.pad_table_to(5); }, pad_table_to: function (count) { @@ -1038,7 +1040,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis record: record, row_parity: (index % 2 === 0) ? 'even' : 'odd', view: this.view, - render_cell: $.proxy(this, 'render_cell') + render_cell: function () { return this.render_cell(); } }); }, /** @@ -1092,7 +1094,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L this.page = 0; - this.records.bind('reset', $.proxy(this, 'on_records_reset')); + var self = this; + this.records.bind('reset', function () { + return self.on_records_reset(); }); }, make_fragment: function () { return document.createDocumentFragment(); diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index daf708104e8..dcbd22fe2bb 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -211,7 +211,7 @@ openerp.web.list_editable = function (openerp) { .delegate('button', 'keyup', function (e) { e.stopImmediatePropagation(); }) - .keyup($.proxy(self, 'on_row_keyup')); + .keyup(function () { return self.on_row_keyup(); }); if (row) { $new_row.replaceAll(row); } else if (self.options.editable) { @@ -359,7 +359,8 @@ openerp.web.list_editable = function (openerp) { this.render_row_as_form(); }, render_record: function (record) { - var index = this.records.indexOf(record); + var index = this.records.indexOf(record), + self = this; // FIXME: context dict should probably be extracted cleanly return QWeb.render('ListView.row', { columns: this.columns, @@ -367,7 +368,7 @@ openerp.web.list_editable = function (openerp) { record: record, row_parity: (index % 2 === 0) ? 'even' : 'odd', view: this.view, - render_cell: $.proxy(this, 'render_cell'), + render_cell: function () { return self.render_cell(); }, edited: !!this.edition_form }); }