diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 8925c3a0964..e4b8885b1a1 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -25,7 +25,6 @@ display: none !important; } } - .openerp.openerp_webclient_container { height: 100%; position: relative; @@ -2562,6 +2561,12 @@ .openerp .oe_list.oe_list_editable td.oe_list_record_delete { position: absolute; } +.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) { + color: transparent; +} +.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * { + visibility: hidden; +} .openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button { top: 5px; } @@ -2580,10 +2585,10 @@ } .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input { text-align: right; - width: 100%; + width: 100% !important; } .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime > span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date > span { - width: 100%; + width: 100% !important; } .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master { width: 100% !important; @@ -2592,6 +2597,9 @@ min-width: 0 !important; max-width: none !important; } +.openerp .oe_list_group_name { + white-space: nowrap; +} .openerp .oe_form .oe_form_field_many2many > .oe_list .oe_list_pager_single_page { display: none; } @@ -2651,14 +2659,17 @@ background: #eeeeee; vertical-align: top; } -.openerp .oe_list_content > thead th { - position: relative; -} .openerp .oe_list_content td, .openerp .oe_list_content th { padding: 3px 6px; line-height: 18px; } -.openerp .oe_list_content th:after { +.openerp .oe_list_content th.oe_sortable, .openerp .oe_list_content th.oe_sortable div { + cursor: pointer; +} +.openerp .oe_list_content th.oe_sortable div { + position: relative; +} +.openerp .oe_list_content th.oe_sortable div:after { position: absolute; right: 6px; content: ""; @@ -2668,12 +2679,12 @@ border-color: black transparent; visibility: hidden; } -.openerp .oe_list_content th.sortup:after { +.openerp .oe_list_content th.sortup div:after { visibility: visible; filter: alpha(opacity=60); opacity: 0.6; } -.openerp .oe_list_content th.sortdown:after { +.openerp .oe_list_content th.sortdown div:after { border-bottom: none; border-left: 4px solid transparent; border-right: 4px solid transparent; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index a1adfe67e75..a128fcec24b 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -2026,6 +2026,10 @@ $sheet-max-width: 860px td.oe_list_record_delete position: absolute .oe_list.oe_list_editable.oe_editing + .oe_edition .oe_list_field_cell:not(.oe_readonly) + * + visibility: hidden + color: transparent .oe_m2o_drop_down_button top: 5px .oe_m2o_cm_button @@ -2040,15 +2044,17 @@ $sheet-max-width: 860px &.oe_form_field_float,&.oe_form_view_integer input text-align: right - width: 100% + width: 100% !important &.oe_form_field_datetime,&.oe_form_field_date > span - width: 100% + width: 100% !important input.oe_datepicker_master width: 100% !important .oe_form_field_float,.oe_form_view_integer,&.oe_form_field_datetime,&.oe_form_field_date min-width: 0 !important max-width: none !important + .oe_list_group_name + white-space: nowrap // }}} // FormView.many2many {{{ .oe_form .oe_form_field_many2many > .oe_list @@ -2098,12 +2104,14 @@ $sheet-max-width: 860px border-bottom: 2px solid #cacaca background: #eee vertical-align: top - th - position: relative td, th padding: 3px 6px line-height: 18px - th:after + th.oe_sortable, th.oe_sortable div + cursor: pointer + th.oe_sortable div + position: relative + th.oe_sortable div:after position: absolute right: 6px content: "" @@ -2112,10 +2120,10 @@ $sheet-max-width: 860px border-style: solid border-color: #000 transparent visibility: hidden - th.sortup:after + th.sortup div:after visibility: visible @include opacity(0.6) - th.sortdown:after + th.sortdown div:after border-bottom: none border-left: 4px solid transparent border-right: 4px solid transparent diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index a298ac67159..3cb4437d1c3 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -701,9 +701,12 @@ instance.web.Menu = instance.web.Widget.extend({ return this.do_reload(); }, do_reload: function() { - return this.rpc("/web/menu/load", {}).then(this.on_loaded); + var self = this; + return this.rpc("/web/menu/load", {}).then(function(r) { + self.menu_loaded(r); + }); }, - on_loaded: function(data) { + menu_loaded: function(data) { var self = this; this.data = data; this.renderElement(); @@ -720,6 +723,7 @@ instance.web.Menu = instance.web.Widget.extend({ if (self.current_menu) { self.open_menu(self.current_menu); } + this.trigger('menu_loaded', data); this.has_been_loaded.resolve(); }, limit_entries: function() { diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 8c38724b1d5..6ecbfdd01ac 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -291,6 +291,17 @@ var Events = instance.web.Class.extend({ return this; }, + callbackList: function() { + var lst = []; + _.each(this._callbacks || {}, function(el, eventName) { + var node = el; + while ((node = node.next) && node.next) { + lst.push([eventName, node.callback, node.context]); + } + }); + return lst; + }, + trigger : function(events) { var event, node, calls, tail, args, all, rest; if (!(calls = this._callbacks)) @@ -369,9 +380,9 @@ instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, { event.source.__edispatcherEvents.off(event.name, event.func, self); }); this.__edispatcherRegisteredEvents = []; - if(!this.__edispatcherEvents) { - debugger; - } + _.each(this.__edispatcherEvents.callbackList(), function(cal) { + this.off(cal[0], cal[2], cal[1]); + }, this); this.__edispatcherEvents.off(); instance.web.ParentedMixin.destroy.call(this); } @@ -382,8 +393,17 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, { instance.web.EventDispatcherMixin.init.call(this); this.__getterSetterInternalMap = {}; }, - set: function(map, options) { - options = options || {}; + set: function(arg1, arg2, arg3) { + var map; + var options; + if (typeof arg1 === "string") { + map = {}; + map[arg1] = arg2; + options = arg3 || {}; + } else { + map = arg1; + options = arg2 || {}; + } var self = this; var changed = false; _.each(map, function(val, key) { @@ -410,71 +430,16 @@ instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, { init: function() { instance.web.PropertiesMixin.init.call(this); var self = this; - var callback_maker = function(obj, name, method) { - var callback = function() { - var args = Array.prototype.slice.call(arguments); - self.trigger.apply(self, [name].concat(args)); - var r; - for(var i = 0; i < callback.callback_chain.length; i++) { - var c = callback.callback_chain[i]; - if(c.unique) { - callback.callback_chain.splice(i, 1); - i -= 1; - } - var result = c.callback.apply(c.self, c.args.concat(args)); - if (c.callback === method) { - // return the result of the original method - r = result; - } - // TODO special value to stop the chain - // instance.web.callback_stop - } - return r; - }; - callback.callback_chain = []; - callback.add = function(f) { - if(typeof(f) == 'function') { - f = { callback: f, args: Array.prototype.slice.call(arguments, 1) }; - } - f.self = f.self || null; - f.args = f.args || []; - f.unique = !!f.unique; - if(f.position == 'last') { - callback.callback_chain.push(f); - } else { - callback.callback_chain.unshift(f); - } - return callback; - }; - callback.add_first = function(f) { - return callback.add.apply(null,arguments); - }; - callback.add_last = function(f) { - return callback.add({ - callback: f, - args: Array.prototype.slice.call(arguments, 1), - position: "last" - }); - }; - callback.remove = function(f) { - callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) { - return el.callback === f; - })); - return callback; - }; - - return callback.add({ - callback: method, - self:obj, - args:Array.prototype.slice.call(arguments, 3) - }); - }; // Transform on_/do_* methods into callbacks + var callback_maker = function(fn) { + return function() { + return fn.apply(self, arguments); + } + }; for (var name in this) { if(typeof(this[name]) == "function") { - this[name].debug_name = name; if((/^on_|^do_/).test(name)) { - this[name] = callback_maker(this, name, this[name]); + this[name] = callback_maker(this[name]); } } } diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 77583899bf1..30a8b9862fc 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -120,6 +120,10 @@ instance.web.Query = instance.web.Class.extend({ if (_.isEmpty(grouping)) { return null; } var self = this; + + // FIXME: when pyeval is merged + var ctx = instance.session.test_eval_contexts( + [this._model.context(this._context)]); return this._model.call('read_group', { groupby: grouping, fields: _.uniq(grouping.concat(this._fields || [])), @@ -130,6 +134,8 @@ instance.web.Query = instance.web.Class.extend({ orderby: instance.web.serialize_sort(this._order_by) || false }).pipe(function (results) { return _(results).map(function (result) { + // FIX: querygroup initialization + _.defaults(result.__context, ctx); return new instance.web.QueryGroup( self._model.name, grouping[0], result); }); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index a5e1aaeb3ae..33d20980732 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -61,7 +61,7 @@ my.SearchQuery = B.Collection.extend({ this.on('change', function (facet) { if(!facet.values.isEmpty()) { return; } - this.remove(facet); + this.remove(facet, {silent: true}); }, this); }, add: function (values, options) { @@ -324,8 +324,9 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea context: this.dataset.get_context() }); $.when(load_view) - .pipe(this.on_loaded) - .fail(function () { + .pipe(function(r) { + self.search_view_loaded(r) + }).fail(function () { self.ready.reject.apply(null, arguments); }); } @@ -645,7 +646,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea (new instance.web.search.Advanced(this)); }, - on_loaded: function(data) { + search_view_loaded: function(data) { var self = this; this.fields_view = data.fields_view; if (data.fields_view.type !== 'search' || @@ -654,7 +655,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea "Got non-search view after asking for a search view: type %s, arch root %s", data.fields_view.type, data.fields_view.arch.tag)); } - this.make_widgets( data.fields_view['arch'].children, data.fields_view.fields); @@ -671,48 +671,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea 'facet_for_defaults', this.defaults)).then(function () { self.query.reset(_(arguments).compact(), {preventSearch: true}); }); - + return $.when(drawer_started, defaults_fetched) - .then(function () { self.ready.resolve(); }) - }, - /** - * Handle event when the user make a selection in the filters management select box. - */ - on_filters_management: function(e) { - var self = this; - var select = this.$el.find(".oe_search-view-filters-management"); - var val = select.val(); - switch(val) { - case 'advanced_filter': - this.extended_search.on_activate(); - break; - case '': - this.do_clear(); - } - if (val.slice(0, 4) == "get:") { - val = val.slice(4); - val = parseInt(val, 10); - var filter = this.managed_filters[val]; - this.do_clear(false).then(_.bind(function() { - select.val('get:' + val); - - var groupbys = []; - var group_by = filter.context.group_by; - if (group_by) { - groupbys = _.map( - group_by instanceof Array ? group_by : group_by.split(','), - function (el) { return { group_by: el }; }); - } - this.filter_data = { - domains: [filter.domain], - contexts: [filter.context], - groupbys: groupbys - }; - this.do_search(); - }, this)); - } else { - select.val(''); - } + .then(function () { + self.trigger("search_view_loaded", data); + self.ready.resolve(); + }); }, /** * Extract search data from the view's facets. @@ -763,7 +727,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea groupbys: groupbys, errors: errors }; - }, /** + }, + /** * Performs the search view collection of widget data. * * If the collection went well (all fields are valid), then triggers @@ -771,8 +736,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea * * If at least one field failed its validation, triggers * :js:func:`instance.web.SearchView.on_invalid` instead. - * - * @param e jQuery event object coming from the "Search" button */ do_search: function (_query, options) { if (options && options.preventSearch) { @@ -783,7 +746,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea this.on_invalid(search.errors); return; } - return this.on_search(search.domains, search.contexts, search.groupbys); + return this.trigger('search_data', search.domains, search.contexts, search.groupbys); }, /** * Triggered after the SearchView has collected all relevant domains and @@ -801,8 +764,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea * @param {Array} contexts an array of literal contexts or context refs * @param {Array} groupbys ordered contexts which may or may not have group_by keys */ - on_search: function (domains, contexts, groupbys) { - }, /** * Triggered after a validation error in the SearchView fields. * diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 31aa2fe62eb..d096e15b51d 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -127,6 +127,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM self.init_pager(); }); self.on("load_record", self, self.load_record); + this.on('view_loaded', self, self.load_form); instance.web.bus.on('clear_uncommitted_changes', this, function(e) { if (!this.can_be_discarded()) { e.preventDefault(); @@ -143,13 +144,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } this._super(); }, - on_loaded: function(data) { + load_form: function(data) { var self = this; if (!data) { throw new Error("No data provided."); } if (this.arch) { - throw "Form view does not support multiple calls to on_loaded"; + throw "Form view does not support multiple calls to load_form"; } this.fields_order = []; this.fields_view = data; @@ -213,7 +214,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM e.stopPropagation(); } }); - + this.trigger('form_view_loaded', data); return $.when(); }, widgetFocused: function() { @@ -2152,6 +2153,20 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w self.do_action(r); }); }, + + set_dimensions: function (height, width) { + // remove width css property + this.$el.css('width', ''); + // extract style (without width) + var old_style = this.$el.attr('style'); + // jQuery doesn't understand/use !important + var style = 'width:' + width + 'px !important;'; + if (old_style) { + style += old_style + } + this.$el.attr('style', style); + this.$el.css('minHeight', height); + } }); /** @@ -2530,6 +2545,13 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we focus: function($el) { this.$textarea.focus(); }, + set_dimensions: function (height, width) { + this._super(); + this.$textarea.css({ + width: width, + minHeight: height + }); + }, }); /** @@ -3150,7 +3172,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc return ! this.get("value"); }, focus: function () { - this.$input.focus(); + if (!this.get('effective_readonly')) { + this.$input.focus(); + } }, _quick_create: function() { this.no_ed = true; @@ -3802,7 +3826,7 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({ instance.web.form.One2ManyFormView = instance.web.FormView.extend({ form_template: 'One2Many.formview', - on_loaded: function(data) { + load_form: function(data) { this._super(data); var self = this; this.$buttons.find('button.oe_form_button_create').click(function() { @@ -4012,7 +4036,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ } this.list_view.m2m_field = this; var loaded = $.Deferred(); - this.list_view.on_loaded.add_last(function() { + this.list_view.on("list_view_loaded", self, function() { self.initial_is_loaded.resolve(); loaded.resolve(); }); @@ -4057,13 +4081,17 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in ); var self = this; pop.on("elements_selected", self, function(element_ids) { - _.each(element_ids, function(one_id) { - if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) { - self.dataset.set_ids([].concat(self.dataset.ids, [one_id])); + var reload = false; + _(element_ids).each(function (id) { + if(! _.detect(self.dataset.ids, function(x) {return x == id;})) { + self.dataset.set_ids(self.dataset.ids.concat([id])); self.m2m_field.dataset_changed(); - self.reload_content(); + reload = true; } }); + if (reload) { + self.reload_content(); + } }); }, do_activate_record: function(index, id) { @@ -4132,7 +4160,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( } this.kanban_view.m2m = this; var loaded = $.Deferred(); - this.kanban_view.on_loaded.add_last(function() { + this.kanban_view.on("kanban_view_loaded",self,function() { self.initial_is_loaded.resolve(); loaded.resolve(); }); @@ -4356,7 +4384,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ this.view_form.set_embedded_view(this.options.alternative_form_view); } this.view_form.appendTo(this.$el.find(".oe_popup_form")); - this.view_form.on_loaded.add_last(function() { + this.view_form.on("form_view_loaded", self, function() { var multi_select = self.row_id === null && ! self.options.disable_multiple_selection; self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", { multi_select: multi_select, @@ -4467,7 +4495,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend } this.searchview = new instance.web.SearchView(this, this.dataset, false, search_defaults); - this.searchview.on_search.add(function(domains, contexts, groupbys) { + this.searchview.on('search_data', self, function(domains, contexts, groupbys) { if (self.initial_ids) { self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]), contexts, groupbys); @@ -4476,7 +4504,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys); } }); - this.searchview.on_loaded.add_last(function () { + this.searchview.on("search_view_loaded", self, function() { self.view_list = new instance.web.form.SelectCreateListView(self, self.dataset, false, _.extend({'deletable': false, @@ -4493,7 +4521,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend }).pipe(function() { self.searchview.do_search(); }); - self.view_list.on_loaded.add_last(function() { + self.view_list.on("list_view_loaded", self, function() { self.$buttonpane.html(QWeb.render("SelectCreatePopup.search.buttons", {widget:self})); var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-close"); $cbutton.click(function() { @@ -4642,14 +4670,19 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance. var self = this; this._super(field_manager, node); this.binary_value = false; - this.fileupload_id = _.uniqueId('oe_fileupload'); - $(window).on(this.fileupload_id, function() { - var args = [].slice.call(arguments).slice(1); - self.on_file_uploaded.apply(self, args); - }); + this.useFileAPI = !!window.FileReader; + if (!this.useFileAPI) { + this.fileupload_id = _.uniqueId('oe_fileupload'); + $(window).on(this.fileupload_id, function() { + var args = [].slice.call(arguments).slice(1); + self.on_file_uploaded.apply(self, args); + }); + } }, stop: function() { - $(window).off(this.fileupload_id); + if (!this.useFileAPI) { + $(window).off(this.fileupload_id); + } this._super.apply(this, arguments); }, initialize_content: function() { @@ -4667,13 +4700,22 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance. return size.toFixed(2) + ' ' + units[i]; }, on_file_change: function(e) { - // TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper - // http://www.html5rocks.com/tutorials/file/dndfiles/ - // http://deepliquid.com/projects/Jcrop/demos.php?demo=handler - - if ($(e.target).val() !== '') { - this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id); - this.$el.find('form.oe_form_binary_form').submit(); + var self = this; + var file_node = e.target; + if ((this.useFileAPI && file_node.files.length) || (!this.useFileAPI && $(file_node).val() !== '')) { + if (this.useFileAPI) { + var file = file_node.files[0]; + var filereader = new FileReader(); + filereader.readAsDataURL(file); + filereader.onloadend = function(upload) { + var data = upload.target.result; + data = data.split(',')[1]; + self.on_file_uploaded(file.size, file.name, file.type, data); + }; + } else { + this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id); + this.$el.find('form.oe_form_binary_form').submit(); + } this.$el.find('.oe_form_binary_progress').show(); this.$el.find('.oe_form_binary').hide(); } diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index a6aa637f655..29bc81ea9ec 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -83,6 +83,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi }); this.no_leaf = false; + this.on('view_load', self, self.load_list); }, set_default_options: function (options) { this._super(options); @@ -218,7 +219,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi * @param {Object} data.fields_view.arch current list view descriptor * @param {Boolean} grouped Is the list view grouped */ - on_loaded: function(data, grouped) { + load_list: function(data, grouped) { var self = this; this.fields_view = data; this.name = "" + this.fields_view.arch.attrs.string; @@ -358,6 +359,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.sidebar.add_toolbar(this.fields_view.toolbar); this.sidebar.$el.hide(); } + this.trigger('list_view_loaded', data, grouped); }, /** * Configures the ListView pager based on the provided dataset's information @@ -468,7 +470,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi reload_view: function (grouped, context, initial) { var self = this; var callback = function (field_view_get) { - self.on_loaded(field_view_get, grouped); + self.load_list(field_view_get, grouped); }; if (this.embedded_view) { return $.Deferred().then(callback).resolve(this.embedded_view); @@ -1230,6 +1232,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we .replaceWith(self.render()); }); this.$row.children().last() + .addClass('oe_list_group_pagination') .append($prev) .append('') .append($next); @@ -1414,10 +1417,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we })) .end() .find('button[data-pager-action=previous]') - .attr('disabled', page === 0) + .css('visibility', + page === 0 ? 'hidden' : '') .end() .find('button[data-pager-action=next]') - .attr('disabled', page === pages - 1); + .css('visibility', + page === pages - 1 ? 'hidden' : ''); } } diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index cd6b018ea0d..9bc0e96032f 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -100,7 +100,7 @@ openerp.web.list_editable = function (instance) { this._super(); } }, - on_loaded: function (data, grouped) { + load_list: function (data, grouped) { var self = this; // tree/@editable takes priority on everything else if present. var result = this._super(data, grouped); @@ -190,10 +190,10 @@ openerp.web.list_editable = function (instance) { this.records.add(record, { at: this.prepends_on_create() ? 0 : null}); } - var $recordRow = this.groups.get_row_for(record); - var cells = this.get_cells_for($recordRow); return this.ensure_saved().pipe(function () { + var $recordRow = self.groups.get_row_for(record); + var cells = self.get_cells_for($recordRow); self.fields_for_resize.splice(0, self.fields_for_resize.length); return self.with_event('edit', { record: record.attributes, @@ -251,12 +251,10 @@ openerp.web.list_editable = function (instance) { var $cell = $(cell); var position = $cell.position(); - // jquery does not understand !important - field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important'); + field.set_dimensions($cell.outerHeight(), $cell.outerWidth()); field.$el.css({ top: position.top, left: position.left, - minHeight: $cell.outerHeight() }); }, /** @@ -398,7 +396,6 @@ openerp.web.list_editable = function (instance) { setup_events: function () { var self = this; _.each(this.editor.form.fields, function(field, field_name) { - var field; var setting = false; var set_invisible = function() { if (!setting && field.get("effective_readonly")) { @@ -774,7 +771,7 @@ openerp.web.list_editable = function (instance) { return this._super.apply(this, arguments); } var record_id = $(event.currentTarget).data('id'); - this.view.start_edition( + return this.view.start_edition( record_id ? this.records.get(record_id) : null, { focus_field: $(event.target).data('field') }); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 45993db27bb..705bc1b214e 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -648,7 +648,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ } this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false); - this.searchview.on_search.add(this.do_searchview_search); + this.searchview.on('search_data', self, this.do_searchview_search); return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search")); }, do_searchview_search: function(domains, contexts, groupbys) { @@ -1135,11 +1135,13 @@ instance.web.View = instance.web.Widget.extend({ return this.load_view(); }, load_view: function() { + var self = this; if (this.embedded_view) { var def = $.Deferred(); - var self = this; $.async_when().then(function() {def.resolve(self.embedded_view);}); - return def.pipe(this.on_loaded); + return def.pipe(function(r) { + self.trigger('view_loaded', r); + }); } else { var context = new instance.web.CompoundContext(this.dataset.get_context()); if (! this.view_type) @@ -1150,15 +1152,11 @@ instance.web.View = instance.web.Widget.extend({ "view_type": this.view_type, toolbar: !!this.options.$sidebar, context: context - }).pipe(this.on_loaded); + }).pipe(function(r) { + self.trigger('view_loaded', r); + }); } }, - /** - * Called after a successful call to fields_view_get. - * Must return a promise. - */ - on_loaded: function(fields_view_get) { - }, set_default_options: function(options) { this.options = options || {}; _.defaults(this.options, { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index b031ee172e2..06603ad17a5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -631,9 +631,9 @@ + t-attf-class="oe_list_header_#{column.widget or column.type} #{((options.sortable and column.tag !== 'button') ? 'oe_sortable' : null)}">
- +
diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js index 6dc37f31d08..1db6c587801 100644 --- a/addons/web/static/test/search.js +++ b/addons/web/static/test/search.js @@ -652,7 +652,7 @@ $(document).ready(function () { } }); var ds, cs, gs; - view.on_search.add(function (d, c, g) { + view.on('search_data', this, function (d, c, g) { ds = d, cs = c, gs = g; }); view.appendTo($fix) @@ -690,7 +690,7 @@ $(document).ready(function () { } }, {dummy: 42}); var ds, cs, gs; - view.on_search.add(function (d, c, g) { + view.on('search_data', this, function (d, c, g) { ds = d, cs = c, gs = g; }); view.appendTo($fix) @@ -718,7 +718,7 @@ $(document).ready(function () { } }, {dummy: 42}); var ds; - view.on_search.add(function (d) { ds = d; }); + view.on('search_data', this, function (d) { ds = d; }); view.appendTo($fix) .always(start) .fail(function (error) { ok(false, error.message); }) @@ -1044,8 +1044,12 @@ $(document).ready(function () { "second value should be clicked filter"); }); }); - asyncTest('click removing from query', 2, function () { + asyncTest('click removing from query', 4, function () { + var calls = 0; var view = makeSearchView({}, {foo2: true}); + view.on('search_data', null, function () { + ++calls; + }); var $fix = $('#qunit-fixture'); view.appendTo($fix) .always(start) @@ -1054,8 +1058,10 @@ $(document).ready(function () { var $fs = $fix.find('.oe_searchview_filters ul'); // sanity check equal(view.query.length, 1, "query should have default facet"); + strictEqual(calls, 0); $fs.children(':eq(1)').trigger('click'); equal(view.query.length, 0, "click should have removed facet"); + strictEqual(calls, 1, "one search should have been triggered"); }); }); diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 03d22e0dcbd..9bc76e814e6 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -12,6 +12,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ display_name: _lt('Calendar'), // Dhtmlx scheduler ? init: function(parent, dataset, view_id, options) { + var self = this; this._super(parent); this.ready = $.Deferred(); this.set_default_options(options); @@ -38,6 +39,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ this.range_stop = null; this.update_range_dates(Date.today()); this.selected_filters = []; + this.on('view_loaded', self, self.load_calendar); }, start: function() { this._super(); @@ -47,7 +49,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ scheduler.clearAll(); this._super(); }, - on_loaded: function(data) { + load_calendar: function(data) { this.fields_view = data; this.$el.addClass(this.fields_view.arch.attrs['class']); this.calendar_fields = {}; @@ -106,7 +108,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ this.sidebar = new instance.web_calendar.Sidebar(this); this.has_been_loaded.pipe(this.sidebar.appendTo(this.$el.find('.oe_calendar_sidebar_container'))); } - + this.trigger('calendar_view_loaded', data); return this.has_been_loaded.resolve(); }, init_scheduler: function() { diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 4bae1aed3db..e44dabcc0cb 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -11,6 +11,7 @@ instance.web.DiagramView = instance.web.View.extend({ display_name: _lt('Diagram'), searchable: false, init: function(parent, dataset, view_id, options) { + var self = this; this._super(parent); this.set_default_options(options); this.view_manager = parent; @@ -20,17 +21,20 @@ instance.web.DiagramView = instance.web.View.extend({ this.domain = this.dataset._domain || []; this.context = {}; this.ids = this.dataset.ids; + this.on('view_loaded', self, self.load_diagram); }, start: function() { - return this.rpc("/web_diagram/diagram/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); + var self = this; + return this.rpc("/web_diagram/diagram/load", {"model": this.model, "view_id": this.view_id}).then(function(r) { + self.load_diagram(r); + }); }, toTitleCase: function(str) { return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); }, - on_loaded: function(result) { - + load_diagram: function(result) { var self = this; if(this.ids && this.ids.length) { this.id = this.ids[self.dataset.index || 0]; @@ -47,7 +51,7 @@ instance.web.DiagramView = instance.web.View.extend({ return label.tag == "label"; }); - this.$el.html(QWeb.render("DiagramView", {'widget': this})); + this.$el.html(QWeb.render("DiagramView", {'widget': self})); this.$el.addClass(this.fields_view.arch.attrs['class']); _.each(self.labels,function(label){ @@ -68,7 +72,7 @@ instance.web.DiagramView = instance.web.View.extend({ if(this.id) { self.get_diagram_info(); } - + this.trigger('diagram_view_loaded', result); }, get_diagram_info: function() { @@ -239,7 +243,8 @@ instance.web.DiagramView = instance.web.View.extend({ title: _t("Open: ") + title } ); - pop.on('on_write_complete', self, function() { + + pop.on('write_completed', self, function() { self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded); }); @@ -303,7 +308,7 @@ instance.web.DiagramView = instance.web.View.extend({ title: _t("Open: ") + title } ); - pop.on('on_write_complete', self, function() { + pop.on('write_completed', self, function() { self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded); }); }, diff --git a/addons/web_gantt/static/src/js/gantt.js b/addons/web_gantt/static/src/js/gantt.js index 01de4f69b61..040f53568ae 100644 --- a/addons/web_gantt/static/src/js/gantt.js +++ b/addons/web_gantt/static/src/js/gantt.js @@ -12,11 +12,13 @@ instance.web_gantt.GanttView = instance.web.View.extend({ template: "GanttView", view_type: "gantt", init: function() { + var self = this; this._super.apply(this, arguments); this.has_been_loaded = $.Deferred(); this.chart_id = _.uniqueId(); + this.on('view_loaded', self, self.load_gantt); }, - on_loaded: function(fields_view_get, fields_get) { + load_gantt: function(fields_view_get, fields_get) { var self = this; this.fields_view = fields_view_get; this.$el.addClass(this.fields_view.arch.attrs['class']); diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index b82b1521e87..788ec3bcb3f 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -23,6 +23,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ view_type: "graph", init: function(parent, dataset, view_id, options) { + var self = this; this._super(parent); this.set_default_options(options); this.dataset = dataset; @@ -41,6 +42,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ this.group_by = []; this.graph = null; + this.on('view_loaded', self, self.load_graph); }, destroy: function () { if (this.graph) { @@ -49,7 +51,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ this._super(); }, - on_loaded: function(fields_view_get) { + load_graph: function(fields_view_get) { // TODO: move to load_view and document var self = this; this.fields_view = fields_view_get; @@ -98,7 +100,7 @@ instance.web_graph.GraphView = instance.web.View.extend({ self.forcehtml = false; }); }); - return this._super(); + this.trigger('graph_view_loaded', fields_view_get); }, get_format: function (options) { diff --git a/addons/web_hello/static/openerp/base_hello.js b/addons/web_hello/static/openerp/base_hello.js index a74b8ef8f2f..0a22079aa53 100644 --- a/addons/web_hello/static/openerp/base_hello.js +++ b/addons/web_hello/static/openerp/base_hello.js @@ -7,7 +7,7 @@ openerp.web_hello = function(instance) { instance.web.SearchView = instance.web.SearchView.extend({ init:function() { this._super.apply(this,arguments); - this.on_search.add(function(){console.log('hello');}); + this.on('search_data', this, function(){console.log('hello');}); } }); diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index a38b083fe6c..7a179990f6a 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -14,6 +14,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ number_of_color_schemes: 10, init: function (parent, dataset, view_id, options) { this._super(parent, dataset, view_id, options); + var self = this; _.defaults(this.options, { "quick_creatable": true, "creatable": true, @@ -41,6 +42,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.currently_dragging = {}; this.limit = options.limit || 40; this.add_group_mutex = new $.Mutex(); + this.on('view_loaded', self, self.load_kanban); }, start: function() { var self = this; @@ -55,7 +57,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this._super.apply(this, arguments); $('html').off('click.kanban'); }, - on_loaded: function(data) { + load_kanban: function(data) { this.fields_view = data; this.$el.addClass(this.fields_view.arch.attrs['class']); this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this})); @@ -71,7 +73,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ this.fields_keys = _.keys(this.fields_view.fields); this.add_qweb_template(); this.has_been_loaded.resolve(); - this._super.apply(this, arguments); + this.trigger('kanban_view_loaded', data); return $.when(); }, _is_quick_create_enabled: function() { @@ -246,6 +248,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ var remaining = groups.length - 1, groups_array = []; return $.when.apply(null, _.map(groups, function (group, index) { + self.do_clear_groups(); var dataset = new instance.web.DataSetSearch(self, self.dataset.model, new instance.web.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain()); return dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }) @@ -259,14 +262,14 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }); })); }); - self.do_clear_groups(); }, do_process_dataset: function() { var self = this; - this.$el.remove('oe_kanban_grouped').addClass('oe_kanban_ungrouped'); + this.$el.removeClass('oe_kanban_grouped').addClass('oe_kanban_ungrouped'); this.add_group_mutex.exec(function() { var def = $.Deferred(); self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) { + self.do_clear_groups(); var kgroup = new instance.web_kanban.KanbanGroup(self, records, null, self.dataset); self.do_add_groups([kgroup]).then(function() { if (_.isEmpty(records)) { @@ -279,7 +282,6 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }); return def; }); - self.do_clear_groups(); }, do_reload: function() { this.do_search(this.search_domain, this.search_context, this.search_group_by); diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index 2732954097d..0750a5fe0b8 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -74,7 +74,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ $.when(this.action_manager.do_action(action)).then(function() { var viewmanager = self.action_manager.inner_widget; var controller = viewmanager.views[viewmanager.active_view].controller; - controller.on_loaded.add_last(function(){ + controller.on('view_loaded', function(){ $(controller.groups).bind({ 'selected': function(e, ids, records) { self.main_view_id = ids[0];