/*---------------------------------------------------------
* OpenERP Web core
*--------------------------------------------------------*/
+var console;
+if (!console) {
+ console = {log: function () {}};
+}
+if (!console.debug) {
+ console.debug = console.log;
+}
openerp.web.core = function(openerp) {
+openerp.web.qweb = new QWeb2.Engine();
+openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1);
/**
* John Resig Class with factory improvement
*/
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(){};
- // Create a new Class that inherits from this class
+ /**
+ * 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;
}
return this;
}
+ // This should NOT be used, like callbackenable it's too hackish not enough javasish
Class.include = function (properties) {
for (var name in properties) {
if (typeof properties[name] !== 'function'
* that does nothing and always return undefined).
*
* @param {Class} claz
- * @param {dict} add Additional functions to override.
+ * @param {Object} add Additional functions to override.
* @return {Class}
*/
openerp.web.generate_null_object_class = function(claz, add) {
/**
* Thrown when a key could not be found in a mapping
*
- * @constructs
+ * @constructs openerp.web.KeyNotFound
* @extends openerp.web.NotFound
* @param {String} key the key which could not be found
*/
* Thrown when an object path does not designate a valid class or object
* in the openerp hierarchy.
*
- * @constructs
+ * @constructs openerp.web.ObjectNotFound
* @extends openerp.web.NotFound
* @param {String} path the invalid object path
*/
* object pointed to (e.g. ``"openerp.web.Session"`` for an OpenERP
* session object).
*
- * @constructs
+ * @constructs openerp.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
}
});
-openerp.web.CallbackEnabled = openerp.web.Class.extend({
+openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.CallbackEnabled# */{
+ /**
+ * @constructs openerp.web.CallbackEnabled
+ * @extends openerp.web.Class
+ */
init: function() {
// Transform on_* method into openerp.web.callbacks
for (var name in this) {
openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Session# */{
/**
- * @constructs
- * @param server
- * @param port
+ * @constructs openerp.web.Session
+ * @extends openerp.web.CallbackEnabled
+ *
+ * @param {String} [server] JSON-RPC endpoint hostname
+ * @param {String} [port] JSON-RPC endpoint port
*/
init: function(server, port) {
this._super();
this.port = (port == undefined) ? location.port : port;
this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
this.debug = (window.location.search.indexOf('?debug') !== -1);
- this.db = "";
- this.login = "";
- this.password = "";
- this.user_context= {};
- this.uid = false;
this.session_id = false;
+ this.uid = false;
+ this.user_context= {};
+ this.db = false;
this.module_list = [];
this.module_loaded = {"web": true};
this.context = {};
this.shortcuts = [];
this.active_id = null;
- this.session = this;
},
start: function() {
this.session_restore();
jsonrpc: "2.0",
method: "call",
params: params,
- id:null
+ id: _.uniqueId('browser-client-')
}).then(function () {deferred.resolve.apply(deferred, arguments);},
function(error) {deferred.reject(error, $.Event());});
return deferred.fail(function() {
},
session_login: function(db, login, password, success_callback) {
var self = this;
- this.db = db;
- this.login = login;
- this.password = password;
- var params = { db: this.db, login: this.login, password: this.password };
- this.rpc("/web/session/login", params, function(result) {
+ 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();
- self.on_session_valid();
- if (success_callback)
- success_callback();
- });
- },
- session_logout: function() {
- this.uid = false;
+ return true;
+ }).then(success_callback);
},
/**
* Reloads uid and session_id from local storage, if they exist
*/
session_restore: function () {
- this.uid = this.get_cookie('uid');
+ var self = this;
this.session_id = this.get_cookie('session_id');
- this.db = this.get_cookie('db');
- this.login = this.get_cookie('login');
- this.user_context = this.get_cookie("user_context");
- // we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
- // then call on_session_valid
- this.on_session_valid();
+ 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();
+ });
},
/**
* Saves the session id and uid locally
*/
session_save: function () {
- this.set_cookie('uid', this.uid);
this.set_cookie('session_id', this.session_id);
- this.set_cookie('db', this.db);
- this.set_cookie('login', this.login);
- this.set_cookie('user_context', this.user_context);
},
logout: function() {
- delete this.uid;
- delete this.session_id;
- delete this.db;
- delete this.login;
- this.set_cookie('uid', '');
this.set_cookie('session_id', '');
- this.set_cookie('db', '');
- this.set_cookie('login', '');
- this.on_session_invalid(function() {});
+ this.reload_client();
+ },
+ reload_client: function() {
+ window.location.reload();
},
/**
* Fetches a cookie stored by an openerp session
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.user_context.lang.replace("_", "-") + ".js"
+ ];
if(self.debug) {
self.rpc('/web/webclient/csslist', {"mods": modules}, self.do_load_css);
self.rpc('/web/webclient/jslist', {"mods": modules}, function(files) {
});
},
do_load_css: function (files) {
+ var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
- 'href': file,
+ 'href': file + (self.debug ? '?debug=' + (new Date().getTime()) : ''),
'rel': 'stylesheet',
'type': 'text/css'
}));
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
- tag.src = file;
+ tag.src = file + (this.debug ? '?debug=' + (new Date().getTime()) : '');
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);
};
- document.head.appendChild(tag);
+ $('head').append(tag);
+ self.do_load_js(files);
} else {
this.on_modules_loaded();
}
}
});
-/**
- * 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.
- */
-openerp.web.SessionAware = openerp.web.CallbackEnabled.extend({
+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;
*/
rpc: function(url, data, success, error) {
return this.session.rpc(url, data, success, error);
- },
- log: function() {
- var args = Array.prototype.slice.call(arguments);
- var caller = arguments.callee.caller;
- // TODO add support for line number using
- // https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js
- // args.unshift("" + caller.debug_name);
- this.on_log.apply(this,args);
- },
- on_log: function() {
- if(this.session.debug) {
- var notify = false;
- var body = false;
- if(window.console) {
- console.log(arguments);
- } else {
- body = true;
- }
- var a = Array.prototype.slice.call(arguments, 0);
- for(var i = 0; i < a.length; i++) {
- var v = a[i]==null ? "null" : a[i].toString();
- if(i==0) {
- notify = v.match(/^not/);
- body = v.match(/^bod/);
- }
- if(body) {
- $('<pre></pre>').text(v).appendTo($('body'));
- }
- if(notify && this.notification) {
- this.notification.notify("Logging:",v);
- }
- }
- }
}
});
-openerp.web.Widget = openerp.web.SessionAware.extend({
+/**
+ * Base class for all visual components. Provides a lot of functionalities helpful
+ * for the management of a part of the DOM.
+ *
+ * Widget handles:
+ * - Rendering with QWeb.
+ * - Life-cycle management and parenting (when a parent is destroyed, all its children are
+ * destroyed too).
+ * - Insertion in DOM.
+ *
+ * Widget also extends SessionAware for ease of use.
+ *
+ * Guide to create implementations of the Widget class:
+ * ==============================================
+ *
+ * Here is a sample child class:
+ *
+ * MyWidget = openerp.base.Widget.extend({
+ * // the name of the QWeb template to use for rendering
+ * template: "MyQWebTemplate",
+ * // identifier prefix, it is useful to put an obvious one for debugging
+ * identifier_prefix: 'my-id-prefix-',
+ *
+ * init: function(parent) {
+ * this._super(parent);
+ * // stuff that you want to init before the rendering
+ * },
+ * start: function() {
+ * // stuff you want to make after the rendering, `this.$element` holds a correct value
+ * this.$element.find(".my_button").click(/* an example of event binding * /);
+ *
+ * // if you have some asynchronous operations, it's a good idea to return
+ * // a promise in start()
+ * var promise = this.rpc(...);
+ * return promise;
+ * }
+ * });
+ *
+ * Now this class can simply be used with the following syntax:
+ *
+ * var my_widget = new MyWidget(this);
+ * my_widget.appendTo($(".some-div"));
+ *
+ * With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
+ * DOM inside the ".some-div" div and its events were binded.
+ *
+ * And of course, when you don't need that widget anymore, just do:
+ *
+ * my_widget.stop();
+ *
+ * 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# */{
/**
* 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.
/**
* Construct the widget and set its parent if a parent is given.
*
- * @constructs
+ * @constructs openerp.web.Widget
+ * @extends openerp.web.SessionAware
+ *
* @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
* destroyed too. Can be null.
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 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 QWeb.render(this.template, _.extend({widget: this}, additional || {}));
+ return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
},
/**
* 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 destory all its children before destroying itself.
+ * Destroys the current widget, also destroy all its children before destroying itself.
*/
stop: function() {
_.each(_.clone(this.widget_children), function(el) {
});
/**
+ * @class
+ * @extends openerp.web.Widget
* @deprecated
* For retro compatibility only, the only difference with is that render() uses
- * directly `this` instead of context with a "widget" key.
+ * directly ``this`` instead of context with a ``widget`` key.
*/
-openerp.web.OldWidget = openerp.web.Widget.extend({
+openerp.web.OldWidget = openerp.web.Widget.extend(/** @lends openerp.web.OldWidget# */{
render: function (additional) {
- return QWeb.render(this.template, _.extend(_.extend({}, this), additional || {}));
+ return openerp.web.qweb.render(this.template, _.extend(_.extend({}, this), additional || {}));
}
});
-openerp.web.TranslationDataBase = openerp.web.Class.extend({
+openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.web.TranslationDataBase# */{
+ /**
+ * @constructs openerp.web.TranslationDataBase
+ * @extends openerp.web.Class
+ */
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',