callback.callback_chain.splice(i, 1);
i -= 1;
}
- r = c.callback.apply(c.self, c.args.concat(args));
+ var result = c.callback.apply(c.self, c.args.concat(args));
+ if (c.callback === method) {
+ // return the result of the original method
+ r = result;
+ }
// TODO special value to stop the chain
// openerp.web.callback_stop
}
position: "last"
});
};
+ callback.remove = function(f) {
+ callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) {
+ return el.callback === f;
+ }));
+ return callback;
+ };
return callback.add({
callback: method,
* registry was created.
*
* An object path is simply a dotted name from the openerp root to the
- * object pointed to (e.g. ``"openerp.web.Session"`` for an OpenERP
- * session object).
+ * object pointed to (e.g. ``"openerp.web.Connection"`` for an OpenERP
+ * connection object).
*
* @constructs openerp.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
* 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
*
* @throws {openerp.web.KeyNotFound} if the object was not in the mapping
* @throws {openerp.web.ObjectNotFound} if the object path was invalid
*/
- get_object: function (key) {
+ get_object: function (key, silent_error) {
var path_string = this.map[key];
if (path_string === undefined) {
+ if (silent_error) { return void 'nooo'; }
throw new openerp.web.KeyNotFound(key);
}
object_match = object_match[path[i]];
if (object_match === undefined) {
+ if (silent_error) { return void 'noooooo'; }
throw new openerp.web.ObjectNotFound(path_string);
}
}
}
}
}
+ },
+ /**
+ * Proxies a method of the object, in order to keep the right ``this`` on
+ * method invocations.
+ *
+ * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
+ * even more so to ``jQuery.proxy`` with a fundamental difference: its
+ * resolution of the method being called is lazy, meaning it will use the
+ * method as it is when the proxy is called, not when the proxy is created.
+ *
+ * Other methods will fix the bound method to what it is when creating the
+ * binding/proxy, which is fine in most javascript code but problematic in
+ * OpenERP Web where developers may want to replace existing callbacks with
+ * theirs.
+ *
+ * The semantics of this precisely replace closing over the method call.
+ *
+ * @param {String} method_name name of the method to invoke
+ * @returns {Function} proxied method
+ */
+ proxy: function (method_name) {
+ var self = this;
+ return function () {
+ return self[method_name].apply(self, arguments);
+ }
}
});
-openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Session# */{
+openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Connection# */{
/**
- * @constructs openerp.web.Session
+ * @constructs openerp.web.Connection
* @extends openerp.web.CallbackEnabled
*
* @param {String} [server] JSON-RPC endpoint hostname
* @param {String} [port] JSON-RPC endpoint port
*/
- init: function(server, port) {
+ init: function() {
this._super();
- this.server = (server == undefined) ? location.hostname : server;
- this.port = (port == undefined) ? location.port : port;
- this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
- this.debug = (window.location.search.indexOf('?debug') !== -1);
+ this.server = null;
+ this.debug = ($.deparam($.param.querystring()).debug != undefined);
+ // TODO: session store in cookie should be optional
+ this.name = openerp._session_id;
+ this.qweb_mutex = new $.Mutex();
+ },
+ bind: function(origin) {
+ var window_origin = location.protocol+"//"+location.host;
+ this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
+ this.prefix = this.origin;
+ this.server = this.origin; // keep chs happy
+ openerp.web.qweb.default_dict['_s'] = this.origin;
+ this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp;
this.session_id = false;
this.uid = false;
+ this.username = false;
this.user_context= {};
this.db = false;
+ this.openerp_entreprise = false;
this.module_list = [];
this.module_loaded = {"web": true};
this.context = {};
this.shortcuts = [];
this.active_id = null;
- },
- start: function() {
- this.session_restore();
+ return this.session_init();
},
/**
* Executes an RPC call, registering the provided callbacks.
*/
rpc: function(url, params, success_callback, error_callback) {
var self = this;
+ // url can be an $.ajax option object
+ if (_.isString(url)) {
+ url = { url: url };
+ }
// Construct a JSON-RPC2 request, method is currently unused
params.session_id = this.session_id;
if (this.debug)
params.debug = 1;
-
- // Call using the rpc_mode
- var deferred = $.Deferred();
- this.rpc_ajax(url, {
- jsonrpc: "2.0",
- method: "call",
+ var payload = {
+ jsonrpc: '2.0',
+ method: 'call',
params: params,
- id: _.uniqueId('browser-client-')
- }).then(function () {deferred.resolve.apply(deferred, arguments);},
- function(error) {deferred.reject(error, $.Event());});
- return deferred.fail(function() {
+ id: _.uniqueId('r')
+ };
+ var deferred = $.Deferred();
+ this.on_rpc_request();
+ this.rpc_function(url, payload).then(
+ function (response, textStatus, jqXHR) {
+ self.on_rpc_response();
+ if (!response.error) {
+ deferred.resolve(response["result"], textStatus, jqXHR);
+ } else if (response.error.data.type === "session_invalid") {
+ self.uid = false;
+ // TODO deprecate or use a deferred on login.do_ask_login()
+ self.on_session_invalid(function() {
+ self.rpc(url, payload.params,
+ function() { deferred.resolve.apply(deferred, arguments); },
+ function() { deferred.reject.apply(deferred, arguments); });
+ });
+ } else {
+ deferred.reject(response.error, $.Event());
+ }
+ },
+ function(jqXHR, textStatus, errorThrown) {
+ self.on_rpc_response();
+ 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 on_rpc_error in fail
+ deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
self.on_rpc_error(error, event);
}
});
}).then(success_callback, error_callback).promise();
+ return deferred;
},
/**
* Raw JSON-RPC call
*
* @returns {jQuery.Deferred} ajax-webd deferred object
*/
- rpc_ajax: function(url, payload) {
+ rpc_json: function(url, payload) {
var self = this;
- this.on_rpc_request();
- // url can be an $.ajax option object
- if (_.isString(url)) {
- url = {
- url: url
- }
- }
var ajax = _.extend({
type: "POST",
- url: url,
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(payload),
- processData: false
+ processData: false,
}, url);
- var deferred = $.Deferred();
- $.ajax(ajax).done(function(response, textStatus, jqXHR) {
- self.on_rpc_response();
- if (!response.error) {
- deferred.resolve(response["result"], textStatus, jqXHR);
- return;
- }
- if (response.error.data.type !== "session_invalid") {
- deferred.reject(response.error);
- return;
- }
- self.uid = false;
- self.on_session_invalid(function() {
- self.rpc(url, payload.params,
- function() {
- deferred.resolve.apply(deferred, arguments);
- },
- function(error, event) {
- event.preventDefault();
- deferred.reject.apply(deferred, arguments);
- });
- });
- }).fail(function(jqXHR, textStatus, errorThrown) {
- self.on_rpc_response();
- var error = {
- code: -32098,
- message: "XmlHttpRequestError " + errorThrown,
- data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
+ 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,
+ };
+ url.url = this.get_url(url.url);
+ 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);
+ } else {
+ // Indirect jsonp request
+ var ifid = _.uniqueId('oe_rpc_iframe');
+ var display = options.openerp.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 + '?' + $.param(data))
+ .append($('<input type="hidden" name="r" />').attr('value', payload_str))
+ .hide()
+ .appendTo($('body'));
+ var cleanUp = function() {
+ if ($iframe) {
+ $iframe.unbind("load").attr("src", "javascript:false;").remove();
+ }
+ $form.remove();
};
- deferred.reject(error);
- });
- return deferred.promise();
+ 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();
+ }).then(
+ function() { deferred.resolve.apply(deferred, arguments); },
+ 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;
+ }
},
on_rpc_request: function() {
},
on_rpc_error: function(error) {
},
/**
- * The session is validated either by login or by restoration of a previous session
+ * Init a session, reloads from cookie, if it exists
*/
- on_session_valid: function() {
- if(!openerp._modules_loaded)
- this.load_modules();
- },
- on_session_invalid: function(contination) {
- },
- session_is_valid: function() {
- return this.uid;
- },
- session_login: function(db, login, password, success_callback) {
+ session_init: function () {
var self = this;
- var params = { db: db, login: login, password: password };
- return this.rpc("/web/session/login", params, function(result) {
- self.session_id = result.session_id;
- self.uid = result.uid;
- self.user_context = result.context;
- self.db = result.db;
- self.session_save();
- return true;
- }).then(success_callback);
+ // TODO: session store in cookie should be optional
+ this.session_id = this.get_cookie('session_id');
+ return this.session_reload().pipe(function(result) {
+ var modules = openerp._modules.join(',');
+ var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb);
+ if(self.session_is_valid()) {
+ return deferred.pipe(function() { return self.load_modules(); });
+ }
+ return deferred;
+ });
},
/**
- * Reloads uid and session_id from local storage, if they exist
+ * (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_restore: function () {
+ session_reload: function () {
var self = this;
- this.session_id = this.get_cookie('session_id');
return this.rpc("/web/session/get_session_info", {}).then(function(result) {
- self.uid = result.uid;
- self.user_context = result.context;
- self.db = result.db;
- if (self.uid)
- self.on_session_valid();
- else
- self.on_session_invalid();
- }, function() {
- self.on_session_invalid();
+ // 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, {
+ db: result.db,
+ username: result.login,
+ uid: result.uid,
+ user_context: result.context,
+ openerp_entreprise: result.openerp_entreprise
+ });
});
},
+ session_is_valid: function() {
+ return !!this.uid;
+ },
/**
- * Saves the session id and uid locally
+ * The session is validated either by login or by restoration of a previous session
*/
- session_save: function () {
- this.set_cookie('session_id', this.session_id);
+ session_authenticate: function(db, login, password, _volatile) {
+ 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).pipe(function(result) {
+ _.extend(self, {
+ session_id: result.session_id,
+ db: result.db,
+ username: result.login,
+ uid: result.uid,
+ user_context: result.context,
+ openerp_entreprise: result.openerp_entreprise
+ });
+ if (!_volatile) {
+ self.set_cookie('session_id', self.session_id);
+ }
+ return self.load_modules();
+ });
},
- logout: function() {
+ session_logout: function() {
this.set_cookie('session_id', '');
- this.reload_client();
},
- reload_client: function() {
- window.location.reload();
+ on_session_valid: function() {
+ },
+ /**
+ * Called when a rpc call fail due to an invalid session.
+ * By default, it's a noop
+ */
+ on_session_invalid: function(retry_callback) {
},
/**
* Fetches a cookie stored by an openerp session
* @param name the cookie's name
*/
get_cookie: function (name) {
- var nameEQ = this.element_id + '|' + name + '=';
+ if (!this.name) { return null; }
+ var nameEQ = this.name + '|' + name + '=';
var cookies = document.cookie.split(';');
for(var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
* @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
*/
set_cookie: function (name, value, ttl) {
+ if (!this.name) { return; }
ttl = ttl || 24*60*60*365;
document.cookie = [
- this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
+ this.name + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
+ 'path=/',
'max-age=' + ttl,
'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
].join(';');
*/
load_modules: function() {
var self = this;
- this.rpc('/web/session/modules', {}, function(result) {
+ return this.rpc('/web/session/modules', {}).pipe(function(result) {
self.module_list = result;
var lang = self.user_context.lang;
var params = { mods: ["web"].concat(result), lang: lang};
- self.rpc('/web/webclient/translations',params).then(function(transs) {
- openerp.web._t.database.set_bundle(transs);
- var modules = self.module_list.join(',');
- var file_list = ["/web/static/lib/datejs/globalization/" +
- self.user_context.lang.replace("_", "-") + ".js"
- ];
-
- self.rpc('/web/webclient/csslist', {"mods": modules}, self.do_load_css);
- self.rpc('/web/webclient/jslist', {"mods": modules}, function(files) {
- self.do_load_js(file_list.concat(files));
- });
- openerp._modules_loaded = true;
+ var modules = self.module_list.join(',');
+ return $.when(
+ self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css),
+ self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb),
+ self.rpc('/web/webclient/translations', params).pipe(function(trans) {
+ openerp.web._t.database.set_bundle(trans);
+ var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
+ return self.rpc('/web/webclient/jslist', {mods: modules}).pipe(function(files) {
+ return self.do_load_js(file_list.concat(files));
+ });
+ })
+ ).then(function() {
+ self.on_modules_loaded();
+ self.on_session_valid();
});
});
},
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
- 'href': file,
+ 'href': self.get_url(file),
'rel': 'stylesheet',
'type': 'text/css'
}));
},
do_load_js: function(files) {
var self = this;
+ var d = $.Deferred();
if(files.length != 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
- tag.src = file;
+ tag.src = self.get_url(file);
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
- self.do_load_js(files);
+ self.do_load_js(files).then(function () {
+ d.resolve();
+ });
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(tag);
} else {
- this.on_modules_loaded();
+ d.resolve();
}
+ return d;
+ },
+ do_load_qweb: function(files) {
+ var self = this;
+ _.each(files, function(file) {
+ self.qweb_mutex.exec(function() {
+ return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) {
+ openerp.web.qweb.add_template(_.str.trim(xml));
+ });
+ });
+ });
+ return self.qweb_mutex.def;
},
on_modules_loaded: function() {
for(var j=0; j<this.module_list.length; j++) {
}
}
},
+ get_url: function (file) {
+ return this.prefix + file;
+ },
/**
* Cooperative file download implementation, for ajaxy APIs.
*
if (parseInt(cookie_val, 10) !== token) { continue; }
// clear cookie
- document.cookie = _.sprintf("%s=;expires=%s;path=/",
+ document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
cookie_name, new Date().toGMTString());
if (options.success) { options.success(); }
complete();
}
};
timer = setTimeout(waitLoop, CHECK_INTERVAL);
- }
-});
-
-openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.SessionAware# */{
- /**
- * Utility class that any class is allowed to extend to easy common manipulations.
- *
- * It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
- * logging facility.
- *
- * @constructs openerp.web.SessionAware
- * @extends openerp.web.CallbackEnabled
- *
- * @param {openerp.web.Session} session
- */
- init: function(session) {
- this._super();
- this.session = session;
},
- /**
- * Performs a JSON-RPC call
- *
- * @param {String} url endpoint url
- * @param {Object} data RPC parameters
- * @param {Function} success RPC call success callback
- * @param {Function} error RPC call error callback
- * @returns {jQuery.Deferred} deferred object for the RPC call
- */
- rpc: function(url, data, success, error) {
- return this.session.rpc(url, data, success, error);
- }
+ synchronized_mode: function(to_execute) {
+ var synch = this.synch;
+ this.synch = true;
+ try {
+ return to_execute();
+ } finally {
+ this.synch = synch;
+ }
+ },
});
/**
* destroyed too).
* - Insertion in DOM.
*
- * Widget also extends SessionAware for ease of use.
- *
* Guide to create implementations of the Widget class:
* ==============================================
*
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
-openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widget# */{
+openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.Widget# */{
/**
* 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.
*/
identifier_prefix: 'generic-identifier-',
/**
- * Construct the widget and set its parent if a parent is given.
+ * 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.SessionAware
+ * @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
* for new components this argument should not be provided any more.
*/
init: function(parent, /** @deprecated */ element_id) {
- this._super((parent || {}).session);
+ this._super();
+ this.session = openerp.connection;
// if given an element_id, try to get the associated DOM element and save
// a reference in this.$element. Else just generate a unique identifier.
this.element_id = element_id;
this.element_id = this.element_id || _.uniqueId(this.identifier_prefix);
var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : undefined;
+ this.$element = tmp ? $(tmp) : $(document.createElement(this.tag_name));
this.widget_parent = parent;
this.widget_children = [];
this.widget_is_stopped = false;
},
/**
- * Render the current widget and appends it to the given jQuery object or Widget.
+ * Renders the current widget and appends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
}, target);
},
/**
- * Render the current widget and prepends it to the given jQuery object or Widget.
+ * Renders the current widget and prepends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
}, target);
},
/**
- * Render the current widget and inserts it after to the given jQuery object or Widget.
+ * Renders the current widget and inserts it after to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
}, target);
},
/**
- * Render the current widget and inserts it before to the given jQuery object or Widget.
+ * Renders the current widget and inserts it before to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
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) {
- var rendered = this.render();
- this.$element = $(rendered);
+ this.render_element();
if (target instanceof openerp.web.Widget)
target = target.$element;
insertion(target);
},
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) {
+ 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) {
- return openerp.web.qweb.render(this.template, _.extend({widget: this}, 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
* @returns {jQuery.Deferred}
*/
start: function() {
- /* The default implementation is only useful for retro-compatibility, it is
- not necessary to call it using _super() when using Widget for new components. */
- if (!this.$element) {
- var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : undefined;
- }
return $.Deferred().done().promise();
},
/**
- * Destroys the current widget, also destroy all its children before destroying itself.
+ * Destroys the current widget, also destroys all its children before destroying itself.
*/
stop: function() {
_.each(_.clone(this.widget_children), function(el) {
this.widget_is_stopped = true;
},
/**
- * Inform the action manager to do an action. Of course, this suppose that
+ * 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`.
*/
}
return false;
},
+
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
- this._super(url, data). then(function() {
+ openerp.connection.rpc(url, data). then(function() {
if (!self.widget_is_stopped)
def.resolve.apply(def, arguments);
}, function() {
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
- "grouping": "[]",
+ "grouping": [],
"decimal_point": ".",
"thousands_sep": ","};
},
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
- var modules = _.keys(translation_bundle.modules).sort();
+ var modules = _.keys(translation_bundle.modules);
+ modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
}
});
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
+ this.parameters.grouping = py.eval(
+ this.parameters.grouping).toJSON();
}
},
add_module_translation: function(mod) {
}
});
+/** Configure blockui */
+if ($.blockUI) {
+ $.blockUI.defaults.baseZ = 1100;
+ $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
+}
+
+/** Configure default qweb */
openerp.web._t = new openerp.web.TranslationDataBase().build_translation_function();
+/**
+ * Lazy translation function, only performs the translation when actually
+ * printed (e.g. inserted into a template)
+ *
+ * Useful when defining translatable strings in code evaluated before the
+ * translation database is loaded, as class attributes or at the top-level of
+ * an OpenERP Web module
+ *
+ * @param {String} s string to translate
+ * @returns {Object} lazy translation object
+ */
+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.default_dict = {
+ '_' : _,
+ '_t' : openerp.web._t
+};
openerp.web.qweb.format_text_node = function(s) {
// Note that 'this' is the Qweb Node of the text
- var translation = this.node.parentElement.attributes['t-translation'];
+ var translation = this.node.parentNode.attributes['t-translation'];
if (translation && translation.value === 'off') {
return s;
}
- var ts = _.trim(s);
+ var ts = _.str.trim(s);
if (ts.length === 0) {
return s;
}
return tr === ts ? s : tr;
}
+/** 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.pipe(function() {
+ return $.when(action()).always(function() {
+ next.resolve();
+ });
+ });
+ };
+ return Mutex;
+})();
+
+/** Setup default connection */
+openerp.connection = new openerp.web.Connection();
+openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
+
+
+$.async_when = function() {
+ var async = false;
+ var def = $.Deferred();
+ $.when.apply($, arguments).then(function() {
+ var args = arguments;
+ var action = function() {
+ def.resolve.apply(def, args);
+ };
+ if (async)
+ action();
+ else
+ setTimeout(action, 0);
+ }, function() {
+ var args = arguments;
+ var action = function() {
+ def.reject.apply(def, args);
+ };
+ if (async)
+ action();
+ else
+ setTimeout(action, 0);
+ });
+ async = true;
+ return def;
+};
+
+// special tweak for the web client
+var old_async_when = $.async_when;
+$.async_when = function() {
+ if (openerp.connection.synch)
+ return $.when.apply(this, arguments);
+ else
+ return old_async_when.apply(this, arguments);
+};
+
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: