summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/framework
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/framework')
-rw-r--r--addons/web/static/src/js/framework/ajax.js399
-rw-r--r--addons/web/static/src/js/framework/bus.js16
-rw-r--r--addons/web/static/src/js/framework/class.js154
-rw-r--r--addons/web/static/src/js/framework/crash_manager.js211
-rw-r--r--addons/web/static/src/js/framework/data.js1135
-rw-r--r--addons/web/static/src/js/framework/data_manager.js280
-rw-r--r--addons/web/static/src/js/framework/data_model.js80
-rw-r--r--addons/web/static/src/js/framework/dialog.js200
-rw-r--r--addons/web/static/src/js/framework/dom_utils.js68
-rw-r--r--addons/web/static/src/js/framework/domain_utils.js19
-rw-r--r--addons/web/static/src/js/framework/formats.js204
-rw-r--r--addons/web/static/src/js/framework/local_storage.js34
-rw-r--r--addons/web/static/src/js/framework/misc.js438
-rw-r--r--addons/web/static/src/js/framework/mixins.js367
-rw-r--r--addons/web/static/src/js/framework/model.js60
-rw-r--r--addons/web/static/src/js/framework/pyeval.js1061
-rw-r--r--addons/web/static/src/js/framework/qweb.js51
-rw-r--r--addons/web/static/src/js/framework/registry.js54
-rw-r--r--addons/web/static/src/js/framework/session.js411
-rw-r--r--addons/web/static/src/js/framework/time.js299
-rw-r--r--addons/web/static/src/js/framework/translation.js117
-rw-r--r--addons/web/static/src/js/framework/utils.js566
-rw-r--r--addons/web/static/src/js/framework/view.js211
-rw-r--r--addons/web/static/src/js/framework/widget.js426
24 files changed, 0 insertions, 6861 deletions
diff --git a/addons/web/static/src/js/framework/ajax.js b/addons/web/static/src/js/framework/ajax.js
deleted file mode 100644
index dee6f4599b4..00000000000
--- a/addons/web/static/src/js/framework/ajax.js
+++ /dev/null
@@ -1,399 +0,0 @@
-odoo.define('web.ajax', function (require) {
-"use strict";
-
-var core = require('web.core');
-var utils = require('web.utils');
-var time = require('web.time');
-
-function genericJsonRpc (fct_name, params, fct) {
- var data = {
- jsonrpc: "2.0",
- method: fct_name,
- params: params,
- id: Math.floor(Math.random() * 1000 * 1000 * 1000)
- };
- var xhr = fct(data);
- var result = xhr.pipe(function(result) {
- core.bus.trigger('rpc:result', data, result);
- if (result.error !== undefined) {
- if (result.error.data.arguments[0] !== "bus.Bus not available in test mode") {
- console.error("Server application error", JSON.stringify(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)));
- });
- // FIXME: jsonp?
- result.abort = function () { if (xhr.abort) xhr.abort(); };
- return result;
-}
-
-function jsonRpc(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, time.date_to_utc),
- contentType: 'application/json'
- }));
- });
-}
-
-function jsonpRpc(url, fct_name, params, settings) {
- settings = settings || {};
- return genericJsonRpc(fct_name, params, function(data) {
- var payload_str = JSON.stringify(data, time.date_to_utc);
- 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);
- if (settings.timeout) {
- realSetTimeout(function() {
- deferred.reject({});
- }, settings.timeout);
- }
- return deferred;
- }
- });
-}
-
-// helper function to make a rpc with a function name hardcoded to 'call'
-function rpc(url, params, settings) {
- return jsonRpc(url, 'call', params, settings);
-}
-
-// helper
-function realSetTimeout (fct, millis) {
- var finished = new Date().getTime() + millis;
- var wait = function() {
- var current = new Date().getTime();
- if (current < finished) {
- setTimeout(wait, finished - current);
- } else {
- fct();
- }
- };
- setTimeout(wait, millis);
-}
-
-function loadCSS(url) {
- if (!$('link[href="' + url + '"]').length) {
- $('head').append($('<link>', {
- 'href': url,
- 'rel': 'stylesheet',
- 'type': 'text/css'
- }));
- }
-}
-
-var loadJS = (function () {
- var urls = [];
- var defs = [];
-
- var load = function loadJS(url) {
- // Check the DOM to see if a script with the specified url is already there
- var alreadyRequired = ($('script[src="' + url + '"]').length > 0);
-
- // If loadJS was already called with the same URL, it will have a registered deferred indicating if
- // the script has been fully loaded. If not, the deferred has to be initialized. This is initialized
- // as already resolved if the script was already there without the need of loadJS.
- var index = _.indexOf(urls, url);
- if (index < 0) {
- urls.push(url);
- index = defs.push(alreadyRequired ? $.when() : $.Deferred()) - 1;
- }
-
- // Get the script associated deferred and returns it after initializing the script if needed. The
- // deferred is marked to be resolved on script load and rejected on script error.
- var def = defs[index];
- if (!alreadyRequired) {
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = url;
- script.onload = script.onreadystatechange = function() {
- if ((script.readyState && script.readyState !== "loaded" && script.readyState !== "complete") || script.onload_done) {
- return;
- }
- script.onload_done = true;
- def.resolve(url);
- };
- script.onerror = function () {
- console.error("Error loading file", script.src);
- def.reject(url);
- };
- var head = document.head || document.getElementsByTagName('head')[0];
- head.appendChild(script);
- }
- return def;
- };
-
- return load;
-})();
-
-
-/**
- * Cooperative file download implementation, for ajaxy APIs.
- *
- * Requires that the server side implements an httprequest correctly
- * setting the `fileToken` cookie to the value provided as the `token`
- * parameter. The cookie *must* be set on the `/` path and *must not* be
- * `httpOnly`.
- *
- * It would probably also be a good idea for the response to use a
- * `Content-Disposition: attachment` header, especially if the MIME is a
- * "known" type (e.g. text/plain, or for some browsers application/json
- *
- * @param {Object} options
- * @param {String} [options.url] used to dynamically create a form
- * @param {Object} [options.data] data to add to the form submission. If can be used without a form, in which case a form is created from scratch. Otherwise, added to form data
- * @param {HTMLFormElement} [options.form] the form to submit in order to fetch the file
- * @param {Function} [options.success] callback in case of download success
- * @param {Function} [options.error] callback in case of request error, provided with the error body
- * @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
- */
-function get_file(options) {
- // need to detect when the file is done downloading (not used
- // yet, but we'll need it to fix the UI e.g. with a throbber
- // while dump is being generated), iframe load event only fires
- // when the iframe content loads, so we need to go smarter:
- // http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx
- var timer, token = new Date().getTime(),
- cookie_name = 'fileToken', cookie_length = cookie_name.length,
- CHECK_INTERVAL = 1000, id = _.uniqueId('get_file_frame'),
- remove_form = false;
-
-
- // iOS devices doesn't allow iframe use the way we do it,
- // opening a new window seems the best way to workaround
- if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
- var params = _.extend({}, options.data || {}, {token: token});
- var url = options.session.url(options.url, params);
- if (options.complete) { options.complete(); }
-
- return window.open(url);
- }
-
- var $form, $form_data = $('<div>');
-
- var complete = function () {
- if (options.complete) { options.complete(); }
- clearTimeout(timer);
- $form_data.remove();
- $target.remove();
- if (remove_form && $form) { $form.remove(); }
- };
- var $target = $('<iframe style="display: none;">')
- .attr({id: id, name: id})
- .appendTo(document.body)
- .load(function () {
- try {
- if (options.error) {
- var body = this.contentDocument.body;
- var nodes = body.children.length === 0 ? body.childNodes : body.children;
- var node = nodes[1] || nodes[0];
- options.error(JSON.parse(node.textContent));
- }
- } finally {
- complete();
- }
- });
-
- if (options.form) {
- $form = $(options.form);
- } else {
- remove_form = true;
- $form = $('<form>', {
- action: options.url,
- method: 'POST'
- }).appendTo(document.body);
- }
- if (core.csrf_token) {
- $('<input type="hidden" name="csrf_token">')
- .val(core.csrf_token)
- .appendTo($form_data);
- }
-
- var hparams = _.extend({}, options.data || {}, {token: token});
- _.each(hparams, function (value, key) {
- var $input = $form.find('[name=' + key +']');
- if (!$input.length) {
- $input = $('<input type="hidden" name="' + key + '">')
- .appendTo($form_data);
- }
- $input.val(value);
- });
-
- $form
- .append($form_data)
- .attr('target', id)
- .get(0).submit();
-
- var waitLoop = function () {
- var cookies = document.cookie.split(';');
- // setup next check
- timer = setTimeout(waitLoop, CHECK_INTERVAL);
- for (var i=0; i<cookies.length; ++i) {
- var cookie = cookies[i].replace(/^\s*/, '');
- if (!cookie.indexOf(cookie_name === 0)) { continue; }
- var cookie_val = cookie.substring(cookie_length + 1);
- if (parseInt(cookie_val, 10) !== token) { continue; }
-
- // clear cookie
- document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
- cookie_name, new Date().toGMTString());
- if (options.success) { options.success(); }
- complete();
- return;
- }
- };
- timer = setTimeout(waitLoop, CHECK_INTERVAL);
-};
-
-function post (controller_url, data) {
-
- var progressHandler = function (deferred) {
- return function (state) {
- if(state.lengthComputable) {
- deferred.notify({
- h_loaded: utils.human_size(state.loaded),
- h_total : utils.human_size(state.total),
- loaded : state.loaded,
- total : state.total,
- pcent : Math.round((state.loaded/state.total)*100)
- });
- }
- };
- };
-
- var Def = $.Deferred();
- var postData = new FormData();
-
- $.each(data, function(i,val) {
- postData.append(i, val);
- });
- if (core.csrf_token) {
- postData.append('csrf_token', core.csrf_token);
- }
-
- var xhr = new XMLHttpRequest();
- if(xhr.upload) xhr.upload.addEventListener('progress', progressHandler(Def), false);
-
- var ajaxDef = $.ajax(controller_url, {
- xhr: function() {return xhr;},
- data: postData,
- processData: false,
- contentType: false,
- type: 'POST'
- }).then(function (data) {Def.resolve(data);})
- .fail(function (data) {Def.reject(data);});
-
- return Def;
-}
-
-var loadXML = (function () {
- var loading = false;
- var urls = [];
- var qwebs = [];
- var templates_def = $.Deferred();
-
- var load = function loadXML(url, qweb) {
- if (url) {
- urls.push(url);
- qwebs.push(qweb);
- }
-
- if (!loading && urls.length) {
- if (templates_def.state() === "resolved") {
- templates_def = $.Deferred();
- }
-
- loading = true;
- qwebs.shift().add_template(urls.shift(), function () {
- loading = false;
- if (!urls.length) {
- templates_def.resolve();
- }
- load(null);
- });
- }
-
- return templates_def;
- };
-
- return load;
-})();
-
-return {
- jsonRpc: jsonRpc,
- jsonpRpc: jsonpRpc,
- rpc: rpc,
- loadCSS: loadCSS,
- loadJS: loadJS,
- loadXML: loadXML,
- get_file: get_file,
- post: post,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/bus.js b/addons/web/static/src/js/framework/bus.js
deleted file mode 100644
index bf33c1fe01e..00000000000
--- a/addons/web/static/src/js/framework/bus.js
+++ /dev/null
@@ -1,16 +0,0 @@
-odoo.define('web.Bus', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-var mixins = require('web.mixins');
-
-/**
- * Event Bus used to bind events scoped in the current instance
- */
-return Class.extend(mixins.EventDispatcherMixin, {
- init: function() {
- mixins.EventDispatcherMixin.init.call(this, parent);
- },
-});
-
-});
diff --git a/addons/web/static/src/js/framework/class.js b/addons/web/static/src/js/framework/class.js
deleted file mode 100644
index 8e53f0ba06d..00000000000
--- a/addons/web/static/src/js/framework/class.js
+++ /dev/null
@@ -1,154 +0,0 @@
-odoo.define('web.Class', function () {
-"use strict";
-/**
- * Improved John Resig's inheritance, based on:
- *
- * Simple JavaScript Inheritance
- * By John Resig http://ejohn.org/
- * MIT Licensed.
- *
- * Adds "include()"
- *
- * Defines The Class object. That object can be used to define and inherit classes using
- * the extend() method.
- *
- * Example:
- *
- * var Person = Class.extend({
- * init: function(isDancing){
- * this.dancing = isDancing;
- * },
- * dance: function(){
- * return this.dancing;
- * }
- * });
- *
- * The init() method act as a constructor. This class can be instancied this way:
- *
- * var person = new Person(true);
- * person.dance();
- *
- * The Person class can also be extended again:
- *
- * var Ninja = Person.extend({
- * init: function(){
- * this._super( false );
- * },
- * dance: function(){
- * // Call the inherited version of dance()
- * return this._super();
- * },
- * swingSword: function(){
- * return true;
- * }
- * });
- *
- * When extending a class, each re-defined method can use this._super() to call the previous
- * implementation of that method.
- */
-var initializing = false;
-var fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
-
-// The web Class implementation (does nothing)
-function OdooClass(){};
-
-/**
- * Subclass an existing class
- *
- * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
- */
-OdooClass.extend = function() {
- var _super = this.prototype;
- // Support mixins arguments
- var args = _.toArray(arguments);
- args.unshift({});
- var prop = _.extend.apply(_,args);
-
- // Instantiate a web class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- var This = this;
- var prototype = new This();
- initializing = false;
-
- // Copy the properties over onto the new prototype
- _.each(prop, function(val, name) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[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() {
- if(this.constructor !== OdooClass){
- throw new Error("You can only instanciate objects with the 'new' operator");
- }
- // All construction is actually done in the init method
- this._super = null;
- if (!initializing && this.init) {
- var ret = this.init.apply(this, arguments);
- if (ret) { return ret; }
- }
- return this;
- }
- Class.include = function (properties) {
- _.each(properties, function(val, name) {
- 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 = this.extend;
-
- return Class;
-};
-
-return OdooClass;
-});
diff --git a/addons/web/static/src/js/framework/crash_manager.js b/addons/web/static/src/js/framework/crash_manager.js
deleted file mode 100644
index af982ecd15d..00000000000
--- a/addons/web/static/src/js/framework/crash_manager.js
+++ /dev/null
@@ -1,211 +0,0 @@
-odoo.define('web.CrashManager', function (require) {
-"use strict";
-
-var ajax = require('web.ajax');
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-
-var QWeb = core.qweb;
-var _t = core._t;
-var _lt = core._lt;
-
-var map_title ={
- user_error: _lt('Warning'),
- warning: _lt('Warning'),
- access_error: _lt('Access Error'),
- missing_error: _lt('Missing Record'),
- validation_error: _lt('Validation Error'),
- except_orm: _lt('Global Business Error'),
- access_denied: _lt('Access Denied'),
-};
-
-var CrashManager = core.Class.extend({
- init: function() {
- this.active = true;
- },
- enable: function () {
- this.active = true;
- },
- disable: function () {
- this.active = false;
- },
- rpc_error: function(error) {
- var self = this;
- if (!this.active) {
- return;
- }
- if (this.connection_lost) {
- return;
- }
- if (error.code === -32098) {
- core.bus.trigger('connection_lost');
- this.connection_lost = true;
- var timeinterval = setInterval(function() {
- ajax.jsonRpc('/web/webclient/version_info').then(function() {
- clearInterval(timeinterval);
- core.bus.trigger('connection_restored');
- self.connection_lost = false;
- });
- }, 2000);
- return;
- }
- var handler = core.crash_registry.get(error.data.name, true);
- if (handler) {
- new (handler)(this, error).display();
- return;
- }
- if (error.data.name === "openerp.http.SessionExpiredException" || error.data.name === "werkzeug.exceptions.Forbidden") {
- this.show_warning({type: _t("Odoo Session Expired"), data: {message: _t("Your Odoo session expired. Please refresh the current web page.")}});
- return;
- }
- if (_.has(map_title, error.data.exception_type)) {
- if(error.data.exception_type === 'except_orm'){
- if(error.data.arguments[1]) {
- error = _.extend({}, error,
- {
- data: _.extend({}, error.data,
- {
- message: error.data.arguments[1],
- title: error.data.arguments[0] !== 'Warning' ? (" - " + error.data.arguments[0]) : '',
- })
- });
- }
- else {
- error = _.extend({}, error,
- {
- data: _.extend({}, error.data,
- {
- message: error.data.arguments[0],
- title: '',
- })
- });
- }
- }
- else {
- error = _.extend({}, error,
- {
- data: _.extend({}, error.data,
- {
- message: error.data.arguments[0],
- title: map_title[error.data.exception_type] !== 'Warning' ? (" - " + map_title[error.data.exception_type]) : '',
- })
- });
- }
-
- this.show_warning(error);
- //InternalError
-
- } else {
- this.show_error(error);
- }
- },
- show_warning: function(error) {
- if (!this.active) {
- return;
- }
- new Dialog(this, {
- size: 'medium',
- title: _.str.capitalize(error.type || error.message) || _t("Odoo Warning"),
- subtitle: error.data.title,
- $content: $(QWeb.render('CrashManager.warning', {error: error}))
- }).open();
- },
- show_error: function(error) {
- if (!this.active) {
- return;
- }
- var dialog = new Dialog(this, {
- title: _.str.capitalize(error.type || error.message) || _t("Odoo Error"),
- $content: $(QWeb.render('CrashManager.error', {error: error}))
- }).open();
-
- // When the dialog opens, initialize the copy feature and destroy it when the dialog is closed
- var $clipboardBtn;
- var clipboard;
- dialog.opened(function () {
- $clipboardBtn = dialog.$(".o_clipboard_button");
- $clipboardBtn.tooltip({title: _t("Copied !"), trigger: "manual", placement: "left"});
- clipboard = new window.Clipboard($clipboardBtn[0], {
- text: function () {
- return (_t("Error") + ":\n" + error.message + "\n\n" + error.data.debug).trim();
- }
- });
- clipboard.on("success", function (e) {
- _.defer(function () {
- $clipboardBtn.tooltip("show");
- _.delay(function () {
- $clipboardBtn.tooltip("hide");
- }, 800);
- });
- });
- });
- dialog.on("closed", this, function () {
- $clipboardBtn.tooltip("destroy");
- clipboard.destroy();
- });
-
- // When the full traceback is shown, scroll it to the end (useful for better python error reporting)
- dialog.$(".o_error_detail").on("shown.bs.collapse", function (e) {
- e.target.scrollTop = e.target.scrollHeight;
- });
- },
- show_message: function(exception) {
- this.show_error({
- type: _t("Odoo Client Error"),
- message: exception,
- data: {debug: ""}
- });
- },
-});
-
-/**
- An interface to implement to handle exceptions. Register implementation in instance.web.crash_manager_registry.
-*/
-var ExceptionHandler = {
- /**
- @param parent The parent.
- @param error The error object as returned by the JSON-RPC implementation.
- */
- init: function(parent, error) {},
- /**
- Called to inform to display the widget, if necessary. A typical way would be to implement
- this interface in a class extending instance.web.Dialog and simply display the dialog in this
- method.
- */
- display: function() {},
-};
-
-
-/**
- * Handle redirection warnings, which behave more or less like a regular
- * warning, with an additional redirection button.
- */
-var RedirectWarningHandler = Dialog.extend(ExceptionHandler, {
- init: function(parent, error) {
- this._super(parent);
- this.error = error;
- },
- display: function() {
- var self = this;
- var error = this.error;
- error.data.message = error.data.arguments[0];
-
- new Dialog(this, {
- size: 'medium',
- title: _.str.capitalize(error.type) || _t("Odoo Warning"),
- buttons: [
- {text: error.data.arguments[2], classes : "btn-primary", click: function() {
- window.location.href = '#action='+error.data.arguments[1];
- self.destroy();
- }},
- {text: _t("Cancel"), click: function() { self.destroy(); }, close: true}
- ],
- $content: QWeb.render('CrashManager.warning', {error: error}),
- }).open();
- }
-});
-
-core.crash_registry.add('openerp.exceptions.RedirectWarning', RedirectWarningHandler);
-
-return CrashManager;
-});
diff --git a/addons/web/static/src/js/framework/data.js b/addons/web/static/src/js/framework/data.js
deleted file mode 100644
index 1d4258019ae..00000000000
--- a/addons/web/static/src/js/framework/data.js
+++ /dev/null
@@ -1,1135 +0,0 @@
-odoo.define('web.data', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-var mixins = require('web.mixins');
-var Model = require('web.Model');
-var session = require('web.session');
-var translation = require('web.translation');
-var pyeval = require('web.pyeval');
-var utils = require('web.utils');
-
-var _t = translation._t;
-
-/**
- * Serializes the sort criterion array of a dataset into a form which can be
- * consumed by OpenERP's RPC APIs.
- *
- * @param {Array} criterion array of fields, from first to last criteria, prefixed with '-' for reverse sorting
- * @returns {String} SQL-like sorting string (``ORDER BY``) clause
- */
-function serialize_sort(criterion) {
- return _.map(criterion,
- function (criteria) {
- if (criteria[0] === '-') {
- return criteria.slice(1) + ' DESC';
- }
- return criteria + ' ASC';
- }).join(', ');
-}
-
-/**
- * Reverse of the serialize_sort function: convert an array of SQL-like sort
- * descriptors into a list of fields prefixed with '-' if necessary.
- */
-function deserialize_sort(criterion) {
- return _.map(criterion, function (criteria) {
- var split = _.without(criteria.split(' '), '');
- return (split[1] && split[1].toLowerCase() === 'desc' ? '-' : '') + split[0];
- });
-}
-
-var Query = Class.extend({
- init: function (model, fields) {
- this._model = model;
- this._fields = fields;
- this._filter = [];
- this._context = {};
- this._lazy = true;
- this._limit = false;
- this._offset = 0;
- this._order_by = [];
- },
- clone: function (to_set) {
- to_set = to_set || {};
- var q = new Query(this._model, this._fields);
- q._context = this._context;
- q._filter = this._filter;
- q._lazy = this._lazy;
- q._limit = this._limit;
- q._offset = this._offset;
- q._order_by = this._order_by;
-
- for(var key in to_set) {
- if (!to_set.hasOwnProperty(key)) { continue; }
- switch(key) {
- case 'filter':
- q._filter = new CompoundDomain(
- q._filter, to_set.filter);
- break;
- case 'context':
- q._context = new CompoundContext(
- q._context, to_set.context);
- break;
- case 'lazy':
- case 'limit':
- case 'offset':
- case 'order_by':
- q['_' + key] = to_set[key];
- }
- }
- return q;
- },
- _execute: function (options) {
- var self = this;
- options = options || {};
- return session.rpc('/web/dataset/search_read', {
- model: this._model.name,
- fields: this._fields || false,
- domain: pyeval.eval('domains',
- [this._model.domain(this._filter)]),
- context: pyeval.eval('contexts',
- [this._model.context(this._context)]),
- offset: this._offset,
- limit: this._limit,
- sort: serialize_sort(this._order_by)
- }, options).then(function (results) {
- self._count = results.length;
- return results.records;
- }, null);
- },
- /**
- * Fetches the first record matching the query, or null
- *
- * @param {Object} [options] additional options for the rpc() method
- * @returns {jQuery.Deferred<Object|null>}
- */
- first: function (options) {
- var self = this;
- return this.clone({limit: 1})._execute(options).then(function (records) {
- delete self._count;
- if (records.length) { return records[0]; }
- return null;
- });
- },
- /**
- * Fetches all records matching the query
- *
- * @param {Object} [options] additional options for the rpc() method
- * @returns {jQuery.Deferred<Array<>>}
- */
- all: function (options) {
- return this._execute(options);
- },
- /**
- * Fetches the number of records matching the query in the database
- *
- * @returns {jQuery.Deferred<Number>}
- */
- count: function () {
- if (this._count !== undefined) { return $.when(this._count); }
- return this._model.call(
- 'search_count', [this._filter], {
- context: this._model.context(this._context)});
- },
- /**
- * Performs a groups read according to the provided grouping criterion
- *
- * @param {String|Array<String>} grouping
- * @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null}
- */
- group_by: function (grouping) {
- var ctx = pyeval.eval(
- 'context', this._model.context(this._context));
-
- // undefined passed in explicitly (!)
- if (_.isUndefined(grouping)) {
- grouping = [];
- }
-
- if (!(grouping instanceof Array)) {
- grouping = _.toArray(arguments);
- }
- if (_.isEmpty(grouping) && !ctx.group_by_no_leaf) {
- return null;
- }
- var raw_fields = _.map(grouping.concat(this._fields || []), function (field) {
- return field.split(':')[0];
- });
-
- var self = this;
- return this._model.call('read_group', {
- groupby: grouping,
- fields: _.uniq(raw_fields),
- domain: this._model.domain(this._filter),
- context: ctx,
- offset: this._offset,
- lazy: this._lazy,
- limit: this._limit,
- orderby: serialize_sort(this._order_by) || false
- }).then(function (results) {
- return _(results).map(function (result) {
- // FIX: querygroup initialization
- result.__context = result.__context || {};
- result.__context.group_by = result.__context.group_by || [];
- _.defaults(result.__context, ctx);
- var grouping_fields = self._lazy ? [grouping[0]] : grouping;
- return new QueryGroup(
- self._model.name, grouping_fields, result);
- });
- });
- },
- /**
- * Creates a new query with the union of the current query's context and
- * the new context.
- *
- * @param context context data to add to the query
- * @returns {openerp.web.Query}
- */
- context: function (context) {
- if (!context) { return this; }
- return this.clone({context: context});
- },
- /**
- * Creates a new query with the union of the current query's filter and
- * the new domain.
- *
- * @param domain domain data to AND with the current query filter
- * @returns {openerp.web.Query}
- */
- filter: function (domain) {
- if (!domain) { return this; }
- return this.clone({filter: domain});
- },
- /**
- * Creates a new query with the provided parameter lazy replacing the current
- * query's own.
- *
- * @param {Boolean} lazy indicates if the read_group should return only the
- * first level of groupby records, or should return the records grouped by
- * all levels at once (so, it makes only 1 db request).
- * @returns {openerp.web.Query}
- */
- lazy: function (lazy) {
- return this.clone({lazy: lazy});
- },
- /**
- * Creates a new query with the provided limit replacing the current
- * query's own limit
- *
- * @param {Number} limit maximum number of records the query should retrieve
- * @returns {openerp.web.Query}
- */
- limit: function (limit) {
- return this.clone({limit: limit});
- },
- /**
- * Creates a new query with the provided offset replacing the current
- * query's own offset
- *
- * @param {Number} offset number of records the query should skip before starting its retrieval
- * @returns {openerp.web.Query}
- */
- offset: function (offset) {
- return this.clone({offset: offset});
- },
- /**
- * Creates a new query with the provided ordering parameters replacing
- * those of the current query
- *
- * @param {String...} fields ordering clauses
- * @returns {openerp.web.Query}
- */
- order_by: function (fields) {
- if (fields === undefined) { return this; }
- if (!(fields instanceof Array)) {
- fields = _.toArray(arguments);
- }
- if (_.isEmpty(fields)) { return this; }
- return this.clone({order_by: fields});
- }
-});
-
-var QueryGroup = Class.extend({
- init: function (model, grouping_fields, read_group_group) {
- // In cases where group_by_no_leaf and no group_by, the result of
- // read_group has aggregate fields but no __context or __domain.
- // Create default (empty) values for those so that things don't break
- var fixed_group = _.extend(
- {__context: {group_by: []}, __domain: []},
- read_group_group);
-
- var count_key = (grouping_fields[0] && grouping_fields[0].split(':')[0]) + '_count';
- var aggregates = {};
- _(fixed_group).each(function (value, key) {
- if (key.indexOf('__') === 0
- || _.contains(grouping_fields, key)
- || (key === count_key)) {
- return;
- }
- aggregates[key] = value || 0;
- });
-
- this.model = new Model(
- model, fixed_group.__context, fixed_group.__domain);
-
- var group_size = fixed_group[count_key] || fixed_group.__count || 0;
- var leaf_group = fixed_group.__context.group_by.length === 0;
-
- var value = (grouping_fields.length === 1)
- ? fixed_group[grouping_fields[0]]
- : _.map(grouping_fields, function (field) { return fixed_group[field]; });
- var grouped_on = (grouping_fields.length === 1)
- ? grouping_fields[0]
- : grouping_fields;
- this.attributes = {
- folded: !!(fixed_group.__fold),
- grouped_on: grouped_on,
- // if terminal group (or no group) and group_by_no_leaf => use group.__count
- length: group_size,
- value: value,
- // A group is open-able if it's not a leaf in group_by_no_leaf mode
- has_children: !(leaf_group && fixed_group.__context.group_by_no_leaf),
-
- aggregates: aggregates
- };
- },
- get: function (key) {
- return this.attributes[key];
- },
- subgroups: function () {
- return this.model.query().group_by(this.model.context().group_by);
- },
- query: function () {
- return this.model.query.apply(this.model, arguments);
- }
-});
-
-var DataSet = Class.extend(mixins.PropertiesMixin, {
- /**
- * Collection of OpenERP records, used to share records and the current selection between views.
- *
- * @constructs instance.web.DataSet
- *
- * @param {String} model the OpenERP model this dataset will manage
- */
- init: function(parent, model, context) {
- mixins.PropertiesMixin.init.call(this);
- this.model = model;
- this.context = context || {};
- this.index = null;
- this._sort = [];
- this._model = new Model(model, context);
- this.orderer = new utils.DropMisordered();
- },
- previous: function () {
- this.index -= 1;
- if (!this.ids.length) {
- this.index = null;
- } else if (this.index < 0) {
- this.index = this.ids.length - 1;
- }
- return this;
- },
- next: function () {
- this.index += 1;
- if (!this.ids.length) {
- this.index = null;
- } else if (this.index >= this.ids.length) {
- this.index = 0;
- }
- return this;
- },
- select_id: function(id) {
- var idx = this.get_id_index(id);
- if (idx === null) {
- return false;
- } else {
- this.index = idx;
- return true;
- }
- },
- get_id_index: function(id) {
- for (var i=0, ii=this.ids.length; i<ii; i++) {
- // Here we use type coercion because of the mess potentially caused by
- // OpenERP ids fetched from the DOM as string. (eg: dhtmlxcalendar)
- // OpenERP ids can be non-numeric too ! (eg: recursive events in calendar)
- if (id == this.ids[i]) {
- return i;
- }
- }
- return null;
- },
- /**
- * Read records.
- *
- * @param {Array} ids identifiers of the records to read
- * @param {Array} [fields] fields to read and return, by default all fields are returned
- * @param {Object} [options]
- * @returns {$.Deferred}
- */
- read_ids: function (ids, fields, options) {
- if (_.isEmpty(ids))
- return $.Deferred().resolve([]);
-
- options = options || {};
- var method = 'read';
- var ids_arg = ids;
- var context = this.get_context(options.context);
- if (options.check_access_rule === true){
- method = 'search_read';
- ids_arg = [['id', 'in', ids]];
- context = new CompoundContext(context, {active_test: false});
- }
- return this._model.call(method,
- [ids_arg, fields || false],
- {context: context})
- .then(function (records) {
- if (records.length <= 1) { return records; }
- var indexes = {};
- for (var i = 0; i < ids.length; i++) {
- indexes[ids[i]] = i;
- }
- records.sort(function (a, b) {
- return indexes[a.id] - indexes[b.id];
- });
- return records;
- });
- },
- /**
- * Read a slice of the records represented by this DataSet, based on its
- * domain and context.
- *
- * @param {Array} [fields] fields to read and return, by default all fields are returned
- * @params {Object} [options]
- * @param {Number} [options.offset=0] The index from which selected records should be returned
- * @param {Number} [options.limit=null] The maximum number of records to return
- * @returns {$.Deferred}
- */
- read_slice: function (fields, options) {
- var self = this;
- options = options || {};
- var query = this._model.query(fields)
- .limit(options.limit || false)
- .offset(options.offset || 0)
- .all();
- return this.orderer.add(query).done(function (records) {
- self.ids = _(records).pluck('id');
- });
- },
- /**
- * Reads the current dataset record (from its index)
- *
- * @params {Array} [fields] fields to read and return, by default all fields are returned
- * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
- * @returns {$.Deferred}
- */
- read_index: function (fields, options) {
- options = options || {};
- return this.read_ids([this.ids[this.index]], fields, options).then(function (records) {
- if (_.isEmpty(records)) { return $.Deferred().reject().promise(); }
- return records[0];
- });
- },
- /**
- * Reads default values for the current model
- *
- * @param {Array} [fields] fields to get default values for, by default all defaults are read
- * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
- * @returns {$.Deferred}
- */
- default_get: function(fields, options) {
- options = options || {};
- return this._model.call('default_get',
- [fields], {context: this.get_context(options.context)});
- },
- /**
- * Creates a new record in db
- *
- * @param {Object} data field values to set on the new record
- * @param {Object} options Dictionary that can contain the following keys:
- * - readonly_fields: Values from readonly fields that were updated by
- * on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
- * @returns {$.Deferred}
- */
- create: function(data, options) {
- var self = this;
- return this._model.call('create', [data], {
- context: this.get_context()
- }).done(function () {
- self.trigger('dataset_changed', data, options);
- });
- },
- /**
- * Saves the provided data in an existing db record
- *
- * @param {Number|String} id identifier for the record to alter
- * @param {Object} data field values to write into the record
- * @param {Object} options Dictionary that can contain the following keys:
- * - context: The context to use in the server-side call.
- * - readonly_fields: Values from readonly fields that were updated by
- * on_changes. Only used by the BufferedDataSet to make the o2m work correctly.
- * @returns {$.Deferred}
- */
- write: function (id, data, options) {
- options = options || {};
- var self = this;
- return this._model.call('write', [[id], data], {
- context: this.get_context(options.context)
- }).done(function () {
- self.trigger('dataset_changed', id, data, options);
- });
- },
- /**
- * Deletes an existing record from the database
- *
- * @param {Number|String} ids identifier of the record to delete
- */
- unlink: function(ids) {
- var self = this;
- return this._model.call('unlink', [ids], {
- context: this.get_context()
- }).done(function () {
- self.trigger('dataset_changed', ids);
- });
- },
- /**
- * Calls an arbitrary RPC method
- *
- * @param {String} method name of the method (on the current model) to call
- * @param {Array} [args] arguments to pass to the method
- * @param {Function} callback
- * @param {Function} error_callback
- * @returns {$.Deferred}
- */
- call: function (method, args) {
- return this._model.call(method, args);
- },
- /**
- * Calls a button method, usually returning some sort of action
- *
- * @param {String} method
- * @param {Array} [args]
- * @returns {$.Deferred}
- */
- call_button: function (method, args) {
- return this._model.call_button(method, args);
- },
- /**
- * Fetches the "readable name" for records, based on intrinsic rules
- *
- * @param {Array} ids
- * @returns {$.Deferred}
- */
- name_get: function(ids) {
- return this._model.call('name_get', [ids], {context: this.get_context()});
- },
- /**
- *
- * @param {String} name name to perform a search for/on
- * @param {Array} [domain=[]] filters for the objects returned, OpenERP domain
- * @param {String} [operator='ilike'] matching operator to use with the provided name value
- * @param {Number} [limit=0] maximum number of matches to return
- * @param {Function} callback function to call with name_search result
- * @returns {$.Deferred}
- */
- name_search: function (name, domain, operator, limit) {
- return this._model.call('name_search', {
- name: name || '',
- args: domain || false,
- operator: operator || 'ilike',
- context: this._model.context(),
- limit: limit || 0
- });
- },
- /**
- * @param name
- */
- name_create: function(name, context) {
- return this._model.call('name_create', [name], {context: this.get_context(context)});
- },
- get_context: function(request_context) {
- return this._model.context(request_context);
- },
- /**
- * Reads or changes sort criteria on the dataset.
- *
- * If not provided with any argument, serializes the sort criteria to
- * an SQL-like form usable by OpenERP's ORM.
- *
- * If given a field, will set that field as first sorting criteria or,
- * if the field is already the first sorting criteria, will reverse it.
- *
- * @param {String} [field] field to sort on, reverses it (toggle from ASC to DESC) if already the main sort criteria
- * @param {Boolean} [force_reverse=false] forces inserting the field as DESC
- * @returns {String|undefined}
- */
- sort: function (field, force_reverse) {
- if (!field) {
- return serialize_sort(this._sort);
- }
- var reverse = force_reverse || (this._sort[0] === field);
- this._sort.splice.apply(
- this._sort, [0, this._sort.length].concat(
- _.without(this._sort, field, '-' + field)));
-
- this._sort.unshift((reverse ? '-' : '') + field);
- return undefined;
- },
- /**
- * Set the sort criteria on the dataset.
- *
- * @param {Array} fields_list: list of fields order descriptors, as used by
- * Odoo's ORM (such as 'name desc', 'product_id', 'order_date asc')
- */
- set_sort: function (fields_list) {
- this._sort = deserialize_sort(fields_list);
- },
- size: function () {
- return this.ids.length;
- },
- alter_ids: function(n_ids) {
- this.ids = n_ids;
- },
- remove_ids: function (ids) {
- this.alter_ids(_(this.ids).difference(ids));
- },
- add_ids: function(ids, at) {
- var args = [at, 0].concat(_.difference(ids, this.ids));
- this.ids.splice.apply(this.ids, args);
- },
- /**
- * Resequence records.
- *
- * @param {Array} ids identifiers of the records to resequence
- * @returns {$.Deferred}
- */
- resequence: function (ids, options) {
- options = options || {};
- return session.rpc('/web/dataset/resequence', {
- model: this.model,
- ids: ids,
- context: pyeval.eval(
- 'context', this.get_context(options.context)),
- }).then(function (results) {
- return results;
- });
- },
-});
-
-var DataSetStatic = DataSet.extend({
- init: function(parent, model, context, ids) {
- this._super(parent, model, context);
- // all local records
- this.ids = ids || [];
- },
- read_slice: function (fields, options) {
- options = options || {};
- fields = fields || {};
- var offset = options.offset || 0,
- limit = options.limit || false;
- var end_pos = limit && limit !== -1 ? offset + limit : this.ids.length;
- return this.read_ids(this.ids.slice(offset, end_pos), fields, options);
- },
- set_ids: function (ids) {
- this.ids = ids;
- if (ids.length === 0) {
- this.index = null;
- } else if (this.index >= ids.length - 1) {
- this.index = ids.length - 1;
- }
- },
- unlink: function(ids) {
- this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
- this.trigger('unlink', ids);
- return $.Deferred().resolve({result: true});
- },
-});
-
-var DataSetSearch = DataSet.extend({
- /**
- * @constructs instance.web.DataSetSearch
- * @extends instance.web.DataSet
- *
- * @param {Object} parent
- * @param {String} model
- * @param {Object} context
- * @param {Array} domain
- */
- init: function(parent, model, context, domain) {
- this._super(parent, model, context);
- this.domain = domain || [];
- this._length = null;
- this.ids = [];
- this._model = new Model(model, context, domain);
- },
- /**
- * Read a slice of the records represented by this DataSet, based on its
- * domain and context.
- *
- * @params {Object} options
- * @param {Array} [options.fields] fields to read and return, by default all fields are returned
- * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
- * @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain
- * @param {Number} [options.offset=0] The index from which selected records should be returned
- * @param {Number} [options.limit=null] The maximum number of records to return
- * @returns {$.Deferred}
- */
- read_slice: function (fields, options) {
- options = options || {};
- var self = this;
- var q = this._model.query(fields || false)
- .filter(options.domain)
- .context(options.context)
- .offset(options.offset || 0)
- .limit(options.limit || false);
- q = q.order_by.apply(q, this._sort);
-
- return this.orderer.add(q.all()).done(function (records) {
- // FIXME: not sure about that one, *could* have discarded count
- q.count().done(function (count) { self._length = count; });
- self.ids = _(records).pluck('id');
- });
- },
- get_domain: function (other_domain) {
- return this._model.domain(other_domain);
- },
- alter_ids: function (ids) {
- this._super(ids);
- if (this.index !== null && this.index >= this.ids.length) {
- this.index = this.ids.length > 0 ? this.ids.length - 1 : 0;
- }
- },
- remove_ids: function (ids) {
- var before = this.ids.length;
- this._super(ids);
- if (this._length) {
- this._length -= (before - this.ids.length);
- }
- },
- add_ids: function(ids, at) {
- var before = this.ids.length;
- this._super(ids, at);
- if(this._length){
- this._length += (this.ids.length - before);
- }
- },
- unlink: function(ids, callback, error_callback) {
- var self = this;
- return this._super(ids).done(function() {
- self.remove_ids( ids);
- self.trigger("dataset_changed", ids, callback, error_callback);
- });
- },
- size: function () {
- if (this._length !== null) {
- return this._length;
- }
- return this._super();
- }
-});
-
-var BufferedDataSet = DataSetStatic.extend({
- virtual_id_prefix: "one2many_v_id_",
- debug_mode: true,
- init: function() {
- this._super.apply(this, arguments);
- this.reset_ids([]);
- this.last_default_get = {};
- this.running_reads = [];
- this.mutex = new utils.Mutex();
- },
- default_get: function(fields, options) {
- var self = this;
- return this._super(fields, options).done(function(res) {
- self.last_default_get = _.clone(res);
- });
- },
- get_cache: function (id) {
- if (!this.cache[id]) {
- this.cache[id] = {
- 'id': id,
- 'values': {},
- 'from_read': {},
- 'changes': {},
- 'readonly_fields': {},
- 'to_create': false,
- 'to_delete': false};
- }
- return this.cache[id];
- },
- _update_cache: function (id, options) {
- // One should call this method after modifying this.from_read,
- // this.to_create or this.change. It updates this.cache and
- // this.readonly_fields.
- var cached = this.get_cache(id);
- if (options) {
- _.extend(cached.from_read, options.from_read);
- _.extend(cached.changes, options.changes);
- _.extend(cached.readonly_fields, options.readonly_fields);
- if (options.to_create !== undefined) cached.to_create = options.to_create;
- if (options.to_delete !== undefined) cached.to_delete = options.to_delete;
- }
- cached.values = _.extend({'id': id}, cached.from_read, cached.changes, cached.readonly_fields);
- return cached;
- },
- create: function(data, options) {
- var changes = _.extend({}, this.last_default_get, data);
- var cached = this._update_cache(_.uniqueId(this.virtual_id_prefix), _.extend({'changes': changes, 'to_create': true}, options));
- this.trigger("dataset_changed", data, options);
- return $.Deferred().resolve(cached.id);
- },
- write: function (id, data, options) {
- var self = this;
- var cached = this.get_cache(id);
-
- // if update after a remove, it's like an add before updating
- cached.to_delete = false;
-
- // apply change
- var def = $.Deferred();
- this.mutex.exec(function () {
- var dirty = false;
- // _.each is broken if a field "length" is present
- for (var k in data) {
- if (!_.isEqual(data[k], cached.values[k])) {
- dirty = true;
- if (_.isEqual(data[k], cached.from_read[k])) { // clean changes
- delete cached.changes[k];
- } else {
- cached.changes[k] = data[k];
- }
- } else {
- delete data[k];
- }
- }
- self._update_cache(id, options);
-
- if (dirty) {
- self.trigger("dataset_changed", id, data, options);
- }
-
- return def.resolve(data).promise();
- });
-
- return def;
- },
- unlink: function(ids, callback, error_callback) {
- var self = this;
- _.each(ids, function (id) {
- self.get_cache(id).to_delete = true;
- });
- this.set_ids(_.difference(this.ids, _.pluck(_.filter(this.cache, function (c) {return c.to_delete;}), 'id')));
- this.trigger("dataset_changed", ids, callback, error_callback);
- return utils.async_when({result: true}).done(callback);
- },
- reset_ids: function(ids, options) {
- var self = this;
- this.set_ids(ids);
- if (!options || !options.keep_read_data) {
- this.cache = {};
- } else {
- _.each(this.cache, function (cache) {
- self._update_cache(cache.id, {'changes': {}, 'to_delete': false});
- });
- }
- this.delete_all = false;
- this.cancel_read();
- },
- cancel_read: function () {
- _.invoke(_.clone(this.running_reads), 'reject');
- },
- read_ids: function (ids, fields, options) {
- // read what is necessary from the server to have ids and the given
- // fields in this.from_read
- var self = this;
- var to_get = _.filter(ids, function(id) {
- var cache = self.get_cache(id);
- return !cache.to_create && _.any(fields, function(x) {return cache.from_read[x] === undefined;});
- });
- options = options || {};
-
- var return_records = function() {
- var records = _.map(ids, function(id) {
- return _.clone(self.get_cache(id).values);
- });
- if (self.debug_mode) {
- if (_.include(records, undefined)) {
- throw "Record not correctly loaded";
- }
- }
- var sort_fields = self._sort,
- compare = function (v1, v2) {
- return (v1 < v2) ? -1
- : (v1 > v2) ? 1
- : 0;
- };
- // Array.sort is not necessarily stable. We must be careful with this because
- // sorting an array where all items are considered equal is a worst-case that
- // will randomize the array with an unstable sort! Therefore we must avoid
- // sorting if there are no sort_fields (i.e. all items are considered equal)
- // See also: http://ecma262-5.com/ELS5_Section_15.htm#Section_15.4.4.11
- // http://code.google.com/p/v8/issues/detail?id=90
- if (sort_fields.length) {
- records.sort(function (a, b) {
- return _.reduce(sort_fields, function (acc, field) {
- if (acc) { return acc; }
- var sign = 1;
- if (field[0] === '-') {
- sign = -1;
- field = field.slice(1);
- }
- if(!a[field] && a[field] !== 0){ return sign; }
- if(!b[field] && b[field] !== 0){ return (sign == -1) ? 1 : -1; }
- //m2o should be searched based on value[1] not based whole value(i.e. [id, value])
- if(_.isArray(a[field]) && a[field].length == 2 && _.isString(a[field][1])){
- return sign * compare(a[field][1], b[field][1]);
- }
- return sign * compare(a[field], b[field]);
- }, 0);
- });
- }
- return $.when(records);
- };
- if(to_get.length > 0) {
- var def = $.Deferred();
- self.running_reads.push(def);
- def.always(function() {
- self.running_reads = _.without(self.running_reads, def);
- });
- var _super = this._super;
- this.mutex.exec(function () {
- if (def.state() !== "pending") return;
- _super.call(self, to_get, fields, options).then(_.bind(def.resolve, def), _.bind(def.reject, def));
- return def;
- });
- return def.then(function(records) {
- _.each(records, function(record, index) {
- // add information into from_read
- var id = to_get[index];
- self._update_cache(id, _.extend(options, {'from_read': record}));
- });
- return return_records();
- });
- } else {
- return return_records();
- }
- },
- /**
- * Invalidates caching of a record in the dataset to ensure the next read
- * of that record will hit the server.
- *
- * Of use when an action is going to remote-alter a record which will then
- * need to be reloaded, e.g. action button.
- *
- * @param {Object} id record to remove from the BDS's cache
- */
- evict_record: function (id) {
- // Don't evict records which haven't yet been saved: there is no more
- // recent data on the server (and there potentially isn't any data),
- // and this breaks the assumptions of other methods (that the data
- // for new and altered records is both in the cache and in the change
- // or to_create collection)
- this.get_cache(id).from_read = {};
- this._update_cache(id);
- },
- call_button: function (method, args) {
- this.evict_record(args[0][0]);
- return this._super(method, args);
- },
- alter_ids: function(n_ids, options) {
- var dirty = !_.isEqual(this.ids, n_ids);
- this._super(n_ids, options);
- if (dirty) {
- this.trigger("dataset_changed", n_ids, options);
- }
- },
-});
-
-BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
-
-var ProxyDataSet = DataSetSearch.extend({
- init: function() {
- this._super.apply(this, arguments);
- this.create_function = null;
- this.write_function = null;
- this.read_function = null;
- this.default_get_function = null;
- this.unlink_function = null;
- },
- read_ids: function (ids, fields, options) {
- if (this.read_function) {
- return this.read_function(ids, fields, options, this._super);
- } else {
- return this._super.apply(this, arguments);
- }
- },
- default_get: function(fields, options) {
- if (this.default_get_function) {
- return this.default_get_function(fields, options, this._super);
- } else {
- return this._super.apply(this, arguments);
- }
- },
- create: function(data, options) {
- if (this.create_function) {
- return this.create_function(data, options, this._super);
- } else {
- return this._super.apply(this, arguments);
- }
- },
- write: function (id, data, options) {
- if (this.write_function) {
- return this.write_function(id, data, options, this._super);
- } else {
- return this._super.apply(this, arguments);
- }
- },
- unlink: function(ids) {
- if (this.unlink_function) {
- return this.unlink_function(ids, this._super);
- } else {
- return this._super.apply(this, arguments);
- }
- },
-});
-
-var CompoundContext = Class.extend({
- init: function () {
- this.__ref = "compound_context";
- this.__contexts = [];
- this.__eval_context = null;
- var self = this;
- _.each(arguments, function(x) {
- self.add(x);
- });
- },
- add: function (context) {
- this.__contexts.push(context);
- return this;
- },
- set_eval_context: function (eval_context) {
- this.__eval_context = eval_context;
- return this;
- },
- get_eval_context: function () {
- return this.__eval_context;
- },
- eval: function() {
- return pyeval.eval('context', this, undefined, {no_user_context: true});
- },
-});
-
-var CompoundDomain = Class.extend({
- init: function () {
- this.__ref = "compound_domain";
- this.__domains = [];
- this.__eval_context = null;
- var self = this;
- _.each(arguments, function(x) {
- self.add(x);
- });
- },
- add: function(domain) {
- this.__domains.push(domain);
- return this;
- },
- set_eval_context: function(eval_context) {
- this.__eval_context = eval_context;
- return this;
- },
- get_eval_context: function() {
- return this.__eval_context;
- },
- eval: function() {
- return pyeval.eval('domain', this);
- },
-});
-
-function compute_domain (expr, fields) {
- if (! (expr instanceof Array))
- return !! expr;
- var stack = [];
- for (var i = expr.length - 1; i >= 0; i--) {
- var ex = expr[i];
- if (ex.length == 1) {
- var top = stack.pop();
- switch (ex) {
- case '|':
- stack.push(stack.pop() || top);
- continue;
- case '&':
- stack.push(stack.pop() && top);
- continue;
- case '!':
- stack.push(!top);
- continue;
- default:
- throw new Error(_.str.sprintf(
- _t("Unknown operator %s in domain %s"),
- ex, JSON.stringify(expr)));
- }
- }
-
- var field = fields[ex[0]];
- if (!field) {
- throw new Error(_.str.sprintf(
- _t("Unknown field %s in domain %s"),
- ex[0], JSON.stringify(expr)));
- }
- var field_value = field.get_value ? field.get_value() : field.value;
- var op = ex[1];
- var val = ex[2];
-
- switch (op.toLowerCase()) {
- case '=':
- case '==':
- stack.push(_.isEqual(field_value, val));
- break;
- case '!=':
- case '<>':
- stack.push(!_.isEqual(field_value, val));
- break;
- case '<':
- stack.push(field_value < val);
- break;
- case '>':
- stack.push(field_value > val);
- break;
- case '<=':
- stack.push(field_value <= val);
- break;
- case '>=':
- stack.push(field_value >= val);
- break;
- case 'in':
- if (!_.isArray(val)) val = [val];
- stack.push(_(val).contains(field_value));
- break;
- case 'not in':
- if (!_.isArray(val)) val = [val];
- stack.push(!_(val).contains(field_value));
- break;
- default:
- console.warn(
- _t("Unsupported operator %s in domain %s"),
- op, JSON.stringify(expr));
- }
- }
- return _.all(stack, _.identity);
-}
-
-return {
- Query: Query,
- DataSet: DataSet,
- DataSetStatic: DataSetStatic,
- DataSetSearch: DataSetSearch,
- BufferedDataSet: BufferedDataSet,
- ProxyDataSet: ProxyDataSet,
- CompoundContext: CompoundContext,
- CompoundDomain: CompoundDomain,
- compute_domain: compute_domain,
- noDisplayContent: "<em class=\"text-warning\">" + _t("Unnamed") + "</em>",
-};
-
-});
diff --git a/addons/web/static/src/js/framework/data_manager.js b/addons/web/static/src/js/framework/data_manager.js
deleted file mode 100644
index 55a4b4da15b..00000000000
--- a/addons/web/static/src/js/framework/data_manager.js
+++ /dev/null
@@ -1,280 +0,0 @@
-odoo.define('web.DataManager', function (require) {
-"use strict";
-
-var core = require('web.core');
-var Model = require('web.DataModel');
-var session = require('web.session');
-var utils = require('web.utils');
-
-function traverse(tree, f) {
- if (f(tree)) {
- _.each(tree.children, function(c) { traverse(c, f); });
- }
-}
-
-return core.Class.extend({
- init: function () {
- this.Filters = new Model('ir.filters');
- this._init_cache();
- },
-
- _init_cache: function () {
- this._cache = {
- actions: {},
- fields_views: {},
- fields: {},
- filters: {},
- views: {},
- };
- },
-
- /**
- * Invalidates the whole cache
- * Suggestion: could be refined to invalidate some part of the cache
- */
- invalidate: function () {
- this._init_cache();
- },
-
- /**
- * Loads an action from its id or xmlid.
- *
- * @param {int|string} [action_id] the action id or xmlid
- * @param {Object} [additional_context] used to load the action
- * @return {Deferred} resolved with the action whose id or xmlid is action_id
- */
- load_action: function (action_id, additional_context) {
- var self = this;
- var key = this._gen_key(action_id, additional_context || {});
-
- if (!this._cache.actions[key]) {
- this._cache.actions[key] = session.rpc("/web/action/load", {
- action_id: action_id,
- additional_context : additional_context,
- }).then(function (action) {
- self._cache.actions[key] = action.no_cache ? null : self._cache.actions[key];
- return action;
- }, this._invalidate.bind(this, this._cache.actions, key));
- }
-
- return this._cache.actions[key].then(function (action) {
- return $.extend(true, {}, action);
- });
- },
-
- /**
- * Loads various information concerning views: fields_view for each view,
- * and optionally filters and fields.
- *
- * @param {Object} [dataset] the dataset for which the views are loaded
- * @param {Array} [views_descr] array of [view_id, view_type]
- * @param {Object} [options] dictionnary of various options:
- * - options.load_filters: whether or not to load the filters,
- * - options.action_id: the action_id (required to load filters),
- * - options.load_fields: whether or not to load the fields,
- * - options.toolbar: whether or not a toolbar will be displayed,
- * @return {Deferred} resolved with the requested views information
- */
- load_views: function (dataset, views_descr, options) {
- var self = this;
- var model = dataset.model;
- var context = dataset.get_context();
- var key = this._gen_key(model, views_descr, options || {}, context);
-
- if (!this._cache.views[key]) {
- // Don't load fields or filters if already in cache
- options.load_fields = options.load_fields && !this._cache.fields[model];
- var filters_key;
- if (options.load_filters) {
- filters_key = this._gen_key(model, options.action_id);
- options.load_filters = !this._cache.filters[filters_key];
- }
-
- this._cache.views[key] = dataset.call('load_views', {
- views: views_descr,
- options: options,
- context: context,
- }).then(function (result) {
- // Postprocess fields_views and insert them into the fields_views cache
- result.fields_views = _.mapObject(result.fields_views, self._postprocess_fvg.bind(self)); // FIXME: lazy postprocess?
- _.each(views_descr, function (view_descr) {
- var toolbar = options.toolbar && view_descr[1] !== 'search';
- var fv_key = self._gen_key(model, view_descr[0], view_descr[1], toolbar, context);
- self._cache.fields_views[fv_key] = $.when(result.fields_views[view_descr[1]]);
- });
-
- // Insert filters, if any, into the filters cache
- if (result.filters) {
- self._cache.filters[filters_key] = $.when(result.filters);
- }
-
- // Insert fields, if any, into the fields cache
- if (result.fields) {
- self._cache.fields[model] = $.when(result.fields);
- }
-
- return result.fields_views;
- }, this._invalidate.bind(this, this._cache.views, key));
- }
-
- return this._cache.views[key];
- },
-
- /**
- * Loads the fields_view of a given model, view_type and optional view_id.
- *
- * @param {Object} [dataset] the dataset for which the fields_view is loaded
- * @param {int} [view_id] the id of the view (optional)
- * @param {string} [view_type] the type of the view
- * @param {Boolean} [toolbar] whether or not a toolbar will be displayed
- * @return {Deferred} resolved with the requested fields_view
- */
- load_fields_view: function (dataset, view_id, view_type, toolbar) {
- var key = this._gen_key(dataset.model, view_id, view_type, toolbar, dataset.get_context());
- if (!this._cache.fields_views[key]) {
- this._cache.fields_views[key] = dataset.call('fields_view_get', {
- view_id: view_id,
- view_type: view_type,
- toolbar: toolbar,
- context: dataset.get_context(),
- }).then(this._postprocess_fvg.bind(this), this._invalidate.bind(this, this._cache.fields_views, key));
- }
- return this._cache.fields_views[key];
- },
-
- /**
- * Loads the fields of a given model.
- *
- * @param {Object} [dataset] the dataset for which the fields are loaded
- * @return {Deferred} resolved with the requested fields
- */
- load_fields: function (dataset) {
- if (!this._cache.fields[dataset.model]) {
- this._cache.fields[dataset.model] = dataset.call('fields_get', {
- context: dataset.get_context(),
- }).fail(this._invalidate.bind(this, this._cache.fields, dataset.model));
- }
- return this._cache.fields[dataset.model];
- },
-
- /**
- * Loads the filters of a given model and optional action id.
- *
- * @param {Object} [dataset] the dataset for which the filters are loaded
- * @param {int} [action_id] the id of the action (optional)
- * @return {Deferred} resolved with the requested filters
- */
- load_filters: function (dataset, action_id) {
- var key = this._gen_key(dataset.model, action_id);
- if (!this._cache.filters[key]) {
- this._cache.filters[key] = this.Filters.call('get_filters', [dataset.model, action_id], {
- context: dataset.get_context(),
- }).fail(this._invalidate.bind(this, this._cache.filters, key));
- }
- return this._cache.filters[key];
- },
-
- /**
- * Calls 'create_or_replace' on 'ir_filters'.
- *
- * @param {Object} [filter] the filter description
- * @return {Deferred} resolved with the id of the created or replaced filter
- */
- create_filter: function (filter) {
- var self = this;
- return this.Filters
- .call('create_or_replace', [filter])
- .then(function (filter_id) {
- var key = [
- filter.model_id,
- filter.action_id || false,
- ].join(',');
- self._invalidate(self._cache.filters, key);
- return filter_id;
- });
- },
-
- /**
- * Calls 'unlink' on 'ir_filters'.
- *
- * @param {Object} [filter] the description of the filter to remove
- * @return {Deferred}
- */
- delete_filter: function (filter) {
- var self = this;
- return this.Filters
- .call('unlink', [filter.id])
- .then(function () {
- self._cache.filters = {}; // invalidate cache
- });
- },
-
- /**
- * Private function that postprocesses fields_view (mainly parses the arch attribute)
- */
- _postprocess_fvg: function (fields_view) {
- var self = this;
-
- // Parse and process arch
- var doc = $.parseXML(fields_view.arch).documentElement;
- var fields = fields_view.fields;
- fields_view.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
- traverse(fields_view.arch, function(node) {
- if (typeof node === 'string') {
- return false;
- }
- if (node.tag === 'field') {
- fields[node.attrs.name].__attrs = node.attrs;
- if (fields[node.attrs.name].type === 'many2one') { // FIXME: this shouldn't be done here
- if (node.attrs.widget === 'statusbar' || node.attrs.widget === 'radio') {
- fields[node.attrs.name].__fetch_status = true;
- } else if (node.attrs.widget === 'selection') {
- fields[node.attrs.name].__fetch_selection = true;
- }
- }
- if (node.attrs.widget === 'many2many_checkboxes') {
- fields[node.attrs.name].__fetch_many2manys = true;
- }
- return false;
- }
- return node.tag !== 'arch';
- });
-
- // Special case for id's
- if ('id' in fields_view.fields) {
- var id_field = fields_view.fields.id;
- id_field.original_type = id_field.type;
- id_field.type = 'id';
- }
-
- // Process inner views (one2manys)
- _.each(fields_view.fields, function(field) {
- _.each(field.views || {}, function(view) {
- self._postprocess_fvg(view);
- });
- });
-
- return fields_view;
- },
-
- /**
- * Private function that generates a cache key from its arguments
- */
- _gen_key: function () {
- return _.map(Array.prototype.slice.call(arguments), function (arg) {
- if (!arg) {
- return false;
- }
- return _.isObject(arg) ? JSON.stringify(arg) : arg;
- }).join(',');
- },
-
- /**
- * Private function that invalidates a cache entry
- */
- _invalidate: function (cache, key) {
- delete cache[key];
- },
-});
-
-});
diff --git a/addons/web/static/src/js/framework/data_model.js b/addons/web/static/src/js/framework/data_model.js
deleted file mode 100644
index f722339167f..00000000000
--- a/addons/web/static/src/js/framework/data_model.js
+++ /dev/null
@@ -1,80 +0,0 @@
-odoo.define('web.DataModel', function (require) {
-"use strict";
-
-var data = require('web.data');
-var Model = require('web.Model');
-var pyeval = require('web.pyeval');
-var session = require('web.session');
-
-Model.include({
- /**
- * Fetches a Query instance bound to this model, for searching
- *
- * @param {Array<String>} [fields] fields to ultimately fetch during the search
- * @returns {instance.web.Query}
- */
- query: function (fields) {
- return new data.Query(this, fields);
- },
- /**
- * Fetches the model's domain, combined with the provided domain if any
- *
- * @param {Array} [domain] to combine with the model's internal domain
- * @returns {instance.web.CompoundDomain} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain
- */
- domain: function (domain) {
- if (!domain) { return this._domain; }
- return new data.CompoundDomain(this._domain, domain);
- },
- /**
- * Fetches the combination of the user's context and the domain context,
- * combined with the provided context if any
- *
- * @param {Object} [context] to combine with the model's internal context
- * @returns {instance.web.CompoundContext} The union of the user's context and the model's internal context, as well as the provided context if any. In that order.
- */
- context: function (context) {
- return new data.CompoundContext(session.user_context, this._context, context || {});
- },
- /**
- * 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 = [];
- }
- pyeval.ensure_evaluated(args, kwargs);
- var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method;
- return session.rpc(call_kw, {
- model: this.name,
- method: method,
- args: args,
- kwargs: kwargs
- }, options);
- },
- call_button: function (method, args) {
- pyeval.ensure_evaluated(args, {});
- return session.rpc('/web/dataset/call_button', {
- model: this.name,
- method: method,
- // Should not be necessary anymore. Integrate remote in this?
- domain_id: null,
- context_id: args.length - 1,
- args: args || []
- });
- },
-
-});
-
-return Model;
-});
diff --git a/addons/web/static/src/js/framework/dialog.js b/addons/web/static/src/js/framework/dialog.js
deleted file mode 100644
index 6dc65b63281..00000000000
--- a/addons/web/static/src/js/framework/dialog.js
+++ /dev/null
@@ -1,200 +0,0 @@
-odoo.define('web.Dialog', function (require) {
-"use strict";
-
-var core = require('web.core');
-var Widget = require('web.Widget');
-
-var QWeb = core.qweb;
-var _t = core._t;
-
-/**
- A useful class to handle dialogs.
-
- Attributes:
- - $footer: A jQuery element targeting a dom part where buttons can be added. It always exists
- during the lifecycle of the dialog.
-*/
-var Dialog = Widget.extend({
- /**
- Constructor.
-
- @param {Widget} parent
- @param {dictionary} options
- - title
- - subtitle
- - size: one of the following: 'large', 'medium', 'small'
- - dialogClass: class to add to the modal-body
- - buttons: It must be a list of dictionaries -> text, classes, close, click, disabled
- -> If no buttons, a "ok" primary button is added with close = true
- -> By default: close = false and classes = 'btn-primary' if only one button and 'btn-default' if many buttons
- - $content: Some content to replace this.$el .
- */
- init: function (parent, options) {
- this._super(parent);
- this._opened = $.Deferred();
-
- options = _.defaults(options || {}, {
- title: _t('Odoo'), subtitle: '',
- size: 'large',
- dialogClass: '',
- $content: false,
- buttons: [{text: _t("Ok"), close: true}]
- });
-
- this.$content = options.$content;
-
- this.title = options.title;
- this.subtitle = options.subtitle;
- this.$modal = $(QWeb.render('Dialog', {title: this.title, subtitle: this.subtitle}));
-
- switch(options.size) {
- case 'large':
- this.$modal.find('.modal-dialog').addClass('modal-lg');
- break;
- case 'small':
- this.$modal.find('.modal-dialog').addClass('modal-sm');
- break;
- }
-
- this.dialogClass = options.dialogClass;
- this.$footer = this.$modal.find(".modal-footer");
-
- this.set_buttons(options.buttons);
-
- this.$modal.on('hidden.bs.modal', _.bind(this.destroy, this));
- },
-
- renderElement: function() {
- this._super();
- if(this.$content) {
- this.setElement(this.$content);
- }
- this.$el.addClass('modal-body ' + this.dialogClass);
- },
-
- set_buttons: function(buttons) {
- var self = this;
-
- self.$footer.empty();
-
- _.each(buttons, function(b) {
- var text = b.text || "";
- var classes = b.classes || ((buttons.length === 1)? "btn-primary" : "btn-default");
-
- var $b = $(QWeb.render('WidgetButton', { widget : { string: text, node: { attrs: {'class': classes, icon: b.icon} }, fa_icon: true }}));
- $b.prop('disabled', b.disabled);
- $b.on('click', function(e) {
- var click_def;
- if(b.click) {
- click_def = b.click.call(self, e);
- }
- if(b.close) {
- $.when(click_def).always(self.close.bind(self));
- }
- });
- self.$footer.append($b);
- });
- },
-
- set_title: function(title, subtitle) {
- this.title = title || "";
- if (subtitle !== undefined) {
- this.subtitle = subtitle || "";
- }
-
- var $title = this.$modal.find('.modal-title').first();
- var $subtitle = $title.find('.o_subtitle').detach();
- $title.html(this.title);
- $subtitle.html(this.subtitle).appendTo($title);
-
- return this;
- },
-
- opened: function(handler) {
- return (handler)? this._opened.then(handler) : this._opened;
- },
-
- open: function() {
- $('.tooltip').remove(); // remove open tooltip if any to prevent them staying when modal is opened
-
- var self = this;
- this.replace(this.$modal.find(".modal-body")).then(function() {
- self.$modal.modal('show');
- self._opened.resolve();
- });
-
- return self;
- },
-
- close: function() {
- this.$modal.modal('hide');
- },
-
- destroy: function(reason) {
- if (this.isDestroyed()) {
- return;
- }
-
- this.trigger("closed", reason);
-
- this._super();
-
- $('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal has disappeared
- this.$modal.modal('hide');
- this.$modal.remove();
-
- setTimeout(function () { // Keep class modal-open (deleted by bootstrap hide fnct) on body to allow scrolling inside the modal
- var modals = $('body > .modal').filter(':visible');
- if(modals.length) {
- modals.last().focus();
- $('body').addClass('modal-open');
- }
- }, 0);
- }
-});
-
-// static method to open simple alert dialog
-Dialog.alert = function (owner, message, options) {
- var buttons = [{
- text: _t("Ok"),
- close: true,
- click: options && options.confirm_callback,
- }];
- return new Dialog(owner, _.extend({
- size: 'medium',
- buttons: buttons,
- $content: $('<div>', {
- text: message,
- }),
- title: _t("Alert"),
- }, options)).open();
-};
-
-// static method to open simple confirm dialog
-Dialog.confirm = function (owner, message, options) {
- var buttons = [
- {
- text: _t("Ok"),
- classes: 'btn-primary',
- close: true,
- click: options && options.confirm_callback
- },
- {
- text: _t("Cancel"),
- close: true,
- click: options && options.cancel_callback
- }
- ];
- return new Dialog(owner, _.extend({
- size: 'medium',
- buttons: buttons,
- $content: $('<div>', {
- text: message,
- }),
- title: _t("Confirmation"),
- }, options)).open();
-};
-
-return Dialog;
-
-});
diff --git a/addons/web/static/src/js/framework/dom_utils.js b/addons/web/static/src/js/framework/dom_utils.js
deleted file mode 100644
index d0beff2988b..00000000000
--- a/addons/web/static/src/js/framework/dom_utils.js
+++ /dev/null
@@ -1,68 +0,0 @@
-odoo.define('web.dom_utils', function (require) {
-"use strict";
-
-var core = require('web.core');
-
-/**
- * Autoresize a $textarea node, by recomputing its height when necessary
- * @param {number} [options.min_height] by default, 50.
- * @param {Widget} [options.parent] if set, autoresize will listen to some extra
- * events to decide when to resize itself. This is useful for widgets that are
- * not in the dom when the autoresize is declared.
- */
-function autoresize ($textarea, options) {
- if ($textarea.data("auto_resize")) {
- return;
- }
- options = options || {};
-
- var $fixed_text_area;
- var min_height = (options && options.min_height) || 50;
- if (!$fixed_text_area) {
- $fixed_text_area = $('<textarea disabled>').css({
- position: 'absolute',
- opacity: 0,
- height: 10,
- top: -10000,
- left: -10000,
- });
- $fixed_text_area.addClass($textarea[0].className);
- $fixed_text_area.insertAfter($textarea);
- $fixed_text_area.data("auto_resize", true);
- }
-
- var style = window.getComputedStyle($textarea[0], null);
- if (style.resize === 'vertical') {
- $textarea[0].style.resize = 'none';
- } else if (style.resize === 'both') {
- $textarea[0].style.resize = 'horizontal';
- }
- resize();
- $textarea.data("auto_resize", true);
-
- $textarea.on('input focus', resize);
- if (options.parent) {
- core.bus.on('DOM_updated', options.parent, resize);
- core.bus.on('view_shown', options.parent, resize);
- }
-
- function resize () {
- var heightOffset;
- var style = window.getComputedStyle($textarea[0], null);
- if (style.boxSizing === 'content-box') {
- heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
- } else {
- heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
- }
- $fixed_text_area.width($textarea.width());
- $fixed_text_area.val($textarea.val());
- var height = $fixed_text_area[0].scrollHeight;
- $textarea.css({height: Math.max(height + heightOffset, min_height)});
- }
-}
-
-return {
- autoresize: autoresize,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/domain_utils.js b/addons/web/static/src/js/framework/domain_utils.js
deleted file mode 100644
index 8cefb7cabbe..00000000000
--- a/addons/web/static/src/js/framework/domain_utils.js
+++ /dev/null
@@ -1,19 +0,0 @@
-odoo.define("web.domainUtils", function (require) {
-"use strict";
-
-var pyeval = require("web.pyeval");
-
-function domainToString(domain) {
- if (_.isString(domain)) return domain;
- return JSON.stringify(domain || []).replace(/false/g, "False").replace(/true/g, "True");
-}
-function stringToDomain(domain) {
- if (!_.isString(domain)) return domain;
- return pyeval.eval("domain", domain || "[]");
-}
-
-return {
- domainToString: domainToString,
- stringToDomain: stringToDomain,
-};
-});
diff --git a/addons/web/static/src/js/framework/formats.js b/addons/web/static/src/js/framework/formats.js
deleted file mode 100644
index 479fa1082cd..00000000000
--- a/addons/web/static/src/js/framework/formats.js
+++ /dev/null
@@ -1,204 +0,0 @@
-odoo.define('web.formats', function (require) {
-"use strict";
-
-var translation = require('web.translation');
-var time = require('web.time');
-var utils = require('web.utils');
-
-var _t = translation._t;
-
-/**
- * Formats a single atomic value based on a field descriptor
- *
- * @param {Object} value read from OpenERP
- * @param {Object} descriptor union of orm field and view field
- * @param {Object} [descriptor.widget] widget to use to display the value
- * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
- * @param {Object} [descriptor.digits] used for the formatting of floats
- * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
- */
-function format_value (value, descriptor, value_if_empty) {
- var l10n = _t.database.parameters;
- var date_format = time.strftime_to_moment_format(l10n.date_format);
- var time_format = time.strftime_to_moment_format(l10n.time_format);
- // If NaN value, display as with a `false` (empty cell)
- if (typeof value === 'number' && isNaN(value)) {
- value = false;
- }
- //noinspection FallthroughInSwitchStatementJS
- switch (value) {
- case '':
- if (descriptor.type === 'char' || descriptor.type === 'text') {
- return '';
- }
- console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
- return value_if_empty === undefined ? '' : value_if_empty;
- case false:
- case undefined:
- case Infinity:
- case -Infinity:
- return value_if_empty === undefined ? '' : value_if_empty;
- }
- switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
- case 'id':
- return value.toString();
- case 'integer':
- return utils.insert_thousand_seps(
- _.str.sprintf('%d', value));
- case 'monetary':
- case 'float':
- var digits = descriptor.digits ? descriptor.digits : [69,2];
- digits = typeof digits === "string" ? py.eval(digits) : digits;
- var precision = digits[1];
- var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
- formatted[0] = utils.insert_thousand_seps(formatted[0]);
- return formatted.join(l10n.decimal_point);
- case 'float_time':
- var pattern = '%02d:%02d';
- if (value < 0) {
- value = Math.abs(value);
- pattern = '-' + pattern;
- }
- var hour = Math.floor(value);
- var min = Math.round((value % 1) * 60);
- if (min == 60){
- min = 0;
- hour = hour + 1;
- }
- return _.str.sprintf(pattern, hour, min);
- case 'many2one':
- // name_get value format
- return value[1] ? value[1].split("\n")[0] : value[1];
- case 'one2many':
- case 'many2many':
- if (typeof value === 'string') {
- return value;
- }
- return _.str.sprintf(_t("(%d records)"), value.length);
- case 'datetime':
- if (typeof(value) == "string")
- value = moment(time.auto_str_to_date(value));
- else {
- value = moment(value);
- }
- return value.format(date_format + ' ' + time_format);
- case 'date':
- if (typeof(value) == "string")
- value = moment(time.auto_str_to_date(value));
- else {
- value = moment(value);
- }
- return value.format(date_format);
- case 'time':
- if (typeof(value) == "string")
- value = moment(time.auto_str_to_date(value));
- else {
- value = moment(value);
- }
- return value.format(time_format);
- case 'selection': case 'statusbar':
- // Each choice is [value, label]
- if(_.isArray(value)) {
- return value[1];
- }
- var result = _(descriptor.selection).detect(function (choice) {
- return choice[0] === value;
- });
- if (result) { return result[1]; }
- return;
- default:
- return value;
- }
-}
-
-function parse_value (value, descriptor, value_if_empty) {
- var date_pattern = time.strftime_to_moment_format(_t.database.parameters.date_format),
- time_pattern = time.strftime_to_moment_format(_t.database.parameters.time_format);
- var date_pattern_wo_zero = date_pattern.replace('MM','M').replace('DD','D'),
- time_pattern_wo_zero = time_pattern.replace('HH','H').replace('mm','m').replace('ss','s');
- switch (value) {
- case false:
- case "":
- return value_if_empty === undefined ? false : value_if_empty;
- }
- var tmp;
- switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
- case 'integer':
- do {
- tmp = value;
- value = value.replace(_t.database.parameters.thousands_sep, "");
- } while(tmp !== value);
- tmp = Number(value);
- // do not accept not numbers or float values
- if (isNaN(tmp) || tmp % 1)
- throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
- return tmp;
- case 'monetary':
- case 'float':
- var tmp2 = value;
- do {
- tmp = tmp2;
- tmp2 = tmp.replace(_t.database.parameters.thousands_sep, "");
- } while(tmp !== tmp2);
- var reformatted_value = tmp.replace(_t.database.parameters.decimal_point, ".");
- var parsed = Number(reformatted_value);
- if (isNaN(parsed))
- throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value));
- return parsed;
- case 'float_time':
- var factor = 1;
- if (value[0] === '-') {
- value = value.slice(1);
- factor = -1;
- }
- var float_time_pair = value.split(":");
- if (float_time_pair.length != 2)
- return factor * parse_value(value, {type: "float"});
- var hours = parse_value(float_time_pair[0], {type: "integer"});
- var minutes = parse_value(float_time_pair[1], {type: "integer"});
- return factor * (hours + (minutes / 60));
- case 'progressbar':
- return parse_value(value, {type: "float"});
- case 'datetime':
- var datetime = moment(value, [date_pattern + ' ' + time_pattern, date_pattern_wo_zero + ' ' + time_pattern_wo_zero, moment.ISO_8601], true);
- if (datetime.isValid() && datetime.year() >= 1900)
- return time.datetime_to_str(datetime.toDate());
- datetime = moment(value, [date_pattern + ' ' + time_pattern, date_pattern_wo_zero + ' ' + time_pattern_wo_zero, moment.ISO_8601]);
- if (datetime.isValid()) {
- if (datetime.year() === 0) {
- datetime.year(moment.utc().year());
- }
- if (datetime.year() >= 1900) {
- return time.datetime_to_str(datetime.toDate());
- }
- }
- throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
- case 'date':
- var date = moment(value, [date_pattern, date_pattern_wo_zero, moment.ISO_8601], true);
- if (date.isValid() && date.year() >= 1900)
- return time.date_to_str(date.toDate());
- date = moment(value, [date_pattern, date_pattern_wo_zero, moment.ISO_8601]);
- if (date.isValid()) {
- if (date.year() === 0) {
- date.year(moment.utc().year());
- }
- if (date.year() >= 1900) {
- return time.date_to_str(date.toDate());
- }
- }
- throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value));
- case 'time':
- var _time = moment(value, [time_pattern, time_pattern_wo_zero, moment.ISO_8601], true);
- if (_time.isValid())
- return time.time_to_str(_time.toDate());
- throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value));
- }
- return value;
-}
-
-return {
- format_value: format_value,
- parse_value: parse_value,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/local_storage.js b/addons/web/static/src/js/framework/local_storage.js
deleted file mode 100644
index 6cf82e87752..00000000000
--- a/addons/web/static/src/js/framework/local_storage.js
+++ /dev/null
@@ -1,34 +0,0 @@
-odoo.define('web.local_storage', function (require) {
-'use strict';
-
-var Class = require('web.Class');
-
-// Compatibility with private browsing in Safari
-// Use dict in case of local storage is disabled
-var localStorage = window.localStorage;
-try {
- var uid = new Date();
- localStorage.setItem(uid, uid);
- localStorage.removeItem(uid);
-} catch (exception) {
- var storage = {};
- var RamStorage = Class.extend({
- setItem: function(key, value) {
- storage[key] = value;
- },
- getItem: function(key) {
- return storage[key];
- },
- removeItem: function(key) {
- delete storage[key];
- },
- clear: function() {
- storage = {};
- },
- });
- localStorage = new RamStorage();
-}
-
-return localStorage;
-
-});
diff --git a/addons/web/static/src/js/framework/misc.js b/addons/web/static/src/js/framework/misc.js
deleted file mode 100644
index e083ee25b22..00000000000
--- a/addons/web/static/src/js/framework/misc.js
+++ /dev/null
@@ -1,438 +0,0 @@
-odoo.define('web.framework', function (require) {
-"use strict";
-
-var core = require('web.core');
-var crash_manager = require('web.crash_manager');
-var ajax = require('web.ajax');
-var Widget = require('web.Widget');
-
-var _t = core._t;
-
-var messages_by_seconds = function() {
- return [
- [0, _t("Loading...")],
- [20, _t("Still loading...")],
- [60, _t("Still loading...<br />Please be patient.")],
- [120, _t("Don't leave yet,<br />it's still loading...")],
- [300, _t("You may not believe it,<br />but the application is actually loading...")],
- [420, _t("Take a minute to get a coffee,<br />because it's loading...")],
- [3600, _t("Maybe you should consider reloading the application by pressing F5...")]
- ];
-};
-
-var Throbber = Widget.extend({
- template: "Throbber",
- start: function() {
- this.start_time = new Date().getTime();
- this.act_message();
- },
- act_message: function() {
- var self = this;
- setTimeout(function() {
- if (self.isDestroyed())
- return;
- var seconds = (new Date().getTime() - self.start_time) / 1000;
- var mes;
- _.each(messages_by_seconds(), function(el) {
- if (seconds >= el[0])
- mes = el[1];
- });
- self.$(".oe_throbber_message").html(mes);
- self.act_message();
- }, 1000);
- },
-});
-
-/** Setup blockui */
-if ($.blockUI) {
- $.blockUI.defaults.baseZ = 1100;
- $.blockUI.defaults.message = '<div class="openerp oe_blockui_spin_container" style="background-color: transparent;">';
- $.blockUI.defaults.css.border = '0';
- $.blockUI.defaults.css["background-color"] = '';
-}
-
-
-var throbbers = [];
-
-function blockUI () {
- var tmp = $.blockUI.apply($, arguments);
- var throbber = new Throbber();
- throbbers.push(throbber);
- throbber.appendTo($(".oe_blockui_spin_container"));
- $('body').addClass('o_ui_blocked');
- return tmp;
-}
-
-function unblockUI () {
- _.invoke(throbbers, 'destroy');
- throbbers = [];
- $('body').removeClass('o_ui_blocked');
- return $.unblockUI.apply($, arguments);
-}
-
-/**
- * Redirect to url by replacing window.location
- * If wait is true, sleep 1s and wait for the server i.e. after a restart.
- */
-function redirect (url, wait) {
- // Dont display a dialog if some xmlhttprequest are in progress
- crash_manager.disable();
-
- var load = function() {
- var old = "" + window.location;
- var old_no_hash = old.split("#")[0];
- var url_no_hash = url.split("#")[0];
- location.assign(url);
- if (old_no_hash === url_no_hash) {
- location.reload(true);
- }
- };
-
- var wait_server = function() {
- ajax.rpc("/web/webclient/version_info", {}).done(load).fail(function() {
- setTimeout(wait_server, 250);
- });
- };
-
- if (wait) {
- setTimeout(wait_server, 1000);
- } else {
- load();
- }
-}
-
-// * Client action to reload the whole interface.
-// * If params.menu_id, it opens the given menu entry.
-// * If params.wait, reload will wait the openerp server to be reachable before reloading
-
-function Reload(parent, action) {
- var params = action.params || {};
- var menu_id = params.menu_id || false;
- var l = window.location;
-
- var sobj = $.deparam(l.search.substr(1));
- if (params.url_search) {
- sobj = _.extend(sobj, params.url_search);
- }
- var search = '?' + $.param(sobj);
-
- var hash = l.hash;
- if (menu_id) {
- hash = "#menu_id=" + menu_id;
- }
- var url = l.protocol + "//" + l.host + l.pathname + search + hash;
-
- redirect(url, params.wait);
-}
-
-core.action_registry.add("reload", Reload);
-
-
-/**
- * Client action to go back home.
- */
-function Home (parent, action) {
- var url = '/' + (window.location.search || '');
- redirect(url, action && action.params && action.params.wait);
-}
-core.action_registry.add("home", Home);
-
-/**
- * Client action to go back in breadcrumb history.
- * If can't go back in history stack, will go back to home.
- */
-function HistoryBack (parent) {
- parent.history_back().fail(function () {
- Home(parent);
- });
-}
-core.action_registry.add("history_back", HistoryBack);
-
-function login() {
- redirect('/web/login');
-}
-core.action_registry.add("login", login);
-
-function logout() {
- redirect('/web/session/logout');
- return $.Deferred();
-}
-core.action_registry.add("logout", logout);
-
-/**
- * Client action to refresh the session context (making sure
- * HTTP requests will have the right one) then reload the
- * whole interface.
- */
-function ReloadContext (parent, action) {
- // side-effect of get_session_info is to refresh the session context
- ajax.rpc("/web/session/get_session_info", {}).then(function() {
- Reload(parent, action);
- });
-}
-core.action_registry.add("reload_context", ReloadContext);
-
-
-// nvd3 customization
-//-------------------------------------------------------------------------
-if ('nv' in window) {
- nv.dev = false; // sets nvd3 library in production mode
-
- // monkey patch nvd3 to allow removing eventhandler on windowresize events
- // see https://github.com/novus/nvd3/pull/396 for more details
-
- // Adds a resize listener to the window.
- nv.utils.onWindowResize = function(fun) {
- if (fun === null) return;
- window.addEventListener('resize', fun);
- };
-
- // Backwards compatibility with current API.
- nv.utils.windowResize = nv.utils.onWindowResize;
-
- // Removes a resize listener from the window.
- nv.utils.offWindowResize = function(fun) {
- if (fun === null) return;
- window.removeEventListener('resize', fun);
- };
-
- // monkey patch nvd3 to prevent crashes when user changes view and nvd3 tries
- // to remove tooltips after 500 ms... seriously nvd3, what were you thinking?
- nv.tooltip.cleanup = function () {
- $('.nvtooltip').remove();
- };
-
- // monkey patch nvd3 to prevent it to display a tooltip (position: absolute) with
- // a negative `top`; with this patch the highest tooltip's position is still in the
- // graph
- var originalCalcTooltipPosition = nv.tooltip.calcTooltipPosition;
- nv.tooltip.calcTooltipPosition = function () {
- var container = originalCalcTooltipPosition.apply(this, arguments);
- container.style.top = container.style.top.split('px')[0] < 0 ? 0 + 'px' : container.style.top;
- return container;
- };
-}
-
-// Bootstrap customization
-//-------------------------------------------------------------------------
-/* Bootstrap defaults overwrite */
-$.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
-$.fn.tooltip.Constructor.DEFAULTS.html = true;
-$.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click';
-$.fn.tooltip.Constructor.DEFAULTS.container = 'body';
-$.fn.tooltip.Constructor.DEFAULTS.delay = { show: 1000, hide: 0 };
-//overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time
-var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
-$.fn.modal.Constructor.prototype.enforceFocus = function () { };
-$.fn.tooltip.Constructor.prototype.show = function () {
- $('.tooltip').remove();
- //the following fix the bug when using placement
- //auto and the parent element does not exist anymore resulting in
- //an error. This should be remove once we updade bootstrap to a version that fix the bug
- //edit: bug has been fixed here : https://github.com/twbs/bootstrap/pull/13752
- var e = $.Event('show.bs.' + this.type);
- var inDom = $.contains(document.documentElement, this.$element[0]);
- if (e.isDefaultPrevented() || !inDom) return;
- return bootstrap_show_function.call(this);
-};
-
-// jquery customization
-//-------------------------------------------------------------------------
-jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
- return function( elem ) {
- return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
- };
-});
-
-/** Custom jQuery plugins */
-$.fn.getAttributes = function() {
- var o = {};
- if (this.length) {
- for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) {
- attr = attrs.item(i);
- o[attr.nodeName] = attr.value;
- }
- }
- return o;
-};
-$.fn.openerpClass = function(additionalClass) {
- // This plugin should be applied on top level elements
- additionalClass = additionalClass || '';
- if (!!$.browser.msie) {
- additionalClass += ' openerp_ie';
- }
- return this.each(function() {
- $(this).addClass('openerp ' + additionalClass);
- });
-};
-$.fn.openerpBounce = function() {
- return this.each(function() {
- $(this).css('box-sizing', 'content-box').effect('bounce', {distance: 18, times: 5}, 250);
- });
-};
-
-// jquery autocomplete tweak to allow html and classnames
-var proto = $.ui.autocomplete.prototype,
- initSource = proto._initSource;
-
-function filter( array, term ) {
- var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
- return $.grep( array, function(value_) {
- return matcher.test( $( "<div>" ).html( value_.label || value_.value || value_ ).text() );
- });
-}
-
-$.extend( proto, {
- _initSource: function() {
- if ( this.options.html && $.isArray(this.options.source) ) {
- this.source = function( request, response ) {
- response( filter( this.options.source, request.term ) );
- };
- } else {
- initSource.call( this );
- }
- },
-
- _renderItem: function( ul, item) {
- return $( "<li></li>" )
- .data( "item.autocomplete", item )
- .append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) )
- .appendTo( ul )
- .addClass(item.classname);
- }
-});
-
-/**
- * Private function to notify that something has been attached in the DOM
- * @param {htmlString or Element or Array or jQuery} [content] the content that
- * has been attached in the DOM
- * @params {Array} [callbacks] array of {widget: w, callback_args: args} such
- * that on_attach_callback() will be called on each w with arguments args
- */
-function _notify (content, callbacks) {
- _.each(callbacks, function(c) {
- if (c.widget && c.widget.on_attach_callback) {
- c.widget.on_attach_callback(c.callback_args);
- }
- });
- core.bus.trigger('DOM_updated', content);
-}
-/**
- * Appends content in a jQuery object and optionnally triggers an event
- * @param {jQuery} [$target] the node where content will be appended
- * @param {htmlString or Element or Array or jQuery} [content] DOM element,
- * array of elements, HTML string or jQuery object to append to $target
- * @param {Boolean} [options.in_DOM] true if $target is in the DOM
- * @param {Array} [options.callbacks] array of objects describing the callbacks
- * to perform (see _notify for a complete description)
- */
-function append ($target, content, options) {
- $target.append(content);
- if (options && options.in_DOM) {
- _notify(content, options.callbacks);
- }
-}
-/**
- * Prepends content in a jQuery object and optionnally triggers an event
- * @param {jQuery} [$target] the node where content will be prepended
- * @param {htmlString or Element or Array or jQuery} [content] DOM element,
- * array of elements, HTML string or jQuery object to prepend to $target
- * @param {Boolean} [options.in_DOM] true if $target is in the DOM
- * @param {Array} [options.callbacks] array of objects describing the callbacks
- * to perform (see _notify for a complete description)
- */
-function prepend ($target, content, options) {
- $target.prepend(content);
- if (options && options.in_DOM) {
- _notify(content, options.callbacks);
- }
-}
-
-/**
- * Detaches widgets from the DOM and performs their on_detach_callback()
- * @param {Array} [to_detach] array of {widget: w, callback_args: args} such
- * that w.$el will be detached and w.on_detach_callback(args) will be called
- * @param {jQuery} [options.$to_detach] if given, detached instead of widgets' $el
- * @return {jQuery} the detached elements
- */
-function detach (to_detach, options) {
- _.each(to_detach, function(d) {
- if (d.widget.on_detach_callback) {
- d.widget.on_detach_callback(d.callback_args);
- }
- });
- var $to_detach = options && options.$to_detach;
- if (!$to_detach) {
- $to_detach = $(_.map(to_detach, function(d) {
- return d.widget.el;
- }));
- }
- return $to_detach.detach();
-}
-
-/**
- * Returns the distance between a DOM element and the top-left corner of the window
- * @param {element} [e] the DOM element
- * @return {Object} the left and top distances in pixels
- */
-function getPosition(e) {
- var position = {left: 0, top: 0};
- while (e) {
- position.left += e.offsetLeft;
- position.top += e.offsetTop;
- e = e.offsetParent;
- }
- return position;
-}
-
-return {
- blockUI: blockUI,
- unblockUI: unblockUI,
- redirect: redirect,
- append: append,
- prepend: prepend,
- detach: detach,
- getPosition: getPosition,
-};
-
-});
-
-odoo.define('web.IFrameWidget', function (require) {
-"use strict";
-
-var Widget = require('web.Widget');
-
-/**
- * Generic widget to create an iframe that listens for clicks
- *
- * It should be extended by overwritting the methods:
- * init: function(parent) {
- * this._super(parent, <url_of_iframe>
- * },
- * iframe_clicked: function(e){
- * filter the clicks you want to use and apply
- * an action on it
- * }
- */
-var IFrameWidget = Widget.extend({
- tagName: 'iframe',
- init: function(parent, url) {
- this._super(parent);
- this.url = url;
- },
- start: function() {
- this.$el.css({height: '100%', width: '100%', border: 0});
- this.$el.attr({src: this.url});
- this.$el.on("load", this.bind_events.bind(this));
- return this._super();
- },
- bind_events: function(){
- this.$el.contents().click(this.iframe_clicked.bind(this));
- },
- iframe_clicked: function(e){
- }
-});
-
-return IFrameWidget;
-
-});
diff --git a/addons/web/static/src/js/framework/mixins.js b/addons/web/static/src/js/framework/mixins.js
deleted file mode 100644
index 05d4105e466..00000000000
--- a/addons/web/static/src/js/framework/mixins.js
+++ /dev/null
@@ -1,367 +0,0 @@
-odoo.define('web.mixins', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-var utils = require('web.utils');
-
-/**
- * Mixin to structure objects' life-cycles folowing a parent-children
- * relationship. Each object can a have a parent and multiple children.
- * When an object is destroyed, all its children are destroyed too releasing
- * any resource they could have reserved before.
- */
-var ParentedMixin = {
- __parentedMixin : true,
- init: function() {
- this.__parentedDestroyed = false;
- this.__parentedChildren = [];
- this.__parentedParent = null;
- },
- /**
- * Set the parent of the current object. When calling this method, the
- * parent will also be informed and will return the current object
- * when its getChildren() method is called. If the current object did
- * already have a parent, it is unregistered before, which means the
- * previous parent will not return the current object anymore when its
- * getChildren() method is called.
- */
- setParent : function(parent) {
- if (this.getParent()) {
- if (this.getParent().__parentedMixin) {
- this.getParent().__parentedChildren = _.without(this
- .getParent().getChildren(), this);
- }
- }
- this.__parentedParent = parent;
- if (parent && parent.__parentedMixin) {
- parent.__parentedChildren.push(this);
- }
- },
- /**
- * Return the current parent of the object (or null).
- */
- getParent : function() {
- return this.__parentedParent;
- },
- /**
- * Return a list of the children of the current object.
- */
- getChildren : function() {
- return _.clone(this.__parentedChildren);
- },
- /**
- * Returns true if destroy() was called on the current object.
- */
- isDestroyed : function() {
- return this.__parentedDestroyed;
- },
- /**
- Utility method to only execute asynchronous actions if the current
- object has not been destroyed.
-
- @param {$.Deferred} promise The promise representing the asynchronous
- action.
- @param {bool} [reject=false] If true, the returned promise will be
- rejected with no arguments if the current
- object is destroyed. If false, the
- returned promise will never be resolved
- or rejected.
- @returns {$.Deferred} A promise that will mirror the given promise if
- everything goes fine but will either be rejected
- with no arguments or never resolved if the
- current object is destroyed.
- */
- alive: function(promise, reject) {
- var self = this;
- return $.Deferred(function (def) {
- promise.then(function () {
- if (!self.isDestroyed()) {
- def.resolve.apply(def, arguments);
- }
- }, function () {
- if (!self.isDestroyed()) {
- def.reject.apply(def, arguments);
- }
- }).always(function () {
- if (reject) {
- // noop if def already resolved or rejected
- def.reject();
- }
- // otherwise leave promise in limbo
- });
- }).promise();
- },
- /**
- * Inform the object it should destroy itself, releasing any
- * resource it could have reserved.
- */
- destroy : function() {
- _.each(this.getChildren(), function(el) {
- el.destroy();
- });
- this.setParent(undefined);
- this.__parentedDestroyed = true;
- },
- /**
- * Find the closest ancestor matching predicate
- */
- findAncestor: function (predicate) {
- var ancestor = this;
- while (ancestor && !(predicate(ancestor)) && ancestor.getParent) {
- ancestor = ancestor.getParent();
- }
- return ancestor;
- },
-};
-
-function OdooEvent (target, name, data) {
- this.target = target;
- this.name = name;
- this.data = Object.create(null);
- _.extend(this.data, data);
- this.stopped = false;
-}
-
-OdooEvent.prototype.stop_propagation = function () {
- this.stopped = true;
-};
-
-OdooEvent.prototype.is_stopped = function () {
- return this.stopped;
-};
-
-/**
- * Backbone's events. Do not ever use it directly, use EventDispatcherMixin instead.
- *
- * This class just handle the dispatching of events, it is not meant to be extended,
- * nor used directly. All integration with parenting and automatic unregistration of
- * events is done in EventDispatcherMixin.
- *
- * Copyright notice for the following Class:
- *
- * (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
- * Backbone may be freely distributed under the MIT license.
- * For all details and documentation:
- * http://backbonejs.org
- *
- */
-var Events = Class.extend({
- on : function(events, callback, context) {
- var ev;
- events = events.split(/\s+/);
- var calls = this._callbacks || (this._callbacks = {});
- while ((ev = events.shift())) {
- var list = calls[ev] || (calls[ev] = {});
- var tail = list.tail || (list.tail = list.next = {});
- tail.callback = callback;
- tail.context = context;
- list.tail = tail.next = {};
- }
- return this;
- },
-
- off : function(events, callback, context) {
- var ev, calls, node;
- if (!events) {
- delete this._callbacks;
- } else if ((calls = this._callbacks)) {
- events = events.split(/\s+/);
- while ((ev = events.shift())) {
- node = calls[ev];
- delete calls[ev];
- if (!callback || !node)
- continue;
- while ((node = node.next) && node.next) {
- if (node.callback === callback
- && (!context || node.context === context))
- continue;
- this.on(ev, node.callback, node.context);
- }
- }
- }
- return this;
- },
-
- callbackList: function() {
- var lst = [];
- _.each(this._callbacks || {}, function(el, eventName) {
- var node = el;
- while ((node = node.next) && node.next) {
- lst.push([eventName, node.callback, node.context]);
- }
- });
- return lst;
- },
-
- trigger : function(events) {
- var event, node, calls, tail, args, all, rest;
- if (!(calls = this._callbacks))
- return this;
- all = calls.all;
- (events = events.split(/\s+/)).push(null);
- // Save references to the current heads & tails.
- while ((event = events.shift())) {
- if (all)
- events.push({
- next : all.next,
- tail : all.tail,
- event : event
- });
- if (!(node = calls[event]))
- continue;
- events.push({
- next : node.next,
- tail : node.tail
- });
- }
- rest = Array.prototype.slice.call(arguments, 1);
- while ((node = events.pop())) {
- tail = node.tail;
- args = node.event ? [ node.event ].concat(rest) : rest;
- while ((node = node.next) !== tail) {
- node.callback.apply(node.context || this, args);
- }
- }
- return this;
- }
-});
-
-/**
- Mixin containing an event system. Events are also registered by specifying the target object
- (the object which will receive the event when it is raised). Both the event-emitting object
- and the target object store or reference to each other. This is used to correctly remove all
- reference to the event handler when any of the object is destroyed (when the destroy() method
- from ParentedMixin is called). Removing those references is necessary to avoid memory leak
- and phantom events (events which are raised and sent to a previously destroyed object).
-*/
-var EventDispatcherMixin = _.extend({}, ParentedMixin, {
- __eventDispatcherMixin: true,
- init: function() {
- ParentedMixin.init.call(this);
- this.__edispatcherEvents = new Events();
- this.__edispatcherRegisteredEvents = [];
- },
- on: function(events, dest, func) {
- var self = this;
- if (typeof func !== "function") {
- throw new Error("Event handler must be a function.");
- }
- events = events.split(/\s+/);
- _.each(events, function(eventName) {
- self.__edispatcherEvents.on(eventName, func, dest);
- if (dest && dest.__eventDispatcherMixin) {
- dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
- }
- });
- return this;
- },
- off: function(events, dest, func) {
- var self = this;
- events = events.split(/\s+/);
- _.each(events, function(eventName) {
- self.__edispatcherEvents.off(eventName, func, dest);
- if (dest && dest.__eventDispatcherMixin) {
- dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
- return !(el.name === eventName && el.func === func && el.source === self);
- });
- }
- });
- return this;
- },
- once: function(events, dest, func) {
- // similar to this.on(), but func is executed only once
- var self = this
- if (typeof func !== "function") {
- throw new Error("Event handler must be a function.");
- }
- self.on(events, dest, function what() {
- func.apply(this, arguments);
- self.off(events, dest, what);
- });
- },
- trigger: function() {
- this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
- return this;
- },
- trigger_up: function(name, info) {
- var event = new OdooEvent(this, name, info);
- this._trigger_up(event);
- },
- _trigger_up: function(event) {
- var parent;
- this.__edispatcherEvents.trigger(event.name, event);
- if (!event.is_stopped() && (parent = this.getParent())) {
- parent._trigger_up(event);
- }
- },
- destroy: function() {
- var self = this;
- _.each(this.__edispatcherRegisteredEvents, function(event) {
- event.source.__edispatcherEvents.off(event.name, event.func, self);
- });
- this.__edispatcherRegisteredEvents = [];
- _.each(this.__edispatcherEvents.callbackList(), function(cal) {
- this.off(cal[0], cal[2], cal[1]);
- }, this);
- this.__edispatcherEvents.off();
- ParentedMixin.destroy.call(this);
- }
-});
-
-var PropertiesMixin = _.extend({}, EventDispatcherMixin, {
- init: function() {
- EventDispatcherMixin.init.call(this);
- this.__getterSetterInternalMap = {};
- },
- set: function(arg1, arg2, arg3) {
- var map;
- var options;
- if (typeof arg1 === "string") {
- map = {};
- map[arg1] = arg2;
- options = arg3 || {};
- } else {
- map = arg1;
- options = arg2 || {};
- }
- var self = this;
- var changed = false;
- _.each(map, function(val, key) {
- var tmp = self.__getterSetterInternalMap[key];
- if (tmp === val)
- return;
- // seriously, why are you doing this? it is obviously a stupid design.
- // the properties mixin should not be concerned with handling fields details.
- // this also has the side effect of introducing a dependency on utils. Todo:
- // remove this, or move it elsewhere. Also, learn OO programming.
- if (key === 'value' && self.field && self.field.type === 'float' && tmp && val){
- var digits = self.field.digits;
- if (_.isArray(digits)) {
- if (utils.float_is_zero(tmp - val, digits[1])) {
- return;
- }
- }
- }
- changed = true;
- self.__getterSetterInternalMap[key] = val;
- if (! options.silent)
- self.trigger("change:" + key, self, {
- oldValue: tmp,
- newValue: val
- });
- });
- if (changed)
- self.trigger("change", self);
- },
- get: function(key) {
- return this.__getterSetterInternalMap[key];
- }
-});
-
-return {
- ParentedMixin: ParentedMixin,
- EventDispatcherMixin: EventDispatcherMixin,
- PropertiesMixin: PropertiesMixin,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/model.js b/addons/web/static/src/js/framework/model.js
deleted file mode 100644
index 377ff1a1757..00000000000
--- a/addons/web/static/src/js/framework/model.js
+++ /dev/null
@@ -1,60 +0,0 @@
-odoo.define('web.Model', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-var session = require('web.session');
-
-var Model = Class.extend({
- /**
- new openerp.web.Model(name[, context[, domain]])
-
- @constructs instance.web.Model
- @extends instance.web.Class
-
- @param {String} name name of the OpenERP model this object is bound to
- @param {Object} [context]
- @param {Array} [domain]
- */
- init: function(name, context, domain) {
- this.name = name;
- this._context = context || {};
- this._domain = domain || [];
- },
- /**
- * @deprecated does not allow to specify kwargs, directly use call() instead
- */
- get_func: function (method_name) {
- var self = this;
- return function () {
- return self.call(method_name, _.toArray(arguments));
- };
- },
- /**
- * 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 = [];
- }
- var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method;
- return session.rpc(call_kw, {
- model: this.name,
- method: method,
- args: args,
- kwargs: kwargs
- }, options);
- },
-});
-
-return Model;
-});
diff --git a/addons/web/static/src/js/framework/pyeval.js b/addons/web/static/src/js/framework/pyeval.js
deleted file mode 100644
index bcab0b0717f..00000000000
--- a/addons/web/static/src/js/framework/pyeval.js
+++ /dev/null
@@ -1,1061 +0,0 @@
-odoo.define('web.pyeval', function (require) {
-"use strict";
-
-var core = require('web.core');
-var session = require('web.session');
-var utils = require('web.utils');
-
-var _t = core._t;
-var py = window.py; // to silence linters
-/*
- * py.js helpers and setup
- */
-
-var obj = function () {};
-obj.prototype = py.object;
-var asJS = function (arg) {
- if (arg instanceof obj) {
- return arg.toJSON();
- }
- return arg;
-};
-
-var datetime = py.PY_call(py.object);
-
-var zero = py.float.fromJSON(0);
-
-// Port from pypy/lib_pypy/datetime.py
-var DAYS_IN_MONTH = [null, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
-var DAYS_BEFORE_MONTH = [null];
-var dbm = 0;
-
-for (var i=1; i<DAYS_IN_MONTH.length; ++i) {
- DAYS_BEFORE_MONTH.push(dbm);
- dbm += DAYS_IN_MONTH[i];
-}
-
-function is_leap (year) {
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
-}
-
-function days_before_year (year) {
- var y = year - 1;
- return y*365 + Math.floor(y/4) - Math.floor(y/100) + Math.floor(y/400);
-}
-
-function days_in_month (year, month) {
- if (month === 2 && is_leap(year)) {
- return 29;
- }
- return DAYS_IN_MONTH[month];
-}
-
-function days_before_month (year, month) {
- var post_leap_feb = month > 2 && is_leap(year);
- return DAYS_BEFORE_MONTH[month] + (post_leap_feb ? 1 : 0);
-}
-
-function ymd2ord (year, month, day) {
- var dim = days_in_month(year, month);
- if (!(1 <= day && day <= dim)) {
- throw new Error("ValueError: day must be in 1.." + dim);
- }
- return days_before_year(year) +
- days_before_month(year, month) +
- day;
-}
-
-var DI400Y = days_before_year(401);
-var DI100Y = days_before_year(101);
-var DI4Y = days_before_year(5);
-
-function ord2ymd (n) {
- --n;
- var n400, n100, n4, n1, n0;
- utils.divmod(n, DI400Y, function (_n400, n) {
- n400 = _n400;
- utils.divmod(n, DI100Y, function (_n100, n) {
- n100 = _n100;
- utils.divmod(n, DI4Y, function (_n4, n) {
- n4 = _n4;
- utils.divmod(n, 365, function (_n1, n) {
- n1 = _n1;
- n0 = n;
- });
- });
- });
- });
-
- n = n0;
- var year = n400 * 400 + 1 + n100 * 100 + n4 * 4 + n1;
- if (n1 == 4 || n100 == 100) {
- utils.assert(n0 === 0);
- return {
- year: year - 1,
- month: 12,
- day: 31
- };
- }
-
- var leapyear = n1 === 3 && (n4 !== 24 || n100 == 3);
- utils.assert(leapyear == is_leap(year));
- var month = (n + 50) >> 5;
- var preceding = DAYS_BEFORE_MONTH[month] + ((month > 2 && leapyear) ? 1 : 0);
- if (preceding > n) {
- --month;
- preceding -= DAYS_IN_MONTH[month] + ((month === 2 && leapyear) ? 1 : 0);
- }
- n -= preceding;
- return {
- year: year,
- month: month,
- day: n+1
- };
-}
-
-/**
- * Converts the stuff passed in into a valid date, applying overflows as needed
- */
-function tmxxx(year, month, day, hour, minute, second, microsecond) {
- hour = hour || 0; minute = minute || 0; second = second || 0;
- microsecond = microsecond || 0;
-
- if (microsecond < 0 || microsecond > 999999) {
- utils.divmod(microsecond, 1000000, function (carry, ms) {
- microsecond = ms;
- second += carry;
- });
- }
- if (second < 0 || second > 59) {
- utils.divmod(second, 60, function (carry, s) {
- second = s;
- minute += carry;
- });
- }
- if (minute < 0 || minute > 59) {
- utils.divmod(minute, 60, function (carry, m) {
- minute = m;
- hour += carry;
- });
- }
- if (hour < 0 || hour > 23) {
- utils.divmod(hour, 24, function (carry, h) {
- hour = h;
- day += carry;
- });
- }
- // That was easy. Now it gets muddy: the proper range for day
- // can't be determined without knowing the correct month and year,
- // but if day is, e.g., plus or minus a million, the current month
- // and year values make no sense (and may also be out of bounds
- // themselves).
- // Saying 12 months == 1 year should be non-controversial.
- if (month < 1 || month > 12) {
- utils.divmod(month-1, 12, function (carry, m) {
- month = m + 1;
- year += carry;
- });
- }
- // Now only day can be out of bounds (year may also be out of bounds
- // for a datetime object, but we don't care about that here).
- // If day is out of bounds, what to do is arguable, but at least the
- // method here is principled and explainable.
- var dim = days_in_month(year, month);
- if (day < 1 || day > dim) {
- // Move day-1 days from the first of the month. First try to
- // get off cheap if we're only one day out of range (adjustments
- // for timezone alone can't be worse than that).
- if (day === 0) {
- --month;
- if (month > 0) {
- day = days_in_month(year, month);
- } else {
- --year; month=12; day=31;
- }
- } else if (day == dim + 1) {
- ++month;
- day = 1;
- if (month > 12) {
- month = 1;
- ++year;
- }
- } else {
- var r = ord2ymd(ymd2ord(year, month, 1) + (day - 1));
- year = r.year;
- month = r.month;
- day = r.day;
- }
- }
- return {
- year: year,
- month: month,
- day: day,
- hour: hour,
- minute: minute,
- second: second,
- microsecond: microsecond
- };
-}
-
-datetime.timedelta = py.type('timedelta', null, {
- __init__: function () {
- var args = py.PY_parseArgs(arguments, [
- ['days', zero], ['seconds', zero], ['microseconds', zero],
- ['milliseconds', zero], ['minutes', zero], ['hours', zero],
- ['weeks', zero]
- ]);
-
- var d = 0, s = 0, m = 0;
- var days = args.days.toJSON() + args.weeks.toJSON() * 7;
- var seconds = args.seconds.toJSON()
- + args.minutes.toJSON() * 60
- + args.hours.toJSON() * 3600;
- var microseconds = args.microseconds.toJSON()
- + args.milliseconds.toJSON() * 1000;
-
- // Get rid of all fractions, and normalize s and us.
- // Take a deep breath <wink>.
- var daysecondsfrac = utils.modf(days, function (dayfrac, days) {
- d = days;
- if (dayfrac) {
- return utils.modf(dayfrac * 24 * 3600, function (dsf, dsw) {
- s = dsw;
- return dsf;
- });
- }
- return 0;
- });
-
- var secondsfrac = utils.modf(seconds, function (sf, s) {
- seconds = s;
- return sf + daysecondsfrac;
- });
- utils.divmod(seconds, 24*3600, function (days, seconds) {
- d += days;
- s += seconds;
- });
- // seconds isn't referenced again before redefinition
-
- microseconds += secondsfrac * 1e6;
- utils.divmod(microseconds, 1000000, function (seconds, microseconds) {
- utils.divmod(seconds, 24*3600, function (days, seconds) {
- d += days;
- s += seconds;
- m += Math.round(microseconds);
- });
- });
-
- // Carrying still possible here?
-
- this.days = d;
- this.seconds = s;
- this.microseconds = m;
- },
- __str__: function () {
- var hh, mm, ss;
- utils.divmod(this.seconds, 60, function (m, s) {
- utils.divmod(m, 60, function (h, m) {
- hh = h;
- mm = m;
- ss = s;
- });
- });
- var s = _.str.sprintf("%d:%02d:%02d", hh, mm, ss);
- if (this.days) {
- s = _.str.sprintf("%d day%s, %s",
- this.days,
- (this.days != 1 && this.days != -1) ? 's' : '',
- s);
- }
- if (this.microseconds) {
- s = _.str.sprintf("%s.%06d", s, this.microseconds);
- }
- return py.str.fromJSON(s);
- },
- __eq__: function (other) {
- if (!py.PY_isInstance(other, datetime.timedelta)) {
- return py.False;
- }
-
- return (this.days === other.days
- && this.seconds === other.seconds
- && this.microseconds === other.microseconds)
- ? py.True : py.False;
- },
- __add__: function (other) {
- if (!py.PY_isInstance(other, datetime.timedelta)) {
- return py.NotImplemented;
- }
- return py.PY_call(datetime.timedelta, [
- py.float.fromJSON(this.days + other.days),
- py.float.fromJSON(this.seconds + other.seconds),
- py.float.fromJSON(this.microseconds + other.microseconds)
- ]);
- },
- __radd__: function (other) { return this.__add__(other); },
- __sub__: function (other) {
- if (!py.PY_isInstance(other, datetime.timedelta)) {
- return py.NotImplemented;
- }
- return py.PY_call(datetime.timedelta, [
- py.float.fromJSON(this.days - other.days),
- py.float.fromJSON(this.seconds - other.seconds),
- py.float.fromJSON(this.microseconds - other.microseconds)
- ]);
- },
- __rsub__: function (other) {
- if (!py.PY_isInstance(other, datetime.timedelta)) {
- return py.NotImplemented;
- }
- return this.__neg__().__add__(other);
- },
- __neg__: function () {
- return py.PY_call(datetime.timedelta, [
- py.float.fromJSON(-this.days),
- py.float.fromJSON(-this.seconds),
- py.float.fromJSON(-this.microseconds)
- ]);
- },
- __pos__: function () { return this; },
- __mul__: function (other) {
- if (!py.PY_isInstance(other, py.float)) {
- return py.NotImplemented;
- }
- var n = other.toJSON();
- return py.PY_call(datetime.timedelta, [
- py.float.fromJSON(this.days * n),
- py.float.fromJSON(this.seconds * n),
- py.float.fromJSON(this.microseconds * n)
- ]);
- },
- __rmul__: function (other) { return this.__mul__(other); },
- __div__: function (other) {
- if (!py.PY_isInstance(other, py.float)) {
- return py.NotImplemented;
- }
- var usec = ((this.days * 24 * 3600) + this.seconds) * 1000000
- + this.microseconds;
- return py.PY_call(
- datetime.timedelta, [
- zero, zero, py.float.fromJSON(usec / other.toJSON())]);
- },
- __floordiv__: function (other) { return this.__div__(other); },
- total_seconds: function () {
- return py.float.fromJSON(
- this.days * 86400
- + this.seconds
- + this.microseconds / 1000000);
- },
- __nonzero__: function () {
- return (!!this.days || !!this.seconds || !!this.microseconds)
- ? py.True
- : py.False;
- }
-});
-
-datetime.datetime = py.type('datetime', null, {
- __init__: function () {
- var zero = py.float.fromJSON(0);
- var args = py.PY_parseArgs(arguments, [
- 'year', 'month', 'day',
- ['hour', zero], ['minute', zero], ['second', zero],
- ['microsecond', zero], ['tzinfo', py.None]
- ]);
- for(var key in args) {
- if (!args.hasOwnProperty(key)) { continue; }
- this[key] = asJS(args[key]);
- }
- },
- replace: function () {
- var args = py.PY_parseArgs(arguments, [
- ['year', py.None], ['month', py.None], ['day', py.None],
- ['hour', py.None], ['minute', py.None], ['second', py.None],
- ['microsecond', py.None] // FIXME: tzinfo, can't use None as valid input
- ]);
- var params = {};
- for(var key in args) {
- if (!args.hasOwnProperty(key)) { continue; }
-
- var arg = args[key];
- params[key] = (arg === py.None ? this[key] : asJS(arg));
- }
- return py.PY_call(datetime.datetime, params);
- },
- strftime: function () {
- var self = this;
- var args = py.PY_parseArgs(arguments, 'format');
- return py.str.fromJSON(args.format.toJSON()
- .replace(/%([A-Za-z])/g, function (m, c) {
- switch (c) {
- case 'Y': return _.str.sprintf('%04d', self.year);
- case 'm': return _.str.sprintf('%02d', self.month);
- case 'd': return _.str.sprintf('%02d', self.day);
- case 'H': return _.str.sprintf('%02d', self.hour);
- case 'M': return _.str.sprintf('%02d', self.minute);
- case 'S': return _.str.sprintf('%02d', self.second);
- }
- throw new Error('ValueError: No known conversion for ' + m);
- }));
- },
- now: py.classmethod.fromJSON(function () {
- var d = new Date();
- return py.PY_call(datetime.datetime, [
- d.getFullYear(), d.getMonth() + 1, d.getDate(),
- d.getHours(), d.getMinutes(), d.getSeconds(),
- d.getMilliseconds() * 1000]);
- }),
- today: py.classmethod.fromJSON(function () {
- var dt_class = py.PY_getAttr(datetime, 'datetime');
- return py.PY_call(py.PY_getAttr(dt_class, 'now'));
- }),
- utcnow: py.classmethod.fromJSON(function () {
- var d = new Date();
- return py.PY_call(datetime.datetime,
- [d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
- d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(),
- d.getUTCMilliseconds() * 1000]);
- }),
- combine: py.classmethod.fromJSON(function () {
- var args = py.PY_parseArgs(arguments, 'date time');
- return py.PY_call(datetime.datetime, [
- py.PY_getAttr(args.date, 'year'),
- py.PY_getAttr(args.date, 'month'),
- py.PY_getAttr(args.date, 'day'),
- py.PY_getAttr(args.time, 'hour'),
- py.PY_getAttr(args.time, 'minute'),
- py.PY_getAttr(args.time, 'second')
- ]);
- }),
- toJSON: function () {
- return new Date(
- this.year,
- this.month - 1,
- this.day,
- this.hour,
- this.minute,
- this.second,
- this.microsecond / 1000);
- },
-});
-
-datetime.date = py.type('date', null, {
- __init__: function () {
- var args = py.PY_parseArgs(arguments, 'year month day');
- this.year = asJS(args.year);
- this.month = asJS(args.month);
- this.day = asJS(args.day);
- },
- strftime: function () {
- var self = this;
- var args = py.PY_parseArgs(arguments, 'format');
- return py.str.fromJSON(args.format.toJSON()
- .replace(/%([A-Za-z])/g, function (m, c) {
- switch (c) {
- case 'Y': return self.year;
- case 'm': return _.str.sprintf('%02d', self.month);
- case 'd': return _.str.sprintf('%02d', self.day);
- }
- throw new Error('ValueError: No known conversion for ' + m);
- }));
- },
- __eq__: function (other) {
- return (this.year === other.year
- && this.month === other.month
- && this.day === other.day)
- ? py.True : py.False;
- },
- replace: function () {
- var args = py.PY_parseArgs(arguments, [
- ['year', py.None], ['month', py.None], ['day', py.None]
- ]);
- var params = {};
- for(var key in args) {
- if (!args.hasOwnProperty(key)) { continue; }
-
- var arg = args[key];
- params[key] = (arg === py.None ? this[key] : asJS(arg));
- }
- return py.PY_call(datetime.date, params);
- },
- __add__: function (other) {
- if (!py.PY_isInstance(other, datetime.timedelta)) {
- return py.NotImplemented;
- }
- var s = tmxxx(this.year, this.month, this.day + other.days);
- return datetime.date.fromJSON(s.year, s.month, s.day);
- },
- __radd__: function (other) { return this.__add__(other); },
- __sub__: function (other) {
- if (py.PY_isInstance(other, datetime.timedelta)) {
- return py.PY_add(this, py.PY_negative(other));
- }
- if (py.PY_isInstance(other, datetime.date)) {
- // FIXME: getattr and sub API methods
- return py.PY_call(datetime.timedelta, [
- py.PY_subtract(
- py.PY_call(py.PY_getAttr(this, 'toordinal')),
- py.PY_call(py.PY_getAttr(other, 'toordinal')))
- ]);
- }
- return py.NotImplemented;
- },
- toordinal: function () {
- return py.float.fromJSON(ymd2ord(this.year, this.month, this.day));
- },
- weekday: function () {
- return py.float.fromJSON((this.toordinal().toJSON()+6)%7);
- },
- fromJSON: function (year, month, day) {
- return py.PY_call(datetime.date, [year, month, day]);
- },
- today: py.classmethod.fromJSON(function () {
- var d = new Date ();
- return py.PY_call(datetime.date, [
- d.getFullYear(), d.getMonth() + 1, d.getDate()]);
- }),
-});
-/**
- Returns the current local date, which means the date on the client (which can be different
- compared to the date of the server).
-
- @return {datetime.date}
-*/
-function context_today () {
- var d = new Date();
- return py.PY_call(
- datetime.date, [d.getFullYear(), d.getMonth() + 1, d.getDate()]);
-}
-
-datetime.time = py.type('time', null, {
- __init__: function () {
- var zero = py.float.fromJSON(0);
- var args = py.PY_parseArgs(arguments, [
- ['hour', zero], ['minute', zero], ['second', zero], ['microsecond', zero],
- ['tzinfo', py.None]
- ]);
-
- for(var k in args) {
- if (!args.hasOwnProperty(k)) { continue; }
- this[k] = asJS(args[k]);
- }
- }
-});
-
-var time = py.PY_call(py.object);
-time.strftime = py.PY_def.fromJSON(function () {
- var args = py.PY_parseArgs(arguments, 'format');
- var dt_class = py.PY_getAttr(datetime, 'datetime');
- var d = py.PY_call(py.PY_getAttr(dt_class, 'utcnow'));
- return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
-});
-
-var args = _.map(('year month day '
- + 'years months weeks days '
- + 'weekday leapdays yearday nlyearday').split(' '), function (arg) {
- switch (arg) {
- case 'years':case 'months':case 'days':case 'leapdays':case 'weeks':
- return [arg, zero];
- case 'year':case 'month':case 'day':case 'weekday':
- case 'yearday':case 'nlyearday':
- return [arg, null];
- default:
- throw new Error("Unknown relativedelta argument " + arg);
- }
-});
-args.unshift('*');
-
-var _utils = {
- monthrange: function (year, month) {
- if (month < 1 || month > 12) {
- throw new Error("Illegal month " + month);
- }
-
- var day1 = this.weekday(year, month, 1);
- var ndays = this.mdays[month] + (month == this.February && this.isleap(year));
- return [day1, ndays];
- },
- weekday: function (year, month, day) {
- var date = py.PY_call(datetime.date, [year, month, day]);
- return py.PY_call(py.PY_getAttr(date, 'weekday'));
- },
- isleap: function (year) {
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
- },
- mdays: [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
- January: 1,
- February: 2
-};
-
-var relativedelta = py.type('relativedelta', null, {
- __init__: function () {
- this.ops = py.PY_parseArgs(arguments, args);
- this.ops.days = py.float.fromJSON(
- asJS(this.ops.days) + asJS(this.ops.weeks) * 7
- );
-
- var yday = zero;
- if (this.ops.nlyearday) {
- yday = this.ops.nlyearday;
- } else if (this.ops.yearday) {
- yday = this.ops.yearday;
- if (asJS(this.ops.yearday) > 59) {
- this.ops.leapdays = py.float.fromJS(-1);
- }
- }
- if (py.PY_isTrue(yday)) {
- var ydayidx = [31, 59, 90, 120, 151, 181, 212,
- 243, 273, 304, 334, 366];
- for(var idx=0; idx<ydayidx.length; ++idx) {
- var ydays = ydayidx[idx];
- if (asJS(yday) <= ydays) {
- this.ops.month = py.float.fromJSON(idx+1);
- if (!idx) {
- this.ops.day = yday;
- } else {
- this.ops.day = py.PY_subtract(
- yday,
- py.float.fromJSON(ydayidx[idx-1])
- );
- }
- break;
- }
- }
- if (idx === ydayidx.length) {
- throw new Error("Invalid year day (" + asJS(yday) + ")");
- }
- }
- this._fix();
- },
- _fix: function () {
- var months = asJS(this.ops.months);
- if (Math.abs(months) > 11) {
- var s = months > 0 ? 1 : -1;
- var r = utils.divmod(months * s, 12);
- this.ops.months = py.float.fromJSON(r.mod*s);
- this.ops.years = py.float.fromJSON(
- asJS(this.ops.years) + r.div*s);
- }
- this._has_time = 0;
- },
- __add__: function (other) {
- if (!py.PY_isInstance(other, datetime.date)) {
- return py.NotImplemented;
- }
- // TODO: test this whole mess
- var year = (asJS(this.ops.year) || asJS(other.year)) + asJS(this.ops.years);
- var month = asJS(this.ops.month) || asJS(other.month);
- var months;
- if (months = asJS(this.ops.months)) {
- if (Math.abs(months) < 1 || Math.abs(months) > 12) {
- throw new Error("Can only use relative months between -12 and +12");
- }
- month += months;
- if (month > 12) {
- year += 1;
- month -= 12;
- }
- if (month < 1) {
- year -= 1;
- month += 12;
- }
- }
-
- var day = Math.min(_utils.monthrange(year, month)[1],
- asJS(this.ops.day) || asJS(other.day));
-
- var repl = {
- year: py.float.fromJSON(year),
- month: py.float.fromJSON(month),
- day: py.float.fromJSON(day)
- };
-
- var days = asJS(this.ops.days);
- if (py.PY_isTrue(this.ops.leapdays) && month > 2 && _utils.isleap(year)) {
- days += asJS(this.ops.leapdays);
- }
-
- var ret = py.PY_add(
- py.PY_call(py.PY_getAttr(other, 'replace'), repl),
- py.PY_call(datetime.timedelta, {
- days: py.float.fromJSON(days)
- })
- );
-
- if (this.ops.weekday) {
- // FIXME: only handles numeric weekdays, not decorated
- var weekday = asJS(this.ops.weekday), nth = 1;
- var jumpdays = (Math.abs(nth) - 1) * 7;
-
- var ret_weekday = asJS(py.PY_call(py.PY_getAttr(ret, 'weekday')));
- if (nth > 0) {
- jumpdays += (7-ret_weekday+weekday) % 7;
- } else {
- jumpdays += (ret_weekday - weekday) % 7;
- jumpdays *= -1;
- }
- ret = py.PY_add(
- ret,
- py.PY_call(datetime.timedelta, {
- days: py.float.fromJSON(jumpdays)
- })
- );
- }
-
- return ret;
- },
- __radd__: function (other) {
- return this.__add__(other);
- },
- __rsub__: function (other) {
- return this.__neg__().__radd__(other);
- },
- __neg__: function () {
- return py.PY_call(relativedelta, {
- years: py.PY_negative(this.ops.years),
- months: py.PY_negative(this.ops.months),
- days: py.PY_negative(this.ops.days),
- leapdays: this.ops.leapdays,
- year: this.ops.year,
- month: this.ops.month,
- day: this.ops.day,
- weekday: this.ops.weekday
- });
- }
-});
-
-// recursively wraps JS objects passed into the context to attributedicts
-// which jsonify back to JS objects
-function wrap(value) {
- if (value === null) { return py.None; }
-
- switch (typeof value) {
- case 'undefined': throw new Error("No conversion for undefined");
- case 'boolean': return py.bool.fromJSON(value);
- case 'number': return py.float.fromJSON(value);
- case 'string': return py.str.fromJSON(value);
- }
-
- switch(value.constructor) {
- case Object: return wrapping_dict.fromJSON(value);
- case Array: return wrapping_list.fromJSON(value);
- }
-
- throw new Error("ValueError: unable to wrap " + value);
-}
-
-var wrapping_dict = py.type('wrapping_dict', null, {
- __init__: function () {
- this._store = {};
- },
- __getitem__: function (key) {
- var k = key.toJSON();
- if (!(k in this._store)) {
- throw new Error("KeyError: '" + k + "'");
- }
- return wrap(this._store[k]);
- },
- __getattr__: function (key) {
- return this.__getitem__(py.str.fromJSON(key));
- },
- __len__: function () {
- return Object.keys(this._store).length
- },
- __nonzero__: function () {
- return py.PY_size(this) > 0 ? py.True : py.False;
- },
- get: function () {
- var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
-
- if (!(args.k.toJSON() in this._store)) { return args.d; }
- return this.__getitem__(args.k);
- },
- fromJSON: function (d) {
- var instance = py.PY_call(wrapping_dict);
- instance._store = d;
- return instance;
- },
- toJSON: function () {
- return this._store;
- },
-});
-
-var wrapping_list = py.type('wrapping_list', null, {
- __init__: function () {
- this._store = [];
- },
- __getitem__: function (index) {
- return wrap(this._store[index.toJSON()]);
- },
- __len__: function () {
- return this._store.length;
- },
- __nonzero__: function () {
- return py.PY_size(this) > 0 ? py.True : py.False;
- },
- fromJSON: function (ar) {
- var instance = py.PY_call(wrapping_list);
- instance._store = ar;
- return instance;
- },
- toJSON: function () {
- return this._store;
- },
-});
-
-function wrap_context (context) {
- for (var k in context) {
- if (!context.hasOwnProperty(k)) { continue; }
- var val = context[k];
-
- if (val === null) { continue; }
- if (val.constructor === Array) {
- context[k] = wrapping_list.fromJSON(val);
- } else if (val.constructor === Object
- && !py.PY_isInstance(val, py.object)) {
- context[k] = wrapping_dict.fromJSON(val);
- }
- }
- return context;
-}
-
-function eval_contexts (contexts, evaluation_context) {
- evaluation_context = _.extend(pycontext(), evaluation_context || {});
- return _(contexts).reduce(function (result_context, ctx) {
- // __eval_context evaluations can lead to some of `contexts`'s
- // values being null, skip them as well as empty contexts
- if (_.isEmpty(ctx)) { return result_context; }
- if (_.isString(ctx)) {
- // wrap raw strings in context
- ctx = { __ref: 'context', __debug: ctx };
- }
- var evaluated = ctx;
- switch(ctx.__ref) {
- case 'context':
- evaluation_context.context = evaluation_context;
- evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
- break;
- case 'compound_context':
- var eval_context = eval_contexts([ctx.__eval_context]);
- evaluated = eval_contexts(
- ctx.__contexts, _.extend({}, evaluation_context, eval_context));
- break;
- }
- // add newly evaluated context to evaluation context for following
- // siblings
- _.extend(evaluation_context, evaluated);
- return _.extend(result_context, evaluated);
- }, {});
-}
-
-function eval_domains (domains, evaluation_context) {
- evaluation_context = _.extend(pycontext(), evaluation_context || {});
- var result_domain = [];
- // Normalize only if the first domain is the array ["|"] or ["!"]
- var need_normalization = (
- domains &&
- domains.length > 0 &&
- domains[0].length === 1 &&
- (domains[0][0] === "|" || domains[0][0] === "!")
- );
- _(domains).each(function (domain) {
- if (_.isString(domain)) {
- // wrap raw strings in domain
- domain = { __ref: 'domain', __debug: domain };
- }
- var domain_array_to_combine;
- switch(domain.__ref) {
- case 'domain':
- evaluation_context.context = evaluation_context;
- domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context));
- break;
- case 'compound_domain':
- var eval_context = eval_contexts([domain.__eval_context]);
- domain_array_to_combine = eval_domains(
- domain.__domains, _.extend({}, evaluation_context, eval_context)
- );
- break;
- default:
- domain_array_to_combine = domain;
- }
- if (need_normalization) {
- domain_array_to_combine = get_normalized_domain(domain_array_to_combine);
- }
- result_domain.push.apply(result_domain, domain_array_to_combine);
- });
- return result_domain;
-}
-
-/**
- * Returns a normalized copy of the given domain array. Normalization is
- * is making the implicit "&" at the start of the domain explicit, e.g.
- * [A, B, C] would become ["&", "&", A, B, C].
- *
- * @param {Array} domain_array
- * @returns {Array} normalized copy of the given array
- */
-function get_normalized_domain(domain_array) {
- var expected = 1; // Holds the number of expected domain expressions
- _.each(domain_array, function (item) {
- if (item === "&" || item === "|") {
- expected++;
- } else if (item !== "!") {
- expected--;
- }
- });
- var new_explicit_ands = _.times(-expected, _.constant("&"));
- return new_explicit_ands.concat(domain_array);
-}
-
-function eval_groupbys (contexts, evaluation_context) {
- evaluation_context = _.extend(pycontext(), evaluation_context || {});
- var result_group = [];
- _(contexts).each(function (ctx) {
- if (_.isString(ctx)) {
- // wrap raw strings in context
- ctx = { __ref: 'context', __debug: ctx };
- }
- var group;
- var evaluated = ctx;
- switch(ctx.__ref) {
- case 'context':
- evaluation_context.context = evaluation_context;
- evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
- break;
- case 'compound_context':
- var eval_context = eval_contexts([ctx.__eval_context]);
- evaluated = eval_contexts(
- ctx.__contexts, _.extend({}, evaluation_context, eval_context));
- break;
- }
- group = evaluated.group_by;
- if (!group) { return; }
- if (typeof group === 'string') {
- result_group.push(group);
- } else if (group instanceof Array) {
- result_group.push.apply(result_group, group);
- } else {
- throw new Error('Got invalid groupby {{'
- + JSON.stringify(group) + '}}');
- }
- _.extend(evaluation_context, evaluated);
- });
- return result_group;
-}
-
-
-function pycontext () {
- return _.extend({
- datetime: datetime,
- context_today: context_today,
- time: time,
- relativedelta: relativedelta,
- current_date: py.PY_call(
- time.strftime, [py.str.fromJSON('%Y-%m-%d')]),
- }, session.user_context);
-}
-
-/**
- * @param {String} type "domains", "contexts" or "groupbys"
- * @param {Array} object domains or contexts to evaluate
- * @param {Object} [context] evaluation context
- */
-function pyeval (type, object, context, options) {
- options = options || {};
- context = _.extend(pycontext(), context || {});
-
- //noinspection FallthroughInSwitchStatementJS
- switch(type) {
- case 'context':
- case 'contexts':
- if (type === 'context')
- object = [object];
- return eval_contexts((options.no_user_context ? [] : [session.user_context]).concat(object), context);
- case 'domain':
- case 'domains':
- if (type === 'domain')
- object = [object];
- return eval_domains(object, context);
- case 'groupbys':
- return eval_groupbys(object, context);
- }
- throw new Error("Unknow evaluation type " + type);
-}
-
-function eval_arg (arg) {
- if (typeof arg !== 'object' || !arg.__ref) { return arg; }
- switch(arg.__ref) {
- case 'domain': case 'compound_domain':
- return pyeval('domains', [arg]);
- case 'context': case 'compound_context':
- return pyeval('contexts', [arg]);
- default:
- throw new Error(_t("Unknown nonliteral type ") + ' ' + arg.__ref);
- }
-}
-
-/**
- * If args or kwargs are unevaluated contexts or domains (compound or not),
- * evaluated them in-place.
- *
- * Potentially mutates both parameters.
- *
- * @param args
- * @param kwargs
- */
-function ensure_evaluated (args, kwargs) {
- for (var i=0; i<args.length; ++i) {
- args[i] = eval_arg(args[i]);
- }
- for (var k in kwargs) {
- if (!kwargs.hasOwnProperty(k)) { continue; }
- kwargs[k] = eval_arg(kwargs[k]);
- }
-}
-
-function eval_domains_and_contexts (source) {
- return new $.Deferred(function (d) {setTimeout(function () {
- var result;
- try {
- result = sync_eval_domains_and_contexts(source);
- }
- catch (e) {
- result = { error: {
- code: 400,
- message: _t("Evaluation Error"),
- data: {
- type: 'local_exception',
- debug: _.str.sprintf(
- _t("Local evaluation failure\n%s\n\n%s"),
- e.message, JSON.stringify(source))
- }
- }};
- }
- d.resolve(result);
- }, 0); });
-}
-
-function sync_eval_domains_and_contexts (source) {
- var contexts = ([session.user_context] || []).concat(source.contexts);
- // see Session.eval_context in Python
- return {
- context: pyeval('contexts', contexts),
- domain: pyeval('domains', source.domains),
- group_by: pyeval('groupbys', source.group_by_seq || [])
- };
-}
-
-function py_eval (expr, context) {
- return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
-}
-
-
-return {
- context: pycontext,
- ensure_evaluated: ensure_evaluated,
- eval: pyeval,
- eval_domains_and_contexts: eval_domains_and_contexts,
- sync_eval_domains_and_contexts: sync_eval_domains_and_contexts,
- py_eval: py_eval,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/qweb.js b/addons/web/static/src/js/framework/qweb.js
deleted file mode 100644
index 50742075e05..00000000000
--- a/addons/web/static/src/js/framework/qweb.js
+++ /dev/null
@@ -1,51 +0,0 @@
-odoo.define('web.QWeb', function (require) {
-"use strict";
-
-var translation = require('web.translation');
-
-var _t = translation._t;
-
-function QWeb(debug, default_dict) {
- var qweb = new QWeb2.Engine();
- qweb.default_dict = _.extend({}, default_dict || {}, {
- '_' : _,
- 'JSON': JSON,
- '_t' : translation._t,
- '__debug__': debug,
- 'moment': function(date) { return new moment(date); },
- 'csrf_token': odoo.csrf_token,
- });
- qweb.debug = debug;
- qweb.preprocess_node = preprocess_node;
- return qweb;
-}
-
-function preprocess_node() {
- // Note that 'this' is the Qweb Node
- switch (this.node.nodeType) {
- case Node.TEXT_NODE:
- case Node.CDATA_SECTION_NODE:
- // Text and CDATAs
- var translation = this.node.parentNode.attributes['t-translation'];
- if (translation && translation.value === 'off') {
- return;
- }
- var match = /^(\s*)([\s\S]+?)(\s*)$/.exec(this.node.data);
- if (match) {
- this.node.data = match[1] + _t(match[2]) + match[3];
- }
- break;
- case Node.ELEMENT_NODE:
- // Element
- var attr, attrs = ['label', 'title', 'alt', 'placeholder'];
- while ((attr = attrs.pop())) {
- if (this.attributes[attr]) {
- this.attributes[attr] = _t(this.attributes[attr]);
- }
- }
- }
-}
-
-return QWeb;
-
-});
diff --git a/addons/web/static/src/js/framework/registry.js b/addons/web/static/src/js/framework/registry.js
deleted file mode 100644
index b8bc1dbadfe..00000000000
--- a/addons/web/static/src/js/framework/registry.js
+++ /dev/null
@@ -1,54 +0,0 @@
-odoo.define('web.Registry', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-
-var Registry = Class.extend({
- init: function (mapping) {
- this.map = Object.create(mapping || null);
- },
- get: function (key) {
- return this.map[key];
- },
- contains: function (key) {
- return (key in this.map);
- },
- /**
- * 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++) {
- if (keys[i] in this.map) {
- return this.map[keys[i]];
- }
- }
- return null;
- },
- add: function (key, value) {
- this.map[key] = value;
- 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 Registry(this.map);
- _.extend(child.map, mapping);
- return child;
- },
-});
-
-return Registry;
-
-});
-
diff --git a/addons/web/static/src/js/framework/session.js b/addons/web/static/src/js/framework/session.js
deleted file mode 100644
index 516436af93c..00000000000
--- a/addons/web/static/src/js/framework/session.js
+++ /dev/null
@@ -1,411 +0,0 @@
-odoo.define('web.Session', function (require) {
-"use strict";
-
-var ajax = require('web.ajax');
-var core = require('web.core');
-var utils = require('web.utils');
-
-var _t = core._t;
-var qweb = core.qweb;
-var mixins = core.mixins;
-
-// To do: refactor session. Session accomplishes several concerns (rpc,
-// configuration, currencies (wtf?), user permissions...). They should be
-// clarified and separated.
-
-var Session = core.Class.extend(mixins.EventDispatcherMixin, {
- /**
-
- @param parent The parent of the newly created 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) {
- mixins.EventDispatcherMixin.init.call(this, parent);
- options = options || {};
- this.module_list = (options.modules && options.modules.slice()) || (window.odoo._modules && window.odoo._modules.slice()) || [];
- 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.use_cors = options.use_cors || false;
- this.setup(origin);
- var debug_param = $.deparam($.param.querystring()).debug;
- this.debug = (debug_param !== undefined ? debug_param || 1 : false);
-
- // for historic reasons, the session requires a name to properly work
- // (see the methods get_cookie and set_cookie). We should perhaps
- // remove it totally (but need to make sure the cookies are properly set)
- this.name = "instance0";
- // TODO: session store in cookie should be optional
- this.qweb_mutex = new utils.Mutex();
- this.currencies = {};
- this._groups_def = {};
- },
- setup: function(origin, options) {
- // 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;
- options = options || {};
- if ('use_cors' in options) {
- this.use_cors = options.use_cors;
- }
- },
- /**
- * Setup a session
- */
- session_bind: function(origin) {
- var self = this;
- this.setup(origin);
- qweb.default_dict._s = this.origin;
- this.uid = null;
- this.username = null;
- this.user_context= {};
- this.db = null;
- this.module_loaded = {};
- _(this.module_list).each(function (mod) {
- self.module_loaded[mod] = true;
- });
- this.active_id = null;
- return this.session_init();
- },
- /**
- * Init a session, reloads from cookie, if it exists
- */
- session_init: function () {
- var def = this.session_reload();
- if (this.is_frontend) return def;
-
- var self = this;
- return def.then(function() {
- var modules = self.module_list.join(',');
- var deferred = self.load_qweb(modules);
- if(self.session_is_valid()) {
- return deferred.then(function() { return self.load_modules(); });
- }
- return $.when(
- deferred,
- self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list}).then(function(trans) {
- _t.database.set_bundle(trans);
- })
- );
- });
- },
- session_is_valid: function() {
- var db = $.deparam.querystring().db;
- if (db && this.db !== db) {
- return false;
- }
- return !!this.uid;
- },
- /**
- * The session is validated by restoration of a previous session
- */
- session_authenticate: function() {
- var self = this;
- return $.when(this._session_authenticate.apply(this, arguments)).then(function() {
- return self.load_modules();
- });
- },
- /**
- * 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);
- });
- },
- session_logout: function() {
- $.bbq.removeState();
- return this.rpc("/web/session/destroy", {});
- },
- user_has_group: function(group) {
- if (!this.uid) {
- return $.when(false);
- }
- var def = this._groups_def[group];
- if (!def) {
- def = this._groups_def[group] = this.rpc('/web/dataset/call_kw/res.users/has_group', {
- "model": "res.users",
- "method": "has_group",
- "args": [group],
- "kwargs": {}
- });
- }
- return def;
- },
- get_cookie: function (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*/, '');
- if(cookie.indexOf(nameEQ) === 0) {
- try {
- return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
- } catch(err) {
- // wrong cookie, delete it
- this.set_cookie(name, '', -1);
- }
- }
- }
- return null;
- },
- /**
- * Create a new cookie with the provided name and value
- *
- * @private
- * @param name the cookie's name
- * @param value the cookie's value
- * @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;
- utils.set_cookie(this.name + '|' + name, value, ttl);
- },
- /**
- * Load additional web addons of that instance and init them
- *
- */
- load_modules: function() {
- var self = this;
- var modules = odoo._modules;
- var all_modules = _.uniq(self.module_list.concat(modules));
- var to_load = _.difference(modules, self.module_list).join(',');
- this.module_list = all_modules;
-
- var loaded = $.when(self.load_translations());
- var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
- var file_list = [ locale ];
- if(to_load.length) {
- loaded = $.when(
- loaded,
- self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)),
- self.load_qweb(to_load),
- self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) {
- file_list = file_list.concat(files);
- })
- );
- }
- return loaded.then(function () {
- return self.load_js(file_list);
- }).done(function() {
- self.on_modules_loaded();
- self.trigger('module_loaded');
- });
- },
- load_translations: function() {
- return _t.database.load_translations(this, this.module_list, this.user_context.lang);
- },
- load_css: function (files) {
- var self = this;
- _.each(files, function (file) {
- ajax.loadCSS(self.url(file, null));
- });
- },
- load_js: function(files) {
- var self = this;
- var d = $.Deferred();
- if (files.length !== 0) {
- var file = files.shift();
- var url = self.url(file, null);
- ajax.loadJS(url).done(d.resolve);
- } else {
- d.resolve();
- }
- return d;
- },
- load_qweb: function(mods) {
- this.qweb_mutex.exec(function () {
- return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) {
- if (!doc) { return; }
- qweb.add_template(doc);
- });
- });
- return this.qweb_mutex.def;
- },
- on_modules_loaded: function() {
- var openerp = window.openerp;
- for(var j=0; j<this.module_list.length; j++) {
- var mod = this.module_list[j];
- if(this.module_loaded[mod])
- continue;
- openerp[mod] = {};
- // init module mod
- var fct = openerp._openerp[mod];
- if(typeof(fct) === "function") {
- openerp._openerp[mod] = {};
- for (var k in fct) {
- openerp._openerp[mod][k] = fct[k];
- }
- fct(openerp, openerp._openerp[mod]);
- }
- this.module_loaded[mod] = true;
- }
- },
- get_currency: function(currency_id) {
- return this.currencies[currency_id];
- },
- get_file: function (options) {
- if (this.override_session){
- options.data.session_id = this.session_id;
- }
- options.session = this;
- ajax.get_file(options);
- },
- /**
- * (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 result = _.extend({}, window.odoo.session_info);
- delete result.session_id;
- _.extend(this, result);
- return $.when();
- },
- 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.use_cors && (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 = utils.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;
- options.headers = _.extend({}, options.headers)
- if (odoo.debug) {
- options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug;
- }
-
- 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 = ajax.jsonRpc;
- if (self.override_session) {
- options.headers["X-Openerp-Session-Id"] = self.session_id || '';
- }
- } else if (self.use_cors) {
- fct = ajax.jsonRpc;
- url = self.url(url, null);
- options.session_id = self.session_id || '';
- if (self.override_session) {
- options.headers["X-Openerp-Session-Id"] = self.session_id || '';
- }
- } else {
- fct = ajax.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;
- },
-});
-
-return Session;
-
-});
diff --git a/addons/web/static/src/js/framework/time.js b/addons/web/static/src/js/framework/time.js
deleted file mode 100644
index af13aa5a3b1..00000000000
--- a/addons/web/static/src/js/framework/time.js
+++ /dev/null
@@ -1,299 +0,0 @@
-odoo.define('web.time', function (require) {
-"use strict";
-
-var translation = require('web.translation');
-var utils = require('web.utils');
-
-var lpad = utils.lpad;
-var rpad = utils.rpad;
-var _t = translation._t;
-
-/**
- * Replacer function for JSON.stringify, serializes Date objects to UTC
- * datetime in the OpenERP Server format.
- *
- * However, if a serialized value has a toJSON method that method is called
- * *before* the replacer is invoked. Date#toJSON exists, and thus the value
- * passed to the replacer is a string, the original Date has to be fetched
- * on the parent object (which is provided as the replacer's context).
- *
- * @param {String} k
- * @param {Object} v
- * @returns {Object}
- */
-function date_to_utc (k, v) {
- var value = this[k];
- if (!(value instanceof Date)) { return v; }
-
- return datetime_to_str(value);
-}
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * datetime string format (exemple: '2011-12-01 15:12:35.832').
- *
- * The time zone is assumed to be UTC (standard for OpenERP 6.1)
- * and will be converted to the browser's time zone.
- *
- * @param {String} str A string representing a datetime.
- * @returns {Date}
- */
-function str_to_datetime (str) {
- if(!str) {
- return str;
- }
- var regex = /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d(?:\.(\d+))?)$/;
- var res = regex.exec(str);
- if ( !res ) {
- throw new Error("'" + str + "' is not a valid datetime");
- }
- var tmp = new Date(2000,0,1);
- tmp.setUTCMonth(1970);
- tmp.setUTCMonth(0);
- tmp.setUTCDate(1);
- tmp.setUTCFullYear(parseFloat(res[1]));
- tmp.setUTCMonth(parseFloat(res[2]) - 1);
- tmp.setUTCDate(parseFloat(res[3]));
- tmp.setUTCHours(parseFloat(res[4]));
- tmp.setUTCMinutes(parseFloat(res[5]));
- tmp.setUTCSeconds(parseFloat(res[6]));
- tmp.setUTCSeconds(parseFloat(res[6]));
- tmp.setUTCMilliseconds(parseFloat(utils.rpad((res[7] || "").slice(0, 3), 3)));
- return tmp;
-}
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * date string format (exemple: '2011-12-01').
- *
- * As a date is not subject to time zones, we assume it should be
- * represented as a Date javascript object at 00:00:00 in the
- * time zone of the browser.
- *
- * @param {String} str A string representing a date.
- * @returns {Date}
- */
-function str_to_date (str) {
- if(!str) {
- return str;
- }
- var regex = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
- var res = regex.exec(str);
- if ( !res ) {
- throw new Error("'" + str + "' is not a valid date");
- }
- var tmp = new Date(2000,0,1);
- tmp.setFullYear(parseFloat(res[1]));
- tmp.setMonth(parseFloat(res[2]) - 1);
- tmp.setDate(parseFloat(res[3]));
- tmp.setHours(0);
- tmp.setMinutes(0);
- tmp.setSeconds(0);
- return tmp;
-}
-
-/**
- * Converts a string to a Date javascript object using OpenERP's
- * time string format (exemple: '15:12:35').
- *
- * The OpenERP times are supposed to always be naive times. We assume it is
- * represented using a javascript Date with a date 1 of January 1970 and a
- * time corresponding to the meant time in the browser's time zone.
- *
- * @param {String} str A string representing a time.
- * @returns {Date}
- */
-function str_to_time (str) {
- if(!str) {
- return str;
- }
- var regex = /^(\d\d):(\d\d):(\d\d(?:\.(\d+))?)$/;
- var res = regex.exec(str);
- if ( !res ) {
- throw new Error("'" + str + "' is not a valid time");
- }
- var tmp = new Date();
- tmp.setFullYear(1970);
- tmp.setMonth(0);
- tmp.setDate(1);
- tmp.setHours(parseFloat(res[1]));
- tmp.setMinutes(parseFloat(res[2]));
- tmp.setSeconds(parseFloat(res[3]));
- tmp.setMilliseconds(parseFloat(rpad((res[4] || "").slice(0, 3), 3)));
- return tmp;
-}
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * datetime string format (exemple: '2011-12-01 15:12:35').
- *
- * The time zone of the Date object is assumed to be the one of the
- * browser and it will be converted to UTC (standard for OpenERP 6.1).
- *
- * @param {Date} obj
- * @returns {String} A string representing a datetime.
- */
-function datetime_to_str (obj) {
- if (!obj) {
- return false;
- }
- return lpad(obj.getUTCFullYear(),4) + "-" + lpad(obj.getUTCMonth() + 1,2) + "-"
- + lpad(obj.getUTCDate(),2) + " " + lpad(obj.getUTCHours(),2) + ":"
- + lpad(obj.getUTCMinutes(),2) + ":" + lpad(obj.getUTCSeconds(),2);
-}
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * date string format (exemple: '2011-12-01').
- *
- * As a date is not subject to time zones, we assume it should be
- * represented as a Date javascript object at 00:00:00 in the
- * time zone of the browser.
- *
- * @param {Date} obj
- * @returns {String} A string representing a date.
- */
-function date_to_str (obj) {
- if (!obj) {
- return false;
- }
- return lpad(obj.getFullYear(),4) + "-" + lpad(obj.getMonth() + 1,2) + "-"
- + lpad(obj.getDate(),2);
-}
-
-/**
- * Converts a Date javascript object to a string using OpenERP's
- * time string format (exemple: '15:12:35').
- *
- * The OpenERP times are supposed to always be naive times. We assume it is
- * represented using a javascript Date with a date 1 of January 1970 and a
- * time corresponding to the meant time in the browser's time zone.
- *
- * @param {Date} obj
- * @returns {String} A string representing a time.
- */
-function time_to_str (obj) {
- if (!obj) {
- return false;
- }
- return lpad(obj.getHours(),2) + ":" + lpad(obj.getMinutes(),2) + ":"
- + lpad(obj.getSeconds(),2);
-}
-
-function auto_str_to_date (value) {
- try {
- return str_to_datetime(value);
- } catch(e) {}
- try {
- return str_to_date(value);
- } catch(e) {}
- try {
- return str_to_time(value);
- } catch(e) {}
- throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
-}
-
-function auto_date_to_str (value, type) {
- switch(type) {
- case 'datetime':
- return datetime_to_str(value);
- case 'date':
- return date_to_str(value);
- case 'time':
- return time_to_str(value);
- default:
- throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
- }
-}
-
-/**
- * Convert Python strftime to escaped moment.js format.
- *
- * @param {String} value original format
- */
-function strftime_to_moment_format (value) {
- if (_normalize_format_cache[value] === undefined) {
- var isletter = /[a-zA-Z]/,
- output = [],
- inToken = false;
-
- for (var index=0; index < value.length; ++index) {
- var character = value[index];
- if (character === '%' && !inToken) {
- inToken = true;
- continue;
- }
- if (isletter.test(character)) {
- if (inToken && normalize_format_table[character] !== undefined) {
- character = normalize_format_table[character];
- } else {
- character = '[' + character + ']'; // moment.js escape
- }
- }
- output.push(character);
- inToken = false;
- }
- _normalize_format_cache[value] = output.join('');
- }
- return _normalize_format_cache[value];
-}
-
-/**
- * Convert moment.js format to python strftime
- *
- * @param {String} value original format
- */
-function moment_to_strftime_format(value) {
- var regex = /(MMMM|DDDD|dddd|YYYY|MMM|ddd|mm|ss|ww|WW|MM|YY|hh|HH|DD|A|d)/g;
- return value.replace(regex, function(val){
- return '%'+inverse_normalize_format_table[val];
- });
-}
-
-var _normalize_format_cache = {};
-var normalize_format_table = {
- // Python strftime to moment.js conversion table
- // See openerp/addons/base/res/res_lang_view.xml
- // for details about supported directives
- 'a': 'ddd',
- 'A': 'dddd',
- 'b': 'MMM',
- 'B': 'MMMM',
- 'd': 'DD',
- 'H': 'HH',
- 'I': 'hh',
- 'j': 'DDDD',
- 'm': 'MM',
- 'M': 'mm',
- 'p': 'A',
- 'S': 'ss',
- 'U': 'ww',
- 'W': 'WW',
- 'w': 'd',
- 'y': 'YY',
- 'Y': 'YYYY',
- // unsupported directives
- 'c': 'ddd MMM D HH:mm:ss YYYY',
- 'x': 'MM/DD/YY',
- 'X': 'HH:mm:ss'
-};
-var inverse_normalize_format_table = _.invert(normalize_format_table);
-
-
-
-return {
- date_to_utc: date_to_utc,
- str_to_datetime: str_to_datetime,
- str_to_date: str_to_date,
- str_to_time: str_to_time,
- datetime_to_str: datetime_to_str,
- date_to_str: date_to_str,
- time_to_str: time_to_str,
- auto_str_to_date: auto_str_to_date,
- auto_date_to_str: auto_date_to_str,
- strftime_to_moment_format: strftime_to_moment_format,
- moment_to_strftime_format: moment_to_strftime_format,
-};
-
-});
-
diff --git a/addons/web/static/src/js/framework/translation.js b/addons/web/static/src/js/framework/translation.js
deleted file mode 100644
index f2bad0db4ee..00000000000
--- a/addons/web/static/src/js/framework/translation.js
+++ /dev/null
@@ -1,117 +0,0 @@
-
-odoo.define('web.translation', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-
-var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase# */{
- init: function() {
- this.db = {};
- this.multi_lang = false
- 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.multi_lang = translation_bundle.multi_lang
- 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;
- this.parameters.grouping = JSON.parse(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) {
- 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);
- });
- }
-});
-
-var _t = new 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
- */
-var _lt = function (s) {
- return {toString: function () { return _t(s); }};
-};
-
-/** Setup jQuery timeago */
-/*
- * Strings in timeago are "composed" with prefixes, words and suffixes. This
- * makes their detection by our translating system impossible. Use all literal
- * strings we're using with a translation mark here so the extractor can do its
- * job.
- */
-{
- _t('less than a minute ago');
- _t('about a minute ago');
- _t('%d minutes ago');
- _t('about an hour ago');
- _t('%d hours ago');
- _t('a day ago');
- _t('%d days ago');
- _t('about a month ago');
- _t('%d months ago');
- _t('about a year ago');
- _t('%d years ago');
-}
-
-
-return {
- _t: _t,
- _lt: _lt,
- TranslationDataBase: TranslationDataBase,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/utils.js b/addons/web/static/src/js/framework/utils.js
deleted file mode 100644
index 6b0a2ddbcc0..00000000000
--- a/addons/web/static/src/js/framework/utils.js
+++ /dev/null
@@ -1,566 +0,0 @@
-odoo.define('web.utils', function (require) {
-"use strict";
-
-var Class = require('web.Class');
-var translation = require('web.translation');
-
-var _t = translation._t;
-
-/**
- * computes (Math.floor(a/b), a%b and passes that to the callback.
- *
- * returns the callback's result
- */
-function divmod (a, b, fn) {
- var mod = a%b;
- // in python, sign(a % b) === sign(b). Not in JS. If wrong side, add a
- // round of b
- if (mod > 0 && b < 0 || mod < 0 && b > 0) {
- mod += b;
- }
- return fn(Math.floor(a/b), mod);
-}
-
-/**
- * Passes the fractional and integer parts of x to the callback, returns
- * the callback's result
- */
-function modf (x, fn) {
- var mod = x%1;
- if (mod < 0) {
- mod += 1;
- }
- return fn(mod, Math.floor(x));
-}
-
-/*
- * Left-pad provided arg 1 with zeroes until reaching size provided by second
- * argument.
- *
- * @param {Number|String} str value to pad
- * @param {Number} size size to reach on the final padded value
- * @returns {String} padded string
- */
-function lpad (str, size) {
- str = "" + str;
- return new Array(size - str.length + 1).join('0') + str;
-}
-
-function rpad (str, size) {
- str = "" + str;
- return str + new Array(size - str.length + 1).join('0');
-}
-
-var id = -1;
-
-function generate_id () {
- return ++id;
-}
-
-/**
- * Intersperses ``separator`` in ``str`` at the positions indicated by
- * ``indices``.
- *
- * ``indices`` is an array of relative offsets (from the previous insertion
- * position, starting from the end of the string) at which to insert
- * ``separator``.
- *
- * There are two special values:
- *
- * ``-1``
- * indicates the insertion should end now
- * ``0``
- * indicates that the previous section pattern should be repeated (until all
- * of ``str`` is consumed)
- *
- * @param {String} str
- * @param {Array<Number>} indices
- * @param {String} separator
- * @returns {String}
- */
-function intersperse (str, indices, separator) {
- separator = separator || '';
- var result = [], last = str.length;
-
- for(var i=0; i<indices.length; ++i) {
- var section = indices[i];
- if (section === -1 || last <= 0) {
- // Done with string, or -1 (stops formatting string)
- break;
- } else if(section === 0 && i === 0) {
- // repeats previous section, which there is none => stop
- break;
- } else if (section === 0) {
- // repeat previous section forever
- //noinspection AssignmentToForLoopParameterJS
- section = indices[--i];
- }
- result.push(str.substring(last-section, last));
- last -= section;
- }
-
- var s = str.substring(0, last);
- if (s) { result.push(s); }
- return result.reverse().join(separator);
-}
-
-function xml_to_json (node, strip_whitespace) {
- switch (node.nodeType) {
- case 9:
- return xml_to_json(node.documentElement, strip_whitespace);
- case 3:
- case 4:
- return (strip_whitespace && node.data.trim() === '') ? undefined : node.data;
- case 1:
- var attrs = $(node).getAttributes();
- _.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) {
- if (attrs[key]) {
- try {
- attrs[key] = JSON.parse(attrs[key]);
- } catch(e) { }
- }
- });
- return {
- tag: node.tagName.toLowerCase(),
- attrs: attrs,
- children: _.compact(_.map(node.childNodes, function(node) {
- return xml_to_json(node, strip_whitespace);
- })),
- };
- }
-}
-
-function json_node_to_xml (node, human_readable, indent) {
- // For debugging purpose, this function will convert a json node back to xml
- indent = indent || 0;
- var sindent = (human_readable ? (new Array(indent + 1).join('\t')) : ''),
- r = sindent + '<' + node.tag,
- cr = human_readable ? '\n' : '';
-
- if (typeof(node) === 'string') {
- return sindent + node.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
- } else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
- throw new Error(
- _.str.sprintf(_t("Node [%s] is not a JSONified XML node"),
- JSON.stringify(node)));
- }
- for (var attr in node.attrs) {
- var vattr = node.attrs[attr];
- if (typeof(vattr) !== 'string') {
- // domains, ...
- vattr = JSON.stringify(vattr);
- }
- vattr = vattr.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
- if (human_readable) {
- vattr = vattr.replace(/&quot;/g, "'");
- }
- r += ' ' + attr + '="' + vattr + '"';
- }
- if (node.children && node.children.length) {
- r += '>' + cr;
- var childs = [];
- for (var i = 0, ii = node.children.length; i < ii; i++) {
- childs.push(json_node_to_xml(node.children[i], human_readable, indent + 1));
- }
- r += childs.join(cr);
- r += cr + sindent + '</' + node.tag + '>';
- return r;
- } else {
- return r + '/>';
- }
-}
-
-function xml_to_str (node) {
- var str = "";
- if (window.XMLSerializer) {
- str = (new XMLSerializer()).serializeToString(node);
- } else if (window.ActiveXObject) {
- str = node.xml;
- } else {
- throw new Error(_t("Could not serialize XML"));
- }
- // Browsers won't deal with self closing tags except void elements:
- // http://www.w3.org/TR/html-markup/syntax.html
- var void_elements = 'area base br col command embed hr img input keygen link meta param source track wbr'.split(' ');
-
- // The following regex is a bit naive but it's ok for the xmlserializer output
- str = str.replace(/<([a-z]+)([^<>]*)\s*\/\s*>/g, function(match, tag, attrs) {
- if (void_elements.indexOf(tag) < 0) {
- return "<" + tag + attrs + "></" + tag + ">";
- } else {
- return match;
- }
- });
- return str;
-}
-
-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();
- });
- });
-};
-
-function get_cookie (c_name) {
- var cookies = document.cookie ? document.cookie.split('; ') : [];
- for (var i = 0, l = cookies.length; i < l; i++) {
- var parts = cookies[i].split('=');
- var name = parts.shift();
- var cookie = parts.join('=');
-
- if (c_name && c_name === name) {
- return cookie;
- }
- }
- return "";
-}
-
-/**
- * Create a cookie
- * @param {String} name : the name of the cookie
- * @param {String} value : the value stored in the cookie
- * @param {Integer} ttl : time to live of the cookie in millis. -1 to erase the cookie.
- */
-function set_cookie(name, value, ttl) {
- ttl = ttl || 24*60*60*365;
- document.cookie = [
- name + '=' + value,
- 'path=/',
- 'max-age=' + ttl,
- 'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
- ].join(';');
-};
-
-/**
- * Insert "thousands" separators in the provided number (which is actually
- * a string)
- *
- * @param {String} num
- * @returns {String}
- */
-function insert_thousand_seps (num) {
- var negative = num[0] === '-';
- num = (negative ? num.slice(1) : num);
- return (negative ? '-' : '') + intersperse(
- num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
-}
-
-function is_bin_size (v) {
- return (/^\d+(\.\d*)? \w+$/).test(v);
-}
-
-/**
- * Check with a scary heuristic if the value is a bin_size or not.
- * If not, compute an approximate size out of the base64 encoded string.
- *
- * @param {String} value original format
- */
-function binary_to_binsize (value) {
- if (!value) {
- return human_size(0);
- }
- if (value.substr(0, 10).indexOf(' ') == -1) {
- // Computing approximate size out of base64 encoded string
- // http://en.wikipedia.org/wiki/Base64#MIME
- return human_size(value.length / 1.37);
- } else {
- // already bin_size
- return value;
- }
-}
-
-/**
- * Returns a human readable size
- *
- * @param {Number} number of bytes
- */
-function human_size (size) {
- var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
- var i = 0;
- while (size >= 1024) {
- size /= 1024;
- ++i;
- }
- return size.toFixed(2) + ' ' + units[i];
-}
-
-/**
- * Naive and unlocalized human size parser.
- * Returns the numeric amount specified in the given human formatted string.
- *
- * Eg:
- *
- * parse_human_size('4 Kb') === 4096
- * parse_human_size('10m') === 10485760
- * parse_human_size('340.99') === 340.99
- *
- * @param {String} input human formatted string
- */
-function parse_human_size(size) {
- var units = "bkmgtpezy".split('');
- var parsed = size.toString().match(/^([0-9\.,]*)(\s*)?([a-z]{1,2})?$/i);
- if (parsed === null) {
- throw("Could not parse: " + size);
- }
- var amount = parseFloat(parsed[1].replace(',', '.'));
- if (isNaN(amount) || !isFinite(amount)) {
- throw("Invalid amount: " + size);
- }
- var unit = parsed[3] ? parsed[3][0].toLowerCase() : '';
- var index = units.indexOf(unit);
- if (unit && index === -1) {
- throw("Invalid unit: " + size);
- }
- if (index > 0) {
- amount = amount * Math.pow(1024, index);
- }
- return amount;
-}
-
-/**
- * Returns a human readable number
- *
- * @param {Number} number
- */
-function human_number (number, decimals) {
- var decimals = decimals | 0;
- var number = Math.round(number);
- var d2 = Math.pow(10, decimals);
- var val = _t("kMGTPE");
- var i = val.length-1, s;
- while( i ) {
- s = Math.pow(10,i--*3);
- if( s <= number ) {
- number = Math.round(number*d2/s)/d2 + val[i];
- }
- }
- return number;
-}
-
-/**
- * performs a half up rounding with arbitrary precision, correcting for float loss of precision
- * See the corresponding float_round() in server/tools/float_utils.py for more info
- * @param {Number} the value to be rounded
- * @param {Number} a precision parameter. eg: 0.01 rounds to two digits.
- */
-function round_precision (value, precision) {
- if (!value) {
- return 0;
- } else if (!precision || precision < 0) {
- precision = 1;
- }
- var normalized_value = value / precision;
- var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2);
- var epsilon = Math.pow(2, epsilon_magnitude - 53);
- normalized_value += normalized_value >= 0 ? epsilon : -epsilon;
-
- /**
- * Javascript performs strictly the round half up method, which is asymmetric. However, in
- * Python, the method is symmetric. For example:
- * - In JS, Math.round(-0.5) is equal to -0.
- * - In Python, round(-0.5) is equal to -1.
- * We want to keep the Python behavior for consistency.
- */
- var sign = normalized_value < 0 ? -1.0 : 1.0;
- var rounded_value = sign * Math.round(Math.abs(normalized_value));
- return rounded_value * precision;
-}
-
-/**
- * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
- * See the corresponding float_round() in server/tools/float_utils.py for more info
- * @param {Number} the value to be rounded
- * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
- */
-function round_decimals (value, decimals) {
- return round_precision(value, Math.pow(10,-decimals));
-}
-
-function float_is_zero (value, decimals) {
- var epsilon = Math.pow(10, -decimals);
- return Math.abs(round_precision(value, epsilon)) < epsilon;
-};
-
-/**
- * Confines a value inside an interval
- * @param {Number} [val] the value to confine
- * @param {Number} [min] the minimum of the interval
- * @param {Number} [max] the maximum of the interval
- * @return val if val is in [min, max], min if val < min and max otherwise
- */
-function confine (val, min, max) {
- return Math.max(min, Math.min(max, val));
-}
-
-function assert (bool) {
- if (!bool) {
- throw new Error("AssertionError");
- }
-}
-
-/* Logical XOR */
-function xor (a, b) {
- return (a && !b) || (!a && b);
-}
-
-var DropMisordered = Class.extend({
- /**
- * @constructs instance.web.DropMisordered
- * @extends instance.web.Class
- *
- * @param {Boolean} [failMisordered=false] whether mis-ordered responses should be failed or just ignored
- */
- init: function (failMisordered) {
- // local sequence number, for requests sent
- this.lsn = 0;
- // remote sequence number, seqnum of last received request
- this.rsn = -1;
- this.failMisordered = failMisordered || false;
- },
- /**
- * Adds a deferred (usually an async request) to the sequencer
- *
- * @param {$.Deferred} deferred to ensure add
- * @returns {$.Deferred}
- */
- add: function (deferred) {
- var res = $.Deferred();
-
- var self = this, seq = this.lsn++;
- deferred.done(function () {
- if (seq > self.rsn) {
- self.rsn = seq;
- res.resolve.apply(res, arguments);
- } else if (self.failMisordered) {
- res.reject();
- }
- }).fail(function () {
- res.reject.apply(res, arguments);
- });
-
- return res.promise();
- },
-});
-
-var DropPrevious = Class.extend({
- /**
- * Registers a new deferred and rejects the previous one
- * @param {$.Deferred} the new deferred
- * @returns {$.Promise}
- */
- add: function (deferred) {
- if (this.current_def) { this.current_def.reject(); }
- var res = $.Deferred();
- deferred.then(res.resolve, res.reject);
- this.current_def = res;
- return res.promise();
- }
-});
-
-/**
- * Rejects a deferred as soon as a reference deferred is either resolved or rejected
- * @param {$.Deferred} [target_def] the deferred to potentially reject
- * @param {$.Deferred} [reference_def] the reference target
- * @returns {$.Deferred}
- */
-function reject_after(target_def, reference_def) {
- var res = $.Deferred();
- target_def.then(res.resolve, res.reject);
- reference_def.always(res.reject);
- return res.promise();
-}
-
-/**
- * Returns a deferred resolved after 'wait' milliseconds
- * @param {int} [wait] the delay in ms
- * @return {$.Deferred}
- */
-function delay (wait) {
- var def = $.Deferred();
- setTimeout(def.resolve, wait);
- return def;
-}
-
-function swap(array, elem1, elem2) {
- var i1 = array.indexOf(elem1);
- var i2 = array.indexOf(elem2);
- array[i2] = elem1;
- array[i1] = elem2;
-}
-
-function toBoolElse (str, elseValues, trueValues, falseValues) {
- var ret = _.str.toBool(str, trueValues, falseValues);
- if (_.isUndefined(ret)) {
- return elseValues;
- }
- return ret;
-}
-
-function async_when() {
- var async = false;
- var def = $.Deferred();
- $.when.apply($, arguments).done(function() {
- var args = arguments;
- var action = function() {
- def.resolve.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- }).fail(function() {
- var args = arguments;
- var action = function() {
- def.reject.apply(def, args);
- };
- if (async)
- action();
- else
- setTimeout(action, 0);
- });
- async = true;
- return def;
-}
-
-return {
- divmod: divmod,
- modf: modf,
- lpad: lpad,
- rpad: rpad,
- generate_id: generate_id,
- intersperse: intersperse,
- xml_to_json: xml_to_json,
- json_node_to_xml: json_node_to_xml,
- xml_to_str: xml_to_str,
- Mutex: Mutex,
- get_cookie: get_cookie,
- set_cookie: set_cookie,
- insert_thousand_seps: insert_thousand_seps,
- is_bin_size: is_bin_size,
- binary_to_binsize: binary_to_binsize,
- human_size: human_size,
- parse_human_size: parse_human_size,
- human_number: human_number,
- round_precision: round_precision,
- round_decimals: round_decimals,
- float_is_zero: float_is_zero,
- confine: confine,
- assert: assert,
- xor: xor,
- DropMisordered: DropMisordered,
- DropPrevious: DropPrevious,
- reject_after: reject_after,
- delay: delay,
- swap: swap,
- toBoolElse: toBoolElse,
- async_when: async_when,
-};
-
-});
diff --git a/addons/web/static/src/js/framework/view.js b/addons/web/static/src/js/framework/view.js
deleted file mode 100644
index c5a017710e9..00000000000
--- a/addons/web/static/src/js/framework/view.js
+++ /dev/null
@@ -1,211 +0,0 @@
-odoo.define('web.View', function (require) {
-"use strict";
-
-var core = require('web.core');
-var data = require('web.data');
-var data_manager = require('web.data_manager');
-var pyeval = require('web.pyeval');
-var Widget = require('web.Widget');
-
-var View = Widget.extend({
- events: {
- 'click a[type=action]': function (ev) {
- ev.preventDefault();
- var action_data = $(ev.target).attr('name');
- this.do_action(action_data);
- }
- },
- defaults: {
- action: {},
- },
- // name displayed in view switchers
- display_name: '',
- // used by views that need a searchview.
- searchable: true,
- // used by views that need a searchview but don't want it to be displayed.
- searchview_hidden: false,
- // multi_record is used to distinguish views displaying a single record
- // (e.g. FormView) from those that display several records (e.g. ListView)
- multi_record: true,
- // indicates whether or not the view is mobile-friendly
- mobile_friendly: false,
- // icon is the font-awesome icon to display in the view switcher
- icon: 'fa-question',
- // whether or not the view requires the model's fields to render itself
- require_fields: false,
- init: function(parent, dataset, fields_view, options) {
- this._super(parent);
- this.ViewManager = parent;
- this.dataset = dataset;
- this.model = dataset.model;
- this.fields_view = fields_view;
- this.options = _.defaults({}, options, this.defaults);
- },
- /**
- * Triggers event 'view_loaded'.
- * Views extending start() must call this.super() once they are ready.
- * @return {Deferred}
- */
- start: function() {
- // add classname that reflect the (absence of) access rights
- this.$el.toggleClass('o_cannot_create', !this.is_action_enabled('create'));
- return this._super().then(this.trigger.bind(this, 'view_loaded'));
- },
- /**
- * Fetches and executes the action identified by ``action_data``.
- *
- * @param {Object} action_data the action descriptor data
- * @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
- * @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
- * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
- * @param {Object} [action_data.context=null] additional action context, to add to the current context
- * @param {DataSet} dataset a dataset object used to communicate with the server
- * @param {Object} [record_id] the identifier of the object on which the action is to be applied
- * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
- */
- do_execute_action: function (action_data, dataset, record_id, on_closed) {
- var self = this;
- var result_handler = function () {
- if (on_closed) { on_closed.apply(null, arguments); }
- if (self.getParent() && self.getParent().on_action_executed) {
- return self.getParent().on_action_executed.apply(null, arguments);
- }
- };
- var context = new data.CompoundContext(dataset.get_context(), action_data.context || {});
-
- // response handler
- var handler = function (action) {
- if (action && action.constructor === Object) {
- // filter out context keys that are specific to the current action.
- // Wrong default_* and search_default_* values will no give the expected result
- // Wrong group_by values will simply fail and forbid rendering of the destination view
- var ncontext = new data.CompoundContext(
- _.object(_.reject(_.pairs(dataset.get_context().eval()), function(pair) {
- return pair[0].match('^(?:(?:default_|search_default_|show_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
- }))
- );
- ncontext.add(action_data.context || {});
- ncontext.add({active_model: dataset.model});
- if (record_id) {
- ncontext.add({
- active_id: record_id,
- active_ids: [record_id],
- });
- }
- ncontext.add(action.context || {});
- action.context = ncontext;
- return self.do_action(action, {
- on_close: result_handler,
- });
- } else {
- self.do_action({"type":"ir.actions.act_window_close"});
- return result_handler();
- }
- };
-
- if (action_data.special === 'cancel') {
- return handler({"type":"ir.actions.act_window_close"});
- } else if (action_data.type === "object") {
- var args = record_id ? [[record_id]] : [dataset.ids];
- if (action_data.args) {
- try {
- // Warning: quotes and double quotes problem due to json and xml clash
- // Maybe we should force escaping in xml or do a better parse of the args array
- var additional_args = JSON.parse(action_data.args.replace(/'/g, '"'));
- args = args.concat(additional_args);
- } catch(e) {
- console.error("Could not JSON.parse arguments", action_data.args);
- }
- }
- args.push(context);
- return dataset.call_button(action_data.name, args).then(handler);
- } else if (action_data.type === "action") {
- return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), {
- active_model: dataset.model,
- active_ids: dataset.ids,
- active_id: record_id
- })).then(handler);
- }
- },
- do_show: function () {
- this._super();
- core.bus.trigger('view_shown', this);
- },
- do_push_state: function(state) {
- if (this.getParent() && this.getParent().do_push_state) {
- this.getParent().do_push_state(state);
- }
- },
- do_load_state: function (state, warm) {
- },
- /**
- * This function should render the action buttons of the view.
- * It should be called after start().
- * @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
- * $node may be undefined, in which case the View can insert the buttons somewhere else
- */
- render_buttons: function($node) {
- },
- /**
- * This function should render the sidebar of the view.
- * It should be called after start().
- * @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
- * $node may be undefined, in which case the View can insert the sidebar somewhere else
- */
- render_sidebar: function($node) {
- },
- /**
- * This function should render the pager of the view.
- * It should be called after start().
- * @param {jQuery} [$node] a jQuery node where the pager should be inserted
- * $node may be undefined, in which case the View can insert the pager somewhere else
- */
- render_pager: function($node) {
- },
- /**
- * Switches to a specific view type
- */
- do_switch_view: function() {
- this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments)));
- },
- do_search: function(domain, context, group_by) {
- },
- sidebar_eval_context: function () {
- return $.when({});
- },
- /**
- * Asks the view to reload itself, if the reloading is asynchronous should
- * return a {$.Deferred} indicating when the reloading is done.
- */
- reload: function () {
- return $.when();
- },
- /**
- * Return whether the user can perform the action ('create', 'edit', 'delete') in this view.
- * An action is disabled by setting the corresponding attribute in the view's main element,
- * like: <form string="" create="false" edit="false" delete="false">
- */
- is_action_enabled: function(action) {
- var attrs = this.fields_view.arch.attrs;
- return (action in attrs) ? JSON.parse(attrs[action]) : true;
- },
- get_context: function () {
- return {};
- },
- destroy: function () {
- if (this.$buttons) {
- this.$buttons.off();
- }
- return this._super.apply(this, arguments);
- },
- set_scrollTop: function(scrollTop) {
- this.scrollTop = scrollTop;
- },
- get_scrollTop: function() {
- return this.scrollTop;
- }
-});
-
-return View;
-
-});
diff --git a/addons/web/static/src/js/framework/widget.js b/addons/web/static/src/js/framework/widget.js
deleted file mode 100644
index 25a30ae6511..00000000000
--- a/addons/web/static/src/js/framework/widget.js
+++ /dev/null
@@ -1,426 +0,0 @@
-odoo.define('web.Widget', function (require) {
-"use strict";
-
-var core = require('web.core');
-var session = require('web.session');
-
-var qweb = core.qweb;
-var mixins = core.mixins;
-
-/**
- * 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.
- *
- * 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",
- *
- * 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.$el` holds a correct value
- * this.$(".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.destroy();
- *
- * That will kill the widget in a clean way and erase its content from the dom.
- */
-
-
-var Widget = core.Class.extend(mixins.PropertiesMixin, {
- // Backbone-ish API
- tagName: 'div',
- id: null,
- className: null,
- attributes: {},
- events: {},
- custom_events: {},
- /**
- * 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.
- *
- * @type string
- */
- template: null,
- /**
- * Constructs the widget and sets its parent if a parent is given.
- *
- * @constructs openerp.Widget
- *
- * @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) {
- mixins.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] = _.bind(this[name], this);
- }
- }
- }
- // FIXME: this should not be
- this.setElement(this._make_descriptive());
- this.delegateCustomEvents();
- },
- /**
- * Method called between init and start. Performs asynchronous calls required by start.
- *
- * This method should return a Deferred which is resolved when start can be executed.
- *
- * @return {jQuery.Deferred}
- */
- willStart: function() {
- return $.when();
- },
- /**
- * Destroys the current widget, also destroys all its children before destroying itself.
- */
- destroy: function() {
- _.each(this.getChildren(), function(el) {
- el.destroy();
- });
- if(this.$el) {
- this.$el.remove();
- }
- mixins.PropertiesMixin.destroy.call(this);
- },
- /**
- * Renders the current widget and appends it to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- appendTo: function(target) {
- var self = this;
- return this.__widgetRenderAndInsert(function(t) {
- self.$el.appendTo(t);
- }, target);
- },
- /**
- * Renders the current widget and prepends it to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- prependTo: function(target) {
- var self = this;
- return this.__widgetRenderAndInsert(function(t) {
- self.$el.prependTo(t);
- }, target);
- },
- /**
- * Renders the current widget and inserts it after to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- insertAfter: function(target) {
- var self = this;
- return this.__widgetRenderAndInsert(function(t) {
- self.$el.insertAfter(t);
- }, target);
- },
- /**
- * Renders the current widget and inserts it before to the given jQuery object or Widget.
- *
- * @param target A jQuery object or a Widget instance.
- */
- insertBefore: function(target) {
- var self = this;
- return this.__widgetRenderAndInsert(function(t) {
- self.$el.insertBefore(t);
- }, target);
- },
- /**
- * Attach the current widget to a dom element
- *
- * @param target A jQuery object or a Widget instance.
- */
- attachTo: function(target) {
- var self = this;
- this.setElement(target.$el || target);
- return this.willStart().then(function() {
- return self.start();
- });
- },
- /**
- * Renders the current widget and replaces the given jQuery object.
- *
- * @param target A jQuery object or a Widget instance.
- */
- replace: function(target) {
- return this.__widgetRenderAndInsert(_.bind(function(t) {
- this.$el.replaceAll(t);
- }, this), target);
- },
- __widgetRenderAndInsert: function(insertion, target) {
- var self = this;
- return this.willStart().then(function() {
- self.renderElement();
- insertion(target);
- return self.start();
- });
- },
- /**
- * Method called after rendering. Mostly used to bind actions, perform asynchronous
- * calls, etc...
- *
- * By convention, this method should return an object that can be passed to $.when()
- * to inform the caller when this widget has been initialized.
- *
- * @returns {jQuery.Deferred or any}
- */
- start: function() {
- return $.when();
- },
- /**
- * Renders the element. The default implementation renders the widget using QWeb,
- * `this.template` must be defined. The context given to QWeb contains the "widget"
- * key that references `this`.
- */
- renderElement: function() {
- var $el;
- if (this.template) {
- $el = $(qweb.render(this.template, {widget: this}).trim());
- } else {
- $el = this._make_descriptive();
- }
- this.replaceElement($el);
- },
- /**
- * Re-sets the widget's root element and replaces the old root element
- * (if any) by the new one in the DOM.
- *
- * @param {HTMLElement | jQuery} $el
- * @returns {*} this
- */
- replaceElement: function ($el) {
- var $oldel = this.$el;
- this.setElement($el);
- if ($oldel && !$oldel.is(this.$el)) {
- if ($oldel.length > 1) {
- $oldel.wrapAll('<div/>');
- $oldel.parent().replaceWith(this.$el);
- } else {
- $oldel.replaceWith(this.$el);
- }
- }
- return this;
- },
- /**
- * Re-sets the widget's root element (el/$el/$el).
- *
- * Includes:
- * * re-delegating events
- * * re-binding sub-elements
- * * if the widget already had a root element, replacing the pre-existing
- * element in the DOM
- *
- * @param {HTMLElement | jQuery} element new root element for the widget
- * @return {*} this
- */
- setElement: function (element) {
- // NB: completely useless, as WidgetMixin#init creates a $el
- // always
- if (this.$el) {
- this.undelegateEvents();
- }
-
- this.$el = (element instanceof $) ? element : $(element);
- this.el = this.$el[0];
-
- this.delegateEvents();
-
- return this;
- },
- /**
- * Utility function to build small DOM elements.
- *
- * @param {String} tagName name of the DOM element to create
- * @param {Object} [attributes] map of DOM attributes to set on the element
- * @param {String} [content] HTML content to set on the element
- * @return {Element}
- */
- make: function (tagName, attributes, content) {
- var el = document.createElement(tagName);
- if (!_.isEmpty(attributes)) {
- $(el).attr(attributes);
- }
- if (content) {
- $(el).html(content);
- }
- return el;
- },
- /**
- * Makes a potential root element from the declarative builder of the
- * widget
- *
- * @return {jQuery}
- * @private
- */
- _make_descriptive: function () {
- var attrs = _.extend({}, this.attributes || {});
- if (this.id) { attrs.id = this.id; }
- if (this.className) { attrs['class'] = this.className; }
- return $(this.make(this.tagName, attrs));
- },
- delegateEvents: function () {
- var events = this.events;
- if (_.isEmpty(events)) { return; }
-
- for(var key in events) {
- if (!events.hasOwnProperty(key)) { continue; }
-
- var method = this.proxy(events[key]);
-
- var match = /^(\S+)(\s+(.*))?$/.exec(key);
- var event = match[1];
- var selector = match[3];
-
- event += '.widget_events';
- if (!selector) {
- this.$el.on(event, method);
- } else {
- this.$el.on(event, selector, method);
- }
- }
- },
- delegateCustomEvents: function () {
- if (_.isEmpty(this.custom_events)) { return; }
- for (var key in this.custom_events) {
- if (!this.custom_events.hasOwnProperty(key)) { continue; }
-
- var method = this.proxy(this.custom_events[key]);
- this.on(key, this, wrap_handler(method));
- }
- function wrap_handler(callback) {
- return function (event) {
- event.stop_propagation();
- callback(event);
- };
- }
- },
- undelegateEvents: function () {
- this.$el.off('.widget_events');
- },
- /**
- * Shortcut for ``this.$el.find(selector)``
- *
- * @param {String} selector CSS selector, rooted in $el
- * @returns {jQuery} selector match
- */
- $: function(selector) {
- if (selector === undefined)
- return this.$el;
- return this.$el.find(selector);
- },
- /**
- * Displays the widget
- */
- do_show: function () {
- this.$el.removeClass('o_hidden');
- },
- /**
- * Hides the widget
- */
- do_hide: function () {
- this.$el.addClass('o_hidden');
- },
- /**
- * Displays or hides the widget
- * @param {Boolean} [display] use true to show the widget or false to hide it
- */
- do_toggle: function (display) {
- if (_.isBoolean(display)) {
- display ? this.do_show() : this.do_hide();
- } else {
- this.$el.hasClass('o_hidden') ? this.do_show() : this.do_hide();
- }
- },
- /**
- * 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|Function} method function or name of the method to invoke
- * @returns {Function} proxied method
- */
- proxy: function (method) {
- var self = this;
- return function () {
- var fn = (typeof method === 'string') ? self[method] : method;
- if (fn === void 0) {
- throw new Error("Couldn't find method '" + method + "' in widget " + self);
- }
- return fn.apply(self, arguments);
- };
- },
- /**
- * 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(title, message, sticky) {
- this.trigger_up('notification', {title: title, message: message, sticky: sticky});
- },
- do_warn: function(title, message, sticky) {
- this.trigger_up('warning', {title: title, message: message, sticky: sticky});
- },
- rpc: function(url, data, options) {
- return this.alive(session.rpc(url, data, options));
- },
-});
-
-return Widget;
-
-});