/*--------------------------------------------------------- * OpenERP Web core *--------------------------------------------------------*/ var console; if (!console) { console = {log: function () {}}; } if (!console.debug) { console.debug = console.log; } openerp.web.core = function(openerp) { /** * John Resig Class with factory improvement */ (function() { var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The web Class implementation (does nothing) /** * Extended version of John Resig's Class pattern * * @class */ openerp.web.Class = function(){}; /** * Subclass an existing class * * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class */ openerp.web.Class.extend = function(prop) { var _super = this.prototype; // Instantiate a web class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { return function() { var tmp = this._super; // Add a new ._super() method that is the same // method but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so // we remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if (!initializing && this.init) { var ret = this.init.apply(this, arguments); if (ret) { return ret; } } return this; } Class.include = function (properties) { for (var name in properties) { if (typeof properties[name] !== 'function' || !fnTest.test(properties[name])) { prototype[name] = properties[name]; } else if (typeof prototype[name] === 'function' && prototype.hasOwnProperty(name)) { prototype[name] = (function (name, fn, previous) { return function () { var tmp = this._super; this._super = previous; var ret = fn.apply(this, arguments); this._super = tmp; return ret; } })(name, properties[name], prototype[name]); } else if (typeof _super[name] === 'function') { prototype[name] = (function (name, fn) { return function () { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; } })(name, properties[name]); } } }; // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; }; })(); openerp.web.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" }); }; 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.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 (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.rpc("/web/session/get_session_info", {}).pipe(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 }); var deferred = self.do_load_qweb(['/web/webclient/qweb']); if(self.uid) { return deferred.then(self.load_modules()); } return deferred; }); }, 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) { 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 }); // TODO: session store in cookie should be optional self.set_cookie('session_id', self.session_id); return self.load_modules(); }); }, session_logout: function() { this.set_cookie('session_id', ''); window.location.reload(); }, /** * 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 { self.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'); var complete = function () { if (options.complete) { options.complete(); } clearTimeout(timer); $form_data.remove(); $target.remove(); if (remove_form && $form) { $form.remove(); } }; var $target = $('