summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-03-22 15:33:31 +0100
committerXavier Morel <xmo@openerp.com>2012-03-22 15:33:31 +0100
commit7353e02e2ef4a403d4b79c1b157f2ca00b77d2fc (patch)
treea319552e3664ea272fdfb8abf31c961bcd5e1e6e /addons/web/static/src/js
parent514c63288c7d5d88d764b44696c5e3a5fc148dfb (diff)
parent1da49a44a57aa413e5eb96afe9c6d294e09848fd (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20120322143331-iajlifomw1hohbht
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome.js640
-rw-r--r--addons/web/static/src/js/core.js624
-rw-r--r--addons/web/static/src/js/data.js31
-rw-r--r--addons/web/static/src/js/data_import.js14
-rw-r--r--addons/web/static/src/js/formats.js4
-rw-r--r--addons/web/static/src/js/search.js80
-rw-r--r--addons/web/static/src/js/view_editor.js4
-rw-r--r--addons/web/static/src/js/view_form.js282
-rw-r--r--addons/web/static/src/js/view_list.js35
-rw-r--r--addons/web/static/src/js/view_list_editable.js33
-rw-r--r--addons/web/static/src/js/view_page.js30
-rw-r--r--addons/web/static/src/js/view_tree.js16
-rw-r--r--addons/web/static/src/js/views.js56
13 files changed, 978 insertions, 871 deletions
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index ba31ec81846..f8af815d187 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -45,6 +45,12 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.
});
+openerp.web.dialog = function(element) {
+ var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
+ result.dialog("widget").addClass("openerp");
+ return result;
+}
+
openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{
dialog_title: "",
/**
@@ -86,7 +92,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
if (this.dialog_options.autoOpen) {
this.open();
} else {
- this.$element.dialog(this.get_options());
+ openerp.web.dialog(this.$element, this.get_options());
}
},
get_options: function(options) {
@@ -125,7 +131,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
this.$element.html(this.render());
}
var o = this.get_options(options);
- this.$element.dialog(o).dialog('open');
+ openerp.web.dialog(this.$element, o).dialog('open');
if (o.height === 'auto' && o.max_height) {
this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
@@ -142,7 +148,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
on_resized: function() {
//openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height());
},
- stop: function () {
+ destroy: function () {
// Destroy widget
this.close();
this.$element.dialog('destroy');
@@ -166,7 +172,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
}
},
on_managed_error: function(error) {
- $('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>').dialog({
+ openerp.web.dialog($('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -234,7 +240,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.session.on_rpc_request.add_first(this.request_call);
this.session.on_rpc_response.add_last(this.response_call);
},
- stop: function() {
+ destroy: function() {
this.session.on_rpc_request.remove(this.request_call);
this.session.on_rpc_response.remove(this.response_call);
this.on_rpc_event(-this.count);
@@ -255,7 +261,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
$(".loading",this.$element).text(_.str.sprintf(
_t("Loading (%d)"), this.count));
$(".loading",this.$element).show();
- this.widget_parent.$element.addClass('loading');
+ this.getParent().$element.addClass('loading');
} else {
this.count = 0;
clearTimeout(this.long_running_timer);
@@ -265,7 +271,7 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
$.unblockUI();
}
$(".loading",this.$element).fadeOut();
- this.widget_parent.$element.removeClass('loading');
+ this.getParent().$element.removeClass('loading');
}
}
});
@@ -312,7 +318,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
self.hide();
});
},
- stop: function () {
+ destroy: function () {
this.hide();
this.$option_id.empty();
@@ -345,44 +351,6 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
return result;
},
/**
- * Waits until the new database is done creating, then unblocks the UI and
- * logs the user in as admin
- *
- * @param {Number} db_creation_id identifier for the db-creation operation, used to fetch the current installation progress
- * @param {Object} info info fields for this database creation
- * @param {String} info.db name of the database being created
- * @param {String} info.password super-admin password for the database
- */
- wait_for_newdb: function (db_creation_id, info) {
- var self = this;
- self.rpc('/web/database/progress', {
- id: db_creation_id,
- password: info.password
- }, function (result) {
- var progress = result[0];
- // I'd display a progress bar, but turns out the progress status
- // the server report kind-of blows goats: it's at 0 for ~75% of
- // the installation, then jumps to 75%, then jumps down to either
- // 0 or ~40%, then back up to 75%, then terminates. Let's keep that
- // mess hidden behind a not-very-useful but not overly weird
- // message instead.
- if (progress < 1) {
- setTimeout(function () {
- self.wait_for_newdb(db_creation_id, info);
- }, 500);
- return;
- }
-
- var admin = result[1][0];
- setTimeout(function () {
- self.widget_parent.do_login(
- info.db, admin.login, admin.password);
- self.stop();
- self.unblockUI();
- });
- });
- },
- /**
* Blocks UI and replaces $.unblockUI by a noop to prevent third parties
* from unblocking the UI
*/
@@ -407,7 +375,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
* @param {String} error.error message of the error dialog
*/
display_error: function (error) {
- return $('<div>').dialog({
+ return openerp.web.dialog($('<div>'), {
modal: true,
title: error.title,
buttons: [
@@ -421,23 +389,19 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
self.$option_id.find("form[name=create_db_form]").validate({
submitHandler: function (form) {
var fields = $(form).serializeArray();
- self.blockUI();
self.rpc("/web/database/create", {'fields': fields}, function(result) {
- if (result.error) {
- self.unblockUI();
- self.display_error(result);
- return;
- }
if (self.db_list) {
self.db_list.push(self.to_object(fields)['db_name']);
self.db_list.sort();
self.widget_parent.set_db_list(self.db_list);
}
+
var form_obj = self.to_object(fields);
- self.wait_for_newdb(result, {
- password: form_obj['super_admin_pwd'],
- db: form_obj['db_name']
- });
+ self.getParent().do_login(
+ form_obj['db_name'],
+ 'admin',
+ form_obj['create_admin_pwd']);
+ self.destroy();
});
}
});
@@ -449,7 +413,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
submitHandler: function (form) {
var $form = $(form),
fields = $form.serializeArray(),
- $db_list = $form.find('select[name=drop_db]'),
+ $db_list = $form.find('[name=drop_db]'),
db = $db_list.val();
if (!confirm("Do you really want to delete the database: " + db + " ?")) {
@@ -463,7 +427,7 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
$db_list.find(':selected').remove();
if (self.db_list) {
self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
- self.widget_parent.set_db_list(self.db_list);
+ self.getParent().set_db_list(self.db_list);
}
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
});
@@ -660,58 +624,212 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
}
});
-openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header# */{
- template: "Header",
+openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
/**
- * @constructs openerp.web.Header
- * @extends openerp.web.OldWidget
+ * @constructs openerp.web.Menu
+ * @extends openerp.web.Widget
+ *
+ * @param parent
+ */
+ template: 'Menu',
+ init: function() {
+ this._super.apply(this, arguments);
+ this.has_been_loaded = $.Deferred();
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.$secondary_menus = this.getParent().$element.find('.oe_secondary_menus_container');
+ this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
+ },
+ do_reload: function() {
+ var self = this;
+ return this.rpc("/web/menu/load", {}, this.on_loaded).then(function () {
+ if (self.current_menu) {
+ self.open_menu(self.current_menu);
+ }
+ });
+ },
+ on_loaded: function(data) {
+ this.data = data;
+ this.renderElement();
+ this.$element.on('click', 'a[data-menu]', this.on_menu_click);
+ this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
+ // Hide second level submenus
+ this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
+ this.has_been_loaded.resolve();
+ },
+ /**
+ * Opens a given menu by id, as if a user had browsed to that menu by hand
+ * except does not trigger any event on the way
+ *
+ * @param {Number} id database id of the terminal menu to select
+ */
+ open_menu: function (id) {
+ var $clicked_menu, $sub_menu, $main_menu;
+ $clicked_menu = this.$element.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
+
+ if (this.$secondary_menus.has($clicked_menu).length) {
+ $sub_menu = $clicked_menu.parents('.oe_secondary_menu');
+ $main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
+ } else {
+ $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
+ $main_menu = $clicked_menu;
+ }
+
+ // Activate current main menu
+ this.$element.find('.oe_active').removeClass('oe_active');
+ $main_menu.addClass('oe_active');
+
+ // Show current sub menu
+ this.$secondary_menus.find('.oe_secondary_menu').hide();
+ $sub_menu.show();
+
+ // Activate current menu item and show parents
+ this.$secondary_menus.find('.oe_active').removeClass('oe_active');
+ if ($main_menu !== $clicked_menu) {
+ $clicked_menu.parents().show();
+ if ($clicked_menu.is('.oe_menu_toggler')) {
+ $clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggle();
+ } else {
+ $clicked_menu.parent().addClass('oe_active');
+ }
+ }
+ },
+ open_action: function (id) {
+ var menu_id, $menu = this.$element.add(this.$secondary_menus).find('a[data-action-id=' + id + ']');
+ if (menu_id = $menu.data('menu')) {
+ this.open_menu(menu_id);
+ }
+ },
+ on_menu_click: function(ev, id) {
+ id = id || 0;
+ var $clicked_menu, manual = false;
+
+ if (id) {
+ // We can manually activate a menu with it's id (for hash url mapping)
+ manual = true;
+ $clicked_menu = this.$element.find('a[data-menu=' + id + ']');
+ if (!$clicked_menu.length) {
+ $clicked_menu = this.$secondary_menus.find('a[data-menu=' + id + ']');
+ }
+ } else {
+ $clicked_menu = $(ev.currentTarget);
+ id = $clicked_menu.data('menu');
+ }
+
+ this.trigger('menuClicked', id, $clicked_menu);
+
+ if (id) {
+ this.open_menu(id);
+ this.current_menu = id;
+ this.session.active_id = id;
+ var action_id = $clicked_menu.data('action-id');
+ if (action_id) {
+ this.on_action(action_id);
+ }
+ }
+ if (ev) {
+ ev.stopPropagation();
+ }
+ return false;
+ },
+ do_show_secondary: function($sub_menu, $main_menu) {
+ var self = this;
+ this.$secondary_menus.show();
+ if (!arguments.length) {
+ return;
+ }
+ $sub_menu.show();
+ },
+ on_action: function(action) {
+ }
+});
+
+openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMenu# */{
+ template: "UserMenu",
+ /**
+ * @constructs openerp.web.UserMenu
+ * @extends openerp.web.Widget
*
* @param parent
*/
init: function(parent) {
this._super(parent);
- this.qs = "?" + jQuery.param.querystring();
- this.$content = $();
this.update_promise = $.Deferred().resolve();
},
start: function() {
- this._super();
+ var self = this;
+ this._super.apply(this, arguments);
+ $('html').bind('click', function() {
+ self.$element.find('.oe_dropdown_options').hide();
+ });
+ this.$element.find('.oe_dropdown_toggle').click(function() {
+ self.$element.find('.oe_dropdown_options').toggle();
+ return false;
+ });
+ this.$element.on('click', '.oe_dropdown_options li a[data-menu]', function() {
+ var f = self['on_menu_' + $(this).data('menu')];
+ f && f($(this));
+ self.$element.find('.oe_dropdown_options').hide();
+ return false;
+ });
+ },
+ change_password :function() {
+ var self = this;
+ this.dialog = new openerp.web.Dialog(this, {
+ title: _t("Change Password"),
+ width : 'auto'
+ }).open();
+ this.dialog.$element.html(QWeb.render("Change_Pwd", self));
+ this.dialog.$element.find("form[name=change_password_form]").validate({
+ submitHandler: function (form) {
+ self.rpc("/web/session/change_password",{
+ 'fields': $(form).serializeArray()
+ }, function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
+ } else {
+ openerp.webclient.on_logout();
+ }
+ });
+ }
+ });
+ },
+ display_error: function (error) {
+ return openerp.web.dialog($('<div>'), {
+ modal: true,
+ title: error.title,
+ buttons: [
+ {text: _("Ok"), click: function() { $(this).dialog("close"); }}
+ ]
+ }).html(error.error);
},
do_update: function () {
var self = this;
var fct = function() {
- self.$content.remove();
+ var $avatar = self.$element.find('.oe_topbar_avatar');
+ $avatar.attr('src', $avatar.data('default-src'));
if (!self.session.uid)
return;
var func = new openerp.web.Model("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);
+ // TODO: Only show company if multicompany in use
+ self.$element.find('.oe_topbar_name').text(res.name + '/' + res.company_id[1]);
return self.shortcut_load();
});
};
this.update_promise = this.update_promise.pipe(fct, fct);
},
- on_about: function() {
- var self = this;
- self.rpc("/web/webclient/version_info", {}).then(function(res) {
- var $help = $(QWeb.render("About-Page", {version_info: res}));
- $help.find('a.oe_activate_debug_mode').click(function (e) {
- e.preventDefault();
- window.location = $.param.querystring(
- window.location.href, 'debug');
- });
- $help.dialog({autoOpen: true,
- modal: true, width: 960, title: _t("About")});
- });
+ on_action: function() {
},
shortcut_load :function(){
var self = this,
sc = self.session.shortcuts,
shortcuts_ds = new openerp.web.DataSet(this, 'ir.ui.view_sc');
+ self.$element.find('.oe_dropdown_options a[data-menu=shortcut]').each(function() {
+ $(this).parent().remove();
+ });
// TODO: better way to communicate between sections.
// sc.bindings, because jquery does not bind/trigger on arrays...
if (!sc.binding) {
@@ -719,52 +837,41 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
$(sc.binding).bind({
'add': function (e, attrs) {
shortcuts_ds.create(attrs, function (out) {
- $('<li>', {
- 'data-shortcut-id':out.result,
- 'data-id': attrs.res_id
- }).text(attrs.name)
- .appendTo(self.$element.find('.oe-shortcuts ul'));
+ var shortcut = QWeb.render('UserMenu.shortcut', {
+ shortcuts : [{
+ name : attrs.name,
+ id : out.result,
+ res_id : attrs.res_id
+ }]
+ });
+ $(shortcut).appendTo(self.$element.find('.oe_dropdown_options'));
attrs.id = out.result;
sc.push(attrs);
});
},
'remove-current': function () {
var menu_id = self.session.active_id;
- var $shortcut = self.$element
- .find('.oe-shortcuts li[data-id=' + menu_id + ']');
+ var $shortcut = self.$element.find('.oe_dropdown_options li a[data-id=' + menu_id + ']');
var shortcut_id = $shortcut.data('shortcut-id');
$shortcut.remove();
shortcuts_ds.unlink([shortcut_id]);
var sc_new = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id});
sc.splice(0, sc.length);
sc.push.apply(sc, sc_new);
- }
+ }
});
}
return this.rpc('/web/session/sc_list', {}, function(shortcuts) {
sc.splice(0, sc.length);
sc.push.apply(sc, shortcuts);
- self.$element.find('.oe-shortcuts')
- .html(QWeb.render('Shortcuts', {'shortcuts': shortcuts}))
- .undelegate('li', 'click')
-
- .delegate('li', 'click', function(e) {
- e.stopPropagation();
- var id = $(this).data('id');
- self.session.active_id = id;
- self.rpc('/web/menu/action', {'menu_id':id}, function(ir_menu_data) {
- if (ir_menu_data.action.length){
- self.on_action(ir_menu_data.action[0][2]);
- }
- });
- });
+ $(QWeb.render('UserMenu.shortcut', {'shortcuts': shortcuts}))
+ .appendTo(self.$element.find('.oe_dropdown_options'));
});
},
-
- on_action: function(action) {
+ on_menu_logout: function() {
},
- on_preferences: function(){
+ on_menu_settings: function() {
var self = this;
var action_manager = new openerp.web.ActionManager(this);
var dataset = new openerp.web.DataSet (this,'res.users',this.context);
@@ -793,7 +900,7 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
var inner_viewmanager = action_manager.inner_viewmanager;
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
.then(function() {
- self.dialog.stop();
+ self.dialog.destroy();
// needs to refresh interface in case language changed
window.location.reload();
});
@@ -804,298 +911,66 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
action_manager.appendTo(this.dialog);
action_manager.render(this.dialog);
},
-
- change_password :function() {
+ on_menu_about: function() {
var self = this;
- this.dialog = new openerp.web.Dialog(this, {
- title: _t("Change Password"),
- width : 'auto'
- }).open();
- this.dialog.$element.html(QWeb.render("Change_Pwd", self));
- this.dialog.$element.find("form[name=change_password_form]").validate({
- submitHandler: function (form) {
- self.rpc("/web/session/change_password",{
- 'fields': $(form).serializeArray()
- }, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- } else {
- openerp.webclient.on_logout();
- }
- });
- }
+ self.rpc("/web/webclient/version_info", {}).then(function(res) {
+ var $help = $(QWeb.render("About-Page", {version_info: res}));
+ $help.find('a.oe_activate_debug_mode').click(function (e) {
+ e.preventDefault();
+ window.location = $.param.querystring(
+ window.location.href, 'debug');
+ });
+ openerp.web.dialog($help, {autoOpen: true,
+ modal: true, width: 960, title: _t("About")});
});
},
- display_error: function (error) {
- return $('<div>').dialog({
- modal: true,
- title: error.title,
- buttons: [
- {text: _("Ok"), click: function() { $(this).dialog("close"); }}
- ]
- }).html(error.error);
- },
- on_logout: function() {
- }
-});
-
-openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */{
- /**
- * @constructs openerp.web.Menu
- * @extends openerp.web.OldWidget
- *
- * @param parent
- * @param element_id
- * @param secondary_menu_id
- */
- init: function(parent, element_id, secondary_menu_id) {
- this._super(parent, element_id);
- this.secondary_menu_id = secondary_menu_id;
- this.$secondary_menu = $("#" + secondary_menu_id);
- this.menu = false;
- this.folded = false;
- if (window.localStorage) {
- this.folded = localStorage.getItem('oe_menu_folded') === 'true';
- }
- this.float_timeout = 700;
- },
- start: function() {
- this.$secondary_menu.addClass(this.folded ? 'oe_folded' : 'oe_unfolded');
- },
- do_reload: function() {
- var self = this;
- return this.rpc("/web/menu/load", {}, this.on_loaded).then(function () {
- if (self.current_menu) {
- self.open_menu(self.current_menu);
+ on_menu_shortcut: function($link) {
+ var self = this,
+ id = $link.data('id');
+ self.session.active_id = id;
+ self.rpc('/web/menu/action', {'menu_id': id}, function(ir_menu_data) {
+ if (ir_menu_data.action.length){
+ self.on_action(ir_menu_data.action[0][2]);
}
});
- },
- on_loaded: function(data) {
- this.data = data;
- this.$element.html(QWeb.render("Menu", { widget : this }));
- this.$secondary_menu.html(QWeb.render("Menu.secondary", { widget : this }));
- this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
- this.$secondary_menu.find('.oe_toggle_secondary_menu').click(this.on_toggle_fold);
- },
- on_toggle_fold: function() {
- this.$secondary_menu.toggleClass('oe_folded').toggleClass('oe_unfolded');
- if (this.folded) {
- this.$secondary_menu.find('.oe_secondary_menu.active').show();
- } else {
- this.$secondary_menu.find('.oe_secondary_menu').hide();
- }
- this.folded = !this.folded;
- if (window.localStorage) {
- localStorage.setItem('oe_menu_folded', this.folded.toString());
- }
- },
- /**
- * Opens a given menu by id, as if a user had browsed to that menu by hand
- * except does not trigger any event on the way
- *
- * @param {Number} menu_id database id of the terminal menu to select
- */
- open_menu: function (menu_id) {
- this.$element.add(this.$secondary_menu).find('.active')
- .removeClass('active');
- this.$secondary_menu.find('> .oe_secondary_menu').hide();
-
- var $primary_menu;
- var $secondary_submenu = this.$secondary_menu.find(
- 'a[data-menu=' + menu_id +']');
- if ($secondary_submenu.length) {
- for(;;) {
- if ($secondary_submenu.hasClass('leaf')) {
- $secondary_submenu.addClass('active');
- } else if ($secondary_submenu.hasClass('submenu')) {
- $secondary_submenu.addClass('opened')
- }
- var $parent = $secondary_submenu.parent().show();
- if ($parent.hasClass('oe_secondary_menu')) {
- var primary_id = $parent.data('menu-parent');
- $primary_menu = this.$element.find(
- 'a[data-menu=' + primary_id + ']');
- break;
- }
- $secondary_submenu = $parent.prev();
- }
- } else {
- $primary_menu = this.$element.find('a[data-menu=' + menu_id + ']');
- }
- if (!$primary_menu.length) {
- return;
- }
- $primary_menu.addClass('active');
- this.$secondary_menu.find(
- 'div[data-menu-parent=' + $primary_menu.data('menu') + ']').addClass('active').toggle(!this.folded);
- },
- on_menu_click: function(ev, id) {
- id = id || 0;
- var $clicked_menu, manual = false;
-
- if (id) {
- // We can manually activate a menu with it's id (for hash url mapping)
- manual = true;
- $clicked_menu = this.$element.find('a[data-menu=' + id + ']');
- if (!$clicked_menu.length) {
- $clicked_menu = this.$secondary_menu.find('a[data-menu=' + id + ']');
- }
- } else {
- $clicked_menu = $(ev.currentTarget);
- id = $clicked_menu.data('menu');
- }
-
- if (this.do_menu_click($clicked_menu, manual) && id) {
- this.current_menu = id;
- this.session.active_id = id;
- this.rpc('/web/menu/action', {'menu_id': id}, this.on_menu_action_loaded);
- }
- if (ev) {
- ev.stopPropagation();
- }
- return false;
- },
- do_menu_click: function($clicked_menu, manual) {
- var $sub_menu, $main_menu,
- active = $clicked_menu.is('.active'),
- sub_menu_visible = false,
- has_submenu_items = false;
-
- if (this.$secondary_menu.has($clicked_menu).length) {
- $sub_menu = $clicked_menu.parents('.oe_secondary_menu');
- $main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
- } else {
- $sub_menu = this.$secondary_menu.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
- $main_menu = $clicked_menu;
- }
-
- sub_menu_visible = $sub_menu.is(':visible');
- has_submenu_items = !!$sub_menu.children().length;
- this.$secondary_menu.find('.oe_secondary_menu').hide();
-
- $('.active', this.$element.add(this.$secondary_menu)).removeClass('active');
- $main_menu.add($clicked_menu).add($sub_menu).addClass('active');
-
- if (has_submenu_items) {
- if (!(this.folded && manual)) {
- this.do_show_secondary($sub_menu, $main_menu);
- } else {
- this.do_show_secondary();
- }
- }
-
- if ($main_menu != $clicked_menu) {
- if ($clicked_menu.is('.submenu')) {
- $sub_menu.find('.submenu.opened').each(function() {
- if (!$(this).next().has($clicked_menu).length && !$(this).is($clicked_menu)) {
- $(this).removeClass('opened').next().hide();
- }
- });
- $clicked_menu.toggleClass('opened').next().toggle();
- } else if ($clicked_menu.is('.leaf')) {
- $sub_menu.toggle(!this.folded);
- return true;
- }
- } else if (this.folded) {
- if ((active && sub_menu_visible) || !has_submenu_items) {
- $sub_menu.hide();
- return true;
- }
- return manual;
- } else {
- return true;
- }
- return false;
- },
- do_hide_secondary: function() {
- this.$secondary_menu.hide();
- },
- do_show_secondary: function($sub_menu, $main_menu) {
- var self = this;
- this.$secondary_menu.show();
- if (!arguments.length) {
- return;
- }
- if (this.folded) {
- var css = $main_menu.position(),
- fold_width = this.$secondary_menu.width() + 2,
- window_width = $(window).width();
- css.top += 33;
- css.left -= Math.round(($sub_menu.width() - $main_menu.width()) / 2);
- css.left = css.left < fold_width ? fold_width : css.left;
- if ((css.left + $sub_menu.width()) > window_width) {
- delete(css.left);
- css.right = 1;
- }
- $sub_menu.css(css);
- $sub_menu.mouseenter(function() {
- clearTimeout($sub_menu.data('timeoutId'));
- $sub_menu.data('timeoutId', null);
- return false;
- }).mouseleave(function(evt) {
- var timeoutId = setTimeout(function() {
- if (self.folded && $sub_menu.data('timeoutId')) {
- $sub_menu.hide().unbind('mouseenter').unbind('mouseleave');
- }
- }, self.float_timeout);
- $sub_menu.data('timeoutId', timeoutId);
- return false;
- });
- }
- $sub_menu.show();
- },
- on_menu_action_loaded: function(data) {
- var self = this;
- if (data.action.length) {
- var action = data.action[0][2];
- action.from_menu = true;
- self.on_action(action);
- } else {
- self.on_action({type: 'null_action'});
- }
- },
- on_action: function(action) {
}
});
-openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebClient */{
+openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{
/**
* @constructs openerp.web.WebClient
- * @extends openerp.web.OldWidget
- *
- * @param element_id
+ * @extends openerp.web.Widget
*/
init: function(parent) {
var self = this;
this._super(parent);
openerp.webclient = this;
-
+ this.querystring = '?' + jQuery.param.querystring();
this._current_state = null;
},
start: function() {
var self = this;
- this.$element = $(document.body);
+ this.$element.addClass("openerp openerp2");
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.$element.addClass("kitten-mode-activated");
this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
self.$element.toggleClass('clark-gable');
});
}
- this.session.bind().then(function() {
+ this.session.bind_session().then(function() {
if (!self.session.session_is_valid()) {
self.show_login();
}
});
this.session.on_session_valid.add(function() {
self.show_application();
-
- self.header.do_update();
+
+ self.user_menu.do_update();
self.menu.do_reload();
if(self.action_manager)
- self.action_manager.stop();
+ self.action_manager.destroy();
self.action_manager = new openerp.web.ActionManager(self);
- self.action_manager.appendTo($("#oe_app"));
+ self.action_manager.appendTo(self.$element.find('.oe_application'));
self.bind_hashchange();
var version_label = _t("OpenERP - Unsupported/Community Version");
if (!self.session.openerp_entreprise) {
@@ -1103,6 +978,9 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
document.title = version_label;
}
});
+ this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
+ $(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
+ });
},
show_login: function() {
var self = this;
@@ -1115,15 +993,15 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
var self = this;
this.destroy_content();
this.show_common();
- self.$table = $(QWeb.render("Interface", {}));
+ self.$table = $(QWeb.render("WebClient", {}));
self.$element.append(self.$table);
- self.header = new openerp.web.Header(self);
- 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 = new openerp.web.Menu(self);
+ self.menu.replace(this.$element.find('.oe_menu_placeholder'));
self.menu.on_action.add(this.proxy('on_menu_action'));
- self.menu.start();
+ self.user_menu = new openerp.web.UserMenu(self);
+ self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder'));
+ self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
+ self.user_menu.on_action.add(this.proxy('on_menu_action'));
},
show_common: function() {
var self = this;
@@ -1144,8 +1022,8 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
this.loading.appendTo(this.$element);
},
destroy_content: function() {
- _.each(_.clone(this.widget_children), function(el) {
- el.stop();
+ _.each(_.clone(this.getChildren()), function(el) {
+ el.destroy();
});
this.$element.children().remove();
},
@@ -1207,6 +1085,12 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
self.menu.on_menu_click(null, action.menu_id);
});
}
+ },
+ set_content_full_screen: function(fullscreen) {
+ if (fullscreen)
+ $(".oe_webclient", this.$element).addClass("oe_content_full_screen");
+ else
+ $(".oe_webclient", this.$element).removeClass("oe_content_full_screen");
}
});
@@ -1250,7 +1134,7 @@ openerp.web.embed = function (origin, dbname, login, key, action, options) {
var sc = document.getElementsByTagName('script');
currentScript = sc[sc.length-1];
}
- openerp.connection.bind(origin).then(function () {
+ openerp.connection.bind_session(origin).then(function () {
openerp.connection.session_authenticate(dbname, login, key, true).then(function () {
var client = new openerp.web.EmbeddedClient(action, options);
client.insertAfter(currentScript);
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index a1d2d04f697..9bcf037fefc 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -10,110 +10,8 @@ if (!console.debug) {
}
openerp.web.core = function(openerp) {
-/**
- * John Resig Class with factory improvement
- */
-(function() {
- var initializing = false,
- fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
- // The web Class implementation (does nothing)
- /**
- * Extended version of John Resig's Class pattern
- *
- * @class
- */
- openerp.web.Class = function(){};
-
- /**
- * Subclass an existing class
- *
- * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
- */
- openerp.web.Class.extend = function(prop) {
- var _super = this.prototype;
-
- // Instantiate a web class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- var prototype = new this();
- initializing = false;
-
- // Copy the properties over onto the new prototype
- for (var name in prop) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" &&
- fnTest.test(prop[name]) ?
- (function(name, fn) {
- return function() {
- var tmp = this._super;
-
- // Add a new ._super() method that is the same
- // method but on the super-class
- this._super = _super[name];
-
- // The method only need to be bound temporarily, so
- // we remove it when we're done executing
- var ret = fn.apply(this, arguments);
- this._super = tmp;
-
- return ret;
- };
- })(name, prop[name]) :
- prop[name];
- }
-
- // The dummy class constructor
- function Class() {
- // All construction is actually done in the init method
- if (!initializing && this.init) {
- var ret = this.init.apply(this, arguments);
- if (ret) { return ret; }
- }
- return this;
- }
- Class.include = function (properties) {
- for (var name in properties) {
- if (typeof properties[name] !== 'function'
- || !fnTest.test(properties[name])) {
- prototype[name] = properties[name];
- } else if (typeof prototype[name] === 'function'
- && prototype.hasOwnProperty(name)) {
- prototype[name] = (function (name, fn, previous) {
- return function () {
- var tmp = this._super;
- this._super = previous;
- var ret = fn.apply(this, arguments);
- this._super = tmp;
- return ret;
- }
- })(name, properties[name], prototype[name]);
- } else if (typeof _super[name] === 'function') {
- prototype[name] = (function (name, fn) {
- return function () {
- var tmp = this._super;
- this._super = _super[name];
- var ret = fn.apply(this, arguments);
- this._super = tmp;
- return ret;
- }
- })(name, properties[name]);
- }
- }
- };
-
- // Populate our constructed prototype object
- Class.prototype = prototype;
-
- // Enforce the constructor to be what we expect
- Class.constructor = Class;
- // And make this class extendable
- Class.extend = arguments.callee;
-
- return Class;
- };
-})();
+openerp.web.Class = nova.Class;
openerp.web.callback = function(obj, method) {
var callback = function() {
@@ -175,31 +73,6 @@ openerp.web.callback = function(obj, method) {
};
/**
- * Generates an inherited class that replaces all the methods by null methods (methods
- * that does nothing and always return undefined).
- *
- * @param {Class} claz
- * @param {Object} add Additional functions to override.
- * @return {Class}
- */
-openerp.web.generate_null_object_class = function(claz, add) {
- var newer = {};
- var copy_proto = function(prototype) {
- for (var name in prototype) {
- if(typeof prototype[name] == "function") {
- newer[name] = function() {};
- }
- }
- if (prototype.prototype)
- copy_proto(prototype.prototype);
- };
- copy_proto(claz.prototype);
- newer.init = openerp.web.Widget.prototype.init;
- var tmpclass = claz.extend(newer);
- return tmpclass.extend(add || {});
-};
-
-/**
* web error for lookup failure
*
* @class
@@ -362,11 +235,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
}
});
-openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.CallbackEnabled# */{
- /**
- * @constructs openerp.web.CallbackEnabled
- * @extends openerp.web.Class
- */
+openerp.web.CallbackEnabledMixin = {
init: function() {
// Transform on_* method into openerp.web.callbacks
for (var name in this) {
@@ -404,7 +273,14 @@ openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.Ca
return self[method_name].apply(self, arguments);
}
}
-});
+};
+
+openerp.web.CallbackEnabled = openerp.web.Class.extend(_.extend({}, nova.GetterSetterMixin, openerp.web.CallbackEnabledMixin, {
+ init: function() {
+ nova.GetterSetterMixin.init.call(this);
+ openerp.web.CallbackEnabledMixin.init.call(this);
+ }
+}));
openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Connection# */{
/**
@@ -422,7 +298,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.name = openerp._session_id;
this.qweb_mutex = new $.Mutex();
},
- bind: function(origin) {
+ bind_session: function(origin) {
var window_origin = location.protocol+"//"+location.host, self=this;
this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
this.prefix = this.origin;
@@ -445,6 +321,269 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.active_id = null;
return this.session_init();
},
+ test_eval_get_context: function () {
+ var asJS = function (arg) {
+ if (arg instanceof py.object) {
+ return arg.toJSON();
+ }
+ return arg;
+ };
+
+ var datetime = new py.object();
+ datetime.datetime = new py.type(function datetime() {
+ throw new Error('datetime.datetime not implemented');
+ });
+ var date = datetime.date = new py.type(function date(y, m, d) {
+ if (y instanceof Array) {
+ d = y[2];
+ m = y[1];
+ y = y[0];
+ }
+ this.year = asJS(y);
+ this.month = asJS(m);
+ this.day = asJS(d);
+ }, py.object, {
+ strftime: function (args) {
+ var f = asJS(args[0]), self = this;
+ return new py.str(f.replace(/%([A-Za-z])/g, function (m, c) {
+ switch (c) {
+ case 'Y': return self.year;
+ case 'm': return _.str.sprintf('%02d', self.month);
+ case 'd': return _.str.sprintf('%02d', self.day);
+ }
+ throw new Error('ValueError: No known conversion for ' + m);
+ }));
+ }
+ });
+ date.__getattribute__ = function (name) {
+ if (name === 'today') {
+ return date.today;
+ }
+ throw new Error("AttributeError: object 'date' has no attribute '" + name +"'");
+ };
+ date.today = new py.def(function () {
+ var d = new Date();
+ return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
+ });
+ datetime.time = new py.type(function time() {
+ throw new Error('datetime.time not implemented');
+ });
+
+ var time = new py.object();
+ time.strftime = new py.def(function (args) {
+ return date.today.__call__().strftime(args);
+ });
+
+ var relativedelta = new py.type(function relativedelta(args, kwargs) {
+ if (!_.isEmpty(args)) {
+ throw new Error('Extraction of relative deltas from existing datetimes not supported');
+ }
+ this.ops = kwargs;
+ }, py.object, {
+ __add__: function (other) {
+ if (!(other instanceof datetime.date)) {
+ return py.NotImplemented;
+ }
+ // TODO: test this whole mess
+ var year = asJS(this.ops.year) || asJS(other.year);
+ if (asJS(this.ops.years)) {
+ year += asJS(this.ops.years);
+ }
+
+ var month = asJS(this.ops.month) || asJS(other.month);
+ if (asJS(this.ops.months)) {
+ month += asJS(this.ops.months);
+ // FIXME: no divmod in JS?
+ while (month < 1) {
+ year -= 1;
+ month += 12;
+ }
+ while (month > 12) {
+ year += 1;
+ month -= 12;
+ }
+ }
+
+ var lastMonthDay = new Date(year, month, 0).getDate();
+ var day = asJS(this.ops.day) || asJS(other.day);
+ if (day > lastMonthDay) { day = lastMonthDay; }
+ var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
+ if (days_offset) {
+ day = new Date(year, month-1, day + days_offset).getDate();
+ }
+ // TODO: leapdays?
+ // TODO: hours, minutes, seconds? Not used in XML domains
+ // TODO: weekday?
+ return new datetime.date(year, month, day);
+ },
+ __radd__: function (other) {
+ return this.__add__(other);
+ },
+
+ __sub__: function (other) {
+ if (!(other instanceof datetime.date)) {
+ return py.NotImplemented;
+ }
+ // TODO: test this whole mess
+ var year = asJS(this.ops.year) || asJS(other.year);
+ if (asJS(this.ops.years)) {
+ year -= asJS(this.ops.years);
+ }
+
+ var month = asJS(this.ops.month) || asJS(other.month);
+ if (asJS(this.ops.months)) {
+ month -= asJS(this.ops.months);
+ // FIXME: no divmod in JS?
+ while (month < 1) {
+ year -= 1;
+ month += 12;
+ }
+ while (month > 12) {
+ year += 1;
+ month -= 12;
+ }
+ }
+
+ var lastMonthDay = new Date(year, month, 0).getDate();
+ var day = asJS(this.ops.day) || asJS(other.day);
+ if (day > lastMonthDay) { day = lastMonthDay; }
+ var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
+ if (days_offset) {
+ day = new Date(year, month-1, day - days_offset).getDate();
+ }
+ // TODO: leapdays?
+ // TODO: hours, minutes, seconds? Not used in XML domains
+ // TODO: weekday?
+ return new datetime.date(year, month, day);
+ },
+ __rsub__: function (other) {
+ return this.__sub__(other);
+ }
+ });
+
+ return {
+ uid: new py.float(this.uid),
+ datetime: datetime,
+ time: time,
+ relativedelta: relativedelta
+ };
+ },
+ /**
+ * FIXME: Huge testing hack, especially the evaluation context, rewrite + test for real before switching
+ */
+ test_eval: function (source, expected) {
+ try {
+ var ctx = this.test_eval_contexts(source.contexts);
+ if (!_.isEqual(ctx, expected.context)) {
+ console.group('Local context does not match remote, nothing is broken but please report to R&D (xmo)');
+ console.warn('source', source.contexts);
+ console.warn('local', ctx);
+ console.warn('remote', expected.context);
+ console.groupEnd();
+ }
+ } catch (e) {
+ console.group('Failed to evaluate contexts, nothing is broken but please report to R&D (xmo)');
+ console.error(e);
+ console.log('source', source.contexts);
+ console.groupEnd();
+ }
+
+ try {
+ var dom = this.test_eval_domains(source.domains, this.test_eval_get_context());
+ if (!_.isEqual(dom, expected.domain)) {
+ console.group('Local domain does not match remote, nothing is broken but please report to R&D (xmo)');
+ console.warn('source', source.domains);
+ console.warn('local', dom);
+ console.warn('remote', expected.domain);
+ console.groupEnd();
+ }
+ } catch (e) {
+ console.group('Failed to evaluate domains, nothing is broken but please report to R&D (xmo)');
+ console.error(e);
+ console.log('source', source.domains);
+ console.groupEnd();
+ }
+
+ try {
+ var groups = this.test_eval_groupby(source.group_by_seq);
+ if (!_.isEqual(groups, expected.group_by)) {
+ console.group('Local groupby does not match remote, nothing is broken but please report to R&D (xmo)');
+ console.warn('source', source.group_by_seq);
+ console.warn('local', groups);
+ console.warn('remote', expected.group_by);
+ console.groupEnd();
+ }
+ } catch (e) {
+ console.group('Failed to evaluate groupby, nothing is broken but please report to R&D (xmo)');
+ console.error(e);
+ console.log('source', source.group_by_seq);
+ console.groupEnd();
+ }
+ },
+ test_eval_contexts: function (contexts) {
+ var result_context = _.extend({}, this.user_context),
+ self = this;
+ _(contexts).each(function (ctx) {
+ switch(ctx.__ref) {
+ case 'context':
+ _.extend(result_context, py.eval(ctx.__debug));
+ break;
+ case 'compound_context':
+ _.extend(
+ result_context, self.test_eval_contexts(ctx.__contexts));
+ break;
+ default:
+ _.extend(result_context, ctx);
+ }
+ });
+ return result_context;
+ },
+ test_eval_domains: function (domains, eval_context) {
+ var result_domain = [], self = this;
+ _(domains).each(function (dom) {
+ switch(dom.__ref) {
+ case 'domain':
+ result_domain.push.apply(
+ result_domain, py.eval(dom.__debug, eval_context));
+ break;
+ case 'compound_domain':
+ result_domain.push.apply(
+ result_domain, self.test_eval_domains(
+ dom.__domains, eval_context));
+ break;
+ default:
+ result_domain.push.apply(
+ result_domain, dom);
+ }
+ });
+ return result_domain;
+ },
+ test_eval_groupby: function (contexts) {
+ var result_group = [], self = this;
+ _(contexts).each(function (ctx) {
+ var group;
+ switch(ctx.__ref) {
+ case 'context':
+ group = py.eval(ctx.__debug).group_by;
+ break;
+ case 'compound_context':
+ group = self.test_eval_contexts(ctx.__contexts).group_by;
+ break;
+ default:
+ group = ctx.group_by
+ }
+ if (!group) { return; }
+ if (typeof group === 'string') {
+ result_group.push(group);
+ } else if (group instanceof Array) {
+ result_group.push.apply(result_group, group);
+ } else {
+ throw new Error('Got invalid groupby {{'
+ + JSON.stringify(group) + '}}');
+ }
+ });
+ return result_group;
+ },
/**
* Executes an RPC call, registering the provided callbacks.
*
@@ -480,6 +619,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
function (response, textStatus, jqXHR) {
self.on_rpc_response();
if (!response.error) {
+ if (url.url === '/web/session/eval_domain_and_context') {
+ self.test_eval(params, response.result);
+ }
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.data.type === "session_invalid") {
self.uid = false;
@@ -733,6 +875,14 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
return self.do_load_js(file_list.concat(files));
+ }).then(function () {
+ if (!Date.CultureInfo.pmDesignator) {
+ // If no am/pm designator is specified but the openerp
+ // datetime format uses %i, date.js won't be able to
+ // correctly format a date. See bug#938497.
+ Date.CultureInfo.amDesignator = 'AM';
+ Date.CultureInfo.pmDesignator = 'PM';
+ }
});
}))
}
@@ -873,9 +1023,12 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
_(_.extend({}, options.data || {},
{session_id: this.session_id, token: token}))
.each(function (value, key) {
- $('<input type="hidden" name="' + key + '">')
- .val(value)
- .appendTo($form_data);
+ var $input = $form.find('[name=' + key +']');
+ if (!$input.length) {
+ $input = $('<input type="hidden" name="' + key + '">')
+ .appendTo($form_data);
+ }
+ $input.val(value)
});
$form
@@ -958,11 +1111,11 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
*
* And of course, when you don't need that widget anymore, just do:
*
- * my_widget.stop();
+ * my_widget.destroy();
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
-openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.Widget# */{
+openerp.web.Widget = nova.Widget.extend(_.extend({}, openerp.web.CallbackEnabledMixin, {
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the default render() method can not be used.
@@ -971,18 +1124,13 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
*/
template: null,
/**
- * Tag name when creating a default $element.
- * @type string
- */
- tag_name: 'div',
- /**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs openerp.web.Widget
* @extends openerp.web.CallbackEnabled
*
* @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
- * When that widget is destroyed by calling stop(), the current instance will be
+ * When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
* @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
* to bind the current Widget to an already existing part of the DOM, which is not compatible
@@ -990,132 +1138,24 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
* for new components this argument should not be provided any more.
*/
init: function(parent) {
- this._super();
+ this._super(parent);
+ openerp.web.CallbackEnabledMixin.init.call(this);
this.session = openerp.connection;
-
- this.$element = $(document.createElement(this.tag_name));
-
- this.widget_parent = parent;
- this.widget_children = [];
- if(parent && parent.widget_children) {
- parent.widget_children.push(this);
- }
- // useful to know if the widget was destroyed and should not be used anymore
- this.widget_is_stopped = false;
- },
- /**
- * Renders the current widget and appends it to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- appendTo: function(target) {
- var self = this;
- return this._render_and_insert(function(t) {
- self.$element.appendTo(t);
- }, target);
- },
- /**
- * Renders the current widget and prepends it to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- prependTo: function(target) {
- var self = this;
- return this._render_and_insert(function(t) {
- self.$element.prependTo(t);
- }, target);
},
/**
- * Renders the current widget and inserts it after to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
+ * Renders the element. The default implementation renders the widget using QWeb,
+ * `this.template` must be defined. The context given to QWeb contains the "widget"
+ * key that references `this`.
*/
- insertAfter: function(target) {
- var self = this;
- return this._render_and_insert(function(t) {
- self.$element.insertAfter(t);
- }, target);
- },
- /**
- * Renders the current widget and inserts it before to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- insertBefore: function(target) {
- var self = this;
- return this._render_and_insert(function(t) {
- self.$element.insertBefore(t);
- }, target);
- },
- /**
- * Renders the current widget and replaces the given jQuery object.
- *
- * @param target A jQuery object or a Widget instance.
- */
- replace: function(target) {
- return this._render_and_insert(_.bind(function(t) {
- this.$element.replaceAll(t);
- }, this), target);
- },
- _render_and_insert: function(insertion, target) {
- this.render_element();
- if (target instanceof openerp.web.Widget)
- target = target.$element;
- insertion(target);
- this.on_inserted(this.$element, this);
- return this.start();
- },
- on_inserted: function(element, widget) {},
- /**
- * Renders the element and insert the result of the render() method in this.$element.
- */
- render_element: function() {
- var rendered = this.render();
- if (rendered) {
+ renderElement: function() {
+ var rendered = null;
+ if (this.template)
+ rendered = openerp.web.qweb.render(this.template, {widget: this});
+ if (_.str.trim(rendered)) {
var elem = $(rendered);
this.$element.replaceWith(elem);
this.$element = elem;
}
- return this;
- },
- /**
- * Renders the widget using QWeb, `this.template` must be defined.
- * The context given to QWeb contains the "widget" key that references `this`.
- *
- * @param {Object} additional Additional context arguments to pass to the template.
- */
- render: function (additional) {
- if (this.template)
- return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
- return null;
- },
- /**
- * Method called after rendering. Mostly used to bind actions, perform asynchronous
- * calls, etc...
- *
- * By convention, the method should return a promise to inform the caller when
- * this widget has been initialized.
- *
- * @returns {jQuery.Deferred}
- */
- start: function() {
- return $.Deferred().done().promise();
- },
- /**
- * Destroys the current widget, also destroys all its children before destroying itself.
- */
- stop: function() {
- _.each(_.clone(this.widget_children), function(el) {
- el.stop();
- });
- if(this.$element != null) {
- this.$element.remove();
- }
- if (this.widget_parent && this.widget_parent.widget_children) {
- this.widget_parent.widget_children = _.without(this.widget_parent.widget_children, this);
- }
- this.widget_parent = null;
- this.widget_is_stopped = true;
},
/**
* Informs the action manager to do an action. This supposes that
@@ -1123,37 +1163,36 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W
* If that's not the case this method will simply return `false`.
*/
do_action: function(action, on_finished) {
- if (this.widget_parent) {
- return this.widget_parent.do_action(action, on_finished);
+ if (this.getParent()) {
+ return this.getParent().do_action(action, on_finished);
}
return false;
},
do_notify: function() {
- if (this.widget_parent) {
- return this.widget_parent.do_notify.apply(this,arguments);
+ if (this.getParent()) {
+ return this.getParent().do_notify.apply(this,arguments);
}
return false;
},
do_warn: function() {
- if (this.widget_parent) {
- return this.widget_parent.do_warn.apply(this,arguments);
+ if (this.getParent()) {
+ return this.getParent().do_warn.apply(this,arguments);
}
return false;
},
-
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
openerp.connection.rpc(url, data). then(function() {
- if (!self.widget_is_stopped)
+ if (!self.isDestroyed())
def.resolve.apply(def, arguments);
}, function() {
- if (!self.widget_is_stopped)
+ if (!self.isDestroyed())
def.reject.apply(def, arguments);
});
return def.promise();
}
-});
+}));
/**
* @deprecated use :class:`openerp.web.Widget`
@@ -1164,7 +1203,21 @@ openerp.web.OldWidget = openerp.web.Widget.extend({
this.element_id = element_id;
this.element_id = this.element_id || _.uniqueId('widget-');
var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : $(document.createElement(this.tag_name));
+ this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
+ },
+ renderElement: function() {
+ var rendered = this.render();
+ if (rendered) {
+ var elem = $(rendered);
+ this.$element.replaceWith(elem);
+ this.$element = elem;
+ }
+ return this;
+ },
+ render: function (additional) {
+ if (this.template)
+ return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
+ return null;
}
});
@@ -1196,7 +1249,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
this.parameters.grouping = py.eval(
- this.parameters.grouping).toJSON();
+ this.parameters.grouping);
}
},
add_module_translation: function(mod) {
@@ -1244,7 +1297,7 @@ openerp.web._lt = function (s) {
return {toString: function () { return openerp.web._t(s); }}
};
openerp.web.qweb = new QWeb2.Engine();
-openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1);
+openerp.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
openerp.web.qweb.default_dict = {
'_' : _,
'_t' : openerp.web._t
@@ -1279,6 +1332,23 @@ openerp.web.qweb.preprocess_node = function() {
}
};
+/**
+ * A small utility function to check if a class implements correctly an interface, assuming that
+ * interface is simply specified using a dictionary containing methods and attributes with the
+ * correct type. It only performs the check when in debug mode and the only effect of an invalid
+ * check is messages in the console.
+ */
+openerp.web.check_interface = function(_class, _interface) {
+ if (! openerp.web.check_interface.debug)
+ return;
+ for (var member in _interface) {
+ if ( (typeof _class.prototype[member] != typeof _interface[member]) ) {
+ console.error("class failed to implement interface member '" + member + "'");
+ }
+ }
+}
+openerp.web.check_interface.debug = ($.deparam($.param.querystring()).debug != undefined);
+
/** Jquery extentions */
$.Mutex = (function() {
function Mutex() {
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index b3ed5b08280..a29a9c5ad36 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -1012,18 +1012,25 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
: (v1 > v2) ? 1
: 0;
};
- records.sort(function (a, b) {
- return _.reduce(sort_fields, function (acc, field) {
- if (acc) { return acc; }
-
- var sign = 1;
- if (field[0] === '-') {
- sign = -1;
- field = field.slice(1);
- }
- return sign * compare(a[field], b[field]);
- }, 0);
- });
+ // Array.sort is not necessarily stable. We must be careful with this because
+ // sorting an array where all items are considered equal is a worst-case that
+ // will randomize the array with an unstable sort! Therefore we must avoid
+ // sorting if there are no sort_fields (i.e. all items are considered equal)
+ // See also: http://ecma262-5.com/ELS5_Section_15.htm#Section_15.4.4.11
+ // http://code.google.com/p/v8/issues/detail?id=90
+ if (sort_fields.length) {
+ records.sort(function (a, b) {
+ return _.reduce(sort_fields, function (acc, field) {
+ if (acc) { return acc; }
+ var sign = 1;
+ if (field[0] === '-') {
+ sign = -1;
+ field = field.slice(1);
+ }
+ return sign * compare(a[field], b[field]);
+ }, 0);
+ });
+ }
completion.resolve(records);
};
if(to_get.length > 0) {
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
index 83eea2cccbb..3054a7784c4 100644
--- a/addons/web/static/src/js/data_import.js
+++ b/addons/web/static/src/js/data_import.js
@@ -66,11 +66,11 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
this._super();
this.open({
buttons: [
- {text: _t("Close"), click: function() { self.stop(); }},
+ {text: _t("Close"), click: function() { self.destroy(); }},
{text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
],
close: function(event, ui) {
- self.stop();
+ self.destroy();
}
});
this.toggle_import_button(false);
@@ -151,7 +151,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
});
},
toggle_import_button: function (newstate) {
- this.$element.dialog('widget')
+ openerp.web.dialog(this.$element, 'widget')
.find('.oe-dialog-import-button')
.button('option', 'disabled', !newstate);
},
@@ -201,10 +201,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
return;
}
if (results['success']) {
- if (this.widget_parent.widget_parent.active_view == "list") {
- this.widget_parent.reload_content();
+ if (this.getParent().getParent().active_view == "list") {
+ this.getParent().reload_content();
}
- this.stop();
+ this.destroy();
return;
}
@@ -358,7 +358,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
}
return true;
},
- stop: function() {
+ destroy: function() {
this.$element.remove();
this._super();
}
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 4fb576e4a90..53863e63e97 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -148,7 +148,7 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
if (typeof(value) == "string")
value = openerp.web.auto_str_to_date(value);
return value.toString(normalize_format(l10n.time_format));
- case 'selection':
+ case 'selection': case 'statusbar':
// Each choice is [value, label]
if(_.isArray(value)) {
value = value[0]
@@ -336,7 +336,7 @@ openerp.web.format_cell = function (row_data, column, options) {
case 'progressbar':
return _.template(
'<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
- value: row_data[column.id].value
+ value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
});
}
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index b1ee33c4125..7263aa948e2 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -32,6 +32,8 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
this.hidden = !!hidden;
this.headless = this.hidden && !this.has_defaults;
+ this.filter_data = {};
+
this.ready = $.Deferred();
},
start: function() {
@@ -232,7 +234,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
context.add({"group_by": groupbys});
var dial_html = QWeb.render("SearchView.managed-filters.add");
var $dial = $(dial_html);
- $dial.dialog({
+ openerp.web.dialog($dial, {
modal: true,
title: _t("Filter Entry"),
buttons: [
@@ -263,10 +265,20 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
var filter = this.managed_filters[val];
this.do_clear(false).then(_.bind(function() {
select.val('get:' + val);
- var groupbys = _.map(filter.context.group_by.split(","), function(el) {
- return {"group_by": el};
- });
- this.on_search([filter.domain], [filter.context], groupbys);
+
+ 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('');
@@ -281,7 +293,7 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
selected_menu_id : menu.$element.find('a.active').data('menu')
}));
$dialog.find('input').val(this.fields_view.name);
- $dialog.dialog({
+ openerp.web.dialog($dialog, {
modal: true,
title: _t("Add to Dashboard"),
buttons: [
@@ -303,10 +315,10 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
});
self.rpc('/web/searchview/add_to_dashboard', {
menu_id: menu_id,
- action_id: self.widget_parent.action.id,
+ action_id: self.getParent().action.id,
context_to_save: context,
domain: domain,
- view_mode: self.widget_parent.active_view,
+ view_mode: self.getParent().active_view,
name: title
}, function(r) {
if (r === false) {
@@ -334,9 +346,6 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
if (this.headless && !this.has_defaults) {
return this.on_search([], [], []);
}
- // reset filters management
- var select = this.$element.find(".oe_search-view-filters-management");
- select.val("_filters");
if (e && e.preventDefault) { e.preventDefault(); }
@@ -380,6 +389,16 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
.map(function (filter) { return filter.get_context();})
.compact()
.value();
+
+ if (this.filter_data.contexts) {
+ contexts = this.filter_data.contexts.concat(contexts)
+ }
+ if (this.filter_data.domains) {
+ domains = this.filter_data.domains.concat(domains);
+ }
+ if (this.filter_data.groupbys) {
+ groupbys = this.filter_data.groupbys.concat(groupbys);
+ }
return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
},
/**
@@ -418,6 +437,9 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
* @param {Boolean} [reload_view=true]
*/
do_clear: function (reload_view) {
+ this.filter_data = {};
+ this.$element.find(".oe_search-view-filters-management").val('');
+
this.$element.find('.filter_label, .filter_icon').removeClass('enabled');
this.enabled_filters.splice(0);
var string = $('a.searchview_group_string');
@@ -552,7 +574,7 @@ openerp.web.search.Widget = openerp.web.OldWidget.extend( /** @lends openerp.web
* "Stops" the widgets. Called when the view destroys itself, this
* lets the widgets clean up after themselves.
*/
- stop: function () {
+ destroy: function () {
delete this.view;
this._super();
},
@@ -1114,7 +1136,7 @@ openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
return null;
}
- return _.reduce(this.widget_children,
+ return _.reduce(this.getChildren(),
function(mem, x) { return mem.concat(x.get_domain());}, []);
},
on_activate: function() {
@@ -1133,9 +1155,9 @@ openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
}
},
check_last_element: function() {
- _.each(this.widget_children, function(x) {x.set_last_group(false);});
- if (this.widget_children.length >= 1) {
- this.widget_children[this.widget_children.length - 1].set_last_group(true);
+ _.each(this.getChildren(), function(x) {x.set_last_group(false);});
+ if (this.getChildren().length >= 1) {
+ this.getChildren()[this.getChildren().length - 1].set_last_group(true);
}
}
});
@@ -1148,7 +1170,7 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
},
add_prop: function() {
var prop = new openerp.web.search.ExtendedSearchProposition(this, this.fields);
- var render = prop.render({'index': this.widget_children.length - 1});
+ var render = prop.render({'index': this.getChildren().length - 1});
this.$element.find('.searchview_extended_propositions_list').append(render);
prop.start();
},
@@ -1159,11 +1181,11 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
_this.add_prop();
});
this.$element.find('.searchview_extended_delete_group').click(function () {
- _this.stop();
+ _this.destroy();
});
},
get_domain: function() {
- var props = _(this.widget_children).chain().map(function(x) {
+ var props = _(this.getChildren()).chain().map(function(x) {
return x.get_proposition();
}).compact().value();
var choice = this.$element.find(".searchview_extended_group_choice").val();
@@ -1172,10 +1194,10 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({
_.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
props);
},
- stop: function() {
- var parent = this.widget_parent;
- if (this.widget_parent.widget_children.length == 1)
- this.widget_parent.hide();
+ destroy: function() {
+ var parent = this.getParent();
+ if (this.getParent().getChildren().length == 1)
+ this.getParent().hide();
this._super();
parent.check_last_element();
},
@@ -1210,16 +1232,16 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
_this.changed();
});
this.$element.find('.searchview_extended_delete_prop').click(function () {
- _this.stop();
+ _this.destroy();
});
},
- stop: function() {
+ destroy: function() {
var parent;
- if (this.widget_parent.widget_children.length == 1)
- parent = this.widget_parent;
+ if (this.getParent().getChildren().length == 1)
+ parent = this.getParent();
this._super();
if (parent)
- parent.stop();
+ parent.destroy();
},
changed: function() {
var nval = this.$element.find(".searchview_extended_prop_field").val();
@@ -1235,7 +1257,7 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/**
select_field: function(field) {
var self = this;
if(this.attrs.selected != null) {
- this.value.stop();
+ this.value.destroy();
this.value = null;
this.$element.find('.searchview_extended_prop_op').html('');
}
diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js
index 0258509f172..2c0dc3d70b4 100644
--- a/addons/web/static/src/js/view_editor.js
+++ b/addons/web/static/src/js/view_editor.js
@@ -1002,10 +1002,10 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
$.when(action_manager.do_action(action)).then(function() {
var controller = action_manager.dialog_viewmanager.views['form'].controller;
controller.on_button_cancel.add_last(function(){
- action_manager.stop()
+ action_manager.destroy()
});
controller.do_save.add_last(function(){
- action_manager.stop();
+ action_manager.destroy();
var value =controller.fields.name.value;
self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value",value).text(value));
_.detect(self.add_widget,function(widget){
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 2a678bee393..7808c646a43 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -74,13 +74,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}, this.on_loaded);
}
},
- stop: function() {
+ destroy: function() {
if (this.sidebar) {
- this.sidebar.attachments.stop();
- this.sidebar.stop();
+ this.sidebar.attachments.destroy();
+ this.sidebar.destroy();
}
_.each(this.widgets, function(w) {
- w.stop();
+ w.destroy();
});
this._super();
},
@@ -90,17 +90,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
},
on_loaded: function(data) {
var self = this;
- if (data) {
- this.fields_order = [];
- this.fields_view = data;
- var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
-
- this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this });
+ if (!data) {
+ throw "No data provided.";
+ }
+ if (this.root_frame) {
+ throw "Form view does not support multiple calls to on_loaded";
}
+ this.fields_order = [];
+ this.fields_view = data;
+
+ this.rendered = QWeb.render(this.form_template, {'widget': this});
this.$element.html(this.rendered);
- _.each(this.widgets, function(w) {
- w.start();
- });
+
+ this.root_frame = instanciate_widget(this.registry.get_object('frame'), this, this.fields_view.arch);
+ var to_append = $(".oe_form_header", this.$element);
+ this.root_frame.appendTo(to_append.length > 0 ? to_append : this.$element);
+ this.root_frame.$element.children().unwrap();
+
this.$form_header = this.$element.find('.oe_form_header:first');
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
var action = $(this).data('pager-action');
@@ -205,7 +211,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
if (self.sidebar) {
self.sidebar.attachments.do_update();
}
- if (self.default_focus_field && !self.embedded_view) {
+ if (self.default_focus_field) {
self.default_focus_field.focus();
}
if (record.id) {
@@ -418,7 +424,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.on_form_changed();
}
if (!_.isEmpty(result.warning)) {
- $(QWeb.render("CrashManagerWarning", result.warning)).dialog({
+ openerp.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), {
modal: true,
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -879,7 +885,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
};
openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
- template: 'Widget',
+ form_template: 'Widget',
/**
* @constructs openerp.web.form.Widget
* @extends openerp.web.OldWidget
@@ -894,13 +900,9 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
this.type = this.type || node.tag;
this.element_name = this.element_name || this.type;
- this.element_class = [
- 'formview', this.view.view_id, this.element_name,
- this.view.widgets_counter++].join("_");
this._super(view);
- this.view.widgets[this.element_class] = this;
this.children = node.children;
this.colspan = parseInt(node.attrs.colspan || 1, 10);
this.decrease_max_width = 0;
@@ -919,14 +921,9 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
this.align = 'center';
}
-
this.width = this.node.attrs.width;
},
- start: function() {
- this.$element = this.view.$element.find(
- '.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
- },
- stop: function() {
+ destroy: function() {
this._super.apply(this, arguments);
$.fn.tipsy.clear();
},
@@ -939,9 +936,8 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
update_dom: function() {
this.$element.toggle(!this.invisible);
},
- render: function() {
- var template = this.template;
- return QWeb.render(template, { "widget": this });
+ renderElement: function() {
+ this.$element.html(QWeb.render(this.form_template, { "widget": this }));
},
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
@@ -951,7 +947,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
delayOut: 0,
fade: true,
title: function() {
- var template = widget.template + '.tooltip';
+ var template = widget.form_template + '.tooltip';
if (!QWeb.has_template(template)) {
template = 'WidgetLabel.tooltip';
}
@@ -1016,7 +1012,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
});
openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
- template: 'WidgetFrame',
+ form_template: 'WidgetFrame',
init: function(view, node) {
this._super(view, node);
this.columns = parseInt(node.attrs.col || 4, 10);
@@ -1034,6 +1030,23 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
}
this.set_row_cells_with(this.table[this.table.length - 1]);
},
+ renderElement: function() {
+ this._super();
+ var self = this;
+ _.each(this.table, function(row) {
+ _.each(row, function(td) {
+ td.$element = self.$element.find('.' + td.element_class);
+ td.renderElement();
+ });
+ });
+ },
+ start: function() {
+ _.each(this.table, function(row) {
+ _.each(row, function(td) {
+ td.start();
+ });
+ });
+ },
add_row: function(){
if (this.table.length) {
this.set_row_cells_with(this.table[this.table.length - 1]);
@@ -1081,14 +1094,14 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
node.attrs.nolabel = '1';
}
}
- var widget = new (this.view.registry.get_any(
- [node.attrs.widget, type.type, node.tag])) (this.view, node);
+ var widget = instanciate_widget(this.view.registry.get_any(
+ [node.attrs.widget, type.type, node.tag]), this.view, node);
if (node.tag == 'field') {
if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
this.view.default_focus_field = widget;
}
if (node.attrs.nolabel != '1') {
- var label = new (this.view.registry.get_object('label')) (this.view, node);
+ var label = instanciate_widget(this.view.registry.get_object('label'), this.view, node);
label["for"] = widget;
this.add_widget(label, widget.colspan + 1);
}
@@ -1110,23 +1123,31 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
});
openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetGroup'
+ form_template: 'WidgetGroup'
}),
openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
- template: 'WidgetNotebook',
+ form_template: 'WidgetNotebook',
init: function(view, node) {
this._super(view, node);
this.pages = [];
for (var i = 0; i < node.children.length; i++) {
var n = node.children[i];
if (n.tag == "page") {
- var page = new (this.view.registry.get_object('notebookpage'))(
+ var page = instanciate_widget(this.view.registry.get_object('notebookpage'),
this.view, n, this, this.pages.length);
this.pages.push(page);
}
}
},
+ renderElement: function() {
+ this._super();
+ var self = this;
+ _.each(this.pages, function(page) {
+ page.$element = self.$element.find('.' + page.element_class);
+ page.renderElement();
+ });
+ },
start: function() {
var self = this;
this._super.apply(this, arguments);
@@ -1146,6 +1167,9 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
gravity: 's'
});
}
+ _.each(this.pages, function(page) {
+ page.start();
+ });
},
do_select_first_visible_tab: function() {
for (var i = 0; i < this.pages.length; i++) {
@@ -1159,7 +1183,7 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
});
openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetNotebookPage',
+ form_template: 'WidgetNotebookPage',
init: function(view, node, notebook, index) {
this.notebook = notebook;
this.index = index;
@@ -1181,7 +1205,7 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
});
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
- template: 'WidgetSeparator',
+ form_template: 'WidgetSeparator',
init: function(view, node) {
this._super(view, node);
this.orientation = node.attrs.orientation || 'horizontal';
@@ -1193,7 +1217,7 @@ openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
});
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
- template: 'WidgetButton',
+ form_template: 'WidgetButton',
init: function(view, node) {
this._super(view, node);
this.force_disabled = false;
@@ -1227,7 +1251,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
var exec_action = function() {
if (self.node.attrs.confirm) {
var def = $.Deferred();
- var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
+ var dialog = openerp.web.dialog($('<div>' + self.node.attrs.confirm + '</div>'), {
title: _t('Confirm'),
modal: true,
buttons: [
@@ -1283,7 +1307,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
});
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
- template: 'WidgetLabel',
+ form_template: 'WidgetLabel',
init: function(view, node) {
this.element_name = 'label_' + node.attrs.name;
@@ -1295,7 +1319,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
}
if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
- this.template = "WidgetParagraph";
+ this.form_template = "WidgetParagraph";
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
// Widgets default to right-aligned, but paragraph defaults to
// left-aligned
@@ -1311,12 +1335,15 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
this.nowrap = true;
}
},
- render: function () {
+ renderElement: function() {
+ var rendered;
if (this['for'] && this.type !== 'label') {
- return QWeb.render(this.template, {widget: this['for']});
+ rendered = QWeb.render(this.form_template, {widget: this['for']});
+ } else {
+ // Actual label widgets should not have a false and have type label
+ rendered = QWeb.render(this.form_template, {widget: this});
}
- // Actual label widgets should not have a false and have type label
- return QWeb.render(this.template, {widget: this});
+ this.$element.html(rendered);
},
start: function() {
this._super();
@@ -1332,6 +1359,58 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
}
});
+/**
+ * Interface to be implemented by fields.
+ *
+ * Novajs Attributes:
+ * - ...
+ *
+ * Novajs Events:
+ * - ...
+ *
+ */
+openerp.web.form.FieldInterface = {
+ /**
+ * Called by the form view to indicate the value of the field.
+ *
+ * set_value() may return an object that can be passed to $.when() that represents the moment when
+ * the field has finished all operations necessary before the user can effectively use the widget.
+ *
+ * Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
+ * regardless of any asynchronous operation currently running and the status of any promise that a
+ * previous call to set_value() could have returned.
+ *
+ * set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
+ * osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
+ * also be able to handle any other format commonly used in the _defaults key on the models in the addons
+ * as well as any format commonly returned in a on_change. It must be able to autodetect those formats as
+ * no information is ever given to know which format is used.
+ */
+ set_value: function(value) {},
+ /**
+ * Get the current value of the widget.
+ *
+ * Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
+ * the OpenERP server, although it is not assumed to respect the constraints applied to the field.
+ * For example if the field is marqued as "required", a call to get_value() can return false.
+ *
+ * get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
+ * return a defaut value according to the type of field.
+ *
+ * This method is always assumed to perform synchronously, it can not return a promise.
+ *
+ * If there was no user interaction to modify the value of the field, it is always assumed that
+ * get_value() return the same semantic value than the one passed in the last call to set_value(),
+ * altough the syntax can be different. This can be the case for type of fields that have a different
+ * syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
+ */
+ get_value: function() {},
+};
+
+/**
+ * Abstract class for classes implementing FieldInterface. Should be renamed to AbstractField some
+ * day.
+ */
openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
/**
* @constructs openerp.web.form.Field
@@ -1448,7 +1527,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
});
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
- template: 'FieldChar',
+ form_template: 'FieldChar',
init: function (view, node) {
this._super(view, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
@@ -1493,7 +1572,7 @@ openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
});
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
- template: 'FieldEmail',
+ form_template: 'FieldEmail',
start: function() {
this._super.apply(this, arguments);
this.$element.find('button').click(this.on_button_clicked);
@@ -1508,7 +1587,7 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
});
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
- template: 'FieldUrl',
+ form_template: 'FieldUrl',
start: function() {
this._super.apply(this, arguments);
this.$element.find('button').click(this.on_button_clicked);
@@ -1526,16 +1605,11 @@ openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
init: function (view, node) {
this._super(view, node);
if (node.attrs.digits) {
- this.parse_digits(node.attrs.digits);
+ this.digits = py.eval(node.attrs.digits).toJSON();
} else {
this.digits = view.fields_view.fields[node.attrs.name].digits;
}
},
- parse_digits: function (digits_attr) {
- // could use a Python parser instead.
- var match = /^\s*[\(\[](\d+),\s*(\d+)/.exec(digits_attr);
- return [parseInt(match[1], 10), parseInt(match[2], 10)];
- },
set_value: function(value) {
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
@@ -1631,7 +1705,7 @@ openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
});
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
- template: "EmptyComponent",
+ form_template: "EmptyComponent",
build_widget: function() {
return new openerp.web.DateTimeWidget(this);
},
@@ -1668,7 +1742,7 @@ openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
});
openerp.web.form.FieldText = openerp.web.form.Field.extend({
- template: 'FieldText',
+ form_template: 'FieldText',
start: function() {
this._super.apply(this, arguments);
this.$element.find('textarea').change(this.on_ui_change);
@@ -1729,7 +1803,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
});
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
- template: 'FieldBoolean',
+ form_template: 'FieldBoolean',
start: function() {
var self = this;
this._super.apply(this, arguments);
@@ -1753,7 +1827,7 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
});
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
- template: 'FieldProgressBar',
+ form_template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
this.$element.find('div').progressbar({
@@ -1777,7 +1851,7 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
});
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
- template: 'FieldSelection',
+ form_template: 'FieldSelection',
init: function(view, node) {
var self = this;
this._super(view, node);
@@ -1883,7 +1957,7 @@ openerp.web.form.dialog = function(content, options) {
};
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
- template: 'FieldMany2One',
+ form_template: 'FieldMany2One',
init: function(view, node) {
this._super(view, node);
this.limit = 7;
@@ -1912,9 +1986,13 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
var bindings = {};
bindings[self.cm_id + "_search"] = function() {
+ if (self.readonly)
+ return;
self._search_create_popup("search");
};
bindings[self.cm_id + "_create"] = function() {
+ if (self.readonly)
+ return;
self._search_create_popup("form");
};
bindings[self.cm_id + "_open"] = function() {
@@ -2019,6 +2097,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
minLength: 0,
delay: 0
});
+ this.$input.autocomplete("widget").addClass("openerp");
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
if (e.which === 13) {
@@ -2201,6 +2280,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
this.$input.prop('readonly', this.readonly);
}
});
+openerp.web.check_interface(openerp.web.form.FieldMany2One, openerp.web.form.FieldInterface);
/*
# Values: (0, 0, { fields }) create
@@ -2248,7 +2328,7 @@ var commands = {
}
};
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
- template: 'FieldOne2Many',
+ form_template: 'FieldOne2Many',
multi_selection: false,
init: function(view, node) {
this._super(view, node);
@@ -2522,7 +2602,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
this.previous_readonly = this.readonly;
if (this.viewmanager) {
this.is_loaded = this.is_loaded.pipe(function() {
- self.viewmanager.stop();
+ self.viewmanager.destroy();
return $.when(self.load_views()).then(function() {
self.reload_current_view();
});
@@ -2622,7 +2702,7 @@ openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
});
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
- template: 'FieldMany2Many',
+ form_template: 'FieldMany2Many',
multi_selection: false,
init: function(view, node) {
this._super(view, node);
@@ -2702,7 +2782,7 @@ openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
this.previous_readonly = this.readonly;
if (this.list_view) {
this.is_loaded = this.is_loaded.pipe(function() {
- self.list_view.stop();
+ self.list_view.destroy();
return $.when(self.load_view()).then(function() {
self.reload_content();
});
@@ -2750,7 +2830,7 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open
var pop = new openerp.web.form.FormOpenPopup(this);
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
title: _t("Open: ") + this.name,
- readonly: this.widget_parent.is_readonly()
+ readonly: this.getParent().is_readonly()
});
pop.on_write_completed.add_last(function() {
self.reload_content();
@@ -2787,7 +2867,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
}, read_function: null});
this.initial_ids = this.options.initial_ids;
this.created_elements = [];
- this.render_element();
+ this.renderElement();
openerp.web.form.dialog(this.$element, {
close: function() {
self.check_exit();
@@ -2831,10 +2911,14 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
this.new_object();
}
},
+ stop: function () {
+ this.$element.dialog('close');
+ this._super();
+ },
setup_search_view: function(search_defaults) {
var self = this;
if (this.searchview) {
- this.searchview.stop();
+ this.searchview.destroy();
}
this.searchview = new openerp.web.SearchView(this,
this.dataset, false, search_defaults);
@@ -2864,7 +2948,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
$cbutton.click(function() {
- self.stop();
+ self.destroy();
});
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
if(self.options.disable_multiple_selection) {
@@ -2872,7 +2956,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
}
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
- self.stop();
+ self.destroy();
});
});
});
@@ -2956,7 +3040,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
if (this.created_elements.length > 0) {
this.on_select_elements(this.created_elements);
}
- this.stop();
+ this.destroy();
},
on_default_get: function(res) {}
});
@@ -2967,7 +3051,7 @@ openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
},
select_record: function(index) {
this.popup.on_select_elements([this.dataset.ids[index]]);
- this.popup.stop();
+ this.popup.destroy();
},
do_select: function(ids, records) {
this._super(ids, records);
@@ -2996,8 +3080,8 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
this.row_id = row_id;
this.context = context || {};
this.options = _.defaults(options || {}, {"auto_write": true});
- this.render_element();
- this.$element.dialog({
+ this.renderElement();
+ openerp.web.dialog(this.$element, {
title: options.title || '',
modal: true,
width: 960,
@@ -3043,12 +3127,12 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
$nbutton.click(function() {
self.view_form.do_save().then(function() {
- self.stop();
+ self.destroy();
});
});
var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
$cbutton.click(function() {
- self.stop();
+ self.destroy();
});
if (self.options.readonly) {
$nbutton.hide();
@@ -3071,7 +3155,7 @@ openerp.web.form.FormOpenDataset = openerp.web.ProxyDataSet.extend({
});
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
- template: 'FieldReference',
+ form_template: 'FieldReference',
init: function(view, node) {
this._super(view, node);
this.fields_view = {
@@ -3086,7 +3170,7 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
};
this.get_fields_values = view.get_fields_values;
this.get_selected_ids = view.get_selected_ids;
- this.do_onchange = this.on_form_changed = this.on_nop;
+ this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
this.dataset = this.view.dataset;
this.widgets_counter = 0;
this.view_id = 'reference_' + _.uniqueId();
@@ -3117,7 +3201,11 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
},
start: function() {
this._super();
+ this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
+ this.selection.renderElement();
this.selection.start();
+ this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
+ this.m2o.renderElement();
this.m2o.start();
},
is_valid: function() {
@@ -3226,7 +3314,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
});
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
- template: 'FieldBinaryFile',
+ form_template: 'FieldBinaryFile',
update_dom: function() {
this._super.apply(this, arguments);
this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
@@ -3264,7 +3352,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
});
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
- template: 'FieldBinaryImage',
+ form_template: 'FieldBinaryImage',
start: function() {
this._super.apply(this, arguments);
this.$image = this.$element.find('img.oe-binary-image');
@@ -3276,8 +3364,14 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
set_value: function(value) {
this._super.apply(this, arguments);
this.set_image_maxwidth();
- var url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+
+ var url;
+ if (value && value.substr(0, 10).indexOf(' ') == -1) {
+ url = 'data:image/png;base64,' + this.value;
+ } else {
+ url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
+ this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ }
this.$image.attr('src', url);
},
set_image_maxwidth: function() {
@@ -3299,7 +3393,7 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
});
openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
- template: "EmptyComponent",
+ form_template: "EmptyComponent",
start: function() {
this._super();
this.selected_value = null;
@@ -3364,10 +3458,11 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
});
openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
- render: function () {
+ renderElement: function () {
var $root = $('<div class="oe_form_html_view">');
this.render_children(this, $root);
- return $root.html();
+ var rendered = $root.html();
+ this.$element.html(rendered);
},
render_children: function (object, $into) {
var self = this,
@@ -3376,10 +3471,9 @@ openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
if (typeof child === 'string') {
$into.text(child);
} else if (child.tag === 'field') {
- $into.append(
- new (self.view.registry.get_object('frame'))(
- self.view, {tag: 'ueule', attrs: {}, children: [child] })
- .render());
+ var widget = instanciate_widget(self.view.registry.get_object('frame'),
+ self.view, {tag: 'ueule', attrs: {}, children: [child] });
+ widget.appendTo($into);
} else {
var $child = $(document.createElement(child.tag))
.attr(child.attrs)
@@ -3426,6 +3520,14 @@ openerp.web.form.widgets = new openerp.web.Registry({
'html': 'openerp.web.form.WidgetHtml'
});
+var instanciate_widget = function(claz, view, node, o1, o2) {
+ var widget = new (claz)(view, node, o1, o2);
+ widget.element_class = (['formview', view.view_id, widget.element_name,
+ view.widgets_counter++].join("_")).replace(/[^\r\n\f0-9A-Za-z_-]/g, "_");
+ view.widgets[widget.element_class] = widget;
+ return widget;
+}
+
};
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 8d8e342370e..e8fff450c6e 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -96,7 +96,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
if (this._limit === undefined) {
this._limit = (this.options.limit
|| this.defaults.limit
- || (this.widget_parent.action || {}).limit
+ || (this.getParent().action || {}).limit
|| 80);
}
return this._limit;
@@ -158,7 +158,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
var pair = this.colors[i],
color = pair[0],
expression = pair[1];
- if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
+ if (py.evaluate(expression, context).toJSON()) {
return 'color: ' + color + ';';
}
// TODO: handle evaluation errors
@@ -291,7 +291,10 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*/
configure_pager: function (dataset) {
this.dataset.ids = dataset.ids;
- this.dataset._length = dataset._length;
+ // Not exactly clean
+ if (dataset._length) {
+ this.dataset._length = dataset._length;
+ }
var limit = this.limit(),
total = dataset.size(),
@@ -360,7 +363,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
_(this.fields_view.arch.children).map(field_to_column));
if (grouped) {
this.columns.unshift({
- id: '_group', tag: '', string: "Group", meta: true,
+ id: '_group', tag: '', string: _t("Group"), meta: true,
modifiers_for: function () { return {}; }
}, {
id: '_count', tag: '', string: '#', meta: true,
@@ -541,19 +544,17 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {Array} records selected record values
*/
do_select: function (ids, records) {
- this.$element.find('.oe-list-delete')
- .attr('disabled', !ids.length);
- if (this.sidebar) {
- if (ids.length) {
- this.sidebar.do_unfold();
- } else {
- this.sidebar.do_fold();
- }
- }
- if (!records.length) {
+ this.$element.find('.oe-list-delete').attr('disabled', !ids.length);
+ if (!ids.length) {
+ this.dataset.index = 0;
+ if (this.sidebar) { this.sidebar.do_fold(); }
this.compute_aggregates();
return;
}
+
+ this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
+ if (this.sidebar) { this.sidebar.do_unfold(); }
+
this.compute_aggregates(_(records).map(function (record) {
return {count: 1, values: record};
}));
@@ -1260,9 +1261,11 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
if (column.meta) {
// do not do anything
} else if (column.id in group.aggregates) {
- var value = group.aggregates[column.id];
+ var r = {};
+ r[column.id] = {value: group.aggregates[column.id]};
$('<td class="oe-number">')
- .html(openerp.web.format_value(value, column))
+ .html(openerp.web.format_cell(
+ r, column, {process_modifiers: false}))
.appendTo($row);
} else {
$row.append('<td>');
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index c7222455889..b8699bcc006 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -139,7 +139,7 @@ openerp.web.list_editable = function (openerp) {
}
cancelled.then(function () {
self.view.unpad_columns();
- self.edition_form.stop();
+ self.edition_form.destroy();
self.edition_form.$element.remove();
delete self.edition_form;
delete self.edition_id;
@@ -235,26 +235,16 @@ openerp.web.list_editable = function (openerp) {
}
self.edition = true;
self.edition_id = record_id;
- self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), {
- form_template: 'ListView.row.form',
- registry: openerp.web.list.form.widgets,
- $element: $new_row
- });
- // HA HA
- self.edition_form.appendTo();
+ self.edition_form = new openerp.web.ListEditableFormView(self.view, self.dataset, false);
+ self.edition_form.$element = $new_row;
+ self.edition_form.editable_list = self;
+ // HO HO
+ // empty
$.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
// put in $.when just in case FormView.on_loaded becomes asynchronous
$new_row.find('> td')
- .addClass('oe-field-cell')
- .removeAttr('width')
.end()
.find('td:last').removeClass('oe-field-cell').end();
- if (self.options.selectable) {
- $new_row.prepend('<th>');
- }
- if (self.options.isClarkGable) {
- $new_row.prepend('<th>');
- }
// pad in case of groupby
_(self.columns).each(function (column) {
if (column.meta) {
@@ -383,7 +373,7 @@ openerp.web.list_editable = function (openerp) {
openerp.web.list.form = {};
}
openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
- template: 'ListView.row.frame'
+ form_template: 'ListView.row.frame'
});
var form_widgets = openerp.web.form.widgets;
openerp.web.list.form.widgets = form_widgets.extend({
@@ -418,9 +408,10 @@ openerp.web.list_editable = function (openerp) {
});
openerp.web.ListEditableFormView = openerp.web.FormView.extend({
- init_view: function() {},
- _render_and_insert: function () {
- return this.start();
- }
+ form_template: 'ListView.row.form',
+ init: function() {
+ this._super.apply(this, arguments);
+ this.registry = openerp.web.list.form.widgets;
+ },
});
};
diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js
index 33eabc63b3c..e3367f56b4a 100644
--- a/addons/web/static/src/js/view_page.js
+++ b/addons/web/static/src/js/view_page.js
@@ -68,13 +68,13 @@ openerp.web.page = function (openerp) {
openerp.web.page = {};
openerp.web.page.WidgetFrameReadonly = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetFrame.readonly'
+ form_template: 'WidgetFrame.readonly'
});
openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({
});
openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({
- template: 'FieldChar.readonly',
+ form_template: 'FieldChar.readonly',
init: function(view, node) {
this._super(view, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
@@ -89,13 +89,27 @@ openerp.web.page = function (openerp) {
return show_value;
}
});
+ openerp.web.page.FieldFloatReadonly = openerp.web.page.FieldCharReadonly.extend({
+ init: function (view, node) {
+ this._super(view, node);
+ if (node.attrs.digits) {
+ this.digits = py.eval(node.attrs.digits).toJSON();
+ } else {
+ this.digits = view.fields_view.fields[node.attrs.name].digits;
+ }
+ }
+ });
openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({
- template: 'FieldURI.readonly',
+ form_template: 'FieldURI.readonly',
scheme: null,
format_value: function (value) {
return value;
},
set_value: function (value) {
+ if (!value) {
+ this.$element.find('a').text('').attr('href', '#');
+ return;
+ }
this.$element.find('a')
.attr('href', this.scheme + ':' + value)
.text(this.format_value(value));
@@ -106,6 +120,10 @@ openerp.web.page = function (openerp) {
});
openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({
set_value: function (value) {
+ if (!value) {
+ this.$element.find('a').text('').attr('href', '#');
+ return;
+ }
var s = /(\w+):(.+)/.exec(value);
if (!s) {
value = "http://" + value;
@@ -120,7 +138,7 @@ openerp.web.page = function (openerp) {
}
});
openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({
- template: 'FieldChar.readonly',
+ form_template: 'FieldChar.readonly',
init: function(view, node) {
// lifted straight from r/w version
var self = this;
@@ -225,7 +243,7 @@ openerp.web.page = function (openerp) {
}
});
openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({
- template: 'FieldURI.readonly',
+ form_template: 'FieldURI.readonly',
start: function() {
this._super.apply(this, arguments);
var self = this;
@@ -261,7 +279,7 @@ openerp.web.page = function (openerp) {
'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly',
'reference': 'openerp.web.page.FieldReferenceReadonly',
'boolean': 'openerp.web.page.FieldBooleanReadonly',
- 'float': 'openerp.web.page.FieldCharReadonly',
+ 'float': 'openerp.web.page.FieldFloatReadonly',
'integer': 'openerp.web.page.FieldCharReadonly',
'float_time': 'openerp.web.page.FieldCharReadonly',
'binary': 'openerp.web.page.FieldBinaryFileReadonly',
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index dea53793ffb..6039e4a0822 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -146,7 +146,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
var pair = this.colors[i],
color = pair[0],
expression = pair[1];
- if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
+ if (py.evaluate(expression, context).toJSON()) {
return 'color: ' + color + ';';
}
// TODO: handle evaluation errors
@@ -224,21 +224,25 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
active_model: self.dataset.model,
active_id: id,
active_ids: [id]};
- this.rpc('/web/treeview/action', {
+ return this.rpc('/web/treeview/action', {
id: id,
model: this.dataset.model,
context: new openerp.web.CompoundContext(
this.dataset.get_context(), local_context)
- }, function (actions) {
+ }).pipe(function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
var c = new openerp.web.CompoundContext(local_context);
if (action.context) {
c.add(action.context);
}
- action.context = c;
- self.do_action(action);
- });
+ return self.rpc('/web/session/eval_domain_and_context', {
+ contexts: [c], domains: []
+ }).pipe(function (res) {
+ action.context = res.context;
+ return self.do_action(action);
+ }, null);
+ }, null);
},
// show & hide the contents
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 56997190bc1..5be0fad74f0 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -30,31 +30,31 @@ session.web.ActionManager = session.web.OldWidget.extend({
},
dialog_stop: function () {
if (this.dialog) {
- this.dialog_viewmanager.stop();
+ this.dialog_viewmanager.destroy();
this.dialog_viewmanager = null;
- this.dialog.stop();
+ this.dialog.destroy();
this.dialog = null;
}
},
content_stop: function () {
if (this.inner_viewmanager) {
- this.inner_viewmanager.stop();
+ this.inner_viewmanager.destroy();
this.inner_viewmanager = null;
}
if (this.client_widget) {
- this.client_widget.stop();
+ this.client_widget.destroy();
this.client_widget = null;
}
},
do_push_state: function(state) {
- if (this.widget_parent && this.widget_parent.do_push_state) {
+ if (this.getParent() && this.getParent().do_push_state) {
if (this.inner_action) {
state['model'] = this.inner_action.res_model;
if (this.inner_action.id) {
state['action_id'] = this.inner_action.id;
}
}
- this.widget_parent.do_push_state(state);
+ this.getParent().do_push_state(state);
}
},
do_load_state: function(state, warm) {
@@ -65,6 +65,9 @@ session.web.ActionManager = session.web.OldWidget.extend({
if (run_action) {
this.null_action();
action_loaded = this.do_action(state.action_id);
+ session.webclient.menu.has_been_loaded.then(function() {
+ session.webclient.menu.open_action(state.action_id);
+ });
}
} else if (state.model && state.id) {
// TODO handle context & domain ?
@@ -142,7 +145,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
if(on_close)
this.dialog.on_close.add(on_close);
} else {
- this.dialog_viewmanager.stop();
+ this.dialog_viewmanager.destroy();
}
this.dialog.dialog_title = action.name;
this.dialog_viewmanager = new session.web.ViewManagerAction(this, action);
@@ -150,7 +153,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
this.dialog.open();
} else {
if(action.menu_id) {
- return this.widget_parent.do_action(action, function () {
+ return this.getParent().do_action(action, function () {
session.webclient.menu.open_menu(action.menu_id);
});
}
@@ -180,7 +183,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
this.content_stop();
this.dialog_stop();
var ClientWidget = session.web.client_actions.get_object(action.tag);
- (this.client_widget = new ClientWidget(this, action.params)).appendTo(this);
+ (this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element);
},
ir_actions_report_xml: function(action, on_closed) {
var self = this;
@@ -209,7 +212,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
window.open(action.url, action.target === 'self' ? '_self' : '_blank');
},
ir_ui_menu: function (action) {
- this.widget_parent.do_action(action);
+ this.getParent().do_action(action);
}
});
@@ -385,7 +388,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
setup_search_view: function(view_id, search_defaults) {
var self = this;
if (this.searchview) {
- this.searchview.stop();
+ this.searchview.destroy();
}
this.searchview = new session.web.SearchView(
this, this.dataset,
@@ -408,6 +411,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
var groupby = results.group_by.length
? results.group_by
: action_context.group_by;
+ if (_.isString(groupby)) {
+ groupby = [groupby];
+ }
controller.do_search(results.domain, results.context, groupby || []);
});
},
@@ -680,9 +686,9 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
});
},
do_push_state: function(state) {
- if (this.widget_parent && this.widget_parent.do_push_state) {
+ if (this.getParent() && this.getParent().do_push_state) {
state["view_type"] = this.active_view;
- this.widget_parent.do_push_state(state);
+ this.getParent().do_push_state(state);
}
},
do_load_state: function(state, warm) {
@@ -702,7 +708,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
},
shortcut_check : function(view) {
var self = this;
- var grandparent = this.widget_parent && this.widget_parent.widget_parent;
+ var grandparent = this.getParent() && this.getParent().getParent();
// display shortcuts if on the first view for the action
var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle');
if (!this.action.name ||
@@ -796,8 +802,8 @@ session.web.Sidebar = session.web.OldWidget.extend({
},
add_default_sections: function() {
var self = this,
- view = this.widget_parent,
- view_manager = view.widget_parent,
+ view = this.getParent(),
+ view_manager = view.getParent(),
action = view_manager.action;
if (this.session.uid === 1) {
this.add_section(_t('Customize'), 'customize');
@@ -912,11 +918,11 @@ session.web.Sidebar = session.web.OldWidget.extend({
},
on_item_action_clicked: function(item) {
var self = this;
- self.widget_parent.sidebar_context().then(function (context) {
- var ids = self.widget_parent.get_selected_ids();
+ self.getParent().sidebar_context().then(function (context) {
+ var ids = self.getParent().get_selected_ids();
if (ids.length == 0) {
//TODO: make prettier warning?
- $("<div />").text(_t("You must choose at least one record.")).dialog({
+ openerp.web.dialog($("<div />").text(_t("You must choose at least one record.")), {
title: _t("Warning"),
modal: true
});
@@ -925,7 +931,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
var additional_context = _.extend({
active_id: ids[0],
active_ids: ids,
- active_model: self.widget_parent.dataset.model
+ active_model: self.getParent().dataset.model
}, context);
self.rpc("/web/action/load", {
action_id: item.action.id,
@@ -937,7 +943,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
result.result.flags.new_window = true;
self.do_action(result.result, function () {
// reload view
- self.widget_parent.reload();
+ self.getParent().reload();
});
});
});
@@ -1111,8 +1117,8 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
var self = this;
var result_handler = function () {
if (on_closed) { on_closed.apply(null, arguments); }
- if (self.widget_parent && self.widget_parent.on_action_executed) {
- return self.widget_parent.on_action_executed.apply(null, arguments);
+ if (self.getParent() && self.getParent().on_action_executed) {
+ return self.getParent().on_action_executed.apply(null, arguments);
}
};
var context = new session.web.CompoundContext(dataset.get_context(), action_data.context || {});
@@ -1184,8 +1190,8 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
this.$element.hide();
},
do_push_state: function(state) {
- if (this.widget_parent && this.widget_parent.do_push_state) {
- this.widget_parent.do_push_state(state);
+ if (this.getParent() && this.getParent().do_push_state) {
+ this.getParent().do_push_state(state);
}
},
do_load_state: function(state, warm) {