diff options
| author | Kunal Chavda (OpenERP) <kch@tinyerp.com> | 2011-09-15 12:32:22 +0530 |
|---|---|---|
| committer | Kunal Chavda (OpenERP) <kch@tinyerp.com> | 2011-09-15 12:32:22 +0530 |
| commit | 50567fcb632abf3e0cd596da77e037e2845d37e1 (patch) | |
| tree | b1bb35711e4377540f0e1b30a042076949608b8e /addons/web/static/src/js | |
| parent | 4eb0672324b905d4e6f43eb194b7c85e8043d856 (diff) | |
| parent | 79440f2b7bbd9ff8aa5610bb29761779fdb7fd87 (diff) | |
[MERGE]Merge with trunk upto revision no 1043.
bzr revid: kch@tinyerp.com-20110915070222-1sw9bpp36s42nvt2
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/chrome.js | 62 | ||||
| -rw-r--r-- | addons/web/static/src/js/core.js | 88 | ||||
| -rw-r--r-- | addons/web/static/src/js/data.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/search.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_form.js (renamed from addons/web/static/src/js/form.js) | 142 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list.js (renamed from addons/web/static/src/js/list.js) | 41 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list_editable.js (renamed from addons/web/static/src/js/list-editable.js) | 0 | ||||
| -rw-r--r-- | addons/web/static/src/js/views.js | 270 |
8 files changed, 377 insertions, 231 deletions
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 582ef512f83..c797169fb68 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -581,24 +581,29 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# * this._super(parent); this.qs = "?" + jQuery.param.querystring(); this.$content = $(); + console.debug("initializing header with id", this.element_id); + this.update_promise = $.Deferred().resolve(); }, start: function() { this._super(); }, do_update: function () { var self = this; - this.$content.remove(); - if (! this.session.uid) - return; - var func = new openerp.web.Model(self.session, "res.users").get_func("read"); - func(self.session.uid, ["name", "company_id"]).then(function(res) { - self.$content = $(QWeb.render("Header-content", {widget: self, user: res})); - self.$content.appendTo(self.$element); - self.$element.find(".logout").click(self.on_logout); - self.$element.find("a.preferences").click(self.on_preferences); - self.$element.find(".about").click(self.on_about); - self.shortcut_load(); - }); + var fct = function() { + self.$content.remove(); + if (!self.session.uid) + return; + var func = new openerp.web.Model(self.session, "res.users").get_func("read"); + return func(self.session.uid, ["name", "company_id"]).pipe(function(res) { + self.$content = $(QWeb.render("Header-content", {widget: self, user: res})); + self.$content.appendTo(self.$element); + self.$element.find(".logout").click(self.on_logout); + self.$element.find("a.preferences").click(self.on_preferences); + self.$element.find(".about").click(self.on_about); + return self.shortcut_load(); + }); + }; + this.update_promise = this.update_promise.pipe(fct, fct); }, on_about: function() { var self = this; @@ -834,7 +839,11 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ $menu.addClass('active'); $menu.parent('h4').addClass('active'); - return !$menu.is(".leaf"); + if (this.$secondary_menu.has($menu).length) { + return !$menu.is(".leaf"); + } else { + return false; + } }, on_menu_action_loaded: function(data) { var self = this; @@ -873,7 +882,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie // Do you autorize this ? will be replaced by notify() in controller openerp.web.Widget.prototype.notification = new openerp.web.Notification(this, "oe_notification"); - this.header = new openerp.web.Header(this); this.login = new openerp.web.Login(this, "oe_login"); this.header.on_logout.add(this.login.on_logout); @@ -886,6 +894,11 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie this.menu = new openerp.web.Menu(this, "oe_menu", "oe_secondary_menu"); this.menu.on_action.add(this.on_menu_action); + + this.url_internal_hashchange = false; + this.url_external_hashchange = false; + jQuery(window).bind('hashchange', this.on_url_hashchange); + }, start: function() { this.header.appendTo($("#oe_header")); @@ -899,6 +912,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie this.action_manager.stop(); this.action_manager = new openerp.web.ActionManager(this); this.action_manager.appendTo($("#oe_app")); + this.action_manager.do_url_set_hash.add_last(this.do_url_set_hash); // if using saved actions, load the action and give it to action manager var parameters = jQuery.deparam(jQuery.param.querystring()); @@ -925,7 +939,6 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie load_url_state: function () { var self = this; // TODO: add actual loading if there is url state to unpack, test on window.location.hash - // not logged in if (!this.session.uid) { return; } var ds = new openerp.web.DataSetSearch(this, 'res.users'); @@ -961,6 +974,25 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.action_manager.do_action(action); }); }, + do_url_set_hash: function(url) { + if(!this.url_external_hashchange) { + console.log("url set #hash to",url); + this.url_internal_hashchange = true; + jQuery.bbq.pushState(url); + } + }, + on_url_hashchange: function() { + if(this.url_internal_hashchange) { + this.url_internal_hashchange = false; + console.log("url jump to FLAG OFF"); + } else { + var url = jQuery.deparam.fragment(); + console.log("url jump to",url); + this.url_external_hashchange = true; + this.action_manager.on_url_hashchange(url); + this.url_external_hashchange = false; + } + }, on_menu_action: function(action) { this.action_manager.do_action(action); }, diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 77b48820e83..6462529d0c9 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -2,6 +2,10 @@ * OpenERP Web core *--------------------------------------------------------*/ +if (!console.debug) { + console.debug = console.log; +} + openerp.web.core = function(openerp) { openerp.web.qweb = new QWeb2.Engine(); openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1); @@ -349,12 +353,10 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web this.port = (port == undefined) ? location.port : port; this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp"; this.debug = (window.location.search.indexOf('?debug') !== -1); - this.db = ""; - this.login = ""; - this.password = ""; - this.user_context= {}; - this.uid = false; this.session_id = false; + this.uid = false; + this.user_context= {}; + this.db = false; this.module_list = []; this.module_loaded = {"web": true}; this.context = {}; @@ -474,58 +476,45 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web }, session_login: function(db, login, password, success_callback) { var self = this; - this.db = db; - this.login = login; - this.password = password; - var params = { db: this.db, login: this.login, password: this.password }; + var params = { db: db, login: login, password: password }; this.rpc("/web/session/login", params, function(result) { self.session_id = result.session_id; self.uid = result.uid; self.user_context = result.context; + self.db = result.db; self.session_save(); self.on_session_valid(); return true; }).then(success_callback); }, - session_logout: function() { - this.uid = false; - }, /** * Reloads uid and session_id from local storage, if they exist */ session_restore: function () { - this.uid = this.get_cookie('uid'); + var self = this; this.session_id = this.get_cookie('session_id'); - this.db = this.get_cookie('db'); - this.login = this.get_cookie('login'); - this.user_context = this.get_cookie("user_context"); - // we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login - // then call on_session_valid - if (this.uid) - this.on_session_valid(); - else - this.on_session_invalid(); + return this.rpc("/web/session/get_session_info", {}).then(function(result) { + self.uid = result.uid; + self.user_context = result.context; + self.db = result.db; + if (self.uid) + self.on_session_valid(); + else + self.on_session_invalid(); + }); }, /** * Saves the session id and uid locally */ session_save: function () { - this.set_cookie('uid', this.uid); this.set_cookie('session_id', this.session_id); - this.set_cookie('db', this.db); - this.set_cookie('login', this.login); - this.set_cookie('user_context', this.user_context); }, logout: function() { - delete this.uid; - delete this.session_id; - delete this.db; - delete this.login; - this.set_cookie('uid', ''); this.set_cookie('session_id', ''); - this.set_cookie('db', ''); - this.set_cookie('login', ''); - this.on_session_invalid(function() {}); + this.reload_client(); + }, + reload_client: function() { + window.location.reload(); }, /** * Fetches a cookie stored by an openerp session @@ -749,36 +738,6 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp */ rpc: function(url, data, success, error) { return this.session.rpc(url, data, success, error); - }, - log: function() { - var args = Array.prototype.slice.call(arguments); - var caller = arguments.callee.caller; - // TODO add support for line number using - // https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js - // args.unshift("" + caller.debug_name); - this.on_log.apply(this,args); - }, - on_log: function() { - if(this.session.debug) { - var notify = false; - var body = false; - if(window.console) { - console.log(arguments); - } else { - body = true; - } - var a = Array.prototype.slice.call(arguments, 0); - for(var i = 0; i < a.length; i++) { - var v = a[i]==null ? "null" : a[i].toString(); - if(i==0) { - notify = v.match(/^not/); - body = v.match(/^bod/); - } - if(body) { - $('<pre></pre>').text(v).appendTo($('body')); - } - } - } } }); @@ -929,6 +888,7 @@ openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widg * If that's not the case this method will simply return `false`. */ do_action: function(action, on_finished) { + console.log('Widget.do_action', action, on_finished); if (this.widget_parent) { return this.widget_parent.do_action(action, on_finished); } diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 1eb57241fa6..e5d323c8a96 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -356,7 +356,7 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * @returns {$.Deferred} */ write: function (id, data, options, callback, error_callback) { - var options = options || {}; + options = options || {}; return this.rpc('/web/dataset/save', { model: this.model, id: id, diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 21f397c0867..f6c53b4ad17 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -28,7 +28,6 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search this.ready = $.Deferred(); }, start: function() { - //this.log('Starting SearchView '+this.model+this.view_id) this.rpc("/web/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded); return this.ready.promise(); }, @@ -1049,7 +1048,7 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** throw e; } type = "char"; - this.log('Unknow field type ' + e.key); + console.log('Unknow field type ' + e.key); } this.value = new (openerp.web.search.custom_filters.get_object(type)) (this); diff --git a/addons/web/static/src/js/form.js b/addons/web/static/src/js/view_form.js index 9858433db32..d6da6e6c095 100644 --- a/addons/web/static/src/js/form.js +++ b/addons/web/static/src/js/view_form.js @@ -35,7 +35,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.datarecord = {}; this.ready = false; this.show_invalid = true; - this.dirty = false; + this.dirty_for_user = false; this.default_focus_field = null; this.default_focus_button = null; this.registry = openerp.web.form.widgets; @@ -45,7 +45,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# _.defaults(this.options, {"always_show_new_button": true}); }, start: function() { - //this.log('Starting FormView '+this.model+this.view_id) if (this.embedded_view) { var def = $.Deferred().then(this.on_loaded); var self = this; @@ -91,12 +90,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel); this.$form_header.find('button.oe_form_button_new').click(this.on_button_new); - if (this.session.debug) { - this.$form_header.find('button.oe_get_xml_view').click(function() { - $('<xmp>' + openerp.web.json_node_to_xml(self.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600}); - }); - } - if (this.options.sidebar && this.options.sidebar_id) { this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); this.sidebar.start(); @@ -142,7 +135,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.$form_header.find('.oe_form_on_update').show(); this.$form_header.find('button.oe_form_button_new').show(); } - this.dirty = false; + this.dirty_for_user = false; this.datarecord = record; for (var f in this.fields) { var field = this.fields[f]; @@ -152,7 +145,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } if (!record.id) { // New record: Second pass in order to trigger the onchanges - this.dirty = true; this.show_invalid = false; for (var f in record) { var field = this.fields[f]; @@ -181,21 +173,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }, on_pager_action: function(action) { - switch (action) { - case 'first': - this.dataset.index = 0; - break; - case 'previous': - this.dataset.previous(); - break; - case 'next': - this.dataset.next(); - break; - case 'last': - this.dataset.index = this.dataset.ids.length - 1; - break; + if (this.can_be_discarded()) { + switch (action) { + case 'first': + this.dataset.index = 0; + break; + case 'previous': + this.dataset.previous(); + break; + case 'next': + this.dataset.next(); + break; + case 'last': + this.dataset.index = this.dataset.ids.length - 1; + break; + } + this.reload(); } - this.reload(); }, do_update_pager: function(hide_index) { var $pager = this.$element.find('#' + this.element_id + '_header div.oe_form_pager'); @@ -262,7 +256,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# self.on_processed_onchange(response, processed); }); } else { - this.log("Wrong on_change format", on_change); + console.log("Wrong on_change format", on_change); } } }, @@ -277,7 +271,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# processed.push(field.name); if (field.get_value() != value) { field.set_value(value); - field.dirty = true; + field.dirty = this.dirty_for_user = true; if (_.indexOf(processed, field.name) < 0) { this.do_onchange(field, processed); } @@ -305,13 +299,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# var self = this; var def = $.Deferred(); $.when(this.has_been_loaded).then(function() { - self.dataset.default_get( - _.keys(self.fields_view.fields)).then(self.on_record_loaded).then(function() { + if (self.can_be_discarded()) { + self.dataset.default_get(_.keys(self.fields_view.fields)).then(self.on_record_loaded).then(function() { def.resolve(); - }); + }); + } }); return def.promise(); }, + can_be_discarded: function() { + return !this.dirty_for_user || confirm(_t("Warning, the record has been modified, your changes will be discarded.")); + }, /** * Triggers saving the form's record. Chooses between creating a new * record or saving an existing one depending on whether the record @@ -347,7 +345,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.on_invalid(); return false; } else if (form_dirty) { - this.log("About to save", values); + console.log("About to save", values); if (!this.datarecord.id) { return this.dataset.create(values, function(r) { self.on_created(r, success, prepend_on_create); @@ -595,7 +593,7 @@ openerp.web.form.compute_domain = function(expr, fields) { stack.push(!_(val).contains(field_value)); break; default: - this.log("Unsupported operator in modifiers :", op); + console.log("Unsupported operator in modifiers :", op); } } return _.all(stack, _.identity); @@ -816,7 +814,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ }, on_click: function(saved) { var self = this; - if (!this.node.attrs.special && this.view.dirty && saved !== true) { + if ((!this.node.attrs.special && this.view.dirty_for_user && saved !== true) || !this.view.recordcount.id) { this.view.do_save(function() { self.on_click(true); }); @@ -843,7 +841,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ on_confirmed: function() { var self = this; - this.view.execute_action( + this.view.do_execute_action( this.node.attrs, this.view.dataset, this.view.datarecord.id, function () { self.view.reload(); }); @@ -880,7 +878,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({ var self = this; this.$element.find("label").dblclick(function() { var widget = self['for'] || self; - self.log(widget.element_id , widget); + console.log(widget.element_id , widget); window.w = widget; }); } @@ -964,7 +962,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f } }, on_ui_change: function() { - this.dirty = this.view.dirty = true; + this.dirty = this.view.dirty_for_user = true; this.validate(); if (this.is_valid()) { this.set_value_from_ui(); @@ -1112,37 +1110,51 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ this.jqueryui_object = 'datetimepicker'; }, start: function() { + var self = this; this._super.apply(this, arguments); - this.$element.find('input').change(this.on_ui_change)[this.jqueryui_object]({ - dateFormat: 'yy-mm-dd', - timeFormat: 'hh:mm:ss', - showOn: 'button', - buttonImage: '/web/static/src/img/ui/field_calendar.png', - buttonImageOnly: true, - constrainInput: false + this.$element.find('input').change(this.on_ui_change); + this.picker({ + onSelect: this.on_picker_select, + changeMonth: true, + changeYear: true, + showWeek: true, + showButtonPanel: false + }); + this.$element.find('img.oe_datepicker_trigger').click(function() { + if (!self.readonly) { + self.picker('setDate', self.value || new Date()); + self.$element.find('.oe_datepicker').toggle(); + } }); + this.$element.find('.ui-datepicker-inline').removeClass('ui-widget-content ui-corner-all'); + this.$element.find('button.oe_datepicker_close').click(function() { + self.$element.find('.oe_datepicker').hide(); + }); + }, + picker: function() { + return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments); + }, + on_picker_select: function(text, instance) { + var date = this.picker('getDate'); + this.$element.find('input').val(date ? this.format_client(date) : '').change(); }, set_value: function(value) { - this._super.apply(this, arguments); - if (!value) { - this.$element.find('input').val(''); - } else { - this.$element.find('input').unbind('change'); - // jQuery UI date picker wrongly call on_change event herebelow - this.$element.find('input')[this.jqueryui_object]('setDate', this.parse(value)); - this.$element.find('input').change(this.on_ui_change); - } + value = this.parse(value); + this._super(value); + this.$element.find('input').val(value ? this.format_client(value) : ''); + }, + get_value: function() { + return this.format(this.value); }, set_value_from_ui: function() { - this.value = this.$element.find('input')[this.jqueryui_object]('getDate') || false; - if (this.value) { - this.value = this.format(this.value); - } + var value = this.$element.find('input').val() || false; + this.value = this.parse_client(value); this._super(); }, update_dom: function() { this._super.apply(this, arguments); - this.$element.find('input').datepicker(this.readonly ? 'disable' : 'enable'); + this.$element.find('input').attr('disabled', this.readonly); + this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', this.readonly); }, validate: function() { this.invalid = false; @@ -1150,15 +1162,26 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ if (value === "") { this.invalid = this.required; } else { - this.invalid = !this.$element.find('input')[this.jqueryui_object]('getDate'); + try { + this.parse_client(value); + this.invalid = false; + } catch(e) { + this.invalid = true; + } } }, focus: function() { this.$element.find('input').focus(); }, parse: openerp.web.auto_str_to_date, + parse_client: function(v) { + return openerp.web.parse_value(v, this.field); + }, format: function(val) { return openerp.web.auto_date_to_str(val, this.field.type); + }, + format_client: function(v) { + return openerp.web.format_value(v, this.field); } }); @@ -1166,6 +1189,10 @@ openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({ init: function(view, node) { this._super(view, node); this.jqueryui_object = 'datepicker'; + }, + on_picker_select: function(text, instance) { + this._super(text, instance); + this.$element.find('.oe_datepicker').hide(); } }); @@ -2524,6 +2551,7 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ var self = this; var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","), function(x) { return x.trim(); }); + shown = _.select(shown, function(x) { return x.length > 0; }); if (shown.length == 0) { this.to_show = this.field.selection; diff --git a/addons/web/static/src/js/list.js b/addons/web/static/src/js/view_list.js index b9df9e8e2df..883fae81bbf 100644 --- a/addons/web/static/src/js/list.js +++ b/addons/web/static/src/js/view_list.js @@ -41,8 +41,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @param {void|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button. * @param {Boolean} [options.sortable=true] is it possible to sort the table by clicking on column headers * @param {Boolean} [options.reorderable=true] is it possible to reorder list rows - * - * @borrows openerp.web.ActionExecutor#execute_action as #execute_action */ init: function(parent, element_id, dataset, view_id, options) { var self = this; @@ -61,6 +59,13 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# if (this.dataset instanceof openerp.web.DataSetStatic) { this.groups.datagroup = new openerp.web.StaticDataGroup(this.dataset); + } else { + this.groups.datagroup = new openerp.web.DataGroup( + this, this.model, + dataset.get_domain(), + dataset.get_context(), + {}); + this.groups.datagroup.sort = this.dataset._sort; } this.page = 0; @@ -152,7 +157,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# on_loaded: function(data, grouped) { var self = this; this.fields_view = data; - //this.log(this.fields_view); this.name = "" + this.fields_view.arch.attrs.string; this.setup_columns(this.fields_view.fields, grouped); @@ -484,7 +488,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# return field.name === name; }); if (!action) { return; } - this.execute_action(action, this.dataset, id, callback); + this.do_execute_action(action, this.dataset, id, callback); }, /** * Handles the activation of a record (clicking on it) @@ -771,6 +775,31 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis this.$current.empty().append( QWeb.render('ListView.rows', _.extend({ render_cell: openerp.web.format_cell}, this))); + this.pad_table_to(5); + }, + pad_table_to: function (count) { + if (this.records.length >= count || + _(this.columns).any(function(column) { return column.meta; })) { + return; + } + var cells = []; + if (this.options.selectable) { + cells.push('<td title="selection"></td>'); + } + _(this.columns).each(function(column) { + if (column.invisible !== '1') { + cells.push('<td title="' + column.string + '"> </td>'); + } + }); + if (this.options.deletable) { + cells.push('<td><button type="button" style="visibility: hidden"> </button></td>'); + } + cells.unshift('<tr>'); + cells.push('</tr>'); + + var row = cells.join(''); + this.$current.append(new Array(count - this.records.length + 1).join(row)); + this.refresh_zebra(this.records.length); }, /** * Gets the ids of all currently selected records, if any @@ -1133,12 +1162,14 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L } // ondrop, move relevant record & fix sequences list.$current.sortable({ + axis: 'y', + items: '> tr[data-id]', stop: function (event, ui) { var to_move = list.records.get(ui.item.data('id')), target_id = ui.item.prev().data('id'); list.records.remove(to_move); - var to = target_id ? list.records.indexOf(list.records.get(target_id)) : 0; + var to = target_id ? list.records.indexOf(list.records.get(target_id)) + 1 : 0; list.records.add(to_move, { at: to }); // resequencing time! diff --git a/addons/web/static/src/js/list-editable.js b/addons/web/static/src/js/view_list_editable.js index c38c43a4c9c..c38c43a4c9c 100644 --- a/addons/web/static/src/js/list-editable.js +++ b/addons/web/static/src/js/view_list_editable.js diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 258ea92ad03..8d07d4faca0 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -2,17 +2,21 @@ * OpenERP web library *---------------------------------------------------------*/ -openerp.web.views = function(openerp) { +openerp.web.views = function(db) { -var _t = openerp.web._t; -var QWeb = openerp.web.qweb; +var _t = db.web._t; /** * Registry for all the client actions key: tag value: widget */ -openerp.web.client_actions = new openerp.web.Registry(); +db.web.client_actions = new db.web.Registry(); -openerp.web.ActionManager = openerp.web.Widget.extend({ +/** + * Registry for all the main views + */ +db.web.views = new db.web.Registry(); + +db.web.ActionManager = db.web.Widget.extend({ identifier_prefix: "actionmanager", init: function(parent) { this._super(parent); @@ -20,7 +24,6 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ this.dialog = null; this.dialog_viewmanager = null; this.client_widget = null; - this.url = {} }, render: function() { return "<div id='"+this.element_id+"'></div>"; @@ -44,8 +47,11 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ } }, url_update: function(action) { + var url = {}; + if(action.id) + url.action_id = action.id; // this.url = { - // "model": action.model, + // "model": action.res_model, // "domain": action.domain, // }; // action.res_model @@ -56,14 +62,15 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ // action.res_id // mode // menu + this.do_url_set_hash(url); }, - url_stringify: function(action) { + do_url_set_hash: function(url) { }, - url_parse: function(action) { - }, - on_url_update: function(url) { - }, - do_url_action: function(url) { + on_url_hashchange: function(url) { + var self = this; + self.rpc("/web/action/load", { action_id: url.action_id }, function(result) { + self.do_action(result.result); + }); }, do_action: function(action, on_close) { var type = action.type.replace(/\./g,'_'); @@ -76,7 +83,7 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ pager : !popup }, action.flags || {}); if (!(type in this)) { - this.log("Action manager can't handle action of type " + action.type, action); + console.log("Action manager can't handle action of type " + action.type, action); return; } this[type](action, on_close); @@ -84,20 +91,20 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ ir_actions_act_window: function (action, on_close) { if (action.target === 'new') { if (this.dialog == null) { - this.dialog = new openerp.web.Dialog(this, { title: action.name, width: '80%' }); + this.dialog = new db.web.Dialog(this, { title: action.name, width: '80%' }); if(on_close) this.dialog.on_close.add(on_close); this.dialog.start(); } else { this.dialog_viewmanager.stop(); } - this.dialog_viewmanager = new openerp.web.ViewManagerAction(this, action); + this.dialog_viewmanager = new db.web.ViewManagerAction(this, action); this.dialog_viewmanager.appendTo(this.dialog.$element); this.dialog.open(); } else { this.dialog_stop(); this.content_stop(); - this.inner_viewmanager = new openerp.web.ViewManagerAction(this, action); + this.inner_viewmanager = new db.web.ViewManagerAction(this, action); this.inner_viewmanager.appendTo(this.$element); this.url_update(action); } @@ -122,7 +129,7 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ }, ir_actions_client: function (action) { this.content_stop(); - var ClientWidget = openerp.web.client_actions.get_object(action.tag); + var ClientWidget = db.web.client_actions.get_object(action.tag); (this.client_widget = new ClientWidget(this, action.params)).appendTo(this); }, ir_actions_report_xml: function(action) { @@ -135,12 +142,12 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ } }); -openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.ViewManager# */{ +db.web.ViewManager = db.web.Widget.extend(/** @lends db.web.ViewManager# */{ identifier_prefix: "viewmanager", template: "ViewManager", /** - * @constructs openerp.web.ViewManager - * @extends openerp.web.Widget + * @constructs db.web.ViewManager + * @extends db.web.Widget * * @param parent * @param dataset @@ -155,10 +162,13 @@ openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.View this.views_src = _.map(views, function(x) {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;}); this.views = {}; this.flags = this.flags || {}; - this.registry = openerp.web.views; + this.registry = db.web.views; }, render: function() { - return QWeb.render(this.template, {"prefix": this.element_id, views: this.views_src}) + return db.web.qweb.render(this.template, { + self: this, + prefix: this.element_id, + views: this.views_src}); }, /** * @returns {jQuery.Deferred} initial view loading promise @@ -266,7 +276,7 @@ openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.View /** * Sets up the current viewmanager's search view. * - * @param view_id the view to use or false for a default one + * @param {Number|false} view_id the view to use or false for a default one * @returns {jQuery.Deferred} search view startup deferred */ setup_search_view: function(view_id, search_defaults) { @@ -274,10 +284,10 @@ openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.View if (this.searchview) { this.searchview.stop(); } - this.searchview = new openerp.web.SearchView(this, this.element_id + "_search", this.dataset, view_id, search_defaults); - if (this.flags.search_view === false) { - this.searchview.hide(); - } + this.searchview = new db.web.SearchView( + this, this.element_id + "_search", this.dataset, + view_id, search_defaults); + this.searchview.on_search.add(function(domains, contexts, groupbys) { var controller = self.views[self.active_view].controller; controller.do_search.call(controller, domains, contexts, groupbys); @@ -294,66 +304,128 @@ openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.View on_remove: function() { }, on_edit: function() { - } + }, + /** + * Called by children view after executing an action + */ + on_action_executed: function () {} }); -openerp.web.ViewManagerAction = openerp.web.ViewManager.extend({ - template: "ViewManagerAction", - init: function(parent, action) { +db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{ + template:"ViewManagerAction", + /** + * @constructs db.web.ViewManagerAction + * @extends db.web.ViewManager + * + * @param {db.web.ActionManager} parent parent object/widget + * @param {Object} action descriptor for the action this viewmanager needs to manage its views. + */ + init: function(parent, action) { + // dataset initialization will take the session from ``this``, so if we + // do not have it yet (and we don't, because we've not called our own + // ``_super()``) rpc requests will blow up. this.session = parent.session; this.action = action; - var dataset; - if (!action.res_id) { - dataset = new openerp.web.DataSetSearch(this, action.res_model, action.context, action.domain); - } else { - this.action.flags.search_view = false; - dataset = new openerp.web.DataSetStatic(this, action.res_model, action.context, [action.res_id]); + var dataset = new db.web.DataSetSearch(this, action.res_model, action.context, action.domain); + if (action.res_id) { + dataset.ids.push(action.res_id); dataset.index = 0; } this._super(parent, dataset, action.views); - this.action = action; this.flags = this.action.flags || {}; if (action.res_model == 'board.board' && action.views.length == 1 && action.views) { - // Not elegant but allows to avoid flickering of SearchView#do_hide + // Not elegant but allows to avoid form chrome (pager, save/new + // buttons, sidebar, ...) displaying this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false; } + + // setup storage for session-wise menu hiding + if (this.session.hidden_menutips) { + return; + } + this.session.hidden_menutips = {} }, + /** + * Initializes the ViewManagerAction: sets up the searchview (if the + * searchview is enabled in the manager's action flags), calls into the + * parent to initialize the primary view and (if the VMA has a searchview) + * launches an initial search after both views are done rendering. + */ start: function() { - var inital_view_loaded = this._super(); - - var search_defaults = {}; - _.each(this.action.context, function (value, key) { - var match = /^search_default_(.*)$/.exec(key); - if (match) { - search_defaults[match[1]] = value; - } - }); + var self = this; + var searchview_loaded; if (this.flags.search_view !== false) { + var search_defaults = {}; + _.each(this.action.context, function (value, key) { + var match = /^search_default_(.*)$/.exec(key); + if (match) { + search_defaults[match[1]] = value; + } + }); // init search view - var searchview_id = this.action.search_view_id && this.action.search_view_id[0]; + var searchview_id = this.action['search_view_id'] && this.action['search_view_id'][0]; - var searchview_loaded = this.setup_search_view( + searchview_loaded = this.setup_search_view( searchview_id || false, search_defaults); + } + + var main_view_loaded = this._super(); + var manager_ready = $.when(searchview_loaded, main_view_loaded); + if (searchview_loaded && this.action['auto_search']) { // schedule auto_search - if (searchview_loaded != null && this.action['auto_search']) { - $.when(searchview_loaded, inital_view_loaded) - .then(this.searchview.do_search); + manager_ready.then(this.searchview.do_search); + } + + this.$element.find('.oe_get_xml_view').click(function () { + // TODO: add search view? + $('<pre>').text(db.web.json_node_to_xml( + self.views[self.active_view].controller.fields_view.arch, true)) + .dialog({ width: '95%'}); + }); + if (this.action.help && !this.flags.low_profile) { + var Users = new db.web.DataSet(self, 'res.users'), + header = this.$element.find('.oe-view-manager-header'); + header.delegate('blockquote button', 'click', function() { + var $this = $(this); + //noinspection FallthroughInSwitchStatementJS + switch ($this.attr('name')) { + case 'disable': + Users.write(self.session.uid, {menu_tips:false}); + case 'hide': + $this.closest('blockquote').hide(); + self.session.hidden_menutips[self.action.id] = true; + } + }); + if (!(self.action.id in self.session.hidden_menutips)) { + Users.read_ids([this.session.uid], ['menu_tips'], function(users) { + var user = users[0]; + if (!(user && user.id === self.session.uid)) { + return; + } + header.find('blockquote').toggle(user.menu_tips); + }); } } + + return manager_ready; }, on_mode_switch: function (view_type) { + var self = this; return $.when( this._super(view_type), - this.shortcut_check(this.views[view_type])); + this.shortcut_check(this.views[view_type])).then(function () { + var view_id = self.views[self.active_view].controller.fields_view.view_id; + self.$element.find('.oe_get_xml_view span').text(view_id); + }); }, shortcut_check : function(view) { var self = this; var grandparent = this.widget_parent && this.widget_parent.widget_parent; // display shortcuts if on the first view for the action var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle'); - if (!(grandparent instanceof openerp.web.WebClient) || + if (!(grandparent instanceof db.web.WebClient) || !(view.view_type === this.views_src[0].view_type && view.view_id === this.views_src[0].view_id)) { $shortcut_toggle.hide(); @@ -385,10 +457,39 @@ openerp.web.ViewManagerAction = openerp.web.ViewManager.extend({ $shortcut_toggle.addClass("oe-shortcut-remove"); } }); + }, + /** + * Intercept do_action resolution from children views + */ + on_action_executed: function () { + new db.web.DataSet(this, 'res.log') + .call('get', [], this.do_display_log); + }, + /** + * @param {Array<Object>} log_records + */ + do_display_log: function (log_records) { + var self = this, + $logs = this.$element.find('ul.oe-view-manager-logs:first').empty(); + _(log_records).each(function (record) { + $(_.sprintf('<li><a href="#">%s</a></li>', record.name)) + .appendTo($logs) + .delegate('a', 'click', function (e) { + self.do_action({ + type: 'ir.actions.act_window', + res_model: record.res_model, + res_id: record.res_id, + // TODO: need to have an evaluated context here somehow + //context: record.context, + views: [[false, 'form']] + }); + return false; + }); + }); } }); -openerp.web.Sidebar = openerp.web.Widget.extend({ +db.web.Sidebar = db.web.Widget.extend({ init: function(parent, element_id) { this._super(parent, element_id); this.items = {}; @@ -397,7 +498,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({ start: function() { this._super(this); var self = this; - this.$element.html(QWeb.render('Sidebar')); + this.$element.html(db.web.qweb.render('Sidebar')); this.$element.find(".toggle-sidebar").click(function(e) { self.do_toggle(); }); @@ -439,7 +540,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({ this.items[items[i].element_id] = items[i]; } } - var $section = $(QWeb.render("Sidebar.section", { + var $section = $(db.web.qweb.render("Sidebar.section", { section_id: section_id, name: name, classname: 'oe_sidebar_' + code, @@ -495,7 +596,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({ } }); -openerp.web.TranslateDialog = openerp.web.Dialog.extend({ +db.web.TranslateDialog = db.web.Dialog.extend({ dialog_title: _t("Translations"), init: function(view) { // TODO fme: should add the language to fields_view_get because between the fields view get @@ -515,14 +616,14 @@ openerp.web.TranslateDialog = openerp.web.Dialog.extend({ this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name }); this.languages = null; this.languages_loaded = $.Deferred(); - (new openerp.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), + (new db.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded); }, start: function() { var self = this; this._super(); $.when(this.languages_loaded).then(function() { - self.$element.html(QWeb.render('TranslateDialog', { widget: self })); + self.$element.html(db.web.qweb.render('TranslateDialog', { widget: self })); self.$element.tabs(); if (!(self.view.translatable_fields && self.view.translatable_fields.length)) { self.hide_tabs('fields'); @@ -627,11 +728,7 @@ openerp.web.TranslateDialog = openerp.web.Dialog.extend({ } }); -/** - * @class - * @extends openerp.web.Widget - */ -openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ +db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{ set_default_options: function(options) { this.options = options || {}; _.defaults(this.options, { @@ -644,7 +741,7 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ }, open_translate_dialog: function(field) { if (!this.translate_dialog) { - this.translate_dialog = new openerp.web.TranslateDialog(this).start(); + this.translate_dialog = new db.web.TranslateDialog(this).start(); } this.translate_dialog.open(field); }, @@ -656,12 +753,16 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``) * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'`` * @param {Object} [action_data.context=null] additional action context, to add to the current context - * @param {openerp.web.DataSet} dataset a dataset object used to communicate with the server + * @param {db.web.DataSet} dataset a dataset object used to communicate with the server * @param {Object} [record_id] the identifier of the object on which the action is to be applied * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action) */ - execute_action: function (action_data, dataset, record_id, on_closed) { + do_execute_action: function (action_data, dataset, record_id, on_closed) { var self = this; + var result_handler = function () { + if (on_closed) { on_closed.apply(null, arguments); } + self.widget_parent.on_action_executed.apply(null, arguments); + }; var handler = function (r) { var action = r.result; if (action && action.constructor == Object) { @@ -671,14 +772,14 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ active_ids: [record_id || false], active_model: dataset.model }); - action.context = new openerp.web.CompoundContext(dataset.get_context(), action.context); - self.do_action(action, on_closed); - } else if (on_closed) { - on_closed(action); + action.context = new db.web.CompoundContext(dataset.get_context(), action.context); + self.do_action(action, result_handler); + } else { + result_handler(); } }; - var context = new openerp.web.CompoundContext(dataset.get_context(), action_data.context || {}); + var context = new db.web.CompoundContext(dataset.get_context(), action_data.context || {}); if (action_data.special) { handler({result: {"type":"ir.actions.act_window_close"}}); @@ -693,7 +794,7 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ /** * Directly set a view to use instead of calling fields_view_get. This method must * be called before start(). When an embedded view is set, underlying implementations - * of openerp.web.View must use the provided view instead of any other one. + * of db.web.View must use the provided view instead of any other one. * * @param embedded_view A view. */ @@ -740,23 +841,23 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ }, on_sidebar_manage_view: function() { if (this.fields_view && this.fields_view.arch) { - $('<xmp>' + openerp.web.json_node_to_xml(this.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600}); + $('<xmp>' + db.web.json_node_to_xml(this.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600}); } else { this.notification.warn("Manage Views", "Could not find current view declaration"); } }, on_sidebar_edit_workflow: function() { - this.log('Todo'); + console.log('Todo'); }, on_sidebar_customize_object: function() { - this.log('Todo'); + console.log('Todo'); }, on_sidebar_import: function() { var import_view = new openerp.web.DataImport(this, this.dataset); import_view.start(); }, on_sidebar_export: function() { - var export_view = new openerp.web.DataExport(this, this.dataset); + var export_view = new db.web.DataExport(this, this.dataset); export_view.start(); }, on_sidebar_translate: function() { @@ -766,12 +867,7 @@ openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ } }); -/** - * Registry for all the main views - */ -openerp.web.views = new openerp.web.Registry(); - -openerp.web.json_node_to_xml = function(node, single_quote, indent) { +db.web.json_node_to_xml = function(node, single_quote, indent) { // For debugging purpose, this function will convert a json node back to xml // Maybe usefull for xml view editor @@ -800,7 +896,7 @@ openerp.web.json_node_to_xml = function(node, single_quote, indent) { r += '>\n'; var childs = []; for (var i = 0, ii = node.children.length; i < ii; i++) { - childs.push(openerp.web.json_node_to_xml(node.children[i], single_quote, indent + 1)); + childs.push(db.web.json_node_to_xml(node.children[i], single_quote, indent + 1)); } r += childs.join('\n'); r += '\n' + sindent + '</' + node.tag + '>'; |
