diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 6dcc069fcc2..5b2c7a5e91a 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -497,50 +497,6 @@ def content_disposition(filename):
#----------------------------------------------------------
# OpenERP Web web Controllers
#----------------------------------------------------------
-
-# TODO: to remove once the database manager has been migrated server side
-# and `edi` + `pos` addons has been adapted to use render_bootstrap_template()
-html_template = """
-
-
-
height: 200px; border: 1px solid red;
-
+
* :
diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js
index 0289af91a60..78e4fd8b544 100644
--- a/addons/web/static/lib/qweb/qweb2.js
+++ b/addons/web/static/lib/qweb/qweb2.js
@@ -37,6 +37,7 @@ var QWeb2 = {
'lt': '<',
'lte': '<='
},
+ VOID_ELEMENTS: 'area,base,br,col,embed,hr,img,input,keygen,link,menuitem,meta,param,source,track,wbr'.split(','),
tools: {
exception: function(message, context) {
context = context || {};
@@ -218,6 +219,7 @@ QWeb2.Engine = (function() {
this.jQuery = window.jQuery;
this.reserved_words = QWeb2.RESERVED_WORDS.slice(0);
this.actions_precedence = QWeb2.ACTIONS_PRECEDENCE.slice(0);
+ this.void_elements = QWeb2.VOID_ELEMENTS.slice(0);
this.word_replacement = QWeb2.tools.extend({}, QWeb2.WORD_REPLACEMENT);
this.preprocess_node = null;
for (var i = 0; i < arguments.length; i++) {
@@ -480,6 +482,7 @@ QWeb2.Element = (function() {
this._bottom = [];
this._indent = 1;
this.process_children = true;
+ this.is_void_element = ~QWeb2.tools.arrayIndexOf(this.engine.void_elements, this.tag);
var childs = this.node.childNodes;
if (childs) {
for (var i = 0, ilen = childs.length; i < ilen; i++) {
@@ -677,11 +680,13 @@ QWeb2.Element = (function() {
this.top("r.push(context.engine.tools.gen_attribute(['" + m[1] + "', (" + (this.string_interpolation(v)) + ")]));");
}
}
- if (this.children.length || this.actions.opentag === 'true') {
+ if (this.actions.opentag === 'true' || (!this.children.length && this.is_void_element)) {
+ // We do not enforce empty content on void elements
+ // because QWeb rendering is not necessarily html.
+ this.top_string("/>");
+ } else {
this.top_string(">");
this.bottom_string("" + this.tag + ">");
- } else {
- this.top_string("/>");
}
}
},
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index c64da7e5810..29f6c3e4d9e 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -345,31 +345,16 @@ instance.web.Session.include( /** @lends instance.web.Session# */{
load_css: function (files) {
var self = this;
_.each(files, function (file) {
- $('head').append($('
', {
- 'href': self.url(file, null),
- 'rel': 'stylesheet',
- 'type': 'text/css'
- }));
+ openerp.loadCSS(self.url(file, null));
});
},
load_js: function(files) {
var self = this;
var d = $.Deferred();
- if(files.length !== 0) {
+ if (files.length !== 0) {
var file = files.shift();
- var tag = document.createElement('script');
- tag.type = 'text/javascript';
- tag.src = self.url(file, null);
- tag.onload = tag.onreadystatechange = function() {
- if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
- return;
- tag.onload_done = true;
- self.load_js(files).done(function () {
- d.resolve();
- });
- };
- var head = document.head || document.getElementsByTagName('head')[0];
- head.appendChild(tag);
+ var url = self.url(file, null);
+ openerp.loadJS(url).done(d.resolve);
} else {
d.resolve();
}
diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js
index 5dcc2197944..f190fefeba3 100644
--- a/addons/web/static/src/js/openerpframework.js
+++ b/addons/web/static/src/js/openerpframework.js
@@ -933,6 +933,46 @@ openerp.jsonpRpc = function(url, fct_name, params, settings) {
});
};
+openerp.loadCSS = function (url) {
+ if (!$('link[href="' + url + '"]').length) {
+ $('head').append($('
', {
+ 'href': url,
+ 'rel': 'stylesheet',
+ 'type': 'text/css'
+ }));
+ }
+};
+openerp.loadJS = function (url) {
+ var def = $.Deferred();
+ if ($('script[src="' + url + '"]').length) {
+ def.resolve();
+ } else {
+ 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;
+};
+openerp.loadBundle = function (name) {
+ return $.when(
+ openerp.loadCSS('/web/css/' + name),
+ openerp.loadJS('/web/js/' + name)
+ );
+};
+
var realSetTimeout = function(fct, millis) {
var finished = new Date().getTime() + millis;
var wait = function() {
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index b9015f6b311..753d52d497a 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -451,6 +451,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_client: function (action, options) {
var self = this;
var ClientWidget = instance.web.client_actions.get_object(action.tag);
+ if (!ClientWidget) {
+ return self.do_warn("Action Error", "Could not find client action '" + action.tag + "'.");
+ }
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
var next;
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 18a55656df2..75d4bdbb1c6 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -496,7 +496,7 @@
-
+