/*--------------------------------------------------------- * OpenERP Web core *--------------------------------------------------------*/ var console; if (!console) { console = {log: function () {}}; } if (!console.debug) { console.debug = console.log; } openerp.web.core = function(openerp) { openerp.web.Class = nova.Class; openerp.web.callback = function(obj, method) { var callback = function() { var args = Array.prototype.slice.call(arguments); var r; for(var i = 0; i < callback.callback_chain.length; i++) { var c = callback.callback_chain[i]; if(c.unique) { callback.callback_chain.splice(i, 1); i -= 1; } 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 } return r; }; callback.callback_chain = []; callback.add = function(f) { if(typeof(f) == 'function') { f = { callback: f, args: Array.prototype.slice.call(arguments, 1) }; } f.self = f.self || null; f.args = f.args || []; f.unique = !!f.unique; if(f.position == 'last') { callback.callback_chain.push(f); } else { callback.callback_chain.unshift(f); } return callback; }; callback.add_first = function(f) { return callback.add.apply(null,arguments); }; callback.add_last = function(f) { return callback.add({ callback: f, args: Array.prototype.slice.call(arguments, 1), 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, self:obj, args:Array.prototype.slice.call(arguments, 2) }); }; /** * Generates an inherited class that replaces all the methods by null methods (methods * that does nothing and always return undefined). * * @param {Class} claz * @param {Object} add Additional functions to override. * @return {Class} */ openerp.web.generate_null_object_class = function(claz, add) { var newer = {}; var copy_proto = function(prototype) { for (var name in prototype) { if(typeof prototype[name] == "function") { newer[name] = function() {}; } } if (prototype.prototype) copy_proto(prototype.prototype); }; copy_proto(claz.prototype); newer.init = openerp.web.Widget.prototype.init; var tmpclass = claz.extend(newer); return tmpclass.extend(add || {}); }; /** * web error for lookup failure * * @class */ openerp.web.NotFound = openerp.web.Class.extend( /** @lends openerp.web.NotFound# */ { }); openerp.web.KeyNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.KeyNotFound# */ { /** * Thrown when a key could not be found in a mapping * * @constructs openerp.web.KeyNotFound * @extends openerp.web.NotFound * @param {String} key the key which could not be found */ init: function (key) { this.key = key; }, toString: function () { return "The key " + this.key + " was not found"; } }); openerp.web.ObjectNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.ObjectNotFound# */ { /** * Thrown when an object path does not designate a valid class or object * in the openerp hierarchy. * * @constructs openerp.web.ObjectNotFound * @extends openerp.web.NotFound * @param {String} path the invalid object path */ init: function (path) { this.path = path; }, toString: function () { return "Could not find any object of path " + this.path; } }); openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry# */ { /** * 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 openerp root to the * 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 */ 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 * * @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, 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'; } throw new openerp.web.KeyNotFound(key); } var object_match = openerp; var path = path_string.split('.'); // ignore first section for(var i=1; i", ifid, ifid, display)); var $form = $('
') .attr('method', 'POST') .attr('target', ifid) .attr('enctype', "multipart/form-data") .attr('action', ajax.url + '?' + $.param(data)) .append($('').attr('value', payload_str)) .hide() .appendTo($('body')); var cleanUp = function() { if ($iframe) { $iframe.unbind("load").attr("src", "javascript:false;").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(); }).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_response: function() { }, on_rpc_error: function(error) { }, /** * Init a session, reloads from cookie, if it exists */ session_init: function () { var self = this; // 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; }); }, /** * (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 this.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, { 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; }, /** * The session is validated either by login or by restoration of a previous session */ 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(); }); }, session_logout: function() { this.set_cookie('session_id', ''); return this.rpc("/web/session/destroy", {}); }, 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 * * @private * @param name the cookie's name */ get_cookie: function (name) { if (!this.name) { return null; } var nameEQ = this.name + '|' + name + '='; var cookies = document.cookie.split(';'); for(var i=0; i', { '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 = 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).then(function () { d.resolve(); }); }; var head = document.head || document.getElementsByTagName('head')[0]; head.appendChild(tag); } else { 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) { if (!xml) { return; } openerp.web.qweb.add_template(_.str.trim(xml)); }); }); }); return self.qweb_mutex.def; }, on_modules_loaded: function() { for(var j=0; j'); var complete = function () { if (options.complete) { options.complete(); } clearTimeout(timer); $form_data.remove(); $target.remove(); if (remove_form && $form) { $form.remove(); } }; var $target = $('