summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/boot.js24
-rw-r--r--addons/web/static/src/js/chrome.js8
-rw-r--r--addons/web/static/src/js/core.js (renamed from addons/web/static/src/js/coresetup.js)348
-rw-r--r--addons/web/static/src/js/corelib.js408
-rw-r--r--addons/web/static/src/js/data.js74
-rw-r--r--addons/web/static/src/js/data_export.js9
-rw-r--r--addons/web/static/src/js/dates.js8
-rw-r--r--addons/web/static/src/js/formats.js8
-rw-r--r--addons/web/static/src/js/openerpframework.js521
-rw-r--r--addons/web/static/src/js/pyeval.js7
-rw-r--r--addons/web/static/src/js/search.js9
-rw-r--r--addons/web/static/src/js/test_support.js5
-rw-r--r--addons/web/static/src/js/testing.js28
-rw-r--r--addons/web/static/src/js/view_form.js6
-rw-r--r--addons/web/static/src/js/view_list.js7
-rw-r--r--addons/web/static/src/js/view_list_editable.js7
-rw-r--r--addons/web/static/src/js/view_tree.js7
-rw-r--r--addons/web/static/src/js/views.js7
18 files changed, 854 insertions, 637 deletions
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index 87115fd30ea..cd58c3bb10a 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -7,6 +7,9 @@
* @namespace openerp
*/
(function() {
+ // copy everything in the openerp namespace to openerp.web
+ openerp.web = _.clone(openerp);
+
var inited = false;
_.extend(openerp, {
@@ -30,13 +33,11 @@
if (modules === null) {
modules = [];
}
+ modules = _.without(modules, "web");
if (inited)
throw new Error("OpenERP was already inited");
inited = true;
- init_web_modules();
for(var i=0; i < modules.length; i++) {
- if (modules[i] === "web")
- continue;
var fct = openerp[modules[i]];
if (typeof(fct) === "function") {
openerp[modules[i]] = {};
@@ -50,22 +51,5 @@
return openerp;
}
});
-
- /*---------------------------------------------------------
- * OpenERP Web web module split
- *---------------------------------------------------------*/
- function init_web_modules() {
- var files = ["pyeval", "corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"];
- for(var i=0; i<files.length; i++) {
- var fct = openerp.web[files[i]];
- if(typeof(fct) === "function") {
- openerp.web[files[i]] = {};
- for (var k in fct) {
- openerp.web[files[i]][k] = fct[k];
- }
- fct(openerp, openerp.web[files[i]]);
- }
- }
- }
})();
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 3c8e1e0956a..db3cb69519f 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -1,7 +1,11 @@
/*---------------------------------------------------------
* OpenERP Web chrome
*---------------------------------------------------------*/
-openerp.web.chrome = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.chrome = {};
+
var QWeb = instance.web.qweb,
_t = instance.web._t;
@@ -1619,6 +1623,6 @@ instance.web.embed = function (origin, dbname, login, key, action, options) {
client.insertAfter(currentScript);
};
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/core.js
index c8deca1c9ac..8c69f86c47e 100644
--- a/addons/web/static/src/js/coresetup.js
+++ b/addons/web/static/src/js/core.js
@@ -1,10 +1,9 @@
-/*---------------------------------------------------------
- * OpenERP Web core
- *--------------------------------------------------------*/
-var console;
-if (!console) {
+
+(function() {
+
+if (typeof(console) === "undefined") {
// Even IE9 only exposes console object if debug window opened
- console = {};
+ window.console = {};
('log error debug info warn assert clear dir dirxml trace group'
+ ' groupCollapsed groupEnd time timeEnd profile profileEnd count'
+ ' exception').split(/\s+/).forEach(function(property) {
@@ -12,29 +11,230 @@ if (!console) {
});
}
-openerp.web.coresetup = function(instance) {
+// shim provided by mozilla for function.bind
+if (!Function.prototype.bind) {
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
+
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
+
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
+}
+
+var instance = openerp;
+openerp.web.core = {};
+
+var ControllerMixin = {
+ /**
+ * Informs the action manager to do an action. This supposes that
+ * the action manager can be found amongst the ancestors of the current widget.
+ * If that's not the case this method will simply return `false`.
+ */
+ do_action: function() {
+ var parent = this.getParent();
+ if (parent) {
+ return parent.do_action.apply(parent, arguments);
+ }
+ return false;
+ },
+ do_notify: function() {
+ if (this.getParent()) {
+ return this.getParent().do_notify.apply(this,arguments);
+ }
+ return false;
+ },
+ do_warn: function() {
+ if (this.getParent()) {
+ return this.getParent().do_warn.apply(this,arguments);
+ }
+ return false;
+ },
+ rpc: function(url, data, options) {
+ return this.alive(openerp.session.rpc(url, data, options));
+ }
+};
+
+/**
+ A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
+*/
+openerp.web.Controller = openerp.web.Class.extend(openerp.web.PropertiesMixin, ControllerMixin, {
+ /**
+ * Constructs the object and sets its parent if a parent is given.
+ *
+ * @param {openerp.web.Controller} parent Binds the current instance to the given Controller instance.
+ * When that controller is destroyed by calling destroy(), the current instance will be
+ * destroyed too. Can be null.
+ */
+ init: function(parent) {
+ openerp.web.PropertiesMixin.init.call(this);
+ this.setParent(parent);
+ this.session = openerp.session;
+ },
+});
+
+openerp.web.Widget.include(_.extend({}, ControllerMixin, {
+ init: function() {
+ this._super.apply(this, arguments);
+ this.session = openerp.session;
+ },
+}));
+
+instance.web.Registry = instance.web.Class.extend({
+ /**
+ * Stores a mapping of arbitrary key (strings) to object paths (as strings
+ * as well).
+ *
+ * Resolves those paths at query time in order to always fetch the correct
+ * object, even if those objects have been overloaded/replaced after the
+ * registry was created.
+ *
+ * An object path is simply a dotted name from the instance root to the
+ * object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
+ * session object).
+ *
+ * @constructs instance.web.Registry
+ * @param {Object} mapping a mapping of keys to object-paths
+ */
+ init: function (mapping) {
+ this.parent = null;
+ this.map = mapping || {};
+ },
+ /**
+ * Retrieves the object matching the provided key string.
+ *
+ * @param {String} key the key to fetch the object for
+ * @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
+ * @returns {Class} the stored class, to initialize or null if not found
+ */
+ get_object: function (key, silent_error) {
+ var path_string = this.map[key];
+ if (path_string === undefined) {
+ if (this.parent) {
+ return this.parent.get_object(key, silent_error);
+ }
+ if (silent_error) { return void 'nooo'; }
+ return null;
+ }
+
+ var object_match = instance;
+ var path = path_string.split('.');
+ // ignore first section
+ for(var i=1; i<path.length; ++i) {
+ object_match = object_match[path[i]];
+
+ if (object_match === undefined) {
+ if (silent_error) { return void 'noooooo'; }
+ return null;
+ }
+ }
+ return object_match;
+ },
+ /**
+ * Checks if the registry contains an object mapping for this key.
+ *
+ * @param {String} key key to look for
+ */
+ contains: function (key) {
+ if (key === undefined) { return false; }
+ if (key in this.map) {
+ return true;
+ }
+ if (this.parent) {
+ return this.parent.contains(key);
+ }
+ return false;
+ },
+ /**
+ * Tries a number of keys, and returns the first object matching one of
+ * the keys.
+ *
+ * @param {Array} keys a sequence of keys to fetch the object for
+ * @returns {Class} the first class found matching an object
+ */
+ get_any: function (keys) {
+ for (var i=0; i<keys.length; ++i) {
+ var key = keys[i];
+ if (!this.contains(key)) {
+ continue;
+ }
+
+ return this.get_object(key);
+ }
+ return null;
+ },
+ /**
+ * Adds a new key and value to the registry.
+ *
+ * This method can be chained.
+ *
+ * @param {String} key
+ * @param {String} object_path fully qualified dotted object path
+ * @returns {instance.web.Registry} itself
+ */
+ add: function (key, object_path) {
+ this.map[key] = object_path;
+ return this;
+ },
+ /**
+ * Creates and returns a copy of the current mapping, with the provided
+ * mapping argument added in (replacing existing keys if needed)
+ *
+ * Parent and child remain linked, a new key in the parent (which is not
+ * overwritten by the child) will appear in the child.
+ *
+ * @param {Object} [mapping={}] a mapping of keys to object-paths
+ */
+ extend: function (mapping) {
+ var child = new instance.web.Registry(mapping);
+ child.parent = this;
+ return child;
+ },
+ /**
+ * @deprecated use Registry#extend
+ */
+ clone: function (mapping) {
+ console.warn('Registry#clone is deprecated, use Registry#extend');
+ return this.extend(mapping);
+ }
+});
+
+instance.web.py_eval = function(expr, context) {
+ return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
+};
+
+/*
+ Some retro-compatibility.
+*/
+instance.web.JsonRPC = instance.web.Session;
/** Session openerp specific RPC class */
-instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
+instance.web.Session.include( /** @lends instance.web.Session# */{
init: function() {
this._super.apply(this, arguments);
+ this.debug = ($.deparam($.param.querystring()).debug !== undefined);
// TODO: session store in cookie should be optional
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
- rpc: function(url, params, options) {
- return this._super(url, params, options);
- },
/**
* Setup a sessionm
*/
session_bind: function(origin) {
- if (!_.isUndefined(this.origin)) {
- if (this.origin === origin) {
- return $.when();
- }
- throw new Error('Session already bound to ' + this.origin);
- }
var self = this;
this.setup(origin);
instance.web.qweb.default_dict['_s'] = this.origin;
@@ -69,33 +269,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
);
});
},
- /**
- * (re)loads the content of a session: db name, username, user id, session
- * context and status of the support contract
- *
- * @returns {$.Deferred} deferred indicating the session is done reloading
- */
- session_reload: function () {
- var self = this;
- var def = $.when();
- if (this.override_session) {
- if (! this.session_id) {
- def = this.rpc("/gen_session_id", {}).then(function(result) {
- self.session_id = result;
- });
- }
- } else {
- this.session_id = this.get_cookie('session_id');
- }
- return def.then(function() {
- return self.rpc("/web/session/get_session_info", {});
- }).then(function(result) {
- // If immediately follows a login (triggered by trying to restore
- // an invalid session or no session at all), refresh session data
- // (should not change, but just in case...)
- _.extend(self, result);
- });
- },
session_is_valid: function() {
var db = $.deparam.querystring().db;
if (db && this.db !== db) {
@@ -106,15 +279,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
/**
* The session is validated either by login or by restoration of a previous session
*/
- session_authenticate: function(db, login, password, _volatile) {
+ session_authenticate: function() {
var self = this;
- var base_location = document.location.protocol + '//' + document.location.host;
- var params = { db: db, login: login, password: password, base_location: base_location };
- return this.rpc("/web/session/authenticate", params).then(function(result) {
- if (!result.uid) {
- return $.Deferred().reject();
- }
- _.extend(self, result);
+ return $.when(this._super.apply(this, arguments)).then(function() {
return self.load_modules();
});
},
@@ -193,10 +360,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
});
},
load_translations: function() {
- var params = { mods: this.module_list, lang: this.user_context.lang };
- return this.rpc('/web/webclient/translations', params).done(function(trans) {
- instance.web._t.database.set_bundle(trans);
- });
+ return instance.web._t.database.load_translations(this, this.module_list, this.user_context.lang);
},
load_css: function (files) {
var self = this;
@@ -412,58 +576,13 @@ instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin,
});
instance.web.bus = new instance.web.Bus();
-/** OpenERP Translations */
-instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
- /**
- * @constructs instance.web.TranslationDataBase
- * @extends instance.web.Class
- */
- init: function() {
- this.db = {};
- this.parameters = {"direction": 'ltr',
- "date_format": '%m/%d/%Y',
- "time_format": '%H:%M:%S',
- "grouping": [],
- "decimal_point": ".",
- "thousands_sep": ","};
- },
+instance.web.TranslationDataBase.include({
set_bundle: function(translation_bundle) {
- var self = this;
- this.db = {};
- var modules = _.keys(translation_bundle.modules);
- modules.sort();
- if (_.include(modules, "web")) {
- modules = ["web"].concat(_.without(modules, "web"));
- }
- _.each(modules, function(name) {
- self.add_module_translation(translation_bundle.modules[name]);
- });
+ this._super(translation_bundle);
if (translation_bundle.lang_parameters) {
- this.parameters = translation_bundle.lang_parameters;
- this.parameters.grouping = py.eval(
- this.parameters.grouping);
+ this.parameters.grouping = py.eval(this.parameters.grouping);
}
},
- add_module_translation: function(mod) {
- var self = this;
- _.each(mod.messages, function(message) {
- self.db[message.id] = message.string;
- });
- },
- build_translation_function: function() {
- var self = this;
- var fcnt = function(str) {
- var tmp = self.get(str);
- return tmp === undefined ? str : tmp;
- };
- fcnt.database = this;
- return fcnt;
- },
- get: function(key) {
- if (this.db[key])
- return this.db[key];
- return undefined;
- }
});
/** Custom jQuery plugins */
@@ -494,21 +613,7 @@ $.fn.openerpBounce = function() {
};
/** Jquery extentions */
-$.Mutex = (function() {
- function Mutex() {
- this.def = $.Deferred().resolve();
- }
- Mutex.prototype.exec = function(action) {
- var current = this.def;
- var next = this.def = $.Deferred();
- return current.then(function() {
- return $.when(action()).always(function() {
- next.resolve();
- });
- });
- };
- return Mutex;
-})();
+$.Mutex = openerp.Mutex;
$.async_when = function() {
var async = false;
@@ -548,8 +653,6 @@ $.async_when = function() {
/** Setup default session */
instance.session = new instance.web.Session();
-/** Configure default qweb */
-instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
/**
* Lazy translation function, only performs the translation when actually
* printed (e.g. inserted into a template)
@@ -566,7 +669,6 @@ instance.web._lt = function (s) {
};
instance.web.qweb.debug = instance.session.debug;
_.extend(instance.web.qweb.default_dict, {
- '_t' : instance.web._t,
'__debug__': instance.session.debug,
});
instance.web.qweb.preprocess_node = function() {
@@ -707,6 +809,6 @@ instance.web.unblockUI = function() {
*/
instance.web.client_actions = new instance.web.Registry();
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
deleted file mode 100644
index c419685c222..00000000000
--- a/addons/web/static/src/js/corelib.js
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (c) 2012, OpenERP S.A.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-openerp.web.corelib = function(instance) {
-
-var ControllerMixin = {
- /**
- * Informs the action manager to do an action. This supposes that
- * the action manager can be found amongst the ancestors of the current widget.
- * If that's not the case this method will simply return `false`.
- */
- do_action: function() {
- var parent = this.getParent();
- if (parent) {
- return parent.do_action.apply(parent, arguments);
- }
- return false;
- },
- do_notify: function() {
- if (this.getParent()) {
- return this.getParent().do_notify.apply(this,arguments);
- }
- return false;
- },
- do_warn: function() {
- if (this.getParent()) {
- return this.getParent().do_warn.apply(this,arguments);
- }
- return false;
- },
- rpc: function(url, data, options) {
- return this.alive(openerp.session.rpc(url, data, options));
- }
-};
-
-/**
- A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
-*/
-openerp.web.Controller = openerp.web.Class.extend(openerp.web.PropertiesMixin, ControllerMixin, {
- /**
- * Constructs the object and sets its parent if a parent is given.
- *
- * @param {openerp.web.Controller} parent Binds the current instance to the given Controller instance.
- * When that controller is destroyed by calling destroy(), the current instance will be
- * destroyed too. Can be null.
- */
- init: function(parent) {
- openerp.web.PropertiesMixin.init.call(this);
- this.setParent(parent);
- this.session = openerp.session;
- },
-});
-
-openerp.web.Widget.include(_.extend({}, ControllerMixin, {
- init: function() {
- this._super.apply(this, arguments);
- this.session = openerp.session;
- },
-}));
-
-instance.web.Registry = instance.web.Class.extend({
- /**
- * Stores a mapping of arbitrary key (strings) to object paths (as strings
- * as well).
- *
- * Resolves those paths at query time in order to always fetch the correct
- * object, even if those objects have been overloaded/replaced after the
- * registry was created.
- *
- * An object path is simply a dotted name from the instance root to the
- * object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
- * session object).
- *
- * @constructs instance.web.Registry
- * @param {Object} mapping a mapping of keys to object-paths
- */
- init: function (mapping) {
- this.parent = null;
- this.map = mapping || {};
- },
- /**
- * Retrieves the object matching the provided key string.
- *
- * @param {String} key the key to fetch the object for
- * @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
- * @returns {Class} the stored class, to initialize or null if not found
- */
- get_object: function (key, silent_error) {
- var path_string = this.map[key];
- if (path_string === undefined) {
- if (this.parent) {
- return this.parent.get_object(key, silent_error);
- }
- if (silent_error) { return void 'nooo'; }
- return null;
- }
-
- var object_match = instance;
- var path = path_string.split('.');
- // ignore first section
- for(var i=1; i<path.length; ++i) {
- object_match = object_match[path[i]];
-
- if (object_match === undefined) {
- if (silent_error) { return void 'noooooo'; }
- return null;
- }
- }
- return object_match;
- },
- /**
- * Checks if the registry contains an object mapping for this key.
- *
- * @param {String} key key to look for
- */
- contains: function (key) {
- if (key === undefined) { return false; }
- if (key in this.map) {
- return true;
- }
- if (this.parent) {
- return this.parent.contains(key);
- }
- return false;
- },
- /**
- * Tries a number of keys, and returns the first object matching one of
- * the keys.
- *
- * @param {Array} keys a sequence of keys to fetch the object for
- * @returns {Class} the first class found matching an object
- */
- get_any: function (keys) {
- for (var i=0; i<keys.length; ++i) {
- var key = keys[i];
- if (!this.contains(key)) {
- continue;
- }
-
- return this.get_object(key);
- }
- return null;
- },
- /**
- * Adds a new key and value to the registry.
- *
- * This method can be chained.
- *
- * @param {String} key
- * @param {String} object_path fully qualified dotted object path
- * @returns {instance.web.Registry} itself
- */
- add: function (key, object_path) {
- this.map[key] = object_path;
- return this;
- },
- /**
- * Creates and returns a copy of the current mapping, with the provided
- * mapping argument added in (replacing existing keys if needed)
- *
- * Parent and child remain linked, a new key in the parent (which is not
- * overwritten by the child) will appear in the child.
- *
- * @param {Object} [mapping={}] a mapping of keys to object-paths
- */
- extend: function (mapping) {
- var child = new instance.web.Registry(mapping);
- child.parent = this;
- return child;
- },
- /**
- * @deprecated use Registry#extend
- */
- clone: function (mapping) {
- console.warn('Registry#clone is deprecated, use Registry#extend');
- return this.extend(mapping);
- }
-});
-
-instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
- triggers: {
- 'request': 'Request sent',
- 'response': 'Response received',
- 'response_failed': 'HTTP Error response or timeout received',
- 'error': 'The received response is an JSON-RPC error',
- },
- /**
- * @constructs instance.web.JsonRPC
- *
- * @param {String} [server] JSON-RPC endpoint hostname
- * @param {String} [port] JSON-RPC endpoint port
- */
- init: function() {
- instance.web.PropertiesMixin.init.call(this);
- this.server = null;
- this.debug = ($.deparam($.param.querystring()).debug !== undefined);
- this.override_session = false;
- this.session_id = undefined;
- },
- setup: function(origin) {
- var window_origin = location.protocol+"//"+location.host, self=this;
- this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
- this.prefix = this.origin;
- this.server = this.origin; // keep chs happy
- this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp;
- },
- /**
- * Executes an RPC call, registering the provided callbacks.
- *
- * Registers a default error callback if none is provided, and handles
- * setting the correct session id and session context in the parameter
- * objects
- *
- * @param {String} url RPC endpoint
- * @param {Object} params call parameters
- * @param {Object} options additional options for rpc call
- * @param {Function} success_callback function to execute on RPC call success
- * @param {Function} error_callback function to execute on RPC call failure
- * @returns {jQuery.Deferred} jquery-provided ajax deferred
- */
- rpc: function(url, params, options) {
- var self = this;
- options = options || {};
- // url can be an $.ajax option object
- if (_.isString(url)) {
- url = { url: url };
- }
- _.defaults(params, {
- context: this.user_context || {}
- });
- // Construct a JSON-RPC2 request, method is currently unused
- var payload = {
- jsonrpc: '2.0',
- method: 'call',
- params: params,
- id: _.uniqueId('r')
- };
- var deferred = $.Deferred();
- if (! options.shadow)
- this.trigger('request', url, payload);
-
- if (options.timeout)
- url.timeout = options.timeout;
-
- this.rpc_function(url, payload).then(
- function (response, textStatus, jqXHR) {
- if (! options.shadow)
- self.trigger('response', response);
- if (!response.error) {
- deferred.resolve(response["result"], textStatus, jqXHR);
- } else if (response.error.code === 100) {
- self.uid = false;
- } else {
- deferred.reject(response.error, $.Event());
- }
- },
- function(jqXHR, textStatus, errorThrown) {
- if (! options.shadow)
- self.trigger('response_failed', jqXHR);
- var error = {
- code: -32098,
- message: "XmlHttpRequestError " + errorThrown,
- data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
- };
- deferred.reject(error, $.Event());
- });
- // Allow deferred user to disable rpc_error call in fail
- deferred.fail(function() {
- deferred.fail(function(error, event) {
- if (!event.isDefaultPrevented()) {
- self.trigger('error', error, event);
- }
- });
- });
- return deferred;
- },
- /**
- * Raw JSON-RPC call
- *
- * @returns {jQuery.Deferred} ajax-webd deferred object
- */
- rpc_json: function(url, payload) {
- var self = this;
- var ajax = _.extend({
- type: "POST",
- dataType: 'json',
- contentType: 'application/json',
- data: JSON.stringify(payload),
- processData: false,
- headers: {
- "X-Openerp-Session-Id": this.override_session ? this.session_id : undefined,
- },
- }, url);
- if (this.synch)
- ajax.async = false;
- return $.ajax(ajax);
- },
- rpc_jsonp: function(url, payload) {
- var self = this;
- // extracted from payload to set on the url
- var data = {
- session_id: this.session_id,
- id: payload.id,
- };
-
- var set_sid = function (response, textStatus, jqXHR) {
- // If response give us the http session id, we store it for next requests...
- if (response.session_id) {
- self.session_id = response.session_id;
- }
- };
-
- url.url = this.url(url.url, null);
- var ajax = _.extend({
- type: "GET",
- dataType: 'jsonp',
- jsonp: 'jsonp',
- cache: false,
- data: data
- }, url);
- if (this.synch)
- ajax.async = false;
- var payload_str = JSON.stringify(payload);
- var payload_url = $.param({r:payload_str});
- if (payload_url.length < 2000) {
- // Direct jsonp request
- ajax.data.r = payload_str;
- return $.ajax(ajax).done(set_sid);
- } else {
- // Indirect jsonp request
- var ifid = _.uniqueId('oe_rpc_iframe');
- var display = self.debug ? 'block' : 'none';
- var $iframe = $(_.str.sprintf("<iframe src='javascript:false;' name='%s' id='%s' style='display:%s'></iframe>", ifid, ifid, display));
- var $form = $('<form>')
- .attr('method', 'POST')
- .attr('target', ifid)
- .attr('enctype', "multipart/form-data")
- .attr('action', ajax.url + '?jsonp=1&' + $.param(data))
- .append($('<input type="hidden" name="r" />').attr('value', payload_str))
- .hide()
- .appendTo($('body'));
- var cleanUp = function() {
- if ($iframe) {
- $iframe.unbind("load").remove();
- }
- $form.remove();
- };
- var deferred = $.Deferred();
- // the first bind is fired up when the iframe is added to the DOM
- $iframe.bind('load', function() {
- // the second bind is fired up when the result of the form submission is received
- $iframe.unbind('load').bind('load', function() {
- $.ajax(ajax).always(function() {
- cleanUp();
- }).done(function() {
- deferred.resolve.apply(deferred, arguments);
- }).fail(function() {
- deferred.reject.apply(deferred, arguments);
- });
- });
- // now that the iframe can receive data, we fill and submit the form
- $form.submit();
- });
- // append the iframe to the DOM (will trigger the first load)
- $form.after($iframe);
- return deferred.done(set_sid);
- }
- },
-
- url: function(path, params) {
- params = _.extend(params || {});
- if (this.override_session)
- params.session_id = this.session_id;
- var qs = '?' + $.param(params);
- var prefix = _.any(['http://', 'https://', '//'], _.bind(_.str.startsWith, null, path)) ? '' : this.prefix;
- return prefix + path + qs;
- },
-});
-
-instance.web.py_eval = function(expr, context) {
- return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
-};
-
-};
-
-// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index b63d44a408d..26807a2ee0f 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -1,5 +1,8 @@
-openerp.web.data = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.data = {};
/**
* Serializes the sort criterion array of a dataset into a form which can be
@@ -58,19 +61,18 @@ instance.web.Query = instance.web.Class.extend({
},
_execute: function () {
var self = this;
- return instance.session.rpc('/web/dataset/search_read', {
- model: this._model.name,
- fields: this._fields || false,
+ return this._model.call('search_read', {
domain: instance.web.pyeval.eval('domains',
[this._model.domain(this._filter)]),
- context: instance.web.pyeval.eval('contexts',
- [this._model.context(this._context)]),
+ fields: this._fields || false,
offset: this._offset,
limit: this._limit,
- sort: instance.web.serialize_sort(this._order_by)
+ order: instance.web.serialize_sort(this._order_by),
+ context: instance.web.pyeval.eval('contexts',
+ [this._model.context(this._context)]),
}).then(function (results) {
self._count = results.length;
- return results.records;
+ return results;
}, null);
},
/**
@@ -253,20 +255,42 @@ instance.web.QueryGroup = instance.web.Class.extend({
}
});
-instance.web.Model = instance.web.Class.extend({
+instance.web.Model.include({
/**
- * @constructs instance.web.Model
- * @extends instance.web.Class
- *
- * @param {String} model_name name of the OpenERP model this object is bound to
- * @param {Object} [context]
- * @param {Array} [domain]
- */
- init: function (model_name, context, domain) {
- this.name = model_name;
+ new openerp.web.Model([session,] model_name[, context[, domain]])
+
+ @constructs instance.web.Model
+ @extends instance.web.Class
+
+ @param {openerp.web.Session} [session] The session object used to communicate with
+ the server.
+ @param {String} model_name name of the OpenERP model this object is bound to
+ @param {Object} [context]
+ @param {Array} [domain]
+ */
+ init: function() {
+ var session, model_name, context, domain;
+ var args = _.toArray(arguments);
+ args.reverse();
+ session = args.pop();
+ if (session && ! (session instanceof openerp.web.Session)) {
+ model_name = session;
+ session = null;
+ } else {
+ model_name = args.pop();
+ }
+ context = args.length > 0 ? args.pop() : null;
+ domain = args.length > 0 ? args.pop() : null;
+
+ this._super(session, model_name, context, domain);
this._context = context || {};
this._domain = domain || [];
},
+ session: function() {
+ if (! this._session)
+ return instance.session;
+ return this._super();
+ },
/**
* @deprecated does not allow to specify kwargs, directly use call() instead
*/
@@ -294,13 +318,7 @@ instance.web.Model = instance.web.Class.extend({
args = [];
}
instance.web.pyeval.ensure_evaluated(args, kwargs);
- var debug = instance.session.debug ? '/'+this.name+':'+method : '';
- return instance.session.rpc('/web/dataset/call_kw' + debug, {
- model: this.name,
- method: method,
- args: args,
- kwargs: kwargs
- }, options);
+ return this._super(method, args, kwargs, options);
},
/**
* Fetches a Query instance bound to this model, for searching
@@ -318,7 +336,7 @@ instance.web.Model = instance.web.Class.extend({
* @param {String} signal signal to trigger on the workflow
*/
exec_workflow: function (id, signal) {
- return instance.session.rpc('/web/dataset/exec_workflow', {
+ return this.session().rpc('/web/dataset/exec_workflow', {
model: this.name,
id: id,
signal: signal
@@ -355,7 +373,7 @@ instance.web.Model = instance.web.Class.extend({
*/
call_button: function (method, args) {
instance.web.pyeval.ensure_evaluated(args, {});
- return instance.session.rpc('/web/dataset/call_button', {
+ return this.session().rpc('/web/dataset/call_button', {
model: this.name,
method: method,
// Should not be necessary anymore. Integrate remote in this?
@@ -1071,6 +1089,6 @@ instance.web.DropMisordered = instance.web.Class.extend({
}
});
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js
index 8de6cc506b9..f642f0a334d 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -1,4 +1,9 @@
-openerp.web.data_export = function(instance) {
+
+(function() {
+
+var instance = openerp;
+openerp.web.data_export = {};
+
var QWeb = instance.web.qweb,
_t = instance.web._t;
instance.web.DataExport = instance.web.Dialog.extend({
@@ -408,4 +413,4 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
});
-};
+})();
diff --git a/addons/web/static/src/js/dates.js b/addons/web/static/src/js/dates.js
index 1a48463df2c..0e3c9ff7874 100644
--- a/addons/web/static/src/js/dates.js
+++ b/addons/web/static/src/js/dates.js
@@ -1,5 +1,9 @@
-openerp.web.dates = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.dates = {};
+
var _t = instance.web._t;
/**
@@ -152,4 +156,4 @@ instance.web.time_to_str = function(obj) {
+ zpad(obj.getSeconds(),2);
};
-};
+})();
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 664ca60e8e8..f8394068c7c 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -1,5 +1,9 @@
-openerp.web.formats = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.formats = {};
+
var _t = instance.web._t;
/**
@@ -345,4 +349,4 @@ instance.web.round_decimals = function(value, decimals){
return instance.web.round_precision(value, Math.pow(10,-decimals));
};
-};
+})();
diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js
index 374493b1f7e..f5f5820f278 100644
--- a/addons/web/static/src/js/openerpframework.js
+++ b/addons/web/static/src/js/openerpframework.js
@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2012, OpenERP S.A.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ /*
+ The only dependencies of this file are underscore >= 1.3.1, jQuery >= 1.8.3 and
+ QWeb >= 1.0.0 . No dependencies shall be added.
+
+ This file must compile in EcmaScript 3 and work in IE7.
+ */
(function() {
/* jshint es3: true */
@@ -5,7 +36,6 @@
function declare($, _, QWeb2) {
var openerp = {};
-openerp.web = {};
/**
* Improved John Resig's inheritance, based on:
@@ -21,7 +51,7 @@ openerp.web = {};
*
* Example:
*
- * var Person = openerp.web.Class.extend({
+ * var Person = openerp.Class.extend({
* init: function(isDancing){
* this.dancing = isDancing;
* },
@@ -57,14 +87,14 @@ openerp.web = {};
var initializing = false,
fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
// The web Class implementation (does nothing)
- openerp.web.Class = function(){};
+ openerp.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() {
+ openerp.Class.extend = function() {
var _super = this.prototype;
// Support mixins arguments
var args = _.toArray(arguments);
@@ -104,7 +134,7 @@ openerp.web = {};
// The dummy class constructor
function Class() {
- if(this.constructor !== openerp.web.Class){
+ if(this.constructor !== openerp.Class){
throw new Error("You can only instanciate objects with the 'new' operator");
}
// All construction is actually done in the init method
@@ -165,7 +195,7 @@ openerp.web = {};
* When an object is destroyed, all its children are destroyed too releasing
* any resource they could have reserved before.
*/
-openerp.web.ParentedMixin = {
+openerp.ParentedMixin = {
__parentedMixin : true,
init: function() {
this.__parentedDestroyed = false;
@@ -274,7 +304,7 @@ openerp.web.ParentedMixin = {
* http://backbonejs.org
*
*/
-var Events = openerp.web.Class.extend({
+var Events = openerp.Class.extend({
on : function(events, callback, context) {
var ev;
events = events.split(/\s+/);
@@ -355,10 +385,10 @@ var Events = openerp.web.Class.extend({
}
});
-openerp.web.EventDispatcherMixin = _.extend({}, openerp.web.ParentedMixin, {
+openerp.EventDispatcherMixin = _.extend({}, openerp.ParentedMixin, {
__eventDispatcherMixin: true,
init: function() {
- openerp.web.ParentedMixin.init.call(this);
+ openerp.ParentedMixin.init.call(this);
this.__edispatcherEvents = new Events();
this.__edispatcherRegisteredEvents = [];
},
@@ -403,13 +433,13 @@ openerp.web.EventDispatcherMixin = _.extend({}, openerp.web.ParentedMixin, {
this.off(cal[0], cal[2], cal[1]);
}, this);
this.__edispatcherEvents.off();
- openerp.web.ParentedMixin.destroy.call(this);
+ openerp.ParentedMixin.destroy.call(this);
}
});
-openerp.web.PropertiesMixin = _.extend({}, openerp.web.EventDispatcherMixin, {
+openerp.PropertiesMixin = _.extend({}, openerp.EventDispatcherMixin, {
init: function() {
- openerp.web.EventDispatcherMixin.init.call(this);
+ openerp.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
set: function(arg1, arg2, arg3) {
@@ -493,7 +523,7 @@ openerp.web.PropertiesMixin = _.extend({}, openerp.web.EventDispatcherMixin, {
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
-openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
+openerp.Widget = openerp.Class.extend(openerp.PropertiesMixin, {
// Backbone-ish API
tagName: 'div',
id: null,
@@ -510,21 +540,21 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
/**
* Constructs the widget and sets its parent if a parent is given.
*
- * @constructs openerp.web.Widget
+ * @constructs openerp.Widget
*
- * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
+ * @param {openerp.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
- openerp.web.PropertiesMixin.init.call(this);
+ openerp.PropertiesMixin.init.call(this);
this.setParent(parent);
// Bind on_/do_* methods to this
// We might remove this automatic binding in the future
for (var name in this) {
if(typeof(this[name]) == "function") {
if((/^on_|^do_/).test(name)) {
- this[name] = this[name].bind(this);
+ this[name] = _.bind(this[name], this);
}
}
}
@@ -541,7 +571,7 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
if(this.$el) {
this.$el.remove();
}
- openerp.web.PropertiesMixin.destroy.call(this);
+ openerp.PropertiesMixin.destroy.call(this);
},
/**
* Renders the current widget and appends it to the given jQuery object or Widget.
@@ -622,8 +652,7 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
renderElement: function() {
var $el;
if (this.template) {
- $el = $(_.str.trim(openerp.web.qweb.render(
- this.template, {widget: this})));
+ $el = $(openerp.qweb.render(this.template, {widget: this}).trim());
} else {
$el = this._make_descriptive();
}
@@ -762,13 +791,459 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
}
});
-openerp.web.qweb = new QWeb2.Engine();
+var genericJsonRpc = function(fct_name, params, fct) {
+ var data = {
+ jsonrpc: "2.0",
+ method: fct_name,
+ params: params,
+ id: Math.floor(Math.random() * 1000 * 1000 * 1000)
+ };
+ return fct(data).pipe(function(result) {
+ if (result.error !== undefined) {
+ console.error("Server application error", result.error);
+ return $.Deferred().reject("server", result.error);
+ } else {
+ return result.result;
+ }
+ }, function() {
+ console.error("JsonRPC communication error", _.toArray(arguments));
+ var def = $.Deferred();
+ return def.reject.apply(def, ["communication"].concat(_.toArray(arguments)));
+ });
+};
+
+openerp.jsonRpc = function(url, fct_name, params, settings) {
+ return genericJsonRpc(fct_name, params, function(data) {
+ return $.ajax(url, _.extend({}, settings, {
+ url: url,
+ dataType: 'json',
+ type: 'POST',
+ data: JSON.stringify(data),
+ contentType: 'application/json'
+ }));
+ });
+};
+
+openerp.jsonpRpc = function(url, fct_name, params, settings) {
+ settings = settings || {};
+ return genericJsonRpc(fct_name, params, function(data) {
+ var payload_str = JSON.stringify(data);
+ var payload_url = $.param({r:payload_str});
+ var force2step = settings.force2step || false;
+ delete settings.force2step;
+ var session_id = settings.session_id || null;
+ delete settings.session_id;
+ if (payload_url.length < 2000 && ! force2step) {
+ return $.ajax(url, _.extend({}, settings, {
+ url: url,
+ dataType: 'jsonp',
+ jsonp: 'jsonp',
+ type: 'GET',
+ cache: false,
+ data: {r: payload_str, session_id: session_id}
+ }));
+ } else {
+ var args = {session_id: session_id, id: data.id};
+ var ifid = _.uniqueId('oe_rpc_iframe');
+ var html = "<iframe src='javascript:false;' name='" + ifid + "' id='" + ifid + "' style='display:none'></iframe>";
+ var $iframe = $(html);
+ var nurl = 'jsonp=1&' + $.param(args);
+ nurl = url.indexOf("?") !== -1 ? url + "&" + nurl : url + "?" + nurl;
+ var $form = $('<form>')
+ .attr('method', 'POST')
+ .attr('target', ifid)
+ .attr('enctype', "multipart/form-data")
+ .attr('action', nurl)
+ .append($('<input type="hidden" name="r" />').attr('value', payload_str))
+ .hide()
+ .appendTo($('body'));
+ var cleanUp = function() {
+ if ($iframe) {
+ $iframe.unbind("load").remove();
+ }
+ $form.remove();
+ };
+ var deferred = $.Deferred();
+ // the first bind is fired up when the iframe is added to the DOM
+ $iframe.bind('load', function() {
+ // the second bind is fired up when the result of the form submission is received
+ $iframe.unbind('load').bind('load', function() {
+ $.ajax({
+ url: url,
+ dataType: 'jsonp',
+ jsonp: 'jsonp',
+ type: 'GET',
+ cache: false,
+ data: {session_id: session_id, id: data.id}
+ }).always(function() {
+ cleanUp();
+ }).done(function() {
+ deferred.resolve.apply(deferred, arguments);
+ }).fail(function() {
+ deferred.reject.apply(deferred, arguments);
+ });
+ });
+ // now that the iframe can receive data, we fill and submit the form
+ $form.submit();
+ });
+ // append the iframe to the DOM (will trigger the first load)
+ $form.after($iframe);
+ return deferred;
+ }
+ });
+};
+
+openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, {
+ triggers: {
+ 'request': 'Request sent',
+ 'response': 'Response received',
+ 'response_failed': 'HTTP Error response or timeout received',
+ 'error': 'The received response is an JSON-RPC error'
+ },
+ /**
+ @constructs openerp.Session
+
+ @param parent The parent of the newly created object.
+ @param {String} origin Url of the OpenERP server to contact with this session object
+ or `null` if the server to contact is the origin server.
+ @param {Dict} options A dictionary that can contain the following options:
+
+ * "override_session": Default to false. If true, the current session object will
+ not try to re-use a previously created session id stored in a cookie.
+ * "session_id": Default to null. If specified, the specified session_id will be used
+ by this session object. Specifying this option automatically implies that the option
+ "override_session" is set to true.
+ */
+ init: function(parent, origin, options) {
+ openerp.PropertiesMixin.init.call(this, parent);
+ options = options || {};
+ this.server = null;
+ this.session_id = options.session_id || null;
+ this.override_session = options.override_session || !!options.session_id || false;
+ this.avoid_recursion = false;
+ this.setup(origin);
+ },
+ setup: function(origin) {
+ // must be able to customize server
+ var window_origin = location.protocol + "//" + location.host;
+ origin = origin ? origin.replace( /\/+$/, '') : window_origin;
+ if (!_.isUndefined(this.origin) && this.origin !== origin)
+ throw new Error('Session already bound to ' + this.origin);
+ else
+ this.origin = origin;
+ this.prefix = this.origin;
+ this.server = this.origin; // keep chs happy
+ this.origin_server = this.origin === window_origin;
+ },
+ /**
+ * (re)loads the content of a session: db name, username, user id, session
+ * context and status of the support contract
+ *
+ * @returns {$.Deferred} deferred indicating the session is done reloading
+ */
+ session_reload: function () {
+ var self = this;
+ return self.rpc("/web/session/get_session_info", {}).then(function(result) {
+ delete result.session_id;
+ _.extend(self, result);
+ });
+ },
+ /**
+ * The session is validated either by login or by restoration of a previous session
+ */
+ session_authenticate: function(db, login, password) {
+ var self = this;
+ var params = {db: db, login: login, password: password};
+ return this.rpc("/web/session/authenticate", params).then(function(result) {
+ if (!result.uid) {
+ return $.Deferred().reject();
+ }
+ delete result.session_id;
+ _.extend(self, result);
+ });
+ },
+ check_session_id: function() {
+ var self = this;
+ if (this.avoid_recursion)
+ return $.when();
+ if (this.session_id)
+ return $.when(); // we already have the session id
+ if (this.override_session || ! this.origin_server) {
+ // If we don't use the origin server we consider we should always create a new session.
+ // Even if some browsers could support cookies when using jsonp that behavior is
+ // not consistent and the browser creators are tending to removing that feature.
+ this.avoid_recursion = true;
+ return this.rpc("/gen_session_id", {}).then(function(result) {
+ self.session_id = result;
+ }).always(function() {
+ self.avoid_recursion = false;
+ });
+ } else {
+ // normal use case, just use the cookie
+ self.session_id = openerp.get_cookie("session_id");
+ return $.when();
+ }
+ },
+ /**
+ * Executes an RPC call, registering the provided callbacks.
+ *
+ * Registers a default error callback if none is provided, and handles
+ * setting the correct session id and session context in the parameter
+ * objects
+ *
+ * @param {String} url RPC endpoint
+ * @param {Object} params call parameters
+ * @param {Object} options additional options for rpc call
+ * @param {Function} success_callback function to execute on RPC call success
+ * @param {Function} error_callback function to execute on RPC call failure
+ * @returns {jQuery.Deferred} jquery-provided ajax deferred
+ */
+ rpc: function(url, params, options) {
+ var self = this;
+ options = _.clone(options || {});
+ var shadow = options.shadow || false;
+ delete options.shadow;
+
+ return self.check_session_id().then(function() {
+ // TODO: remove
+ if (! _.isString(url)) {
+ _.extend(options, url);
+ url = url.url;
+ }
+ // TODO correct handling of timeouts
+ if (! shadow)
+ self.trigger('request');
+ var fct;
+ if (self.origin_server) {
+ fct = openerp.jsonRpc;
+ if (self.override_session) {
+ options.headers = _.extend({}, options.headers, {
+ "X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
+ });
+ }
+ } else {
+ fct = openerp.jsonpRpc;
+ url = self.url(url, null);
+ options.session_id = self.session_id || '';
+ }
+ var p = fct(url, "call", params, options);
+ p = p.then(function (result) {
+ if (! shadow)
+ self.trigger('response');
+ return result;
+ }, function(type, error, textStatus, errorThrown) {
+ if (type === "server") {
+ if (! shadow)
+ self.trigger('response');
+ if (error.code === 100) {
+ self.uid = false;
+ }
+ return $.Deferred().reject(error, $.Event());
+ } else {
+ if (! shadow)
+ self.trigger('response_failed');
+ var nerror = {
+ code: -32098,
+ message: "XmlHttpRequestError " + errorThrown,
+ data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] }
+ };
+ return $.Deferred().reject(nerror, $.Event());
+ }
+ });
+ return p.fail(function() { // Allow deferred user to disable rpc_error call in fail
+ p.fail(function(error, event) {
+ if (!event.isDefaultPrevented()) {
+ self.trigger('error', error, event);
+ }
+ });
+ });
+ });
+ },
+ url: function(path, params) {
+ params = _.extend(params || {});
+ if (this.override_session || (! this.origin_server))
+ params.session_id = this.session_id;
+ var qs = $.param(params);
+ if (qs.length > 0)
+ qs = "?" + qs;
+ var prefix = _.any(['http://', 'https://', '//'], function(el) {
+ return path.length >= el.length && path.slice(0, el.length) === el;
+ }) ? '' : this.prefix;
+ return prefix + path + qs;
+ },
+ model: function(model_name) {
+ return new openerp.Model(this, model_name);
+ }
+});
+
+openerp.Model = openerp.Class.extend({
+ /**
+ new openerp.Model([session,] model_name)
+
+ @constructs instance.Model
+ @extends instance.Class
+
+ @param {openerp.Session} [session] The session object used to communicate with
+ the server.
+ @param {String} model_name name of the OpenERP model this object is bound to
+ @param {Object} [context]
+ @param {Array} [domain]
+ */
+ init: function () {
+ var session, model_name;
+ var args = _.toArray(arguments);
+ args.reverse();
+ session = args.pop();
+ if (session && ! (session instanceof openerp.Session)) {
+ model_name = session;
+ session = null;
+ } else {
+ model_name = args.pop();
+ }
+
+ this.name = model_name;
+ this._session = session;
+ },
+ session: function() {
+ if (! this._session)
+ throw new Error("Not session specified");
+ return this._session;
+ },
+ /**
+ * Call a method (over RPC) on the bound OpenERP model.
+ *
+ * @param {String} method name of the method to call
+ * @param {Array} [args] positional arguments
+ * @param {Object} [kwargs] keyword arguments
+ * @param {Object} [options] additional options for the rpc() method
+ * @returns {jQuery.Deferred<>} call result
+ */
+ call: function (method, args, kwargs, options) {
+ args = args || [];
+ kwargs = kwargs || {};
+ if (!_.isArray(args)) {
+ // call(method, kwargs)
+ kwargs = args;
+ args = [];
+ }
+ return this.session().rpc('/web/dataset/call_kw', {
+ model: this.name,
+ method: method,
+ args: args,
+ kwargs: kwargs
+ }, options);
+ }
+});
+
+/** OpenERP Translations */
+openerp.TranslationDataBase = openerp.Class.extend(/** @lends instance.TranslationDataBase# */{
+ /**
+ * @constructs instance.TranslationDataBase
+ * @extends instance.Class
+ */
+ init: function() {
+ this.db = {};
+ this.parameters = {"direction": 'ltr',
+ "date_format": '%m/%d/%Y',
+ "time_format": '%H:%M:%S',
+ "grouping": [],
+ "decimal_point": ".",
+ "thousands_sep": ","};
+ },
+ set_bundle: function(translation_bundle) {
+ var self = this;
+ this.db = {};
+ var modules = _.keys(translation_bundle.modules);
+ modules.sort();
+ if (_.include(modules, "web")) {
+ modules = ["web"].concat(_.without(modules, "web"));
+ }
+ _.each(modules, function(name) {
+ self.add_module_translation(translation_bundle.modules[name]);
+ });
+ if (translation_bundle.lang_parameters) {
+ this.parameters = translation_bundle.lang_parameters;
+ }
+ },
+ add_module_translation: function(mod) {
+ var self = this;
+ _.each(mod.messages, function(message) {
+ self.db[message.id] = message.string;
+ });
+ },
+ build_translation_function: function() {
+ var self = this;
+ var fcnt = function(str) {
+ var tmp = self.get(str);
+ return tmp === undefined ? str : tmp;
+ };
+ fcnt.database = this;
+ return fcnt;
+ },
+ get: function(key) {
+ return this.db[key];
+ },
+ /**
+ Loads the translations from an OpenERP server.
+
+ @param {openerp.Session} session The session object to contact the server.
+ @param {Array} [modules] The list of modules to load the translation. If not specified,
+ it will default to all the modules installed in the current database.
+ @param {Object} [lang] lang The language. If not specified it will default to the language
+ of the current user.
+ @returns {jQuery.Deferred}
+ */
+ load_translations: function(session, modules, lang) {
+ var self = this;
+ return session.rpc('/web/webclient/translations', {
+ "mods": modules || null,
+ "lang": lang || null
+ }).done(function(trans) {
+ self.set_bundle(trans);
+ });
+ }
+});
+
+openerp._t = new openerp.TranslationDataBase().build_translation_function();
+
+openerp.get_cookie = function(c_name) {
+ if (document.cookie.length > 0) {
+ var c_start = document.cookie.indexOf(c_name + "=");
+ if (c_start != -1) {
+ c_start = c_start + c_name.length + 1;
+ var c_end = document.cookie.indexOf(";", c_start);
+ if (c_end == -1) {
+ c_end = document.cookie.length;
+ }
+ return unescape(document.cookie.substring(c_start, c_end));
+ }
+ }
+ return "";
+};
+
+openerp.qweb = new QWeb2.Engine();
-openerp.web.qweb.default_dict = {
+openerp.qweb.default_dict = {
'_' : _,
- 'JSON': JSON
+ 'JSON': JSON,
+ '_t' : openerp._t
};
+openerp.Mutex = openerp.Class.extend({
+ init: function() {
+ this.def = $.Deferred().resolve();
+ },
+ exec: function(action) {
+ var current = this.def;
+ var next = this.def = $.Deferred();
+ return current.then(function() {
+ return $.when(action()).always(function() {
+ next.resolve();
+ });
+ });
+ }
+});
+
openerp.declare = declare;
return openerp;
diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js
index 06dc7ae1815..0b0e9725af9 100644
--- a/addons/web/static/src/js/pyeval.js
+++ b/addons/web/static/src/js/pyeval.js
@@ -1,7 +1,10 @@
/*
* py.js helpers and setup
*/
-openerp.web.pyeval = function (instance) {
+(function() {
+
+ var instance = openerp;
+
instance.web.pyeval = {};
var obj = function () {};
@@ -864,4 +867,4 @@ openerp.web.pyeval = function (instance) {
}
}, 0); });
};
-};
+})();
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index b16d1c29f5b..eab09cc24a0 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -1,4 +1,9 @@
-openerp.web.search = function(instance) {
+
+(function() {
+
+var instance = openerp;
+openerp.web.search = {};
+
var QWeb = instance.web.qweb,
_t = instance.web._t,
_lt = instance.web._lt;
@@ -2179,6 +2184,6 @@ instance.web.search.custom_filters = new instance.web.Registry({
'id': 'instance.web.search.ExtendedSearchProposition.Id'
});
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/test_support.js b/addons/web/static/src/js/test_support.js
index 67b961208db..006ccc580ed 100644
--- a/addons/web/static/src/js/test_support.js
+++ b/addons/web/static/src/js/test_support.js
@@ -1,4 +1,7 @@
+(function() {
+
openerp.test_support = {
+
setup_session: function (session) {
var origin = location.protocol+"//"+location.host;
_.extend(session, {
@@ -76,3 +79,5 @@ openerp.test_support = {
});
}
};
+
+})();
diff --git a/addons/web/static/src/js/testing.js b/addons/web/static/src/js/testing.js
index 880da9f1bc1..a7768f0ca24 100644
--- a/addons/web/static/src/js/testing.js
+++ b/addons/web/static/src/js/testing.js
@@ -3,13 +3,12 @@ openerp.testing = {};
(function (testing) {
var dependencies = {
pyeval: [],
- corelib: ['pyeval'],
- coresetup: ['corelib'],
- data: ['corelib', 'coresetup'],
+ core: ['pyeval'],
+ data: ['core'],
dates: [],
- formats: ['coresetup', 'dates'],
- chrome: ['corelib', 'coresetup'],
- views: ['corelib', 'coresetup', 'data', 'chrome'],
+ formats: ['core', 'dates'],
+ chrome: ['core'],
+ views: ['core', 'data', 'chrome'],
search: ['views', 'formats'],
list: ['views', 'data'],
form: ['data', 'views', 'list', 'formats'],
@@ -53,31 +52,34 @@ openerp.testing = {};
testing.mockifyRPC = function (instance, responses) {
var session = instance.session;
session.responses = responses || {};
- session.rpc_function = function (url, payload) {
+ session.rpc = function(url, rparams, options) {
+ if (_.isString(url)) {
+ url = {url: url};
+ }
var fn, params;
- var needle = payload.params.model + ':' + payload.params.method;
+ var needle = rparams.model + ':' + rparams.method;
if (url.url === '/web/dataset/call_kw'
&& needle in this.responses) {
fn = this.responses[needle];
params = [
- payload.params.args || [],
- payload.params.kwargs || {}
+ rparams.args || [],
+ rparams.kwargs || {}
];
} else {
fn = this.responses[url.url];
- params = [payload];
+ params = [{params: rparams}];
}
if (!fn) {
return $.Deferred().reject({}, 'failed',
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
- url.url, JSON.stringify(payload.params))
+ url.url, JSON.stringify(rparams))
).promise();
}
try {
return $.when(fn.apply(null, params)).then(function (result) {
// Wrap for RPC layer unwrapper thingy
- return {result: result};
+ return result;
});
} catch (e) {
// not sure why this looks like that
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 09daecfdd59..d36ac353461 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -1,4 +1,6 @@
-openerp.web.form = function (instance) {
+(function() {
+
+var instance = openerp;
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
@@ -5664,6 +5666,6 @@ instance.web.form.tags = new instance.web.Registry({
instance.web.form.custom_widgets = new instance.web.Registry({
});
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 975e2a2d248..f9ec22bf712 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -1,4 +1,7 @@
-openerp.web.list = function (instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.list = {};
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
@@ -2318,5 +2321,5 @@ instance.web.list.Many2Many = instance.web.list.Column.extend({
return this._super(row_data, options);
}
});
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 2634cec350f..0f529b7554a 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -2,7 +2,10 @@
* handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
* @namespace
*/
-openerp.web.list_editable = function (instance) {
+(function() {
+
+ var instance = openerp;
+ openerp.web.list_editable = {};
var _t = instance.web._t;
// editability status of list rows
@@ -834,4 +837,4 @@ openerp.web.list_editable = function (instance) {
return null;
}
});
-};
+})();
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 2f9efad9171..5604edf147d 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -2,7 +2,10 @@
* OpenERP web library
*---------------------------------------------------------*/
-openerp.web.view_tree = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.view_tree = {};
var QWeb = instance.web.qweb,
_lt = instance.web._lt;
@@ -265,4 +268,4 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
this.hidden = true;
}
});
-};
+})();
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 5cb8ee44e53..5c2e7a62062 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -2,7 +2,10 @@
* OpenERP web library
*---------------------------------------------------------*/
-openerp.web.views = function(instance) {
+(function() {
+
+var instance = openerp;
+openerp.web.views = {};
var QWeb = instance.web.qweb,
_t = instance.web._t;
@@ -1616,6 +1619,6 @@ instance.web.xml_to_str = function(node) {
*/
instance.web.views = new instance.web.Registry();
-};
+})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: