[MERGE] Sync with trunk

bzr revid: tde@openerp.com-20130725135118-urwxu1ilv1m6ljwh
This commit is contained in:
Thibault Delavallée 2013-07-25 15:51:18 +02:00
commit 7f0824e762
29 changed files with 554 additions and 358 deletions

View File

@ -1,21 +1,15 @@
.*.swp .*
.bzrignore *.egg-info
.idea *.orig
.project *.vim
.pydevproject
.ropeproject
.settings
.DS_Store
openerp/addons/*
openerp/filestore*
.Python
*.pyc
*.pyo
bin/*
build/ build/
include/ RE:^bin/
lib/ RE:^dist/
share/ RE:^include/
doc/_build/*
win32/*.bat RE:^share/
win32/meta.py RE:^man/
RE:^lib/
RE:^addons/\w+/doc/_build/
RE:^.*?/node_modules

20
addons/web/Gruntfile.js Normal file
View File

@ -0,0 +1,20 @@
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
src: ['static/src/**/*.js', 'static/test/**/*.js'],
options: {
sub: true, //[] instead of .
evil: true, //eval
laxbreak: true, //unsafe line breaks
},
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('test', ['jshint']);
grunt.registerTask('default', ['jshint']);
};

View File

@ -88,9 +88,6 @@ def rjsmin(script):
db_list = http.db_list db_list = http.db_list
def db_monodb_redirect():
return http.db_redirect(not config['list_db'])
db_monodb = http.db_monodb db_monodb = http.db_monodb
def redirect_with_hash(url, code=303): def redirect_with_hash(url, code=303):
@ -296,13 +293,13 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False):
r.append((path, fs2web(path[len(addons_path):]))) r.append((path, fs2web(path[len(addons_path):])))
return r return r
def manifest_list(extension, mods=None, db=None): def manifest_list(extension, mods=None, db=None, debug=False):
""" list ressources to load specifying either: """ list ressources to load specifying either:
mods: a comma separated string listing modules mods: a comma separated string listing modules
db: a database name (return all installed modules in that database) db: a database name (return all installed modules in that database)
""" """
files = manifest_glob(extension, addons=mods, db=db, include_remotes=True) files = manifest_glob(extension, addons=mods, db=db, include_remotes=True)
if not request.debug: if not debug:
path = '/web/webclient/' + extension path = '/web/webclient/' + extension
if mods is not None: if mods is not None:
path += '?' + urllib.urlencode({'mods': mods}) path += '?' + urllib.urlencode({'mods': mods})
@ -535,13 +532,24 @@ html_template = """<!DOCTYPE html>
class Home(http.Controller): class Home(http.Controller):
@http.route('/', type='http', auth="none") @http.route('/', type='http', auth="none")
def index(self, s_action=None, db=None, **kw): def index(self, s_action=None, db=None, debug=False, **kw):
db, redir = db_monodb_redirect() debug = debug != False
if redir: if db is not None:
return redirect_with_hash(redir) lst = http.db_list(True)
if db in lst and db != request.session.db:
request.session.logout()
request.session.db = db
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list('js', db=db)) if db != request.session.db:
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list('css', db=db)) query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True))
query.update({'db': request.session.db})
redirect = request.httprequest.path + '?' + urllib.urlencode(query)
return redirect_with_hash(redirect)
db = request.session.db
js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list('js', db=db, debug=debug))
css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list('css', db=db, debug=debug))
r = html_template % { r = html_template % {
'js': js, 'js': js,
@ -549,7 +557,7 @@ class Home(http.Controller):
'modules': simplejson.dumps(module_boot(db=db)), 'modules': simplejson.dumps(module_boot(db=db)),
'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));' 'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));'
} }
return r return request.make_response(r, {'Cache-Control': 'no-cache', 'Content-Type': 'text/html; charset=utf-8'})
@http.route('/login', type='http', auth="user") @http.route('/login', type='http', auth="user")
def login(self, db, login, key): def login(self, db, login, key):
@ -819,7 +827,7 @@ class Session(http.Controller):
@http.route('/web/session/get_session_info', type='json', auth="none") @http.route('/web/session/get_session_info', type='json', auth="none")
def get_session_info(self): def get_session_info(self):
request.uid = request.session.uid request.uid = request.session.uid
request.db = request.session.db request.disable_db = False
return self.session_info() return self.session_info()
@http.route('/web/session/authenticate', type='json', auth="none") @http.route('/web/session/authenticate', type='json', auth="none")

View File

@ -85,10 +85,6 @@ class WebRequest(object):
:class:`~collections.Mapping` of context values for the current request :class:`~collections.Mapping` of context values for the current request
.. attribute:: debug
``bool``, indicates whether the debug mode is active on the client
.. attribute:: db .. attribute:: db
``str``, the name of the database linked to the current request. Can be ``None`` ``str``, the name of the database linked to the current request. Can be ``None``
@ -105,16 +101,13 @@ class WebRequest(object):
self.httpsession = httprequest.session self.httpsession = httprequest.session
self.session = httprequest.session self.session = httprequest.session
self.session_id = httprequest.session.sid self.session_id = httprequest.session.sid
self.db = None self.disable_db = False
self.uid = None self.uid = None
self.func = None self.func = None
self.auth_method = None self.auth_method = None
self._cr_cm = None self._cr_cm = None
self._cr = None self._cr = None
self.func_request_type = None self.func_request_type = None
self.debug = self.httprequest.args.get('debug', False) is not False
with set_request(self):
self.db = self.session.db or db_monodb()
# set db/uid trackers - they're cleaned up at the WSGI # set db/uid trackers - they're cleaned up at the WSGI
# dispatching phase in openerp.service.wsgi_server.application # dispatching phase in openerp.service.wsgi_server.application
if self.db: if self.db:
@ -132,17 +125,16 @@ class WebRequest(object):
self.session.logout() self.session.logout()
raise SessionExpiredException("Session expired for request %s" % self.httprequest) raise SessionExpiredException("Session expired for request %s" % self.httprequest)
if self.auth_method == "none": if self.auth_method == "none":
self.db = None self.disable_db = True
self.uid = None self.uid = None
elif self.auth_method == "admin": elif self.auth_method == "admin":
self.db = self.session.db or db_monodb() self.disable_db = False
if not self.db: if not self.db:
raise SessionExpiredException("No valid database for request %s" % self.httprequest) raise SessionExpiredException("No valid database for request %s" % self.httprequest)
self.uid = openerp.SUPERUSER_ID self.uid = openerp.SUPERUSER_ID
else: # auth else: # auth
self.db = self.session.db self.disable_db = False
self.uid = self.session.uid self.uid = self.session.uid
@property @property
def registry(self): def registry(self):
""" """
@ -151,6 +143,14 @@ class WebRequest(object):
""" """
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
@property
def db(self):
"""
The registry to the database linked to this request. Can be ``None`` if the current request uses the
``none'' authentication.
"""
return self.session.db if not self.disable_db else None
@property @property
def cr(self): def cr(self):
""" """
@ -184,7 +184,7 @@ class WebRequest(object):
return self.func(*args, **kwargs) return self.func(*args, **kwargs)
finally: finally:
# just to be sure no one tries to re-use the request # just to be sure no one tries to re-use the request
self.db = None self.disable_db = True
self.uid = None self.uid = None
def route(route, type="http", auth="user"): def route(route, type="http", auth="user"):
@ -304,8 +304,6 @@ class JsonRequest(WebRequest):
error = None error = None
try: try:
#if _logger.isEnabledFor(logging.DEBUG):
# _logger.debug("--> %s.%s\n%s", func.im_class.__name__, func.__name__, pprint.pformat(self.jsonrequest))
response['id'] = self.jsonrequest.get('id') response['id'] = self.jsonrequest.get('id')
response["result"] = self._call_function(**self.params) response["result"] = self._call_function(**self.params)
except AuthenticationError, e: except AuthenticationError, e:
@ -327,9 +325,6 @@ class JsonRequest(WebRequest):
if error: if error:
response["error"] = error response["error"] = error
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug("<--\n%s", pprint.pformat(response))
if self.jsonp: if self.jsonp:
# If we use jsonp, that's mean we are called from another host # If we use jsonp, that's mean we are called from another host
# Some browser (IE and Safari) do no allow third party cookies # Some browser (IE and Safari) do no allow third party cookies
@ -394,7 +389,7 @@ class HttpRequest(WebRequest):
def __init__(self, *args): def __init__(self, *args):
super(HttpRequest, self).__init__(*args) super(HttpRequest, self).__init__(*args)
params = dict(self.httprequest.args) params = dict(self.httprequest.args)
ex = set(["session_id", "debug"]) ex = set(["session_id"])
for k in params.keys(): for k in params.keys():
if k in ex: if k in ex:
del params[k] del params[k]
@ -409,7 +404,6 @@ class HttpRequest(WebRequest):
akw[key] = value akw[key] = value
else: else:
akw[key] = type(value) akw[key] = type(value)
#_logger.debug("%s --> %s.%s %r", self.httprequest.func, func.im_class.__name__, func.__name__, akw)
try: try:
r = self._call_function(**self.params) r = self._call_function(**self.params)
except werkzeug.exceptions.HTTPException, e: except werkzeug.exceptions.HTTPException, e:
@ -426,10 +420,6 @@ class HttpRequest(WebRequest):
else: else:
if not r: if not r:
r = werkzeug.wrappers.Response(status=204) # no content r = werkzeug.wrappers.Response(status=204) # no content
if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
_logger.debug('<-- %s', r)
else:
_logger.debug("<-- size: %s", len(r))
return r return r
def make_response(self, data, headers=None, cookies=None): def make_response(self, data, headers=None, cookies=None):
@ -626,8 +616,8 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
self.uid = uid self.uid = uid
self.login = login self.login = login
self.password = password self.password = password
request.db = db
request.uid = uid request.uid = uid
request.disable_db = False
if uid: self.get_context() if uid: self.get_context()
return uid return uid
@ -900,6 +890,8 @@ class Root(object):
else: else:
httprequest.session = self.session_store.get(sid) httprequest.session = self.session_store.get(sid)
self._find_db(httprequest)
if not "lang" in httprequest.session.context: if not "lang" in httprequest.session.context:
lang = httprequest.accept_languages.best or "en_US" lang = httprequest.accept_languages.best or "en_US"
lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_') lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
@ -936,6 +928,12 @@ class Root(object):
except werkzeug.exceptions.HTTPException, e: except werkzeug.exceptions.HTTPException, e:
return e(environ, start_response) return e(environ, start_response)
def _find_db(self, httprequest):
db = db_monodb(httprequest)
if db != httprequest.session.db:
httprequest.session.logout()
httprequest.session.db = db
def _build_request(self, httprequest): def _build_request(self, httprequest):
if httprequest.args.get('jsonp'): if httprequest.args.get('jsonp'):
return JsonRequest(httprequest) return JsonRequest(httprequest)
@ -1050,43 +1048,16 @@ class Root(object):
root = None root = None
def db_list(force=False): def db_list(force=False, httprequest=None):
proxy = request.session.proxy("db") httprequest = httprequest or request.httprequest
dbs = proxy.list(force) dbs = openerp.netsvc.dispatch_rpc("db", "list", [force])
h = request.httprequest.environ['HTTP_HOST'].split(':')[0] h = httprequest.environ['HTTP_HOST'].split(':')[0]
d = h.split('.')[0] d = h.split('.')[0]
r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d) r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
dbs = [i for i in dbs if re.match(r, i)] dbs = [i for i in dbs if re.match(r, i)]
return dbs return dbs
def db_redirect(match_first_only_if_unique): def db_monodb(httprequest=None):
db = None
redirect = None
dbs = db_list(True)
# 1 try the db in the url
db_url = request.httprequest.args.get('db')
if db_url in dbs:
return (db_url, None)
# 2 use the database from the cookie if it's listable and still listed
cookie_db = request.httprequest.cookies.get('last_used_database')
if cookie_db in dbs:
db = cookie_db
# 3 use the first db if user can list databases
if dbs and not db and (not match_first_only_if_unique or len(dbs) == 1):
db = dbs[0]
# redirect to the chosen db if multiple are available
if db and len(dbs) > 1:
query = dict(urlparse.parse_qsl(request.httprequest.query_string, keep_blank_values=True))
query.update({'db': db})
redirect = request.httprequest.path + '?' + urllib.urlencode(query)
return (db, redirect)
def db_monodb():
""" """
Magic function to find the current database. Magic function to find the current database.
@ -1095,10 +1066,27 @@ def db_monodb():
* Magic * Magic
* More magic * More magic
Return ``None`` if the magic is not magic enough. Returns ``None`` if the magic is not magic enough.
""" """
return db_redirect(True)[0] httprequest = httprequest or request.httprequest
db = None
redirect = None
dbs = db_list(True, httprequest)
# 1 try the db already in the session
db_session = httprequest.session.db
if db_session in dbs:
return db_session
# 2 if there is only one db in the db filters, take it
if len(dbs) == 1:
return dbs[0]
# 3 if there are multiple dbs, take the first one only if we can list them
if len(dbs) > 1 and config['list_db']:
return dbs[0]
return None
class CommonController(Controller): class CommonController(Controller):

6
addons/web/package.json Normal file
View File

@ -0,0 +1,6 @@
{
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.6.0"
}
}

View File

@ -438,7 +438,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.$el.html(QWeb.render("DatabaseManager", { widget : self })); self.$el.html(QWeb.render("DatabaseManager", { widget : self }));
$('.oe_user_menu_placeholder').append(QWeb.render("DatabaseManager.user_menu",{ widget : self })); $('.oe_user_menu_placeholder').append(QWeb.render("DatabaseManager.user_menu",{ widget : self }));
$('.oe_secondary_menus_container').append(QWeb.render("DatabaseManager.menu",{ widget : self })); $('.oe_secondary_menus_container').append(QWeb.render("DatabaseManager.menu",{ widget : self }));
$('ul.oe_secondary_submenu > li:first').addClass('oe_active') $('ul.oe_secondary_submenu > li:first').addClass('oe_active');
$('ul.oe_secondary_submenu > li').bind('click', function (event) { $('ul.oe_secondary_submenu > li').bind('click', function (event) {
var menuitem = $(this); var menuitem = $(this);
menuitem.addClass('oe_active').siblings().removeClass('oe_active'); menuitem.addClass('oe_active').siblings().removeClass('oe_active');
@ -705,20 +705,9 @@ instance.web.Login = instance.web.Widget.extend({
} }
return d; return d;
}, },
remember_last_used_database: function(db) {
// This cookie will be used server side in order to avoid db reloading on first visit
var ttl = 24 * 60 * 60 * 365;
document.cookie = [
'last_used_database=' + db,
'path=/',
'max-age=' + ttl,
'expires=' + new Date(new Date().getTime() + ttl * 1000).toGMTString()
].join(';');
},
database_selected: function(db) { database_selected: function(db) {
var params = $.deparam.querystring(); var params = $.deparam.querystring();
params.db = db; params.db = db;
this.remember_last_used_database(db);
this.$('.oe_login_dbpane').empty().text(_t('Loading...')); this.$('.oe_login_dbpane').empty().text(_t('Loading...'));
this.$('[name=login], [name=password]').prop('readonly', true); this.$('[name=login], [name=password]').prop('readonly', true);
instance.web.redirect('/?' + $.param(params)); instance.web.redirect('/?' + $.param(params));
@ -769,7 +758,6 @@ instance.web.Login = instance.web.Widget.extend({
self.hide_error(); self.hide_error();
self.$(".oe_login_pane").fadeOut("slow"); self.$(".oe_login_pane").fadeOut("slow");
return this.session.session_authenticate(db, login, password).then(function() { return this.session.session_authenticate(db, login, password).then(function() {
self.remember_last_used_database(db);
if (self.has_local_storage && self.remember_credentials) { if (self.has_local_storage && self.remember_credentials) {
localStorage.setItem(db + '|last_login', login); localStorage.setItem(db + '|last_login', login);
if (self.session.debug) { if (self.session.debug) {
@ -804,10 +792,17 @@ instance.web.redirect = function(url, wait) {
instance.client.crashmanager.active = false; instance.client.crashmanager.active = false;
} }
var wait_server = function() { var load = function() {
instance.session.rpc("/web/webclient/version_info", {}).done(function() { var old = "" + window.location;
if (old === url) {
window.location.reload();
} else {
window.location = url; window.location = url;
}).fail(function() { }
};
var wait_server = function() {
instance.session.rpc("/web/webclient/version_info", {}).done(load).fail(function() {
setTimeout(wait_server, 250); setTimeout(wait_server, 250);
}); });
}; };
@ -815,7 +810,7 @@ instance.web.redirect = function(url, wait) {
if (wait) { if (wait) {
setTimeout(wait_server, 1000); setTimeout(wait_server, 1000);
} else { } else {
window.location = url; load();
} }
}; };
@ -830,7 +825,6 @@ instance.web.Reload = function(parent, action) {
var l = window.location; var l = window.location;
var sobj = $.deparam(l.search.substr(1)); var sobj = $.deparam(l.search.substr(1));
sobj.ts = new Date().getTime();
if (params.url_search) { if (params.url_search) {
sobj = _.extend(sobj, params.url_search); sobj = _.extend(sobj, params.url_search);
} }
@ -875,7 +869,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
$button.appendTo(this.getParent().$buttons); $button.appendTo(this.getParent().$buttons);
$button.eq(2).click(function(){ $button.eq(2).click(function(){
self.getParent().close(); self.getParent().close();
}) });
$button.eq(0).click(function(){ $button.eq(0).click(function(){
self.rpc("/web/session/change_password",{ self.rpc("/web/session/change_password",{
'fields': $("form[name=change_password_form]").serializeArray() 'fields': $("form[name=change_password_form]").serializeArray()
@ -887,7 +881,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
instance.webclient.on_logout(); instance.webclient.on_logout();
} }
}); });
}) });
}, },
display_error: function (error) { display_error: function (error) {
return instance.web.dialog($('<div>'), { return instance.web.dialog($('<div>'), {
@ -898,7 +892,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
] ]
}).html(error.error); }).html(error.error);
}, },
}) });
instance.web.client_actions.add("change_password", "instance.web.ChangePassword"); instance.web.client_actions.add("change_password", "instance.web.ChangePassword");
instance.web.Menu = instance.web.Widget.extend({ instance.web.Menu = instance.web.Widget.extend({
@ -1114,7 +1108,7 @@ instance.web.Menu = instance.web.Widget.extend({
add_menu_ids(menu); add_menu_ids(menu);
}); });
} }
}; }
add_menu_ids(menu); add_menu_ids(menu);
self.do_load_needaction(menu_ids).then(function () { self.do_load_needaction(menu_ids).then(function () {
self.trigger("need_action_reloaded"); self.trigger("need_action_reloaded");

View File

@ -73,7 +73,7 @@ openerp.web.corelib = function(instance) {
*/ */
(function() { (function() {
var initializing = false, var initializing = false,
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
// The web Class implementation (does nothing) // The web Class implementation (does nothing)
instance.web.Class = function(){}; instance.web.Class = function(){};
@ -96,7 +96,7 @@ openerp.web.corelib = function(instance) {
initializing = false; initializing = false;
// Copy the properties over onto the new prototype // Copy the properties over onto the new prototype
for (var name in prop) { _.each(prop, function(val, name) {
// Check if we're overwriting an existing function // Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" && prototype[name] = typeof prop[name] == "function" &&
fnTest.test(prop[name]) ? fnTest.test(prop[name]) ?
@ -117,13 +117,12 @@ openerp.web.corelib = function(instance) {
}; };
})(name, prop[name]) : })(name, prop[name]) :
prop[name]; prop[name];
} });
// The dummy class constructor // The dummy class constructor
function Class() { function Class() {
if(this.constructor !== instance.web.Class){ if(this.constructor !== instance.web.Class){
throw new Error("You can only instanciate objects with the 'new' operator"); throw new Error("You can only instanciate objects with the 'new' operator");
return null;
} }
// All construction is actually done in the init method // All construction is actually done in the init method
if (!initializing && this.init) { if (!initializing && this.init) {
@ -133,7 +132,7 @@ openerp.web.corelib = function(instance) {
return this; return this;
} }
Class.include = function (properties) { Class.include = function (properties) {
for (var name in properties) { _.each(properties, function(val, name) {
if (typeof properties[name] !== 'function' if (typeof properties[name] !== 'function'
|| !fnTest.test(properties[name])) { || !fnTest.test(properties[name])) {
prototype[name] = properties[name]; prototype[name] = properties[name];
@ -146,7 +145,7 @@ openerp.web.corelib = function(instance) {
var ret = fn.apply(this, arguments); var ret = fn.apply(this, arguments);
this._super = tmp; this._super = tmp;
return ret; return ret;
} };
})(name, properties[name], prototype[name]); })(name, properties[name], prototype[name]);
} else if (typeof _super[name] === 'function') { } else if (typeof _super[name] === 'function') {
prototype[name] = (function (name, fn) { prototype[name] = (function (name, fn) {
@ -156,10 +155,10 @@ openerp.web.corelib = function(instance) {
var ret = fn.apply(this, arguments); var ret = fn.apply(this, arguments);
this._super = tmp; this._super = tmp;
return ret; return ret;
} };
})(name, properties[name]); })(name, properties[name]);
} }
} });
}; };
// Populate our constructed prototype object // Populate our constructed prototype object
@ -297,7 +296,7 @@ var Events = instance.web.Class.extend({
var ev; var ev;
events = events.split(/\s+/); events = events.split(/\s+/);
var calls = this._callbacks || (this._callbacks = {}); var calls = this._callbacks || (this._callbacks = {});
while (ev = events.shift()) { while ((ev = events.shift())) {
var list = calls[ev] || (calls[ev] = {}); var list = calls[ev] || (calls[ev] = {});
var tail = list.tail || (list.tail = list.next = {}); var tail = list.tail || (list.tail = list.next = {});
tail.callback = callback; tail.callback = callback;
@ -311,9 +310,9 @@ var Events = instance.web.Class.extend({
var ev, calls, node; var ev, calls, node;
if (!events) { if (!events) {
delete this._callbacks; delete this._callbacks;
} else if (calls = this._callbacks) { } else if ((calls = this._callbacks)) {
events = events.split(/\s+/); events = events.split(/\s+/);
while (ev = events.shift()) { while ((ev = events.shift())) {
node = calls[ev]; node = calls[ev];
delete calls[ev]; delete calls[ev];
if (!callback || !node) if (!callback || !node)
@ -347,7 +346,7 @@ var Events = instance.web.Class.extend({
all = calls['all']; all = calls['all'];
(events = events.split(/\s+/)).push(null); (events = events.split(/\s+/)).push(null);
// Save references to the current heads & tails. // Save references to the current heads & tails.
while (event = events.shift()) { while ((event = events.shift())) {
if (all) if (all)
events.push({ events.push({
next : all.next, next : all.next,
@ -362,7 +361,7 @@ var Events = instance.web.Class.extend({
}); });
} }
rest = Array.prototype.slice.call(arguments, 1); rest = Array.prototype.slice.call(arguments, 1);
while (node = events.pop()) { while ((node = events.pop())) {
tail = node.tail; tail = node.tail;
args = node.event ? [ node.event ].concat(rest) : rest; args = node.event ? [ node.event ].concat(rest) : rest;
while ((node = node.next) !== tail) { while ((node = node.next) !== tail) {
@ -504,7 +503,7 @@ instance.web.Controller = instance.web.Class.extend(instance.web.PropertiesMixin
return function () { return function () {
var fn = (typeof method === 'string') ? self[method] : method; var fn = (typeof method === 'string') ? self[method] : method;
return fn.apply(self, arguments); return fn.apply(self, arguments);
} };
}, },
/** /**
* Informs the action manager to do an action. This supposes that * Informs the action manager to do an action. This supposes that
@ -692,11 +691,6 @@ instance.web.Widget = instance.web.Controller.extend({
insertion(target); insertion(target);
return this.start(); return this.start();
}, },
/**
* This is the method to implement to render the Widget.
*/
renderElement: function() {
},
/** /**
* Method called after rendering. Mostly used to bind actions, perform asynchronous * Method called after rendering. Mostly used to bind actions, perform asynchronous
* calls, etc... * calls, etc...
@ -889,7 +883,7 @@ instance.web.Registry = instance.web.Class.extend({
contains: function (key) { contains: function (key) {
if (key === undefined) { return false; } if (key === undefined) { return false; }
if (key in this.map) { if (key in this.map) {
return true return true;
} }
if (this.parent) { if (this.parent) {
return this.parent.contains(key); return this.parent.contains(key);
@ -966,7 +960,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
init: function() { init: function() {
instance.web.PropertiesMixin.init.call(this); instance.web.PropertiesMixin.init.call(this);
this.server = null; this.server = null;
this.debug = ($.deparam($.param.querystring()).debug != undefined); this.debug = ($.deparam($.param.querystring()).debug !== undefined);
this.override_session = false; this.override_session = false;
this.session_id = undefined; this.session_id = undefined;
}, },
@ -1154,6 +1148,6 @@ instance.web.py_eval = function(expr, context) {
return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null})); return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
}; };
} };
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -88,7 +88,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.session_id = this.get_cookie('session_id'); this.session_id = this.get_cookie('session_id');
} }
return def.then(function() { return def.then(function() {
return self.rpc("/web/session/get_session_info", {}) return self.rpc("/web/session/get_session_info", {});
}).then(function(result) { }).then(function(result) {
// If immediately follows a login (triggered by trying to restore // If immediately follows a login (triggered by trying to restore
// an invalid session or no session at all), refresh session data // an invalid session or no session at all), refresh session data
@ -250,7 +250,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
continue; continue;
instance[mod] = {}; instance[mod] = {};
// init module mod // init module mod
if(instance._openerp[mod] != undefined) { if(instance._openerp[mod] !== undefined) {
instance._openerp[mod](instance,instance[mod]); instance._openerp[mod](instance,instance[mod]);
this.module_loaded[mod] = true; this.module_loaded[mod] = true;
} }
@ -343,7 +343,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
$input = $('<input type="hidden" name="' + key + '">') $input = $('<input type="hidden" name="' + key + '">')
.appendTo($form_data); .appendTo($form_data);
} }
$input.val(value) $input.val(value);
}); });
$form $form
@ -404,7 +404,7 @@ instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin,
}); });
}); });
} }
}) });
instance.web.bus = new instance.web.Bus(); instance.web.bus = new instance.web.Bus();
/** OpenERP Translations */ /** OpenERP Translations */
@ -466,12 +466,12 @@ $.fn.getAttributes = function() {
var o = {}; var o = {};
if (this.length) { if (this.length) {
for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) { for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) {
attr = attrs.item(i) attr = attrs.item(i);
o[attr.nodeName] = attr.nodeValue; o[attr.nodeName] = attr.nodeValue;
} }
} }
return o; return o;
} };
$.fn.openerpClass = function(additionalClass) { $.fn.openerpClass = function(additionalClass) {
// This plugin should be applied on top level elements // This plugin should be applied on top level elements
additionalClass = additionalClass || ''; additionalClass = additionalClass || '';
@ -557,7 +557,7 @@ instance.web._t = new instance.web.TranslationDataBase().build_translation_funct
* @returns {Object} lazy translation object * @returns {Object} lazy translation object
*/ */
instance.web._lt = function (s) { instance.web._lt = function (s) {
return {toString: function () { return instance.web._t(s); }} return {toString: function () { return instance.web._t(s); }};
}; };
instance.web.qweb = new QWeb2.Engine(); instance.web.qweb = new QWeb2.Engine();
instance.web.qweb.debug = instance.session.debug; instance.web.qweb.debug = instance.session.debug;
@ -585,7 +585,7 @@ instance.web.qweb.preprocess_node = function() {
case Node.ELEMENT_NODE: case Node.ELEMENT_NODE:
// Element // Element
var attr, attrs = ['label', 'title', 'alt', 'placeholder']; var attr, attrs = ['label', 'title', 'alt', 'placeholder'];
while (attr = attrs.pop()) { while ((attr = attrs.pop())) {
if (this.attributes[attr]) { if (this.attributes[attr]) {
this.attributes[attr] = instance.web._t(this.attributes[attr]); this.attributes[attr] = instance.web._t(this.attributes[attr]);
} }
@ -692,13 +692,13 @@ instance.web.blockUI = function() {
instance.web.Throbber.throbbers.push(throbber); instance.web.Throbber.throbbers.push(throbber);
throbber.appendTo($(".oe_blockui_spin_container")); throbber.appendTo($(".oe_blockui_spin_container"));
return tmp; return tmp;
} };
instance.web.unblockUI = function() { instance.web.unblockUI = function() {
_.each(instance.web.Throbber.throbbers, function(el) { _.each(instance.web.Throbber.throbbers, function(el) {
el.destroy(); el.destroy();
}); });
return $.unblockUI.apply($, arguments); return $.unblockUI.apply($, arguments);
} };
/** /**
* Registry for all the client actions key: tag value: widget * Registry for all the client actions key: tag value: widget

View File

@ -100,7 +100,7 @@ instance.web.Query = instance.web.Class.extend({
* @returns {jQuery.Deferred<Number>} * @returns {jQuery.Deferred<Number>}
*/ */
count: function () { count: function () {
if (this._count != undefined) { return $.when(this._count); } if (this._count !== undefined) { return $.when(this._count); }
return this._model.call( return this._model.call(
'search_count', [this._filter], { 'search_count', [this._filter], {
context: this._model.context(this._context)}); context: this._model.context(this._context)});
@ -494,7 +494,7 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
return this._model.call('create', [data], { return this._model.call('create', [data], {
context: this.get_context() context: this.get_context()
}).done(function () { }).done(function () {
self.trigger('dataset_changed', data, options) self.trigger('dataset_changed', data, options);
}); });
}, },
/** /**
@ -514,7 +514,7 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
return this._model.call('write', [[id], data], { return this._model.call('write', [[id], data], {
context: this.get_context(options.context) context: this.get_context(options.context)
}).done(function () { }).done(function () {
self.trigger('dataset_changed', id, data, options) self.trigger('dataset_changed', id, data, options);
}); });
}, },
/** /**
@ -527,7 +527,7 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
return this._model.call('unlink', [ids], { return this._model.call('unlink', [ids], {
context: this.get_context() context: this.get_context()
}).done(function () { }).done(function () {
self.trigger('dataset_changed', ids) self.trigger('dataset_changed', ids);
}); });
}, },
/** /**
@ -747,7 +747,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
}); });
}, },
size: function () { size: function () {
if (this._length != null) { if (this._length !== null) {
return this._length; return this._length;
} }
return this._super(); return this._super();
@ -810,7 +810,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
var self = this; var self = this;
_.each(ids, function(id) { _.each(ids, function(id) {
if (! _.detect(self.to_create, function(x) { return x.id === id; })) { if (! _.detect(self.to_create, function(x) { return x.id === id; })) {
self.to_delete.push({id: id}) self.to_delete.push({id: id});
} }
}); });
this.to_create = _.reject(this.to_create, function(x) { return _.include(ids, x.id);}); this.to_create = _.reject(this.to_create, function(x) { return _.include(ids, x.id);});
@ -838,7 +838,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
_.each(fields, function(x) {if (cached.values[x] === undefined) _.each(fields, function(x) {if (cached.values[x] === undefined)
cached.values[x] = created.defaults[x] || false;}); cached.values[x] = created.defaults[x] || false;});
} else { } else {
if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined})) if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined;}))
to_get.push(id); to_get.push(id);
} }
}); });

View File

@ -71,7 +71,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
opt.replaceChild( opt.replaceChild(
document.createTextNode( document.createTextNode(
_.str.sprintf("%s — %s", format.label, format.error)), _.str.sprintf("%s — %s", format.label, format.error)),
opt.childNodes[0]) opt.childNodes[0]);
} }
$fmts.append(opt); $fmts.append(opt);
}); });
@ -93,7 +93,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.$el.find('#fields_list option').remove(); self.$el.find('#fields_list option').remove();
var export_id = self.$el.find('#saved_export_list option:selected').val(); var export_id = self.$el.find('#saved_export_list option:selected').val();
if (export_id) { if (export_id) {
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).done(self.do_load_export_field); self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id, 10)}).done(self.do_load_export_field);
} }
}); });
self.$el.find('#delete_export_list').click(function() { self.$el.find('#delete_export_list').click(function() {
@ -215,7 +215,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.$el.find("tr[id='treerow-" + record.id + "']").click(function(e) { self.$el.find("tr[id='treerow-" + record.id + "']").click(function(e) {
if (e.shiftKey) { if (e.shiftKey) {
var frst_click, scnd_click = ''; var frst_click, scnd_click = '';
if (self.row_index == 0) { if (self.row_index === 0) {
self.row_index = this.rowIndex; self.row_index = this.rowIndex;
frst_click = self.$el.find("tr[id^='treerow-']")[self.row_index-1]; frst_click = self.$el.find("tr[id^='treerow-']")[self.row_index-1];
$(frst_click).addClass("ui-selected"); $(frst_click).addClass("ui-selected");

View File

@ -141,6 +141,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return ''; return '';
} }
console.warn('Field', descriptor, 'had an empty string as value, treating as false...'); console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
return value_if_empty === undefined ? '' : value_if_empty;
case false: case false:
case Infinity: case Infinity:
case -Infinity: case -Infinity:
@ -199,7 +200,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
case 'selection': case 'statusbar': case 'selection': case 'statusbar':
// Each choice is [value, label] // Each choice is [value, label]
if(_.isArray(value)) { if(_.isArray(value)) {
value = value[0] value = value[0];
} }
var result = _(descriptor.selection).detect(function (choice) { var result = _(descriptor.selection).detect(function (choice) {
return choice[0] === value; return choice[0] === value;
@ -219,9 +220,9 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
case "": case "":
return value_if_empty === undefined ? false : value_if_empty; return value_if_empty === undefined ? false : value_if_empty;
} }
var tmp;
switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) { switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
case 'integer': case 'integer':
var tmp;
do { do {
tmp = value; tmp = value;
value = value.replace(instance.web._t.database.parameters.thousands_sep, ""); value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
@ -231,7 +232,7 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value)); throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
return tmp; return tmp;
case 'float': case 'float':
var tmp = Number(value); tmp = Number(value);
if (!isNaN(tmp)) if (!isNaN(tmp))
return tmp; return tmp;
@ -314,4 +315,34 @@ instance.web.auto_date_to_str = function(value, type) {
} }
}; };
/**
* 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 non zero precision parameter. eg: 0.01 rounds to two digits.
*/
instance.web.round_precision = function(value, precision){
if(!value){
return 0;
}else if(!precision){
throw new Error('round_precision(...): Cannot round value: '+value+' with a precision of zero (or undefined)');
}
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;
var rounded_value = Math.round(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
*/
instance.web.round_decimals = function(value, decimals){
return instance.web.round_precision(value, Math.pow(10,-decimals));
};
}; };

View File

@ -97,7 +97,7 @@ openerp.web.pyeval = function (instance) {
divmod(n, 365, function (_n1, n) { divmod(n, 365, function (_n1, n) {
n1 = _n1; n1 = _n1;
n0 = n; n0 = n;
}) });
}); });
}); });
}); });
@ -139,7 +139,7 @@ openerp.web.pyeval = function (instance) {
if (microsecond < 0 || microsecond > 999999) { if (microsecond < 0 || microsecond > 999999) {
divmod(microsecond, 1000000, function (carry, ms) { divmod(microsecond, 1000000, function (carry, ms) {
microsecond = ms; microsecond = ms;
second += carry second += carry;
}); });
} }
if (second < 0 || second > 59) { if (second < 0 || second > 59) {
@ -152,13 +152,13 @@ openerp.web.pyeval = function (instance) {
divmod(minute, 60, function (carry, m) { divmod(minute, 60, function (carry, m) {
minute = m; minute = m;
hour += carry; hour += carry;
}) });
} }
if (hour < 0 || hour > 23) { if (hour < 0 || hour > 23) {
divmod(hour, 24, function (carry, h) { divmod(hour, 24, function (carry, h) {
hour = h; hour = h;
day += carry; day += carry;
}) });
} }
// That was easy. Now it gets muddy: the proper range for day // That was easy. Now it gets muddy: the proper range for day
// can't be determined without knowing the correct month and year, // can't be determined without knowing the correct month and year,
@ -170,7 +170,7 @@ openerp.web.pyeval = function (instance) {
divmod(month-1, 12, function (carry, m) { divmod(month-1, 12, function (carry, m) {
month = m + 1; month = m + 1;
year += carry; year += carry;
}) });
} }
// Now only day can be out of bounds (year may also be out of bounds // 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). // for a datetime object, but we don't care about that here).
@ -247,7 +247,7 @@ openerp.web.pyeval = function (instance) {
}); });
divmod(seconds, 24*3600, function (days, seconds) { divmod(seconds, 24*3600, function (days, seconds) {
d += days; d += days;
s += seconds s += seconds;
}); });
// seconds isn't referenced again before redefinition // seconds isn't referenced again before redefinition
@ -358,7 +358,7 @@ openerp.web.pyeval = function (instance) {
return py.float.fromJSON( return py.float.fromJSON(
this.days * 86400 this.days * 86400
+ this.seconds + this.seconds
+ this.microseconds / 1000000) + this.microseconds / 1000000);
}, },
__nonzero__: function () { __nonzero__: function () {
return (!!this.days || !!this.seconds || !!this.microseconds) return (!!this.days || !!this.seconds || !!this.microseconds)
@ -466,7 +466,7 @@ openerp.web.pyeval = function (instance) {
return py.float.fromJSON(ymd2ord(this.year, this.month, this.day)); return py.float.fromJSON(ymd2ord(this.year, this.month, this.day));
}, },
fromJSON: function (year, month, day) { fromJSON: function (year, month, day) {
return py.PY_call(datetime.date, [year, month, day]) return py.PY_call(datetime.date, [year, month, day]);
} }
}); });
/** /**
@ -505,7 +505,7 @@ openerp.web.pyeval = function (instance) {
var args = _.map(('year month day hour minute second microsecond ' var args = _.map(('year month day hour minute second microsecond '
+ 'years months weeks days hours minutes secondes microseconds ' + 'years months weeks days hours minutes secondes microseconds '
+ 'weekday leakdays yearday nlyearday').split(' '), function (arg) { + 'weekday leakdays yearday nlyearday').split(' '), function (arg) {
return [arg, null] return [arg, null];
}); });
args.unshift('*'); args.unshift('*');
var relativedelta = py.type('relativedelta', null, { var relativedelta = py.type('relativedelta', null, {
@ -795,13 +795,20 @@ openerp.web.pyeval = function (instance) {
//noinspection FallthroughInSwitchStatementJS //noinspection FallthroughInSwitchStatementJS
switch(type) { switch(type) {
case 'context': object = [object]; case 'context':
case 'contexts': return eval_contexts((options.no_user_context ? [] : [instance.session.user_context]).concat(object), context); case 'contexts':
case 'domain': object = [object]; if (type === 'context')
case 'domains': return eval_domains(object, context); object = [object];
case 'groupbys': return eval_groupbys(object, context); return eval_contexts((options.no_user_context ? [] : [instance.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) throw new Error("Unknow evaluation type " + type);
}; };
var eval_arg = function (arg) { var eval_arg = function (arg) {
@ -856,5 +863,5 @@ openerp.web.pyeval = function (instance) {
}}); }});
} }
}, 0); }); }, 0); });
} };
}; };

View File

@ -65,7 +65,7 @@ my.SearchQuery = B.Collection.extend({
}, this); }, this);
}, },
add: function (values, options) { add: function (values, options) {
options || (options = {}); options = options || {};
if (!(values instanceof Array)) { if (!(values instanceof Array)) {
values = [values]; values = [values];
} }
@ -85,7 +85,7 @@ my.SearchQuery = B.Collection.extend({
return this; return this;
}, },
toggle: function (value, options) { toggle: function (value, options) {
options || (options = {}); options = options || {};
var facet = this.detect(function (facet) { var facet = this.detect(function (facet) {
return facet.get('category') === value.category return facet.get('category') === value.category
@ -148,7 +148,7 @@ my.InputView = instance.web.Widget.extend({
range.setStart(root, 0); range.setStart(root, 0);
} }
if (range.endContainer === this.el && range.endOffset === 1) { if (range.endContainer === this.el && range.endOffset === 1) {
range.setEnd(root, root.length) range.setEnd(root, root.length);
} }
assert(range.startContainer === root, assert(range.startContainer === root,
"selection should be in the input view"); "selection should be in the input view");
@ -157,7 +157,7 @@ my.InputView = instance.web.Widget.extend({
return { return {
start: range.startOffset, start: range.startOffset,
end: range.endOffset end: range.endOffset
} };
}, },
onKeydown: function (e) { onKeydown: function (e) {
this.el.normalize(); this.el.normalize();
@ -401,7 +401,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
}); });
$.when(load_view).then(function (r) { $.when(load_view).then(function (r) {
return self.search_view_loaded(r) return self.search_view_loaded(r);
}).fail(function () { }).fail(function () {
self.ready.reject.apply(null, arguments); self.ready.reject.apply(null, arguments);
}); });
@ -950,7 +950,7 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan
* @returns {jQuery.Deferred<null|Array>} * @returns {jQuery.Deferred<null|Array>}
*/ */
complete: function (value) { complete: function (value) {
return $.when(null) return $.when(null);
}, },
/** /**
* Returns a Facet instance for the provided defaults if they apply to * Returns a Facet instance for the provided defaults if they apply to
@ -1073,7 +1073,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
icon: this.icon, icon: this.icon,
values: values, values: values,
field: this field: this
} };
}, },
make_value: function (filter) { make_value: function (filter) {
return { return {
@ -1107,7 +1107,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
if (!contexts.length) { return; } if (!contexts.length) { return; }
if (contexts.length === 1) { return contexts[0]; } if (contexts.length === 1) { return contexts[0]; }
return _.extend(new instance.web.CompoundContext, { return _.extend(new instance.web.CompoundContext(), {
__contexts: contexts __contexts: contexts
}); });
}, },
@ -1176,7 +1176,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
label: _.str.sprintf(self.completion_label.toString(), label: _.str.sprintf(self.completion_label.toString(),
_.escape(facet_value.label)), _.escape(facet_value.label)),
facet: self.make_facet([facet_value]) facet: self.make_facet([facet_value])
} };
})); }));
} }
}); });
@ -1198,7 +1198,7 @@ instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({
get_context: this.proxy('get_context'), get_context: this.proxy('get_context'),
get_domain: this.proxy('get_domain'), get_domain: this.proxy('get_domain'),
get_groupby: this.proxy('get_groupby') get_groupby: this.proxy('get_groupby')
} };
} }
}, },
match_facet: function (facet) { match_facet: function (facet) {
@ -1276,7 +1276,7 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc
if (contexts.length === 1) { return contexts[0]; } if (contexts.length === 1) { return contexts[0]; }
return _.extend(new instance.web.CompoundContext, { return _.extend(new instance.web.CompoundContext(), {
__contexts: contexts __contexts: contexts
}); });
}, },
@ -1321,7 +1321,7 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc
domains.unshift(['|']); domains.unshift(['|']);
} }
return _.extend(new instance.web.CompoundDomain, { return _.extend(new instance.web.CompoundDomain(), {
__domains: domains __domains: domains
}); });
} }
@ -1564,7 +1564,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
return this.model.call('name_get', [value]).then(function (names) { return this.model.call('name_get', [value]).then(function (names) {
if (_(names).isEmpty()) { return null; } if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]); return facet_from(self, names[0]);
}) });
}, },
value_from: function (facetValue) { value_from: function (facetValue) {
return facetValue.get('label'); return facetValue.get('label');
@ -1919,7 +1919,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
}, },
changed: function() { changed: function() {
var nval = this.$(".searchview_extended_prop_field").val(); var nval = this.$(".searchview_extended_prop_field").val();
if(this.attrs.selected == null || nval != this.attrs.selected.name) { if(this.attrs.selected === null || this.attrs.selected === undefined || nval != this.attrs.selected.name) {
this.select_field(_.detect(this.fields, function(x) {return x.name == nval;})); this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
} }
}, },
@ -1941,13 +1941,13 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
*/ */
select_field: function(field) { select_field: function(field) {
var self = this; var self = this;
if(this.attrs.selected != null) { if(this.attrs.selected !== null && this.attrs.selected !== undefined) {
this.value.destroy(); this.value.destroy();
this.value = null; this.value = null;
this.$('.searchview_extended_prop_op').html(''); this.$('.searchview_extended_prop_op').html('');
} }
this.attrs.selected = field; this.attrs.selected = field;
if(field == null) { if(field === null || field === undefined) {
return; return;
} }
@ -1967,7 +1967,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
}, },
get_proposition: function() { get_proposition: function() {
if ( this.attrs.selected == null) if (this.attrs.selected === null || this.attrs.selected === undefined)
return null; return null;
var field = this.attrs.selected; var field = this.attrs.selected;
var op_select = this.$('.searchview_extended_prop_op')[0]; var op_select = this.$('.searchview_extended_prop_op')[0];
@ -2097,7 +2097,7 @@ instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.Exte
get_value: function() { get_value: function() {
try { try {
var val =this.$el.val(); var val =this.$el.val();
return instance.web.parse_value(val == "" ? 0 : val, {'widget': 'integer'}); return instance.web.parse_value(val === "" ? 0 : val, {'widget': 'integer'});
} catch (e) { } catch (e) {
return ""; return "";
} }
@ -2124,7 +2124,7 @@ instance.web.search.ExtendedSearchProposition.Float = instance.web.search.Extend
get_value: function() { get_value: function() {
try { try {
var val =this.$el.val(); var val =this.$el.val();
return instance.web.parse_value(val == "" ? 0.0 : val, {'widget': 'float'}); return instance.web.parse_value(val === "" ? 0.0 : val, {'widget': 'float'});
} catch (e) { } catch (e) {
return ""; return "";
} }

View File

@ -73,6 +73,6 @@ openerp.test_support = {
return; return;
} }
fn(e.data.name); fn(e.data.name);
}) });
} }
}; };

View File

@ -176,9 +176,9 @@ openerp.testing = {};
}); });
QUnit.module(testing.current_module + '.' + name, {_oe: options}); QUnit.module(testing.current_module + '.' + name, {_oe: options});
body(testing.case); body(testing['case']);
}; };
testing.case = function (name, options, callback) { testing['case'] = function (name, options, callback) {
if (_.isFunction(options)) { if (_.isFunction(options)) {
callback = options; callback = options;
options = {}; options = {};
@ -359,7 +359,7 @@ openerp.testing = {};
return $.Deferred(function (d) { return $.Deferred(function (d) {
$.when(result).then(function () { $.when(result).then(function () {
d.resolve.apply(d, arguments) d.resolve.apply(d, arguments);
}, function () { }, function () {
d.reject.apply(d, arguments); d.reject.apply(d, arguments);
}); });

View File

@ -346,7 +346,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.on_form_changed(); self.on_form_changed();
self.rendering_engine.init_fields(); self.rendering_engine.init_fields();
self.is_initialized.resolve(); self.is_initialized.resolve();
self.do_update_pager(record.id == null); self.do_update_pager(record.id === null || record.id === undefined);
if (self.sidebar) { if (self.sidebar) {
self.sidebar.do_attachement_update(self.dataset, self.datarecord.id); self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
} }
@ -437,7 +437,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
var method = call[1]; var method = call[1];
if (!_.str.trim(call[2])) { if (!_.str.trim(call[2])) {
return {method: method, args: []} return {method: method, args: []};
} }
var argument_replacement = { var argument_replacement = {
@ -465,7 +465,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
// form field // form field
if (self.fields[field]) { if (self.fields[field]) {
var value_ = self.fields[field].get_value(); var value_ = self.fields[field].get_value();
return value_ == null ? false : value_; return value_ === null || value_ === undefined ? false : value_;
} }
// parent field // parent field
var splitted = field.split('.'); var splitted = field.split('.');
@ -475,7 +475,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
} }
var p_val = parent_fields[_.str.trim(splitted[1])]; var p_val = parent_fields[_.str.trim(splitted[1])];
if (p_val !== undefined) { if (p_val !== undefined) {
return p_val == null ? false : p_val; return p_val === null || p_val === undefined ? false : p_val;
} }
} }
// string literal // string literal
@ -621,7 +621,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
} }
return $.when(); return $.when();
}); });
}; }
return iterate(); return iterate();
}); });
}, },
@ -778,7 +778,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
} else { } else {
$.async_when().done(function () { $.async_when().done(function () {
def.reject(); def.reject();
}) });
} }
}); });
return def.promise(); return def.promise();
@ -943,11 +943,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
reload: function() { reload: function() {
var self = this; var self = this;
return this.reload_mutex.exec(function() { return this.reload_mutex.exec(function() {
if (self.dataset.index == null) { if (self.dataset.index === null || self.dataset.index === undefined) {
self.trigger("previous_view"); self.trigger("previous_view");
return $.Deferred().reject().promise(); return $.Deferred().reject().promise();
} }
if (self.dataset.index == null || self.dataset.index < 0) { if (self.dataset.index < 0) {
return $.when(self.on_button_new()); return $.when(self.on_button_new());
} else { } else {
var fields = _.keys(self.fields_view.fields); var fields = _.keys(self.fields_view.fields);
@ -1028,7 +1028,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return field.get_displayed(); return field.get_displayed();
} }
return value; return value;
} };
var fields = _.chain(this.fields) var fields = _.chain(this.fields)
.map(function (field) { .map(function (field) {
var value = field.get_value(); var value = field.get_value();
@ -1049,7 +1049,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
string: field.string, string: field.string,
value: value, value: value,
displayed: display(field, value), displayed: display(field, value),
} };
}) })
.compact() .compact()
.sortBy(function (field) { return field.string; }) .sortBy(function (field) { return field.string; })
@ -1063,7 +1063,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
string: field.string, string: field.string,
value: value, value: value,
displayed: display(field, value), displayed: display(field, value),
} };
}) })
.value(); .value();
@ -1429,7 +1429,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
row_cols = cols; row_cols = cols;
} else if (tagName==='group') { } else if (tagName==='group') {
// When <group> <group/><group/> </group>, we need a spacing between the two groups // When <group> <group/><group/> </group>, we need a spacing between the two groups
$td.addClass('oe_group_right') $td.addClass('oe_group_right');
} }
row_cols -= colspan; row_cols -= colspan;
@ -1714,7 +1714,7 @@ instance.web.form.compute_domain = function(expr, fields) {
}; };
instance.web.form.is_bin_size = function(v) { instance.web.form.is_bin_size = function(v) {
return /^\d+(\.\d*)? \w+$/.test(v); return (/^\d+(\.\d*)? \w+$/).test(v);
}; };
/** /**
@ -1852,7 +1852,8 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
return QWeb.render(template, { return QWeb.render(template, {
debug: instance.session.debug, debug: instance.session.debug,
widget: widget widget: widget
})}, });
},
gravity: $.fn.tipsy.autoBounds(50, 'nw'), gravity: $.fn.tipsy.autoBounds(50, 'nw'),
html: true, html: true,
opacity: 0.85, opacity: 0.85,
@ -2073,7 +2074,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
* @param node * @param node
*/ */
init: function(field_manager, node) { init: function(field_manager, node) {
var self = this var self = this;
this._super(field_manager, node); this._super(field_manager, node);
this.name = this.node.attrs.name; this.name = this.node.attrs.name;
this.field = this.field_manager.get_field_desc(this.name); this.field = this.field_manager.get_field_desc(this.name);
@ -2642,7 +2643,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
if (! this.get("effective_readonly")) { if (! this.get("effective_readonly")) {
var show_value = instance.web.format_value(this.get('value'), this, ''); var show_value = instance.web.format_value(this.get('value'), this, '');
if (show_value === '') { if (show_value === '') {
this.$textarea.css('height', parseInt(this.default_height)+"px"); this.$textarea.css('height', parseInt(this.default_height, 10)+"px");
} }
this.$textarea.val(show_value); this.$textarea.val(show_value);
if (! this.auto_sized) { if (! this.auto_sized) {
@ -2937,7 +2938,7 @@ instance.web.form.FieldRadio = instance.web.form.AbstractField.extend(instance.w
set_value: function (value_) { set_value: function (value_) {
if (value_) { if (value_) {
if (this.field.type == "selection") { if (this.field.type == "selection") {
value_ = _.find(this.field.selection, function (sel) { return sel[0] == value_}); value_ = _.find(this.field.selection, function (sel) { return sel[0] == value_;});
} }
else if (!this.selection.length) { else if (!this.selection.length) {
this.selection = [value_]; this.selection = [value_];
@ -2954,7 +2955,7 @@ instance.web.form.FieldRadio = instance.web.form.AbstractField.extend(instance.w
this.$el.toggleClass("oe_readonly", this.get('effective_readonly')); this.$el.toggleClass("oe_readonly", this.get('effective_readonly'));
this.$("input:checked").prop("checked", false); this.$("input:checked").prop("checked", false);
if (this.get_value()) { if (this.get_value()) {
this.$("input").filter(function () {return this.value == self.get_value()}).prop("checked", true); this.$("input").filter(function () {return this.value == self.get_value();}).prop("checked", true);
this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : ""); this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : "");
} }
} }
@ -3090,7 +3091,7 @@ instance.web.form.CompletionFieldMixin = {
self.field.relation, self.field.relation,
{ {
title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string, title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string,
initial_ids: ids ? _.map(ids, function(x) {return x[0]}) : undefined, initial_ids: ids ? _.map(ids, function(x) {return x[0];}) : undefined,
initial_view: view, initial_view: view,
disable_multiple_selection: true disable_multiple_selection: true
}, },
@ -3265,7 +3266,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
this.$input.keydown(input_changed); this.$input.keydown(input_changed);
this.$input.change(input_changed); this.$input.change(input_changed);
this.$drop_down.click(function() { this.$drop_down.click(function() {
self.$input.focus(); self.$input.focus();
if (self.$input.autocomplete("widget").is(":visible")) { if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close"); self.$input.autocomplete("close");
} else { } else {
@ -3455,7 +3456,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
var self = this; var self = this;
if (value_ instanceof Array) { if (value_ instanceof Array) {
this.display_value = {}; this.display_value = {};
this.display_value_backup = {} this.display_value_backup = {};
if (! this.options.always_reload) { if (! this.options.always_reload) {
this.display_value["" + value_[0]] = value_[1]; this.display_value["" + value_[0]] = value_[1];
} }
@ -3720,7 +3721,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}); });
controller.on('pager_action_executed',self,self.save_any_view); controller.on('pager_action_executed',self,self.save_any_view);
} else if (view_type == "graph") { } else if (view_type == "graph") {
self.reload_current_view() self.reload_current_view();
} }
def.resolve(); def.resolve();
}); });
@ -3740,7 +3741,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
}, },
reload_current_view: function() { reload_current_view: function() {
var self = this; var self = this;
return self.is_loaded = self.is_loaded.then(function() { self.is_loaded = self.is_loaded.then(function() {
var active_view = self.viewmanager.active_view; var active_view = self.viewmanager.active_view;
var view = self.viewmanager.views[active_view].controller; var view = self.viewmanager.views[active_view].controller;
if(active_view === "list") { if(active_view === "list") {
@ -3758,13 +3759,15 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
return view.do_search(self.build_domain(), self.dataset.get_context(), []); return view.do_search(self.build_domain(), self.dataset.get_context(), []);
} }
}, undefined); }, undefined);
return self.is_loaded;
}, },
set_value: function(value_) { set_value: function(value_) {
value_ = value_ || []; value_ = value_ || [];
var self = this; var self = this;
this.dataset.reset_ids([]); this.dataset.reset_ids([]);
var ids;
if(value_.length >= 1 && value_[0] instanceof Array) { if(value_.length >= 1 && value_[0] instanceof Array) {
var ids = []; ids = [];
_.each(value_, function(command) { _.each(value_, function(command) {
var obj = {values: command[2]}; var obj = {values: command[2]};
switch (command[0]) { switch (command[0]) {
@ -3795,7 +3798,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this._super(ids); this._super(ids);
this.dataset.set_ids(ids); this.dataset.set_ids(ids);
} else if (value_.length >= 1 && typeof(value_[0]) === "object") { } else if (value_.length >= 1 && typeof(value_[0]) === "object") {
var ids = []; ids = [];
this.dataset.delete_all = true; this.dataset.delete_all = true;
_.each(value_, function(command) { _.each(value_, function(command) {
var obj = {values: command}; var obj = {values: command};
@ -3850,7 +3853,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.viewmanager.views[this.viewmanager.active_view].controller) { this.viewmanager.views[this.viewmanager.active_view].controller) {
var view = this.viewmanager.views[this.viewmanager.active_view].controller; var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") { if (this.viewmanager.active_view === "form") {
if (!view.is_initialized.state() === 'resolved') { if (view.is_initialized.state() !== 'resolved') {
return $.when(false); return $.when(false);
} }
return $.when(view.save()); return $.when(view.save());
@ -3870,7 +3873,6 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
.invoke('is_valid') .invoke('is_valid')
.all(_.identity) .all(_.identity)
.value(); .value();
break;
case 'list': case 'list':
return view.is_valid(); return view.is_valid();
} }
@ -3962,7 +3964,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
var form = editor.form; var form = editor.form;
// If no edition is pending, the listview can not be invalid (?) // If no edition is pending, the listview can not be invalid (?)
if (!editor.record) { if (!editor.record) {
return true return true;
} }
// If the form has not been modified, the view can only be valid // If the form has not been modified, the view can only be valid
// NB: is_dirty will also be set on defaults/onchanges/whatever? // NB: is_dirty will also be set on defaults/onchanges/whatever?
@ -4167,7 +4169,7 @@ instance.web.form.One2ManyList = instance.web.ListView.List.extend({
if ($padding.length) { if ($padding.length) {
$padding.before($newrow); $padding.before($newrow);
} else { } else {
this.$current.append($newrow) this.$current.append($newrow);
} }
} }
}); });
@ -4256,7 +4258,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
}, },
core: { core: {
onSetInputData: function(e, data) { onSetInputData: function(e, data) {
if (data == '') { if (data === '') {
this._plugins.autocomplete._suggestions = null; this._plugins.autocomplete._suggestions = null;
} }
this.input().val(data); this.input().val(data);
@ -4578,8 +4580,9 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
if (type !== "form") if (type !== "form")
return; return;
var self = this; var self = this;
var pop;
if (this.dataset.index === null) { if (this.dataset.index === null) {
var pop = new instance.web.form.SelectCreatePopup(this); pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element( pop.select_element(
this.field.relation, this.field.relation,
{ {
@ -4599,7 +4602,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
}); });
} else { } else {
var id = self.dataset.ids[self.dataset.index]; var id = self.dataset.ids[self.dataset.index];
var pop = new instance.web.form.FormOpenPopup(this); pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(self.field.relation, id, self.build_context(), { pop.show_element(self.field.relation, id, self.build_context(), {
title: _t("Open: ") + self.string, title: _t("Open: ") + self.string,
write_function: function(id, data, options) { write_function: function(id, data, options) {
@ -4935,7 +4938,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.select_elements(self.selected_ids); self.select_elements(self.selected_ids);
self.destroy(); self.destroy();
}); });
var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-create"); $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-create");
$cbutton.click(function() { $cbutton.click(function() {
self.new_object(); self.new_object();
}); });
@ -5020,8 +5023,8 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
this.selection.on("change:value", this, this.on_selection_changed); this.selection.on("change:value", this, this.on_selection_changed);
this.selection.appendTo(this.$(".oe_form_view_reference_selection")); this.selection.appendTo(this.$(".oe_form_view_reference_selection"));
this.selection this.selection
.on('focused', null, function () {self.trigger('focused')}) .on('focused', null, function () {self.trigger('focused');})
.on('blurred', null, function () {self.trigger('blurred')}); .on('blurred', null, function () {self.trigger('blurred');});
this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: { this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
name: 'm2o', name: 'm2o',
@ -5030,8 +5033,8 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
this.m2o.on("change:value", this, this.data_changed); this.m2o.on("change:value", this, this.data_changed);
this.m2o.appendTo(this.$(".oe_form_view_reference_m2o")); this.m2o.appendTo(this.$(".oe_form_view_reference_m2o"));
this.m2o this.m2o
.on('focused', null, function () {self.trigger('focused')}) .on('focused', null, function () {self.trigger('focused');})
.on('blurred', null, function () {self.trigger('blurred')}); .on('blurred', null, function () {self.trigger('blurred');});
}, },
on_selection_changed: function() { on_selection_changed: function() {
if (this.reference_ready) { if (this.reference_ready) {
@ -5190,18 +5193,18 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
} }
}, },
render_value: function() { render_value: function() {
var show_value;
if (!this.get("effective_readonly")) { if (!this.get("effective_readonly")) {
var show_value;
if (this.node.attrs.filename) { if (this.node.attrs.filename) {
show_value = this.view.datarecord[this.node.attrs.filename] || ''; show_value = this.view.datarecord[this.node.attrs.filename] || '';
} else { } else {
show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : ''; show_value = (this.get('value') !== null && this.get('value') !== undefined && this.get('value') !== false) ? this.get('value') : '';
} }
this.$el.find('input').eq(0).val(show_value); this.$el.find('input').eq(0).val(show_value);
} else { } else {
this.$el.find('a').toggle(!!this.get('value')); this.$el.find('a').toggle(!!this.get('value'));
if (this.get('value')) { if (this.get('value')) {
var show_value = _t("Download") show_value = _t("Download");
if (this.view) if (this.view)
show_value += " " + (this.view.datarecord[this.node.attrs.filename] || ''); show_value += " " + (this.view.datarecord[this.node.attrs.filename] || '');
this.$el.find('a').text(show_value); this.$el.find('a').text(show_value);
@ -5420,7 +5423,7 @@ instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractFie
values.push(result.id); values.push(result.id);
this.set({'value': values}); this.set({'value': values});
} }
this.render_value() this.render_value();
}, },
on_file_delete: function (event) { on_file_delete: function (event) {
event.stopPropagation(); event.stopPropagation();
@ -5474,7 +5477,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
var self = this; var self = this;
var content = QWeb.render("FieldStatus.content", { var content = QWeb.render("FieldStatus.content", {
'widget': self, 'widget': self,
'value_folded': _.find(self.selection.folded, function(i){return i[0] === self.get('value')}) 'value_folded': _.find(self.selection.folded, function(i){return i[0] === self.get('value');})
}); });
self.$el.html(content); self.$el.html(content);
}, },
@ -5506,7 +5509,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
return new instance.web.DataSetSearch(self, self.field.relation, self.build_context(), self.get("evaluated_selection_domain")) return new instance.web.DataSetSearch(self, self.field.relation, self.build_context(), self.get("evaluated_selection_domain"))
.read_slice(fields.fold ? ['fold'] : ['id'], {}).then(function (records) { .read_slice(fields.fold ? ['fold'] : ['id'], {}).then(function (records) {
var ids = _.map(records, function (val) {return val.id}); var ids = _.map(records, function (val) {return val.id;});
return self.dataset.name_get(ids).then(function (records_name) { return self.dataset.name_get(ids).then(function (records_name) {
_.each(records, function (record) { _.each(records, function (record) {
var name = _.find(records_name, function (val) {return val[0] == record.id;})[1]; var name = _.find(records_name, function (val) {return val[0] == record.id;})[1];
@ -5516,7 +5519,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
selection_unfolded.push([record.id, name]); selection_unfolded.push([record.id, name]);
} }
}); });
}) });
}); });
}); });
} else { } else {
@ -5548,16 +5551,17 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
return new instance.web.Model(self.field.relation).call("fields_get", [["fold"]]).then(function(fields) { return new instance.web.Model(self.field.relation).call("fields_get", [["fold"]]).then(function(fields) {
self.distant_fields = fields; self.distant_fields = fields;
return fields; return fields;
}) });
}, },
on_click_stage: function (ev) { on_click_stage: function (ev) {
var self = this; var self = this;
var $li = $(ev.currentTarget); var $li = $(ev.currentTarget);
var val;
if (this.field.type == "many2one") { if (this.field.type == "many2one") {
var val = parseInt($li.data("id")); val = parseInt($li.data("id"), 10);
} }
else { else {
var val = $li.data("id"); val = $li.data("id");
} }
if (val != self.get('value')) { if (val != self.get('value')) {
this.view.recursive_save().done(function() { this.view.recursive_save().done(function() {

View File

@ -171,11 +171,13 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
current_date: new Date().toString('yyyy-MM-dd') current_date: new Date().toString('yyyy-MM-dd')
// TODO: time, datetime, relativedelta // TODO: time, datetime, relativedelta
}); });
var i;
var pair;
var expression;
if (this.fonts) { if (this.fonts) {
for(var i=0, len=this.fonts.length; i<len; ++i) { for(i=0, len=this.fonts.length; i<len; ++i) {
var pair = this.fonts[i], pair = this.fonts[i];
font = pair[0], var font = pair[0];
expression = pair[1]; expression = pair[1];
if (py.evaluate(expression, context).toJSON()) { if (py.evaluate(expression, context).toJSON()) {
switch(font) { switch(font) {
@ -194,10 +196,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
} }
if (!this.colors) { return style; } if (!this.colors) { return style; }
for(var i=0, len=this.colors.length; i<len; ++i) { for(i=0, len=this.colors.length; i<len; ++i) {
var pair = this.colors[i], pair = this.colors[i];
color = pair[0], var color = pair[0];
expression = pair[1]; expression = pair[1];
if (py.evaluate(expression, context).toJSON()) { if (py.evaluate(expression, context).toJSON()) {
return style += 'color: ' + color + ';'; return style += 'color: ' + color + ';';
} }
@ -361,7 +363,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
sort_by_column: function (e) { sort_by_column: function (e) {
e.stopPropagation(); e.stopPropagation();
var $column = $(e.currentTarget); var $column = $(e.currentTarget);
var col_name = $column.data('id') var col_name = $column.data('id');
var field = this.fields_view.fields[col_name]; var field = this.fields_view.fields[col_name];
// test if the field is a function field with store=false, since it's impossible // test if the field is a function field with store=false, since it's impossible
// for the server to sort those fields we desactivate the feature // for the server to sort those fields we desactivate the feature
@ -394,7 +396,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
var total = dataset.size(); var total = dataset.size();
var limit = this.limit() || total; var limit = this.limit() || total;
if (total == 0) if (total === 0)
this.$pager.hide(); this.$pager.hide();
else else
this.$pager.css("display", ""); this.$pager.css("display", "");
@ -454,7 +456,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {String} [view="page"] the view type to switch to * @param {String} [view="page"] the view type to switch to
*/ */
select_record:function (index, view) { select_record:function (index, view) {
view = view || index == null ? 'form' : 'form'; view = view || index === null || index === undefined ? 'form' : 'form';
this.dataset.index = index; this.dataset.index = index;
_.delay(_.bind(function () { _.delay(_.bind(function () {
this.do_switch_view(view); this.do_switch_view(view);
@ -503,7 +505,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
var reloaded = $.Deferred(); var reloaded = $.Deferred();
this.$el.find('.oe_list_content').append( this.$el.find('.oe_list_content').append(
this.groups.render(function () { this.groups.render(function () {
if (self.dataset.index == null && self.records.length || if ((self.dataset.index === null || self.dataset.index === undefined) && self.records.length ||
self.dataset.index >= self.records.length) { self.dataset.index >= self.records.length) {
self.dataset.index = 0; self.dataset.index = 0;
} }
@ -1459,7 +1461,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
d = new $.Deferred(), d = new $.Deferred(),
page = this.datagroup.openable ? this.page : view.page; page = this.datagroup.openable ? this.page : view.page;
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'); var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name');
var options = { offset: page * limit, limit: limit, context: {bin_size: true} }; var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
//TODO xmo: investigate why we need to put the setTimeout //TODO xmo: investigate why we need to put the setTimeout
$.async_when().done(function() { $.async_when().done(function() {
@ -1548,20 +1550,21 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
// if drag to 1st row (to = 0), start sequencing from 0 // if drag to 1st row (to = 0), start sequencing from 0
// (exclusive lower bound) // (exclusive lower bound)
seq = to ? list.records.at(to - 1).get(seqname) : 0; seq = to ? list.records.at(to - 1).get(seqname) : 0;
while (++seq, record = list.records.at(index++)) { var fct = function (dataset, id, seq) {
$.async_when().done(function () {
var attrs = {};
attrs[seqname] = seq;
dataset.write(id, attrs);
});
};
while (++seq, (record = list.records.at(index++))) {
// write are independent from one another, so we can just // write are independent from one another, so we can just
// launch them all at the same time and we don't really // launch them all at the same time and we don't really
// give a fig about when they're done // give a fig about when they're done
// FIXME: breaks on o2ms (e.g. Accounting > Financial // FIXME: breaks on o2ms (e.g. Accounting > Financial
// Accounting > Taxes > Taxes, child tax accounts) // Accounting > Taxes > Taxes, child tax accounts)
// when synchronous (without setTimeout) // when synchronous (without setTimeout)
(function (dataset, id, seq) { fct(dataset, record.get('id'), seq);
$.async_when().done(function () {
var attrs = {};
attrs[seqname] = seq;
dataset.write(id, attrs);
});
}(dataset, record.get('id'), seq));
record.set(seqname, seq); record.set(seqname, seq);
} }
} }
@ -1574,7 +1577,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
this.datagroup.list( this.datagroup.list(
_(this.view.visible_columns).chain() _(this.view.visible_columns).chain()
.filter(function (column) { return column.tag === 'field' }) .filter(function (column) { return column.tag === 'field';})
.pluck('name').value(), .pluck('name').value(),
function (groups) { function (groups) {
$el[0].appendChild( $el[0].appendChild(
@ -1619,7 +1622,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
return { return {
count: this.datagroup.length, count: this.datagroup.length,
values: this.datagroup.aggregates values: this.datagroup.aggregates
} };
} }
return _(this.children).chain() return _(this.children).chain()
.map(function (child) { .map(function (child) {
@ -1856,7 +1859,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
var instance_ = (records[i] instanceof Record) ? records[i] : new Record(records[i]); var instance_ = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
instance_.bind(null, this._onRecordEvent); instance_.bind(null, this._onRecordEvent);
this._byId[instance_.get('id')] = instance_; this._byId[instance_.get('id')] = instance_;
if (options.at == undefined) { if (options.at === undefined || options.at === null) {
this.records.push(instance_); this.records.push(instance_);
if (!options.silent) { if (!options.silent) {
this.trigger('add', this, instance_, this.records.length-1); this.trigger('add', this, instance_, this.records.length-1);
@ -1898,7 +1901,8 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
if (!_(this._proxies).isEmpty()) { if (!_(this._proxies).isEmpty()) {
var record = null; var record = null;
_(this._proxies).detect(function (proxy) { _(this._proxies).detect(function (proxy) {
return record = proxy.get(id); record = proxy.get(id);
return record;
}); });
return record; return record;
} }
@ -1912,10 +1916,11 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
* @returns {Collection} * @returns {Collection}
*/ */
proxy: function (section) { proxy: function (section) {
return this._proxies[section] = new Collection(null, { this._proxies[section] = new Collection(null, {
parent: this, parent: this,
key: section key: section
}).bind(null, this._onRecordEvent); }).bind(null, this._onRecordEvent);
return this._proxies[section];
}, },
/** /**
* @param {Array} [records] * @param {Array} [records]
@ -1985,7 +1990,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
var record; var record;
for(var section in this._proxies) { for(var section in this._proxies) {
if (!this._proxies.hasOwnProperty(section)) { if (!this._proxies.hasOwnProperty(section)) {
continue continue;
} }
if ((record = this._proxies[section].find(callback))) { if ((record = this._proxies[section].find(callback))) {
return record; return record;
@ -2102,7 +2107,7 @@ instance.web.list.columns.for_ = function (id, field, node) {
tag + '.'+ description.type, tag + '.'+ description.type,
tag tag
]); ]);
return new Type(id, node.tag, description) return new Type(id, node.tag, description);
}; };
instance.web.list.Column = instance.web.Class.extend({ instance.web.list.Column = instance.web.Class.extend({
@ -2265,7 +2270,7 @@ instance.web.list.Char = instance.web.list.Column.extend({
_format: function (row_data, options) { _format: function (row_data, options) {
var value = row_data[this.id].value; var value = row_data[this.id].value;
if (value && this.password === 'True') { if (value && this.password === 'True') {
return value.replace(/[\s\S]/g, _.escape(this.replacement)) return value.replace(/[\s\S]/g, _.escape(this.replacement));
} }
return this._super(row_data, options); return this._super(row_data, options);
} }

View File

@ -202,7 +202,7 @@ openerp.web.list_editable = function (instance) {
make_empty_record: function (id) { make_empty_record: function (id) {
var attrs = {id: id}; var attrs = {id: id};
_(this.columns).chain() _(this.columns).chain()
.filter(function (x) { return x.tag === 'field'}) .filter(function (x) { return x.tag === 'field';})
.pluck('name') .pluck('name')
.each(function (field) { attrs[field] = false; }); .each(function (field) { attrs[field] = false; });
return new instance.web.list.Record(attrs); return new instance.web.list.Record(attrs);
@ -260,7 +260,7 @@ openerp.web.list_editable = function (instance) {
get_cells_for: function ($row) { get_cells_for: function ($row) {
var cells = {}; var cells = {};
$row.children('td').each(function (index, el) { $row.children('td').each(function (index, el) {
cells[el.getAttribute('data-field')] = el cells[el.getAttribute('data-field')] = el;
}); });
return cells; return cells;
}, },
@ -346,7 +346,7 @@ openerp.web.list_editable = function (instance) {
var record = self.records.get(attrs.id); var record = self.records.get(attrs.id);
if (!record) { if (!record) {
// Record removed by third party during edition // Record removed by third party during edition
return return;
} }
return self.reload_record(record); return self.reload_record(record);
} }
@ -515,10 +515,6 @@ openerp.web.list_editable = function (instance) {
}; };
} else if (document.body.createTextRange) { } else if (document.body.createTextRange) {
throw new Error("Implement text range handling for MSIE"); throw new Error("Implement text range handling for MSIE");
var sel = document.body.createTextRange();
if (sel.parentElement() === el) {
}
} }
// Element without selection ranges (select, div/@contenteditable) // Element without selection ranges (select, div/@contenteditable)
return null; return null;
@ -695,9 +691,9 @@ openerp.web.list_editable = function (instance) {
var arch = edition_view.arch; var arch = edition_view.arch;
if (!(arch && arch.children instanceof Array)) { if (!(arch && arch.children instanceof Array)) {
throw new Error("Editor delegate's #edition_view must have a" + throw new Error("Editor delegate's #edition_view must have a" +
" non-empty arch") " non-empty arch");
} }
if (!(arch.tag === "form")) { if (arch.tag !== "form") {
throw new Error("Editor delegate's #edition_view must have a" + throw new Error("Editor delegate's #edition_view must have a" +
" 'form' root node"); " 'form' root node");
} }

View File

@ -49,7 +49,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
if (last) { if (last) {
last.hide(); last.hide();
} }
var item = _.extend({ item = _.extend({
show: function(index) { show: function(index) {
this.widget.$el.show(); this.widget.$el.show();
}, },
@ -423,7 +423,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
if (!(ClientWidget.prototype instanceof instance.web.Widget)) { if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
var next; var next;
if (next = ClientWidget(this, action)) { if ((next = ClientWidget(this, action))) {
return this.do_action(next, options); return this.do_action(next, options);
} }
return $.when(); return $.when();
@ -459,7 +459,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
action_id: action.id, action_id: action.id,
context: action.context || {} context: action.context || {}
}).done(function (action) { }).done(function (action) {
self.do_action(action, options) self.do_action(action, options);
}); });
}, },
ir_actions_report_xml: function(action, options) { ir_actions_report_xml: function(action, options) {
@ -478,8 +478,8 @@ instance.web.ActionManager = instance.web.Widget.extend({
var params = { var params = {
action: JSON.stringify(action), action: JSON.stringify(action),
token: new Date().getTime() token: new Date().getTime()
} };
var url = self.session.url('/web/report', params) var url = self.session.url('/web/report', params);
instance.web.unblockUI(); instance.web.unblockUI();
$('<a href="'+url+'" target="_blank"></a>')[0].click(); $('<a href="'+url+'" target="_blank"></a>')[0].click();
return; return;
@ -502,7 +502,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
c.rpc_error.apply(c, arguments); c.rpc_error.apply(c, arguments);
d.reject(); d.reject();
} }
}) });
}); });
}); });
}, },
@ -677,7 +677,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.$el.find('.oe_view_title_text:first').text(title); this.$el.find('.oe_view_title_text:first').text(title);
}, },
add_breadcrumb: function(options) { add_breadcrumb: function(options) {
var options = options || {}; options = options || {};
var self = this; var self = this;
var views = [this.active_view || this.views_src[0].view_type]; var views = [this.active_view || this.views_src[0].view_type];
this.on('switch_mode', self, function(mode) { this.on('switch_mode', self, function(mode) {
@ -958,7 +958,6 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
view_type : 'list', view_type : 'list',
view_mode : 'list' view_mode : 'list'
}); });
break;
case 'edit': case 'edit':
this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() }); this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() });
break; break;
@ -1000,7 +999,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
evt.currentTarget.selectedIndex = 0; evt.currentTarget.selectedIndex = 0;
}, },
do_edit_resource: function(model, id, action) { do_edit_resource: function(model, id, action) {
var action = _.extend({ action = _.extend({
res_model : model, res_model : model,
res_id : id, res_id : id,
type : 'ir.actions.act_window', type : 'ir.actions.act_window',
@ -1038,7 +1037,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
}, },
get_action_manager: function() { get_action_manager: function() {
var cur = this; var cur = this;
while (cur = cur.getParent()) { while ((cur = cur.getParent())) {
if (cur instanceof instance.web.ActionManager) { if (cur instanceof instance.web.ActionManager) {
return cur; return cur;
} }
@ -1122,7 +1121,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
self.$("[title]").tipsy({ self.$("[title]").tipsy({
'html': true, 'html': true,
'delayIn': 500, 'delayIn': 500,
}) });
}, },
/** /**
* For each item added to the section: * For each item added to the section:
@ -1166,7 +1165,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
label: items[i]['name'], label: items[i]['name'],
action: items[i], action: items[i],
classname: 'oe_sidebar_' + type classname: 'oe_sidebar_' + type
} };
} }
self.add_items(type=='print' ? 'print' : 'other', items); self.add_items(type=='print' ? 'print' : 'other', items);
} }
@ -1177,7 +1176,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) { self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
var ids = self.getParent().get_selected_ids(); var ids = self.getParent().get_selected_ids();
var domain = self.getParent().get_active_domain(); var domain = self.getParent().get_active_domain();
if (ids.length == 0) { if (ids.length === 0) {
instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true }); instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
return false; return false;
} }
@ -1435,7 +1434,7 @@ instance.web.View = instance.web.Widget.extend({
if (self.is_active()) { if (self.is_active()) {
fn.apply(self, arguments); fn.apply(self, arguments);
} }
} };
}, },
do_push_state: function(state) { do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) { if (this.getParent() && this.getParent().do_push_state) {
@ -1546,9 +1545,10 @@ instance.web.xml_to_json = function(node, strip_whitespace) {
children: _.compact(_.map(node.childNodes, function(node) { children: _.compact(_.map(node.childNodes, function(node) {
return instance.web.xml_to_json(node, strip_whitespace); return instance.web.xml_to_json(node, strip_whitespace);
})), })),
} };
} }
} };
instance.web.json_node_to_xml = function(node, human_readable, indent) { instance.web.json_node_to_xml = function(node, human_readable, indent) {
// For debugging purpose, this function will convert a json node back to xml // For debugging purpose, this function will convert a json node back to xml
indent = indent || 0; indent = indent || 0;

View File

@ -459,7 +459,7 @@
<tr> <tr>
<td class="oe_leftbar" valign="top"> <td class="oe_leftbar" valign="top">
<t t-set="debug" t-value="__debug__ ? '&amp;debug' : ''"/> <t t-set="debug" t-value="__debug__ ? '&amp;debug' : ''"/>
<a class="oe_logo" t-attf-href="/?ts=#{Date.now()}#{debug}"> <a class="oe_logo" t-attf-href="/?#{debug}">
<span class="oe_logo_edit">Edit Company data</span> <span class="oe_logo_edit">Edit Company data</span>
<img t-att-src='widget.session.url("/web/binary/company_logo")'/> <img t-att-src='widget.session.url("/web/binary/company_logo")'/>
</a> </a>

View File

@ -7,7 +7,7 @@ openerp.testing.section('Widget.proxy', {
this.executed = true; this.executed = true;
} }
}); });
var w = new W; var w = new W();
var fn = w.proxy('exec'); var fn = w.proxy('exec');
fn(); fn();
ok(w.executed, 'should execute the named method in the right context'); ok(w.executed, 'should execute the named method in the right context');
@ -18,7 +18,7 @@ openerp.testing.section('Widget.proxy', {
this.executed = arg; this.executed = arg;
} }
}); });
var w = new W; var w = new W();
var fn = w.proxy('exec'); var fn = w.proxy('exec');
fn(42); fn(42);
ok(w.executed, "should execute the named method in the right context"); ok(w.executed, "should execute the named method in the right context");
@ -32,7 +32,7 @@ openerp.testing.section('Widget.proxy', {
this.executed = 1; this.executed = 1;
} }
}); });
var w = new W; var w = new W();
var fn = w.proxy('exec'); var fn = w.proxy('exec');
W.include({ W.include({
exec: function () { this.executed = 2; } exec: function () { this.executed = 2; }
@ -43,14 +43,14 @@ openerp.testing.section('Widget.proxy', {
}); });
test('(Function)', function (instance) { test('(Function)', function (instance) {
var w = new (instance.web.Widget.extend({ })); var w = new (instance.web.Widget.extend({ }))();
var fn = w.proxy(function () { this.executed = true; }); var fn = w.proxy(function () { this.executed = true; });
fn(); fn();
ok(w.executed, "should set the function's context (like Function#bind)"); ok(w.executed, "should set the function's context (like Function#bind)");
}); });
test('(Function)(*args)', function (instance) { test('(Function)(*args)', function (instance) {
var w = new (instance.web.Widget.extend({ })); var w = new (instance.web.Widget.extend({ }))();
var fn = w.proxy(function (arg) { this.executed = arg; }); var fn = w.proxy(function (arg) { this.executed = arg; });
fn(42); fn(42);
@ -79,7 +79,7 @@ openerp.testing.section('Widget.renderElement', {
} }
}, function (test) { }, function (test) {
test('no template, default', function (instance) { test('no template, default', function (instance) {
var w = new (instance.web.Widget.extend({ })); var w = new (instance.web.Widget.extend({ }))();
var $original = w.$el; var $original = w.$el;
ok($original, "should initially have a root element"); ok($original, "should initially have a root element");
@ -96,7 +96,7 @@ openerp.testing.section('Widget.renderElement', {
test('no template, custom tag', function (instance) { test('no template, custom tag', function (instance) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
tagName: 'ul' tagName: 'ul'
})); }))();
w.renderElement(); w.renderElement();
equal(w.el.nodeName, 'UL', "should have generated the custom element tag"); equal(w.el.nodeName, 'UL', "should have generated the custom element tag");
@ -104,7 +104,7 @@ openerp.testing.section('Widget.renderElement', {
test('no template, @id', function (instance) { test('no template, @id', function (instance) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
id: 'foo' id: 'foo'
})); }))();
w.renderElement(); w.renderElement();
equal(w.el.attributes.length, 1, "should have one attribute"); equal(w.el.attributes.length, 1, "should have one attribute");
@ -114,7 +114,7 @@ openerp.testing.section('Widget.renderElement', {
test('no template, @className', function (instance) { test('no template, @className', function (instance) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
className: 'oe_some_class' className: 'oe_some_class'
})); }))();
w.renderElement(); w.renderElement();
equal(w.el.className, 'oe_some_class', "should have the right property"); equal(w.el.className, 'oe_some_class', "should have the right property");
@ -129,7 +129,7 @@ openerp.testing.section('Widget.renderElement', {
'clark': 'gable', 'clark': 'gable',
'spoiler': 'snape kills dumbledore' 'spoiler': 'snape kills dumbledore'
} }
})); }))();
w.renderElement(); w.renderElement();
equal(w.el.attributes.length, 5, "should have all the specified attributes"); equal(w.el.attributes.length, 5, "should have all the specified attributes");
@ -150,7 +150,7 @@ openerp.testing.section('Widget.renderElement', {
test('template', function (instance) { test('template', function (instance) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
template: 'test.widget.template' template: 'test.widget.template'
})); }))();
w.renderElement(); w.renderElement();
equal(w.el.nodeName, 'OL'); equal(w.el.nodeName, 'OL');
@ -160,7 +160,7 @@ openerp.testing.section('Widget.renderElement', {
test('repeated', { asserts: 4 }, function (instance, $fix) { test('repeated', { asserts: 4 }, function (instance, $fix) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
template: 'test.widget.template-value' template: 'test.widget.template-value'
})); }))();
w.value = 42; w.value = 42;
return w.appendTo($fix) return w.appendTo($fix)
.done(function () { .done(function () {
@ -194,7 +194,7 @@ openerp.testing.section('Widget.$', {
test('basic-alias', function (instance) { test('basic-alias', function (instance) {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
template: 'test.widget.template' template: 'test.widget.template'
})); }))();
w.renderElement(); w.renderElement();
ok(w.$('li:eq(3)').is(w.$el.find('li:eq(3)')), ok(w.$('li:eq(3)').is(w.$el.find('li:eq(3)')),
@ -226,13 +226,13 @@ openerp.testing.section('Widget.events', {
events: { events: {
'click': function () { 'click': function () {
a[0] = true; a[0] = true;
strictEqual(this, w, "should trigger events in widget") strictEqual(this, w, "should trigger events in widget");
}, },
'click li.class-3': 'class3', 'click li.class-3': 'class3',
'change input': function () { a[2] = true; } 'change input': function () { a[2] = true; }
}, },
class3: function () { a[1] = true; } class3: function () { a[1] = true; }
})); }))();
w.renderElement(); w.renderElement();
w.$el.click(); w.$el.click();
@ -248,9 +248,9 @@ openerp.testing.section('Widget.events', {
var w = new (instance.web.Widget.extend({ var w = new (instance.web.Widget.extend({
template: 'test.widget.template', template: 'test.widget.template',
events: { 'click li': function () { clicked = true; } } events: { 'click li': function () { clicked = true; } }
})); }))();
w.renderElement(); w.renderElement();
w.$el.on('click', 'li', function () { newclicked = true }); w.$el.on('click', 'li', function () { newclicked = true; });
w.$('li').click(); w.$('li').click();
ok(clicked, "should trigger bound events"); ok(clicked, "should trigger bound events");

View File

@ -0,0 +1,76 @@
openerp.testing.section('data.model.group_by', {
rpc: 'mock',
dependencies: ['web.data'],
}, function (test) {
var group_result = [{
bar: 3, bar_count: 5, __context: {}, __domain: [['bar', '=', 3]],
}, {
bar: 5, bar_count: 3, __context: {}, __domain: [['bar', '=', 5]],
}, {
bar: 8, bar_count: 0, __context: {}, __domain: [['bar', '=', 8]],
}];
test('basic', {asserts: 7}, function (instance, $fix, mock) {
var m = new instance.web.Model('foo');
mock('foo:read_group', function (args, kwargs) {
deepEqual(kwargs.fields, ['bar'],
"should read grouping field");
deepEqual(kwargs.groupby, ['bar'],
"should have single grouping field");
return group_result;
});
mock('/web/dataset/search_read', function (args) {
deepEqual(args.params.domain, [['bar', '=', 3]],
"should have domain matching that of group_by result");
return {records: [
{bar: 3, id: 1},
{bar: 3, id: 2},
{bar: 3, id: 4},
{bar: 3, id: 8},
{bar: 3, id: 16}
], length: 5};
});
return m.query().group_by('bar')
.then(function (groups) {
ok(groups, "should have data");
equal(groups.length, 3, "should have three results");
var first = groups[0];
ok(first.attributes.has_children, "should have children");
return first.query().all();
}).done(function (first) {
equal(first.length, 5, "should have 5 records");
});
});
test('noleaf', {asserts: 5}, function (instance, $fix, mock) {
var m = new instance.web.Model('foo', {group_by_no_leaf: true});
mock('foo:read_group', function (args, kwargs) {
deepEqual(kwargs.fields, ['bar'],
"should read grouping field");
deepEqual(kwargs.groupby, ['bar'],
"should have single grouping field");
return group_result;
});
return m.query().group_by('bar')
.then(function (groups) {
ok(groups, "should have data");
equal(groups.length, 3, "should have three results");
ok(!groups[0].attributes.has_children,
"should not have children because no_leaf");
});
});
test('nogroup', {rpc: false}, function (instance, $f, mock) {
var m = new instance.web.Model('foo');
strictEqual(m.query().group_by(), null, "should not group");
});
test('empty.noleaf', {asserts: 1}, function (instance, $f, mock) {
var m = new instance.web.Model('foo', {group_by_no_leaf: true});
mock('foo:read_group', function (args, kwargs) {
return [{__context: [], __domain: []}];
});
return m.query().group_by().done(function (groups) {
strictEqual(groups.length, 1,
"should generate a single fake-ish group");
});
});
});

View File

@ -9,9 +9,9 @@ openerp.testing.section('eval.types', {
return function (expr, func, message) { return function (expr, func, message) {
// evaluate expr between two calls to new Date(), and check that // evaluate expr between two calls to new Date(), and check that
// the result is between the transformed dates // the result is between the transformed dates
var d0 = new Date; var d0 = new Date();
var result = py.eval(expr, context); var result = py.eval(expr, context);
var d1 = new Date; var d1 = new Date();
ok(func(d0) <= result && result <= func(d1), message); ok(func(d0) <= result && result <= func(d1), message);
}; };
}; };
@ -118,7 +118,7 @@ openerp.testing.section('eval.types', {
// Issue #11576 // Issue #11576
eq('td(999999999, 86399, 999999) - td(999999999, 86399, 999998)', 'td(0, 0, 1)'); eq('td(999999999, 86399, 999999) - td(999999999, 86399, 999998)', 'td(0, 0, 1)');
eq('td(999999999, 1, 1) - td(999999999, 1, 0)', eq('td(999999999, 1, 1) - td(999999999, 1, 0)',
'td(0, 0, 1)') 'td(0, 0, 1)');
}); });
test('timedelta.test_basic_attributes', function (instance) { test('timedelta.test_basic_attributes', function (instance) {
var ctx = instance.web.pyeval.context(); var ctx = instance.web.pyeval.context();
@ -264,7 +264,7 @@ openerp.testing.section('eval.types', {
py.eval("(datetime.date(2012, 2, 15) + relativedelta(days=-1)).strftime('%Y-%m-%d 23:59:59')", py.eval("(datetime.date(2012, 2, 15) + relativedelta(days=-1)).strftime('%Y-%m-%d 23:59:59')",
instance.web.pyeval.context()), instance.web.pyeval.context()),
"2012-02-14 23:59:59"); "2012-02-14 23:59:59");
}) });
}); });
openerp.testing.section('eval.edc', { openerp.testing.section('eval.edc', {
dependencies: ['web.data'], dependencies: ['web.data'],

View File

@ -126,8 +126,8 @@ openerp.testing.section('web-formats', {
var str = "134,112.1234"; var str = "134,112.1234";
var val = instance.web.parse_value(str, {type:"float"}); var val = instance.web.parse_value(str, {type:"float"});
equal(val, 134112.1234); equal(val, 134112.1234);
var str = "-134,112.1234"; str = "-134,112.1234";
var val = instance.web.parse_value(str, {type:"float"}); val = instance.web.parse_value(str, {type:"float"});
equal(val, -134112.1234); equal(val, -134112.1234);
_.extend(instance.web._t.database.parameters, { _.extend(instance.web._t.database.parameters, {
decimal_point: ',', decimal_point: ',',

View File

@ -49,7 +49,7 @@ openerp.testing.section('editor', {
readonly: field.readonly readonly: field.readonly
}) })
} }
} };
}); });
return { return {
arch: { arch: {
@ -108,7 +108,7 @@ openerp.testing.section('editor', {
.done(function (record) { .done(function (record) {
ok(!e.is_editing(), "should have stopped editing"); ok(!e.is_editing(), "should have stopped editing");
equal(record.id, 42, "should have newly created id"); equal(record.id, 42, "should have newly created id");
}) });
}); });
test('toggle-edition-cancel', { asserts: 2 }, function (instance, $fix) { test('toggle-edition-cancel', { asserts: 2 }, function (instance, $fix) {
var e = new instance.web.list.Editor({ var e = new instance.web.list.Editor({
@ -131,7 +131,7 @@ openerp.testing.section('editor', {
.done(function (record) { .done(function (record) {
ok(!e.is_editing(), "should have stopped editing"); ok(!e.is_editing(), "should have stopped editing");
ok(!record.id, "should have no id"); ok(!record.id, "should have no id");
}) });
}); });
test('toggle-save-required', { test('toggle-save-required', {
asserts: 2, asserts: 2,

View File

@ -7,7 +7,7 @@ openerp.testing.section('list.events', {
} }
function Cls() {} function Cls() {}
Cls.prototype = o; Cls.prototype = o;
return new Cls; return new Cls();
}; };
test('Simple event triggering', function (instance) { test('Simple event triggering', function (instance) {
var e = create(instance.web.list.Events), passed = false; var e = create(instance.web.list.Events), passed = false;
@ -23,9 +23,9 @@ openerp.testing.section('list.events', {
}); });
test('Propagate trigger params', function (instance) { test('Propagate trigger params', function (instance) {
var e = create(instance.web.list.Events), p = false; var e = create(instance.web.list.Events), p = false;
e.bind(null, function (_, param) { p = param }); e.bind(null, function (_, param) { p = param; });
e.trigger('foo', true); e.trigger('foo', true);
strictEqual(p, true) strictEqual(p, true);
}); });
test('Bind multiple callbacks', function (instance) { test('Bind multiple callbacks', function (instance) {
var e = create(instance.web.list.Events), count; var e = create(instance.web.list.Events), count;
@ -53,7 +53,7 @@ openerp.testing.section('list.events', {
method: function () { this.trigger('e'); } method: function () { this.trigger('e'); }
}); });
cls.include(instance.web.list.Events); cls.include(instance.web.list.Events);
var i = new cls, triggered = false; var i = new cls(), triggered = false;
i.bind('e', function () { triggered = true; }); i.bind('e', function () { triggered = true; });
i.method(); i.method();
@ -97,7 +97,7 @@ openerp.testing.section('list.records', {
test('Change all the things', function (instance) { test('Change all the things', function (instance) {
var r = new instance.web.list.Record(), changed = false, field; var r = new instance.web.list.Record(), changed = false, field;
r.bind('change', function () { changed = true; }); r.bind('change', function () { changed = true; });
r.bind(null, function (e) { field = field || e.split(':')[1]}); r.bind(null, function (e) { field = field || e.split(':')[1]; });
r.set('foo', 1); r.set('foo', 1);
strictEqual(r.get('foo'), 1); strictEqual(r.get('foo'), 1);
ok(changed); ok(changed);

View File

@ -122,6 +122,6 @@ openerp.testing.section('misordered resolution managemeng', {
ok(!fail2); ok(!fail2);
done.resolve(); done.resolve();
}, 400); }, 400);
return $.when(d1, d2, done) return $.when(d1, d2, done);
}); });
}); });

View File

@ -2,7 +2,7 @@ openerp.testing.section('search.query', {
dependencies: ['web.search'] dependencies: ['web.search']
}, function (test) { }, function (test) {
test('Adding a facet to the query creates a facet and a value', function (instance) { test('Adding a facet to the query creates a facet and a value', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
var field = {}; var field = {};
query.add({ query.add({
category: 'Foo', category: 'Foo',
@ -16,7 +16,7 @@ openerp.testing.section('search.query', {
deepEqual(facet.get('values'), [{label: 'Value', value: 3}]); deepEqual(facet.get('values'), [{label: 'Value', value: 3}]);
}); });
test('Adding two facets', function (instance) { test('Adding two facets', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
query.add([ query.add([
{ category: 'Foo', field: {}, values: [{label: 'Value', value: 3}] }, { category: 'Foo', field: {}, values: [{label: 'Value', value: 3}] },
{ category: 'Bar', field: {}, values: [{label: 'Value 2', value: 4}] } { category: 'Bar', field: {}, values: [{label: 'Value 2', value: 4}] }
@ -27,7 +27,7 @@ openerp.testing.section('search.query', {
equal(query.at(1).values.length, 1); equal(query.at(1).values.length, 1);
}); });
test('If a facet already exists, add values to it', function (instance) { test('If a facet already exists, add values to it', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
var field = {}; var field = {};
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]}); query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
@ -40,18 +40,18 @@ openerp.testing.section('search.query', {
]); ]);
}); });
test('Facet being implicitly changed should trigger change, not add', function (instance) { test('Facet being implicitly changed should trigger change, not add', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
var field = {}, added = false, changed = false; var field = {}, added = false, changed = false;
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.on('add', function () { added = true; }) query.on('add', function () { added = true; })
.on('change', function () { changed = true }); .on('change', function () { changed = true; });
query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]}); query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
ok(!added, "query.add adding values to a facet should not trigger an add"); ok(!added, "query.add adding values to a facet should not trigger an add");
ok(changed, "query.add adding values to a facet should not trigger a change"); ok(changed, "query.add adding values to a facet should not trigger a change");
}); });
test('Toggling a facet, value which does not exist should add it', function (instance) { test('Toggling a facet, value which does not exist should add it', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
var field = {}; var field = {};
query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
@ -63,7 +63,7 @@ openerp.testing.section('search.query', {
}); });
test('Toggling a facet which exists with a value which does not should add the value to the facet', function (instance) { test('Toggling a facet which exists with a value which does not should add the value to the facet', function (instance) {
var field = {}; var field = {};
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]}); query.toggle({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
@ -77,7 +77,7 @@ openerp.testing.section('search.query', {
}); });
test('Toggling a facet which exists with a value which does as well should remove the value from the facet', function (instance) { test('Toggling a facet which exists with a value which does as well should remove the value from the facet', function (instance) {
var field = {}; var field = {};
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]}); query.add({category: 'A', field: field, values: [{label: 'V2', value: 1}]});
@ -92,7 +92,7 @@ openerp.testing.section('search.query', {
}); });
test('Toggling off the last value of a facet should remove the facet', function (instance) { test('Toggling off the last value of a facet should remove the facet', function (instance) {
var field = {}; var field = {};
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.toggle({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
@ -101,7 +101,7 @@ openerp.testing.section('search.query', {
}); });
test('Intermediate emptiness should not remove the facet', function (instance) { test('Intermediate emptiness should not remove the facet', function (instance) {
var field = {}; var field = {};
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]}); query.add({category: 'A', field: field, values: [{label: 'V1', value: 0}]});
query.toggle({category: 'A', field: field, values: [ query.toggle({category: 'A', field: field, values: [
@ -118,7 +118,7 @@ openerp.testing.section('search.query', {
}); });
test('Reseting with multiple facets should still work to load defaults', function (instance) { test('Reseting with multiple facets should still work to load defaults', function (instance) {
var query = new instance.web.search.SearchQuery; var query = new instance.web.search.SearchQuery();
var field = {}; var field = {};
query.reset([ query.reset([
{category: 'A', field: field, values: [{label: 'V1', value: 0}]}, {category: 'A', field: field, values: [{label: 'V1', value: 0}]},
@ -129,7 +129,7 @@ openerp.testing.section('search.query', {
deepEqual(query.at(0).get('values'), [ deepEqual(query.at(0).get('values'), [
{label: 'V1', value: 0}, {label: 'V1', value: 0},
{label: 'V2', value: 1} {label: 'V2', value: 1}
]) ]);
}); });
}); });
@ -346,7 +346,7 @@ openerp.testing.section('search.defaults', {
{attrs: {name: 'dummy', string: 'Dummy'}}, {attrs: {name: 'dummy', string: 'Dummy'}},
{relation: 'dummy.model.name'}, {relation: 'dummy.model.name'},
view); view);
mock('dummy.model.name:name_get', function () { return [] }); mock('dummy.model.name:name_get', function () { return []; });
return f.facet_for_defaults({dummy: id}) return f.facet_for_defaults({dummy: id})
.done(function (facet) { .done(function (facet) {
ok(!facet, "an invalid m2o default should yield a non-facet"); ok(!facet, "an invalid m2o default should yield a non-facet");
@ -358,9 +358,9 @@ openerp.testing.section('search.defaults', {
{attrs: {name: 'dummy', string: 'Dummy'}}, {attrs: {name: 'dummy', string: 'Dummy'}},
{relation: 'dummy.model.name'}, {relation: 'dummy.model.name'},
view); view);
raises(function () { f.facet_for_defaults({dummy: [6, 7]}) }, raises(function () { f.facet_for_defaults({dummy: [6, 7]}); },
"should not accept multiple default values"); "should not accept multiple default values");
}) });
}); });
openerp.testing.section('search.completions', { openerp.testing.section('search.completions', {
dependencies: ['web.search'], dependencies: ['web.search'],
@ -614,6 +614,59 @@ openerp.testing.section('search.completions', {
{relation: 'dummy.model'}, view); {relation: 'dummy.model'}, view);
return f.complete("bob"); return f.complete("bob");
}); });
test('Integer: invalid', {asserts: 1}, function (instance) {
var view = {inputs: []};
var f = new instance.web.search.IntegerField(
{attrs: {string: "Dummy"}}, {}, view);
return f.complete("qux")
.done(function (completions) {
ok(!completions, "non-number => no completion");
});
});
test('Integer: non-zero', {asserts: 5}, function (instance) {
var view = {inputs: []};
var f = new instance.web.search.IntegerField(
{attrs: {string: "Dummy"}}, {}, view);
return f.complete("-2")
.done(function (completions) {
equal(completions.length, 1, "number fields provide 1 completion only");
var facet = new instance.web.search.Facet(completions[0].facet);
equal(facet.get('category'), f.attrs.string);
equal(facet.get('field'), f);
var value = facet.values.at(0);
equal(value.get('label'), "-2");
equal(value.get('value'), -2);
});
});
test('Integer: zero', {asserts: 3}, function (instance) {
var view = {inputs: []};
var f = new instance.web.search.IntegerField(
{attrs: {string: "Dummy"}}, {}, view);
return f.complete("0")
.done(function (completions) {
equal(completions.length, 1, "number fields provide 1 completion only");
var facet = new instance.web.search.Facet(completions[0].facet);
var value = facet.values.at(0);
equal(value.get('label'), "0");
equal(value.get('value'), 0);
});
});
test('Float: non-zero', {asserts: 5}, function (instance) {
var view = {inputs: []};
var f = new instance.web.search.FloatField(
{attrs: {string: "Dummy"}}, {}, view);
return f.complete("42.37")
.done(function (completions) {
equal(completions.length, 1, "float fields provide 1 completion only");
var facet = new instance.web.search.Facet(completions[0].facet);
equal(facet.get('category'), f.attrs.string);
equal(facet.get('field'), f);
var value = facet.values.at(0);
equal(value.get('label'), "42.37");
equal(value.get('value'), 42.37);
});
});
}); });
openerp.testing.section('search.serialization', { openerp.testing.section('search.serialization', {
dependencies: ['web.search'], dependencies: ['web.search'],
@ -651,7 +704,7 @@ openerp.testing.section('search.serialization', {
ok(!got_groupby, "no facet, should not have fetched groupby"); ok(!got_groupby, "no facet, should not have fetched groupby");
ok(_(gs).isEmpty(), "groupby list should be empty"); ok(_(gs).isEmpty(), "groupby list should be empty");
}) });
}); });
test('London, calling', {asserts: 8}, function (instance, $fix) { test('London, calling', {asserts: 8}, function (instance, $fix) {
var got_domain = false, got_context = false, got_groupby = false; var got_domain = false, got_context = false, got_groupby = false;
@ -686,7 +739,7 @@ openerp.testing.section('search.serialization', {
ok(got_groupby, "should have fetched groupby"); ok(got_groupby, "should have fetched groupby");
ok(_(gs).isEmpty(), "groupby list should be empty"); ok(_(gs).isEmpty(), "groupby list should be empty");
}) });
}); });
test('Generate domains', {asserts: 1}, function (instance, $fix) { test('Generate domains', {asserts: 1}, function (instance, $fix) {
var view = makeSearchView(instance, { var view = makeSearchView(instance, {
@ -1065,7 +1118,7 @@ openerp.testing.section('search.groupby', {
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>', '<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
'</search>' '</search>'
].join(''), ].join(''),
} };
}); });
} }
}, function (instance, $fix) { }, function (instance, $fix) {
@ -1076,7 +1129,7 @@ openerp.testing.section('search.groupby', {
equal(view.inputs.length, 7, equal(view.inputs.length, 7,
'should have 7 inputs total'); 'should have 7 inputs total');
var group = _.find(view.inputs, function (f) { var group = _.find(view.inputs, function (f) {
return f instanceof instance.web.search.GroupbyGroup return f instanceof instance.web.search.GroupbyGroup;
}); });
ok(group, "should have a GroupbyGroup input"); ok(group, "should have a GroupbyGroup input");
strictEqual(group.getParent(), view, strictEqual(group.getParent(), view,
@ -1095,7 +1148,7 @@ openerp.testing.section('search.groupby', {
deepEqual(results.groupbys, [ deepEqual(results.groupbys, [
"{'group_by': 'foo'}", "{'group_by': 'foo'}",
"{'group_by': 'baz'}" "{'group_by': 'baz'}"
], "should have sequence of contexts") ], "should have sequence of contexts");
}); });
}); });
test('unified multiple groupby groups', { test('unified multiple groupby groups', {
@ -1114,7 +1167,7 @@ openerp.testing.section('search.groupby', {
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>', '<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
'</search>' '</search>'
].join(''), ].join(''),
} };
}); });
} }
}, function (instance, $fix) { }, function (instance, $fix) {
@ -1125,7 +1178,7 @@ openerp.testing.section('search.groupby', {
equal(view.inputs.length, 9, "should have 9 inputs total"); equal(view.inputs.length, 9, "should have 9 inputs total");
var groups = _.filter(view.inputs, function (f) { var groups = _.filter(view.inputs, function (f) {
return f instanceof instance.web.search.GroupbyGroup return f instanceof instance.web.search.GroupbyGroup;
}); });
equal(groups.length, 3, "should have 3 GroupbyGroups"); equal(groups.length, 3, "should have 3 GroupbyGroups");
@ -1169,7 +1222,7 @@ openerp.testing.section('search.filters.saved', {
"displayed label should be the name of the filter"); "displayed label should be the name of the filter");
equal(values.at(0).get('value'), null, equal(values.at(0).get('value'), null,
"should have no value set"); "should have no value set");
}) });
}); });
test('removal', {asserts: 1}, function (instance, $fix, mock) { test('removal', {asserts: 1}, function (instance, $fix, mock) {
var view = makeSearchView(instance); var view = makeSearchView(instance);
@ -1361,7 +1414,7 @@ openerp.testing.section('search.invisible', {
}, ['<search>', }, ['<search>',
'<field name="field0"/>', '<field name="field0"/>',
'<field name="field1" modifiers="{&quot;invisible&quot;: true}"/>', '<field name="field1" modifiers="{&quot;invisible&quot;: true}"/>',
'</search>'].join()); '</search>'].join(''));
return view.appendTo($fix) return view.appendTo($fix)
.then(function () { .then(function () {
var done = $.Deferred(); var done = $.Deferred();
@ -1380,7 +1433,7 @@ openerp.testing.section('search.invisible', {
'<search>', '<search>',
'<filter string="filter 0"/>', '<filter string="filter 0"/>',
'<filter string="filter 1" modifiers="{&quot;invisible&quot;: true}"/>', '<filter string="filter 1" modifiers="{&quot;invisible&quot;: true}"/>',
'</search>'].join()); '</search>'].join(''));
return view.appendTo($fix) return view.appendTo($fix)
.then(function () { .then(function () {
var $fs = $fix.find('.oe_searchview_filters ul'); var $fs = $fix.find('.oe_searchview_filters ul');
@ -1400,6 +1453,26 @@ openerp.testing.section('search.invisible', {
return done; return done;
}); });
}); });
test('invisible-previous-sibling', {asserts: 3}, function (instance, $fix, mock) {
var view = makeView(instance, mock, {}, [
'<search>',
'<filter string="filter 0" context="{&quot;test&quot;: 0}"/>',
'<filter string="filter 1" modifiers="{&quot;invisible&quot;: true}" context="{&quot;test&quot;: 1}"/>',
'<filter string="filter 2" modifiers="{&quot;invisible&quot;: true}" context="{&quot;test&quot;: 2}"/>',
'<filter string="filter 3" context="{&quot;test&quot;: 3}"/>',
'</search>'].join(''));
return view.appendTo($fix)
.done(function () {
// Select filter 3
$fix.find('.oe_searchview_filters ul li:contains("filter 3")').click();
equal(view.query.length, 1, "should have selected a filter");
var facet = view.query.at(0);
strictEqual(facet.values.at(0).get('label'), "filter 3",
"should have correctly labelled the facet");
deepEqual(view.build_search_data().contexts, [{test: 3}],
"should have built correct context");
});
});
// Invisible filter groups should not appear in the drawer // Invisible filter groups should not appear in the drawer
// Group invisibility should be inherited by children // Group invisibility should be inherited by children
test('group-invisibility', {asserts: 6}, function (instance, $fix, mock) { test('group-invisibility', {asserts: 6}, function (instance, $fix, mock) {

View File

@ -21,7 +21,7 @@ openerp.testing.section('testing.stack', function (test) {
return s.execute(function () { return s.execute(function () {
return $.when(42); return $.when(42);
}).then(function (val) { }).then(function (val) {
strictEqual(val, 42, "should return the handler value") strictEqual(val, 42, "should return the handler value");
}); });
}); });
test('direct, deferred, failure', {asserts: 1}, function () { test('direct, deferred, failure', {asserts: 1}, function () {