diff options
| author | Aaron Bohy <aab@odoo.com> | 2016-04-21 13:58:22 +0200 |
|---|---|---|
| committer | Géry Debongnie <ged@odoo.com> | 2016-04-21 15:21:42 +0200 |
| commit | 6768bbfcc02e0f498110cda2ba9adbc2fd23d24c (patch) | |
| tree | 7dccb1019434582874b285276189b1b3dcbb96a9 /addons/web/static/src/js/framework | |
| parent | e1bfd3fbda97ac7a64a585b07be161d5be606cf9 (diff) | |
| parent | 8e440b173e3b273b3aac541f2ec12527f67dfa60 (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.js | 17 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/core.js | 16 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/crash_manager.js | 17 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/data.js | 12 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/misc.js | 64 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/model.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/session.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/utils.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/view.js | 36 | ||||
| -rw-r--r-- | addons/web/static/src/js/framework/widget.js | 2 |
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() |
