summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/framework
diff options
context:
space:
mode:
authorAaron Bohy <aab@odoo.com>2016-04-21 13:58:22 +0200
committerGéry Debongnie <ged@odoo.com>2016-04-21 15:21:42 +0200
commit6768bbfcc02e0f498110cda2ba9adbc2fd23d24c (patch)
tree7dccb1019434582874b285276189b1b3dcbb96a9 /addons/web/static/src/js/framework
parente1bfd3fbda97ac7a64a585b07be161d5be606cf9 (diff)
parent8e440b173e3b273b3aac541f2ec12527f67dfa60 (diff)
[MERGE] *: stop duplicating web
Avoid duplicating web addon in enterprise by extracting a common basis. Enterprise features stay in enterprise, but use that common basis. In web, mainly: - JS refactoring and linting - Conversion of .sass into .less split into multiple files - Templates cleaning and DOM simplification - Re-generation of web.pot, and update of .po files Minor tweaks in some other addons to make them work with this new structure.
Diffstat (limited to 'addons/web/static/src/js/framework')
-rw-r--r--addons/web/static/src/js/framework/ajax.js17
-rw-r--r--addons/web/static/src/js/framework/core.js16
-rw-r--r--addons/web/static/src/js/framework/crash_manager.js17
-rw-r--r--addons/web/static/src/js/framework/data.js12
-rw-r--r--addons/web/static/src/js/framework/misc.js64
-rw-r--r--addons/web/static/src/js/framework/model.js6
-rw-r--r--addons/web/static/src/js/framework/session.js2
-rw-r--r--addons/web/static/src/js/framework/utils.js1
-rw-r--r--addons/web/static/src/js/framework/view.js36
-rw-r--r--addons/web/static/src/js/framework/widget.js2
10 files changed, 101 insertions, 72 deletions
diff --git a/addons/web/static/src/js/framework/ajax.js b/addons/web/static/src/js/framework/ajax.js
index 869e0214ad9..0301cd4e96c 100644
--- a/addons/web/static/src/js/framework/ajax.js
+++ b/addons/web/static/src/js/framework/ajax.js
@@ -2,8 +2,8 @@ odoo.define('web.ajax', function (require) {
"use strict";
var core = require('web.core');
-var time = require('web.time');
var utils = require('web.utils');
+var time = require('web.time');
function genericJsonRpc (fct_name, params, fct) {
var data = {
@@ -14,6 +14,7 @@ function genericJsonRpc (fct_name, params, fct) {
};
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));
@@ -42,7 +43,7 @@ function jsonRpc(url, fct_name, params, settings) {
contentType: 'application/json'
}));
});
-};
+}
function jsonpRpc(url, fct_name, params, settings) {
settings = settings || {};
@@ -116,7 +117,7 @@ function jsonpRpc(url, fct_name, params, settings) {
return deferred;
}
});
-};
+}
// helper
function realSetTimeout (fct, millis) {
@@ -140,7 +141,7 @@ function loadCSS(url) {
'type': 'text/css'
}));
}
-};
+}
function loadJS(url) {
var def = $.Deferred();
@@ -165,7 +166,7 @@ function loadJS(url) {
head.appendChild(script);
}
return def;
-};
+}
/**
* Cooperative file download implementation, for ajaxy APIs.
@@ -301,7 +302,7 @@ function post (controller_url, data) {
var Def = $.Deferred();
var postData = new FormData();
-
+
$.each(data, function(i,val) {
postData.append(i, val);
});
@@ -311,7 +312,7 @@ function post (controller_url, data) {
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,
@@ -324,8 +325,6 @@ function post (controller_url, data) {
return Def;
}
-
-
var loadXML = (function () {
var loading = false;
var urls = [];
diff --git a/addons/web/static/src/js/framework/core.js b/addons/web/static/src/js/framework/core.js
index 30138102663..f9ab78f1c8e 100644
--- a/addons/web/static/src/js/framework/core.js
+++ b/addons/web/static/src/js/framework/core.js
@@ -177,20 +177,16 @@ return {
_lt: _lt,
// registries
- view_registry: new Registry(),
- crash_registry: new Registry(),
action_registry : new Registry(),
- form_widget_registry: new Registry(),
- form_tag_registry: new Registry(),
+ crash_registry: new Registry(),
form_custom_registry: new Registry(),
+ form_tag_registry: new Registry(),
+ form_widget_registry: new Registry(),
list_widget_registry: new Registry(),
- search_widgets_registry: new Registry(),
- search_filters_registry: new Registry(),
-
- // necessary to make the kanban view compatible between
- // community and enterprise edition
one2many_view_registry: new Registry(),
-
+ search_filters_registry: new Registry(),
+ search_widgets_registry: new Registry(),
+ view_registry: new Registry(),
};
diff --git a/addons/web/static/src/js/framework/crash_manager.js b/addons/web/static/src/js/framework/crash_manager.js
index 4ab40f456bc..99d949ac311 100644
--- a/addons/web/static/src/js/framework/crash_manager.js
+++ b/addons/web/static/src/js/framework/crash_manager.js
@@ -40,22 +40,17 @@ var CrashManager = core.Class.extend({
if (!this.active) {
return;
}
- if (this.$indicator){
+ if (this.connection_lost) {
return;
}
if (error.code == -32098) {
- $.blockUI({ message: '' , overlayCSS: {'z-index': 9999, backgroundColor: '#FFFFFF', opacity: 0.0, cursor: 'wait'}});
- this.$indicator = $('<div class="oe_indicator">' + _t("Trying to reconnect... ") + '<i class="fa fa-refresh"></i></div>');
- this.$indicator.prependTo("body");
- var timeinterval = setInterval(function(){
+ core.bus.trigger('connection_lost');
+ this.connection_lost = true;
+ var timeinterval = setInterval(function() {
ajax.jsonRpc('/web/webclient/version_info').then(function() {
clearInterval(timeinterval);
- self.$indicator.html(_t("You are back online"));
- self.$indicator.delay(2000).fadeOut('slow',function(){
- $(this).remove();
- self.$indicator.remove();
- });
- $.unblockUI();
+ core.bus.trigger('connection_restored');
+ self.connection_lost = false;
});
}, 2000);
return;
diff --git a/addons/web/static/src/js/framework/data.js b/addons/web/static/src/js/framework/data.js
index a1b8e1602e9..592e402d927 100644
--- a/addons/web/static/src/js/framework/data.js
+++ b/addons/web/static/src/js/framework/data.js
@@ -320,6 +320,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
this.index = null;
this._sort = [];
this._model = new Model(model, context);
+ this.orderer = new utils.DropMisordered();
},
previous: function () {
this.index -= 1;
@@ -408,10 +409,11 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
read_slice: function (fields, options) {
var self = this;
options = options || {};
- return this._model.query(fields)
+ var query = this._model.query(fields)
.limit(options.limit || false)
.offset(options.offset || 0)
- .all().done(function (records) {
+ .all();
+ return this.orderer.add(query).done(function (records) {
self.ids = _(records).pluck('id');
});
},
@@ -761,12 +763,6 @@ var BufferedDataSet = DataSetStatic.extend({
_.extend(cached.from_read, options.from_read);
_.extend(cached.changes, options.changes);
_.extend(cached.readonly_fields, options.readonly_fields);
- // discard values from cached.changes that are in cached.from_read
- _.each(cached.changes, function (v, k) {
- if (cached.from_read[k] === v) {
- delete cached.changes[k];
- }
- });
if (options.to_create !== undefined) cached.to_create = options.to_create;
if (options.to_delete !== undefined) cached.to_delete = options.to_delete;
}
diff --git a/addons/web/static/src/js/framework/misc.js b/addons/web/static/src/js/framework/misc.js
index b8c5e5e927e..73345df94c0 100644
--- a/addons/web/static/src/js/framework/misc.js
+++ b/addons/web/static/src/js/framework/misc.js
@@ -211,6 +211,16 @@ if ('nv' in window) {
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
@@ -303,11 +313,18 @@ $.extend( proto, {
});
/**
- * Private function that triggers an event on core.bus
+ * 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) {
+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);
}
/**
@@ -315,12 +332,14 @@ function _notify (content) {
* @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 {jQuery} [trigger] true to trigger an event, false otherwise
+ * @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, trigger) {
+function append ($target, content, options) {
$target.append(content);
- if (trigger) {
- _notify(content);
+ if (options && options.in_DOM) {
+ _notify(content, options.callbacks);
}
}
/**
@@ -328,13 +347,37 @@ function append ($target, content, trigger) {
* @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 {jQuery} [trigger] true to trigger an event, false otherwise
+ * @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, trigger) {
+function prepend ($target, content, options) {
$target.prepend(content);
- if (trigger) {
- _notify(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();
}
/**
@@ -358,6 +401,7 @@ return {
redirect: redirect,
append: append,
prepend: prepend,
+ detach: detach,
getPosition: getPosition,
};
diff --git a/addons/web/static/src/js/framework/model.js b/addons/web/static/src/js/framework/model.js
index fc1422d799a..dd516bb61ba 100644
--- a/addons/web/static/src/js/framework/model.js
+++ b/addons/web/static/src/js/framework/model.js
@@ -6,14 +6,12 @@ var session = require('web.session');
var Model = Class.extend({
/**
- new openerp.web.Model([session,] model_name[, context[, domain]])
+ new openerp.web.Model(name[, context[, domain]])
@constructs instance.web.Model
@extends instance.web.Class
- @param {openerp.web.Session} [session] The session object used to communicate with
- the server.
- @param {String} model_name name of the OpenERP model this object is bound to
+ @param {String} name name of the OpenERP model this object is bound to
@param {Object} [context]
@param {Array} [domain]
*/
diff --git a/addons/web/static/src/js/framework/session.js b/addons/web/static/src/js/framework/session.js
index 71819cc6b7a..8a9269b8dff 100644
--- a/addons/web/static/src/js/framework/session.js
+++ b/addons/web/static/src/js/framework/session.js
@@ -236,7 +236,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
return d;
},
load_qweb: function(mods) {
- this.qweb_mutex.exec(function() {
+ this.qweb_mutex.exec(function () {
return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) {
if (!doc) { return; }
qweb.add_template(doc);
diff --git a/addons/web/static/src/js/framework/utils.js b/addons/web/static/src/js/framework/utils.js
index d50647090d6..8d8d8dab6ee 100644
--- a/addons/web/static/src/js/framework/utils.js
+++ b/addons/web/static/src/js/framework/utils.js
@@ -423,7 +423,6 @@ function swap(array, elem1, elem2) {
array[i1] = elem2;
}
-
return {
divmod: divmod,
modf: modf,
diff --git a/addons/web/static/src/js/framework/view.js b/addons/web/static/src/js/framework/view.js
index fbbede916e5..d2cc73a4b71 100644
--- a/addons/web/static/src/js/framework/view.js
+++ b/addons/web/static/src/js/framework/view.js
@@ -27,6 +27,8 @@ var View = Widget.extend({
// 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
@@ -45,11 +47,8 @@ var View = Widget.extend({
* @return {Deferred}
*/
start: function() {
- // add css classes that reflect the (absence of) access rights
- this.$el.addClass('oe_view')
- .toggleClass('oe_cannot_create', !this.is_action_enabled('create'))
- .toggleClass('oe_cannot_edit', !this.is_action_enabled('edit'))
- .toggleClass('oe_cannot_delete', !this.is_action_enabled('delete'));
+ // 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'));
},
/**
@@ -76,7 +75,7 @@ var View = Widget.extend({
// response handler
var handler = function (action) {
- if (action && action.constructor == Object) {
+ 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
@@ -106,7 +105,7 @@ var View = Widget.extend({
if (action_data.special === 'cancel') {
return handler({"type":"ir.actions.act_window_close"});
- } else if (action_data.type=="object") {
+ } else if (action_data.type === "object") {
var args = [[record_id]];
if (action_data.args) {
try {
@@ -122,7 +121,7 @@ var View = Widget.extend({
return dataset.call_button(action_data.name, args).then(handler).then(function () {
core.bus.trigger('do_reload_needaction');
});
- } else if (action_data.type=="action") {
+ } 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,
@@ -144,27 +143,24 @@ var View = Widget.extend({
do_load_state: function (state, warm) {
},
/**
- * This function should render the buttons of the view, set this.$buttons to
- * the produced jQuery element and define some listeners on it.
- * This function should be called after start().
+ * 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 instantiate and render the sidebar of the view, set this.sidebar to
- * the instantiated Sidebar Widget and possibly add custom items in it.
- * This function should be called after start().
+ * 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, set this.$pager to
- * the produced jQuery element and define some listeners on it.
- * This function should be called after start().
+ * 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
*/
@@ -200,6 +196,12 @@ var View = Widget.extend({
get_context: function () {
return {};
},
+ 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
index a6b189dc832..25a30ae6511 100644
--- a/addons/web/static/src/js/framework/widget.js
+++ b/addons/web/static/src/js/framework/widget.js
@@ -32,7 +32,7 @@ var mixins = core.mixins;
* },
* start: function() {
* // stuff you want to make after the rendering, `this.$el` holds a correct value
- * this.$el.find(".my_button").click(/* an example of event binding * /);
+ * 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()