[tryton-debian-vcs] tryton-sao branch debian created. 46396cf4c0c2c857a47636a094be1cf499b90840

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Fri Nov 13 19:46:56 UTC 2015


The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/tryton-sao.git;a=commitdiff;h=46396cf4c0c2c857a47636a094be1cf499b90840
commit 46396cf4c0c2c857a47636a094be1cf499b90840
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Wed Nov 11 11:57:37 2015 +0100

    Merging upstream version 3.8.1.

diff --git a/CHANGELOG b/CHANGELOG
index adf01ab..a079c79 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,2 +1,5 @@
+Version 3.8.1 - 2015-11-09
+* Bug fixes (see mercurial logs for details)
+
 Version 3.8.0 - 2015-11-02
 * Initial release
diff --git a/dist/sao.css b/dist/sao.css
deleted file mode 100644
index 2c8e538..0000000
--- a/dist/sao.css
+++ /dev/null
@@ -1,283 +0,0 @@
-.navbar {
-  z-index: 1000;
-}
-.tab-form nav.toolbar,
-.tab-board nav.toolbar {
-  margin-bottom: 1px;
-}
-.tab-form nav.toolbar ul.nav > li > a,
-.tab-board nav.toolbar ul.nav > li > a {
-  padding-right: 0px;
-}
-.tab-form nav.toolbar.affix,
-.tab-board nav.toolbar.affix {
-  top: 51px;
-  z-index: 999;
-}
-.nav-tabs > li .close {
-  margin: -2px 0 0 10px;
-  font-size: 18px;
-}
- at media screen and (max-width: 767px) {
-  .tab-form .navbar-nav > li,
-  .tab-board .navbar-nav > li {
-    float: left;
-    width: 50%;
-  }
-}
-input[type='color'] .btn {
-  height: 1.42857143;
-}
-input[type='color'] .btn-lg {
-  height: 1.3333333;
-}
-input[type='color'] .btn-sm {
-  height: 1.5;
-}
-input[type='color'] .btn-xs {
-  height: 1.5;
-}
-button img.icon {
-  height: 1.42857143em;
-}
-.treeview {
-  overflow-x: auto;
-}
-.treeview > table.tree {
-  width: 100%;
-}
-.treeview > table.tree > thead > tr > th.selection {
-  width: 30px;
-}
-.treeview > table.tree > thead > tr > th {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  min-width: 30px;
-}
-.treeview > table.tree > tbody > tr > td {
-  cursor: pointer;
-}
-.treeview > table.tree > tbody > tr > td > * {
-  vertical-align: middle;
-}
-.treeview > table.tree img.column-affix {
-  width: 2em;
-  height: 2em;
-}
-.treeview > table.tree a.column-affix > img {
-  width: 1em;
-  height: 1em;
-}
-.treeview .editabletree-char,
-.treeview .editabletree-date,
-.treeview .editabletree-datetime,
-.treeview .editabletree-integer,
-.treeview .editabletree-float,
-.treeview .editabletree-selection,
-.treeview .editabletree-timedelta,
-.treeview .editabletree-boolean,
-.treeview .editabletree-many2one,
-.treeview .editabletree-one2many {
-  width: 100%;
-}
-.treeview td {
-  white-space: nowrap;
-  text-overflow: ellipsis;
-}
-.treeview .expander {
-  cursor: pointer;
-}
-.treeview .treefooter {
-  text-align: center;
-}
-.treeview label {
-  font-weight: normal;
-  display: inline;
-}
-.treeview label.required {
-  font-weight: bold;
-}
-.treeview label.editable {
-  font-style: italic;
-}
-.form {
-  width: 100%;
-}
-.form > .form-container {
-  width: 100%;
-}
-.form > .form-container td {
-  padding: 1px;
-}
-.form td.form-label {
-  white-space: pre;
-}
-.form .form-char input,
-.form .form-password input,
-.form .form-integer input,
-.form .form-float input {
-  min-width: 8em;
-}
-.form .form-url a > img,
-.form .form-email a > img,
-.form .form-callto a > img,
-.form .form-sip a > img {
-  width: 1em;
-  height: 1em;
-}
-.form td.form-one2many,
-.form td.form-many2many {
-  vertical-align: top;
-}
-.form .form-one2many-menu::after,
-.form .form-many2many-menu::after {
-  content: "";
-  display: table;
-  clear: both;
-}
-.form .form-one2many-menu .form-one2many-string,
-.form .form-many2many-menu .form-one2many-string,
-.form .form-one2many-menu .form-many2many-string,
-.form .form-many2many-menu .form-many2many-string {
-  display: inline-table;
-  float: left;
-  margin: 5px;
-}
-.form .form-one2many-menu .form-one2many-toolbar,
-.form .form-many2many-menu .form-one2many-toolbar,
-.form .form-one2many-menu .form-many2many-toolbar,
-.form .form-many2many-menu .form-many2many-toolbar {
-  display: inline-table;
-  float: right;
-}
-.form .form-richtext .richtext {
-  min-height: 5em;
-}
-.form .form-separator label {
-  margin-top: 10px;
-}
-.form .form-separator hr {
-  margin-top: 0;
-  margin-bottom: 5px;
-}
-.form .form-image_ img {
-  width: 4em;
-  height: 4em;
-}
-.form label {
-  font-weight: normal;
-  display: inline;
-}
-.form label.required {
-  font-weight: bold;
-}
-.form label.editable {
-  font-style: italic;
-}
-.form .panel-heading {
-  padding: 1px 1px;
-}
-.form-binary,
-.editabletree-binary {
-  min-width: calc(12em);
-}
-.form-integer input,
-.form-float input,
-.form-timedelta input,
-.dict-integer input,
-.dict-float input,
-.dict-numeric input,
-.editabletree-integer input,
-.editabletree-float input,
-.editabletree-timedelta input {
-  text-align: right;
-}
-.column-integer,
-.column-float,
-.column-timedelta {
-  text-align: right;
-}
-.form-boolean input[type=checkbox],
-.editabletree-boolean input[type=checkbox] {
-  width: initial;
-}
-.dict-label {
-  text-align: right;
-}
-.progress {
-  margin-bottom: 0px;
-}
- at media screen and (max-width: 767px) {
-  .dict-label {
-    text-align: left;
-  }
-}
- at media screen and (max-width: 767px) {
-  .treeview.responsive {
-    /* Override the width from Sao.View.resize */
-    width: 100% !important;
-  }
-  table.responsive,
-  table.responsive > thead,
-  table.responsive > thead > tr,
-  table.responsive > thead > tr > th,
-  table.responsive > tbody,
-  table.responsive > tbody > tr,
-  table.responsive > tbody > tr > td {
-    display: block !important;
-  }
-  table.responsive {
-    /* Hide table headers (but not display: none; for accessiblity) */
-    /* Force height to empty content */
-  }
-  table.responsive > thead > tr {
-    position: absolute;
-    top: -9999px;
-    left: -9999px;
-  }
-  table.responsive > thead > tr,
-  table.responsive > tbody > tr > td {
-    text-align: left !important;
-  }
-  table.responsive td > div:empty {
-    min-height: 20px;
-  }
-  table.responsive-noheader > tbody > tr > td {
-    width: 100% !important;
-  }
-  table.responsive-header > tbody > tr {
-    border: 1px solid #ccc;
-  }
-  table.responsive-header > tbody > tr > td {
-    /* Behave like a "row" */
-    border: none;
-    border-bottom: 1px solid #eee;
-    position: relative;
-    padding-left: 30%;
-  }
-  table.responsive-header > tbody > tr > td:before {
-    /* Now like a table header */
-    position: absolute;
-    /* Top/left values mimic padding */
-    top: 6px;
-    left: 6px;
-    width: 30%;
-    padding-right: 10px;
-    text-align: left;
-    font-weight: bold;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    /* Label */
-    content: attr(data-title);
-  }
-}
-#processing {
-  position: fixed;
-  top: 0px;
-  width: 100%;
-  z-index: 1000;
-}
-img.global-search-icon {
-  width: 2em;
-  height: 2em;
-}
diff --git a/dist/sao.js b/dist/sao.js
deleted file mode 100644
index 7d5052d..0000000
--- a/dist/sao.js
+++ /dev/null
@@ -1,16872 +0,0 @@
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-var Sao = {};
-
-(function() {
-    'use strict';
-
-    // Browser compatibility: polyfill
-    if (!('contains' in String.prototype)) {
-        String.prototype.contains = function(str, startIndex) {
-            return -1 !== String.prototype.indexOf.call(this, str, startIndex);
-        };
-    }
-    if (!String.prototype.startsWith) {
-        Object.defineProperty(String.prototype, 'startsWith', {
-            enumerable: false,
-            configurable: false,
-            writable: false,
-            value: function(searchString, position) {
-                position = position || 0;
-                return this.indexOf(searchString, position) === position;
-            }
-        });
-    }
-    if (!String.prototype.endsWith) {
-        Object.defineProperty(String.prototype, 'endsWith', {
-            enumerable: false,
-            configurable: false,
-            writable: false,
-            value: function(searchString, position) {
-                position = position || this.length;
-                position = position - searchString.length;
-                var lastIndex = this.lastIndexOf(searchString);
-                return lastIndex !== -1 && lastIndex === position;
-            }
-        });
-    }
-    if (!Array.prototype.some) {
-        Array.prototype.some = function(fun /*, thisp */) {
-            if (this === null) {
-                throw new TypeError();
-            }
-            var thisp, i,
-                t = Object(this),
-                len = t.length >>> 0;
-            if (typeof fun !== 'function') {
-                throw new TypeError();
-            }
-            thisp = arguments[1];
-            for (i = 0; i < len; i++) {
-                if (i in t && fun.call(thisp, t[i], i, t)) {
-                    return true;
-                }
-            }
-            return false;
-        };
-    }
-
-    // Ensure RichText doesn't use style with css
-    try {
-        document.execCommand('styleWithCSS', false, false);
-    } catch (e) {
-    }
-    try {
-        document.execCommand('useCSS', false, true);
-    } catch (e) {
-    }
-
-    // Add .uniqueId to jQuery
-    jQuery.fn.extend({
-        uniqueId: (function() {
-            var uuid = 0;
-            return function() {
-                return this.each(function() {
-                    if (!this.id) {
-                        this.id = "ui-id-" + (++uuid);
-                    }
-                });
-            };
-        })()
-    });
-
-    Sao.class_ = function(Parent, props) {
-        var ClassConstructor = function() {
-            if (!(this instanceof ClassConstructor))
-                throw new Error('Constructor function requires new operator');
-            this.Class = ClassConstructor;
-            if (this.init) {
-                this.init.apply(this, arguments);
-            }
-        };
-
-        // Plug prototype chain
-        ClassConstructor.prototype = Object.create(Parent.prototype);
-        ClassConstructor._super = Parent.prototype;
-        if (props) {
-            for (var name in props) {
-                ClassConstructor.prototype[name] = props[name];
-            }
-        }
-        return ClassConstructor;
-    };
-
-    Sao.Decimal = Number;
-
-    Sao.Date = function(year, month, day) {
-        var date = moment();
-        date.year(year);
-        date.month(month);
-        date.date(day);
-        date.set({hour: 0, minute: 0, second: 0, millisecond: 0});
-        date.isDate = true;
-        return date;
-    };
-
-    Sao.Date.min = moment(new Date(-100000000 * 86400000));
-    Sao.Date.min.set({hour: 0, minute: 0, second: 0, millisecond: 0});
-    Sao.Date.min.isDate = true;
-    Sao.Date.max = moment(new Date(100000000 * 86400000));
-    Sao.Date.max.set({hour: 0, minute: 0, second: 0, millisecond: 0});
-    Sao.Date.max.isDate = true;
-
-    Sao.DateTime = function(year, month, day, hour, minute, second,
-            millisecond, utc) {
-        var datetime = moment();
-        if (utc) {
-            datetime.utc();
-        }
-        datetime.year(year);
-        datetime.month(month);
-        datetime.date(day);
-        datetime.hour(hour || 0);
-        datetime.minute(minute || 0);
-        datetime.second(second || 0);
-        datetime.milliseconds(millisecond || 0);
-        datetime.isDateTime = true;
-        datetime.local();
-        return datetime;
-    };
-
-    Sao.DateTime.combine = function(date, time) {
-        var datetime = date.clone();
-        datetime.set({hour: time.hour(), minute: time.minute(),
-            second: time.second(), millisecond: time.millisecond()});
-        return datetime;
-    };
-
-    Sao.DateTime.min = moment(new Date(-100000000 * 86400000)).local();
-    Sao.DateTime.min.isDateTime = true;
-    Sao.DateTime.max = moment(new Date(100000000 * 86400000)).local();
-    Sao.DateTime.max.isDateTime = true;
-
-    Sao.Time = function(hour, minute, second, millisecond) {
-        var time = moment({hour: hour, minute: minute, second: second,
-           millisecond: millisecond || 0});
-        time.isTime = true;
-        return time;
-    };
-
-    Sao.TimeDelta = function(days, seconds,
-            milliseconds, minutes, hours, weeks) {
-        var timedelta = moment.duration({
-            days: days,
-            seconds: seconds,
-            milliseconds: milliseconds,
-            minutes: minutes,
-            hours: hours,
-            weeks: weeks
-        });
-        timedelta.isTimeDelta = true;
-        return timedelta;
-    };
-
-    Sao.config = {};
-    Sao.config.limit = 1000;
-    Sao.config.display_size = 20;
-    Sao.config.roundup = {};
-    Sao.config.roundup.url = 'http://bugs.tryton.org/roundup/';
-
-    Sao.i18n = i18n();
-    Sao.i18n.setlang = function(lang) {
-        Sao.i18n.setLocale(lang);
-        return jQuery.getJSON('locale/' + lang + '.json', function(data) {
-            if (!data[''].language) {
-                data[''].language = lang;
-            }
-            if (!data['']['plural-forms']) {
-                data['']['plural-forms'] = 'nplurals=2; plural=(n!=1);';
-            }
-            // gettext.js requires to dump untranslated keys
-            for (var key in data) {
-                if ('' === key) {
-                    continue;
-                }
-                if ('' !== data[key][1]) {
-                    data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
-                }
-            }
-            Sao.i18n.loadJSON(data);
-        });
-    };
-    Sao.i18n.setlang(
-            (navigator.language ||
-             navigator.browserLanguge ||
-             navigator.userLanguage ||
-             'en_US').replace('-', '_'));
-
-    Sao.get_preferences = function() {
-        var session = Sao.Session.current_session;
-        return session.reload_context().then(function() {
-            return Sao.rpc({
-                'method': 'model.res.user.get_preferences',
-                'params': [false, {}]
-            }, session).then(function(preferences) {
-                var deferreds = [];
-                deferreds.push(Sao.common.MODELACCESS.load_models());
-                deferreds.push(Sao.common.ICONFACTORY.load_icons());
-                deferreds.push(Sao.common.MODELHISTORY.load_history());
-                deferreds.push(Sao.common.VIEW_SEARCH.load_searches());
-                return jQuery.when.apply(jQuery, deferreds).then(function() {
-                    (preferences.actions || []).forEach(function(action_id) {
-                        Sao.Action.execute(action_id, {}, null, {});
-                    });
-                    var title = 'Tryton';
-                    if (!jQuery.isEmptyObject(preferences.status_bar)) {
-                        title += ' - ' + preferences.status_bar;
-                    }
-                    document.title = title;
-                    var new_lang = preferences.language != Sao.i18n.getLocale();
-                    var prm = jQuery.Deferred();
-                    Sao.i18n.setlang(preferences.language).always(function() {
-                        if (new_lang) {
-                            Sao.user_menu(preferences);
-                        }
-                        prm.resolve(preferences);
-                    });
-                    return prm;
-                });
-            });
-        });
-    };
-
-    Sao.login = function() {
-        Sao.Session.get_credentials()
-            .then(function(session) {
-                Sao.Session.current_session = session;
-                return session.reload_context();
-            }).then(Sao.get_preferences).then(function(preferences) {
-                Sao.menu(preferences);
-                Sao.user_menu(preferences);
-            });
-    };
-
-    Sao.logout = function() {
-        var session = Sao.Session.current_session;
-        Sao.Tab.tabs.close(true).done(function() {
-            jQuery('#user-preferences').children().remove();
-            jQuery('#user-logout').children().remove();
-            jQuery('#menu').children().remove();
-            document.title = 'Tryton';
-            session.do_logout().always(Sao.login);
-        });
-    };
-
-    Sao.preferences = function() {
-        Sao.Tab.tabs.close(true).done(function() {
-            jQuery('#user-preferences').children().remove();
-            jQuery('#user-logout').children().remove();
-            jQuery('#menu').children().remove();
-            new Sao.Window.Preferences(function() {
-                Sao.get_preferences().then(function(preferences) {
-                    Sao.menu(preferences);
-                    Sao.user_menu(preferences);
-                });
-            });
-        });
-    };
-
-    Sao.user_menu = function(preferences) {
-        jQuery('#user-preferences').children().remove();
-        jQuery('#user-logout').children().remove();
-        jQuery('#user-preferences').append(jQuery('<a/>', {
-            'href': '#'
-        }).click(Sao.preferences).append(preferences.status_bar));
-        jQuery('#user-logout').append(jQuery('<a/>', {
-            'href': '#'
-        }).click(Sao.logout).append(Sao.i18n.gettext('Logout')));
-    };
-
-    Sao.menu = function(preferences) {
-        var decoder = new Sao.PYSON.Decoder();
-        var action = decoder.decode(preferences.pyson_menu);
-        var view_ids = false;
-        if (!jQuery.isEmptyObject(action.views)) {
-            view_ids = action.views.map(function(view) {
-                return view[0];
-            });
-        } else if (action.view_id) {
-            view_ids = [action.view_id[0]];
-        }
-        decoder = new Sao.PYSON.Decoder(Sao.Session.current_session.context);
-        var action_ctx = decoder.decode(action.pyson_context || '{}');
-        var domain = decoder.decode(action.pyson_domain);
-        var form = new Sao.Tab.Form(action.res_model, {
-            'mode': ['tree'],
-            'view_ids': view_ids,
-            'domain': domain,
-            'context': action_ctx,
-            'selection_mode': Sao.common.SELECTION_NONE
-        });
-        Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(form), 1);
-        form.view_prm.done(function() {
-            Sao.main_menu_screen = form.screen;
-            var view = form.screen.current_view;
-            view.table.find('thead').hide();
-            jQuery('#menu').children().remove();
-
-            var gs = new Sao.GlobalSearch();
-            jQuery('#menu').append(gs.el);
-            jQuery('#menu').append(
-                form.screen.screen_container.content_box.detach());
-        });
-    };
-    Sao.main_menu_screen = null;
-
-    Sao.Dialog = Sao.class_(Object, {
-        init: function(title, class_, size) {
-            size = size || 'sm';
-            this.modal = jQuery('<div/>', {
-                'class': class_ + ' modal fade',
-                'role': 'dialog'
-            });
-            this.content = jQuery('<form/>', {
-                'class': 'modal-content'
-            }).appendTo(jQuery('<div/>', {
-                'class': 'modal-dialog modal-' + size
-            }).appendTo(this.modal));
-            this.header = jQuery('<div/>', {
-                'class': 'modal-header'
-            }).appendTo(this.content);
-            if (title) {
-                this.add_title(title);
-            }
-            this.body = jQuery('<div/>', {
-                'class': 'modal-body'
-            }).appendTo(this.content);
-            this.footer = jQuery('<div/>', {
-                'class': 'modal-footer'
-            }).appendTo(this.content);
-        },
-        add_title: function(title) {
-            this.header.append(jQuery('<h4/>', {
-                'class': 'modal-title'
-            }).append(title));
-        }
-    });
-
-    Sao.GlobalSearch = Sao.class_(Object, {
-        init: function() {
-            this.el = jQuery('<div/>', {
-                'class': 'global-search-container'
-            });
-            this.search_entry = jQuery('<input>', {
-                'class': 'form-control',
-                'placeholder': Sao.i18n.gettext('Search...')
-            });
-            this.el.append(this.search_entry);
-            var completion = new Sao.common.InputCompletion(
-                    this.search_entry,
-                    this.update.bind(this),
-                    this.match_selected.bind(this),
-                    this.format.bind(this));
-        },
-        format: function(content) {
-            var el = jQuery('<div/>');
-            var img = jQuery('<img/>', {
-                'class': 'global-search-icon'
-            }).appendTo(el);
-            Sao.common.ICONFACTORY.register_icon(content.icon).then(
-                    function(icon_url) {
-                        img.attr('src', icon_url);
-                    });
-            jQuery('<span/>', {
-                'class': 'global-search-text'
-            }).text(content.record_name).appendTo(el);
-            return el;
-        },
-        update: function(text) {
-            var ir_model = new Sao.Model('ir.model');
-            return ir_model.execute('global_search',
-                    [text, Sao.config.limit, Sao.main_menu_screen.model_name],
-                    Sao.main_menu_screen.context).then(function(s_results) {
-                var results = [];
-                for (var i=0, len=s_results.length; i < len; i++) {
-                    results.push({
-                        'model': s_results[i][1],
-                        'model_name': s_results[i][2],
-                        'record_id': s_results[i][3],
-                        'record_name': s_results[i][4],
-                        'icon': s_results[i][5],
-                    });
-                }
-                return results;
-            }.bind(this));
-        },
-        match_selected: function(item) {
-            if (item.model == Sao.main_menu_screen.model_name) {
-                Sao.Action.exec_keyword('tree_open', {
-                    'model': item.model,
-                    'id': item.record_id,
-                    'ids': [item.record_id]
-                }, Sao.main_menu_screen.context);
-            } else {
-                var params = {
-                    'model': item.model,
-                    'res_id': item.record_id,
-                    'mode': ['form', 'tree']
-                };
-                Sao.Tab.create(params);
-            }
-            this.search_entry.val('');
-        }
-    });
-
-    // Fix stacked modal
-    jQuery(document)
-        .on('show.bs.modal', '.modal', function(event) {
-            jQuery(this).appendTo(jQuery('body'));
-        })
-    .on('shown.bs.modal', '.modal.in', function(event) {
-        setModalsAndBackdropsOrder();
-    })
-    .on('hidden.bs.modal', '.modal', function(event) {
-        setModalsAndBackdropsOrder();
-    });
-
-    function setModalsAndBackdropsOrder() {
-        var modalZIndex = 1040;
-        jQuery('.modal.in').each(function(index) {
-            var $modal = jQuery(this);
-            modalZIndex++;
-            $modal.css('zIndex', modalZIndex);
-            $modal.next('.modal-backdrop.in').addClass('hidden')
-            .css('zIndex', modalZIndex - 1);
-        });
-        jQuery('.modal.in:visible:last').focus()
-        .next('.modal-backdrop.in').removeClass('hidden');
-    }
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.rpc = function(args, session) {
-        var dfd = jQuery.Deferred();
-        if (!session) {
-            session = new Sao.Session();
-        }
-        var params = jQuery.extend([], args.params);
-        params.push(jQuery.extend({}, session.context, params.pop()));
-
-        var timeoutID = Sao.common.processing.show();
-        var ajax_prm = jQuery.ajax({
-            'contentType': 'application/json',
-            'data': JSON.stringify(Sao.rpc.prepareObject({
-                'method': args.method,
-                'params': [session.user_id, session.session].concat(params)
-            })),
-            'dataType': 'json',
-            'url': '/' + (session.database || ''),
-            'type': 'post',
-            'complete': [function() {
-                Sao.common.processing.hide(timeoutID);
-            }]
-        });
-
-        var ajax_success = function(data) {
-            if (data === null) {
-                Sao.common.warning.run('',
-                        Sao.i18n.gettext('Unable to reach the server'));
-                dfd.reject();
-            } else if (data.error) {
-                var name, msg, description;
-                if (data.error[0] == 'UserWarning') {
-                    name = data.error[1];
-                    msg = data.error[2];
-                    description = data.error[3];
-                    Sao.common.userwarning.run(description, msg)
-                        .done(function(result) {
-                            if (!~['always', 'ok'].indexOf(result)) {
-                                dfd.reject();
-                                return;
-                            }
-                            Sao.rpc({
-                                'method': 'model.res.user.warning.create',
-                                'params': [[{
-                                    'user': session.user_id,
-                                    'name': name,
-                                    'always': result == 'always'
-                                }], {}]
-                            }, session).done(function() {
-                                Sao.rpc(args, session).then(
-                                    dfd.resolve, dfd.reject);
-                            });
-                        });
-                    return;
-                } else if (data.error[0] == 'UserError') {
-                    msg = data.error[1];
-                    description = data.error[2];
-                    Sao.common.warning.run(msg, description)
-                        .always(dfd.reject);
-                    return;
-                } else if (data.error[0] == 'ConcurrencyException') {
-                    if (args.method.startsWith('model.') &&
-                            (args.method.endsWith('.write') ||
-                             args.method.endsWith('.delete'))) {
-                        var model = args.method.split('.').slice(1, -1).join('.');
-                        Sao.common.concurrency.run(model, args.params[0][0],
-                                args.params.slice(-1)[0])
-                            .then(function() {
-                                delete args.params.slice(-1)[0]._timestamp;
-                                Sao.rpc(args, session).then(
-                                    dfd.resolve, dfd.reject);
-                            }, dfd.reject);
-                        return;
-                    } else {
-                        Sao.common.message.run('Concurrency Exception',
-                                'glyphicon-alert').always(dfd.reject);
-                        return;
-                    }
-                } else if (data.error[0] == 'NotLogged') {
-                    //Try to relog
-                    Sao.Session.renew(session).then(function() {
-                        Sao.rpc(args, session).then(dfd.resolve, dfd.reject);
-                    }, dfd.reject);
-                    return;
-                } else {
-                    Sao.common.error.run(data.error[0], data.error[1]);
-                }
-                dfd.reject();
-            } else {
-                dfd.resolve(data.result);
-            }
-        };
-
-        var ajax_error = function(query, status_, error) {
-            Sao.common.error.run(status_, error);
-            dfd.reject();
-        };
-        ajax_prm.success(ajax_success);
-        ajax_prm.error(ajax_error);
-
-        return dfd.promise();
-    };
-
-    Sao.rpc.convertJSONObject = function(value, index, parent) {
-       if (value instanceof Array) {
-           for (var i = 0, length = value.length; i < length; i++) {
-               Sao.rpc.convertJSONObject(value[i], i, value);
-           }
-       } else if ((typeof(value) != 'string') &&
-           (typeof(value) != 'number') && (value !== null)) {
-           if (value && value.__class__) {
-               switch (value.__class__) {
-                   case 'datetime':
-                       value = Sao.DateTime(value.year,
-                               value.month - 1, value.day, value.hour,
-                               value.minute, value.second,
-                               value.microsecond / 1000, true);
-                       break;
-                   case 'date':
-                       value = Sao.Date(value.year,
-                           value.month - 1, value.day);
-                       break;
-                   case 'time':
-                       value = Sao.Time(value.hour, value.minute,
-                               value.second, value.microsecond / 1000);
-                       break;
-                    case 'timedelta':
-                       value = Sao.TimeDelta(null, value.seconds);
-                       break;
-                   case 'bytes':
-                       // javascript's atob does not understand linefeed
-                       // characters
-                       var byte_string = atob(value.base64.replace(/\s/g, ''));
-                       // javascript decodes base64 string as a "DOMString", we
-                       // need to convert it to an array of bytes
-                       var array_buffer = new ArrayBuffer(byte_string.length);
-                       var uint_array = new Uint8Array(array_buffer);
-                       for (var j=0; j < byte_string.length; j++) {
-                           uint_array[j] = byte_string.charCodeAt(j);
-                       }
-                       value = uint_array;
-                       break;
-                   case 'Decimal':
-                       value = new Sao.Decimal(value.decimal);
-                       break;
-               }
-               if (parent) {
-                   parent[index] = value;
-               }
-           } else {
-               for (var p in value) {
-                   Sao.rpc.convertJSONObject(value[p], p, value);
-               }
-           }
-       }
-       return parent || value;
-    };
-
-    Sao.rpc.prepareObject = function(value, index, parent) {
-        if (value instanceof Array) {
-            for (var i = 0, length = value.length; i < length; i++) {
-                Sao.rpc.prepareObject(value[i], i, value);
-            }
-        } else if ((typeof(value) != 'string') &&
-                (typeof(value) != 'number') &&
-                (value !== null) &&
-                (value !== undefined)) {
-            if (value.isDate){
-                value = {
-                    '__class__': 'date',
-                    'year': value.year(),
-                    'month': value.month() + 1,
-                    'day': value.date()
-                };
-
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else if (value.isDateTime) {
-                value = value.clone();
-                value = {
-                    '__class__': 'datetime',
-                    'year': value.utc().year(),
-                    'month': value.utc().month() + 1,
-                    'day': value.utc().date(),
-                    'hour': value.utc().hour(),
-                    'minute': value.utc().minute(),
-                    'second': value.utc().second(),
-                    'microsecond': value.utc().millisecond() * 1000
-                };
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else if (value.isTime) {
-                value = {
-                    '__class__': 'time',
-                    'hour': value.hour(),
-                    'minute': value.minute(),
-                    'second': value.second(),
-                    'microsecond': value.millisecond() * 1000
-                };
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else if (value.isTimeDelta) {
-                value = {
-                    '__class__': 'timedelta',
-                    'seconds': value.asSeconds()
-                };
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else if (value instanceof Sao.Decimal) {
-                value = {
-                    '__class__': 'Decimal',
-                    'decimal': value.toString()
-                };
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else if (value instanceof Uint8Array) {
-                value = {
-                    '__class__': 'bytes',
-                    'base64': btoa(String.fromCharCode.apply(null, value))
-                };
-                if (parent) {
-                    parent[index] = value;
-                }
-            } else {
-                for (var p in value) {
-                    Sao.rpc.prepareObject(value[p], p, value);
-                }
-            }
-        }
-        return parent || value;
-    };
-
-    jQuery.ajaxSetup({
-        converters: {
-           'text json': function(json) {
-               return Sao.rpc.convertJSONObject(jQuery.parseJSON(json));
-           }
-        }
-    });
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.PYSON = {};
-
-    Sao.PYSON.PYSON = Sao.class_(Object, {
-        init: function() {
-        },
-        pyson: function() {
-            throw 'NotImplementedError';
-        },
-        types: function() {
-            throw 'NotImplementedError';
-        }
-    });
-
-    Sao.PYSON.PYSON.eval_ = function(value, context) {
-        throw 'NotImplementedError';
-    };
-
-    Sao.PYSON.Encoder = Sao.class_(Object, {
-        encode: function(pyson) {
-            return JSON.stringify(pyson, function(k, v) {
-                if (v instanceof Sao.PYSON.PYSON) {
-                    return v.pyson();
-                } else if (v._isAMomentObject) {
-                    if (v.isDate) {
-                        return Sao.PYSON.Date(
-                            v.getFullYear(),
-                            v.getMonth(),
-                            v.getDate()).pyson();
-                    } else {
-                        return Sao.PYSON.DateTime(
-                            v.getFullYear(),
-                            v.getMonth(),
-                            v.getDate(),
-                            v.getHours(),
-                            v.getMinutes(),
-                            v.getSeconds(),
-                            v.getMilliseconds()).pyson();
-                    }
-                }
-                return v;
-            });
-        }
-    });
-
-    Sao.PYSON.Decoder = Sao.class_(Object, {
-        init: function(context) {
-            this.__context = context || {};
-        },
-        decode: function(str) {
-            var reviver = function(k, v) {
-                if (typeof v == 'object' && v !== null) {
-                    var cls = Sao.PYSON[v.__class__];
-                    if (cls) {
-                        return cls.eval_(v, this.__context);
-                    }
-                }
-                return v;
-            };
-            return JSON.parse(str, reviver.bind(this));
-        }
-    });
-
-    Sao.PYSON.Eval = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(value, default_) {
-            if (default_ === undefined) {
-                default_ = '';
-            }
-            Sao.PYSON.Eval._super.init.call(this);
-            this._value = value;
-            this._default = default_;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Eval',
-                'v': this._value,
-                'd': this._default
-            };
-        },
-        types: function() {
-            if (this._default instanceof Sao.PYSON.PYSON) {
-                return this._default.types();
-            } else {
-                return [typeof this._default];
-            }
-        }
-    });
-
-    Sao.PYSON.Eval.eval_ = function(value, context) {
-        if (value.v in context) {
-            return context[value.v];
-        } else {
-            return value.d;
-        }
-    };
-
-    Sao.PYSON.Not = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(value) {
-            Sao.PYSON.Not._super.init.call(this);
-            if (value instanceof Sao.PYSON.PYSON) {
-                if (jQuery(value.types()).not(['boolean']).length ||
-                    jQuery(['boolean']).not(value.types()).length) {
-                    throw 'value must be boolean';
-                    }
-            } else {
-                if (typeof value != 'boolean') {
-                    throw 'value must be boolean';
-                }
-            }
-            this._value = value;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Not',
-                'v': this._value
-                };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.Not.eval_ = function(value, context) {
-        return !value.v;
-    };
-
-    Sao.PYSON.Bool = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(value) {
-            Sao.PYSON.Bool._super.init.call(this);
-            this._value = value;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Bool',
-                'v': this._value
-                };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.Bool.eval_ = function(value, context) {
-        if (value.v instanceof Object) {
-            return !jQuery.isEmptyObject(value.v);
-        } else {
-            return Boolean(value.v);
-        }
-    };
-
-
-    Sao.PYSON.And = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(statements) {
-            if (statements === undefined) {
-                statements = [];
-            }
-            Sao.PYSON.And._super.init.call(this);
-            for (var i = 0, len = statements.length; i < len; i++) {
-                var statement = statements[i];
-                if (statement instanceof Sao.PYSON.PYSON) {
-                    if (jQuery(statement.types()).not(['boolean']).length ||
-                        jQuery(['boolean']).not(statement.types()).length) {
-                        throw 'statement must be boolean';
-                        }
-                } else {
-                    if (typeof statement != 'boolean') {
-                        throw 'statement must be boolean';
-                    }
-                }
-            }
-            if (statements.length < 2) {
-                throw 'must have at least 2 statements';
-            }
-            this._statements = statements;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'And',
-                's': this._statements
-            };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.And.eval_ = function(value, context) {
-        var result = true;
-        for (var i = 0, len = value.s.length; i < len; i++) {
-            var statement = value.s[i];
-            result = result && statement;
-        }
-        return result;
-    };
-
-
-    Sao.PYSON.Or = Sao.class_(Sao.PYSON.And, {
-        pyson: function() {
-            var result = Sao.PYSON.Or._super.pyson.call(this);
-            result.__class__ = 'Or';
-            return result;
-        }
-    });
-
-    Sao.PYSON.Or.eval_ = function(value, context) {
-        var result = false;
-        for (var i = 0, len = value.s.length; i < len; i++) {
-            var statement = value.s[i];
-            result = result || statement;
-        }
-        return result;
-    };
-
-    Sao.PYSON.Equal = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(statement1, statement2) {
-            Sao.PYSON.Equal._super.init.call(this);
-            var types1, types2;
-            if (statement1 instanceof Sao.PYSON.PYSON) {
-                types1 = statement1.types();
-            } else {
-                types1 = [typeof statement1];
-            }
-            if (statement2 instanceof Sao.PYSON.PYSON) {
-                types2 = statement2.types();
-            } else {
-                types2 = [typeof statement2];
-            }
-            if (jQuery(types1).not(types2).length ||
-                jQuery(types2).not(types1).length) {
-                throw 'statements must have the same type';
-                }
-            this._statement1 = statement1;
-            this._statement2 = statement2;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Equal',
-                's1': this._statement1,
-                's2': this._statement2
-            };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.Equal.eval_ = function(value, context) {
-        return value.s1 == value.s2;
-    };
-
-    Sao.PYSON.Greater = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(statement1, statement2, equal) {
-            Sao.PYSON.Greater._super.init.call(this);
-            var statements = [statement1, statement2];
-            for (var i = 0; i < 2; i++) {
-                var statement = statements[i];
-                if (statement instanceof Sao.PYSON.PYSON) {
-                    if (jQuery(statement).not(['number']).length) {
-                        throw 'statement must be an integer or a float';
-                    }
-                } else {
-                    if (typeof statement != 'number') {
-                        throw 'statement must be an integer or a float';
-                    }
-                }
-            }
-            if (equal === undefined) {
-                equal = false;
-            }
-            if (equal instanceof Sao.PYSON.PYSON) {
-                if (jQuery(equal.types()).not(['boolean']).length ||
-                    jQuery(['boolean']).not(equal.types()).length) {
-                    throw 'equal must be boolean';
-                    }
-            } else {
-                if (typeof equal != 'boolean') {
-                    throw 'equal must be boolean';
-                }
-            }
-            this._statement1 = statement1;
-            this._statement2 = statement2;
-            this._equal = equal;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Greater',
-                's1': this._statement1,
-                's2': this._statement2,
-                'e': this._equal
-            };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.Greater._convert = function(value) {
-        value = jQuery.extend({}, value);
-        value.s1 = Number(value.s1);
-        value.s2 = Number(value.s2);
-        return value;
-    };
-
-    Sao.PYSON.Greater.eval_ = function(value, context) {
-        value = Sao.PYSON.Greater._convert(value);
-        if (value.e) {
-            return value.s1 >= value.s2;
-        } else {
-            return value.s1 > value.s2;
-        }
-    };
-
-    Sao.PYSON.Less = Sao.class_(Sao.PYSON.Greater, {
-        pyson: function() {
-            var result = Sao.PYSON.Less._super.pyson.call(this);
-            result.__class__ = 'Less';
-            return result;
-        }
-    });
-
-    Sao.PYSON.Less._convert = Sao.PYSON.Greater._convert;
-
-    Sao.PYSON.Less.eval_ = function(value, context) {
-        value = Sao.PYSON.Less._convert(value);
-        if (value.e) {
-            return value.s1 <= value.s2;
-        } else {
-            return value.s1 < value.s2;
-        }
-    };
-
-    Sao.PYSON.If = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(condition, then_statement, else_statement) {
-            Sao.PYSON.If._super.init.call(this);
-            if (condition instanceof Sao.PYSON.PYSON) {
-                if (jQuery(condition.types()).not(['boolean']).length ||
-                    jQuery(['boolean']).not(condition.types()).length) {
-                    throw 'condition must be boolean';
-                }
-            } else {
-                if (typeof condition != 'boolean') {
-                    throw 'condition must be boolean';
-                }
-            }
-            var then_types, else_types;
-            if (then_statement instanceof Sao.PYSON.PYSON) {
-                then_types = then_statement.types();
-            } else {
-                then_types = [typeof then_statement];
-            }
-            if (else_statement === undefined) {
-                else_statement = null;
-            }
-            if (else_statement instanceof Sao.PYSON.PYSON) {
-                else_types = else_statement.types();
-            } else {
-                else_types = [typeof else_statement];
-            }
-            if (jQuery(then_types).not(else_types).length ||
-                jQuery(else_types).not(then_types).length) {
-                throw 'then and else statements must be the same type';
-            }
-            this._condition = condition;
-            this._then_statement = then_statement;
-            this._else_statement = else_statement;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'If',
-                'c': this._condition,
-                't': this._then_statement,
-                'e': this._else_statement
-            };
-        },
-        types: function() {
-            if (this._then_statement instanceof Sao.PYSON.PYSON) {
-                return this._then_statement.types();
-            } else {
-                return [typeof this._then_statement];
-            }
-        }
-    });
-
-    Sao.PYSON.If.eval_ = function(value, context) {
-        if (value.c) {
-            return value.t;
-        } else {
-            return value.e;
-        }
-    };
-
-    Sao.PYSON.Get = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(obj, key, default_) {
-            Sao.PYSON.Get._super.init.call(this);
-            if (default_ === undefined) {
-                default_ = null;
-            }
-            if (obj instanceof Sao.PYSON.PYSON) {
-                if (jQuery(obj.types()).not(['object']).length ||
-                    jQuery(['object']).not(obj.types()).length) {
-                    throw 'obj must be a dict';
-                }
-            } else {
-                if (!(obj instanceof Object)) {
-                    throw 'obj must be a dict';
-                }
-            }
-            this._obj = obj;
-            if (key instanceof Sao.PYSON.PYSON) {
-                if (jQuery(key.types()).not(['string']).length ||
-                    jQuery(['string']).not(key.types()).length) {
-                    throw 'key must be a string';
-                }
-            } else {
-                if (typeof key != 'string') {
-                    throw 'key must be a string';
-                }
-            }
-            this._key = key;
-            this._default = default_;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Get',
-                'v': this._obj,
-                'k': this._key,
-                'd': this._default
-            };
-        },
-        types: function() {
-            if (this._default instanceof Sao.PYSON.PYSON) {
-                return this._default.types();
-            } else {
-                return [typeof this._default];
-            }
-        }
-    });
-
-    Sao.PYSON.Get.eval_ = function(value, context) {
-        if (value.k in value.v) {
-            return value.v[value.k];
-        } else {
-            return value.d;
-        }
-    };
-
-    Sao.PYSON.In = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(key, obj) {
-            Sao.PYSON.In._super.init.call(this);
-            if (key instanceof Sao.PYSON.PYSON) {
-                if (jQuery(key.types()).not(['string', 'number']).length) {
-                    throw 'key must be a string or a number';
-                }
-            } else {
-                if (!~['string', 'number'].indexOf(typeof key)) {
-                    throw 'key must be a string or a number';
-                }
-            }
-            if (obj instanceof Sao.PYSON.PYSON) {
-                if (jQuery(obj.types()).not(['object']).length ||
-                    jQuery(['object']).not(obj.types()).length) {
-                    throw 'obj must be a dict or a list';
-                }
-            } else {
-                if (!(obj instanceof Object)) {
-                    throw 'obj must be a dict or a list';
-                }
-            }
-            this._key = key;
-            this._obj = obj;
-        },
-        pyson: function() {
-            return {'__class__': 'In',
-                'k': this._key,
-                'v': this._obj
-            };
-        },
-        types: function() {
-            return ['boolean'];
-        }
-    });
-
-    Sao.PYSON.In.eval_ = function(value, context) {
-        if (value.v.indexOf) {
-            return Boolean(~value.v.indexOf(value.k));
-        } else {
-            return !!value.v[value.k];
-        }
-    };
-
-    Sao.PYSON.Date = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(year, month, day, delta_years, delta_months, delta_days)
-        {
-            Sao.PYSON.Date._super.init.call(this);
-            if (year === undefined) year = null;
-            if (month === undefined) month = null;
-            if (day === undefined) day = null;
-            if (delta_years === undefined) delta_years = 0;
-            if (delta_months === undefined) delta_months = 0;
-            if (delta_days === undefined) delta_days = 0;
-
-            this._test(year, 'year');
-            this._test(month, 'month');
-            this._test(day, 'day');
-            this._test(delta_years, 'delta_years');
-            this._test(delta_days, 'delta_days');
-            this._test(delta_months, 'delta_months');
-
-            this._year = year;
-            this._month = month;
-            this._day = day;
-            this._delta_years = delta_years;
-            this._delta_months = delta_months;
-            this._delta_days = delta_days;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Date',
-                'y': this._year,
-                'M': this._month,
-                'd': this._day,
-                'dy': this._delta_years,
-                'dM': this._delta_months,
-                'dd': this._delta_days
-            };
-        },
-        types: function() {
-            return ['object'];
-        },
-        _test: function(value, name) {
-            if (value instanceof Sao.PYSON.PYSON) {
-                if (jQuery(value.types()).not(
-                        ['number', typeof null]).length) {
-                    throw name + ' must be an integer or None';
-                }
-            } else {
-                if ((typeof value != 'number') && (value !== null)) {
-                    throw name + ' must be an integer or None';
-                }
-            }
-        }
-    });
-
-    Sao.PYSON.Date.eval_ = function(value, context) {
-        var date = Sao.Date(value.y, value.M && value.M - 1, value.d);
-        if (value.dy) date.add(value.dy, 'y');
-        if (value.dM) date.add(value.dM, 'M');
-        if (value.dd) date.add(value.dd, 'd');
-        return date;
-    };
-
-    Sao.PYSON.DateTime = Sao.class_(Sao.PYSON.Date, {
-        init: function(year, month, day, hour, minute, second, microsecond,
-                  delta_years, delta_months, delta_days, delta_hours,
-                  delta_minutes, delta_seconds, delta_microseconds) {
-            Sao.PYSON.DateTime._super.init.call(this, year, month, day,
-                delta_years, delta_months, delta_days);
-            if (hour === undefined) hour = null;
-            if (minute === undefined) minute = null;
-            if (second === undefined) second = null;
-            if (microsecond === undefined) microsecond = null;
-            if (delta_hours === undefined) delta_hours = 0;
-            if (delta_minutes === undefined) delta_minutes = 0;
-            if (delta_seconds === undefined) delta_seconds = 0;
-            if (delta_microseconds === undefined) delta_microseconds = 0;
-
-            this._test(hour, 'hour');
-            this._test(minute, 'minute');
-            this._test(second, 'second');
-            this._test(microsecond, 'microsecond');
-            this._test(delta_hours, 'delta_hours');
-            this._test(delta_minutes, 'delta_minutes');
-            this._test(delta_seconds, 'delta_seconds');
-            this._test(delta_microseconds, 'delta_microseconds');
-
-            this._hour = hour;
-            this._minute = minute;
-            this._second = second;
-            this._microsecond = microsecond;
-            this._delta_hours = delta_hours;
-            this._delta_minutes = delta_minutes;
-            this._delta_seconds = delta_seconds;
-            this._delta_microseconds = delta_microseconds;
-        },
-        pyson: function() {
-            var result = Sao.PYSON.DateTime._super.pyson.call(this);
-            result.__class__ = 'DateTime';
-            result.h = this._hour;
-            result.m = this._minute;
-            result.s = this._second;
-            result.ms = this._microsecond;
-            result.dh = this._delta_hours;
-            result.dm = this._delta_minutes;
-            result.ds = this._delta_seconds;
-            result.dms = this._delta_microseconds;
-            return result;
-        }
-    });
-
-    Sao.PYSON.DateTime.eval_ = function(value, context) {
-        var date = Sao.DateTime(value.y, value.M && value.M - 1, value.d,
-                value.h, value.m, value.s, value.ms && value.ms / 1000);
-        if (value.dy) date.add(value.dy, 'y');
-        if (value.dM) date.add(value.dM, 'M');
-        if (value.dd) date.add(value.dd, 'd');
-        if (value.dh) date.add(value.dh, 'h');
-        if (value.dm) date.add(value.dm, 'm');
-        if (value.ds) date.add(value.ds, 's');
-        if (value.dms) date.add(value.dms / 1000, 'ms');
-        return date;
-    };
-
-    Sao.PYSON.Len = Sao.class_(Sao.PYSON.PYSON, {
-        init: function(value) {
-            Sao.PYSON.Len._super.init.call(this);
-            if (value instanceof Sao.PYSON.PYSON) {
-                if (jQuery(value.types()).not(['object', 'string']).length ||
-                    jQuery(['object', 'string']).not(value.types()).length) {
-                    throw 'value must be an object or a string';
-                }
-            } else {
-                if ((typeof value != 'object') && (typeof value != 'string')) {
-                    throw 'value must be an object or a string';
-                }
-            }
-            this._value = value;
-        },
-        pyson: function() {
-            return {
-                '__class__': 'Len',
-                'v': this._value
-            };
-        },
-        types: function() {
-            return ['integer'];
-        }
-    });
-
-    Sao.PYSON.Len.eval_ = function(value, context) {
-        if (typeof value.v == 'object') {
-            return Object.keys(value.v).length;
-        } else {
-            return value.v.length;
-        }
-    };
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Session = Sao.class_(Object, {
-        init: function(database, login) {
-            this.user_id = null;
-            this.session = null;
-            this.prm = jQuery.when();  // renew promise
-            this.database = database;
-            this.login = login;
-            this.context = {};
-            if (!Sao.Session.current_session) {
-                Sao.Session.current_session = this;
-            }
-        },
-        do_login: function(login, password) {
-            var dfd = jQuery.Deferred();
-            var timeoutID = Sao.common.processing.show();
-            var args = {
-                'method': 'common.db.login',
-                'params': [login, password]
-            };
-            var ajax_prm = jQuery.ajax({
-                'contentType': 'application/json',
-                'data': JSON.stringify(args),
-                'dataType': 'json',
-                'url': '/' + this.database,
-                'type': 'post',
-                'complete': [function() {
-                    Sao.common.processing.hide(timeoutID);
-                }]
-            });
-
-            var ajax_success = function(data) {
-                if (data === null) {
-                    Sao.common.warning.run('',
-                           Sao.i18n.gettext('Unable to reach the server.'));
-                    dfd.reject();
-                } else if (data.error) {
-                    Sao.common.error.run(data.error[0], data.error[1]);
-                    dfd.reject();
-                } else {
-                    if (!data.result) {
-                        this.user_id = null;
-                        this.session = null;
-                        dfd.reject();
-                    } else {
-                        this.user_id = data.result[0];
-                        this.session = data.result[1];
-                        dfd.resolve();
-                    }
-                }
-            };
-            ajax_prm.success(ajax_success.bind(this));
-            ajax_prm.error(dfd.reject);
-            return dfd.promise();
-        },
-        do_logout: function() {
-            if (!(this.user_id && this.session)) {
-                return jQuery.when();
-            }
-            var args = {
-                'method': 'common.db.logout',
-                'params': []
-            };
-            var prm = Sao.rpc(args, this);
-            this.database = null;
-            this.login = null;
-            this.user_id = null;
-            this.session = null;
-            return prm;
-        },
-        reload_context: function() {
-            var args = {
-                'method': 'model.res.user.get_preferences',
-                'params': [true, {}]
-            };
-            // Call with custom session to not send context
-            var session = jQuery.extend({}, this);
-            session.context = {};
-            var prm = Sao.rpc(args, session);
-            return prm.then(function(context) {
-                this.context = context;
-            }.bind(this));
-        }
-    });
-
-    Sao.Session.login_dialog = function() {
-        var dialog = new Sao.Dialog(Sao.i18n.gettext('Login'), 'lg');
-        dialog.database_select = jQuery('<select/>', {
-            'class': 'form-control',
-            'id': 'login-database'
-        }).hide();
-        dialog.database_input = jQuery('<input/>', {
-            'class': 'form-control',
-            'id': 'login-database'
-        }).hide();
-        dialog.login_input = jQuery('<input/>', {
-            'class': 'form-control',
-            'id': 'login-login',
-            'placeholder': Sao.i18n.gettext('Login')
-        });
-        dialog.password_input = jQuery('<input/>', {
-            'class': 'form-control',
-            'type': 'password',
-            'id': 'login-password',
-            'placeholder': Sao.i18n.gettext('Password')
-        });
-        dialog.body.append(jQuery('<div/>', {
-            'class': 'form-group'
-        }).append(jQuery('<label/>', {
-            'class': 'control-label',
-            'for': 'login-database'
-        }).append(Sao.i18n.gettext('Database')))
-        .append(dialog.database_select)
-        .append(dialog.database_input)
-        ).append(jQuery('<div/>', {
-            'class': 'form-group'
-        }).append(jQuery('<label/>', {
-            'class': 'control-label',
-            'for': 'login-login'
-        }).append(Sao.i18n.gettext('Login')))
-        .append(dialog.login_input)
-        ).append(jQuery('<div/>', {
-            'class': 'form-group'
-        }).append(jQuery('<label/>', {
-            'class': 'control-label',
-            'for': 'login-password'
-        }).append(Sao.i18n.gettext('Password')))
-        .append(dialog.password_input));
-        dialog.button = jQuery('<button/>', {
-            'class': 'btn btn-primary',
-            'type': 'submit'
-        }).append(Sao.i18n.gettext('Login')).appendTo(dialog.footer);
-        return dialog;
-    };
-
-    Sao.Session.get_credentials = function() {
-        var dfd = jQuery.Deferred();
-        var database = window.location.hash.replace(
-                /^(#(!|))/, '') || null;
-        var dialog = Sao.Session.login_dialog();
-
-        var empty_field = function() {
-            return dialog.modal.find('input,select').filter(':visible')
-                .filter(function() {
-                    return !jQuery(this).val();
-                });
-        };
-
-        var ok_func = function() {
-            var login = dialog.login_input.val();
-            var password = dialog.password_input.val();
-            var database = database || dialog.database_select.val() ||
-                dialog.database_input.val();
-            dialog.modal.find('.has-error').removeClass('has-error');
-            if (!(login && password && database)) {
-                empty_field().closest('.form-group').addClass('has-error');
-                return;
-            }
-            dialog.button.focus();
-            dialog.button.prop('disabled', true);
-            var session = new Sao.Session(database, login);
-            session.do_login(login, password)
-                .then(function() {
-                    dfd.resolve(session);
-                    dialog.modal.remove();
-                }, function() {
-                    dialog.button.prop('disabled', false);
-                    dialog.password_input.val('');
-                    empty_field().closest('.form-group').addClass('has-error');
-                    empty_field().first().focus();
-                });
-        };
-
-        dialog.modal.modal({
-            backdrop: false,
-            keyboard: false
-        });
-        dialog.modal.find('form').unbind().submit(function(e) {
-            ok_func();
-            e.preventDefault();
-        });
-
-        jQuery.when(Sao.DB.list()).then(function(databases) {
-            var el;
-            if (jQuery.isEmptyObject(databases)) {
-                el = dialog.database_input;
-            } else {
-                el = dialog.database_select;
-                databases.forEach(function(database) {
-                    el.append(jQuery('<option/>', {
-                        'value': database,
-                        'text': database
-                    }));
-                });
-            }
-            el.show();
-            el.val(database || '');
-            empty_field().first().focus();
-        });
-        return dfd.promise();
-    };
-
-    Sao.Session.password_dialog = function() {
-        var dialog = new Sao.Dialog(Sao.i18n.gettext('Password'), 'lg');
-        dialog.password_input = jQuery('<input/>', {
-            'class': 'form-control',
-            'type': 'password',
-            'id': 'password-password',
-            'placeholder': Sao.i18n.gettext('Password')
-        });
-        dialog.body.append(jQuery('<div/>', {
-            'class': 'form-group'
-        }).append(jQuery('<label/>', {
-            'for': 'password-password'
-        }).append(Sao.i18n.gettext('Password')))
-        .append(dialog.password_input));
-        dialog.button = jQuery('<button/>', {
-            'class': 'btn btn-primary',
-            'type': 'submit'
-        }).append(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
-        return dialog;
-    };
-
-    Sao.Session.renew = function(session) {
-        if (session.prm.state() == 'pending') {
-            return session.prm;
-        }
-        var dfd = jQuery.Deferred();
-        session.prm = dfd.promise();
-        var dialog = Sao.Session.password_dialog();
-        if (!session.login) {
-            dfd.reject();
-            return session.prm;
-        }
-
-        var ok_func = function() {
-            var password = dialog.password_input.val();
-            dialog.button.focus();
-            dialog.button.prop('disabled', true);
-            session.do_login(session.login, password)
-                .then(function() {
-                    dfd.resolve();
-                    dialog.modal.remove();
-                }, function() {
-                    dialog.button.prop('disabled', false);
-                    dialog.password_input.val('').focus();
-                });
-        };
-
-        dialog.modal.modal({
-            backdrop: false,
-            keyboard: false
-        });
-        dialog.modal.on('shown.bs.modal', function() {
-            dialog.password_input.focus();
-        });
-        dialog.modal.find('form').unbind().submit(function(e) {
-            ok_func();
-            e.preventDefault();
-        });
-        dialog.modal.modal('show');
-        return session.prm;
-    };
-
-    Sao.Session.current_session = null;
-
-    Sao.DB = {};
-
-    Sao.DB.list = function() {
-        var timeoutID = Sao.common.processing.show();
-        return jQuery.ajax({
-            'contentType': 'application/json',
-            'data': JSON.stringify({
-                'method': 'common.db.list',
-                'params': [null, null]
-            }),
-            'dataType': 'json',
-            'url': '/',
-            'type': 'post',
-            'complete': [function() {
-                Sao.common.processing.hide(timeoutID);
-            }]
-        }).then(function(data) {
-            return data.result;
-        });
-    };
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Model = Sao.class_(Object, {
-        init: function(name, attributes) {
-            attributes = attributes || {};
-            this.name = name;
-            this.session = Sao.Session.current_session;
-            this.fields = {};
-        },
-        add_fields: function(descriptions) {
-            for (var name in descriptions) {
-                if (descriptions.hasOwnProperty(name) &&
-                    (!(name in this.fields))) {
-                        var desc = descriptions[name];
-                        var Field = Sao.field.get(desc.type);
-                        this.fields[name] = new Field(desc);
-                    }
-            }
-        },
-        execute: function(method, params, context) {
-            if (context === undefined) {
-                context = {};
-            }
-            var args = {
-                'method': 'model.' + this.name + '.' + method,
-                'params': params.concat(context)
-            };
-            return Sao.rpc(args, this.session);
-        },
-        find: function(condition, offset, limit, order, context) {
-            if (!offset) offset = 0;
-            var self = this;
-            var prm = this.execute('search',
-                    [condition, offset, limit, order], context);
-            var instanciate = function(ids) {
-                return Sao.Group(self, context, ids.map(function(id) {
-                    return new Sao.Record(self, id);
-                }));
-            };
-            return prm.pipe(instanciate);
-        },
-        delete_: function(records) {
-            if (jQuery.isEmptyObject(records)) {
-                return jQuery.when();
-            }
-            var record = records[0];
-            var root_group = record.group.root_group();
-            console.assert(records.every(function(r) {
-                return r.model.name == record.model.name;
-            }), 'records not from the same model');
-            console.assert(records.every(function(r) {
-                return r.group.root_group() == record.group.root_group();
-            }), 'records not from the same root group');
-            records = records.filter(function(record) {
-                return record.id >= 0;
-            });
-            var context = {};
-            context._timestamp = {};
-            records.forEach(function(record) {
-                jQuery.extend(context._timestamp, record.get_timestamp());
-            });
-            var record_ids = records.map(function(record) {
-                return record.id;
-            });
-            return root_group.on_write_ids(record_ids).then(function(reload_ids) {
-                reload_ids = reload_ids.filter(function(e) {
-                    return !~reload_ids.indexOf(e);
-                });
-                return this.execute('delete', [record_ids], context)
-                .then(function() {
-                    root_group.reload(reload_ids);
-                });
-            }.bind(this));
-        },
-        copy: function(records, context) {
-            if (jQuery.isEmptyObject(records)) {
-                return jQuery.when();
-            }
-            var record_ids = records.map(function(record) {
-                return record.id;
-            });
-            return this.execute('copy', [record_ids, {}], context);
-        }
-    });
-
-    Sao.Group = function(model, context, array) {
-        array.prm = jQuery.when();
-        array.model = model;
-        array._context = context;
-        array.on_write = [];
-        array.parent = undefined;
-        array.screens = [];
-        array.parent_name = '';
-        array.children = [];
-        array.child_name = '';
-        array.parent_datetime_field = undefined;
-        array.record_removed = [];
-        array.record_deleted = [];
-        array.__readonly = false;
-        array.skip_model_access = false;
-        array.forEach(function(e, i, a) {
-            e.group = a;
-        });
-        array.get_readonly = function() {
-            // Must skip res.user for Preference windows
-            if (this.context._datetime ||
-                    (!Sao.common.MODELACCESS.get(this.model.name).write &&
-                     !this.skip_model_access)) {
-                return true;
-            }
-            return this.__readonly;
-        };
-        array.set_readonly = function(value) {
-            this.__readonly = value;
-        };
-        array.load = function(ids, modified) {
-            var new_records = [];
-            var i, len;
-            for (i = 0, len = ids.length; i < len; i++) {
-                var id = ids[i];
-                var new_record = this.get(id);
-                if (!new_record) {
-                    new_record = new Sao.Record(this.model, id);
-                    new_record.group = this;
-                    this.push(new_record);
-                }
-                new_records.push(new_record);
-            }
-            // Remove previously removed or deleted records
-            var record_removed = [];
-            var record;
-            for (i = 0, len = this.record_removed.length; i < len; i++) {
-                record = this.record_removed[i];
-                if (!~ids.indexOf(record.id)) {
-                    record_removed.push(record);
-                }
-            }
-            this.record_removed = record_removed;
-            var record_deleted = [];
-            for (i = 0, len = this.record_deleted.length; i < len; i++) {
-                record = this.record_deleted[i];
-                if (!~ids.indexOf(record.id)) {
-                    record_deleted.push(record);
-                }
-            }
-            this.record_deleted = record_deleted;
-            if (new_records.length && modified) {
-                this.changed();
-            }
-        };
-        array.get = function(id) {
-            // TODO optimize
-            for (var i = 0, len = this.length; i < len; i++) {
-                var record = this[i];
-                if (record.id == id) {
-                    return record;
-                }
-            }
-        };
-        array.new_ = function(default_, id) {
-            var record = new Sao.Record(this.model, id);
-            record.group = this;
-            if (default_) {
-                record.default_get();
-            }
-            return record;
-        };
-        array.add = function(record, position) {
-            if ((position === undefined) || (position == -1)) {
-                position = this.length;
-            }
-            if (record.group != this) {
-                record.group = this;
-            }
-            this.splice(position, 0, record);
-            for (var record_rm in this.record_removed) {
-                if (record_rm.id == record.id) {
-                    this.record_removed.splice(
-                            this.record_removed.indexOf(record_rm), 1);
-                }
-            }
-            for (var record_del in this.record_deleted) {
-                if (record_del.id == record.id) {
-                    this.record_deleted.splice(
-                            this.record_deleted.indexOf(record_del), 1);
-                }
-            }
-            record._changed.id = true;
-            this.changed();
-            // Set parent field to trigger on_change
-            if (this.parent && this.model.fields[this.parent_name]) {
-                var field = this.model.fields[this.parent_name];
-                if ((field instanceof Sao.field.Many2One) ||
-                        field instanceof Sao.field.Reference) {
-                    var value = [this.parent.id, ''];
-                    if (field instanceof Sao.field.Reference) {
-                        value = [this.parent.model.name, value];
-                    }
-                    field.set_client(record, value);
-                }
-            }
-            return record;
-        };
-        array.remove = function(record, remove, modified, force_remove) {
-            if (modified === undefined) {
-                modified = true;
-            }
-            var idx = this.indexOf(record);
-            if (record.id >= 0) {
-                if (remove) {
-                    if (~this.record_deleted.indexOf(record)) {
-                        this.record_deleted.splice(
-                                this.record_deleted.indexOf(record), 1);
-                    }
-                    this.record_removed.push(record);
-                } else {
-                    if (~this.record_removed.indexOf(record)) {
-                        this.record_removed.splice(
-                                this.record_removed.indexOf(record), 1);
-                    }
-                    this.record_deleted.push(record);
-                }
-            }
-            if (record.group.parent) {
-                record.group.parent._changed.id = true;
-            }
-            if (modified) {
-                record._changed.id = true;
-            }
-            if (!(record.group.parent) || (record.id < 0) || force_remove) {
-                this._remove(record);
-            }
-            record.group.changed();
-            record.group.root_group().screens.forEach(function(screen) {
-                screen.display();
-            });
-        };
-        array._remove = function(record) {
-            var idx = this.indexOf(record);
-            this.splice(idx, 1);
-        };
-        array.unremove = function(record) {
-            this.record_removed.splice(this.record_removed.indexOf(record), 1);
-            this.record_deleted.splice(this.record_deleted.indexOf(record), 1);
-            record.group.changed();
-            record.group.root_group().screens.forEach(function(screen) {
-                screen.display();
-            });
-        };
-        array.changed = function() {
-            if (!this.parent) {
-                return jQuery.when();
-            }
-            this.parent._changed[this.child_name] = true;
-            var prm = jQuery.Deferred();
-            var changed_prm = this.parent.model.fields[this.child_name]
-                .changed(this.parent);
-            // One2Many.changed could return undefined
-            if (changed_prm) {
-                changed_prm.then(function() {
-                    this.parent.validate(null, true).done(function() {
-                        this.parent.group.changed().done(prm.resolve);
-                    }.bind(this));
-                }.bind(this));
-            } else {
-                prm.resolve();
-            }
-            return prm;
-        };
-        array.root_group = function() {
-            var root = this;
-            var parent = this.parent;
-            while (parent) {
-                root = parent.group;
-                parent = parent.parent;
-            }
-            return root;
-        };
-        array.save = function() {
-            var deferreds = [];
-            this.forEach(function(record) {
-                deferreds.push(record.save());
-            });
-            if (!jQuery.isEmptyObject(this.record_deleted)) {
-                deferreds.push(this.model.delete_(this.record_deleted));
-            }
-            return jQuery.when.apply(jQuery, deferreds);
-        };
-        array.written = function(ids) {
-            if (typeof(ids) == 'number') {
-                ids = [ids];
-            }
-            return this.on_write_ids(ids).then(function(to_reload) {
-                to_reload = to_reload.filter(function(e) {
-                    return !~ids.indexOf(e);
-                });
-                this.root_group().reload(to_reload);
-            }.bind(this));
-        };
-        array.reload = function(ids) {
-            this.children.forEach(function(child) {
-                child.reload(ids);
-            });
-            ids.forEach(function(id) {
-                var record = this.get(id);
-                if (record && jQuery.isEmptyObject(record._changed)) {
-                    record.cancel();
-                }
-            }.bind(this));
-        };
-        array.on_write_ids = function(ids) {
-            var deferreds = [];
-            var result = [];
-            this.on_write.forEach(function(fnct) {
-                var prm = this.model.execute(fnct, [ids], this._context)
-                .then(function(res) {
-                    jQuery.extend(result, res);
-                });
-                deferreds.push(prm);
-            }.bind(this));
-            return jQuery.when.apply(jQuery, deferreds).then(function() {
-                return result.filter(function(e, i, a) {
-                    return i == a.indexOf(e);
-                });
-            });
-        };
-        array.set_parent = function(parent) {
-            this.parent = parent;
-            if (parent && parent.model.name == this.model.name) {
-                this.parent.group.children.push(this);
-            }
-        };
-        array.destroy = function() {
-            if (this.parent) {
-                var i = this.parent.group.children.indexOf(this);
-                if (~i) {
-                    this.parent.group.children.splice(i, 1);
-                }
-            }
-            this.parent = null;
-        };
-        array.domain = function() {
-            var domain = [];
-            this.screens.forEach(function(screen) {
-                if (screen.attributes.domain) {
-                    domain.push(screen.attributes.domain);
-                }
-            });
-            if (this.parent && this.child_name) {
-                var field = this.parent.model.fields[this.child_name];
-                return [domain, field.get_domain(this.parent)];
-            } else {
-                return domain;
-            }
-        };
-        array.context = function() {
-            var context = jQuery.extend({}, this._context);
-            if (this.parent) {
-                jQuery.extend(context, this.parent.get_context());
-                if (this.child_name in this.parent.model.fields) {
-                    var field = this.parent.model.fields[this.child_name];
-                    jQuery.extend(context, field.get_context(this.parent));
-                }
-            }
-            jQuery.extend(context, this._context);
-            if (this.parent_datetime_field) {
-                context._datetime = this.parent.get_eval()
-                    [this.parent_datetime_field];
-            }
-            return context;
-        };
-        array.set_context = function(context) {
-            this._context = jQuery.extend({}, context);
-        };
-        array.clean4inversion = function(domain) {
-            if (jQuery.isEmptyObject(domain)) {
-                return [];
-            }
-            var inversion = new Sao.common.DomainInversion();
-            var head = domain[0];
-            var tail = domain.slice(1);
-            if (~['AND', 'OR'].indexOf(head)) {
-            } else if (inversion.is_leaf(head)) {
-                var field = head[0];
-                if ((field in this.model.fields) &&
-                        (this.model.fields[field].description.readonly)) {
-                    head = [];
-                }
-            } else {
-                head = this.clean4inversion(head);
-            }
-            return [head].concat(this.clean4inversion(tail));
-        };
-        array.domain4inversion = function() {
-            var domain = this.domain();
-            if (!this.__domain4inversion ||
-                    !Sao.common.compare(this.__domain4inversion[0], domain)) {
-                this.__domain4inversion = [domain, this.clean4inversion(domain)];
-            }
-            return this.__domain4inversion[1];
-        };
-        array.get_by_path = function(path) {
-            path = jQuery.extend([], path);
-            var record = null;
-            var group = this;
-
-            var browse_child = function() {
-                if (jQuery.isEmptyObject(path)) {
-                    return record;
-                }
-                var child_name = path[0][0];
-                var id = path[0][1];
-                path.splice(0, 1);
-                record = group.get(id);
-                if (!record) {
-                    return null;
-                }
-                if (!child_name) {
-                    return browse_child();
-                }
-                return record.load(child_name).then(function() {
-                    group = record._values[child_name];
-                    if (!group) {
-                        return null;
-                    }
-                    return browse_child();
-                });
-            };
-            return jQuery.when().then(browse_child);
-        };
-        return array;
-    };
-
-    Sao.Record = Sao.class_(Object, {
-        id_counter: -1,
-        init: function(model, id) {
-            this.model = model;
-            this.group = Sao.Group(model, {}, []);
-            this.id = id || Sao.Record.prototype.id_counter--;
-            this._values = {};
-            this._changed = {};
-            this._loaded = {};
-            this.fields = {};
-            this._timestamp = null;
-            this.attachment_count = -1;
-            this.state_attrs = {};
-            this.autocompletion = {};
-            this.exception = false;
-        },
-        has_changed: function() {
-            return !jQuery.isEmptyObject(this._changed);
-        },
-        save: function(force_reload) {
-            if (force_reload === undefined) {
-                force_reload = false;
-            }
-            var context = this.get_context();
-            var prm = jQuery.when();
-            var values = this.get();
-            if ((this.id < 0) || !jQuery.isEmptyObject(values)) {
-                if (this.id < 0) {
-                    prm = this.model.execute('create', [[values]], context);
-                    var created = function(ids) {
-                        this.id = ids[0];
-                    };
-                    prm.done(created.bind(this));
-                } else {
-                    if (!jQuery.isEmptyObject(values)) {
-                        context._timestamp = this.get_timestamp();
-                        prm = this.model.execute('write', [[this.id], values],
-                            context);
-                    }
-                }
-                prm = prm.done(function() {
-                    this.cancel();
-                    if (force_reload) {
-                        return this.reload();
-                    }
-                }.bind(this));
-                if (this.group) {
-                    prm = prm.done(function() {
-                        return this.group.written(this.id);
-                    }.bind(this));
-                }
-            }
-            if (this.group.parent) {
-                delete this.group.parent._changed[this.group.child_name];
-                prm = prm.done(function() {
-                    return this.group.parent.save(force_reload);
-                }.bind(this));
-            }
-            return prm;
-        },
-        reload: function(fields) {
-            if (this.id < 0) {
-                return jQuery.when();
-            }
-            if (!fields) {
-                return this.load('*');
-            } else {
-                var prms = fields.map(function(field) {
-                    return this.load(field);
-                }.bind(this));
-                return jQuery.when.apply(jQuery, prms);
-            }
-        },
-        load: function(name) {
-            var fname;
-            var prm;
-            if ((this.id < 0) || (name in this._loaded)) {
-                return jQuery.when();
-            }
-            if (this.group.prm.state() == 'pending') {
-                prm = jQuery.Deferred();
-                this.group.prm.then(function() {
-                    this.load(name).then(prm.resolve, prm.reject);
-                }.bind(this));
-                return prm;
-            }
-            var id2record = {};
-            id2record[this.id] = this;
-            var loading;
-            if (name == '*') {
-                loading = 'eager';
-                for (fname in this.model.fields) {
-                    if (!this.model.fields.hasOwnProperty(fname)) {
-                        continue;
-                    }
-                    var field_loading = (
-                            this.model.fields[fname].description.loading ||
-                            'eager');
-                    if (field_loading != 'eager') {
-                        loading = 'lazy';
-                        break;
-                    }
-                }
-            } else {
-                loading = (this.model.fields[name].description.loading ||
-                        'eager');
-            }
-            var fnames = [];
-            if (loading == 'eager') {
-                for (fname in this.model.fields) {
-                    if (!this.model.fields.hasOwnProperty(fname)) {
-                        continue;
-                    }
-                    if ((this.model.fields[fname].description.loading ||
-                                'eager') == 'eager') {
-                        fnames.push(fname);
-                    }
-                }
-            } else {
-                fnames = Object.keys(this.model.fields);
-            }
-            fnames = fnames.filter(function(e, i, a) {
-                return !(e in this._loaded);
-            }.bind(this));
-            var fnames_to_fetch = fnames.slice();
-            var rec_named_fields = ['many2one', 'one2one', 'reference'];
-            for (var i in fnames) {
-                fname = fnames[i];
-                var fdescription = this.model.fields[fname].description;
-                if (~rec_named_fields.indexOf(fdescription.type))
-                    fnames_to_fetch.push(fname + '.rec_name');
-            }
-            if (!~fnames.indexOf('rec_name')) {
-                fnames_to_fetch.push('rec_name');
-            }
-            fnames_to_fetch.push('_timestamp');
-
-            var context = jQuery.extend({}, this.get_context());
-            if (loading == 'eager') {
-                var limit = parseInt(Sao.config.limit / fnames_to_fetch.length,
-                        10);
-
-                var filter_group = function(record) {
-                    return !(name in record._loaded) && (record.id >= 0);
-                };
-                var filter_parent_group = function(record) {
-                    return (filter_group(record) &&
-                            (id2record[record.id] === undefined) &&
-                            ((record.group === this.group) ||
-                             // Don't compute context for same group
-                             (JSON.stringify(record.get_context()) ===
-                              JSON.stringify(context))));
-                }.bind(this);
-                var group, filter;
-                if (this.group.parent &&
-                        (this.group.parent.model.name == this.model.name)) {
-                    group = [];
-                    group = group.concat.apply(
-                            group, this.group.parent.group.children);
-                    filter = filter_parent_group;
-                } else {
-                    group = this.group;
-                    filter = filter_group;
-                }
-                var idx = group.indexOf(this);
-                if (~idx) {
-                    var length = group.length;
-                    var n = 1;
-                    while ((Object.keys(id2record).length < limit) &&
-                        ((idx - n >= 0) || (idx + n < length)) &&
-                        (n < 2 * limit)) {
-                            var record;
-                            if (idx - n >= 0) {
-                                record = group[idx - n];
-                                if (filter(record)) {
-                                    id2record[record.id] = record;
-                                }
-                            }
-                            if (idx + n < length) {
-                                record = group[idx + n];
-                                if (filter(record)) {
-                                    id2record[record.id] = record;
-                                }
-                            }
-                            n++;
-                        }
-                }
-            }
-
-            for (fname in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(fname)) {
-                    continue;
-                }
-                if ((this.model.fields[fname].description.type == 'binary') &&
-                        ~fnames_to_fetch.indexOf(fname, fnames_to_fetch)) {
-                    context[this.model.name + '.' + fname] = 'size';
-                }
-            }
-            prm = this.model.execute('read', [Object.keys(id2record).map(
-                        function (e) { return parseInt(e, 10); }),
-                    fnames_to_fetch], context);
-            var succeed = function(values, exception) {
-                if (exception === undefined) exception = false;
-                var id2value = {};
-                values.forEach(function(e, i, a) {
-                    id2value[e.id] = e;
-                });
-                for (var id in id2record) {
-                    if (!id2record.hasOwnProperty(id)) {
-                        continue;
-                    }
-                    var record = id2record[id];
-                    if (!record.exception) {
-                        record.exception = exception;
-                    }
-                    var value = id2value[id];
-                    if (record && value) {
-                        for (var key in this._changed) {
-                            if (!this._changed.hasOwnProperty(key)) {
-                                continue;
-                            }
-                            delete value[key];
-                        }
-                        record.set(value);
-                    }
-                }
-            }.bind(this);
-            var failed = function() {
-                var failed_values = [];
-                var default_values;
-                for (var id in id2record) {
-                    default_values = {
-                        id: id
-                    };
-                    for (var i in fnames_to_fetch) {
-                        default_values[fnames_to_fetch[i]] = null;
-                    }
-                    failed_values.push(default_values);
-                }
-                succeed(failed_values, true);
-            };
-            this.group.prm = prm.then(succeed, failed);
-            return this.group.prm;
-        },
-        set: function(values) {
-            var name, value;
-            var rec_named_fields = ['many2one', 'one2one', 'reference'];
-            var later = {};
-            for (name in values) {
-                if (!values.hasOwnProperty(name)) {
-                    continue;
-                }
-                value = values[name];
-                if (name == '_timestamp') {
-                    // Always keep the older timestamp
-                    if (!this._timestamp) {
-                        this._timestamp = value;
-                    }
-                    continue;
-                }
-                if (!(name in this.model.fields)) {
-                    if (name == 'rec_name') {
-                        this._values[name] = value;
-                    }
-                    continue;
-                }
-                if (this.model.fields[name] instanceof Sao.field.One2Many) {
-                    later[name] = value;
-                }
-                if ((this.model.fields[name] instanceof Sao.field.Many2One) ||
-                        (this.model.fields[name] instanceof Sao.field.Reference)) {
-                    var field_rec_name = name + '.rec_name';
-                    if (values.hasOwnProperty(field_rec_name)) {
-                        this._values[field_rec_name] = values[field_rec_name];
-                    }
-                    else if (this._values.hasOwnProperty(field_rec_name)) {
-                        delete this._values[field_rec_name];
-                    }
-                }
-                this.model.fields[name].set(this, value);
-                this._loaded[name] = true;
-            }
-            for (name in later) {
-                value = later[name];
-                this.model.fields[name].set(this, value);
-                this._loaded[name] = true;
-            }
-        },
-        get: function() {
-            var value = {};
-            for (var name in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(name)) {
-                    continue;
-                }
-                var field = this.model.fields[name];
-                if (field.description.readonly) {
-                    continue;
-                }
-                if ((this._changed[name] === undefined) && this.id >= 0) {
-                    continue;
-                }
-                value[name] = field.get(this);
-            }
-            return value;
-        },
-        invalid_fields: function() {
-            var fields = {};
-            for (var fname in this.model.fields) {
-                var field = this.model.fields[fname];
-                var invalid = field.get_state_attrs(this).invalid;
-                if (invalid) {
-                    fields[fname] = invalid;
-                }
-            }
-            return fields;
-        },
-        get_context: function() {
-            return this.group.context();
-        },
-        field_get: function(name) {
-            return this.model.fields[name].get(this);
-        },
-        field_set: function(name, value) {
-            this.model.fields[name].set(this, value);
-        },
-        field_get_client: function(name) {
-            return this.model.fields[name].get_client(this);
-        },
-        field_set_client: function(name, value, force_change) {
-            this.model.fields[name].set_client(this, value, force_change);
-        },
-        default_get: function() {
-            var promises = [];
-            if (!jQuery.isEmptyObject(this.model.fields)) {
-                var prm = this.model.execute('default_get',
-                        [Object.keys(this.model.fields)], this.get_context());
-                var force_parent = function(values) {
-                    if (this.group.parent &&
-                            this.group.parent_name in this.group.model.fields) {
-                        var parent_field =
-                            this.group.model.fields[this.group.parent_name];
-                        if (parent_field instanceof Sao.field.Reference) {
-                            values[this.group.parent_name] = [
-                                this.group.parent.model.name,
-                                this.group.parent.id];
-                        } else if (parent_field.description.relation ==
-                                this.group.parent.model.name) {
-                            values[this.group.parent_name] =
-                                this.group.parent.id;
-                        }
-                    }
-                    return values;
-                }.bind(this);
-                prm = prm.pipe(force_parent).then(function(values) {
-                    this.set_default(values);
-                    return values;
-                }.bind(this));
-                promises.push(prm);
-            }
-            for (var fname in this.model.fields) {
-                var field = this.model.fields[fname];
-                if (field.description.autocomplete &&
-                        field.description.autocomplete.length > 0) {
-                    promises.push(this.do_autocomplete(fname));
-                }
-            }
-            return jQuery.when.apply(promises);
-        },
-        set_default: function(values) {
-            var fieldnames = [];
-            for (var fname in values) {
-                if (!values.hasOwnProperty(fname)) {
-                    continue;
-                }
-                var value = values[fname];
-                if (!(fname in this.model.fields)) {
-                    continue;
-                }
-                if ((this.model.fields[fname] instanceof Sao.field.Many2One) ||
-                        (this.model.fields[fname] instanceof Sao.field.Reference)) {
-                    var field_rec_name = fname + '.rec_name';
-                    if (values.hasOwnProperty(field_rec_name)) {
-                        this._values[field_rec_name] = values[field_rec_name];
-                    } else if (this._values.hasOwnProperty(field_rec_name)) {
-                        delete this._values[field_rec_name];
-                    }
-                }
-                this.model.fields[fname].set_default(this, value);
-                this._loaded[fname] = true;
-                fieldnames.push(fname);
-            }
-            this.on_change(fieldnames);
-            this.on_change_with(fieldnames);
-            this.validate(null, true).then(function() {
-                this.group.root_group().screens.forEach(function(screen) {
-                    screen.display();
-                });
-            }.bind(this));
-        },
-        get_timestamp: function() {
-            var timestamps = {};
-            timestamps[this.model.name + ',' + this.id] = this._timestamp;
-            for (var fname in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(fname)) {
-                    continue;
-                }
-                if (!(fname in this._loaded)) {
-                    continue;
-                }
-                jQuery.extend(timestamps,
-                    this.model.fields[fname].get_timestamp(this));
-            }
-            return timestamps;
-        },
-        get_eval: function() {
-            var value = {};
-            for (var key in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(key) && this.id >= 0)
-                    continue;
-                value[key] = this.model.fields[key].get_eval(this);
-            }
-            return value;
-        },
-        get_on_change_value: function(skip) {
-            var value = {};
-            for (var key in this.model.fields) {
-                if (skip && ~skip.indexOf(key)) {
-                    continue;
-                }
-                if ((this.id >= 0) &&
-                        (!this._loaded[key] || !this._changed[key])) {
-                    continue;
-                }
-                value[key] = this.model.fields[key].get_on_change_value(this);
-            }
-            value.id = this.id;
-            return value;
-        },
-        _get_on_change_args: function(args) {
-            var result = {};
-            var values = Sao.common.EvalEnvironment(this, 'on_change');
-            args.forEach(function(arg) {
-                var scope = values;
-                arg.split('.').forEach(function(e) {
-                    if (scope !== undefined) {
-                        scope = scope[e];
-                    }
-                });
-                result[arg] = scope;
-            });
-            result.id = this.id;
-            return result;
-        },
-        on_change: function(fieldnames) {
-            var values = {};
-            fieldnames.forEach(function(fieldname) {
-                var on_change = this.model.fields[fieldname]
-                .description.on_change;
-                if (!jQuery.isEmptyObject(on_change)) {
-                    values = jQuery.extend(values,
-                        this._get_on_change_args(on_change));
-                }
-            }.bind(this));
-            if (!jQuery.isEmptyObject(values)) {
-                var prm = this.model.execute('on_change',
-                        [values, fieldnames], this.get_context());
-                return prm.then(function(changes) {
-                    changes.forEach(this.set_on_change.bind(this));
-                }.bind(this));
-            } else {
-                return jQuery.when();
-            }
-        },
-        on_change_with: function(field_names) {
-            var fieldnames = {};
-            var values = {};
-            var later = {};
-            var fieldname, on_change_with;
-            for (fieldname in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(fieldname)) {
-                    continue;
-                }
-                on_change_with = this.model.fields[fieldname]
-                    .description.on_change_with;
-                if (jQuery.isEmptyObject(on_change_with)) {
-                    continue;
-                }
-                for (var i = 0; i < field_names.length; i++) {
-                    if (~on_change_with.indexOf(field_names[i])) {
-                        break;
-                    }
-                }
-                if (i >= field_names.length) {
-                    continue;
-                }
-                if (!jQuery.isEmptyObject(Sao.common.intersect(
-                                Object.keys(fieldnames).sort(),
-                                on_change_with.sort()))) {
-                    later[fieldname] = true;
-                    continue;
-                }
-                fieldnames[fieldname] = true;
-                values = jQuery.extend(values,
-                        this._get_on_change_args(on_change_with));
-                if ((this.model.fields[fieldname] instanceof
-                            Sao.field.Many2One) ||
-                        (this.model.fields[fieldname] instanceof
-                         Sao.field.Reference)) {
-                    delete this._values[fieldname + '.rec_name'];
-                }
-            }
-            var prms = [];
-            var prm;
-            if (!jQuery.isEmptyObject(fieldnames)) {
-                prm = this.model.execute('on_change_with',
-                        [values, Object.keys(fieldnames)], this.get_context());
-                prms.push(prm.then(this.set_on_change.bind(this)));
-            }
-            var set_on_change = function(fieldname) {
-                return function(result) {
-                    this.model.fields[fieldname].set_on_change(this, result);
-                };
-            };
-            for (fieldname in later) {
-                if (!later.hasOwnProperty(fieldname)) {
-                    continue;
-                }
-                on_change_with = this.model.fields[fieldname]
-                    .description.on_change_with;
-                values = this._get_on_change_args(on_change_with);
-                prm = this.model.execute('on_change_with_' + fieldname,
-                    [values], this.get_context());
-                prms.push(prm.then(set_on_change(fieldname).bind(this)));
-            }
-            return jQuery.when.apply(jQuery, prms);
-        },
-        set_on_change: function(values) {
-            var fieldname, value;
-            for (fieldname in values) {
-                if (!values.hasOwnProperty(fieldname)) {
-                    continue;
-                }
-                value = values[fieldname];
-                if (!(fieldname in this.model.fields)) {
-                    continue;
-                }
-                if ((this.model.fields[fieldname] instanceof
-                            Sao.field.Many2One) ||
-                        (this.model.fields[fieldname] instanceof
-                         Sao.field.Reference)) {
-                    var field_rec_name = fieldname + '.rec_name';
-                    if (values.hasOwnProperty(field_rec_name)) {
-                        this._values[field_rec_name] = values[field_rec_name];
-                    } else if (this._values.hasOwnProperty(field_rec_name)) {
-                        delete this._values[field_rec_name];
-                    }
-                }
-                this.model.fields[fieldname].set_on_change(this, value);
-            }
-        },
-        autocomplete_with: function(fieldname) {
-            var promises = [];
-            for (var fname in this.model.fields) {
-                var field = this.model.fields[fname];
-                var autocomplete = field.description.autocomplete || [];
-                if (!~autocomplete.indexOf(fieldname)) {
-                    continue;
-                }
-                promises.push(this.do_autocomplete(fname));
-            }
-            return jQuery.when.apply(promises);
-        },
-        do_autocomplete: function(fieldname) {
-            this.autocompletion[fieldname] = [];
-            var field = this.model.fields[fieldname];
-            var autocomplete = field.description.autocomplete;
-            var values = this._get_on_change_args(autocomplete);
-            var prm = this.model.execute(
-                    'autocomplete_' + fieldname, [values], this.get_context());
-            return prm.then(function(result) {
-                this.autocompletion[fieldname] = result;
-            }.bind(this));
-        },
-        expr_eval: function(expr) {
-            if (typeof(expr) != 'string') return expr;
-            var ctx = jQuery.extend({}, this.get_context());
-            ctx.context = jQuery.extend({}, this.model.session.context, ctx);
-            jQuery.extend(ctx, this.get_eval());
-            ctx.active_model = this.model.name;
-            ctx.active_id = this.id;
-            ctx._user = this.model.session.user_id;
-            if (this.group.parent && this.group.parent_name) {
-                var parent_env = Sao.common.EvalEnvironment(this.group.parent);
-                ctx['_parent_' + this.group.parent_name] = parent_env;
-            }
-            return new Sao.PYSON.Decoder(ctx).decode(expr);
-        },
-        rec_name: function() {
-            var prm = this.model.execute('read', [[this.id], ['rec_name']],
-                    this.get_context());
-            return prm.then(function(values) {
-                return values[0].rec_name;
-            });
-        },
-        validate: function(fields, softvalidation, pre_validate, sync) {
-            var validate_fields = function() {
-                var result = true;
-                var exclude_fields = [];
-                this.group.screens.forEach(function(screen) {
-                    if (screen.exclude_field) {
-                        exclude_fields.push(screen.exclude_field);
-                    }
-                });
-                for (var fname in this.model.fields) {
-                    if (!this.model.fields.hasOwnProperty(fname)) {
-                        continue;
-                    }
-                    var field = this.model.fields[fname];
-                    if (fields && !~fields.indexOf(fname)) {
-                        continue;
-                    }
-                    if (field.get_state_attrs(this).readonly) {
-                        continue;
-                    }
-                    if (~exclude_fields.indexOf(fname)) {
-                        continue;
-                    }
-                    if (!field.validate(this, softvalidation, pre_validate)) {
-                        result = false;
-                    }
-                }
-                return result;
-            }.bind(this);
-            if (sync) {
-                return validate_fields();
-            } else {
-                return this._check_load(fields).then(validate_fields);
-            }
-        },
-        pre_validate: function() {
-            if (jQuery.isEmptyObject(this._changed)) {
-                return jQuery.Deferred().resolve(true);
-            }
-            var values = this._get_on_change_args(Object.keys(this._changed));
-            return this.model.execute('pre_validate',
-                    [values], this.get_context())
-                .then(function() {
-                    return true;
-                }, function() {
-                    return false;
-                });
-        },
-        cancel: function() {
-            this._loaded = {};
-            this._changed = {};
-            this._timestamp = null;
-        },
-        _check_load: function(fields) {
-            if (!this.get_loaded(fields)) {
-                return this.reload(fields);
-            }
-            return jQuery.when();
-        },
-        get_loaded: function(fields) {
-            if (!jQuery.isEmptyObject(fields)) {
-                var result = true;
-                fields.forEach(function(field) {
-                    if (!(field in this._loaded) | !(field in this._changed)) {
-                        result = false;
-                    }
-                }.bind(this));
-                return result;
-            }
-            return Sao.common.compare(Object.keys(this.model.fields).sort(),
-                    Object.keys(this._loaded).sort());
-        },
-        root_parent: function root_parent() {
-            var parent = this;
-            while (!parent.group.parent) {
-                parent = parent.group.parent;
-            }
-            return parent;
-        },
-        get_path: function(group) {
-            var path = [];
-            var i = this;
-            var child_name = '';
-            while (i) {
-                path.push([child_name, i.id]);
-                if (i.group === group) {
-                    break;
-                }
-                child_name = i.group.child_name;
-                i = i.group.parent;
-            }
-            path.reverse();
-            return path;
-        },
-        deleted: function() {
-            return Boolean(~this.group.record_deleted.indexOf(this));
-        },
-        removed: function() {
-            return Boolean(~this.group.record_removed.indexOf(this));
-        },
-        readonly: function() {
-            return this.deleted() || this.removed() || this.exception;
-        },
-        set_field_context: function() {
-            for (var name in this.model.fields) {
-                if (!this.model.fields.hasOwnProperty(name)) {
-                    continue;
-                }
-                var field = this.model.fields[name];
-                var value = this._values[name];
-                if (!value || !value.set_context) {
-                    continue;
-                }
-                var context = field.description.context;
-                if (context) {
-                    value.set_context(this.expr_eval(context));
-                }
-            }
-        },
-        get_attachment_count: function(reload) {
-            var prm = jQuery.Deferred();
-            if (this.id < 0) {
-                prm.resolve(0);
-                return prm;
-            }
-            if ((this.attachment_count < 0) || reload) {
-                prm = Sao.rpc({
-                    method: 'model.ir.attachment.search_count',
-                    params: [
-                    [['resource', '=', this.model.name + ',' + this.id]],
-                    this.get_context()]
-                }, this.model.session).then(function(count) {
-                    this.attachment_count = count;
-                    return count;
-                }.bind(this));
-            } else {
-                prm.resolve(this.attachment_count);
-            }
-            return prm;
-        }
-    });
-
-
-    Sao.field = {};
-
-    Sao.field.get = function(type) {
-        switch (type) {
-            case 'char':
-                return Sao.field.Char;
-            case 'selection':
-                return Sao.field.Selection;
-            case 'datetime':
-                return Sao.field.DateTime;
-            case 'date':
-                return Sao.field.Date;
-            case 'time':
-                return Sao.field.Time;
-            case 'timedelta':
-                return Sao.field.TimeDelta;
-            case 'float':
-                return Sao.field.Float;
-            case 'numeric':
-                return Sao.field.Numeric;
-            case 'integer':
-                return Sao.field.Integer;
-            case 'boolean':
-                return Sao.field.Boolean;
-            case 'many2one':
-                return Sao.field.Many2One;
-            case 'one2one':
-                return Sao.field.One2One;
-            case 'one2many':
-                return Sao.field.One2Many;
-            case 'many2many':
-                return Sao.field.Many2Many;
-            case 'reference':
-                return Sao.field.Reference;
-            case 'binary':
-                return Sao.field.Binary;
-            case 'dict':
-                return Sao.field.Dict;
-            default:
-                return Sao.field.Char;
-        }
-    };
-
-    Sao.field.Field = Sao.class_(Object, {
-        _default: null,
-        init: function(description) {
-            this.description = description;
-            this.name = description.name;
-        },
-        set: function(record, value) {
-            record._values[this.name] = value;
-        },
-        get: function(record) {
-            var value = record._values[this.name];
-            if (value === undefined) {
-                value = this._default;
-            }
-            return value;
-        },
-        set_client: function(record, value, force_change) {
-            var previous_value = this.get(record);
-            this.set(record, value);
-            // Use stringify to compare object instance like Number for Decimal
-            if (JSON.stringify(previous_value) !=
-                JSON.stringify(this.get(record))) {
-                record._changed[this.name] = true;
-                this.changed(record).done(function() {
-                    record.validate(null, true).then(function() {
-                        record.group.changed().done(function() {
-                            var root_group = record.group.root_group();
-                            root_group.screens.forEach(function(screen) {
-                                screen.display();
-                            });
-                        });
-                    });
-                });
-            } else if (force_change) {
-                record._changed[this.name] = true;
-                this.changed(record).done(function() {
-                    record.validate(null, true).then(function() {
-                        var root_group = record.group.root_group();
-                        root_group.screens.forEach(function(screen) {
-                            screen.display();
-                        });
-                    });
-                });
-            }
-        },
-        get_client: function(record) {
-            return this.get(record);
-        },
-        set_default: function(record, value) {
-            record._values[this.name] = value;
-            record._changed[this.name] = true;
-        },
-        set_on_change: function(record, value) {
-            record._values[this.name] = value;
-            record._changed[this.name] = true;
-        },
-        changed: function(record) {
-            var prms = [];
-            if (this.get_state_attrs(record).readonly) {
-                return jQuery.when();
-            }
-            prms.push(record.on_change([this.name]));
-            prms.push(record.on_change_with([this.name]));
-            prms.push(record.autocomplete_with(this.name));
-            record.set_field_context();
-            return jQuery.when.apply(jQuery, prms);
-        },
-        get_timestamp: function(record) {
-            return {};
-        },
-        get_context: function(record) {
-            var context = jQuery.extend({}, record.get_context());
-            if (record.group.parent) {
-                jQuery.extend(context, record.group.parent.get_context());
-            }
-            jQuery.extend(context,
-                record.expr_eval(this.description.context || {}));
-            return context;
-        },
-        get_domains: function(record, pre_validate) {
-            var inversion = new Sao.common.DomainInversion();
-            var screen_domain = inversion.domain_inversion(
-                    [record.group.domain4inversion(), pre_validate || []],
-                    this.name, Sao.common.EvalEnvironment(record));
-            if ((typeof screen_domain == 'boolean') && !screen_domain) {
-                screen_domain = [['id', '=', null]];
-            } else if ((typeof screen_domain == 'boolean') && screen_domain) {
-                screen_domain = [];
-            }
-            var attr_domain = record.expr_eval(this.description.domain || []);
-            return [screen_domain, attr_domain];
-        },
-        get_domain: function(record) {
-            var domains = this.get_domains(record);
-            var screen_domain = domains[0];
-            var attr_domain = domains[1];
-            var inversion = new Sao.common.DomainInversion();
-            return inversion.concat(
-                    [inversion.localize_domain(screen_domain), attr_domain]);
-        },
-        validation_domains: function(record, pre_validate) {
-            var inversion = new Sao.common.DomainInversion();
-            return inversion.concat(this.get_domains(record, pre_validate));
-        },
-        get_eval: function(record) {
-            return this.get(record);
-        },
-        get_on_change_value: function(record) {
-            return this.get_eval(record);
-        },
-        set_state: function(record, states) {
-            if (states === undefined) {
-                states = ['readonly', 'required', 'invisible'];
-            }
-            var state_changes = record.expr_eval(
-                    this.description.states || {});
-            states.forEach(function(state) {
-                if ((state == 'readonly') && this.description.readonly) {
-                    return;
-                }
-                if (state_changes[state] !== undefined) {
-                    this.get_state_attrs(record)[state] = state_changes[state];
-                } else if (this.description[state] !== undefined) {
-                    this.get_state_attrs(record)[state] =
-                        this.description[state];
-                }
-            }.bind(this));
-            if (record.group.get_readonly() ||
-                    this.get_state_attrs(record).domain_readonly) {
-                this.get_state_attrs(record).readonly = true;
-            }
-        },
-        get_state_attrs: function(record) {
-            if (!(this.name in record.state_attrs)) {
-                record.state_attrs[this.name] = jQuery.extend(
-                        {}, this.description);
-            }
-            if (record.group.get_readonly() || record.readonly()) {
-                record.state_attrs[this.name].readonly = true;
-            }
-            return record.state_attrs[this.name];
-        },
-        check_required: function(record) {
-            var state_attrs = this.get_state_attrs(record);
-            if (state_attrs.required == 1) {
-                if (!this.get(record) && (state_attrs.readonly != 1)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        validate: function(record, softvalidation, pre_validate) {
-            if (this.description.readonly) {
-                return true;
-            }
-            var invalid = false;
-            this.get_state_attrs(record).domain_readonly = false;
-            var inversion = new Sao.common.DomainInversion();
-            var domain = inversion.simplify(this.validation_domains(record,
-                        pre_validate));
-            if (!softvalidation) {
-                if (!this.check_required(record)) {
-                    invalid = 'required';
-                }
-            }
-            if (typeof domain == 'boolean') {
-                if (!domain) {
-                    invalid = 'domain';
-                }
-            } else if (Sao.common.compare(domain, [['id', '=', null]])) {
-                invalid = 'domain';
-            } else {
-                var uniques = inversion.unique_value(domain);
-                var unique = uniques[0];
-                var leftpart = uniques[1];
-                var value = uniques[2];
-                if (unique) {
-                    // If the inverted domain is so constraint that only one
-                    // value is possible we should use it. But we must also pay
-                    // attention to the fact that the original domain might be
-                    // a 'OR' domain and thus not preventing the modification
-                    // of fields.
-                    if (value === false) {
-                        // XXX to remove once server domains are fixed
-                        value = null;
-                    }
-                    var setdefault = true;
-                    var original_domain;
-                    if (!jQuery.isEmptyObject(record.group.domain())) {
-                        original_domain = inversion.merge(record.group.domain());
-                    } else {
-                        original_domain = inversion.merge(domain);
-                    }
-                    var domain_readonly = original_domain[0] == 'AND';
-                    if (leftpart.contains('.')) {
-                        var recordpart = leftpart.split('.', 1)[0];
-                        var localpart = leftpart.split('.', 1)[1];
-                        var constraintfields = [];
-                        if (domain_readonly) {
-                            inversion.localize_domain(
-                                    original_domain.slice(1))
-                                .forEach(function(leaf) {
-                                    constraintfields.push(leaf);
-                                });
-                        }
-                        if ((localpart != 'id') ||
-                                !~constraintfields.indexOf(recordpart)) {
-                            setdefault = false;
-                        }
-                    }
-                    if (setdefault && !pre_validate) {
-                        this.set_client(record, value);
-                        this.get_state_attrs(record).domain_readonly =
-                            domain_readonly;
-                    }
-                }
-                if (!inversion.eval_domain(domain,
-                            Sao.common.EvalEnvironment(record))) {
-                    invalid = domain;
-                }
-            }
-            this.get_state_attrs(record).invalid = invalid;
-            return !invalid;
-        }
-    });
-
-    Sao.field.Char = Sao.class_(Sao.field.Field, {
-        _default: '',
-        get: function(record) {
-            return Sao.field.Char._super.get.call(this, record) || this._default;
-        }
-    });
-
-    Sao.field.Selection = Sao.class_(Sao.field.Field, {
-        _default: null
-    });
-
-    Sao.field.DateTime = Sao.class_(Sao.field.Field, {
-        _default: null,
-        time_format: function(record) {
-            return record.expr_eval(this.description.format);
-        },
-        set_client: function(record, value, force_change) {
-            var current_value;
-            if (value) {
-                if (value.isTime) {
-                    current_value = this.get(record);
-                    if (current_value) {
-                        value = Sao.DateTime.combine(current_value, value);
-                    } else {
-                        value = null;
-                    }
-                } else if (value.isDate) {
-                    current_value = this.get(record);
-                    if (current_value) {
-                        value = Sao.DateTime.combine(value, current_value);
-                    }
-                }
-            }
-            Sao.field.DateTime._super.set_client.call(this, record, value,
-                force_change);
-        },
-        date_format: function(record) {
-            var context = this.get_context(record);
-            return Sao.common.date_format(context.date_format);
-        }
-    });
-
-    Sao.field.Date = Sao.class_(Sao.field.Field, {
-        _default: null,
-        set_client: function(record, value, force_change) {
-            if (value && !value.isDate) {
-                value.isDate = true;
-                value.isDateTime = false;
-            }
-            Sao.field.Date._super.set_client.call(this, record, value,
-                force_change);
-        },
-        date_format: function(record) {
-            var context = this.get_context(record);
-            return Sao.common.date_format(context.date_format);
-        }
-    });
-
-    Sao.field.Time = Sao.class_(Sao.field.Field, {
-        _default: null,
-        time_format: function(record) {
-            return record.expr_eval(this.description.format);
-        },
-        set_client: function(record, value, force_change) {
-            if (value && (value.isDate || value.isDateTime)) {
-                value = Sao.Time(value.hour(), value.minute(),
-                    value.second(), value.millisecond());
-            }
-            Sao.field.Time._super.set_client.call(this, record, value,
-                force_change);
-        }
-    });
-
-    Sao.field.TimeDelta = Sao.class_(Sao.field.Field, {
-        _default: null,
-        converter: function(record) {
-            // TODO allow local context converter
-            return record.model.session.context[this.description.converter];
-        },
-        set_client: function(record, value, force_change) {
-            if (typeof(value) == 'string') {
-                value = Sao.common.timedelta.parse(value, this.converter(record));
-            }
-            Sao.field.TimeDelta._super.set_client.call(
-                this, record, value, force_change);
-        },
-        get_client: function(record) {
-            var value = Sao.field.TimeDelta._super.get_client.call(
-                this, record);
-            return Sao.common.timedelta.format(value, this.converter(record));
-        }
-    });
-
-    Sao.field.Float = Sao.class_(Sao.field.Field, {
-        _default: null,
-        digits: function(record, factor) {
-            if (factor === undefined) {
-                factor = 1;
-            }
-            var digits = record.expr_eval(this.description.digits);
-            if (!digits || !digits.every(function(e) {
-                return e !== null;
-            })) {
-                return;
-            }
-            var shift = Math.round(Math.log(Math.abs(factor)) / Math.LN10);
-            return [digits[0] + shift, digits[1] - shift];
-        },
-        check_required: function(record) {
-            var state_attrs = this.get_state_attrs(record);
-            if (state_attrs.required == 1) {
-                if ((this.get(record) === null) &&
-                    (state_attrs.readonly != 1)) {
-                    return false;
-                }
-            }
-            return true;
-        },
-        convert: function(value) {
-            if (!value) {
-                return null;
-            }
-            value = Number(value);
-            if (isNaN(value)) {
-                value = this._default;
-            }
-            return value;
-        },
-        apply_factor: function(value, factor) {
-            if (value !== null) {
-                value /= factor;
-            }
-            return value;
-        },
-        set_client: function(record, value, force_change, factor) {
-            if (factor === undefined) {
-                factor = 1;
-            }
-            value = this.apply_factor(this.convert(value), factor);
-            Sao.field.Float._super.set_client.call(this, record, value,
-                force_change);
-        },
-        get_client: function(record, factor) {
-            if (factor === undefined) {
-                factor = 1;
-            }
-            var value = this.get(record);
-            if (value !== null) {
-                var digits = this.digits(record, factor);
-                if (digits) {
-                    return (value * factor).toFixed(digits[1]);
-                } else {
-                    return '' + (value * factor);
-                }
-            } else {
-                return '';
-            }
-        }
-    });
-
-    Sao.field.Numeric = Sao.class_(Sao.field.Float, {
-        convert: function(value) {
-            if (!value) {
-                return null;
-            }
-            value = new Sao.Decimal(value);
-            if (isNaN(value.valueOf())) {
-                value = this._default;
-            }
-            return value;
-        },
-        apply_factor: function(value, factor) {
-            value = Sao.field.Numeric._super.apply_factor(value, factor);
-            return new Sao.Decimal(value);
-        }
-    });
-
-    Sao.field.Integer = Sao.class_(Sao.field.Float, {
-        convert: function(value) {
-            value = parseInt(value, 10);
-            if (isNaN(value)) {
-                value = this._default;
-            }
-            return value;
-        }
-    });
-
-    Sao.field.Boolean = Sao.class_(Sao.field.Field, {
-        _default: false,
-        set_client: function(record, value, force_change) {
-            value = Boolean(value);
-            Sao.field.Boolean._super.set_client.call(this, record, value,
-                force_change);
-        },
-        get: function(record) {
-            return Boolean(record._values[this.name]);
-        },
-        get_client: function(record) {
-            return Boolean(record._values[this.name]);
-        }
-    });
-
-    Sao.field.Many2One = Sao.class_(Sao.field.Field, {
-        _default: null,
-        get_client: function(record) {
-            var rec_name = record._values[this.name + '.rec_name'];
-            if (rec_name === undefined) {
-                this.set(record, this.get(record));
-                rec_name = record._values[this.name + '.rec_name'] || '';
-            }
-            return rec_name;
-        },
-        set: function(record, value) {
-            var rec_name = record._values[this.name + '.rec_name'] || '';
-            var store_rec_name = function(rec_name) {
-                record._values[this.name + '.rec_name'] = rec_name[0].rec_name;
-            };
-            if (!rec_name && (value >= 0) && (value !== null)) {
-                var model_name = record.model.fields[this.name].description
-                    .relation;
-                Sao.rpc({
-                    'method': 'model.' + model_name + '.read',
-                    'params': [[value], ['rec_name'], record.get_context()]
-                }, record.model.session).done(store_rec_name.bind(this));
-            } else {
-                store_rec_name.call(this, [{'rec_name': rec_name}]);
-            }
-            record._values[this.name] = value;
-        },
-        set_client: function(record, value, force_change) {
-            var rec_name;
-            if (value instanceof Array) {
-                rec_name = value[1];
-                value = value[0];
-            } else {
-                if (value == this.get(record)) {
-                    rec_name = record._values[this.name + '.rec_name'] || '';
-                } else {
-                    rec_name = '';
-                }
-            }
-            record._values[this.name + '.rec_name'] = rec_name;
-            Sao.field.Many2One._super.set_client.call(this, record, value,
-                    force_change);
-        },
-        validation_domains: function(record, pre_validate) {
-            return this.get_domains(record, pre_validate)[0];
-        },
-        get_domain: function(record) {
-            var domains = this.get_domains(record);
-            var screen_domain = domains[0];
-            var attr_domain = domains[1];
-            var inversion = new Sao.common.DomainInversion();
-            return inversion.concat([inversion.localize_domain(
-                        inversion.inverse_leaf(screen_domain), this.name),
-                    attr_domain]);
-        },
-        get_on_change_value: function(record) {
-            if ((record.group.parent_name == this.name) &&
-                    record.group.parent) {
-                return record.group.parent.get_on_change_value(
-                        [record.group.child_name]);
-            }
-            return Sao.field.Many2One._super.get_on_change_value.call(
-                    this, record);
-        }
-    });
-
-    Sao.field.One2One = Sao.class_(Sao.field.Many2One, {
-    });
-
-    Sao.field.One2Many = Sao.class_(Sao.field.Field, {
-        init: function(description) {
-            Sao.field.One2Many._super.init.call(this, description);
-        },
-        _default: null,
-        _set_value: function(record, value, default_) {
-            this._set_default_value(record);
-            var group = record._values[this.name];
-            var mode;
-            if ((value instanceof Array) && !isNaN(parseInt(value[0], 10))) {
-                mode = 'list ids';
-            } else {
-                mode = 'list values';
-            }
-            var prm = jQuery.when();
-            if ((mode == 'list values') && !jQuery.isEmptyObject(value)) {
-                var context = this.get_context(record);
-                var field_names = {};
-                for (var val in value) {
-                    if (!value.hasOwnProperty(val)) {
-                        continue;
-                    }
-                    for (var fieldname in val) {
-                        if (!val.hasOwnProperty(fieldname)) {
-                            continue;
-                        }
-                        field_names[fieldname] = true;
-                    }
-                }
-                if (!jQuery.isEmptyObject(field_names)) {
-                    var args = {
-                        'method': 'model.' + this.description.relation +
-                            '.fields_get',
-                        'params': [Object.keys(field_names), context]
-                    };
-                    prm = Sao.rpc(args, record.model.session);
-                }
-            }
-            var set_value = function(fields) {
-                if (!jQuery.isEmptyObject(fields)) {
-                    group.model.add_fields(fields);
-                }
-                record._values[this.name] = group;
-                if (mode == 'list ids') {
-                    for (var i = 0, len = group.length; i < len; i++) {
-                        var old_record = group[i];
-                        group.remove(old_record, true);
-                    }
-                    group.load(value);
-                } else {
-                    for (var vals in value) {
-                        if (!value.hasOwnProperty(vals)) {
-                            continue;
-                        }
-                        var new_record = group.new_(false);
-                        if (default_) {
-                            new_record.set_default(vals);
-                            group.add(new_record);
-                        } else {
-                            new_record.id *= 1;
-                            new_record.set(vals);
-                            group.push(new_record);
-                        }
-                    }
-                }
-            };
-            return prm.pipe(set_value.bind(this));
-        },
-        set: function(record, value, _default) {
-            if (_default === undefined) {
-                _default = false;
-            }
-            var group = record._values[this.name];
-            var model;
-            if (group !== undefined) {
-                model = group.model;
-                group.destroy();
-            } else if (record.model.name == this.description.relation) {
-                model = record.model;
-            } else {
-                model = new Sao.Model(this.description.relation);
-            }
-            record._values[this.name] = undefined;
-            this._set_default_value(record, model);
-            return this._set_value(record, value, _default);
-        },
-        get: function(record) {
-            var group = record._values[this.name];
-            if (group === undefined) {
-                return [];
-            }
-            var record_removed = group.record_removed;
-            var record_deleted = group.record_deleted;
-            var result = [];
-            var parent_name = this.description.relation_field || '';
-            var to_add = [];
-            var to_create = [];
-            var to_write = [];
-            for (var i = 0, len = group.length; i < len; i++) {
-                var record2 = group[i];
-                if (~record_removed.indexOf(record2) ||
-                        ~record_deleted.indexOf(record2)) {
-                    continue;
-                }
-                var values;
-                if (record2.id >= 0) {
-                    values = record2.get();
-                    delete values[parent_name];
-                    if (record2.has_changed() &&
-                            !jQuery.isEmptyObject(values)) {
-                        to_write.push([record2.id]);
-                        to_write.push(values);
-                    }
-                    to_add.push(record2.id);
-                } else {
-                    values = record2.get();
-                    delete values[parent_name];
-                    to_create.push(values);
-                }
-            }
-            if (!jQuery.isEmptyObject(to_add)) {
-                result.push(['add', to_add]);
-            }
-            if (!jQuery.isEmptyObject(to_create)) {
-                result.push(['create', to_create]);
-            }
-            if (!jQuery.isEmptyObject(to_write)) {
-                result.push(['write'].concat(to_write));
-            }
-            if (!jQuery.isEmptyObject(record_removed)) {
-                result.push(['remove', record_removed.map(function(r) {
-                    return r.id;
-                })]);
-            }
-            if (!jQuery.isEmptyObject(record_deleted)) {
-                result.push(['delete', record_deleted.map(function(r) {
-                    return r.id;
-                })]);
-            }
-            return result;
-        },
-        set_client: function(record, value, force_change) {
-            // domain inversion try to set None as value
-            if (value === null) {
-                value = [];
-            }
-            // domain inversion could try to set id as value
-            if (typeof value == 'number') {
-                value = [value];
-            }
-
-            var previous_ids = this.get_eval(record);
-            this._set_value(record, value);
-            if (!Sao.common.compare(previous_ids.sort(), value.sort())) {
-                record._changed[this.name] = true;
-                this.changed(record).done(function() {
-                    record.validate(null, true).then(function() {
-                        record.group.changed().done(function() {
-                            var root_group = record.group.root_group();
-                            root_group.screens.forEach(function(screen) {
-                                screen.display();
-                            });
-                        });
-                    });
-                });
-            } else if (force_change) {
-                record._changed[this.name] = true;
-                this.changed(record).done(function() {
-                    record.validate(null, true).then(function() {
-                        var root_group = record.group.root_group();
-                        root_group.screens.forEach(function(screen) {
-                            screen.display();
-                        });
-                    });
-                });
-            }
-        },
-        get_client: function(record) {
-            this._set_default_value(record);
-            return record._values[this.name];
-        },
-        set_default: function(record, value) {
-            var previous_group = record._values[this.name];
-            this.set(record, value, true);
-            record._changed[this.name] = true;
-        },
-        set_on_change: function(record, value) {
-            record._changed[this.name] = true;
-            this._set_default_value(record);
-            if (value instanceof Array) {
-                this._set_value(record, value);
-                return;
-            }
-            var prm = jQuery.when();
-            if (value.add || value.update) {
-                var context = this.get_context(record);
-                var fields = record._values[this.name].model.fields;
-                var field_names = {};
-                var adding_values = [];
-                if (value.add) {
-                    for (var i=0; i < value.add.length; i++) {
-                        adding_values.push(value.add[i][1]);
-                    }
-                }
-                [adding_values, value.update].forEach(function(l) {
-                    if (!jQuery.isEmptyObject(l)) {
-                        l.forEach(function(v) {
-                            Object.keys(v).forEach(function(f) {
-                                if (!(f in fields) &&
-                                    (f != 'id')) {
-                                        field_names[f] = true;
-                                    }
-                            });
-                        });
-                    }
-                });
-                if (!jQuery.isEmptyObject(field_names)) {
-                    var args = {
-                        'method': 'model.' + this.description.relation +
-                            '.fields_get',
-                        'params': [Object.keys(field_names), context]
-                    };
-                    prm = Sao.rpc(args, record.model.session);
-                }
-            }
-
-            var to_remove = [];
-            var group = record._values[this.name];
-            group.forEach(function(record2) {
-                if (!record2.id) {
-                    to_remove.push(record2);
-                }
-            });
-            if (value.remove) {
-                value.remove.forEach(function(record_id) {
-                    var record2 = group.get(record_id);
-                    if (record2) {
-                        to_remove.push(record2);
-                    }
-                }.bind(this));
-            }
-            to_remove.forEach(function(record2) {
-                group.remove(record2, false, true, false);
-            }.bind(this));
-
-            if (value.add || value.update) {
-                prm.then(function(fields) {
-                    group.model.add_fields(fields);
-                    if (value.add) {
-                        value.add.forEach(function(vals) {
-                            var index = vals[0];
-                            var data = vals[1];
-                            var new_record = group.new_(false);
-                            group.add(new_record, index);
-                            new_record.set_on_change(data);
-                        });
-                    }
-                    if (value.update) {
-                        value.update.forEach(function(vals) {
-                            if (!vals.id) {
-                                return;
-                            }
-                            var record2 = group.get(vals.id);
-                            if (record2) {
-                                record2.set_on_change(vals);
-                            }
-                        });
-                    }
-                }.bind(this));
-            }
-        },
-        _set_default_value: function(record, model) {
-            if (record._values[this.name] !== undefined) {
-                return;
-            }
-            if (!model) {
-                model = new Sao.Model(this.description.relation);
-            }
-            if (record.model.name == this.description.relation) {
-                model = record.model;
-            }
-            var context = record.expr_eval(this.description.context || {});
-            var group = Sao.Group(model, context, []);
-            group.set_parent(record);
-            group.parent_name = this.description.relation_field;
-            group.child_name = this.name;
-            record._values[this.name] = group;
-        },
-        get_timestamp: function(record) {
-            var group = record._values[this.name];
-            if (group === undefined) {
-                return {};
-            }
-
-            var timestamps = {};
-            var record2;
-            for (var i = 0, len = group.length; i < len; i++) {
-                record2 = group[i];
-                jQuery.extend(timestamps, record2.get_timestamp());
-            }
-            return timestamps;
-        },
-        get_eval: function(record) {
-            var result = [];
-            var group = record._values[this.name];
-            if (group === undefined) return result;
-
-            var record_removed = group.record_removed;
-            var record_deleted = group.record_deleted;
-            for (var i = 0, len = record._values[this.name].length; i < len;
-                    i++) {
-                var record2 = group[i];
-                if (~record_removed.indexOf(record2) ||
-                        ~record_deleted.indexOf(record2))
-                    continue;
-                result.push(record2.id);
-            }
-            return result;
-        },
-        get_on_change_value: function(record) {
-            var result = [];
-            var group = record._values[this.name];
-            if (group === undefined) return result;
-            for (var i = 0, len = record._values[this.name].length; i < len;
-                    i++) {
-                var record2 = group[i];
-                if (!record2.deleted() || !record2.removed())
-                    result.push(record2.get_on_change_value(
-                                [this.description.relation_field || '']));
-            }
-            return result;
-        },
-        get_removed_ids: function(record) {
-            return record._values[this.name].record_removed.map(function(r) {
-                return r.id;
-            });
-        },
-        get_domain: function(record) {
-            var domains = this.get_domains(record);
-            var screen_domain = domains[0];
-            var attr_domain = domains[1];
-            var inversion = new Sao.common.DomainInversion();
-            return inversion.concat([inversion.localize_domain(
-                        inversion.inverse_leaf(screen_domain), this.name),
-                    attr_domain]);
-        },
-        validation_domains: function(record, pre_validate) {
-            return this.get_domains(record, pre_validate)[0];
-        },
-        validate: function(record, softvalidation, pre_validate) {
-            if (this.description.readonly) {
-                return true;
-            }
-            var invalid = false;
-            var inversion = new Sao.common.DomainInversion();
-            var ldomain = inversion.localize_domain(inversion.domain_inversion(
-                        record.group.clean4inversion(pre_validate || []), this.name,
-                        Sao.common.EvalEnvironment(record)), this.name);
-            if (typeof ldomain == 'boolean') {
-                if (ldomain) {
-                    ldomain = [];
-                } else {
-                    ldomain = [['id', '=', null]];
-                }
-            }
-            for (var i = 0, len = (record._values[this.name] || []).length;
-                    i < len; i++) {
-                var record2 = record._values[this.name][i];
-                if (!record2.get_loaded() && (record2.id >= 0) &&
-                        !pre_validate) {
-                    continue;
-                }
-                if (!record2.validate(null, softvalidation, ldomain, true)) {
-                    invalid = 'children';
-                }
-            }
-            var test = Sao.field.One2Many._super.validate.call(this, record,
-                        softvalidation, pre_validate);
-            if (test && invalid) {
-                this.get_state_attrs(record).invalid = invalid;
-                return false;
-            }
-            return test;
-        },
-        set_state: function(record, states) {
-            this._set_default_value(record);
-            Sao.field.One2Many._super.set_state.call(this, record, states);
-            record._values[this.name].readonly = this.get_state_attrs(record)
-                .readonly;
-        }
-    });
-
-    Sao.field.Many2Many = Sao.class_(Sao.field.One2Many, {
-        get_on_change_value: function(record) {
-            return this.get_eval(record);
-        }
-    });
-
-    Sao.field.Reference = Sao.class_(Sao.field.Field, {
-        _default: null,
-        get_client: function(record) {
-            if (record._values[this.name]) {
-                var model = record._values[this.name][0];
-                var name = record._values[this.name + '.rec_name'] || '';
-                return [model, name];
-            } else {
-                return null;
-            }
-        },
-        get: function(record) {
-            if (record._values[this.name] &&
-                record._values[this.name][0] &&
-                record._values[this.name][1] >= -1) {
-                return record._values[this.name].join(',');
-            }
-            return null;
-        },
-        set_client: function(record, value, force_change) {
-            if (value) {
-                if (typeof(value) == 'string') {
-                    value = value.split(',');
-                }
-                var ref_model = value[0];
-                var ref_id = value[1];
-                var rec_name;
-                if (ref_id instanceof Array) {
-                    rec_name = ref_id[1];
-                    ref_id = ref_id[0];
-                } else {
-                    if (ref_id && !isNaN(parseInt(ref_id, 10))) {
-                        ref_id = parseInt(ref_id, 10);
-                    }
-                    if ([ref_model, ref_id].join(',') == this.get(record)) {
-                        rec_name = record._values[this.name + '.rec_name'] || '';
-                    } else {
-                        rec_name = '';
-                    }
-                }
-                record._values[this.name + '.rec_name'] = rec_name;
-                value = [ref_model, ref_id];
-            }
-            Sao.field.Reference._super.set_client.call(
-                    this, record, value, force_change);
-        },
-        set: function(record, value) {
-            if (!value) {
-                record._values[this.name] = this._default;
-                return;
-            }
-            var ref_model, ref_id;
-            if (typeof(value) == 'string') {
-                ref_model = value.split(',')[0];
-                ref_id = value.split(',')[1];
-                if (!ref_id) {
-                    ref_id = null;
-                } else if (!isNaN(parseInt(ref_id, 10))) {
-                    ref_id = parseInt(ref_id, 10);
-                }
-            } else {
-                ref_model = value[0];
-                ref_id = value[1];
-            }
-            var rec_name = record._values[this.name + '.rec_name'] || '';
-            var store_rec_name = function(rec_name) {
-                record._values[this.name + '.rec_name'] = rec_name;
-            }.bind(this);
-            if (ref_model && ref_id !== null && ref_id >= 0) {
-                if (!rec_name && ref_id >= 0) {
-                    Sao.rpc({
-                        'method': 'model.' + ref_model + '.read',
-                        'params': [[ref_id], ['rec_name'], record.get_context()]
-                    }, record.model.session).done(function(result) {
-                        store_rec_name(result[0].rec_name);
-                    });
-                }
-            } else if (ref_model) {
-                rec_name = '';
-            } else {
-                rec_name = ref_id;
-            }
-            record._values[this.name] = [ref_model, ref_id];
-            store_rec_name(rec_name);
-        },
-        get_on_change_value: function(record) {
-            if ((record.group.parent_name == this.name) &&
-                    record.group.parent) {
-                return [record.group.parent.model.name,
-                    record.group.parent.get_on_change_value(
-                        [record.group.child_name])];
-            }
-            return Sao.field.Reference._super.get_on_change_value.call(
-                    this, record);
-        },
-        validation_domains: function(record, pre_validate) {
-            return this.get_domains(record, pre_validate)[0];
-        },
-        get_domain: function(record) {
-            var model = null;
-            if (record._values[this.name]) {
-                model = record._values[this.name][0];
-            }
-            var domains = this.get_domains(record);
-            var screen_domain = domains[0];
-            var attr_domain = domains[1];
-            var inversion = new Sao.common.DomainInversion();
-            return inversion.concat([inversion.localize_domain(
-                        inversion.filter_leaf(screen_domain, this.name, model),
-                        true), attr_domain]);
-        }
-    });
-
-    Sao.field.Binary = Sao.class_(Sao.field.Field, {
-        _default: null,
-        get_size: function(record) {
-            var data = record._values[this.name] || 0;
-            if (data instanceof Uint8Array) {
-                return data.length;
-            }
-            return data;
-        },
-        get_data: function(record) {
-            var prm = jQuery.when();
-            var data = record._values[this.name] || 0;
-            if (!(data instanceof Uint8Array)) {
-                if (record.id < 0) {
-                    return prm;
-                }
-                var context = record.get_context();
-                prm = record.model.execute('read', [[record.id], [this.name]],
-                    context);
-                prm.done(function(data) {
-                    return data[0][this.name];
-                }.bind(this));
-                return prm;
-            }
-        }
-    });
-
-    Sao.field.Dict = Sao.class_(Sao.field.Field, {
-        _default: {},
-        get: function(record) {
-            return (Sao.field.Dict._super.get.call(this, record) ||
-                    this._default);
-        },
-        get_client: function(record) {
-            return (Sao.field.Dict._super.get_client.call(this, record) ||
-                    this._default);
-        },
-        validation_domains: function(record, pre_validate) {
-            return this.get_domains(record, pre_validate)[0];
-        },
-        get_domain: function(record) {
-            var inversion = new Sao.common.DomainInversion();
-            var domains = this.get_domains(record);
-            var screen_domain = domains[0];
-            var attr_domain = domains[1];
-            return inversion.concat([inversion.localize_domain(
-                        inversion.inverse_leaf(screen_domain)),
-                    attr_domain]);
-        },
-        date_format: function(record) {
-            var context = this.get_context(record);
-            return Sao.common.date_format(context.date_format);
-        },
-        time_format: function(record) {
-            return '%X';
-        }
-    });
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Tab = Sao.class_(Object, {
-        init: function() {
-            Sao.Tab.tabs.push(this);
-            this.buttons = {};
-            this.id = 'tab-' + Sao.Tab.counter++;
-            this.name_el = jQuery('<span/>');
-        },
-        create_tabcontent: function() {
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-
-            var toolbar = this.create_toolbar().appendTo(this.el);
-            this.title = toolbar.find('a.navbar-brand');
-
-            if (this.info_bar) {
-                this.el.append(this.info_bar.el);
-            }
-        },
-        set_menu: function(menu) {
-            this.menu_def().forEach(function(definition) {
-                var icon = definition[0];
-                var name = definition[1];
-                var func = definition[2];
-                var item = jQuery('<li/>', {
-                    'role': 'presentation'
-                }).appendTo(menu);
-                var link = jQuery('<a/>', {
-                    'role': 'menuitem',
-                    'href': '#',
-                    'tabindex': -1
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon ' + icon,
-                    'aria-hidden': 'true'
-                })).append(' ' + name).appendTo(item);
-                if (func) {
-                    link.click(function() {
-                        this[func]();
-                    }.bind(this));
-                } else {
-                    item.addClass('disabled');
-                }
-            }.bind(this));
-        },
-        create_toolbar: function() {
-            var toolbar = jQuery(
-                    '<nav class="navbar navbar-default toolbar" role="navigation">' +
-                    '<div class="container-fluid">' +
-                    '<div class="navbar-header">' +
-                    '<a class="navbar-brand" href="#"></a>' +
-                    '<button type="button" class="navbar-toggle collapsed" ' +
-                    'data-toggle="collapse" ' +
-                    'data-target="#navbar-' + this.id + '">' +
-                    '<span class="sr-only">' +
-                    Sao.i18n.gettext('Toggle navigation') +
-                    '</span>' +
-                    '<span class="icon-bar"></span>' +
-                    '<span class="icon-bar"></span>' +
-                    '<span class="icon-bar"></span>' +
-                    '</div>' +
-                    '<div class="collapse navbar-collapse" ' +
-                    'id="navbar-' + this.id + '">' +
-                    '<ul class="nav navbar-nav">' +
-                    '<li class="dropdown">' +
-                    '<a href="#" class="dropdown-toggle" ' +
-                    'data-toggle="dropdown" role="button" ' +
-                    'aria-expanded="false">' +
-                    '<span class="glyphicon glyphicon-wrench" ' +
-                    'aria-hidden="true"></span>' +
-                    '<span class="visible-xs">' +
-                    Sao.i18n.gettext('Menu') +
-                    '</span>' +
-                    '<span class="caret"></span>' +
-                    '</a>' +
-                    '<ul class="dropdown-menu" role="menu">' +
-                    '</ul>' +
-                    '</li>' +
-                    '</ul>' +
-                    '</div>' +
-                    '</div>' +
-                    '</nav>'
-                    );
-            var wrapper = jQuery('<div/>', {
-                'class': 'nav-wrapper'
-            }).append(toolbar);
-            this.set_menu(toolbar.find('ul[role*="menu"]'));
-
-            var add_button = function(tool) {
-                var item = jQuery('<li/>', {
-                    'role': 'presentation'
-                }).appendTo(toolbar.find('.navbar-collapse > ul'));
-                this.buttons[tool[0]] = jQuery('<a/>', {
-                    'role': 'menuitem',
-                    'href': '#',
-                    'id': tool[0]
-                })
-                .append(jQuery('<span/>', {
-                    'class': 'glyphicon ' + tool[1],
-                    'aria-hidden': 'true'
-                }))
-                .append(jQuery('<span/>', {
-                    'class': 'hidden-sm'
-                }).append(' ' + tool[2]))
-                .appendTo(item);
-                if (tool[4]) {
-                    this.buttons[tool[0]].click(this[tool[4]].bind(this));
-                } else {
-                    item.addClass('disabled');
-                }
-                // TODO tooltip
-            };
-            this.toolbar_def().forEach(add_button.bind(this));
-            var tabs = jQuery('#tabs');
-            toolbar.affix({
-                'target': tabs.parent(),
-                'offset': {
-                    'top': function() {
-                        return tabs.find('.nav-tabs').height();
-                    }
-                }
-            });
-            toolbar.on('affix.bs.affix', function() {
-                wrapper.height(toolbar.height());
-            });
-            toolbar.on('affix-top.bs.affix affix-bottom.bs.affix',
-                    function() {
-                        wrapper.height('');
-                    });
-            toolbar.on('affixed.bs.affix', function() {
-                Sao.Tab.affix_set_with(toolbar);
-            });
-            toolbar.on('affixed-top.bs.affix affixed-bottom.bs.affix',
-                    function() {
-                        Sao.Tab.affix_unset_width(toolbar);
-                    });
-            return wrapper;
-        },
-        close: function() {
-            var tabs = jQuery('#tabs');
-            var tab = tabs.find('#nav-' + this.id);
-            var content = tabs.find('#' + this.id);
-            tabs.find('a[href="#' + this.id + '"]').tab('show');
-            return this._close_allowed().then(function() {
-                var next = tab.next();
-                if (!next.length) {
-                    next = tab.prev();
-                }
-                tab.remove();
-                content.remove();
-                Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(this), 1);
-                if (next) {
-                    next.find('a').tab('show');
-                }
-                tabs.trigger('ready');
-            }.bind(this));
-        },
-        _close_allowed: function() {
-            return jQuery.when();
-        },
-        set_name: function(name) {
-            this.name_el.text(name);
-        }
-    });
-
-    Sao.Tab.affix_set_with = function(toolbar) {
-        var width = jQuery(toolbar.parent()).width();
-        toolbar.css('width', width);
-    };
-    Sao.Tab.affix_unset_width = function(toolbar) {
-        toolbar.css('width', '');
-    };
-    jQuery(window).resize(function() {
-        jQuery('.toolbar').each(function(i, toolbar) {
-            toolbar = jQuery(toolbar);
-            toolbar.affix('checkPosition');
-            if (toolbar.hasClass('affix')) {
-                Sao.Tab.affix_set_with(toolbar);
-            } else {
-                Sao.Tab.affix_unset_width(toolbar);
-            }
-        });
-    });
-
-    Sao.Tab.counter = 0;
-    Sao.Tab.tabs = [];
-    Sao.Tab.tabs.close = function(warning) {
-        if (warning && Sao.Tab.tabs.length) {
-            return Sao.common.sur.run(
-                    Sao.i18n.gettext(
-                        'The following action requires to close all tabs.\n' +
-                        'Do you want to continue?')).then(function() {
-                return Sao.Tab.tabs.close(false);
-            });
-        }
-        if (Sao.Tab.tabs.length) {
-            var tab = Sao.Tab.tabs[0];
-            return tab.close().then(function() {
-                if (!~Sao.Tab.tabs.indexOf(tab)) {
-                    return Sao.Tab.tabs.close();
-                } else {
-                    return jQuery.Deferred().reject();
-                }
-            });
-        }
-        if (Sao.main_menu_screen) {
-            return Sao.main_menu_screen.save_tree_state().then(function() {
-                Sao.main_menu_screen = null;
-            });
-        }
-        return jQuery.when();
-    };
-    Sao.Tab.tabs.get_current = function() {
-        var tabs = jQuery('#tabs > ul');
-        var i = tabs.find('li').index(tabs.find('li.active'));
-        return Sao.Tab.tabs[i];
-    };
-    Sao.Tab.tabs.close_current = function() {
-        var tab = this.get_current();
-        tab.close();
-    };
-
-    Sao.Tab.create = function(attributes) {
-        if (attributes.context === undefined) {
-            attributes.context = {};
-        }
-        var tab;
-        if (attributes.model) {
-            tab = new Sao.Tab.Form(attributes.model, attributes);
-        } else {
-            tab = new Sao.Tab.Board(attributes);
-        }
-        Sao.Tab.add(tab);
-    };
-
-    Sao.Tab.add = function(tab) {
-        var tabs = jQuery('#tabs');
-        var tab_link = jQuery('<a/>', {
-            'aria-controls': tab.id,
-            'role': 'tab',
-            'data-toggle': 'tab',
-            'href': '#' + tab.id
-        })
-        .append(jQuery('<button/>', {
-            'class': 'close'
-        }).append(jQuery('<span/>', {
-            'aria-hidden': true
-        }).append('×')).append(jQuery('<span/>', {
-            'class': 'sr-only'
-        }).append(Sao.i18n.gettext('Close'))).click(function() {
-            tab.close();
-        }))
-        .append(tab.name_el);
-        jQuery('<li/>', {
-            'role': 'presentation',
-            id: 'nav-' + tab.id
-        }).append(tab_link)
-        .appendTo(tabs.find('> .nav-tabs'));
-        jQuery('<div/>', {
-            role: 'tabpanel',
-            'class': 'tab-pane',
-            id: tab.id
-        }).html(tab.el)
-        .appendTo(tabs.find('> .tab-content'));
-        tab_link.on('shown.bs.tab', function() {
-            Sao.View.resize(tab.el);
-        });
-        tab_link.tab('show');
-        tabs.trigger('ready');
-    };
-
-    Sao.Tab.Form = Sao.class_(Sao.Tab, {
-        class_: 'tab-form',
-        init: function(model_name, attributes) {
-            Sao.Tab.Form._super.init.call(this);
-            var screen = new Sao.Screen(model_name, attributes);
-            screen.tab = this;
-            this.screen = screen;
-            this.attributes = jQuery.extend({}, attributes);
-
-            this.info_bar = new Sao.Window.InfoBar();
-            this.create_tabcontent();
-
-            var access = Sao.common.MODELACCESS.get(model_name);
-            [['new', 'create'], ['save', 'write']].forEach(function(e) {
-                var button = e[0];
-                var access_type = e[1];
-                this.buttons[button].prop('disabled', !access[access_type]);
-            }.bind(this));
-
-            this.view_prm = this.screen.switch_view().done(function() {
-                this.set_name(attributes.name ||
-                        this.screen.current_view.attributes.string);
-                this.el.append(screen.screen_container.el);
-                if (attributes.res_id) {
-                    screen.group.load([attributes.res_id]);
-                    screen.set_current_record(
-                        screen.group.get(attributes.res_id));
-                    screen.display();
-                } else {
-                    if (screen.current_view.view_type == 'form') {
-                        screen.new_();
-                    }
-                    if (~['tree', 'graph', 'calendar'].indexOf(
-                            screen.current_view.view_type)) {
-                        screen.search_filter();
-                    }
-                }
-                this.update_revision();
-            }.bind(this));
-        },
-        toolbar_def: function() {
-            return [
-                ['new', 'glyphicon-edit',
-                Sao.i18n.gettext('New'),
-                Sao.i18n.gettext('Create a new record'), 'new_'],
-                ['save', 'glyphicon-save',
-                Sao.i18n.gettext('Save'),
-                Sao.i18n.gettext('Save this record'), 'save'],
-                ['switch', 'glyphicon-list-alt',
-                Sao.i18n.gettext('Switch'),
-                Sao.i18n.gettext('Switch view'), 'switch_'],
-                ['reload', 'glyphicon-refresh',
-                Sao.i18n.gettext('Reload'),
-                Sao.i18n.gettext('Reload'), 'reload'],
-                ['previous', 'glyphicon-chevron-left',
-                Sao.i18n.gettext('Previous'),
-                Sao.i18n.gettext('Previous Record'), 'previous'],
-                ['next', 'glyphicon-chevron-right',
-                Sao.i18n.gettext('Next'),
-                Sao.i18n.gettext('Next Record'), 'next'],
-                ['attach', 'glyphicon-paperclip',
-                Sao.i18n.gettext('Attachment'),
-                Sao.i18n.gettext('Add an attachment to the record'), 'attach']
-            ];
-        },
-        menu_def: function() {
-            return [
-                ['glyphicon-edit', Sao.i18n.gettext('New'), 'new_'],
-                ['glyphicon-save', Sao.i18n.gettext('Save'), 'save'],
-                ['glyphicon-list-alt', Sao.i18n.gettext('Switch'), 'switch_'],
-                ['glyphicon-refresh', Sao.i18n.gettext('Reload/Undo'),
-                    'reload'],
-                ['glyphicon-duplicate', Sao.i18n.gettext('Duplicate'), 'copy'],
-                ['glyphicon-trash', Sao.i18n.gettext('Delete'), 'delete_'],
-                ['glyphicon-chevron-left', Sao.i18n.gettext('Previous'),
-                    'previous'],
-                ['glyphicon-chevron-right', Sao.i18n.gettext('Next'), 'next'],
-                ['glyphicon-search', Sao.i18n.gettext('Search'), 'search'],
-                ['glyphicon-time', Sao.i18n.gettext('View Logs...'), 'logs'],
-                ['glyphicon-time', Sao.i18n.gettext('Show revisions...'),
-                    Sao.common.MODELHISTORY.contains(this.screen.model_name) ?
-                        'revision' : null],
-                ['glyphicon-remove', Sao.i18n.gettext('Close Tab'), 'close'],
-                ['glyphicon-paperclip', Sao.i18n.gettext('Attachment'),
-                    'attach'],
-                ['glyphicon-cog', Sao.i18n.gettext('Action'), 'action'],
-                ['glyphicon-share-alt', Sao.i18n.gettext('Relate'), 'relate'],
-                ['glyphicon-print', Sao.i18n.gettext('Print'), 'print']
-            ];
-        },
-        create_toolbar: function() {
-            var toolbar = Sao.Tab.Form._super.create_toolbar.call(this);
-            var screen = this.screen;
-            var buttons = this.buttons;
-            var prm = screen.model.execute('view_toolbar_get', [],
-                    screen.context);
-            prm.done(function(toolbars) {
-                [
-                ['action', 'glyphicon-cog',
-                    Sao.i18n.gettext('Action'),
-                    Sao.i18n.gettext('Launch action')],
-                ['relate', 'glyphicon-share-alt',
-                     Sao.i18n.gettext('Relate'),
-                     Sao.i18n.gettext('Open related records')],
-                ['print', 'glyphicon-print',
-                     Sao.i18n.gettext('Print'),
-                     Sao.i18n.gettext('Print report')]
-                ].forEach(function(menu_action) {
-                    var button = jQuery('<li/>', {
-                        'class': 'dropdown'
-                    })
-                    .append(jQuery('<a/>', {
-                        href: '#',
-                        id: menu_action[0],
-                        'class': 'dropdown-toggle',
-                        'data-toggle': 'dropdown',
-                        role: 'button',
-                        'aria-expanded': 'false'
-                    })
-                        .append(jQuery('<span/>', {
-                            'class': 'glyphicon ' + menu_action[1],
-                            'aria-hidden': 'true'
-                        }))
-                        .append(jQuery('<span/>', {
-                            'class': 'hidden-sm'
-                        }).append(' ' + menu_action[2] + ' '))
-                        .append(jQuery('<span/>', {
-                            'class': 'caret'
-                        })))
-                    .append(jQuery('<ul/>', {
-                        'class': 'dropdown-menu',
-                        role: 'menu',
-                        'aria-labelledby': menu_action[0]
-                    }))
-                    .appendTo(toolbar.find('.navbar-collapse > ul'));
-                    buttons[menu_action[0]] = button;
-                    var menu = button.find('ul[role*="menu"]');
-                    if (menu_action[0] == 'action') {
-                        button.find('a').click(function() {
-                            menu.find('.action_button').remove();
-                            var buttons = screen.get_buttons();
-                            buttons.forEach(function(button) {
-                                var item = jQuery('<li/>', {
-                                    'role': 'presentation',
-                                    'class': 'action_button'
-                                })
-                                .append(
-                                    jQuery('<a/>', {
-                                        'role': 'menuitem',
-                                        'href': '#',
-                                        'tabindex': -1
-                                    }).append(
-                                        button.attributes.string || ''))
-                                .click(function() {
-                                    screen.button(button.attributes);
-                                })
-                            .appendTo(menu);
-                            });
-                        });
-                    }
-
-                    toolbars[menu_action[0]].forEach(function(action) {
-                        var item = jQuery('<li/>', {
-                            'role': 'presentation'
-                        })
-                        .append(jQuery('<a/>', {
-                            'role': 'menuitem',
-                            'href': '#',
-                            'tabindex': -1
-                        }).append(action.name))
-                        .click(function() {
-                            screen.save_current().then(function() {
-                                var exec_action = jQuery.extend({}, action);
-                                var record_id = null;
-                                if (screen.current_record) {
-                                    record_id = screen.current_record.id;
-                                }
-                                var record_ids = screen.current_view
-                                .selected_records().map(function(record) {
-                                    return record.id;
-                                });
-                                exec_action = Sao.Action.evaluate(exec_action,
-                                    menu_action[0], screen.current_record);
-                                var data = {
-                                    model: screen.model_name,
-                                    id: record_id,
-                                    ids: record_ids
-                                };
-                                Sao.Action.exec_action(exec_action, data,
-                                    screen.context);
-                            });
-                        })
-                        .appendTo(menu);
-                    });
-                });
-            });
-            return toolbar;
-        },
-        _close_allowed: function() {
-            return this.modified_save();
-        },
-        modified_save: function() {
-            this.screen.save_tree_state();
-            this.screen.current_view.set_value();
-            if (this.screen.modified()) {
-                return Sao.common.sur_3b.run(
-                        Sao.i18n.gettext('This record has been modified\n' +
-                            'do you want to save it?'))
-                    .then(function(result) {
-                        switch(result) {
-                            case 'ok':
-                                return this.save();
-                            case 'ko':
-                                return this.reload(false);
-                            default:
-                                return jQuery.Deferred().reject();
-                        }
-                    }.bind(this));
-            }
-            return jQuery.when();
-        },
-        new_: function() {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
-                return;
-            }
-            this.modified_save().done(function() {
-                this.screen.new_().then(function() {
-                    this.info_bar.message();
-                }.bind(this));
-                // TODO activate_save
-            }.bind(this));
-        },
-        save: function() {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name).write) {
-                return jQuery.when();
-            }
-            return this.screen.save_current().then(
-                    function() {
-                        this.info_bar.message(
-                                Sao.i18n.gettext('Record saved.'), 'info');
-                    }.bind(this),
-                    function() {
-                        this.info_bar.message(
-                            this.screen.invalid_message(), 'danger');
-                    }.bind(this));
-        },
-        switch_: function() {
-            this.modified_save().done(function() {
-                this.screen.switch_view();
-            }.bind(this));
-        },
-        reload: function(test_modified) {
-            if (test_modified === undefined) {
-                test_modified = true;
-            }
-            var reload = function() {
-                return this.screen.cancel_current().then(function() {
-                    this.screen.save_tree_state(false);
-                    if (this.screen.current_view.view_type != 'form') {
-                        this.screen.search_filter(
-                            this.screen.screen_container.search_entry.val());
-                        // TODO set current_record
-                    }
-                    return this.screen.display().then(function() {
-                        this.info_bar.message();
-                    }.bind(this));
-                    // TODO activate_save
-                }.bind(this));
-            }.bind(this);
-            if (test_modified) {
-                return this.modified_save().then(reload);
-            } else {
-                return reload();
-            }
-        },
-        copy: function() {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
-                return;
-            }
-            this.modified_save().done(function() {
-                this.screen.copy().then(function() {
-                    this.info_bar.message(
-                            Sao.i18n.gettext(
-                                'Working now on the duplicated record(s).'),
-                            'info');
-                }.bind(this));
-            }.bind(this));
-        },
-        delete_: function() {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name)['delete']) {
-                return;
-            }
-            var msg;
-            if (this.screen.current_view.view_type == 'form') {
-                msg = Sao.i18n.gettext('Are you sure to remove this record?');
-            } else {
-                msg = Sao.i18n.gettext('Are you sure to remove those records?');
-            }
-            Sao.common.sur.run(msg).done(function() {
-                this.screen.remove(true, false, true).then(
-                        function() {
-                            this.info_bar.message(
-                                    Sao.i18n.gettext('Records removed.'),
-                                    'info');
-                        }.bind(this), function() {
-                            this.info_bar.message(
-                                    Sao.i18n.gettext('Records not removed.'),
-                                    'danger');
-                        }.bind(this));
-            }.bind(this));
-        },
-        previous: function() {
-            this.modified_save().done(function() {
-                this.screen.display_previous();
-                this.info_bar.message();
-                // TODO activate_save
-            }.bind(this));
-        },
-        next: function() {
-            this.modified_save().done(function() {
-                this.screen.display_next();
-                this.info_bar.message();
-                // TODO activate_save
-            }.bind(this));
-        },
-        search: function() {
-            var search_entry = this.screen.screen_container.search_entry;
-            if (search_entry.is(':visible')) {
-                window.setTimeout(function() {
-                    search_entry.focus();
-                }, 0);
-            }
-        },
-        logs: function() {
-            var record = this.screen.current_record;
-            if ((!record) || (record.id < 0)) {
-                this.info_bar.message(
-                        Sao.i18n.gettext('You have to select one record.'),
-                        'info');
-                return;
-            }
-            var fields = [
-                ['id', Sao.i18n.gettext('ID:')],
-                ['create_uid.rec_name',
-                    Sao.i18n.gettext('Creation User:')],
-                ['create_date', Sao.i18n.gettext('Creation Date:')],
-                ['write_uid.rec_name',
-                    Sao.i18n.gettext('Latest Modification by:')],
-                ['write_date', Sao.i18n.gettext('Latest Modification Date:')]
-                ];
-
-            this.screen.model.execute('read', [[record.id],
-                    fields.map(function(field) {
-                        return field[0];
-                    })], this.screen.context)
-            .then(function(result) {
-                result = result[0];
-                var message = '';
-                fields.forEach(function(field) {
-                    var key = field[0];
-                    var label = field[1];
-                    var value = result[key] || '/';
-                    if (result[key] &&
-                        ~['create_date', 'write_date'].indexOf(key)) {
-                        value = Sao.common.format_datetime(
-                            Sao.common.date_format(),
-                            '%H:%M:%S',
-                            value);
-                    }
-                    message += label + ' ' + value + '\n';
-                });
-                message += Sao.i18n.gettext('Model: ') + this.screen.model.name;
-                Sao.common.message.run(message);
-            }.bind(this));
-        },
-        revision: function() {
-            var current_id = null;
-            if (this.screen.current_record) {
-                current_id = this.screen.current_record.id;
-            }
-            var set_revision = function(revisions) {
-                return function(revision) {
-                    if (revision) {
-                        // Add a millisecond as microseconds are truncated
-                        revision.setMilliseconds(revision.getMilliseconds() + 1);
-                    }
-                    if ((this.screen.current_view.view_type == 'form') &&
-                            (revision < revisions[revisions.length - 1][0])) {
-                        revision = revisions[revisions.length - 1][0];
-                    }
-                    if (revision != this.screen.context._datetime) {
-                        // Update screen context that will be propagated by
-                        // recreating new group
-                        this.screen.context._datetime = revision;
-                        if (this.screen.current_view.view_type != 'form') {
-                            this.screen.search_filter(
-                                    this.screen.screen_container
-                                    .search_entry.val());
-                        } else {
-                            // Test if record exist in revisions
-                            this.screen.new_group([current_id]);
-                        }
-                        this.screen.display(true);
-                        this.update_revision();
-                    }
-                }.bind(this);
-            }.bind(this);
-            this.modified_save().done(function() {
-                var ids = this.screen.current_view.selected_records().map(
-                    function(record) {
-                        return record.id;
-                    });
-                this.screen.model.execute('history_revisions',
-                    [ids], this.screen.context)
-                    .then(function(revisions) {
-                        new Sao.Window.Revision(revisions, set_revision(revisions));
-                    });
-            }.bind(this));
-        },
-        update_revision: function() {
-            var revision = this.screen.context._datetime;
-            var label;
-            if (revision) {
-                var date_format = Sao.common.date_format();
-                var time_format = '%H:%M:%S.%f';
-                revision = Sao.common.format_datetime(date_format, time_format,
-                        revision);
-                label = this.name_el.text() + ' @ '+ revision;
-            } else {
-                label = this.name_el.text();
-            }
-            this.title.html(label);
-            ['new', 'save'].forEach(function(button) {
-                this.buttons[button].prop('disabled', revision);
-            }.bind(this));
-        },
-        attach: function() {
-            var record = this.screen.current_record;
-            if (!record || (record.id < 0)) {
-                return;
-            }
-            new Sao.Window.Attachment(record, function() {
-                this.update_attachment_count(true);
-            }.bind(this));
-        },
-        update_attachment_count: function(reload) {
-            var record = this.screen.current_record;
-            if (record) {
-                record.get_attachment_count(reload).always(
-                        this.attachment_count.bind(this));
-            } else {
-                this.attachment_count(0);
-            }
-        },
-        attachment_count: function(count) {
-            var label = Sao.i18n.gettext('Attachment(%1)', count);
-            this.buttons.attach.text(label);
-            var record_id = this.screen.get_id();
-            this.buttons.attach.prop('disabled',
-                record_id < 0 || record_id === null);
-        },
-        record_message: function() {
-            this.info_bar.message();
-        },
-        action: function() {
-            window.setTimeout(function() {
-                this.buttons.action.find('ul.dropdown-menu')
-                    .dropdown('toggle');
-            }.bind(this));
-        },
-        relate: function() {
-            window.setTimeout(function() {
-                this.buttons.relate.find('ul.dropdown-menu')
-                    .dropdown('toggle');
-            }.bind(this));
-        },
-        print: function() {
-            window.setTimeout(function() {
-                this.buttons.print.find('ul.dropdown-menu')
-                    .dropdown('toggle');
-            }.bind(this));
-        }
-    });
-
-    Sao.Tab.Board = Sao.class_(Sao.Tab, {
-        class_: 'tab-board',
-        init: function(attributes) {
-            var UIView, view_prm;
-            Sao.Tab.Board._super.init.call(this);
-            this.model = attributes.model;
-            this.view_id = (attributes.view_ids.length > 0 ?
-                    attributes.view_ids[0] : null);
-            this.context = attributes.context;
-            this.name = attributes.name;
-            this.dialogs = [];
-            this.board = null;
-            UIView = new Sao.Model('ir.ui.view');
-            view_prm = UIView.execute('read', [[this.view_id], ['arch']],
-                    this.context);
-            view_prm.done(function(views) {
-                var view, board;
-                view = jQuery(jQuery.parseXML(views[0].arch));
-                this.board = new Sao.View.Board(view, this.context);
-                this.board.actions_prms.done(function() {
-                    var i, len, action;
-                    for (i = 0, len = this.board.actions.length; i < len; i++) {
-                        action = this.board.actions[i];
-                        action.screen.tab = this;
-                    }
-                }.bind(this));
-                this.el.append(this.board.el);
-            }.bind(this));
-            this.create_tabcontent();
-            this.set_name(this.name);
-            this.title.html(this.name_el.text());
-        },
-        toolbar_def: function() {
-            return [
-                ['reload', 'glyphicon-refresh',
-                Sao.i18n.gettext('Reload'),
-                Sao.i18n.gettext('Reload'), 'reload']
-            ];
-        },
-        menu_def: function() {
-            return [
-                ['glyphicon-refresh', Sao.i18n.gettext('Reload/Undo'), 'reload']
-            ];
-        },
-        reload: function() {
-            this.board.reload();
-        },
-        record_message: function() {
-            var i, len;
-            var action;
-
-            len = this.board.actions.length;
-            for (i = 0, len=len; i < len; i++) {
-                action = this.board.actions[i];
-                action.update_domain(this.board.actions);
-            }
-        },
-        attachment_count: function() {
-        }
-    });
-
-    Sao.Tab.Wizard = Sao.class_(Sao.Tab, {
-        class_: 'tab-wizard',
-        init: function(wizard) {
-            Sao.Tab.Wizard._super.init.call(this);
-            this.wizard = wizard;
-            this.set_name(wizard.name);
-            wizard.tab = this;
-            this.create_tabcontent();
-            this.title.html(this.name_el.text());
-            this.el.append(wizard.form);
-        },
-        create_toolbar: function() {
-            return jQuery('<span/>');
-        },
-        _close_allowed: function() {
-            var wizard = this.wizard;
-            var prm = jQuery.when();
-            if ((wizard.state !== wizard.end_state) &&
-                (wizard.end_state in wizard.states)) {
-                prm = wizard.response(wizard.end_state);
-            }
-            var dfd = jQuery.Deferred();
-            prm.always(function() {
-                if (wizard.state === wizard.end_state) {
-                    dfd.resolve();
-                } else {
-                    dfd.reject();
-                }
-            });
-            return dfd.promise();
-        }
-    });
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.ScreenContainer = Sao.class_(Object, {
-        init: function(tab_domain) {
-            this.alternate_viewport = jQuery('<div/>', {
-                'class': 'screen-container'
-            });
-            this.alternate_view = false;
-            this.search_modal = null;
-            this.search_form = null;
-            this.tab_domain = tab_domain || [];
-            this.el = jQuery('<div/>', {
-                'class': 'screen-container'
-            });
-            this.filter_box = jQuery('<div/>', {
-                'class': 'row filter-box'
-            });
-            this.el.append(this.filter_box);
-            this.filter_button = jQuery('<button/>', {
-                type: 'button',
-                'class': 'btn btn-default'
-            }).append(Sao.i18n.gettext('Filters'));
-            this.filter_button.click(this.search_box.bind(this));
-            this.search_entry = jQuery('<input/>', {
-                'class': 'form-control',
-                'placeholder': Sao.i18n.gettext('Search')
-            });
-            this.search_list = jQuery('<datalist/>');
-            this.search_list.uniqueId();
-            this.search_entry.attr('list', this.search_list.attr('id'));
-            this.search_entry.keypress(this.key_press.bind(this));
-            this.search_entry.on('input', this.update.bind(this));
-
-            this.but_bookmark = jQuery('<button/>', {
-                type: 'button',
-                'class': 'btn btn-default dropdown-toggle',
-                'data-toggle': 'dropdown',
-                'aria-expanded': false,
-                'aria-label': Sao.i18n.gettext('Bookmarks'),
-                'id': 'bookmarks'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-bookmark',
-                'aria-hidden': true
-            }));
-            var dropdown_bookmark = jQuery('<ul/>', {
-                'class': 'dropdown-menu',
-                'role': 'menu',
-                'aria-labelledby': 'bookmarks'
-            });
-            this.but_bookmark.click(function() {
-                dropdown_bookmark.children().remove();
-                var bookmarks = this.bookmarks();
-                for (var i=0; i < bookmarks.length; i++) {
-                    var name = bookmarks[i][1];
-                    var domain = bookmarks[i][2];
-                    jQuery('<li/>', {
-                        'role': 'presentation'
-                    })
-                    .append(jQuery('<a/>', {
-                        'role': 'menuitem',
-                        'href': '#',
-                        'tabindex': -1
-                    }).append(name)
-                        .click(domain, this.bookmark_activate.bind(this)))
-                    .appendTo(dropdown_bookmark);
-                }
-            }.bind(this));
-            this.but_star = jQuery('<button/>', {
-                'class': 'btn btn-default',
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon',
-                'aria-hidden': true
-            })).click(this.star_click.bind(this));
-            this.set_star();
-
-            jQuery('<div/>', {
-                'class': 'input-group'
-            })
-            .append(jQuery('<span/>', {
-                'class': 'input-group-btn'
-            }).append(this.filter_button))
-            .append(this.search_entry)
-            .append(this.search_list)
-            .append(jQuery('<span/>', {
-                'class': 'input-group-btn'
-            }).append(this.but_star)
-                    .append(this.but_bookmark)
-                    .append(dropdown_bookmark))
-            .appendTo(jQuery('<div/>', {
-                'class': 'col-md-8'
-            }).appendTo(this.filter_box));
-
-
-            this.but_prev = jQuery('<button/>', {
-                type: 'button',
-                'class': 'btn btn-default',
-                'aria-label': Sao.i18n.gettext('Previous')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-menu-left',
-                'aria-hidden': true
-            }));
-            this.but_prev.click(this.search_prev.bind(this));
-            this.but_next = jQuery('<button/>', {
-                type: 'button',
-                'class': 'btn btn-default',
-                'aria-label': Sao.i18n.gettext('Next')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-menu-right',
-                'aria-hidden': true
-            }));
-            this.but_next.click(this.search_next.bind(this));
-
-            jQuery('<div/>', {
-                'class': 'btn-group',
-                role: 'group',
-            })
-            .append(this.but_prev)
-            .append(this.but_next)
-            .appendTo(jQuery('<div/>', {
-                'class': 'col-md-4'
-            }).appendTo(this.filter_box));
-
-            this.content_box = jQuery('<div/>', {
-                'class': 'content-box'
-            });
-
-            if (!jQuery.isEmptyObject(this.tab_domain)) {
-                this.tab = jQuery('<div/>', {
-                    'class': 'tab-domain'
-                }).appendTo(this.el);
-                var nav = jQuery('<ul/>', {
-                    'class': 'nav nav-tabs',
-                    role: 'tablist'
-                }).appendTo(this.tab);
-                var content = jQuery('<div/>', {
-                    'class': 'tab-content'
-                }).appendTo(this.tab);
-                this.tab_domain.forEach(function(tab_domain, i) {
-                    var name = tab_domain[0];
-                    var page = jQuery('<li/>', {
-                        role: 'presentation',
-                        id: 'nav-' + i
-                    }).append(jQuery('<a/>', {
-                        'aria-controls':  i,
-                        role: 'tab',
-                        'data-toggle': 'tab',
-                        'href': '#' + i
-                    }).append(name)).appendTo(nav);
-                }.bind(this));
-                nav.find('a:first').tab('show');
-                var self = this;
-                nav.find('a').click(function(e) {
-                    e.preventDefault();
-                    jQuery(this).tab('show');
-                    self.do_search();
-                });
-            } else {
-                this.tab = null;
-            }
-            this.el.append(this.content_box);
-        },
-        set_text: function(value) {
-            this.search_entry.val(value);
-            this.bookmark_match();
-        },
-        update: function() {
-            var completions = this.screen.domain_parser().completion(
-                    this.get_text());
-            this.search_list.children().remove();
-            completions.forEach(function(e) {
-                jQuery('<option/>', {
-                    'value': e.trim()
-                }).appendTo(this.search_list);
-            }, this);
-        },
-        set_star: function(star) {
-            var glyphicon = this.but_star.children('span.glyphicon');
-            if (star) {
-                glyphicon.removeClass('glyphicon-star-empty');
-                glyphicon.addClass('glyphicon-star');
-                this.but_star.attr('aria-label',
-                        Sao.i18n.gettext('Remove this bookmark'));
-            } else {
-                glyphicon.removeClass('glyphicon-star');
-                glyphicon.addClass('glyphicon-star-empty');
-                this.but_star.attr('aria-label',
-                       Sao.i18n.gettext('Bookmark this filter'));
-            }
-        },
-        get_star: function() {
-            var glyphicon = this.but_star.children('span.glyphicon');
-            return glyphicon.hasClass('glyphicon-star');
-        },
-        star_click: function() {
-            var star = this.get_star();
-            var model_name = this.screen.model_name;
-            var refresh = function() {
-                this.bookmark_match();
-                this.but_bookmark.prop('disabled',
-                        jQuery.isEmptyObject(this.bookmarks()));
-            }.bind(this);
-            if (!star) {
-                var text = this.get_text();
-                if (!text) {
-                    return;
-                }
-                Sao.common.ask.run(Sao.i18n.gettext('Bookmark Name:'))
-                    .then(function(name) {
-                        if (!name) {
-                            return;
-                        }
-                        var domain = this.screen.domain_parser().parse(text);
-                        Sao.common.VIEW_SEARCH.add(model_name, name, domain)
-                        .then(function() {
-                            refresh();
-                        });
-                        this.set_text(
-                            this.screen.domain_parser().string(domain));
-                    }.bind(this));
-            } else {
-                var id = this.bookmark_match();
-                Sao.common.VIEW_SEARCH.remove(model_name, id).then(function() {
-                    refresh();
-                });
-            }
-        },
-        bookmarks: function() {
-            var searches = Sao.common.VIEW_SEARCH.get(this.screen.model_name);
-            return searches.filter(function(search) {
-                return this.screen.domain_parser().stringable(search[2]);
-            }.bind(this));
-        },
-        bookmark_activate: function(e) {
-            var domain = e.data;
-            this.set_text(this.screen.domain_parser().string(domain));
-            this.do_search();
-        },
-        bookmark_match: function() {
-            var current_text = this.get_text();
-            var current_domain = this.screen.domain_parser().parse(current_text);
-            this.but_star.prop('disabled', !current_text);
-            var star = this.get_star();
-            var bookmarks = this.bookmarks();
-            for (var i=0; i < bookmarks.length; i++) {
-                var id = bookmarks[i][0];
-                var name = bookmarks[i][1];
-                var domain = bookmarks[i][2];
-                var text = this.screen.domain_parser().string(domain);
-                if ((text === current_text) ||
-                        (Sao.common.compare(domain, current_domain))) {
-                    this.set_star(true);
-                    return id;
-                }
-            }
-            this.set_star(false);
-        },
-        search_prev: function() {
-            this.screen.search_prev(this.get_text());
-        },
-        search_next: function() {
-            this.screen.search_next(this.get_text());
-        },
-        get_tab_domain: function() {
-            if (!this.tab) {
-                return [];
-            }
-            var i = this.tab.find('li').index(this.tab.find('li.active'));
-            return this.tab_domain[i][1];
-        },
-        do_search: function() {
-            return this.screen.search_filter(this.get_text());
-        },
-        key_press: function(e) {
-            if (e.which == Sao.common.RETURN_KEYCODE) {
-                this.do_search();
-                return false;
-            }
-            // Wait the current event finished
-            window.setTimeout(function() {
-                this.bookmark_match();
-            }.bind(this));
-        },
-        set_screen: function(screen) {
-            this.screen = screen;
-            this.but_bookmark.prop('disabled',
-                    jQuery.isEmptyObject(this.bookmarks()));
-            this.bookmark_match();
-        },
-        show_filter: function() {
-            this.filter_box.show();
-            if (this.tab) {
-                this.tab.show();
-            }
-        },
-        hide_filter: function() {
-            this.filter_box.hide();
-            if (this.tab) {
-                this.tab.hide();
-            }
-        },
-        set: function(widget) {
-            if (this.alternate_view) {
-                this.alternate_viewport.children().detach();
-                this.alternate_viewport.append(widget);
-            } else {
-                this.content_box.children().detach();
-                this.content_box.append(widget);
-            }
-        },
-        get_text: function() {
-            return this.search_entry.val();
-        },
-        search_box: function() {
-            var domain_parser = this.screen.domain_parser();
-            var search = function() {
-                this.search_modal.modal('hide');
-                var text = '';
-                var quote = domain_parser.quote.bind(domain_parser);
-                for (var i = 0; i < this.search_form.fields.length; i++) {
-                    var label = this.search_form.fields[i][0];
-                    var entry = this.search_form.fields[i][1];
-                    var value;
-                    switch(entry.type) {
-                        case 'selection':
-                        case 'date':
-                        case 'datetime':
-                        case 'time':
-                            value = entry.get_value(quote);
-                            break;
-                        default:
-                        value = quote(entry.val());
-                    }
-                    if (value) {
-                        text += quote(label) + ': ' + value + ' ';
-                    }
-                }
-                this.set_text(text);
-                this.do_search().then(function() {
-                    this.last_search_text = this.get_text();
-                }.bind(this));
-            }.bind(this);
-            if (!this.search_modal) {
-                var dialog = new Sao.Dialog(
-                        Sao.i18n.gettext('Filters'), '', 'lg');
-                this.search_modal = dialog.modal;
-                this.search_form = dialog.content;
-                this.search_form.addClass('form-horizontal');
-                this.search_form.submit(search);
-
-                var fields = [];
-                var field;
-                for (var f in domain_parser.fields) {
-                    field = domain_parser.fields[f];
-                    if (field.searchable || field.searchable === undefined) {
-                        fields.push(field);
-                    }
-                }
-
-                var boolean_option = function(input) {
-                    return function(e) {
-                        jQuery('<option/>', {
-                            value: e,
-                            text: e
-                        }).appendTo(input);
-                    };
-                };
-                var selection_option = function(input) {
-                    return function(s) {
-                        jQuery('<option/>', {
-                            value: s[1],
-                            text: s[1]
-                        }).appendTo(input);
-                    };
-                };
-
-                var prefix = 'filter-' + this.screen.model_name + '-';
-                this.search_form.fields = [];
-                for (var i = 0; i < fields.length; i++) {
-                    field = fields[i];
-                    var form_group = jQuery('<div/>', {
-                        'class': 'form-group form-group-sm'
-                    }).append(jQuery('<label/>', {
-                        'class': 'col-sm-4 control-label',
-                        'for': prefix + field.name,
-                        text: field.string
-                    })).appendTo(dialog.body);
-
-                    var input;
-                    var entry;
-                    switch (field.type) {
-                        case 'boolean':
-                            entry = input = jQuery('<select/>', {
-                                'class': 'form-control input-sm',
-                                id: prefix + field.name
-                            });
-                            ['',
-                            Sao.i18n.gettext('True'),
-                            Sao.i18n.gettext('False')].forEach(
-                                    boolean_option(input));
-                            break;
-                        case 'selection':
-                            entry = new Sao.ScreenContainer.Selection(
-                                    field.selection, prefix + field.name);
-                            input = entry.el;
-                            break;
-                        case 'date':
-                        case 'datetime':
-                        case 'time':
-                            var format;
-                            var date_format = Sao.common.date_format();
-                            if (field.type == 'date') {
-                                format = date_format;
-                            } else {
-                                var time_format = new Sao.PYSON.Decoder({}).decode(
-                                        field.format);
-                                time_format = Sao.common.moment_format(time_format);
-                                if (field.type == 'time') {
-                                    format = time_format;
-                                } else if (field.type == 'datetime') {
-                                    format = date_format + ' ' + time_format;
-                                }
-                            }
-                            entry = new Sao.ScreenContainer.DateTimes(
-                                    format, prefix + field.name);
-                            input = entry.el;
-                            break;
-                        default:
-                            entry = input = jQuery('<input/>', {
-                                'class': 'form-control input-sm',
-                                type: 'text',
-                                placeholder: field.string,
-                                id: prefix + field.name
-                            });
-                            break;
-                    }
-                    jQuery('<div/>', {
-                        'class': 'col-sm-8'
-                    }).append(input).appendTo(form_group);
-                    this.search_form.fields.push([field.string, entry]);
-                }
-
-                jQuery('<button/>', {
-                    'class': 'btn btn-primary',
-                    type: 'submit'
-                }).append(Sao.i18n.gettext('Find'))
-                .click(search).appendTo(dialog.footer);
-            }
-            this.search_modal.modal('show');
-            if (this.last_search_text.trim() !== this.get_text().trim()) {
-                for (var j = 0; j < this.search_form.fields.length; j++) {
-                    var fentry = this.search_form.fields[j][1];
-                    switch(fentry.type) {
-                        case 'selection':
-                            fentry.set_value([]);
-                            break;
-                        case 'date':
-                        case 'datetime':
-                        case 'time':
-                            fentry.set_value(null, null);
-                            break;
-                        default:
-                            fentry.val('');
-                    }
-                }
-                this.search_form.fields[0][1].focus();
-            }
-        }
-    });
-
-    Sao.ScreenContainer.DateTimes = Sao.class_(Object, {
-        type: 'date',
-        init: function(format, id) {
-            this.el = jQuery('<div/>', {
-                'class': 'row',
-                id: id
-            });
-            var build_entry = function(placeholder) {
-                var entry = jQuery('<div/>', {
-                    'class': 'input-group input-group-sm'
-                });
-                jQuery('<input/>', {
-                    'class': 'form-control input-sm',
-                    type: 'text',
-                    placeholder: placeholder,
-                    id: id + '-from'
-                }).appendTo(entry);
-                jQuery('<span/>', {
-                    'class': 'input-group-btn'
-                }).append(jQuery('<button/>', {
-                    'class': 'btn btn-default',
-                    type: 'button'
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-calendar'
-                }))).appendTo(entry);
-                entry.datetimepicker();
-                entry.data('DateTimePicker').format(format);
-                return entry;
-            };
-            this.from = build_entry('From').appendTo(jQuery('<div/>', {
-                'class': 'col-md-5'
-            }).appendTo(this.el));
-            jQuery('<p/>', {
-                'class': 'text-center'
-            }).append('..').appendTo(jQuery('<div/>', {
-                'class': 'col-md-1'
-            }).appendTo(this.el));
-            this.to = build_entry('To').appendTo(jQuery('<div/>', {
-                'class': 'col-md-5'
-            }).appendTo(this.el));
-        },
-        _get_value: function(entry) {
-            return entry.find('input').val();
-        },
-        get_value: function(quote) {
-            var from = this._get_value(this.from);
-            var to = this._get_value(this.to);
-            if (from && to) {
-                if (from !== to) {
-                    return quote(from) + '..' + quote(to);
-                } else {
-                    return quote(from);
-                }
-            } else if (from) {
-                return '>=' + quote(from);
-            } else if (to) {
-                return '<=' + quote(to);
-            }
-        },
-        set_value: function(from, to) {
-            this.from.data('DateTimePicker').date(from);
-            this.to.data('DateTimePicker').date(to);
-        }
-    });
-
-    Sao.ScreenContainer.Selection = Sao.class_(Object, {
-        type: 'selection',
-        init: function(selections, id) {
-            this.el = jQuery('<select/>', {
-                'class': 'form-control input-sm',
-                multiple: true,
-                id: id
-            });
-            selections.forEach(function(s) {
-                jQuery('<option/>', {
-                    value: s[1],
-                    text: s[1]
-                }).appendTo(this.el);
-            }.bind(this));
-        },
-        get_value: function(quote) {
-            var value = this.el.val();
-            if (value) {
-                value = value.reduce(function(a, b) {
-                    if (a) {a += ';';}
-                    return a + quote(b);
-                });
-            }
-            return value;
-        },
-        set_value: function(value) {
-            this.el.val(value);
-        }
-    });
-
-    Sao.Screen = Sao.class_(Object, {
-        init: function(model_name, attributes) {
-            this.model_name = model_name;
-            this.model = new Sao.Model(model_name, attributes);
-            this.attributes = jQuery.extend({}, attributes);
-            this.attributes.limit = this.attributes.limit || Sao.config.limit;
-            this.view_ids = jQuery.extend([], attributes.view_ids);
-            this.view_to_load = jQuery.extend([],
-                attributes.mode || ['tree', 'form']);
-            this.views = [];
-            this.views_preload = attributes.views_preload || {};
-            this.exclude_field = attributes.exclude_field;
-            this.context = attributes.context || {};
-            this.new_group();
-            this.current_view = null;
-            this.current_record = null;
-            this.domain = attributes.domain || [];
-            this.size_limit = null;
-            this.limit = attributes.limit || Sao.config.limit;
-            this.offset = 0;
-            if (!Sao.common.MODELACCESS.get(model_name).write) {
-                this.attributes.readonly = true;
-            }
-            this.search_count = 0;
-            this.screen_container = new Sao.ScreenContainer(
-                attributes.tab_domain);
-            if (!attributes.row_activate) {
-                this.row_activate = this.default_row_activate;
-            } else {
-                this.row_activate = attributes.row_activate;
-            }
-            this.tree_states = {};
-            this.tree_states_done = [];
-            this.fields_view_tree = {};
-            this._domain_parser = {};
-            this.pre_validate = false;
-            this.tab = null;
-        },
-        load_next_view: function() {
-            if (!jQuery.isEmptyObject(this.view_to_load)) {
-                var view_id;
-                if (!jQuery.isEmptyObject(this.view_ids)) {
-                    view_id = this.view_ids.shift();
-                }
-                var view_type = this.view_to_load.shift();
-                return this.add_view_id(view_id, view_type);
-            }
-            return jQuery.when();
-        },
-        add_view_id: function(view_id, view_type) {
-            var view;
-            if (view_id && this.views_preload[String(view_id)]) {
-                view = this.views_preload[String(view_id)];
-            } else if (!view_id && this.views_preload[view_type]) {
-                view = this.views_preload[view_type];
-            } else {
-                var prm = this.model.execute('fields_view_get',
-                        [view_id, view_type], this.context);
-                return prm.pipe(this.add_view.bind(this));
-            }
-            this.add_view(view);
-            return jQuery.when();
-        },
-        add_view: function(view) {
-            var arch = view.arch;
-            var fields = view.fields;
-            var view_id = view.view_id;
-            var xml_view = jQuery(jQuery.parseXML(arch));
-
-            if (xml_view.children().prop('tagName') == 'tree') {
-                this.fields_view_tree[view_id] = view;
-            }
-
-            var loading = 'eager';
-            if (xml_view.children().prop('tagName') == 'form') {
-                loading = 'lazy';
-            }
-            for (var field in fields) {
-                if (!(field in this.model.fields) || loading == 'eager') {
-                    fields[field].loading = loading;
-                } else {
-                    fields[field].loading = this.model.fields[field]
-                        .description.loading;
-                }
-            }
-            this.model.add_fields(fields);
-            var view_widget = Sao.View.parse(this, xml_view, view.field_childs);
-            view_widget.view_id = view_id;
-            this.views.push(view_widget);
-
-            return view_widget;
-        },
-        number_of_views: function() {
-            return this.views.length + this.view_to_load.length;
-        },
-        switch_view: function(view_type) {
-            if (this.current_view) {
-                if (!this.group.parent && this.modified()) {
-                    return jQuery.when();
-                }
-                this.current_view.set_value();
-                if (this.current_record &&
-                        !~this.current_record.group.indexOf(
-                            this.current_record)) {
-                    this.current_record = null;
-                }
-                var fields = this.current_view.get_fields();
-                if (this.current_record && this.current_view.editable &&
-                        !this.current_record.validate(
-                            fields, false, false, true)) {
-                    this.screen_container.set(this.current_view.el);
-                    return this.current_view.display().done(function() {
-                        this.set_cursor();
-                    }.bind(this));
-                }
-            }
-            var _switch = function() {
-                if ((!view_type) || (!this.current_view) ||
-                        (this.current_view.view_type != view_type)) {
-                    var switch_current_view = (function() {
-                        this.current_view = this.views[this.views.length - 1];
-                        return _switch();
-                    }.bind(this));
-                    for (var i = 0; i < this.number_of_views(); i++) {
-                        if (this.view_to_load.length) {
-                            if (!view_type) {
-                                view_type = this.view_to_load[0];
-                            }
-                            return this.load_next_view().then(
-                                    switch_current_view);
-                        }
-                        this.current_view = this.views[
-                            (this.views.indexOf(this.current_view) + 1) %
-                            this.views.length];
-                        if (!view_type) {
-                            break;
-                        } else if (this.current_view.view_type == view_type) {
-                            break;
-                        }
-                    }
-                }
-                this.screen_container.set(this.current_view.el);
-                return this.display().done(function() {
-                    this.set_cursor();
-                }.bind(this));
-            }.bind(this);
-            return _switch();
-        },
-        search_filter: function(search_string) {
-            var domain = [];
-            var domain_parser = this.domain_parser();
-
-            if (domain_parser && !this.group.parent) {
-                if (search_string || search_string === '') {
-                    domain = domain_parser.parse(search_string);
-                } else {
-                    domain = this.attributes.search_value;
-                }
-                this.screen_container.set_text(
-                        domain_parser.string(domain));
-            } else {
-                domain = [['id', 'in', this.group.map(function(r) {
-                    return r.id;
-                })]];
-            }
-
-            if (!jQuery.isEmptyObject(domain) && this.attributes.domain) {
-                domain = ['AND', domain, this.attributes.domain];
-            } else
-                domain = this.attributes.domain || [];
-
-            var tab_domain = this.screen_container.get_tab_domain();
-            if (!jQuery.isEmptyObject(tab_domain)) {
-                domain = ['AND', domain, tab_domain];
-            }
-
-            var grp_prm = this.model.find(domain, this.offset, this.limit,
-                    this.attributes.order, this.context);
-            var count_prm = this.model.execute('search_count', [domain],
-                    this.context);
-            count_prm.done(function(count) {
-                this.search_count = count;
-            }.bind(this));
-            grp_prm.done(this.set_group.bind(this));
-            grp_prm.done(this.display.bind(this));
-            jQuery.when(grp_prm, count_prm).done(function(group, count) {
-                this.screen_container.but_next.prop('disabled',
-                        !(group.length == this.limit &&
-                            count > this.limit + this.offset));
-            }.bind(this));
-            this.screen_container.but_prev.prop('disabled', this.offset <= 0);
-            return grp_prm;
-        },
-        set_group: function(group) {
-            if (this.group) {
-                jQuery.extend(group.model.fields, this.group.model.fields);
-                this.group.screens.splice(
-                        this.group.screens.indexOf(this), 1);
-                jQuery.extend(group.on_write, this.group.on_write);
-                group.on_write = group.on_write.filter(function(e, i, a) {
-                    return i == a.indexOf(e);
-                });
-            }
-            group.screens.push(this);
-            this.tree_states_done = [];
-            this.group = group;
-            this.model = group.model;
-            if (jQuery.isEmptyObject(group)) {
-                this.set_current_record(null);
-            } else {
-                this.set_current_record(group[0]);
-            }
-        },
-        new_group: function(ids) {
-            var group = new Sao.Group(this.model, this.context, []);
-            group.set_readonly(this.attributes.readonly || false);
-            if (ids) {
-                group.load(ids);
-            }
-            this.set_group(group);
-        },
-        set_current_record: function(record) {
-            this.current_record = record;
-            // TODO position
-            if (this.tab) {
-                if (record) {
-                    record.get_attachment_count().always(
-                            this.tab.attachment_count.bind(this.tab));
-                } else {
-                    this.tab.attachment_count(0);
-                }
-                this.tab.record_message();
-            }
-        },
-        display: function(set_cursor) {
-            var deferreds = [];
-            if (this.views) {
-                var search_prm = this.search_active(
-                        ~['tree', 'graph', 'calendar'].indexOf(
-                            this.current_view.view_type));
-                deferreds.push(search_prm);
-                for (var i = 0; i < this.views.length; i++) {
-                    if (this.views[i]) {
-                        deferreds.push(this.views[i].display());
-                    }
-                }
-            }
-            return jQuery.when.apply(jQuery, deferreds).then(function() {
-                this.set_tree_state();
-                this.set_current_record(this.current_record);
-                // set_cursor must be called after set_tree_state because
-                // set_tree_state redraws the tree
-                if (set_cursor) {
-                    this.set_cursor(false, false);
-                }
-            }.bind(this));
-        },
-        display_next: function() {
-            var view = this.current_view;
-            view.set_value();
-            this.set_cursor(false, false);
-            if (~['tree', 'form'].indexOf(view.view_type) &&
-                    this.current_record && this.current_record.group) {
-                var group = this.current_record.group;
-                var record = this.current_record;
-                while (group) {
-                    var index = group.indexOf(record);
-                    if (index < group.length - 1) {
-                        record = group[index + 1];
-                        break;
-                    } else if (group.parent &&
-                            (record.group.model_name ==
-                             group.parent.group.model_name)) {
-                        record = group.parent;
-                        group = group.parent.group;
-                    } else {
-                        break;
-                    }
-                }
-                this.set_current_record(record);
-            } else {
-                this.set_current_record(this.group[0]);
-            }
-            this.set_cursor(false, false);
-            view.display();
-        },
-        display_previous: function() {
-            var view = this.current_view;
-            view.set_value();
-            this.set_cursor(false, false);
-            if (~['tree', 'form'].indexOf(view.view_type) &&
-                    this.current_record && this.current_record.group) {
-                var group = this.current_record.group;
-                var record = this.current_record;
-                while (group) {
-                    var index = group.indexOf(record);
-                    if (index > 0) {
-                        record = group[index - 1];
-                        break;
-                    } else if (group.parent &&
-                            (record.group.model_name ==
-                             group.parent.group.model_name)) {
-                        record = group.parent;
-                        group = group.parent.group;
-                    } else {
-                        break;
-                    }
-                }
-                this.set_current_record(record);
-            } else {
-                this.set_current_record(this.group[0]);
-            }
-            this.set_cursor(false, false);
-            view.display();
-        },
-        default_row_activate: function() {
-            if ((this.current_view.view_type == 'tree') &&
-                    this.current_view.attributes.keyword_open) {
-                Sao.Action.exec_keyword('tree_open', {
-                    'model': this.model_name,
-                    'id': this.get_id(),
-                    'ids': [this.get_id()]
-                    }, jQuery.extend({}, this.context), false);
-            } else {
-                this.switch_view('form');
-            }
-        },
-        get_id: function() {
-            if (this.current_record) {
-                return this.current_record.id;
-            }
-        },
-        new_: function(default_) {
-            if (default_ === undefined) {
-                default_ = true;
-            }
-            var prm = jQuery.when();
-            if (this.current_view &&
-                    ((this.current_view.view_type == 'tree' &&
-                      !this.current_view.editable) ||
-                     this.current_view.view_type == 'graph')) {
-                prm = this.switch_view('form');
-            }
-            return prm.then(function() {
-                var group;
-                if (this.current_record) {
-                    group = this.current_record.group;
-                } else {
-                    group = this.group;
-                }
-                var record = group.new_(default_);
-                group.add(record, this.new_model_position());
-                this.set_current_record(record);
-                this.display().done(function() {
-                    this.set_cursor(true, true);
-                }.bind(this));
-                return record;
-            }.bind(this));
-        },
-        new_model_position: function() {
-            var position = -1;
-            if (this.current_view && (this.current_view.view_type == 'tree') &&
-                    (this.current_view.attributes.editable == 'top')) {
-                position = 0;
-            }
-            return position;
-        },
-        set_on_write: function(name) {
-            if(name) {
-                if (!~this.group.on_write.indexOf(name)) {
-                    this.group.on_write.push(name);
-                }
-            }
-        },
-        cancel_current: function() {
-            var prms = [];
-            if (this.current_record) {
-                this.current_record.cancel();
-                if (this.current_record.id < 0) {
-                    prms.push(this.remove());
-                }
-            }
-            return jQuery.when.apply(jQuery, prms);
-        },
-        save_current: function() {
-            var current_record = this.current_record;
-            if (!current_record) {
-                if ((this.current_view.view_type == 'tree') &&
-                        (!jQuery.isEmptyObject(this.group))) {
-                    this.set_current_record(this.group[0]);
-                } else {
-                    return jQuery.when();
-                }
-            }
-            this.current_view.set_value();
-            var fields = this.current_view.get_fields();
-            var path = current_record.get_path(this.group);
-            var prm = jQuery.Deferred();
-            if (this.current_view.view_type == 'tree') {
-                prm = this.group.save();
-            } else {
-                current_record.validate(fields).then(function(validate) {
-                    if (validate) {
-                        current_record.save().then(
-                            prm.resolve, prm.reject);
-                    } else {
-                        this.current_view.display().done(
-                                this.set_cursor.bind(this));
-                        prm.reject();
-                    }
-                }.bind(this));
-            }
-            var dfd = jQuery.Deferred();
-            prm = prm.then(function() {
-                if (path && current_record.id) {
-                    path.splice(-1, 1,
-                            [path[path.length - 1][0], current_record.id]);
-                }
-                return this.group.get_by_path(path).then(function(record) {
-                    this.set_current_record(record);
-                }.bind(this));
-            }.bind(this));
-            prm.then(function() {
-                this.display().always(dfd.resolve);
-            }.bind(this), function() {
-                this.display().always(dfd.reject);
-            }.bind(this));
-            return dfd.promise();
-        },
-        set_cursor: function(new_, reset_view) {
-            if (!this.current_view) {
-                return;
-            } else if (~['tree', 'form'].indexOf(this.current_view.view_type)) {
-                this.current_view.set_cursor(new_, reset_view);
-            }
-        },
-        modified: function() {
-            var test = function(record) {
-                return (record.has_changed() || record.id < 0);
-            };
-            if (this.current_view.view_type != 'tree') {
-                if (this.current_record) {
-                    if (test(this.current_record)) {
-                        return true;
-                    }
-                }
-            } else {
-                if (this.group.some(test)) {
-                    return true;
-                }
-            }
-            // TODO test view modified
-            return false;
-        },
-        unremove: function() {
-            var records = this.current_view.selected_records();
-            records.forEach(function(record) {
-                record.group.unremove(record);
-            });
-        },
-        remove: function(delete_, remove, force_remove) {
-            var records = null;
-            if ((this.current_view.view_type == 'form') &&
-                    this.current_record) {
-                records = [this.current_record];
-            } else if (this.current_view.view_type == 'tree') {
-                records = this.current_view.selected_records();
-            }
-            if (jQuery.isEmptyObject(records)) {
-                return;
-            }
-            var prm = jQuery.when();
-            if (delete_) {
-                // TODO delete children before parent
-                prm = this.model.delete_(records);
-            }
-            var top_record = records[0];
-            var top_group = top_record.group;
-            var idx = top_group.indexOf(top_record);
-            var path = top_record.get_path(this.group);
-            return prm.then(function() {
-                records.forEach(function(record) {
-                    record.group.remove(record, remove, true, force_remove);
-                });
-                var prms = [];
-                if (delete_) {
-                    records.forEach(function(record) {
-                        if (record.group.parent) {
-                            prms.push(record.group.parent.save(false));
-                        }
-                        if (~record.group.record_deleted.indexOf(record)) {
-                            record.group.record_deleted.splice(
-                                record.group.record_deleted.indexOf(record), 1);
-                        }
-                        if (~record.group.record_removed.indexOf(record)) {
-                            record.group.record_removed.splice(
-                                record.group.record_removed.indexOf(record), 1);
-                        }
-                        // TODO destroy
-                    });
-                }
-                if (idx > 0) {
-                    var record = top_group[idx - 1];
-                    path.splice(-1, 1, [path[path.length - 1][0], record.id]);
-                } else {
-                    path.splice(-1, 1);
-                }
-                if (!jQuery.isEmptyObject(path)) {
-                    prms.push(this.group.get_by_path(path).then(function(record) {
-                        this.set_current_record(record);
-                    }.bind(this)));
-                } else if (this.group.length) {
-                    this.set_current_record(this.group[0]);
-                }
-
-                return jQuery.when.apply(jQuery, prms).then(function() {
-                    this.display().done(function() {
-                        this.set_cursor();
-                    }.bind(this));
-                }.bind(this));
-            }.bind(this));
-        },
-        copy: function() {
-            var dfd = jQuery.Deferred();
-            var records = this.current_view.selected_records();
-            this.model.copy(records, this.context).then(function(new_ids) {
-                this.group.load(new_ids);
-                if (!jQuery.isEmptyObject(new_ids)) {
-                    this.set_current_record(this.group.get(new_ids[0]));
-                }
-                this.display().always(dfd.resolve);
-            }.bind(this), dfd.reject);
-            return dfd.promise();
-        },
-        search_active: function(active) {
-            if (active && !this.group.parent) {
-                this.screen_container.set_screen(this);
-                this.screen_container.show_filter();
-            } else {
-                this.screen_container.hide_filter();
-            }
-            return jQuery.when();
-        },
-        domain_parser: function() {
-            var view_id, view_tree;
-            if (this.current_view) {
-                view_id = this.current_view.view_id;
-            } else {
-                view_id = null;
-            }
-            if (view_id in this._domain_parser) {
-                return this._domain_parser[view_id];
-            }
-            if (!(view_id in this.fields_view_tree)) {
-                // Fetch default view for the next time
-                this.model.execute('fields_view_get', [false, 'tree'],
-                        this.context).then(function(view) {
-                    this.fields_view_tree[view_id] = view;
-                }.bind(this));
-                view_tree = {};
-                view_tree.fields = {};
-            } else {
-                view_tree = this.fields_view_tree[view_id];
-            }
-            var fields = jQuery.extend({}, view_tree.fields);
-
-            var set_selection = function(props) {
-                return function(selection) {
-                    props.selection = selection;
-                };
-            };
-            for (var name in fields) {
-                var props = fields[name];
-                if ((props.type != 'selection') &&
-                        (props.type != 'reference')) {
-                    continue;
-                }
-                if (props.selection instanceof Array) {
-                    continue;
-                }
-                this.get_selection(props).then(set_selection);
-            }
-
-            if ('arch' in view_tree) {
-                // Filter only fields in XML view
-                var xml_view = jQuery(jQuery.parseXML(view_tree.arch));
-                var xml_fields = xml_view.find('tree').children()
-                    .filter(function(node) {
-                        return node.tagName == 'field';
-                    }).map(function(node) {
-                        return node.getAttribute('name');
-                    });
-                var dom_fields = {};
-                xml_fields.each(function(name) {
-                    dom_fields[name] = fields[name];
-                });
-            }
-
-            // Add common fields
-            [
-                ['id', Sao.i18n.gettext('ID'), 'integer'],
-                ['create_uid', Sao.i18n.gettext('Creation User'),
-                    'many2one'],
-                ['create_date', Sao.i18n.gettext('Creation Date'),
-                    'datetime'],
-                ['write_uid', Sao.i18n.gettext('Modification User'),
-                     'many2one'],
-                ['write_date', Sao.i18n.gettext('Modification Date'),
-                     'datetime']
-                    ] .forEach(function(e) {
-                        var name = e[0];
-                        var string = e[1];
-                        var type = e[2];
-                        if (!(name in fields)) {
-                            fields[name] = {
-                                'string': string,
-                                'name': name,
-                                'type': type
-                            };
-                            if (type == 'datetime') {
-                                fields[name].format = '"%H:%M:%S"';
-                            }
-                        }
-                    });
-            if (!('id' in fields)) {
-                fields.id = {
-                    'string': Sao.i18n.gettext('ID'),
-                    'name': 'id',
-                    'type': 'integer'
-                };
-            }
-
-            var context = jQuery.extend({},
-                    this.model.session.context,
-                    this.context);
-            var domain_parser = new Sao.common.DomainParser(
-                    fields, context);
-            this._domain_parser[view_id] = domain_parser;
-            return domain_parser;
-        },
-        get_selection: function(props) {
-            var prm;
-            var change_with = props.selection_change_with;
-            if (!jQuery.isEmptyObject(change_with)) {
-                var values = {};
-                change_with.forEach(function(p) {
-                    values[p] = null;
-                });
-                prm = this.model.execute(props.selection,
-                        [values]);
-            } else {
-                prm = this.model.execute(props.selection,
-                        []);
-            }
-            return prm.then(function(selection) {
-                return selection.sort(function(a, b) {
-                    return a[1].localeCompare(b[1]);
-                });
-            });
-        },
-        search_prev: function(search_string) {
-            this.offset -= this.limit;
-            this.search_filter(search_string);
-        },
-        search_next: function(search_string) {
-            this.offset += this.limit;
-            this.search_filter(search_string);
-        },
-        invalid_message: function(record) {
-            if (!record) {
-                record = this.current_record;
-            }
-            var fields_desc = {};
-            for (var fname in record.model.fields) {
-                var field = record.model.fields[fname];
-                fields_desc[fname] = field.description;
-            }
-            var domain_parser = new Sao.common.DomainParser(fields_desc);
-            var fields = [];
-            var invalid_fields = record.invalid_fields();
-            Object.keys(invalid_fields).sort().forEach(
-                function(field) {
-                    var invalid = invalid_fields[field];
-                    var string = record.model.fields[field].description.string;
-                    if ((invalid == 'required') ||
-                            (Sao.common.compare(invalid,
-                                                [[field, '!=', null]]))) {
-                        fields.push(Sao.i18n.gettext('"%1" is required', string));
-                    } else if (invalid == 'domain') {
-                        fields.push(Sao.i18n.gettext(
-                                    '"%1" is not valid according to its domain',
-                                    string));
-                    } else if (invalid == 'children') {
-                        fields.push(Sao.i18n.gettext(
-                                'The values of "%1" are not valid', string));
-                    } else {
-                        if (domain_parser.stringable(invalid)) {
-                            fields.push(domain_parser.string(invalid));
-                        } else {
-                            fields.push(Sao.i18n.gettext(
-                                    '"%1" is not valid according to its domain'),
-                                string);
-                        }
-                    }
-                });
-            if (fields.length > 5) {
-                fields.splice(5, fields.length);
-                fields.push('...');
-            }
-            return fields.join('\n');
-        },
-        get: function() {
-            if (!this.current_record) {
-                return null;
-            }
-            this.current_view.set_value();
-            return this.current_record.get();
-        },
-        get_on_change_value: function() {
-            if (!this.current_record) {
-                return null;
-            }
-            this.current_view.set_value();
-            return this.current_record.get_on_change_value();
-        },
-        reload: function(ids, written) {
-            this.group.reload(ids);
-            if (written) {
-                this.group.written(ids);
-            }
-            if (this.group.parent) {
-                this.group.parent.root_parent().reload();
-            }
-            this.display();
-        },
-        get_buttons: function() {
-            var selected_records = this.current_view.selected_records();
-            if (jQuery.isEmptyObject(selected_records)) {
-                return [];
-            }
-            var buttons = this.current_view.get_buttons();
-            selected_records.forEach(function(record) {
-                buttons = buttons.filter(function(button) {
-                    if (record.group.get_readonly() || record.readonly()) {
-                        return false;
-                    }
-                    if (button.attributes.type === 'instance') {
-                        return false;
-                    }
-                    var states = record.expr_eval(
-                        button.attributes.states || {});
-                    return !(states.invisible || states.readonly);
-                });
-            });
-            return buttons;
-        },
-        button: function(attributes) {
-            var ids;
-            var process_action = function(action) {
-                this.reload(ids, true);
-                if (typeof action == 'string') {
-                    this.client_action(action);
-                }
-                else if (action) {
-                    Sao.Action.execute(action, {
-                        model: this.model_name,
-                        id: record.id,
-                        ids: ids
-                    }, null, this.context);
-                }
-            };
-
-            var selected_records = this.current_view.selected_records();
-            this.current_view.set_value();
-            var fields = this.current_view.get_fields();
-
-            var prms = [];
-            var reset_state = function(record) {
-                return function() {
-                    this.display(true);
-                    // Reset valid state with normal domain
-                    record.validate(fields);
-                }.bind(this);
-            }.bind(this);
-            for (var i = 0; i < selected_records.length; i++) {
-                var record = selected_records[i];
-                var domain = record.expr_eval(
-                    (attributes.states || {})).pre_validate || [];
-                prms.push(record.validate(fields, false, domain));
-            }
-            jQuery.when.apply(jQuery, prms).then(function() {
-                var record;
-                for (var i = 0; i < selected_records.length; i++) {
-                    record = selected_records[i];
-                    var result = arguments[i];
-                    if (result) {
-                        continue;
-                    }
-                    Sao.common.warning.run(
-                            this.invalid_message(record),
-                            Sao.i18n.gettext('Pre-validation'))
-                        .then(reset_state(record));
-                    return;
-                }
-
-                // TODO confirm
-                record = this.current_record;
-                if (attributes.type === 'instance') {
-                    var args = record.expr_eval(attributes.change || []);
-                    var values = record._get_on_change_args(args);
-                    record.model.execute(attributes.name, [values], this.context)
-                        .then(function(changes) {
-                            record.set_on_change(changes);
-                            record.group.root_group().screens.forEach(function(screen) {
-                                screen.display();
-                            });
-                        });
-                } else {
-                    var context = jQuery.extend({}, this.context);
-                    context._timestamp = {};
-                    for (i = 0; i < selected_records.length; i++) {
-                        record = selected_records[i];
-                        jQuery.extend(context._timestamp, record.get_timestamp());
-                    }
-                    record.save(false).done(function() {
-                        ids = selected_records.map(
-                                function(record) {
-                                    return record.id;
-                                });
-                        record.model.execute(attributes.name,
-                            [ids], context).then(process_action.bind(this));
-                    }.bind(this));
-                }
-            }.bind(this));
-        },
-        client_action: function(action) {
-            var access = Sao.common.MODELACCESS.get(this.model_name);
-            if (action == 'new') {
-                if (access.create) {
-                    this.new_();
-                }
-            } else if (action == 'delete') {
-                if (access['delete']) {
-                    this.remove(!this.group.parent, false, !this.group.parent);
-                }
-            } else if (action == 'remove') {
-                if (access.write && access.read && this.group.parent) {
-                    this.remove(false, true, false);
-                }
-            } else if (action == 'copy') {
-                if (access.create) {
-                    this.copy();
-                }
-            } else if (action == 'next') {
-                this.display_next();
-            } else if (action == 'previous') {
-                this.display_previous();
-            } else if (action == 'close') {
-                Sao.Tab.close_current();
-            } else if (action.startsWith('switch')) {
-                var view_type = action.split(' ')[1];
-                this.switch_view(view_type);
-            } else if (action == 'reload') {
-                if (~['tree', 'graph', 'calendar'].indexOf(this.current_view.view_type) &&
-                        !this.group.parent) {
-                    this.search_filter();
-                }
-            } else if (action == 'reload menu') {
-                Sao.get_preferences().then(function(preferences) {
-                    Sao.menu(preferences);
-                });
-            } else if (action == 'reload context') {
-                Sao.get_preferences();
-            }
-        },
-        save_tree_state: function(store) {
-            var prms = [];
-            var prm;
-            store = (store === undefined) ? true : store;
-            var i, len, view, widgets, wi, wlen;
-            var parent_ = this.group.parent ? this.group.parent.id : null;
-            var timestamp = this.group.parent ?
-                this.group.parent._timestamp : null;
-            for (i = 0, len = this.views.length; i < len; i++) {
-                view = this.views[i];
-                if (view.view_type == 'form') {
-                    for (var wid_key in view.widgets) {
-                        if (!view.widgets.hasOwnProperty(wid_key)) {
-                            continue;
-                        }
-                        widgets = view.widgets[wid_key];
-                        for (wi = 0, wlen = widgets.length; wi < wlen; wi++) {
-                            if (widgets[wi].screen) {
-                                prm = widgets[wi].screen.save_tree_state(store);
-                                prms.push(prm);
-                            }
-                        }
-                    }
-                    if ((this.views.length == 1) && this.current_record) {
-                        if (!(parent_ in this.tree_states)) {
-                            this.tree_states[parent_] = {};
-                        }
-                        this.tree_states[parent_][
-                            view.children_field || null] = [
-                            timestamp, [], [[this.current_record.id]]];
-                    }
-                } else if (view.view_type == 'tree') {
-                    var paths = view.get_expanded_paths();
-                    var selected_paths = view.get_selected_paths();
-                    if (!(parent_ in this.tree_states)) {
-                        this.tree_states[parent_] = {};
-                    }
-                    this.tree_states[parent_][view.children_field || null] = [
-                        timestamp, paths, selected_paths];
-                    if (store && view.attributes.tree_state) {
-                        var tree_state_model = new Sao.Model(
-                                'ir.ui.view_tree_state');
-                        prm = tree_state_model.execute('set', [
-                                this.model_name,
-                                this.get_tree_domain(parent_),
-                                view.children_field,
-                                JSON.stringify(paths),
-                                JSON.stringify(selected_paths)], {});
-                        prms.push(prm);
-                    }
-                }
-            }
-            return jQuery.when.apply(jQuery, prms);
-        },
-        get_tree_domain: function(parent_) {
-            var domain;
-            if (parent_) {
-                domain = (this.domain || []).concat([
-                        [this.exclude_field, '=', parent_]]);
-            } else {
-                domain = this.domain;
-            }
-            return JSON.stringify(Sao.rpc.prepareObject(domain));
-        },
-        set_tree_state: function() {
-            var parent_, timestamp, state, state_prm, tree_state_model;
-            var view = this.current_view;
-            if (!~['tree', 'form'].indexOf(view.view_type)) {
-                return;
-            }
-
-            if (~this.tree_states_done.indexOf(view)) {
-                return;
-            }
-            if (view.view_type == 'form' &&
-                    !jQuery.isEmptyObject(this.tree_states_done)) {
-                return;
-            }
-
-            parent_ = this.group.parent ? this.group.parent.id : null;
-            timestamp = parent ? parent._timestamp : null;
-            if (!(parent_ in this.tree_states)) {
-                this.tree_states[parent_] = {};
-            }
-            state = this.tree_states[parent_][view.children_field || null];
-            if (state) {
-                if (timestamp != state[0]) {
-                    state = undefined;
-                }
-            }
-            if (state === undefined) {
-                tree_state_model = new Sao.Model('ir.ui.view_tree_state');
-                state_prm = tree_state_model.execute('get', [
-                        this.model_name,
-                        this.get_tree_domain(parent_),
-                        view.children_field], {})
-                    .then(function(state) {
-                        return [timestamp,
-                            JSON.parse(state[0]), JSON.parse(state[1])];
-                    });
-            } else {
-                state_prm = jQuery.when(state);
-            }
-            state_prm.done(function(state) {
-                var expanded_nodes, selected_nodes, record;
-                this.tree_states[parent_][view.children_field || null] = state;
-                expanded_nodes = state[1];
-                selected_nodes = state[2];
-                if (view.view_type == 'tree') {
-                    view.display(selected_nodes, expanded_nodes);
-                } else {
-                    if (!jQuery.isEmptyObject(selected_nodes)) {
-                        for (var i = 0; i < selected_nodes[0].length; i++) {
-                            var new_record = this.group.get(selected_nodes[0][i]);
-                            if (!new_record) {
-                                break;
-                            } else {
-                                record = new_record;
-                            }
-                        }
-                        if (record && (record != this.current_record)) {
-                            this.set_current_record(record);
-                            // Force a display of the view to synchronize the
-                            // widgets with the new record
-                            view.display();
-                        }
-                    }
-                }
-            }.bind(this));
-            this.tree_states_done.push(view);
-        }
-    });
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View = Sao.class_(Object, {
-        init: function(screen, xml) {
-            this.screen = screen;
-            this.view_type = null;
-            this.el = null;
-            this.view_id = null;
-            this.fields = {};
-            var attributes = xml.children()[0].attributes;
-            this.attributes = {};
-            for (var i = 0, len = attributes.length; i < len; i++) {
-                var attribute = attributes[i];
-                this.attributes[attribute.name] = attribute.value;
-            }
-            screen.set_on_write(this.attributes.on_write);
-        },
-        set_value: function() {
-        },
-        get_fields: function() {
-            return Object.keys(this.fields);
-        },
-        selected_records: function() {
-            return [];
-        },
-        get_buttons: function() {
-            return [];
-        }
-    });
-
-    Sao.View.idpath2path = function(tree, idpath) {
-        var path = [];
-        var child_path;
-        if (!idpath) {
-            return [];
-        }
-        for (var i = 0, len = tree.rows.length; i < len; i++) {
-            if (tree.rows[i].record.id == idpath[0]) {
-                path.push(i);
-                child_path = Sao.View.idpath2path(tree.rows[i],
-                        idpath.slice(1, idpath.length));
-                path = path.concat(child_path);
-                break;
-            }
-        }
-        return path;
-    };
-
-    Sao.View.parse = function(screen, xml, children_field) {
-        switch (xml.children().prop('tagName')) {
-            case 'tree':
-                return new Sao.View.Tree(screen, xml, children_field);
-            case 'form':
-                return new Sao.View.Form(screen, xml);
-            case 'graph':
-                return new Sao.View.Graph(screen, xml);
-            case 'calendar':
-                return new Sao.View.Calendar(screen, xml);
-        }
-    };
-
-    Sao.View.resize = function(el) {
-        // Let the browser compute the table size with the fixed layout
-        // then set this size to the treeview to allow scroll on overflow
-        // and set the table layout to auto to get the width from the content.
-        if (!el) {
-            el = jQuery(document);
-        }
-        el.find('.treeview').each(function() {
-            var treeview = jQuery(this);
-            treeview.css('width', '100%');
-            treeview.children('.tree').css('table-layout', 'fixed');
-        });
-        el.find('.treeview').each(function() {
-            var treeview = jQuery(this);
-            if (treeview.width()) {
-                treeview.css('width', treeview.width());
-                treeview.children('.tree').css('table-layout', 'auto');
-            }
-        });
-    };
-    jQuery(window).resize(function() {
-        Sao.View.resize();
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View.Form = Sao.class_(Sao.View, {
-        editable: true,
-        init: function(screen, xml) {
-            Sao.View.Form._super.init.call(this, screen, xml);
-            this.view_type = 'form';
-            this.el = jQuery('<div/>', {
-                'class': 'form'
-            });
-            this.widgets = {};
-            this.widget_id = 0;
-            this.state_widgets = [];
-            this.containers = [];
-            this.notebooks = [];
-            var root = xml.children()[0];
-            var container = this.parse(screen.model, root);
-            this.el.append(container.el);
-        },
-        _parse_node: function(model, child, container, attributes, labels) {
-            var widget;
-            switch (child.tagName) {
-                case 'image':
-                    this._parse_image(
-                            model, child, container, attributes);
-                    break;
-                case 'separator':
-                    this._parse_separator(
-                            model, child, container, attributes);
-                    break;
-                case 'label':
-                    widget = this._parse_label(
-                            model, child, container, attributes);
-                    if (attributes.name && widget) {
-                        labels[attributes.name] = widget;
-                    }
-                    break;
-                case 'newline':
-                    container.add_row();
-                    break;
-                case 'button':
-                    this._parse_button(child, container, attributes);
-                    break;
-                case 'notebook':
-                    this._parse_notebook(
-                            model, child, container, attributes);
-                    break;
-                case 'page':
-                    this._parse_page(model, child, container, attributes);
-                    break;
-                case 'field':
-                    widget = this._parse_field(
-                            model, child, container, attributes);
-                    if ((attributes.name in labels) &&
-                            widget &&
-                            widget.labelled) {
-                        var label = labels[attributes.name];
-                        label.el.uniqueId();
-                        widget.labelled.uniqueId();
-                        widget.labelled.attr(
-                                'aria-labelledby', label.el.attr('id'));
-                        label.el.attr('for', widget.labelled.attr('id'));
-                    }
-                    break;
-                case 'group':
-                    this._parse_group(model, child, container, attributes);
-                    break;
-                case 'hpaned':
-                    this._parse_paned(model, child, container, attributes,
-                            'horizontal');
-                    break;
-                case 'vpaned':
-                    this._parse_paned(model, child, container, attributes,
-                            'vertical');
-                    break;
-                case 'child':
-                    this._parse_child(model, child, container, attributes);
-                    break;
-            }
-        },
-        parse: function(model, node, container) {
-            if (container === undefined) {
-                container = new Sao.View.Form.Container(
-                    Number(node.getAttribute('col') || 4));
-                this.containers.push(container);
-            }
-            var labels = {};
-            var _parse = function(index, child) {
-                var attributes = {};
-                for (var i = 0, len = child.attributes.length; i < len; i++) {
-                    var attribute = child.attributes[i];
-                    attributes[attribute.name] = attribute.value;
-                }
-                ['readonly', 'invisible'].forEach(function(name) {
-                    if (attributes[name]) {
-                        attributes[name] = attributes[name] == 1;
-                    }
-                });
-                ['yexpand', 'yfill', 'xexpand', 'xfill', 'colspan'].forEach(
-                        function(name) {
-                            if (attributes[name]) {
-                                attributes[name] = Number(attributes[name]);
-                            }
-                        });
-                this._parse_node(model, child, container, attributes, labels);
-            };
-            jQuery(node).children().each(_parse.bind(this));
-            return container;
-        },
-        _parse_image: function(model, node, container, attributes) {
-            var image = new Sao.View.Form.Image_(attributes);
-            this.state_widgets.push(image);
-            container.add(attributes, image);
-        },
-        _parse_separator: function(model, node, container, attributes) {
-            var name = attributes.name;
-            var text = attributes.string;
-            if (name in model.fields) {
-                if (!attributes.states && (name in model.fields)) {
-                    attributes.states = model.fields[name].description.states;
-                }
-                if (!text) {
-                    text = model.fields[name].description.string;
-                }
-            }
-            var separator = new Sao.View.Form.Separator(text, attributes);
-            this.state_widgets.push(separator);
-            container.add(attributes, separator);
-        },
-        _parse_label: function(model, node, container, attributes) {
-            var name = attributes.name;
-            var text = attributes.string;
-            if (attributes.xexpand === undefined) {
-                attributes.xexpand = 0;
-            }
-            if (name in model.fields) {
-                if (name == this.screen.exclude_field) {
-                    container.add(attributes);
-                    return;
-                }
-                if (!attributes.states && (name in model.fields)) {
-                    attributes.states = model.fields[name].description.states;
-                }
-                if (!text) {
-                    // TODO RTL and translation
-                    text = model.fields[name]
-                        .description.string + ': ';
-                }
-                if (attributes.xalign === undefined) {
-                    attributes.xalign = 1.0;
-                }
-            }
-            var label;
-            if (text) {
-                label = new Sao.View.Form.Label(text, attributes);
-                this.state_widgets.push(label);
-            }
-            container.add(attributes, label);
-            return label;
-        },
-        _parse_button: function(node, container, attributes) {
-            var button = new Sao.common.Button(attributes);
-            this.state_widgets.push(button);
-            container.add(attributes, button);
-            button.el.click(button, this.button_clicked.bind(this));
-        },
-        _parse_notebook: function(model, node, container, attributes) {
-            if (attributes.colspan === undefined) {
-                attributes.colspan = 4;
-            }
-            var notebook = new Sao.View.Form.Notebook(attributes);
-            this.notebooks.push(notebook);
-            this.state_widgets.push(notebook);
-            container.add(attributes, notebook);
-            this.parse(model, node, notebook);
-        },
-        _parse_page: function(model, node, container, attributes) {
-            var text = attributes.string;
-            if (attributes.name in model.fields) {
-                var field = model.fields[attributes.name];
-                if (attributes.name == this.screen.exclude_field) {
-                    return;
-                }
-                ['states', 'string'].forEach(function(attr) {
-                    if ((attributes[attr] === undefined) &&
-                            (field.description[attr] !== undefined)) {
-                        attributes[attr] = field.description[attr];
-                    }
-                });
-            }
-            var page = this.parse(model, node);
-            page = new Sao.View.Form.Page(
-                    container.add(page.el, attributes.string), attributes);
-            this.state_widgets.push(page);
-        },
-        _parse_field: function(model, node, container, attributes) {
-            var name = attributes.name;
-            if (!(name in model.fields) || name == this.screen.exclude_field) {
-                container.add(attributes);
-                return;
-            }
-            if (!attributes.widget) {
-                attributes.widget = model.fields[name]
-                    .description.type;
-            }
-            var attribute_names = ['relation', 'domain', 'selection', 'help',
-                'relation_field', 'string', 'views', 'add_remove', 'sort',
-                'context', 'size', 'filename', 'autocomplete', 'translate',
-                'create', 'delete', 'selection_change_with', 'schema_model'];
-            for (var i in attribute_names) {
-                var attr = attribute_names[i];
-                if ((attr in model.fields[name].description) &&
-                        (node.getAttribute(attr) === null)) {
-                    attributes[attr] = model.fields[name]
-                        .description[attr];
-                }
-            }
-            var WidgetFactory = Sao.View.form_widget_get(
-                    attributes.widget);
-            if (!WidgetFactory) {
-                container.add(attributes);
-                return;
-            }
-            var widget = new WidgetFactory(name, model, attributes);
-            widget.position = this.widget_id += 1;
-            widget.view = this;
-            // TODO expand, fill, height, width
-            container.add(attributes, widget);
-            if (this.widgets[name] === undefined) {
-                this.widgets[name] = [];
-            }
-            this.widgets[name].push(widget);
-            this.fields[name] = true;
-            return widget;
-        },
-        _parse_group: function(model, node, container, attributes) {
-            var group = new Sao.View.Form.Group(attributes);
-            group.add(this.parse(model, node));
-            this.state_widgets.push(group);
-            container.add(attributes, group);
-        },
-        _parse_paned: function(model, node, container, attributes,
-                              orientation) {
-            if (attributes.yexpand === undefined) {
-                attributes.yexpand = true;
-            }
-            if (attributes.yfill === undefined) {
-                attributes.yfill = true;
-            }
-            var paned = new Sao.common.Paned(orientation);
-            // TODO position
-            container.add(attributes, paned);
-            this.parse(model, node, paned);
-        },
-        _parse_child: function(model, node, paned, attributes) {
-            var container = this.parse(model, node);
-            var child;
-            if (!paned.get_child1().children().length) {
-                child = paned.get_child1();
-            } else {
-                child = paned.get_child2();
-            }
-            child.append(container.el);
-        },
-        get_buttons: function() {
-            var buttons = [];
-            for (var j in this.state_widgets) {
-                var widget = this.state_widgets[j];
-                if (widget instanceof Sao.common.Button) {
-                    buttons.push(widget);
-                }
-            }
-            return buttons;
-        },
-        display: function() {
-            var record = this.screen.current_record;
-            var field;
-            var name;
-            var promesses = {};
-            if (record) {
-                // Force to set fields in record
-                // Get first the lazy one to reduce number of requests
-                var fields = [];
-                for (name in record.model.fields) {
-                    field = record.model.fields[name];
-                    fields.push([name, field.description.loading || 'eager']);
-                }
-                fields.sort(function(a, b) {
-                    return a[1].localeCompare(b[1]);
-                });
-                fields.forEach(function(e) {
-                    var name = e[0];
-                    promesses[name] = record.load(name);
-                });
-            }
-            var set_state = function(record, field, name) {
-                var prm = jQuery.when();
-                if (name in promesses) {
-                    prm = promesses[name];
-                }
-                prm.done(function() {
-                    field.set_state(record);
-                });
-            };
-            var display = function(record, field, name) {
-                return function(widget) {
-                    var prm = jQuery.when();
-                    if (name in promesses) {
-                        prm = promesses[name];
-                    }
-                    prm.done(function() {
-                        widget.display(record, field);
-                    });
-                };
-            };
-            for (name in this.widgets) {
-                var widgets = this.widgets[name];
-                field = null;
-                if (record) {
-                    field = record.model.fields[name];
-                }
-                if (field) {
-                    set_state(record, field, name);
-                }
-                widgets.forEach(display(record, field, name));
-            }
-            return jQuery.when.apply(jQuery,
-                    jQuery.map(promesses, function(p) {
-                        return p;
-                    })
-                ).done(function() {
-                    var j;
-                    for (j in this.state_widgets) {
-                        var state_widget = this.state_widgets[j];
-                        state_widget.set_state(record);
-                    }
-                    for (j in this.containers) {
-                        var container = this.containers[j];
-                        container.resize();
-                    }
-                    Sao.View.resize(this.el);
-                }.bind(this));
-        },
-        set_value: function() {
-            var record = this.screen.current_record;
-            if (record) {
-                var set_value = function(widget) {
-                    widget.set_value(record, this);
-                };
-                for (var name in this.widgets) {
-                    if (name in record.model.fields) {
-                        var widgets = this.widgets[name];
-                        var field = record.model.fields[name];
-                        widgets.forEach(set_value, field);
-                    }
-                }
-            }
-        },
-        button_clicked: function(event) {
-            var button = event.data;
-            this.screen.button(button.attributes);
-        },
-        selected_records: function() {
-            if (this.screen.current_record) {
-                return [this.screen.current_record];
-            }
-            return [];
-        },
-        set_cursor: function(new_, reset_view) {
-            var i, name, j;
-            var focus_el, notebook, child;
-            var widgets, error_el, pages, is_ancestor;
-
-            var currently_focused = jQuery(document.activeElement);
-            var has_focus = currently_focused.closest(this.el) > 0;
-            if (reset_view || has_focus) {
-                if (reset_view) {
-                    for (i = 0; i < this.notebooks.length; i++) {
-                        notebook = this.notebooks[i];
-                        notebook.set_current_page(0);
-                    }
-                }
-                if (this.attributes.cursor in this.widgets) {
-                    focus_el = Sao.common.find_focusable_child(
-                            this.widgets[this.attributes.cursor][0].el);
-                } else {
-                    child = Sao.common.find_focusable_child(this.el);
-                    if (child) {
-                        child.focus();
-                    }
-                }
-            }
-
-            var record = this.screen.current_record;
-            if (record) {
-                var invalid_widgets = [];
-                // We use the has-error class to find the invalid elements
-                // because Sao.common.find_focusable_child use the :visible
-                // selector which acts differently than GTK's get_visible
-                var error_els = this.el.find('.has-error');
-                var invalid_fields = record.invalid_fields();
-                for (name in invalid_fields) {
-                    widgets = this.widgets[name];
-                    for (i = 0; i < error_els.length; i++) {
-                        error_el = jQuery(error_els[i]);
-                        for (j = 0; j < widgets.length; j++) {
-                            if (error_el.closest(widgets[j].el).length > 0) {
-                                invalid_widgets.push(error_el);
-                                break;
-                            }
-                        }
-                    }
-                }
-                if (invalid_widgets.length > 0) {
-                    focus_el = Sao.common.find_first_focus_widget(this.el,
-                            invalid_widgets);
-                }
-            }
-
-            if (focus_el) {
-                for (i = 0; i < this.notebooks.length; i++) {
-                    notebook = this.notebooks[i];
-                    pages = notebook.get_n_pages();
-                    for (j = 0; j < pages; j++) {
-                        child = notebook.get_nth_page(j);
-                        is_ancestor = (
-                                jQuery(focus_el).closest(child).length > 0);
-                        if (is_ancestor) {
-                            notebook.set_current_page(j);
-                            break;
-                        }
-                    }
-                }
-                // Only input & textarea can grab the focus
-                jQuery(focus_el).find('input,select,textarea').focus();
-            }
-        }
-    });
-
-    Sao.View.Form.Container = Sao.class_(Object, {
-        init: function(col) {
-            if (col === undefined) col = 4;
-            this.col = col;
-            this.el = jQuery('<table/>', {
-                'class': 'form-container responsive responsive-noheader'
-            });
-            this.add_row();
-        },
-        add_row: function() {
-            this.el.append(jQuery('<tr/>'));
-        },
-        rows: function() {
-            return this.el.children().children('tr');
-        },
-        row: function() {
-            return this.rows().last();
-        },
-        add: function(attributes, widget) {
-            var colspan = attributes.colspan;
-            if (colspan === undefined) colspan = 1;
-            var xfill = attributes.xfill;
-            if (xfill === undefined) xfill = 1;
-            var xexpand = attributes.xexpand;
-            if (xexpand === undefined) xexpand = 1;
-            var len = 0;
-            var row = this.row();
-            row.children().map(function(i, e) {
-                len += Number(jQuery(e).attr('colspan') || 1);
-            });
-            if (len + colspan > this.col) {
-                this.add_row();
-                row = this.row();
-            }
-            var el;
-            if (widget) {
-                el = widget.el;
-            }
-            var cell = jQuery('<td/>', {
-                'colspan': colspan,
-                'class': widget ? widget.class_ || '' : ''
-            }).append(el);
-            row.append(cell);
-
-            if (!widget) {
-                return;
-            }
-
-            // TODO yexpand
-            if (attributes.yfill) {
-                cell.css('vertical-align', 'top');
-            }
-
-            if (attributes.xalign !== undefined) {
-                // TODO replace by start/end when supported
-                cell.css('text-align', attributes.xalign >= 0.5? 'right': 'left');
-            }
-            if (xexpand) {
-                cell.addClass('xexpand');
-                cell.css('width', '100%');
-            }
-            if (xfill) {
-                cell.addClass('xfill');
-                if (xexpand) {
-                    el.css('width', '100%');
-                }
-            }
-
-            if (attributes.help) {
-                widget.el.data('toggle', 'tooltip');
-                widget.el.attr('title', attributes.help);
-                widget.el.tooltip();
-            }
-        },
-        resize: function() {
-            var rows = this.rows().toArray();
-            var widths = [];
-            var col = this.col;
-            var has_expand = false;
-            var i, j;
-            var get_xexpands = function(row) {
-                row = jQuery(row);
-                var xexpands = [];
-                i = 0;
-                row.children().map(function() {
-                    var cell = jQuery(this);
-                    var colspan = Math.min(Number(cell.attr('colspan')), col);
-                    if (cell.hasClass('xexpand') &&
-                        (!jQuery.isEmptyObject(cell.children())) &&
-                        (cell.children(':not(.tooltip)').css('display') != 'none')) {
-                        xexpands.push([cell, i]);
-                    }
-                    i += colspan;
-                });
-                return xexpands;
-            };
-            // Sort rows to compute first the most constraining row
-            // which are the one with the more xexpand cells
-            // and with the less colspan
-            rows.sort(function(a, b) {
-                a = get_xexpands(a);
-                b = get_xexpands(b);
-                if (a.length == b.length) {
-                    var reduce = function(previous, current) {
-                        var cell = current[0];
-                        var colspan = Math.min(
-                            Number(cell.attr('colspan')), col);
-                        return previous + colspan;
-                    };
-                    return a.reduce(reduce, 0) - b.reduce(reduce, 0);
-                } else {
-                    return b.length - a.length;
-                }
-            });
-            rows.forEach(function(row) {
-                row = jQuery(row);
-                var xexpands = get_xexpands(row);
-                var width = 100 / xexpands.length;
-                xexpands.forEach(function(e) {
-                    var cell = e[0];
-                    i = e[1];
-                    var colspan = Math.min(Number(cell.attr('colspan')), col);
-                    var current_width = 0;
-                    for (j = 0; j < colspan; j++) {
-                        current_width += widths[i + j] || 0;
-                    }
-                    for (j = 0; j < colspan; j++) {
-                        if (!current_width) {
-                            widths[i + j] = width / colspan;
-                        } else if (current_width > width) {
-                            // Split proprotionally the difference over all cells
-                            // following their current width
-                            var diff = current_width - width;
-                            if (widths[i + j]) {
-                                widths[i + j] -= (diff /
-                                    (current_width / widths[i + j]));
-                            }
-                        }
-                    }
-                });
-                if (!jQuery.isEmptyObject(xexpands)) {
-                    has_expand = true;
-                }
-            });
-            rows.forEach(function(row) {
-                row = jQuery(row);
-                i = 0;
-                row.children().map(function() {
-                    var cell = jQuery(this);
-                    var colspan = Math.min(Number(cell.attr('colspan')), col);
-                    if (cell.hasClass('xexpand') &&
-                        (cell.children(':not(.tooltip)').css('display') !=
-                         'none')) {
-                        var width = 0;
-                        for (j = 0; j < colspan; j++) {
-                            width += widths[i + j] || 0;
-                        }
-                        cell.css('width', width + '%');
-                    } else {
-                        cell.css('width', '');
-                    }
-                    if (cell.children().css('display') == 'none') {
-                        cell.css('visibility', 'collapse');
-                    } else {
-                        cell.css('visibility', 'visible');
-                    }
-                    i += colspan;
-                });
-            });
-            if (has_expand) {
-                this.el.css('width', '100%');
-            } else {
-                this.el.css('width', '');
-            }
-        }
-    });
-
-    Sao.View.Form.StateWidget = Sao.class_(Object, {
-        init: function(attributes) {
-            this.attributes = attributes;
-        },
-        set_state: function(record) {
-            var state_changes;
-            if (record) {
-                state_changes = record.expr_eval(this.attributes.states || {});
-            } else {
-                state_changes = {};
-            }
-            var invisible = state_changes.invisible;
-            if (invisible === undefined) {
-                invisible = this.attributes.invisible;
-            }
-            if (invisible) {
-                this.hide();
-            } else {
-                this.show();
-            }
-        },
-        show: function() {
-            this.el.show();
-        },
-        hide: function() {
-            this.el.hide();
-        }
-    });
-
-    Sao.View.Form.Separator = Sao.class_(Sao.View.Form.StateWidget, {
-        init: function(text, attributes) {
-            Sao.View.Form.Separator._super.init.call(this, attributes);
-            this.el = jQuery('<div/>', {
-                'class': 'form-separator'
-            });
-            if (text) {
-                this.el.append(jQuery('<label/>', {
-                    text: text
-                }));
-            }
-            this.el.append(jQuery('<hr/>'));
-        }
-    });
-
-    Sao.View.Form.Label = Sao.class_(Sao.View.Form.StateWidget, {
-        class_: 'form-label',
-        init: function(text, attributes) {
-            Sao.View.Form.Label._super.init.call(this, attributes);
-            this.el = jQuery('<label/>', {
-                text: text,
-                'class': this.class_ + ' form-label'
-            });
-        },
-        set_state: function(record) {
-            Sao.View.Form.Label._super.set_state.call(this, record);
-            var field;
-            if (this.attributes.name && record) {
-                field = record.model.fields[this.attributes.name];
-            }
-            if ((this.attributes.string === undefined) && field) {
-                var text = '';
-                if (record) {
-                    text = field.get_client(record) || '';
-                }
-                this.el.val(text);
-            }
-            var state_changes;
-            if (record) {
-                state_changes = record.expr_eval(this.attributes.states || {});
-            } else {
-                state_changes = {};
-            }
-            if ((field && field.description.required) ||
-                    state_changes.required) {
-                this.el.addClass('required');
-            } else {
-                this.el.removeClass('required');
-            }
-            if ((field && field.description.readonly) ||
-                    state_changes.readonly) {
-                this.el.removeClass('editable');
-                this.el.removeClass('required');
-            } else {
-                this.el.addClass('editable');
-            }
-        }
-    });
-
-    Sao.View.Form.Notebook = Sao.class_(Sao.View.Form.StateWidget, {
-        class_: 'form-notebook',
-        init: function(attributes) {
-            Sao.View.Form.Notebook._super.init.call(this, attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.nav = jQuery('<ul/>', {
-                'class': 'nav nav-tabs',
-                role: 'tablist'
-            }).appendTo(this.el);
-            this.panes = jQuery('<div/>', {
-                'class': 'tab-content'
-            }).appendTo(this.el);
-            this.selected = false;
-        },
-        add: function(tab, text) {
-            var pane = jQuery('<div/>', {
-                'role': 'tabpanel',
-                'class': 'tab-pane',
-            }).uniqueId();
-            var tab_id = pane.attr('id');
-            var page = jQuery('<li/>', {
-                'role': 'presentation'
-            }).append(
-                jQuery('<a/>', {
-                    'aria-controls': tab_id,
-                    'role': 'tab',
-                    'data-toggle': 'tab',
-                    'href': '#' + tab_id
-                }).append(text)
-                .on('shown.bs.tab', function() {
-                    Sao.View.resize(tab);
-                })).appendTo(this.nav);
-            pane.html(tab).appendTo(this.panes);
-            if (!this.selected) {
-                // Can not use .tab('show')
-                page.addClass('active');
-                pane.addClass('active');
-                this.selected = true;
-            }
-            return page;
-        },
-        set_current_page: function(page_index) {
-            var tab = this.nav.find(
-                    'li[role="presentation"]:eq(' + page_index + ') a');
-            tab.tab('show');
-        },
-        get_n_pages: function() {
-            return this.nav.find("li[role='presentation']").length;
-        },
-        get_nth_page: function(page_index) {
-            return jQuery(this.panes.find("div[role='tabpanel']")[page_index]);
-        }
-    });
-
-    Sao.View.Form.Page = Sao.class_(Sao.View.Form.StateWidget, {
-        init: function(el, attributes) {
-            Sao.View.Form.Page._super.init.call(this, attributes);
-            this.el = el;
-        }
-    });
-
-    Sao.View.Form.Group = Sao.class_(Sao.View.Form.StateWidget, {
-        class_: 'form-group_',
-        init: function(attributes) {
-            Sao.View.Form.Group._super.init.call(this, attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-        },
-        add: function(widget) {
-            this.el.append(widget.el);
-        }
-    });
-
-    Sao.View.Form.Image_ = Sao.class_(Sao.View.Form.StateWidget, {
-        class_: 'form-image_',
-        init: function(attributes) {
-            Sao.View.Form.Image_._super.init.call(this, attributes);
-            this.el = jQuery('<div/>', {
-                'class_': this.class_
-            });
-            this.img = jQuery('<img/>', {
-                'class': 'center-block'
-            }).appendTo(this.el);
-            Sao.common.ICONFACTORY.register_icon(attributes.name)
-                .done(function(url) {
-                    this.img.attr('src', url);
-                }.bind(this));
-        }
-    });
-
-    Sao.View.form_widget_get = function(type) {
-        switch (type) {
-            case 'char':
-                return Sao.View.Form.Char;
-            case 'password':
-                return Sao.View.Form.Password;
-            case 'date':
-                return Sao.View.Form.Date;
-            case 'datetime':
-                return Sao.View.Form.DateTime;
-            case 'time':
-                return Sao.View.Form.Time;
-            case 'timedelta':
-                return Sao.View.Form.TimeDelta;
-            case 'integer':
-            case 'biginteger':
-                return Sao.View.Form.Integer;
-            case 'float':
-            case 'numeric':
-                return Sao.View.Form.Float;
-            case 'selection':
-                return Sao.View.Form.Selection;
-            case 'boolean':
-                return Sao.View.Form.Boolean;
-            case 'text':
-                return Sao.View.Form.Text;
-            case 'richtext':
-                return Sao.View.Form.RichText;
-            case 'many2one':
-                return Sao.View.Form.Many2One;
-            case 'one2one':
-                return Sao.View.Form.One2One;
-            case 'reference':
-                return Sao.View.Form.Reference;
-            case 'one2many':
-                return Sao.View.Form.One2Many;
-            case 'many2many':
-                return Sao.View.Form.Many2Many;
-            case 'binary':
-                return Sao.View.Form.Binary;
-            case 'multiselection':
-                return Sao.View.Form.MultiSelection;
-            case 'image':
-                return Sao.View.Form.Image;
-            case 'url':
-                return Sao.View.Form.URL;
-            case 'email':
-                return Sao.View.Form.Email;
-            case 'callto':
-                return Sao.View.Form.CallTo;
-            case 'sip':
-                return Sao.View.Form.SIP;
-            case 'progressbar':
-                return Sao.View.Form.ProgressBar;
-            case 'dict':
-                return Sao.View.Form.Dict;
-        }
-    };
-
-
-    Sao.View.Form.Widget = Sao.class_(Object, {
-        init: function(field_name, model, attributes) {
-            this.field_name = field_name;
-            this.model = model;
-            this.view = null;  // Filled later
-            this.attributes = attributes;
-            this.el = null;
-            this.position = 0;
-            this.visible = true;
-            this.labelled = null;  // Element which received the labelledby
-        },
-        display: function(record, field) {
-            var readonly = this.attributes.readonly;
-            var invisible = this.attributes.invisible;
-            if (!field) {
-                if (readonly === undefined) {
-                    readonly = true;
-                }
-                if (invisible === undefined) {
-                    invisible = false;
-                }
-                this.set_readonly(readonly);
-                this.set_invisible(invisible);
-                return;
-            }
-            var state_attrs = field.get_state_attrs(record);
-            if (readonly === undefined) {
-                readonly = state_attrs.readonly;
-                if (readonly === undefined) {
-                    readonly = false;
-                }
-            }
-            if (this.view.screen.attributes.readonly) {
-                readonly = true;
-            }
-            this.set_readonly(readonly);
-            var invalid = state_attrs.invalid;
-            if (!readonly && invalid) {
-                this.el.addClass('has-error');
-            } else {
-                this.el.removeClass('has-error');
-            }
-            if (invisible === undefined) {
-                invisible = field.get_state_attrs(record).invisible;
-                if (invisible === undefined) {
-                    invisible = false;
-                }
-            }
-            this.set_invisible(invisible);
-        },
-        record: function() {
-            if (this.view && this.view.screen) {
-                return this.view.screen.current_record;
-            }
-        },
-        field: function() {
-            var record = this.record();
-            if (record) {
-                return record.model.fields[this.field_name];
-            }
-        },
-        focus_out: function() {
-            if (!this.field()) {
-                return;
-            }
-            if (!this.visible) {
-                return;
-            }
-            this.set_value(this.record(), this.field());
-        },
-        set_value: function(record, field) {
-        },
-        set_readonly: function(readonly) {
-            this.el.prop('disabled', readonly);
-        },
-        set_invisible: function(invisible) {
-            this.visible = !invisible;
-            if (invisible) {
-                this.el.hide();
-            } else {
-                this.el.show();
-            }
-        }
-    });
-
-    Sao.View.Form.Char = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-char',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Char._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(this.el);
-            this.input = this.labelled = jQuery('<input/>', {
-                'type': 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(this.group);
-            if (attributes.autocomplete) {
-                this.datalist = jQuery('<datalist/>').appendTo(this.el);
-                this.datalist.uniqueId();
-                this.input.attr('list', this.datalist.attr('id'));
-            }
-            this.el.change(this.focus_out.bind(this));
-
-            if (!attributes.size) {
-                this.group.css('width', '100%');
-            }
-        },
-        display: function(record, field) {
-            Sao.View.Form.Char._super.display.call(this, record, field);
-            if (this.datalist) {
-                this.datalist.children().remove();
-                var selection = [];
-                if (record) {
-                    selection = record.autocompletion[this.field_name] || [];
-                }
-                selection.forEach(function(e) {
-                    jQuery('<option/>', {
-                        'value': e
-                    }).appendTo(this.datalist);
-                }.bind(this));
-            }
-
-            // Set size
-            var length = '';
-            var width = '100%';
-            if (record) {
-                length = record.expr_eval(this.attributes.size);
-                if (length > 0) {
-                    width = null;
-                }
-            }
-            this.input.attr('maxlength', length);
-            this.input.attr('size', length);
-            this.group.css('width', width);
-
-            if (record) {
-                var value = record.field_get_client(this.field_name);
-                this.input.val(value || '');
-            } else {
-                this.input.val('');
-            }
-        },
-        set_value: function(record, field) {
-            field.set_client(record, this.input.val());
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('readonly', readonly);
-        },
-        focus: function() {
-            this.input.focus();
-        }
-    });
-
-    Sao.View.Form.Password = Sao.class_(Sao.View.Form.Char, {
-        class_: 'form-password',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Password._super.init.call(this, field_name, model,
-                attributes);
-            this.input.prop('type', 'password');
-        }
-    });
-
-    Sao.View.Form.Date = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-date',
-        _width: '12em',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Date._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.date = this.labelled = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(this.el);
-            this.input = jQuery('<input/>', {
-                'type': 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(this.date);
-            jQuery('<span/>', {
-                'class': 'input-group-btn'
-            }).append(jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-calendar'
-            }))).appendTo(this.date);
-            this.date.datetimepicker();
-            this.date.css('width', this._width);
-            this.date.on('dp.change', this.focus_out.bind(this));
-        },
-        get_format: function(record, field) {
-            return field.date_format(record);
-        },
-        get_value: function(record, field) {
-            var value = this.date.data('DateTimePicker').date();
-            if (value) {
-                value.isDate = true;
-            }
-            return value;
-        },
-        display: function(record, field) {
-            if (record && field) {
-                this.date.data('DateTimePicker').format(
-                    Sao.common.moment_format(this.get_format(record, field)));
-            }
-            Sao.View.Form.Date._super.display.call(this, record, field);
-            var value;
-            if (record) {
-                value = field.get_client(record);
-            } else {
-                value = null;
-            }
-            this.date.data('DateTimePicker').date(value);
-        },
-        focus: function() {
-            this.input.focus();
-        },
-        set_value: function(record, field) {
-            field.set_client(record, this.get_value(record, field));
-        },
-        set_readonly: function(readonly) {
-            this.date.find('button').prop('disabled', readonly);
-            this.date.find('input').prop('readonly', readonly);
-        }
-    });
-
-    Sao.View.Form.DateTime = Sao.class_(Sao.View.Form.Date, {
-        class_: 'form-datetime',
-        _width: '25em',
-        get_format: function(record, field) {
-            return field.date_format(record) + ' ' + field.time_format(record);
-        },
-        get_value: function(record, field) {
-            var value = this.date.data('DateTimePicker').date();
-            if (value) {
-                value.isDateTime = true;
-            }
-            return value;
-        }
-    });
-
-    Sao.View.Form.Time = Sao.class_(Sao.View.Form.Date, {
-        class_: 'form-time',
-        _width: '10em',
-        get_format: function(record, field) {
-            return field.time_format(record);
-        },
-        get_value: function(record, field) {
-            var value = this.date.data('DateTimePicker').date();
-            if (value) {
-                value.isTime = true;
-            }
-            return value;
-        }
-    });
-
-    Sao.View.Form.TimeDelta = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-timedelta',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.TimeDelta._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.input = this.labelled = jQuery('<input/>', {
-                'type': 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(this.el);
-            this.el.change(this.focus_out.bind(this));
-        },
-        display: function(record, field) {
-            Sao.View.Form.TimeDelta._super.display.call(this, record, field);
-            if (record) {
-                var value = record.field_get_client(this.field_name);
-                this.input.val(value || '');
-            } else {
-                this.input.val('');
-            }
-        },
-        focus: function() {
-            this.input.focus();
-        },
-        set_value: function(record, field) {
-            field.set_client(record, this.input.val());
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('readonly', readonly);
-        }
-    });
-
-    Sao.View.Form.Integer = Sao.class_(Sao.View.Form.Char, {
-        class_: 'form-integer',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Integer._super.init.call(this, field_name, model,
-                attributes);
-            this.input.attr('type', 'text');
-            this.input.attr('width', 8);
-            this.group.css('width', '');
-            this.factor = Number(attributes.factor || 1);
-        },
-        set_value: function(record, field) {
-            field.set_client(record, this.input.val(), undefined, this.factor);
-        },
-        display: function(record, field) {
-            // Skip Char call
-            Sao.View.Form.Char._super.display.call(this, record, field);
-            if (record) {
-                var value = record.model.fields[this.field_name]
-                    .get_client(record, this.factor);
-                this.input.val(value);
-            } else {
-                this.input.val('');
-            }
-        }
-    });
-
-    Sao.View.Form.Float = Sao.class_(Sao.View.Form.Integer, {
-        class_: 'form-float'
-    });
-
-    Sao.View.Form.Selection = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-selection',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Selection._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.select = this.labelled = jQuery('<select/>', {
-                'class': 'form-control input-sm'
-            });
-            this.el.append(this.select);
-            this.select.change(this.focus_out.bind(this));
-            Sao.common.selection_mixin.init.call(this);
-            this.init_selection();
-        },
-        init_selection: function(key) {
-            Sao.common.selection_mixin.init_selection.call(this, key,
-                this.set_selection.bind(this));
-        },
-        update_selection: function(record, field, callbak) {
-            Sao.common.selection_mixin.update_selection.call(this, record,
-                field, function(selection) {
-                    this.set_selection(selection);
-                    if (callbak) {
-                        callbak();
-                    }
-                }.bind(this));
-        },
-        set_selection: function(selection) {
-            var select = this.select;
-            select.empty();
-            selection.forEach(function(e) {
-                select.append(jQuery('<option/>', {
-                    'value': JSON.stringify(e[0]),
-                    'text': e[1]
-                }));
-            });
-        },
-        display_update_selection: function(record, field) {
-            this.update_selection(record, field, function() {
-                if (!field) {
-                    this.select.val('');
-                    return;
-                }
-                var value = field.get(record);
-                var prm, found = false;
-                for (var i = 0, len = this.selection.length; i < len; i++) {
-                    if (this.selection[i][0] === value) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found) {
-                    prm = Sao.common.selection_mixin.get_inactive_selection
-                        .call(this, value);
-                    prm.done(function(inactive) {
-                        this.select.append(jQuery('<option/>', {
-                            value: JSON.stringify(inactive[0]),
-                            text: inactive[1],
-                            disabled: true
-                        }));
-                    }.bind(this));
-                } else {
-                    prm = jQuery.when();
-                }
-                prm.done(function() {
-                    this.select.val(JSON.stringify(value));
-                }.bind(this));
-            }.bind(this));
-        },
-        display: function(record, field) {
-            Sao.View.Form.Selection._super.display.call(this, record, field);
-            this.display_update_selection(record, field);
-        },
-        focus: function() {
-            this.select.focus();
-        },
-        value_get: function() {
-            return JSON.parse(this.select.val());
-        },
-        set_value: function(record, field) {
-            var value = this.value_get();
-            field.set_client(record, value);
-        },
-        set_readonly: function(readonly) {
-            this.select.prop('disabled', readonly);
-        }
-    });
-
-    Sao.View.Form.Boolean = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-boolean',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Boolean._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.input = this.labelled = jQuery('<input/>', {
-                'type': 'checkbox',
-                'class': 'form-control input-sm'
-            }).appendTo(this.el);
-            this.input.change(this.focus_out.bind(this));
-        },
-        display: function(record, field) {
-            Sao.View.Form.Boolean._super.display.call(this, record, field);
-            if (record) {
-                this.input.prop('checked', record.field_get(this.field_name));
-            } else {
-                this.input.prop('checked', false);
-            }
-        },
-        focus: function() {
-            this.input.focus();
-        },
-        set_value: function(record, field) {
-            var value = this.input.prop('checked');
-            field.set_client(record, value);
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('readonly', readonly);
-        }
-    });
-
-    Sao.View.Form.Text = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-text',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Text._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            this.input = this.labelled = jQuery('<textarea/>', {
-                'class': 'form-control input-sm'
-            }).appendTo(this.el);
-            this.input.change(this.focus_out.bind(this));
-        },
-        display: function(record, field) {
-            Sao.View.Form.Text._super.display.call(this, record, field);
-            if (record) {
-                var value = record.field_get_client(this.field_name);
-                this.input.val(value);
-            } else {
-                this.input.val('');
-            }
-        },
-        focus: function() {
-            this.input.focus();
-        },
-        set_value: function(record, field) {
-            var value = this.input.val() || '';
-            field.set_client(record, value);
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('readonly', readonly);
-        }
-    });
-
-    Sao.View.Form.RichText = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-richtext',
-        init: function(field_name, model, attributes) {
-            var i, properties, button;
-            Sao.View.Form.RichText._super.init.call(
-                    this, field_name, model, attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_ + ' panel panel-default'
-            });
-            this.toolbar = jQuery('<div/>', {
-                'class': 'btn-toolbar',
-                'role': 'toolbar'
-            }).appendTo(jQuery('<div/>', {
-                'class': 'panel-heading'
-            }).appendTo(this.el));
-
-            var button_apply_command = function(evt) {
-                document.execCommand(evt.data);
-            };
-
-            var add_buttons = function(buttons) {
-                var group = jQuery('<div/>', {
-                    'class': 'btn-group',
-                    'role': 'group'
-                }).appendTo(this.toolbar);
-                for (i in buttons) {
-                    properties = buttons[i];
-                    button = jQuery('<button/>', {
-                        'class': 'btn btn-default',
-                        'type': 'button'
-                    }).append(jQuery('<span/>', {
-                        'class': 'glyphicon glyphicon-' + properties.icon
-                    })).appendTo(group);
-                    button.click(properties.command, button_apply_command);
-                }
-            }.bind(this);
-
-            add_buttons([
-                    {
-                        'icon': 'bold',
-                        'command': 'bold'
-                    }, {
-                        'icon': 'italic',
-                        'command': 'italic'
-                    }, {
-                        'icon': 'text-color',  // XXX
-                        'command': 'underline'
-                    }]);
-
-            var selections = [
-            {
-                'heading': Sao.i18n.gettext('Font'),
-                'options': ['Normal', 'Serif', 'Sans', 'Monospace'],  // XXX
-                'command': 'fontname'
-            }, {
-                'heading': Sao.i18n.gettext('Size'),
-                'options': [1, 2, 3, 4, 5, 6, 7],
-                'command': 'fontsize'
-            }];
-            var add_option = function(dropdown, properties) {
-                return function(option) {
-                    dropdown.append(jQuery('<li/>').append(jQuery('<a/>', {
-                        'href': '#'
-                    }).append(option).click(function() {
-                        document.execCommand(properties.command, false, option);
-                    })));
-                };
-            };
-            for (i in selections) {
-                properties = selections[i];
-                var group = jQuery('<div/>', {
-                    'class': 'btn-group',
-                    'role': 'group'
-                }).appendTo(this.toolbar);
-                button = jQuery('<button/>', {
-                    'class': 'btn btn-default dropdown-toggle',
-                    'data-toggle': 'dropdown',
-                    'aria-expanded': false,
-                    'aria-haspopup': true
-                }).append(properties.heading)
-                .append(jQuery('<span/>', {
-                    'class': 'caret'
-                })).appendTo(group);
-                var dropdown = jQuery('<ul/>', {
-                    'class': 'dropdown-menu'
-                }).appendTo(group);
-                properties.options.forEach(add_option(dropdown, properties));
-            }
-
-            add_buttons([
-                    {
-                        'icon': 'align-left',
-                        'command': 'justifyLeft'
-                    }, {
-                        'icon': 'align-center',
-                        'command': 'justifyCenter'
-                    }, {
-                        'icon': 'align-right',
-                        'command': 'justifyRight'
-                    }, {
-                        'icon': 'align-justify',
-                        'command': 'justifyFull'
-                    }]);
-
-            // TODO backColor
-            [['foreColor', '#000000']].forEach(
-                    function(e) {
-                        var command = e[0];
-                        var color = e[1];
-                        jQuery('<input/>', {
-                            'class': 'btn btn-default',
-                            'type': 'color'
-                        }).appendTo(this.toolbar)
-                        .change(function() {
-                            document.execCommand(command, false, jQuery(this).val());
-                        }).focusin(function() {
-                            document.execCommand(command, false, jQuery(this).val());
-                        }).val(color);
-            }.bind(this));
-
-            this.input = this.labelled = jQuery('<div/>', {
-                'class': 'richtext',
-                'contenteditable': true
-            }).appendTo(jQuery('<div/>', {
-                'class': 'panel-body'
-            }).appendTo(this.el));
-            this.el.focusout(this.focus_out.bind(this));
-        },
-        focus_out: function() {
-            // Let browser set the next focus before testing
-            // if it moved out of the widget
-            window.setTimeout(function() {
-                if (this.el.find(':focus').length === 0) {
-                    Sao.View.Form.RichText._super.focus_out.call(this);
-                }
-            }.bind(this), 0);
-        },
-        display: function(record, field) {
-            Sao.View.Form.RichText._super.display.call(this, record, field);
-            var value = '';
-            if (record) {
-                value = record.field_get_client(this.field_name);
-            }
-            this.input.html(value);
-        },
-        focus: function() {
-            this.input.focus();
-        },
-        set_value: function(record, field) {
-            // TODO order attributes
-            this.input.find('div').each(function(i, el) {
-                el = jQuery(el);
-                // Not all browsers respect the styleWithCSS
-                if (el.css('text-align')) {
-                    // Remove browser specific prefix
-                    var align = el.css('text-align').split('-').pop();
-                    el.attr('align', align);
-                    el.css('text-align', '');
-                }
-                // Some browsers set start as default align
-                if (el.attr('align') == 'start') {
-                    el.attr('align', 'left');
-                }
-            });
-            var value = this.input.html() || '';
-            field.set_client(record, value);
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('contenteditable', !readonly);
-            this.toolbar.find('button,select').prop('disabled', readonly);
-        }
-    });
-
-    Sao.View.Form.Many2One = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-many2one',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Many2One._super.init.call(this, field_name, model,
-                attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(this.el);
-            this.entry = this.labelled = jQuery('<input/>', {
-                'type': 'input',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-            // Use keydown to not receive focus-in TAB
-            this.entry.on('keydown', this.key_press.bind(this));
-
-            if (!attributes.completion || attributes.completion == "1") {
-                Sao.common.get_completion(group,
-                    this._update_completion.bind(this),
-                    this._completion_match_selected.bind(this),
-                    this._completion_action_activated.bind(this));
-                this.wid_completion = true;
-            }
-
-            // Append buttons after the completion to not break layout
-            var buttons = jQuery('<span/>', {
-                'class': 'input-group-btn'
-            }).appendTo(group);
-            this.but_open = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-search'
-            })).appendTo(buttons);
-            this.but_open.click(this.edit.bind(this));
-
-            this.el.change(this.focus_out.bind(this));
-            this._readonly = false;
-        },
-        get_screen: function() {
-            var domain = this.field().get_domain(this.record());
-            var context = this.field().get_context(this.record());
-            return new Sao.Screen(this.get_model(), {
-                'context': context,
-                'domain': domain,
-                'mode': ['form'],
-                'view_ids': (this.attributes.view_ids || '').split(','),
-                'views_preload': this.attributes.views,
-                'readonly': this._readonly
-            });
-        },
-        set_text: function(value) {
-            if (jQuery.isEmptyObject(value)) {
-                value = '';
-            }
-            this.entry.val(value);
-        },
-        get_text: function() {
-            var record = this.record();
-            if (record) {
-                return record.field_get_client(this.field_name);
-            }
-            return '';
-        },
-        focus_out: function() {
-            if (!this.attributes.completion ||
-                    this.attributes.completion == "1") {
-                if (this.el.find('.dropdown').hasClass('open')) {
-                    return;
-                }
-            }
-            Sao.View.Form.Many2One._super.focus_out.call(this);
-        },
-        set_value: function(record, field) {
-            if (field.get_client(record) != this.entry.val()) {
-                field.set_client(record, this.value_from_id(null, ''));
-                this.entry.val('');
-            }
-        },
-        display: function(record, field) {
-            var screen_record = this.record();
-            if ((screen_record && record) && (screen_record.id != record.id)) {
-                return;
-            }
-
-            var text_value, value;
-            Sao.View.Form.Many2One._super.display.call(this, record, field);
-
-            this._set_button_sensitive();
-            this._set_completion();
-
-            if (!record) {
-                this.entry.val('');
-                return;
-            }
-            this.set_text(field.get_client(record));
-            value = field.get(record);
-            if (this.has_target(value)) {
-                this.but_open.button({
-                    'icons': {
-                        'primary': 'glyphicon-folder-open'
-                    }});
-            } else {
-                this.but_open.button({
-                    'icons': {
-                        'primary': 'glyphicon-search'
-                    }});
-            }
-        },
-        focus: function() {
-            this.entry.focus();
-        },
-        set_readonly: function(readonly) {
-            this._readonly = readonly;
-            this._set_button_sensitive();
-        },
-        _set_button_sensitive: function() {
-            this.entry.prop('readonly', this._readonly);
-            this.but_open.prop('disabled',
-                    this._readonly || !this.read_access());
-        },
-        get_access: function(type) {
-            var model = this.get_model();
-            if (model) {
-                return Sao.common.MODELACCESS.get(model)[type];
-            }
-            return true;
-        },
-        read_access: function() {
-            return this.get_access('read');
-        },
-        create_access: function() {
-            return this.attributes.create && this.get_access('create');
-        },
-        id_from_value: function(value) {
-            return value;
-        },
-        value_from_id: function(id, str) {
-            if (str === undefined) {
-                str = '';
-            }
-            return [id, str];
-        },
-        get_model: function() {
-            return this.attributes.relation;
-        },
-        has_target: function(value) {
-            return value !== undefined && value !== null;
-        },
-        edit: function(evt) {
-            var model = this.get_model();
-            if (!model || !Sao.common.MODELACCESS.get(model).read) {
-                return;
-            }
-            var win, callback;
-            var record = this.record();
-            var value = record.field_get(this.field_name);
-            if (model && this.has_target(value)) {
-                var screen = this.get_screen();
-                var m2o_id =
-                    this.id_from_value(record.field_get(this.field_name));
-                screen.new_group([m2o_id]);
-                callback = function(result) {
-                    if (result) {
-                        var rec_name_prm = screen.current_record.rec_name();
-                        rec_name_prm.done(function(name) {
-                            var value = this.value_from_id(
-                                screen.current_record.id, name);
-                            this.record().field_set_client(this.field_name,
-                                value, true);
-                        }.bind(this));
-                    }
-                };
-                win = new Sao.Window.Form(screen, callback.bind(this), {
-                    save_current: true
-                });
-            } else if (model) {
-                var dom;
-                var domain = this.field().get_domain(record);
-                var context = this.field().get_context(record);
-                var text = this.entry.val();
-                callback = function(result) {
-                    if (!jQuery.isEmptyObject(result)) {
-                        var value = this.value_from_id(result[0][0],
-                                result[0][1]);
-                        this.record().field_set_client(this.field_name,
-                                value, true);
-                    }
-                };
-                var parser = new Sao.common.DomainParser();
-                win = new Sao.Window.Search(model,
-                        callback.bind(this), {
-                            sel_multi: false,
-                            context: context,
-                            domain: domain,
-                            view_ids: (this.attributes.view_ids ||
-                                '').split(','),
-                            views_preload: (this.attributes.views || {}),
-                            new_: this.create_access(),
-                            search_filter: parser.quote(text)
-                        });
-            }
-        },
-        new_: function(evt) {
-            var model = this.get_model();
-            if (!model || ! Sao.common.MODELACCESS.get(model).create) {
-                return;
-            }
-            var screen = this.get_screen();
-            var callback = function(result) {
-                if (result) {
-                    var rec_name_prm = screen.current_record.rec_name();
-                    rec_name_prm.done(function(name) {
-                        var value = this.value_from_id(
-                            screen.current_record.id, name);
-                        this.record().field_set_client(this.field_name, value);
-                    }.bind(this));
-                }
-            };
-            var win = new Sao.Window.Form(screen, callback.bind(this), {
-                new_: true,
-                save_current: true
-            });
-        },
-        key_press: function(event_) {
-            var editable = !this.entry.prop('readonly');
-            var activate_keys = [Sao.common.TAB_KEYCODE];
-            var delete_keys = [Sao.common.BACKSPACE_KEYCODE,
-                Sao.common.DELETE_KEYCODE];
-            if (!this.wid_completion) {
-                activate_keys.push(Sao.common.RETURN_KEYCODE);
-            }
-
-            if (event_.which == Sao.common.F3_KEYCODE &&
-                    editable &&
-                    this.create_access()) {
-                this.new_();
-                event_.preventDefault();
-            } else if (event_.which == Sao.common.F2_KEYCODE &&
-                    this.read_access()) {
-                this.edit();
-                event_.preventDefault();
-            } else if (~activate_keys.indexOf(event_.which) && editable) {
-                if (!this.attributes.completion ||
-                        this.attributes.completion == "1") {
-                    if (this.el.find('.dropdown').hasClass('open')) {
-                        return;
-                    }
-                }
-                this.activate();
-            } else if (this.has_target(this.record().field_get(
-                            this.field_name)) && editable) {
-                var value = this.get_text();
-                if ((value != this.entry.val()) ||
-                        ~delete_keys.indexOf(event_.which)) {
-                    this.entry.val('');
-                    this.record().field_set_client(this.field_name,
-                        this.value_from_id(null, ''));
-                }
-            }
-        },
-        activate: function() {
-            var model = this.get_model();
-            if (!model || !Sao.common.MODELACCESS.get(model).read) {
-                return;
-            }
-            var record = this.record();
-            var value = record.field_get(this.field_name);
-            var sao_model = new Sao.Model(model);
-
-            if (model && !this.has_target(value)) {
-                var text = this.entry.val();
-                if (!this._readonly && (text ||
-                            this.field().get_state_attrs(this.record())
-                            .required)) {
-                    var dom;
-                    var domain = this.field().get_domain(record);
-                    var context = this.field().get_context(record);
-
-                    var callback = function(result) {
-                        if (!jQuery.isEmptyObject(result)) {
-                            var value = this.value_from_id(result[0][0],
-                                result[0][1]);
-                            this.record().field_set_client(this.field_name,
-                                value, true);
-                        } else {
-                            this.entry.val('');
-                        }
-                    };
-                    var parser = new Sao.common.DomainParser();
-                    var win = new Sao.Window.Search(model,
-                            callback.bind(this), {
-                                sel_multi: false,
-                                context: context,
-                                domain: domain,
-                                view_ids: (this.attributes.view_ids ||
-                                    '').split(','),
-                                views_preload: (this.attributes.views ||
-                                    {}),
-                                new_: this.create_access(),
-                                search_filter: parser.quote(text)
-                            });
-                }
-            }
-        },
-        _set_completion: function() {
-            var search = this.el.find('.action-search');
-            if (this.read_access()) {
-                search.removeClass('disabled');
-            } else {
-                search.addClass('disabled');
-            }
-            var create = this.el.find('.action-create');
-            if (this.create_access()) {
-                create.removeClass('disabled');
-            } else {
-                create.addClass('disabled');
-            }
-        },
-        _update_completion: function(text) {
-            var record = this.record();
-            if (!record) {
-                return;
-            }
-            var field = this.field();
-            var value = field.get(record);
-            if (this.has_target(value)) {
-                var id = this.id_from_value(value);
-                if ((id !== undefined) && (id > 0)) {
-                    return jQuery.when();
-                }
-            }
-            var model = this.get_model();
-
-            return Sao.common.update_completion(
-                    this.entry, record, field, model);
-        },
-        _completion_match_selected: function(value) {
-            this.record().field_set_client(this.field_name,
-                    this.value_from_id(
-                        value.id, value.rec_name), true);
-        },
-        _completion_action_activated: function(action) {
-            if (action == 'search') {
-                this.edit();
-            } else if (action == 'create') {
-                this.new_();
-            }
-        }
-    });
-
-    Sao.View.Form.One2One = Sao.class_(Sao.View.Form.Many2One, {
-        class_: 'form-one2one'
-    });
-
-    Sao.View.Form.Reference = Sao.class_(Sao.View.Form.Many2One, {
-        class_: 'form-reference',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Reference._super.init.call(this, field_name, model,
-                attributes);
-            this.el.addClass('form-inline');
-            this.select = jQuery('<select/>', {
-                'class': 'form-control input-sm',
-                'aria-label': attributes.string
-            });
-            this.el.prepend(jQuery('<span/>').text('-'));
-            this.el.prepend(this.select);
-            this.select.change(this.select_changed.bind(this));
-            Sao.common.selection_mixin.init.call(this);
-            this.init_selection();
-        },
-        init_selection: function(key) {
-            Sao.common.selection_mixin.init_selection.call(this, key,
-                this.set_selection.bind(this));
-        },
-        update_selection: function(record, field, callback) {
-            Sao.common.selection_mixin.update_selection.call(this, record,
-                field, function(selection) {
-                    this.set_selection(selection);
-                    if (callback) {
-                        callback();
-                    }
-                }.bind(this));
-        },
-        set_selection: function(selection) {
-            var select = this.select;
-            select.empty();
-            selection.forEach(function(e) {
-                select.append(jQuery('<option/>', {
-                    'value': e[0],
-                    'text': e[1]
-                }));
-            });
-        },
-        id_from_value: function(value) {
-            return parseInt(value.split(',')[1], 10);
-        },
-        value_from_id: function(id, str) {
-            if (!str) {
-                str = '';
-            }
-            return [this.get_model(), [id, str]];
-        },
-        get_text: function() {
-            var record = this.record();
-            if (record) {
-                return record.field_get_client(this.field_name)[1];
-            }
-            return '';
-        },
-        get_model: function() {
-            return this.select.val();
-        },
-        has_target: function(value) {
-            if (value === null) {
-                return false;
-            }
-            var model = value.split(',')[0];
-            value = value.split(',')[1];
-            if (jQuery.isEmptyObject(value)) {
-                value = null;
-            } else {
-                value = parseInt(value, 10);
-                if (isNaN(value)) {
-                    value = null;
-                }
-            }
-            return (model == this.get_model()) && (value >= 0);
-        },
-        _set_button_sensitive: function() {
-            Sao.View.Form.Reference._super._set_button_sensitive.call(this);
-            this.select.prop('disabled', this.entry.prop('readonly'));
-        },
-        select_changed: function() {
-            this.entry.val('');
-            var model = this.get_model();
-            var value;
-            if (model) {
-                value = [model, [-1, '']];
-            } else {
-                value = ['', ''];
-            }
-            this.record().field_set_client(this.field_name, value);
-        },
-        set_value: function(record, field) {
-            var value;
-            if (!this.get_model()) {
-                value = this.entry.val();
-                if (jQuery.isEmptyObject(value)) {
-                    field.set_client(record, null);
-                } else {
-                    field.set_client(record, ['', value]);
-                }
-            } else {
-                value = field.get_client(record, this.field_name);
-                var model, name;
-                if (value instanceof Array) {
-                    model = value[0];
-                    name = value[1];
-                } else {
-                    model = '';
-                    name = '';
-                }
-                if ((model != this.get_model()) ||
-                        (name != this.entry.val())) {
-                    field.set_client(record, null);
-                    this.entry.val('');
-                }
-            }
-        },
-        set_text: function(value) {
-            var model;
-            if (value) {
-                model = value[0];
-                value = value[1];
-            } else {
-                model = null;
-                value = null;
-            }
-            Sao.View.Form.Reference._super.set_text.call(this, value);
-            if (model) {
-                this.select.val(model);
-            } else {
-                this.select.val('');
-            }
-        },
-        display: function(record, field) {
-            this.update_selection(record, field, function() {
-                Sao.View.Form.Reference._super.display.call(this, record, field);
-            }.bind(this));
-        },
-        set_readonly: function(readonly) {
-            Sao.View.Form.Reference._super.set_readonly.call(this, readonly);
-            this.select.prop('disabled', readonly);
-        }
-    });
-
-    Sao.View.Form.One2Many = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-one2many',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.One2Many._super.init.call(this, field_name, model,
-                attributes);
-
-            this._readonly = true;
-
-            this.el = jQuery('<div/>', {
-                'class': this.class_ + ' panel panel-default'
-            });
-            this.menu = jQuery('<div/>', {
-                'class': this.class_ + '-menu panel-heading'
-            });
-            this.el.append(this.menu);
-
-            var label = jQuery('<label/>', {
-                'class': this.class_ + '-string',
-                text: attributes.string
-            });
-            this.menu.append(label);
-
-            label.uniqueId();
-            this.el.uniqueId();
-            this.el.attr('aria-labelledby', label.attr('id'));
-            label.attr('for', this.el.attr('id'));
-
-            var toolbar = jQuery('<div/>', {
-                'class': this.class_ + '-toolbar'
-            });
-            this.menu.append(toolbar);
-
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(toolbar);
-
-            this.wid_text = jQuery('<input/>', {
-                type: 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-            this.wid_text.hide();
-
-            var buttons = jQuery('<div/>', {
-                'class': 'input-group-btn'
-            }).appendTo(group);
-
-            if (attributes.add_remove) {
-                this.wid_text.show();
-                // TODO add completion
-                //
-                this.but_add = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'aria-label': Sao.i18n.gettext('Add')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-plus'
-                })).appendTo(buttons);
-                this.but_add.click(this.add.bind(this));
-
-                this.but_remove = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'aria-label': Sao.i18n.gettext('Remove')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-minus'
-                })).appendTo(buttons);
-                this.but_remove.click(this.remove.bind(this));
-            }
-
-            this.but_new = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('New')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-edit'
-            })).appendTo(buttons);
-            this.but_new.click(this.new_.bind(this));
-
-            this.but_open = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Open')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-folder-open'
-            })).appendTo(buttons);
-            this.but_open.click(this.open.bind(this));
-
-            this.but_del = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Delete')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-trash'
-            })).appendTo(buttons);
-            this.but_del.click(this.delete_.bind(this));
-
-            this.but_undel = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Undelete')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-repeat'
-            })).appendTo(buttons);
-            this.but_undel.click(this.undelete.bind(this));
-
-            this.but_previous = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Previous')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-chevron-left'
-            })).appendTo(buttons);
-            this.but_previous.click(this.previous.bind(this));
-
-            this.label = jQuery('<span/>', {
-                'class': 'btn'
-            }).appendTo(buttons);
-            this.label.text('(0, 0)');
-
-            this.but_next = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Next')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-chevron-right'
-            })).appendTo(buttons);
-            this.but_next.click(this.next.bind(this));
-
-            this.but_switch = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Switch')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-list-alt'
-            })).appendTo(buttons);
-            this.but_switch.click(this.switch_.bind(this));
-
-            this.content = jQuery('<div/>', {
-                'class': this.class_ + '-content panel-body'
-            });
-            this.el.append(this.content);
-
-            var modes = (attributes.mode || 'tree,form').split(',');
-            this.screen = new Sao.Screen(attributes.relation, {
-                mode: modes,
-                view_ids: (attributes.view_ids || '').split(','),
-                views_preload: attributes.views || {},
-                row_activate: this.activate.bind(this),
-                readonly: attributes.readonly || false,
-                exclude_field: attributes.relation_field || null,
-                pre_validate: attributes.pre_validate
-            });
-            this.screen.pre_validate = attributes.pre_validate == 1;
-            this.prm = this.screen.switch_view(modes[0]).done(function() {
-                this.content.append(this.screen.screen_container.el);
-            }.bind(this));
-
-            // TODO key_press
-
-            this.but_switch.prop('disabled', this.screen.number_of_views() <= 0);
-        },
-        set_readonly: function(readonly) {
-            this._readonly = readonly;
-            this._set_button_sensitive();
-        },
-        _set_button_sensitive: function() {
-            var access = Sao.common.MODELACCESS.get(this.screen.model_name);
-            var size_limit, o2m_size;
-            var record = this.record();
-            var field = this.field();
-            if (record && field) {
-                var field_size = record.expr_eval(this.attributes.size);
-                o2m_size = field.get_eval(record);
-                size_limit = (((field_size !== undefined) &&
-                            (field_size !== null)) &&
-                        (o2m_size >= field_size >= 0));
-            } else {
-                o2m_size = null;
-                size_limit = false;
-            }
-            var create = this.attributes.create;
-            if (create === undefined) {
-                create = true;
-            }
-            this.but_new.prop('disabled', this._readonly || !create ||
-                    size_limit || !access.create);
-
-            var delete_ = this.attributes['delete'];
-            if (delete_ === undefined) {
-                delete_ = true;
-            }
-            // TODO position
-            this.but_del.prop('disabled', this._readonly || !delete_ ||
-                    !access['delete']);
-            this.but_undel.prop('disabled', this._readonly || size_limit);
-            this.but_open.prop('disabled', !access.read);
-            // TODO but_next, but_previous
-            if (this.attributes.add_remove) {
-                this.wid_text.prop('disabled', this._readonly);
-                this.but_add.prop('disabled', this._readonly || size_limit ||
-                        !access.write || !access.read);
-                this.but_remove.prop('disabled', this._readonly ||
-                        !access.write || !access.read);
-            }
-        },
-        display: function(record, field) {
-            Sao.View.Form.One2Many._super.display.call(this, record, field);
-
-            this._set_button_sensitive();
-
-            this.prm.done(function() {
-                if (!record) {
-                    return;
-                }
-                if (field === undefined) {
-                    this.screen.new_group();
-                    this.screen.set_current_record(null);
-                    this.screen.group.parent = null;
-                    this.screen.display();
-                    return;
-                }
-
-                var new_group = record.field_get_client(this.field_name);
-                if (new_group != this.screen.group) {
-                    this.screen.set_group(new_group);
-                    if ((this.screen.current_view.view_type == 'tree') &&
-                            this.screen.current_view.editable) {
-                        this.screen.set_current_record(null);
-                    }
-                }
-                var readonly = false;
-                var domain = [];
-                var size_limit = null;
-                if (record) {
-                    readonly = field.get_state_attrs(record).readonly;
-                    domain = field.get_domain(record);
-                    size_limit = record.expr_eval(this.attributes.size);
-                }
-                if (!Sao.common.compare(this.screen.domain, domain)) {
-                    this.screen.domain = domain;
-                }
-                this.screen.group.set_readonly(readonly);
-                this.screen.size_limit = size_limit;
-                this.screen.display();
-            }.bind(this));
-        },
-        focus: function() {
-            if (this.wid_text.is(':visible')) {
-                this.wid_text.focus();
-            }
-        },
-        activate: function(event_) {
-            this.edit();
-        },
-        add: function(event_) {
-            var access = Sao.common.MODELACCESS.get(this.screen.model_name);
-            if (!access.write || !access.read) {
-                return;
-            }
-            this.view.set_value();
-            var domain = this.field().get_domain(this.record());
-            var context = this.field().get_context(this.record());
-            domain = [domain,
-                this.record().expr_eval(this.attributes.add_remove)];
-            var removed_ids = this.field().get_removed_ids(this.record());
-            domain = ['OR', domain, ['id', 'in', removed_ids]];
-            var text = this.wid_text.val();
-
-            // TODO sequence
-
-            var callback = function(result) {
-                var prm = jQuery.when();
-                if (!jQuery.isEmptyObject(result)) {
-                    var ids = [];
-                    var i, len;
-                    for (i = 0, len = result.length; i < len; i++) {
-                        ids.push(result[i][0]);
-                    }
-                    this.screen.group.load(ids, true);
-                    prm = this.screen.display();
-                }
-                prm.done(function() {
-                    this.screen.set_cursor();
-                }.bind(this));
-                this.wid_text.val('');
-            }.bind(this);
-            var parser = new Sao.common.DomainParser();
-            var win = new Sao.Window.Search(this.attributes.relation,
-                    callback, {
-                        sel_multi: true,
-                        context: context,
-                        domain: domain,
-                        view_ids: (this.attributes.view_ids ||
-                                '').split(','),
-                        views_preload: this.attributes.views || {},
-                        new_: !this.but_new.prop('disabled'),
-                        search_filter: parser.quote(text)
-                    });
-        },
-        remove: function(event_) {
-            var access = Sao.common.MODELACCESS.get(this.screen.model_name);
-            if (!access.write || !access.read) {
-                return;
-            }
-            this.screen.remove(false, true, false);
-        },
-        new_: function(event_) {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
-                return;
-            }
-            this.validate().done(function() {
-                if (this.attributes.product) {
-                    this.new_product();
-                } else {
-                    this.new_single();
-                }
-            }.bind(this));
-        },
-        new_single: function() {
-            var context = jQuery.extend({},
-                    this.field().get_context(this.record()));
-            // TODO sequence
-            if (this.screen.current_view.type == 'form' ||
-                    this.screen.current_view.editable) {
-                this.screen.new_();
-                this.screen.current_view.el.prop('disabled', false);
-            } else {
-                var record = this.record();
-                var field_size = record.expr_eval(
-                    this.attributes.size) || -1;
-                field_size -= this.field().get_eval(record);
-                var win = new Sao.Window.Form(this.screen, function() {}, {
-                    new_: true,
-                    many: field_size,
-                    context: context
-                });
-            }
-        },
-        new_product: function() {
-            var fields = this.attributes.product.split(',');
-            var product = {};
-            var screen = this.screen;
-
-            screen.new_(false).then(function(first) {
-                first.default_get().then(function(default_) {
-                    first.set_default(default_);
-
-                    var search_set = function() {
-                        if (jQuery.isEmptyObject(fields)) {
-                            return make_product();
-                        }
-                        var field = screen.model.fields[fields.pop()];
-                        var relation = field.description.relation;
-                        if (!relation) {
-                            search_set();
-                        }
-
-                        var domain = field.get_domain(first);
-                        var context = field.get_context(first);
-
-                        var callback = function(result) {
-                            if (!jQuery.isEmptyObject(result)) {
-                                product[field.name] = result;
-                            }
-                            search_set();
-                        };
-                        var win_search = new Sao.Window.Search(relation,
-                                callback, {
-                                    sel_multi: true,
-                                    context: context,
-                                    domain: domain,
-                                    search_filter: ''
-                        });
-                    };
-
-                    var make_product = function() {
-                        if (jQuery.isEmptyObject(product)) {
-                            screen.group.remove(first, true);
-                            return;
-                        }
-
-                        var fields = Object.keys(product);
-                        var values = fields.map(function(field) {
-                            return product[field];
-                        });
-                        Sao.common.product(values).forEach(function(values) {
-                            var set_default = function(record) {
-                                var default_value = jQuery.extend({}, default_);
-                                fields.forEach(function(field, i) {
-                                    default_value[field] = values[i][0];
-                                    default_value[field + '.rec_name'] = values[i][1];
-                                });
-                                record.set_default(default_value);
-                            };
-
-                            var record;
-                            if (first) {
-                                set_default(first);
-                                first = null;
-                            } else {
-                                screen.new_(false).then(set_default);
-                            }
-                        });
-                    };
-
-                    search_set();
-                });
-            });
-        },
-        open: function(event_) {
-            this.edit();
-        },
-        delete_: function(event_) {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name)['delete']) {
-                return;
-            }
-            this.screen.remove(false, false, false);
-        },
-        undelete: function(event_) {
-            this.screen.unremove();
-        },
-        previous: function(event_) {
-            this.validate().done(function() {
-                this.screen.display_previous();
-            }.bind(this));
-        },
-        next: function(event_) {
-            this.validate().done(function() {
-                this.screen.display_next();
-            }.bind(this));
-        },
-        switch_: function(event_) {
-            this.screen.switch_view();
-        },
-        edit: function() {
-            if (!Sao.common.MODELACCESS.get(this.screen.model_name).read) {
-                return;
-            }
-            this.validate().done(function() {
-                var record = this.screen.current_record;
-                if (record) {
-                    var win = new Sao.Window.Form(this.screen, function() {});
-                }
-            }.bind(this));
-        },
-        validate: function() {
-            var prm = jQuery.Deferred();
-            this.view.set_value();
-            var record = this.screen.current_record;
-            if (record) {
-                var fields = this.screen.current_view.get_fields();
-                record.validate(fields).then(function(validate) {
-                    if (!validate) {
-                        this.screen.display(true);
-                        prm.reject();
-                        return;
-                    }
-                    if (this.screen.pre_validate) {
-                        return record.pre_validate().then(function(validate) {
-                            if (!validate) {
-                                prm.reject();
-                                return;
-                            }
-                            prm.resolve();
-                        });
-                    }
-                    prm.resolve();
-                }.bind(this));
-            } else {
-                prm.resolve();
-            }
-            return prm;
-        },
-        set_value: function(record, field) {
-            this.screen.save_tree_state();
-        }
-    });
-
-    Sao.View.Form.Many2Many = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-many2many',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Many2Many._super.init.call(this, field_name, model,
-                attributes);
-
-            this._readonly = true;
-
-            this.el = jQuery('<div/>', {
-                'class': this.class_ + ' panel panel-default'
-            });
-            this.menu = jQuery('<div/>', {
-                'class': this.class_ + '-menu panel-heading'
-            });
-            this.el.append(this.menu);
-
-            var label = jQuery('<label/>', {
-                'class': this.class_ + '-string',
-                text: attributes.string
-            });
-            this.menu.append(label);
-
-            label.uniqueId();
-            this.el.uniqueId();
-            this.el.attr('aria-labelledby', label.attr('id'));
-            label.attr('for', this.el.attr('id'));
-
-            var toolbar = jQuery('<div/>', {
-                'class': this.class_ + '-toolbar'
-            });
-            this.menu.append(toolbar);
-
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(toolbar);
-            this.entry = jQuery('<input/>', {
-                type: 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-            // Use keydown to not receive focus-in TAB
-            this.entry.on('keydown', this.key_press.bind(this));
-
-            // TODO completion
-
-            var buttons = jQuery('<div/>', {
-                'class': 'input-group-btn'
-            }).appendTo(group);
-            this.but_add = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Add')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-plus'
-            })).appendTo(buttons);
-            this.but_add.click(this.add.bind(this));
-
-            this.but_remove = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'aria-label': Sao.i18n.gettext('Remove')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-minus'
-            })).appendTo(buttons);
-            this.but_remove.click(this.remove.bind(this));
-
-            this.content = jQuery('<div/>', {
-                'class': this.class_ + '-content panel-body'
-            });
-            this.el.append(this.content);
-
-            this.screen = new Sao.Screen(attributes.relation, {
-                mode: ['tree'],
-                view_ids: (attributes.view_ids || '').split(','),
-                views_preload: attributes.views || {},
-                row_activate: this.activate.bind(this)
-            });
-            this.prm = this.screen.switch_view('tree').done(function() {
-                this.content.append(this.screen.screen_container.el);
-            }.bind(this));
-        },
-        set_readonly: function(readonly) {
-            this._readonly = readonly;
-            this._set_button_sensitive();
-        },
-        _set_button_sensitive: function() {
-            var size_limit = false;
-            if (this.record() && this.field()) {
-                // TODO
-            }
-
-            this.entry.prop('disabled', this._readonly);
-            this.but_add.prop('disabled', this._readonly || size_limit);
-            // TODO position
-            this.but_remove.prop('disabled', this._readonly);
-        },
-        display: function(record, field) {
-            Sao.View.Form.Many2Many._super.display.call(this, record, field);
-
-            this.prm.done(function() {
-                if (!record) {
-                    return;
-                }
-                if (field === undefined) {
-                    this.screen.new_group();
-                    this.screen.set_current_record(null);
-                    this.screen.group.parent = null;
-                    this.screen.display();
-                    return;
-                }
-                var new_group = record.field_get_client(this.field_name);
-                if (new_group != this.screen.group) {
-                    this.screen.set_group(new_group);
-                }
-                this.screen.display();
-            }.bind(this));
-        },
-        focus: function() {
-            this.entry.focus();
-        },
-        activate: function() {
-            this.edit();
-        },
-        add: function() {
-            var dom;
-            var domain = this.field().get_domain(this.record());
-            var context = this.field().get_context(this.record());
-            var value = this.entry.val();
-
-            var callback = function(result) {
-                if (!jQuery.isEmptyObject(result)) {
-                    var ids = [];
-                    var i, len;
-                    for (i = 0, len = result.length; i < len; i++) {
-                        ids.push(result[i][0]);
-                    }
-                    this.screen.group.load(ids, true);
-                    this.screen.display();
-                }
-                this.entry.val('');
-            }.bind(this);
-            var parser = new Sao.common.DomainParser();
-            var win = new Sao.Window.Search(this.attributes.relation,
-                    callback, {
-                        sel_multi: true,
-                        context: context,
-                        domain: domain,
-                        view_ids: (this.attributes.view_ids ||
-                            '').split(','),
-                        views_preload: this.attributes.views || {},
-                        new_: this.attributes.create,
-                        search_filter: parser.quote(value)
-                    });
-        },
-        remove: function() {
-            this.screen.remove(false, true, false);
-        },
-        key_press: function(event_) {
-            var activate_keys = [Sao.common.TAB_KEYCODE];
-            if (!this.wid_completion) {
-                activate_keys.push(Sao.common.RETURN_KEYCODE);
-            }
-
-            if (event_.which == Sao.common.F3_KEYCODE) {
-                this.new_();
-                event_.preventDefault();
-            } else if (event_.which == Sao.common.F2_KEYCODE) {
-                this.add();
-                event_.preventDefault();
-            } else if (~activate_keys.indexOf(event_.which) && this.entry.val()) {
-                this.add();
-            }
-        },
-        edit: function() {
-            if (jQuery.isEmptyObject(this.screen.current_record)) {
-                return;
-            }
-            // Create a new screen that is not linked to the parent otherwise
-            // on the save of the record will trigger the save of the parent
-            var domain = this.field().get_domain(this.record());
-            var add_remove = this.record().expr_eval(
-                    this.attributes.add_remove);
-            if (!jQuery.isEmptyObject(add_remove)) {
-                domain = [domain, add_remove];
-            }
-            var context = this.field().get_context(this.record());
-            var screen = new Sao.Screen(this.attributes.relation, {
-                'domain': domain,
-                'view_ids': (this.attributes.view_ids || '').split(','),
-                'mode': ['form'],
-                'views_preload': this.attributes.views,
-                'readonly': this.attributes.readonly || false,
-                'context': context
-            });
-            screen.new_group([this.screen.current_record.id]);
-            var callback = function(result) {
-                if (result) {
-                    screen.current_record.save().done(function() {
-                        // Force a reload on next display
-                        this.screen.current_record.cancel();
-                    }.bind(this));
-                }
-            }.bind(this);
-            screen.switch_view().done(function() {
-                new Sao.Window.Form(screen, callback);
-            });
-        },
-        new_: function() {
-            var domain = this.field().get_domain(this.record());
-            var add_remove = this.record().expr_eval(
-                    this.attributes.add_remove);
-            if (!jQuery.isEmptyObject(add_remove)) {
-                domain = [domain, add_remove];
-            }
-            var context = this.field().get_context(this.record());
-
-            var screen = new Sao.Screen(this.attributes.relation, {
-                'domain': domain,
-                'view_ids': (this.attributes.view_ids || '').split(','),
-                'mode': ['form'],
-                'views_preload': this.attributes.views,
-                'context': context
-            });
-            var callback = function(result) {
-                if (result) {
-                    var record = screen.current_record;
-                    this.screen.group.load([record.id], true);
-                }
-                this.entry.val('');
-            }.bind(this);
-            screen.switch_view().done(function() {
-                new Sao.Window.Form(screen, callback, {
-                    'new_': true,
-                    'save_current': true
-                });
-            });
-        }
-    });
-
-    Sao.View.Form.BinaryMixin = Sao.class_(Sao.View.Form.Widget, {
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.BinaryMixin._super.init.call(
-                    this, field_name, model, attributes);
-            this.filename = attributes.filename || null;
-        },
-        toolbar: function(class_) {
-            var group = jQuery('<div/>', {
-                'class': class_,
-                'role': 'group'
-            });
-
-            this.but_select = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-search'
-            })).appendTo(group);
-            this.but_select.click(this.select.bind(this));
-
-            if (this.filename) {
-                this.but_open = jQuery('<button/>', {
-                    'class': 'btn btn-default',
-                    'type': 'button'
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-folder-open'
-                })).appendTo(group);
-                this.but_open.click(this.open.bind(this));
-            }
-
-            this.but_save_as = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-save'
-            })).appendTo(group);
-            this.but_save_as.click(this.save_as.bind(this));
-
-            this.but_clear = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-erase'
-            })).appendTo(group);
-            this.but_clear.click(this.clear.bind(this));
-
-            return group;
-        },
-        filename_field: function() {
-            var record = this.record();
-            if (record) {
-                return record.model.fields[this.filename];
-            }
-        },
-        select: function() {
-            var record = this.record();
-
-            var close = function() {
-                file_dialog.modal.on('hidden.bs.modal', function(event) {
-                    jQuery(this).remove();
-                });
-                file_dialog.modal.modal('hide');
-            };
-
-            var save_file = function() {
-                var reader = new FileReader();
-                reader.onload = function(evt) {
-                    var uint_array = new Uint8Array(reader.result);
-                    this.field().set_client(record, uint_array);
-                }.bind(this);
-                reader.onloadend = function(evt) {
-                    close();
-                };
-                var file = file_selector[0].files[0];
-                reader.readAsArrayBuffer(file);
-                if (this.filename) {
-                    this.filename_field().set_client(record, file.name);
-                }
-            }.bind(this);
-
-            var file_dialog = new Sao.Dialog(
-                    Sao.i18n.gettext('Select'), 'file-dialog');
-            file_dialog.footer.append(jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(close))
-            .append(jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'submit'
-            }).append(Sao.i18n.gettext('OK')).click(save_file));
-            file_dialog.content.submit(function(e) {
-                save_file();
-                e.preventDefault();
-            });
-
-            var file_selector = jQuery('<input/>', {
-                type: 'file'
-            }).appendTo(file_dialog.body);
-
-            file_dialog.modal.modal('show');
-        },
-        open: function() {
-            // TODO find a way to make the difference
-            // between downloading and opening
-            this.save_as();
-        },
-        save_as: function() {
-            var field = this.field();
-            var record = this.record();
-            field.get_data(record).done(function(data) {
-                var blob = new Blob([data[0].binary],
-                        {type: 'application/octet-binary'});
-                var blob_url = window.URL.createObjectURL(blob);
-                if (this.blob_url) {
-                    window.URL.revokeObjectURL(this.blob_url);
-                }
-                this.blob_url = blob_url;
-                window.open(blob_url);
-            }.bind(this));
-        },
-        clear: function() {
-            this.field().set_client(this.record(), null);
-        }
-    });
-
-    Sao.View.Form.Binary = Sao.class_(Sao.View.Form.BinaryMixin, {
-        class_: 'form-binary',
-        blob_url: '',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Binary._super.init.call(this, field_name, model,
-                attributes);
-
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-
-            if (this.filename && attributes.filename_visible) {
-                this.text = jQuery('<input/>', {
-                    type: 'input',
-                    'class': 'form-control input-sm'
-                }).appendTo(this.el);
-                this.text.change(this.focus_out.bind(this));
-                // Use keydown to not receive focus-in TAB
-                this.text.on('keydown', this.key_press.bind(this));
-            }
-
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(this.el);
-            this.size = jQuery('<input/>', {
-                type: 'input',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-
-            this.toolbar('input-group-btn').appendTo(group);
-        },
-        display: function(record, field) {
-            Sao.View.Form.Binary._super.display.call(this, record, field);
-            if (!field) {
-                this.size.val('');
-                if (this.filename) {
-                    this.but_open.button('disable');
-                }
-                if (this.text) {
-                    this.text.val('');
-                }
-                this.but_save_as.button('disable');
-                return;
-            }
-            var size = field.get_size(record);
-            var button_sensitive;
-            if (size) {
-                button_sensitive = 'enable';
-            } else {
-                button_sensitive = 'disable';
-            }
-
-            if (this.filename) {
-                if (this.text) {
-                    this.text.val(this.filename_field().get(record) || '');
-                }
-                this.but_open.button(button_sensitive);
-            }
-            this.size.val(Sao.common.humanize(size));
-            this.but_save_as.button(button_sensitive);
-        },
-        key_press: function(evt) {
-            var editable = !this.wid_text.prop('readonly');
-            if (evt.which == Sao.common.F3_KEYCODE && editable) {
-                this.new_();
-                evt.preventDefault();
-            } else if (evt.which == Sao.common.F2_KEYCODE) {
-                this.open();
-                evt.preventDefault();
-            }
-        },
-        set_value: function(record, field) {
-            if (this.text) {
-                this.filename_field().set_client(record,
-                        this.text.val() || '');
-            }
-        },
-        set_readonly: function(readonly) {
-            if (readonly) {
-                this.but_select.hide();
-                this.but_clear.hide();
-
-            } else {
-                this.but_select.show();
-                this.but_clear.show();
-            }
-            if (this.wid_text) {
-                this.wid_text.prop('readonly', readonly);
-            }
-        }
-    });
-
-    Sao.View.Form.MultiSelection = Sao.class_(Sao.View.Form.Selection, {
-        class_: 'form-multiselection',
-        init: function(field_name, model, attributes) {
-            this.nullable_widget = false;
-            Sao.View.Form.MultiSelection._super.init.call(this, field_name,
-                model, attributes);
-            this.select.prop('multiple', true);
-        },
-        display_update_selection: function(record, field) {
-            var i, len, element;
-            this.update_selection(record, field, function() {
-                if (!field) {
-                    return;
-                }
-                var value = [];
-                var group = record.field_get_client(this.field_name);
-                for (i = 0, len = group.length; i < len; i++) {
-                    element = group[i];
-                    if (!~group.record_removed.indexOf(element) &&
-                        !~group.record_deleted.indexOf(element)) {
-                            value.push(element.id);
-                    }
-                }
-                this.el.val(value);
-            }.bind(this));
-        },
-        set_value: function(record, field) {
-            var value = this.el.val();
-            if (value) {
-                value = value.map(function(e) { return parseInt(e, 10); });
-            } else {
-                value = [];
-            field.set_client(record, value);
-            }
-        }
-    });
-
-    Sao.View.Form.Image = Sao.class_(Sao.View.Form.BinaryMixin, {
-        class_: 'form-image',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Image._super.init.call(
-                    this, field_name, model, attributes);
-            this.height = parseInt(attributes.height || 100, 10);
-            this.width = parseInt(attributes.width || 300, 10);
-
-            this.el = jQuery('<div/>');
-            this.image = jQuery('<img/>', {
-                'class': 'center-block'
-            }).appendTo(this.el);
-            this.image.css('max-height', this.height);
-            this.image.css('max-width', this.width);
-            this.image.css('height', 'auto');
-            this.image.css('width', 'auto');
-
-            var group = this.toolbar('btn-group');
-            if (!attributes.readonly) {
-                jQuery('<div/>', {
-                    'class': 'text-center'
-                }).append(group).appendTo(this.el);
-            }
-            this.update_img();
-        },
-        set_readonly: function(readonly) {
-            [this.but_select, this.but_open, this.but_save_as, this.but_clear]
-                .forEach(function(button) {
-                    if (button) {
-                        button.prop('disable', readonly);
-                    }
-                });
-        },
-        clear: function() {
-            Sao.View.Form.Image._super.clear.call(this);
-            this.update_img();
-        },
-        update_img: function() {
-            var value;
-            var record = this.record();
-            if (record) {
-                value = record.field_get_client(this.field_name);
-            }
-            if (value) {
-                if (value > Sao.common.BIG_IMAGE_SIZE) {
-                    value = jQuery.when(null);
-                } else {
-                    value = record.model.fields[this.field_name]
-                        .get_data(record);
-                }
-            } else {
-                value = jQuery.when(null);
-            }
-            value.done(function(data) {
-                var url, blob;
-                if (!data) {
-                    url = null;
-                } else {
-                    blob = new Blob([data[0][this.field_name]]);
-                    url = window.URL.createObjectURL(blob);
-                }
-                this.image.attr('src', url);
-            }.bind(this));
-        },
-        display: function(record, field) {
-            Sao.View.Form.Image._super.display.call(this, record, field);
-            this.update_img();
-        }
-    });
-
-    Sao.View.Form.URL = Sao.class_(Sao.View.Form.Char, {
-        class_: 'form-url',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.URL._super.init.call(
-                    this, field_name, model, attributes);
-            this.button = jQuery('<a/>', {
-                'class': 'btn btn-default',
-                'target': '_new'
-            }).appendTo(jQuery('<span/>', {
-                'class': 'input-group-btn'
-            }).appendTo(this.group));
-            this.icon = jQuery('<img/>').appendTo(this.button);
-            this.set_icon();
-        },
-        display: function(record, field) {
-            Sao.View.Form.URL._super.display.call(this, record, field);
-            var url = '';
-            if (record) {
-                url = record.field_get_client(this.field_name);
-            }
-            this.set_url(url);
-            if (record & this.attributes.icon) {
-                var icon = this.attributes.icon;
-                var value;
-                if (icon in record.model.fields) {
-                    value = record.field_get_client(icon);
-                } else {
-                    value = icon;
-                }
-                this.set_icon(value);
-            }
-        },
-        set_icon: function(value) {
-            value = value || 'tryton-web-browser';
-            Sao.common.ICONFACTORY.register_icon(value).done(function(url) {
-                this.icon.attr('src', url);
-            }.bind(this));
-        },
-        set_url: function(value) {
-            this.button.attr('href', value);
-        },
-        set_readonly: function(readonly) {
-            Sao.View.Form.URL._super.set_readonly.call(this, readonly);
-            if (readonly) {
-                this.input.hide();
-                this.button.removeClass('btn-default');
-                this.button.addClass('btn-link');
-            } else {
-                this.input.show();
-                this.button.removeClass('btn-link');
-                this.button.addClass('btn-default');
-            }
-        }
-    });
-
-    Sao.View.Form.Email = Sao.class_(Sao.View.Form.URL, {
-        class_: 'form-email',
-        set_url: function(value) {
-            Sao.View.Form.Email._super.set_url.call(this, 'mailto:' + value);
-        }
-    });
-
-    Sao.View.Form.CallTo = Sao.class_(Sao.View.Form.URL, {
-        class_: 'form-callto',
-        set_url: function(value) {
-            Sao.View.Form.CallTo._super.set_url.call(this, 'callto:' + value);
-        }
-    });
-
-    Sao.View.Form.SIP = Sao.class_(Sao.View.Form.URL, {
-        class_: 'form-sip',
-        set_url: function(value) {
-            Sao.View.Form.SIP._super.set_url.call(this, 'sip:' + value);
-        }
-    });
-
-    Sao.View.Form.ProgressBar = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-char',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.ProgressBar._super.init.call(
-                    this, field_name, model, attributes);
-            this.el = jQuery('<div/>', {
-                'class': this.class_ + ' progress'
-            });
-            this.progressbar = jQuery('<div/>', {
-                'class': 'progress-bar',
-                'role': 'progressbar',
-                'aria-valuemin': 0,
-                'aria-valuemax': 100
-            }).appendTo(this.el);
-            this.progressbar.css('min-width: 2em');
-        },
-        display: function(record, field) {
-            Sao.View.Form.ProgressBar._super.display.call(
-                    this, record, field);
-            var value, text;
-            if (!field) {
-                value = 0;
-                text = '';
-            } else {
-                value = field.get(record);
-                text = field.get_client(record, 100);
-                if (text) {
-                    text = Sao.i18n.gettext('%1%', text);
-                }
-            }
-            this.progressbar.attr('aria-valuenow', value * 100);
-            this.progressbar.css('width', value * 100 + '%');
-            this.progressbar.text(text);
-        }
-    });
-
-    Sao.View.Form.Dict = Sao.class_(Sao.View.Form.Widget, {
-        class_: 'form-dict',
-        init: function(field_name, model, attributes) {
-            Sao.View.Form.Dict._super.init.call(
-                    this, field_name, model, attributes);
-
-            this.schema_model = new Sao.Model(attributes.schema_model);
-            this.keys = {};
-            this.fields = {};
-            this.rows = {};
-
-            this.el = jQuery('<div/>', {
-                'class': this.class_ + ' panel panel-default'
-            });
-            var heading = jQuery('<div/>', {
-                'class': this.class_ + '-heading panel-heading'
-            }).appendTo(this.el);
-            var label = jQuery('<label/>', {
-                'class': this.class_ + '-string',
-                'text': attributes.string
-            }).appendTo(heading);
-
-            label.uniqueId();
-            this.el.uniqueId();
-            this.el.attr('aria-labelledby', label.attr('id'));
-            label.attr('for', this.el.attr('id'));
-
-            var body = jQuery('<div/>', {
-                'class': this.class_ + '-body panel-body'
-            }).appendTo(this.el);
-            this.container = jQuery('<div/>', {
-                'class': this.class_ + '-container'
-            }).appendTo(body);
-
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(jQuery('<div>', {
-                'class': 'col-md-12'
-            }).appendTo(jQuery('<div/>', {
-                'class': 'row'
-            }).appendTo(jQuery('<div/>', {
-                'class': 'container-fluid'
-            }).appendTo(body))));
-            this.wid_text = jQuery('<input/>', {
-                'type': 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-
-            // TODO completion
-
-            this.but_add = jQuery('<button/>', {
-                'class': 'btn btn-default btn-sm',
-                'type': 'button',
-                'aria-label': Sao.i18n.gettext('Add')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-plus'
-            })).appendTo(jQuery('<div/>', {
-                'class': 'input-group-btn'
-            }).appendTo(group));
-            this.but_add.click(this.add.bind(this));
-
-            this._readonly = false;
-            this._record_id = null;
-        },
-        add: function() {
-            var context = this.field().get_context(this.record());
-            var value = this.wid_text.val();
-            var domain = this.field().get_domain(this.record());
-
-            var callback = function(result) {
-                if (!jQuery.isEmptyObject(result)) {
-                    var ids = result.map(function(e) {
-                        return e[0];
-                    });
-                    this.add_new_keys(ids);
-                }
-                this.wid_text.val('');
-            }.bind(this);
-
-            var parser = new Sao.common.DomainParser();
-            var win = new Sao.Window.Search(this.schema_model.name,
-                    callback, {
-                        sel_multi: true,
-                        context: context,
-                        domain: domain,
-                        new_: false,
-                        search_filter: parser.quote(value)
-                    });
-        },
-        add_new_keys: function(ids) {
-            var context = this.field().get_context(this.record());
-            this.schema_model.execute('get_keys', [ids], context)
-                .then(function(new_fields) {
-                    var focus = false;
-                    new_fields.forEach(function(new_field) {
-                        if (this.fields[new_field.name]) {
-                            return;
-                        }
-                        this.keys[new_field.name] = new_field;
-                        this.add_line(new_field.name);
-                        if (!focus) {
-                            this.fields[new_field.name].input.focus();
-                            focus = true;
-                        }
-                    }.bind(this));
-                }.bind(this));
-        },
-        remove: function(key, modified) {
-            if (modified === undefined) {
-                modified = true;
-            }
-            delete this.fields[key];
-            this.rows[key].remove();
-            delete this.rows[key];
-            if (modified) {
-                this.set_value(this.record(), this.field());
-            }
-        },
-        set_value: function(record, field) {
-            field.set_client(record, this.get_value());
-        },
-        get_value: function() {
-            var value = {};
-            for (var key in this.fields) {
-                var widget = this.fields[key];
-                value[key] = widget.get_value();
-            }
-            return value;
-        },
-        set_readonly: function(readonly) {
-            this._readonly = readonly;
-            this._set_button_sensitive();
-            for (var key in this.fields) {
-                var widget = this.fields[key];
-                widget.set_readonly(readonly);
-            }
-            this.wid_text.prop('disabled', readonly);
-        },
-        _set_button_sensitive: function() {
-            var create = this.attributes.create;
-            if (create === undefined) {
-                create = true;
-            }
-            var delete_ = this.attributes['delete'];
-            if (delete_ === undefined) {
-                delete_ = true;
-            }
-            this.but_add.prop('disabled', this._readonly || !create);
-            for (var key in this.fields) {
-                var button = this.fields[key].button;
-                button.prop('disabled', this._readonly || !delete_);
-            }
-        },
-        add_line: function(key) {
-            var field, row;
-            this.fields[key] = field = new (this.get_entries(
-                        this.keys[key].type_))(key, this);
-            this.rows[key] = row = jQuery('<div/>', {
-                'class': 'row'
-            });
-            // TODO RTL
-            var text = this.keys[key].string + Sao.i18n.gettext(':');
-            var label = jQuery('<label/>', {
-                'text': text
-            }).appendTo(jQuery('<div/>', {
-                'class': 'dict-label col-md-4'
-            }).appendTo(row));
-
-            field.el.addClass('col-md-8').appendTo(row);
-
-            label.uniqueId();
-            field.labelled.uniqueId();
-            field.labelled.attr('aria-labelledby', label.attr('id'));
-            label.attr('for', field.labelled.attr('id'));
-
-            field.button.click(function() {
-                this.remove(key, true);
-            }.bind(this));
-
-            row.appendTo(this.container);
-        },
-        add_keys: function(keys) {
-            var context = this.field().get_context(this.record());
-            var domain = this.field().get_domain(this.record());
-            var batchlen = Math.min(10, Sao.config.limit);
-            keys = jQuery.extend([], keys);
-
-            var get_keys = function(key_ids) {
-                return this.schema_model.execute('get_keys',
-                        [key_ids], context).then(update_keys);
-            }.bind(this);
-            var update_keys = function(values) {
-                for (var i = 0, len = values.length; i < len; i++) {
-                    var k = values[i];
-                    this.keys[k.name] = k;
-                }
-            }.bind(this);
-
-            var prms = [];
-            while (keys.length > 0) {
-                var sub_keys = keys.splice(0, batchlen);
-                prms.push(this.schema_model.execute('search',
-                            [[['name', 'in', sub_keys], domain],
-                            0, Sao.config.limit, null], context)
-                        .then(get_keys));
-            }
-            return jQuery.when.apply(jQuery, prms);
-        },
-        display: function(record, field) {
-            Sao.View.Form.Dict._super.display.call(this, record, field);
-
-            if (!field) {
-                return;
-            }
-
-            var record_id = record ? record.id : null;
-            var key;
-
-            if (record_id != this._record_id) {
-                for (key in this.fields) {
-                    this.remove(key, false);
-                }
-                this._record_id = record_id;
-            }
-
-            var value = field.get_client(record);
-            var new_key_names = Object.keys(value).filter(function(e) {
-                return !this.keys[e];
-            }.bind(this));
-
-            var prm;
-            if (!jQuery.isEmptyObject(new_key_names)) {
-                prm = this.add_keys(new_key_names);
-            } else {
-                prm = jQuery.when();
-            }
-            prm.then(function() {
-                var i, len, key;
-                var keys = Object.keys(value).sort();
-                for (i = 0, len = keys.length; i < len; i++) {
-                    key = keys[i];
-                    var val = value[key];
-                    if (!this.keys[key]) {
-                        continue;
-                    }
-                    if (!this.fields[key]) {
-                        this.add_line(key);
-                    }
-                    var widget = this.fields[key];
-                    widget.set_value(val);
-                    widget.set_readonly(this._readonly);
-                }
-                var removed_key_names = Object.keys(this.fields).filter(
-                        function(e) {
-                            return !value[e];
-                        });
-                for (i = 0, len = removed_key_names.length; i < len; i++) {
-                    key = removed_key_names[i];
-                    this.remove(key, false);
-                }
-            }.bind(this));
-            this._set_button_sensitive();
-        },
-        get_entries: function(type) {
-            switch (type) {
-                case 'char':
-                    return Sao.View.Form.Dict.Entry;
-                case 'boolean':
-                    return Sao.View.Form.Dict.Boolean;
-                case 'selection':
-                    return Sao.View.Form.Dict.Selection;
-                case 'integer':
-                    return Sao.View.Form.Dict.Integer;
-                case 'float':
-                    return Sao.View.Form.Dict.Float;
-                case 'numeric':
-                    return Sao.View.Form.Dict.Numeric;
-                case 'date':
-                    return Sao.View.Form.Dict.Date;
-                case 'datetime':
-                    return Sao.View.Form.Dict.DateTime;
-            }
-        }
-    });
-
-    Sao.View.Form.Dict.Entry = Sao.class_(Object, {
-        class_: 'dict-char',
-        init: function(name, parent_widget) {
-            this.name = name;
-            this.definition = parent_widget.keys[name];
-            this.parent_widget = parent_widget;
-            this.create_widget();
-        },
-        create_widget: function() {
-            this.el = jQuery('<div/>', {
-                'class': this.class_
-            });
-            var group = jQuery('<div/>', {
-                'class': 'input-group input-group-sm'
-            }).appendTo(this.el);
-            this.input = this.labelled = jQuery('<input/>', {
-                'type': 'text',
-                'class': 'form-control input-sm'
-            }).appendTo(group);
-            this.button = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button',
-                'arial-label': Sao.i18n.gettext('Remove')
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-minus'
-            })).appendTo(jQuery('<div/>', {
-                'class': 'input-group-btn'
-            }).appendTo(group));
-
-            this.el.change(
-                    this.parent_widget.focus_out.bind(this.parent_widget));
-        },
-        get_value: function() {
-            return this.input.val();
-        },
-        set_value: function(value) {
-            this.input.val(value || '');
-        },
-        set_readonly: function(readonly) {
-            this.input.prop('readonly', readonly);
-        }
-    });
-
-    Sao.View.Form.Dict.Boolean = Sao.class_(Sao.View.Form.Dict.Entry, {
-        class_: 'dict-boolean',
-        create_widget: function() {
-            Sao.View.Form.Dict.Boolean._super.create_widget.call(this);
-            this.input.attr('type', 'checkbox');
-            this.input.change(
-                    this.parent_widget.focus_out.bind(this.parent_widget));
-        },
-        get_value: function() {
-            return this.input.prop('checked');
-        },
-        set_value: function(value) {
-            this.input.prop('checked', value);
-        }
-    });
-
-    Sao.View.Form.Dict.Selection = Sao.class_(Sao.View.Form.Dict.Entry, {
-        class_: 'dict-selection',
-        create_widget: function() {
-            Sao.View.Form.Dict.Selection._super.create_widget.call(this);
-            var select = jQuery('<select/>', {
-                'class': 'form-control input-sm'
-            });
-            select.change(
-                    this.parent_widget.focus_out.bind(this.parent_widget));
-            this.input.replaceWith(select);
-            this.input = this.labelled = select;
-            var selection = jQuery.extend([], this.definition.selection);
-            selection.splice(0, 0, [null, '']);
-            selection.forEach(function(e) {
-                select.append(jQuery('<option/>', {
-                    'value': JSON.stringify(e[0]),
-                    'text': e[1],
-                }));
-            });
-        },
-        get_value: function() {
-            return JSON.parse(this.input.val());
-        },
-        set_value: function(value) {
-            this.input.val(JSON.stringify(value));
-        }
-    });
-
-    Sao.View.Form.Dict.Integer = Sao.class_(Sao.View.Form.Dict.Entry, {
-        class_: 'dict-integer',
-        get_value: function() {
-            var value = parseInt(this.input.val(), 10);
-            if (isNaN(value)) {
-                return null;
-            }
-            return value;
-        }
-    });
-
-    Sao.View.Form.Dict.Float = Sao.class_(Sao.View.Form.Dict.Integer, {
-        class_: 'dict-float',
-        get_value: function() {
-            var value = Number(this.input.val());
-            if (isNaN(value)) {
-                return null;
-            }
-            return value;
-        }
-    });
-
-    Sao.View.Form.Dict.Numeric = Sao.class_(Sao.View.Form.Dict.Float, {
-        class_: 'dict-numeric',
-        get_value: function() {
-            var value = new Sao.Decimal(this.input.val());
-            if (isNaN(value.valueOf())) {
-                return null;
-            }
-            return value;
-        }
-    });
-
-    Sao.View.Form.Dict.Date = Sao.class_(Sao.View.Form.Dict.Entry, {
-        class_: 'dict-date',
-        format: '%x',
-        create_widget: function() {
-            Sao.View.Form.Dict.Date._super.create_widget.call(this);
-            var group = this.button.parent();
-            jQuery('<button/>', {
-                'class': 'datepickerbutton btn btn-default',
-                'type': 'button'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-calendar'
-            })).prependTo(group);
-            this.input.datetimepicker({
-                'format': Sao.common.moment_format(this.format)
-            });
-            this.input.on('dp.change',
-                    this.parent_widget.focus_out.bind(this.parent_widget));
-        },
-        get_value: function() {
-            var value = this.input.data('DateTimePicker').date();
-            if (value) {
-                value.isDate = true;
-            }
-            return value;
-        },
-        set_value: function(value) {
-            this.date.data('DateTimePicker').date(value);
-        }
-    });
-
-    Sao.View.Form.Dict.DateTime = Sao.class_(Sao.View.Form.Dict.Date, {
-        class_: 'dict-datetime',
-        format: '%x %X',
-        get_value: function() {
-            var value = this.input.data('DateTimePicker').date();
-            if (value) {
-                value.isDateTime = true;
-            }
-            return value;
-        }
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View.tree_column_get = function(type) {
-        switch (type) {
-            case 'char':
-            case 'text':
-            case 'binary':
-                return Sao.View.Tree.CharColumn;
-            case 'many2one':
-                return Sao.View.Tree.Many2OneColumn;
-            case 'one2one':
-                return Sao.View.Tree.One2OneColumn;
-            case 'date':
-                return Sao.View.Tree.DateColumn;
-            case 'time':
-                return Sao.View.Tree.TimeColumn;
-            case 'timedelta':
-                return Sao.View.Tree.TimeDeltaColumn;
-            case 'one2many':
-                return Sao.View.Tree.One2ManyColumn;
-            case 'many2many':
-                return Sao.View.Tree.Many2ManyColumn;
-            case 'selection':
-                return Sao.View.Tree.SelectionColumn;
-            case 'reference':
-                return Sao.View.Tree.ReferenceColumn;
-            case 'float':
-            case 'numeric':
-                return Sao.View.Tree.FloatColumn;
-            case 'integer':
-            case 'biginteger':
-                return Sao.View.Tree.IntegerColumn;
-            case 'boolean':
-                return Sao.View.Tree.BooleanColumn;
-            case 'image':
-                return Sao.View.Tree.ImageColumn;
-            case 'url':
-            case 'email':
-            case 'callto':
-            case 'sip':
-                return Sao.View.Tree.URLColumn;
-            case 'progressbar':
-                return Sao.View.Tree.ProgressBar;
-        }
-    };
-
-    Sao.View.Tree = Sao.class_(Sao.View, {
-        init: function(screen, xml, children_field) {
-            Sao.View.Tree._super.init.call(this, screen, xml);
-            this.view_type = 'tree';
-            this.selection_mode = (screen.attributes.selection_mode ||
-                Sao.common.SELECTION_SINGLE);
-            this.el = jQuery('<div/>', {
-                'class': 'treeview responsive'
-            });
-            this.expanded = {};
-            this.children_field = children_field;
-            this.editable = Boolean(this.attributes.editable);
-
-            // Columns
-            this.columns = [];
-            this.create_columns(screen.model, xml);
-
-            // Table of records
-            this.rows = [];
-            this.table = jQuery('<table/>', {
-                'class': 'tree table table-hover table-striped'
-            });
-            if (this.columns.filter(function(c) {
-                return !c.attributes.tree_invisible;
-            }).length > 1) {
-                this.table.addClass('responsive');
-                this.table.addClass('responsive-header');
-            }
-            this.el.append(this.table);
-            var thead = jQuery('<thead/>');
-            this.table.append(thead);
-            var tr = jQuery('<tr/>');
-            if (this.selection_mode != Sao.common.SELECTION_NONE) {
-                var th = jQuery('<th/>', {
-                    'class': 'selection'
-                });
-                this.selection = jQuery('<input/>', {
-                    'type': 'checkbox',
-                    'class': 'selection'
-                });
-                this.selection.change(this.selection_changed.bind(this));
-                th.append(this.selection);
-                tr.append(th);
-            }
-            thead.append(tr);
-            this.columns.forEach(function(column) {
-                th = jQuery('<th/>');
-                var label = jQuery('<label/>')
-                    .text(column.attributes.string);
-                if (this.editable) {
-                    if (column.attributes.required) {
-                        label.addClass('required');
-                    }
-                    if (!column.attributes.readonly) {
-                        label.addClass('editable');
-                    }
-                }
-                tr.append(th.append(label));
-                column.header = th;
-            }, this);
-            this.tbody = jQuery('<tbody/>');
-            this.table.append(this.tbody);
-
-            // Footer for more
-            var footer = jQuery('<div/>', {
-                'class': 'treefooter'
-            });
-            this.more = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('More')
-                ).click(function() {
-                this.display_size += Sao.config.display_size;
-                this.display();
-            }.bind(this));
-            footer.append(this.more);
-            this.more.hide();
-            this.display_size = Sao.config.display_size;
-            this.el.append(footer);
-        },
-        create_columns: function(model, xml) {
-            xml.find('tree').children().each(function(pos, child) {
-                var column, editable_column, attribute;
-                var attributes = {};
-                for (var i = 0, len = child.attributes.length; i < len; i++) {
-                    attribute = child.attributes[i];
-                    attributes[attribute.name] = attribute.value;
-                }
-                ['readonly', 'tree_invisible', 'expand', 'completion'].forEach(
-                    function(name) {
-                        if (attributes[name]) {
-                            attributes[name] = attributes[name] == 1;
-                        }
-                    });
-                if (child.tagName == 'field') {
-                    var name = attributes.name;
-                    if (!attributes.widget) {
-                        attributes.widget = model.fields[name].description.type;
-                    }
-                    var attribute_names = ['relation', 'domain', 'selection',
-                        'relation_field', 'string', 'views', 'invisible',
-                        'add_remove', 'sort', 'context', 'filename',
-                        'autocomplete', 'translate', 'create', 'delete',
-                        'selection_change_with', 'schema_model', 'required',
-                        'readonly'];
-                    for (i in attribute_names) {
-                        var attr = attribute_names[i];
-                        if ((attr in model.fields[name].description) &&
-                            (child.getAttribute(attr) === null)) {
-                            attributes[attr] = model.fields[name]
-                                .description[attr];
-                        }
-                    }
-                    var ColumnFactory = Sao.View.tree_column_get(
-                        attributes.widget);
-                    column = new ColumnFactory(model, attributes);
-
-                    var prefixes = [], suffixes = [];
-                    if (~['url', 'email', 'callto', 'sip'
-                            ].indexOf(attributes.widget)) {
-                        column.prefixes.push(new Sao.View.Tree.Affix(
-                                    attributes, attributes.widget));
-                    }
-                    if ('icon' in attributes) {
-                        column.prefixes.push(new Sao.View.Tree.Affix(
-                                    attributes));
-                    }
-                    var affix, affix_attributes;
-                    var affixes = child.childNodes;
-                    for (i = 0; i < affixes.length; i++) {
-                        affix = affixes[i];
-                        affix_attributes = {};
-                        for (i = 0, len = affix.attributes.length; i < len;
-                                i++) {
-                            attribute = affix.attributes[i];
-                            affix_attributes[attribute.name] = attribute.value;
-                        }
-                        if (!affix_attributes.name) {
-                            affix_attributes.name = name;
-                        }
-                        if (affix.tagName == 'prefix') {
-                            column.prefixes.push(new Sao.View.Tree.Affix(
-                                        affix_attributes));
-                        } else {
-                            column.suffixes.push(new Sao.View.Tree.Affix(
-                                        affix_attributes));
-                        }
-                    }
-
-                    this.fields[name] = true;
-                    // TODO sum
-                } else if (child.tagName == 'button') {
-                    column = new Sao.View.Tree.ButtonColumn(this.screen,
-                            attributes);
-                }
-                this.columns.push(column);
-            }.bind(this));
-        },
-        get_buttons: function() {
-            var buttons = [];
-            this.columns.forEach(function(column) {
-                if (column instanceof Sao.View.Tree.ButtonColumn) {
-                    buttons.push(column);
-                }
-            });
-            return buttons;
-        },
-        display: function(selected, expanded) {
-            var current_record = this.screen.current_record;
-            if (!selected) {
-                selected = this.get_selected_paths();
-                if (current_record) {
-                    var current_path = current_record.get_path(this.screen.group);
-                    current_path = current_path.map(function(e) {
-                        return e[1];
-                    });
-                    if (!Sao.common.contains(selected, current_path)) {
-                        selected = [current_path];
-                    }
-                } else if (!current_record) {
-                    selected = [];
-                }
-            }
-            expanded = expanded || [];
-
-            if ((this.screen.group.length != this.rows.length) ||
-                    !Sao.common.compare(
-                        this.screen.group, this.rows.map(function(row) {
-                            return row.record;
-                        })) || this.children_field) {  // XXX find better check
-                                                       // to keep focus
-                this.construct(selected, expanded);
-            }
-
-            // Set column visibility depending on attributes and domain
-            var domain = [];
-            if (!jQuery.isEmptyObject(this.screen.domain)) {
-                domain.push(this.screen.domain);
-            }
-            var tab_domain = this.screen.screen_container.get_tab_domain();
-            if (!jQuery.isEmptyObject(tab_domain)) {
-                domain.push(tab_domain);
-            }
-            var inversion = new Sao.common.DomainInversion();
-            domain = inversion.simplify(domain);
-            this.columns.forEach(function(column) {
-                var name = column.attributes.name;
-                if (!name) {
-                    return;
-                }
-                if (column.attributes.tree_invisible) {
-                    column.header.hide();
-                } else if (name === this.screen.exclude_field) {
-                    column.header.hide();
-                } else {
-                    var inv_domain = inversion.domain_inversion(domain, name);
-                    if (typeof inv_domain != 'boolean') {
-                        inv_domain = inversion.simplify(inv_domain);
-                    }
-                    var unique = inversion.unique_value(inv_domain)[0];
-                    if (unique && jQuery.isEmptyObject(this.children_field)) {
-                        column.header.hide();
-                    } else {
-                        column.header.show();
-                    }
-                }
-            }.bind(this));
-
-            this.redraw(selected, expanded);
-            return jQuery.when();
-        },
-        construct: function(selected, expanded) {
-            this.rows = [];
-            this.tbody.empty();
-            var add_row = function(record, pos, group) {
-                var RowBuilder;
-                if (this.editable) {
-                    RowBuilder = Sao.View.Tree.RowEditable;
-                } else {
-                    RowBuilder = Sao.View.Tree.Row;
-                }
-                var tree_row = new RowBuilder(this, record, pos);
-                this.rows.push(tree_row);
-                tree_row.construct(selected, expanded);
-            };
-            this.screen.group.slice(0, this.display_size).forEach(
-                    add_row.bind(this));
-            if (this.display_size >= this.screen.group.length) {
-                this.more.hide();
-            } else {
-                this.more.show();
-            }
-        },
-        redraw: function(selected, expanded) {
-            var redraw_row = function(record, pos, group) {
-               this.rows[pos].redraw(selected, expanded);
-            };
-            this.screen.group.slice(0, this.display_size).forEach(
-                    redraw_row.bind(this));
-        },
-        switch_: function(path) {
-            this.screen.row_activate();
-        },
-        select_changed: function(record) {
-            var previous_record = this.screen.current_record;
-            this.screen.set_current_record(record);
-            if (this.editable && previous_record) {
-                var go_previous = function() {
-                    this.screen.set_current_record(previous_record);
-                    this.set_cursor();
-                }.bind(this);
-                if (!this.screen.group.parent && previous_record !== record) {
-                    previous_record.validate(this.get_fields())
-                        .then(function(validate) {
-                            if (!validate) {
-                                go_previous();
-                            } else {
-                                previous_record.save().fail(go_previous);
-                            }
-                        });
-                } else if (previous_record !== record &&
-                        this.screen.attributes.pre_validate) {
-                    previous_record.pre_validate().then(function(validate) {
-                        if (!validate) {
-                            go_previous();
-                        }
-                    });
-                }
-            }
-            // TODO update_children
-        },
-        selected_records: function() {
-            if (this.selection_mode == Sao.common.SELECTION_NONE) {
-                return [];
-            }
-            var records = [];
-            var add_record = function(row) {
-                if (row.is_selected()) {
-                    records.push(row.record);
-                }
-                row.rows.forEach(add_record);
-            };
-            this.rows.forEach(add_record);
-            if (this.selection.prop('checked') &&
-                    !this.selection.prop('indeterminate')) {
-                this.screen.group.slice(this.rows.length)
-                    .forEach(function(record) {
-                        records.push(record);
-                    });
-            }
-            return records;
-        },
-        selection_changed: function() {
-            var value = this.selection.prop('checked');
-            var set_checked = function(row) {
-                row.set_selection(value);
-                row.rows.forEach(set_checked);
-            };
-            this.rows.forEach(set_checked);
-            if (value && this.rows[0]) {
-                this.select_changed(this.rows[0].record);
-            } else {
-                this.select_changed(null);
-            }
-        },
-        update_selection: function() {
-            if (this.selection.prop('checked')) {
-                return;
-            }
-            var selected_records = this.selected_records();
-            this.selection.prop('indeterminate', false);
-            if (jQuery.isEmptyObject(selected_records)) {
-                this.selection.prop('checked', false);
-            } else if (selected_records.length ==
-                    this.tbody.children().length &&
-                    this.display_size >= this.screen.group.length) {
-                this.selection.prop('checked', true);
-            } else {
-                this.selection.prop('indeterminate', true);
-                // Set checked to go first unchecked after first click
-                this.selection.prop('checked', true);
-            }
-        },
-        get_selected_paths: function() {
-            var selected_paths = [];
-            function get_selected(row, path) {
-                var i, r, len, r_path;
-                for (i = 0, len = row.rows.length; i < len; i++) {
-                    r = row.rows[i];
-                    r_path = path.concat([r.record.id]);
-                    if (r.is_selected()) {
-                        selected_paths.push(r_path);
-                    }
-                    get_selected(r, r_path);
-                }
-            }
-            get_selected(this, []);
-            return selected_paths;
-        },
-        get_expanded_paths: function(starting_path, starting_id_path) {
-            var id_path, id_paths, row, children_rows, path;
-            if (starting_path === undefined) {
-                starting_path = [];
-            }
-            if (starting_id_path === undefined) {
-                starting_id_path = [];
-            }
-            id_paths = [];
-            row = this.find_row(starting_path);
-            children_rows = row ? row.rows : this.rows;
-            for (var path_idx = 0, len = this.n_children(row) ;
-                    path_idx < len ; path_idx++) {
-                path = starting_path.concat([path_idx]);
-                row = children_rows[path_idx];
-                if (row && row.is_expanded()) {
-                    id_path = starting_id_path.concat(row.record.id);
-                    id_paths.push(id_path);
-                    id_paths = id_paths.concat(this.get_expanded_paths(path,
-                                id_path));
-                }
-            }
-            return id_paths;
-        },
-        find_row: function(path) {
-            var index;
-            var row = null;
-            var group = this.rows;
-            for (var i=0, len=path.length; i < len; i++) {
-                index = path[i];
-                if (!group || index >= group.length) {
-                    return null;
-                }
-                row = group[index];
-                group = row.rows;
-                if (!this.children_field) {
-                    break;
-                }
-            }
-            return row;
-        },
-        n_children: function(row) {
-            if (!row || !this.children_field) {
-                return this.rows.length;
-            }
-            return row.record._values[this.children_field].length;
-        },
-        set_cursor: function(new_, reset_view) {
-            var i, root_group, path, row_path, row, column;
-            var row_idx, rest, td;
-
-            if (!this.screen.current_record) {
-                return;
-            }
-
-            path = null;
-            for (i = 0; i < this.rows.length; i++) {
-                row_path = this.rows[i].record_to_path(
-                        this.screen.current_record);
-                if (row_path) {
-                    row_path.unshift(i);
-                    path = row_path;
-                    break;
-                }
-            }
-
-            row = null;
-            if (path) {
-                row_idx = path[0];
-                rest = path.slice(1);
-                if (rest.length > 0) {
-                    this.rows[row_idx].expand_to_path(rest);
-                }
-                row = this.find_row(path);
-            } else if (this.rows.length > 0) {
-                row = this.rows[0];
-            }
-
-            if (row) {
-                column = row.next_column(null, new_);
-                td = row._get_column_td(column);
-                if (this.editable) {
-                    td.triggerHandler('click');
-                    if (new_) {
-                        td.triggerHandler('click');
-                    } else {
-                        td.find(':input,[tabindex=0]').focus();
-                    }
-                } else {
-                    td.find(':input,[tabindex=0]').focus();
-                }
-            }
-        }
-    });
-
-    Sao.View.Tree.Row = Sao.class_(Object, {
-        init: function(tree, record, pos, parent) {
-            this.tree = tree;
-            this.rows = [];
-            this.record = record;
-            this.parent_ = parent;
-            this.children_field = tree.children_field;
-            this.expander = null;
-            var path = [];
-            if (parent) {
-                path = jQuery.extend([], parent.path.split('.'));
-            }
-            path.push(pos);
-            this.path = path.join('.');
-            this.el = jQuery('<tr/>');
-        },
-        is_expanded: function() {
-            return (this.path in this.tree.expanded);
-        },
-        get_last_child: function() {
-            if (!this.children_field || !this.is_expanded() ||
-                    jQuery.isEmptyObject(this.rows)) {
-                return this;
-            }
-            return this.rows[this.rows.length - 1].get_last_child();
-        },
-        get_id_path: function() {
-            if (!this.parent_) {
-                return [this.record.id];
-            }
-            return this.parent_.get_id_path().concat([this.record.id]);
-        },
-        build_widgets: function() {
-            var table = jQuery('<table/>');
-            table.css('width', '100%');
-            var row = jQuery('<tr/>');
-            table.append(row);
-            return [table, row];
-        },
-        construct: function(selected, expanded) {
-            selected = selected || [];
-            expanded = expanded || [];
-
-            var el_node = this.el[0];
-            while (el_node.firstChild) {
-                el_node.removeChild(el_node.firstChild);
-            }
-
-            var td;
-            if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
-                td = jQuery('<td/>');
-                this.el.append(td);
-                this.selection = jQuery('<input/>', {
-                    'type': 'checkbox',
-                    'class': 'selection'
-                });
-                this.selection.change(this.selection_changed.bind(this));
-                td.append(this.selection);
-            }
-
-            var depth = this.path.split('.').length;
-            for (var i = 0; i < this.tree.columns.length; i++) {
-                var column = this.tree.columns[i];
-                td = jQuery('<td/>', {
-                    'data-title': column.attributes.string
-                }).append(jQuery('<div/>', { // For responsive min-height
-                    'aria-hidden': true
-                }));
-                td.css('overflow', 'hidden');
-                td.on('click keypress', {column: i, td: td},
-                        Sao.common.click_press(this.select_row.bind(this),
-                            true));
-                if (!this.tree.editable) {
-                    td.dblclick(this.switch_row.bind(this));
-                }
-                var widgets = this.build_widgets();
-                var table = widgets[0];
-                var row = widgets[1];
-                td.append(table);
-                if ((i === 0) && this.children_field) {
-                    var expanded_icon = 'glyphicon-plus';
-                    if (this.is_expanded() ||
-                            ~expanded.indexOf(this.record.id)) {
-                        expanded_icon = 'glyphicon-minus';
-                    }
-                    this.expander = jQuery('<span/>', {
-                        'class': 'glyphicon ' + expanded_icon,
-                        'tabindex': 0
-                    });
-                    this.expander.html(' ');
-                    this.expander.css('margin-left', (depth - 1) + 'em');
-                    this.expander.css('float', 'left');
-                    this.expander.on('click keypress',
-                            Sao.common.click_press(this.toggle_row.bind(this)));
-                    row.append(jQuery('<td/>', {
-                        'class': 'expander'
-                    }).append(this.expander).css('width', 1));
-                }
-                var j;
-                if (column.prefixes) {
-                    for (j = 0; j < column.prefixes.length; j++) {
-                        var prefix = column.prefixes[j];
-                        row.append(jQuery('<td/>', {
-                            'class': 'prefix'
-                        }).css('width', 1));
-                    }
-                }
-                row.append(jQuery('<td/>', {
-                    'class': 'widget'
-                }));
-                if (column.suffixes) {
-                    for (j = 0; j < column.suffixes.length; j++) {
-                        var suffix = column.suffixes[j];
-                        row.append(jQuery('<td/>', {
-                            'class': 'suffix'
-                        }).css('width', 1));
-                    }
-                }
-
-                this.el.append(td);
-            }
-            if (this.parent_) {
-                var last_child = this.parent_.get_last_child();
-                last_child.el.after(this.el);
-            } else {
-                this.tree.tbody.append(this.el);
-            }
-            var row_id_path = this.get_id_path();
-            if (this.is_expanded() ||
-                    Sao.common.contains(expanded, row_id_path)) {
-                this.tree.expanded[this.path] = this;
-                this.expand_children(selected, expanded);
-            }
-        },
-        _get_column_td: function(column_index, row) {
-            row = row || this.el;
-            var child_offset = 0;
-            if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
-                child_offset += 1;
-            }
-            return jQuery(row.children()[column_index + child_offset]);
-        },
-        redraw: function(selected, expanded) {
-            selected = selected || [];
-            expanded = expanded || [];
-            var update_expander = function() {
-                if (jQuery.isEmptyObject(
-                            this.record.field_get(
-                                this.children_field))) {
-                    this.expander.css('visibility', 'hidden');
-                }
-            };
-
-            for (var i = 0; i < this.tree.columns.length; i++) {
-                if ((i === 0) && this.children_field) {
-                    this.record.load(this.children_field).done(
-                        update_expander.bind(this));
-                }
-                var column = this.tree.columns[i];
-                var td = this._get_column_td(i);
-                var tr = td.find('tr');
-                if (column.prefixes) {
-                    for (var j = 0; j < column.prefixes.length; j++) {
-                        var prefix = column.prefixes[j];
-                        jQuery(tr.children('.prefix')[j])
-                            .html(prefix.render(this.record));
-                    }
-                }
-                jQuery(tr.children('.widget')).html(column.render(this.record));
-                if (column.suffixes) {
-                    for (var k = 0; k < column.suffixes.length; k++) {
-                        var suffix = column.suffixes[k];
-                        jQuery(tr.children('.suffix')[k])
-                            .html(suffix.render(this.record));
-                    }
-                }
-                if (column.attributes.tree_invisible ||
-                        column.header.css('display') == 'none') {
-                    td.hide();
-                } else {
-                    td.show();
-                }
-            }
-            var row_id_path = this.get_id_path();
-            this.set_selection(Sao.common.contains(selected, row_id_path));
-            if (this.is_expanded() ||
-                    Sao.common.contains(expanded, row_id_path)) {
-                this.tree.expanded[this.path] = this;
-                if (!this.record._values[this.children_field] ||
-                        (this.record._values[this.children_field].length > 0 &&
-                         this.rows.length === 0)) {
-                    this.expand_children(selected, expanded);
-                } else {
-                    var child_row;
-                    for (i = 0; i < this.rows.length; i++) {
-                        child_row = this.rows[i];
-                        child_row.redraw(selected, expanded);
-                    }
-                }
-                if (this.expander) {
-                    this.update_expander(true);
-                }
-            } else {
-                if (this.expander) {
-                    this.update_expander(false);
-                }
-            }
-            if (this.record.deleted() || this.record.removed()) {
-                this.el.css('text-decoration', 'line-through');
-            } else {
-                this.el.css('text-decoration', 'inherit');
-            }
-        },
-        toggle_row: function() {
-            if (this.is_expanded()) {
-                this.update_expander(false);
-                delete this.tree.expanded[this.path];
-                this.collapse_children();
-            } else {
-                this.update_expander(true);
-                this.tree.expanded[this.path] = this;
-                this.expand_children();
-            }
-            return false;
-        },
-        update_expander: function(expanded) {
-            if (expanded) {
-                this.expander.removeClass('glyphicon-plus');
-                this.expander.addClass('glyphicon-minus');
-            } else {
-                this.expander.removeClass('glyphicon-minus');
-                this.expander.addClass('glyphicon-plus');
-            }
-        },
-        collapse_children: function() {
-            this.rows.forEach(function(row, pos, rows) {
-                row.collapse_children();
-                var node = row.el[0];
-                node.parentNode.removeChild(node);
-            });
-            this.rows = [];
-        },
-        expand_children: function(selected, expanded) {
-            var add_children = function() {
-                if (!jQuery.isEmptyObject(this.rows)) {
-                    return;
-                }
-                var add_row = function(record, pos, group) {
-                    var tree_row = new this.Class(
-                            this.tree, record, pos, this);
-                    tree_row.construct(selected, expanded);
-                    tree_row.redraw(selected, expanded);
-                    this.rows.push(tree_row);
-                };
-                var children = this.record.field_get_client(
-                        this.children_field);
-                children.forEach(add_row.bind(this));
-            };
-            return this.record.load(this.children_field).done(
-                    add_children.bind(this));
-        },
-        switch_row: function() {
-            if (window.getSelection) {
-                if (window.getSelection().empty) {  // Chrome
-                    window.getSelection().empty();
-                } else if (window.getSelection().removeAllRanges) {  // Firefox
-                    window.getSelection().removeAllRanges();
-                }
-            } else if (document.selection) {  // IE?
-                document.selection.empty();
-            }
-            if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
-                this.set_selection(true);
-                this.selection_changed();
-                if (!this.is_selected()) {
-                    return;
-                }
-            }
-            this.tree.switch_(this.path);
-        },
-        select_row: function(event_) {
-            if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
-                this.tree.select_changed(this.record);
-                this.switch_row();
-            } else {
-                if (!event_.ctrlKey &&
-                        this.tree.selection_mode ==
-                        Sao.common.SELECTION_SINGLE) {
-                    this.tree.rows.forEach(function(row) {
-                        row.set_selection(false);
-                    }.bind(this));
-                }
-                this.set_selection(!this.is_selected());
-                this.selection_changed();
-            }
-
-            // The 'click'-event must be handled next time the row is clicked
-            var td = event_.data.td;
-            var column = event_.data.column;
-            td.on('click keypress', {column: column, td: td},
-                    Sao.common.click_press(this.select_row.bind(this), true));
-        },
-        is_selected: function() {
-            if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
-                return false;
-            }
-            return this.selection.prop('checked');
-        },
-        set_selection: function(value) {
-            if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
-                return;
-            }
-            this.selection.prop('checked', value);
-            if (!value) {
-                this.tree.selection.prop('checked', false);
-            }
-        },
-        selection_changed: function() {
-            var is_selected = this.is_selected();
-            this.set_selection(is_selected);
-            if (is_selected) {
-                this.tree.select_changed(this.record);
-            } else {
-                this.tree.select_changed(
-                        this.tree.selected_records()[0] || null);
-            }
-            this.tree.update_selection();
-        },
-        record_to_path: function(record) {
-            // recursively get the path to the record
-            var i, path;
-            if (record == this.record) {
-                return [];
-            } else {
-                for (i = 0; i < this.rows.length; i++) {
-                    path = this.rows[i].record_to_path(record);
-                    if (path) {
-                        path.unshift(i);
-                        return path;
-                    }
-                }
-            }
-        },
-        expand_to_path: function(path) {
-            var row_idx, rest;
-            row_idx = path[0];
-            rest = path.slice(1);
-            if (rest.length > 0) {
-                this.rows[row_idx].expand_children().done(function() {
-                    this.rows[row_idx].expand_to_path(rest);
-                }.bind(this));
-            }
-        },
-        next_column: function(column, editable, sign) {
-            var i, readonly;
-            var column_index, state_attrs;
-
-            sign = sign || 1;
-            if ((column === null) && (sign > 0)) {
-                column = -1;
-            } else if (column === null) {
-                column = 0;
-            }
-            column_index = 0;
-            for (i = 0; i < this.tree.columns.length; i++) {
-                column_index = ((column + (sign * (i + 1))) %
-                        this.tree.columns.length);
-                // javascript modulo returns negative number for negative
-                // numbers
-                if (column_index < 0) {
-                    column_index += this.tree.columns.length;
-                }
-                column = this.tree.columns[column_index];
-                state_attrs = column.field.get_state_attrs(this.record);
-                if (editable) {
-                    readonly = (column.attributes.readonly ||
-                            state_attrs.readonly);
-                } else {
-                    readonly = false;
-                }
-                if (!(state_attrs.invisible || readonly)) {
-                    break;
-                }
-            }
-            return column_index;
-        }
-    });
-
-    Sao.View.Tree.RowEditable = Sao.class_(Sao.View.Tree.Row, {
-        init: function(tree, record, pos, parent) {
-            Sao.View.Tree.RowEditable._super.init.call(this, tree, record, pos,
-                parent);
-            this.edited_column = null;
-        },
-        redraw: function(selected, expanded) {
-            var i, tr, td, widget;
-            var field;
-
-            Sao.View.Tree.RowEditable._super.redraw.call(this, selected,
-                    expanded);
-            // The autocompletion widget do not call display thus we have to
-            // call it when redrawing the row
-            for (i = 0; i < this.tree.columns.length; i++) {
-                td = this._get_column_td(i);
-                tr = td.find('tr');
-                widget = jQuery(tr.children('.widget-editable')).data('widget');
-                if (widget) {
-                    field = this.record.model.fields[widget.field_name];
-                    widget.display(this.record, field);
-                }
-            }
-        },
-        select_row: function(event_) {
-            var previously_selected, previous_td;
-            var inner_rows, readonly_row, editable_row, current_td;
-            var field, widget;
-
-            function get_previously_selected(rows, selected) {
-                var i, r;
-                for (i = 0; i < rows.length; i++) {
-                    r = rows[i];
-                    if (r.is_selected()) {
-                        previously_selected = r;
-                    }
-                    if (r != selected) {
-                        r.set_selection(false);
-                    }
-                    get_previously_selected(r.rows, selected);
-                }
-            }
-            get_previously_selected(this.tree.rows, this);
-            this.selection_changed();
-
-            var save_prm;
-            if (previously_selected && previously_selected != this &&
-                    !this.tree.screen.group.parent) {
-                save_prm = previously_selected.record.save();
-            } else {
-                save_prm = jQuery.when();
-            }
-            save_prm.done(function () {
-                if (previously_selected &&
-                        previously_selected.edited_column !== null) {
-                    previous_td = previously_selected.get_active_td();
-                    previous_td.on('click keypress', {
-                        td: previous_td,
-                        column: previously_selected.edited_column
-                    }, Sao.common.click_press(
-                        previously_selected.select_row.bind(previously_selected),
-                        true));
-                    var previous_column = this.tree.columns[
-                        previously_selected.edited_column];
-                    previously_selected.get_static_el()
-                        .html(previous_column.render(previously_selected.record))
-                        .show();
-                    previously_selected.empty_editable_el();
-                }
-                if (this.is_selected()) {
-                    this.edited_column = event_.data.column;
-                    current_td = this.get_active_td();
-                    var attributes = this.tree.columns[this.edited_column]
-                        .attributes;
-                    var EditableBuilder = Sao.View.editabletree_widget_get(
-                        attributes.widget);
-                    widget = new EditableBuilder(attributes.name,
-                            this.tree.screen.model, attributes);
-                    widget.view = this.tree;
-                    // We have to define an overflow:visible in order for the
-                    // completion widget to be shown
-                    widget.el.on('focusin', function() {
-                        jQuery(this).parents('.treeview td')
-                            .css('overflow', 'visible');
-                    });
-                    widget.el.on('focusout', function() {
-                        jQuery(this).parents('.treeview td')
-                            .css('overflow', 'hidden');
-                    });
-                    var editable_el = this.get_editable_el();
-                    editable_el.append(widget.el);
-                    editable_el.data('widget', widget);
-                    // We use keydown to be able to catch TAB events
-                    widget.el.on('keydown', this.key_press.bind(this));
-                    field = this.record.model.fields[widget.field_name];
-                    widget.display(this.record, field);
-                    this.get_static_el().hide();
-                    this.get_editable_el().show();
-                    widget.focus();
-                } else {
-                    this.set_selection(true);
-                    this.selection_changed();
-                    var td = event_.data.td;
-                    var column = event_.data.column;
-                    td.on('click keypress', {column: column, td: td},
-                        Sao.common.click_press(this.select_row.bind(this),
-                            true));
-                }
-            }.bind(this));
-        },
-        get_static_el: function() {
-            var td = this.get_active_td();
-            return td.find('.widget');
-        },
-        get_editable_el: function() {
-            var td = this.get_active_td();
-            var editable = td.find('.widget-editable');
-            if (!editable.length) {
-                editable = jQuery('<td/>', {
-                        'class': 'widget-editable'
-                    }).insertAfter(td.find('.widget'));
-            }
-            return editable;
-        },
-        empty_editable_el: function() {
-            var editable;
-            editable = this.get_editable_el();
-            editable.empty();
-            editable.data('widget', null);
-            this.edited_column = null;
-        },
-        get_active_td: function() {
-            return this._get_column_td(this.edited_column);
-        },
-        key_press: function(event_) {
-            var current_td, selector, next_column, next_idx, i, next_row;
-            var states;
-
-            if ((event_.which != Sao.common.TAB_KEYCODE) &&
-                    (event_.which != Sao.common.UP_KEYCODE) &&
-                    (event_.which != Sao.common.DOWN_KEYCODE) &&
-                    (event_.which != Sao.common.ESC_KEYCODE) &&
-                    (event_.which != Sao.common.RETURN_KEYCODE)) {
-                return;
-            }
-            var column = this.tree.columns[this.edited_column];
-            if (column.field.validate(this.record)) {
-                if (event_.which == Sao.common.TAB_KEYCODE) {
-                    var sign = 1;
-                    if (event_.shiftKey) {
-                        sign = -1;
-                    }
-                    event_.preventDefault();
-                    next_idx = this.next_column(this.edited_column, true, sign);
-                    window.setTimeout(function() {
-                        var td = this._get_column_td(next_idx);
-                        td.triggerHandler('click', {
-                            column: next_idx,
-                            td: td
-                        });
-                    }.bind(this), 0);
-                } else if (event_.which == Sao.common.UP_KEYCODE ||
-                    event_.which == Sao.common.DOWN_KEYCODE) {
-                    if (event_.which == Sao.common.UP_KEYCODE) {
-                        next_row = this.el.prev('tr');
-                    } else {
-                        next_row = this.el.next('tr');
-                    }
-                    next_column = this.edited_column;
-                    this.record.validate(this.tree.get_fields())
-                        .then(function(validate) {
-                            if (!validate) {
-                                next_row = null;
-                                var invalid_fields =
-                                    this.record.invalid_fields();
-                                for (i = 0; i < this.tree.columns.length; i++) {
-                                    var col = this.tree.columns[i];
-                                    if (col.attributes.name in invalid_fields) {
-                                        next_column = i;
-                                    }
-                                }
-                            } else {
-                                if (this.tree.screen.attributes.pre_validate) {
-                                    return this.record.pre_validate()
-                                        .fail(function() {
-                                            next_row = null;
-                                        });
-                                } else if (!this.tree.screen.model.parent) {
-                                    return this.record.save()
-                                        .fail(function() {
-                                            next_row = null;
-                                        });
-                                }
-                            }
-                        }.bind(this)).then(function() {
-                            window.setTimeout(function() {
-                                this._get_column_td(next_column, next_row)
-                                .trigger('click').trigger('click');
-                            }.bind(this), 0);
-                        }.bind(this));
-                } else if (event_.which == Sao.common.ESC_KEYCODE) {
-                    this.get_static_el().show();
-                    current_td = this.get_active_td();
-                    current_td.on('click keypress',
-                            {column: this.edited_column, td: current_td},
-                            Sao.common.click_press(this.select_row.bind(this),
-                                true));
-                    this.empty_editable_el();
-                } else if (event_.which == Sao.common.RETURN_KEYCODE) {
-                    var focus_cell = function(row) {
-                        var td = this._get_column_td(this.edited_column, row);
-                        td.triggerHandler('click');
-                        td.triggerHandler('click');
-                    }.bind(this);
-                    if (this.tree.attributes.editable == 'bottom') {
-                        next_row = this.el.next('tr');
-                    } else {
-                        next_row = this.el.prev('tr');
-                    }
-                    if (next_row.length) {
-                        focus_cell(next_row);
-                    } else {
-                        // TODO access and size_limit
-                        this.tree.screen.new_().done(function() {
-                            var new_row;
-                            var rows = this.tree.tbody.children('tr');
-                            if (this.tree.attributes.editable == 'bottom') {
-                                new_row = rows.last();
-                            } else {
-                                new_row = rows.first();
-                            }
-                            focus_cell(new_row);
-                        }.bind(this));
-                    }
-                }
-            }
-        }
-    });
-
-    Sao.View.Tree.Affix = Sao.class_(Object, {
-        init: function(attributes, protocol) {
-            this.attributes = attributes;
-            this.protocol = protocol || null;
-            this.icon = attributes.icon;
-            if (this.protocol && !this.icon) {
-                this.icon = 'tryton-web-browser';
-            }
-        },
-        get_cell: function() {
-            var cell;
-            if (this.protocol) {
-                cell = jQuery('<a/>', {
-                    'target': '_new'
-                });
-                cell.append(jQuery('<img/>'));
-                cell.click({'cell': cell}, this.clicked.bind(this));
-            } else if (this.icon) {
-                cell = jQuery('<img/>');
-            } else {
-                cell = jQuery('<span/>');
-                cell.attr('tabindex', 0);
-            }
-            cell.addClass('column-affix');
-            return cell;
-        },
-        render: function(record) {
-            var cell = this.get_cell();
-            record.load(this.attributes.name).done(function() {
-                var value, icon_prm;
-                var field = record.model.fields[this.attributes.name];
-                var invisible = field.get_state_attrs(record).invisible;
-                if (invisible) {
-                    cell.hide();
-                } else {
-                    cell.show();
-                }
-                if (this.protocol) {
-                    value = field.get(record);
-                    if (!jQuery.isEmptyObject(value)) {
-                        switch (this.protocol) {
-                            case 'email':
-                                value = 'mailto:' + value;
-                                break;
-                            case 'callto':
-                                value = 'callto:' + value;
-                                break;
-                            case 'sip':
-                                value = 'sip:' + value;
-                                break;
-                        }
-                    }
-                    cell.attr('src', value);
-                }
-                if (this.icon) {
-                    if (this.icon in record.model.fields) {
-                        var icon_field = record.model.fields[this.icon];
-                        value = icon_field.get_client(record);
-                    }
-                    else {
-                        value = this.icon;
-                    }
-                    icon_prm = Sao.common.ICONFACTORY.register_icon(value);
-                    icon_prm.done(function(url) {
-                        var img_tag;
-                        if (cell.children('img').length) {
-                            img_tag = cell.children('img');
-                        } else {
-                            img_tag = cell;
-                        }
-                        img_tag.attr('src', url);
-                    }.bind(this));
-                } else {
-                    value = this.attributes.string || '';
-                    if (!value) {
-                        value = field.get_client(record) || '';
-                    }
-                    cell.text(value);
-                }
-            }.bind(this));
-            return cell;
-        },
-        clicked: function(event) {
-            event.preventDefault();  // prevent edition
-            window.open(event.data.cell.attr('src'), '_blank');
-        }
-    });
-
-    Sao.View.Tree.CharColumn = Sao.class_(Object, {
-        class_: 'column-char',
-        init: function(model, attributes) {
-            this.type = 'field';
-            this.model = model;
-            this.field = model.fields[attributes.name];
-            this.attributes = attributes;
-            this.prefixes = [];
-            this.suffixes = [];
-            this.header = null;
-        },
-        get_cell: function() {
-            var cell = jQuery('<div/>', {
-                'class': this.class_,
-                'tabindex': 0
-            });
-            return cell;
-        },
-        update_text: function(cell, record) {
-            cell.text(this.field.get_client(record));
-        },
-        render: function(record) {
-            var cell = this.get_cell();
-            record.load(this.attributes.name).done(function() {
-                this.update_text(cell, record);
-                this.field.set_state(record);
-                var state_attrs = this.field.get_state_attrs(record);
-                if (state_attrs.invisible) {
-                    cell.hide();
-                } else {
-                    cell.show();
-                }
-            }.bind(this));
-            return cell;
-        }
-    });
-
-    Sao.View.Tree.IntegerColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-integer',
-        init: function(model, attributes) {
-            Sao.View.Tree.IntegerColumn._super.init.call(this, model, attributes);
-            this.factor = Number(attributes.factor || 1);
-        },
-        get_cell: function() {
-            return Sao.View.Tree.IntegerColumn._super.get_cell.call(this);
-        },
-        update_text: function(cell, record) {
-            cell.text(this.field.get_client(record, this.factor));
-        }
-    });
-
-    Sao.View.Tree.FloatColumn = Sao.class_(Sao.View.Tree.IntegerColumn, {
-        class_: 'column-float'
-    });
-
-    Sao.View.Tree.BooleanColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-boolean',
-        get_cell: function() {
-            return jQuery('<input/>', {
-                'type': 'checkbox',
-                'disabled': true,
-                'class': this.class_,
-                'tabindex': 0
-            });
-        },
-        update_text: function(cell, record) {
-            cell.prop('checked', this.field.get(record));
-        }
-    });
-
-    Sao.View.Tree.Many2OneColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-many2one'
-    });
-
-    Sao.View.Tree.One2OneColumn = Sao.class_(Sao.View.Tree.Many2OneColumn, {
-        class_: 'column-one2one'
-    });
-
-    Sao.View.Tree.SelectionColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-selection',
-        init: function(model, attributes) {
-            Sao.View.Tree.SelectionColumn._super.init.call(this, model,
-                attributes);
-            Sao.common.selection_mixin.init.call(this);
-            this.init_selection();
-        },
-        init_selection: function(key) {
-            Sao.common.selection_mixin.init_selection.call(this, key);
-        },
-        update_selection: function(record, callback) {
-            Sao.common.selection_mixin.update_selection.call(this, record,
-                this.field, callback);
-        },
-        update_text: function(cell, record) {
-            this.update_selection(record, function() {
-                var value = this.field.get(record);
-                var prm, text, found = false;
-                for (var i = 0, len = this.selection.length; i < len; i++) {
-                    if (this.selection[i][0] === value) {
-                        found = true;
-                        text = this.selection[i][1];
-                        break;
-                    }
-                }
-                if (!found) {
-                    prm = Sao.common.selection_mixin.get_inactive_selection
-                        .call(this, value).then(function(inactive) {
-                            return inactive[1];
-                        });
-                } else {
-                    prm = jQuery.when(text);
-                }
-                prm.done(function(text_value) {
-                    cell.text(text_value);
-                }.bind(this));
-            }.bind(this));
-        }
-    });
-
-    Sao.View.Tree.ReferenceColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-reference',
-        init: function(model, attributes) {
-            Sao.View.Tree.ReferenceColumn._super.init.call(this, model,
-                attributes);
-            Sao.common.selection_mixin.init.call(this);
-            this.init_selection();
-        },
-        init_selection: function(key) {
-            Sao.common.selection_mixin.init_selection.call(this, key);
-        },
-        update_selection: function(record, callback) {
-            Sao.common.selection_mixin.update_selection.call(this, record,
-                this.field, callback);
-        },
-        update_text: function(cell, record) {
-            this.update_selection(record, function() {
-                var value = this.field.get_client(record);
-                var model, name;
-                if (!value) {
-                    model = '';
-                    name = '';
-                } else {
-                    model = value[0];
-                    name = value[1];
-                }
-                if (model) {
-                    cell.text(this.selection[model] || model + ',' + name);
-                } else {
-                    cell.text(name);
-                }
-            }.bind(this));
-        }
-    });
-
-    Sao.View.Tree.DateColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-date',
-        update_text: function(cell, record) {
-            var value = this.field.get_client(record);
-            var date_format = this.field.date_format(record);
-            cell.text(Sao.common.format_date(date_format, value));
-        }
-    });
-
-    Sao.View.Tree.TimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-time',
-        update_text: function(cell, record) {
-            var value = this.field.get_client(record);
-            cell.text(Sao.common.format_time(
-                    this.field.time_format(record), value));
-        }
-    });
-
-    Sao.View.Tree.TimeDeltaColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-timedelta'
-    });
-
-    Sao.View.Tree.One2ManyColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-one2many',
-        update_text: function(cell, record) {
-            cell.text('( ' + this.field.get_client(record).length + ' )');
-        }
-    });
-
-    Sao.View.Tree.Many2ManyColumn = Sao.class_(Sao.View.Tree.One2ManyColumn, {
-        class_: 'column-many2many'
-    });
-
-    Sao.View.Tree.ImageColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-image',
-        get_cell: function() {
-            var cell = jQuery('<img/>', {
-                'class': this.class_,
-                'tabindex': 0
-            });
-            cell.css('width', '100%');
-            return cell;
-        },
-        render: function(record) {
-            var cell = this.get_cell();
-            record.load(this.attributes.name).done(function() {
-                var value = this.field.get_client(record);
-                if (value) {
-                    if (value > Sao.common.BIG_IMAGE_SIZE) {
-                        value = jQuery.when(null);
-                    } else {
-                        value = this.field.get_data(record);
-                    }
-                } else {
-                    value = jQuery.when(null);
-                }
-                value.done(function(data) {
-                    var img_url, blob;
-                    if (!data) {
-                        img_url = null;
-                    } else {
-                        blob = new Blob([data[0][this.field.name]]);
-                        img_url = window.URL.createObjectURL(blob);
-                    }
-                    cell.attr('src', img_url);
-                }.bind(this));
-            }.bind(this));
-            return cell;
-        }
-    });
-
-    Sao.View.Tree.URLColumn = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-url',
-        render: function(record) {
-            var cell = Sao.View.Tree.URLColumn._super.render.call(this, record);
-            this.field.set_state(record);
-            var state_attrs = this.field.get_state_attrs(record);
-            if (state_attrs.readonly) {
-                cell.hide();
-            } else {
-                cell.show();
-            }
-        }
-    });
-
-    Sao.View.Tree.ProgressBar = Sao.class_(Sao.View.Tree.CharColumn, {
-        class_: 'column-progressbar',
-        get_cell: function() {
-            var cell = jQuery('<div/>', {
-                'class': this.class_ + ' progress',
-                'tabindex': 0
-            });
-            var progressbar = jQuery('<div/>', {
-                'class': 'progress-bar',
-                'role': 'progressbar',
-                'aria-valuemin': 0,
-                'aria-valuemax': 100
-            }).appendTo(cell);
-            progressbar.css('min-width: 2em');
-            return cell;
-        },
-        update_text: function(cell, record) {
-            var text = this.field.get_client(record, 100);
-            if (text) {
-                text = Sao.i18n.gettext('%1%', text);
-            }
-            var value = this.field.get(record) || 0;
-            var progressbar = cell.find('.progress-bar');
-            progressbar.attr('aria-valuenow', value * 100);
-            progressbar.css('width', value * 100 + '%');
-            progressbar.text(text);
-        }
-    });
-
-    Sao.View.Tree.ButtonColumn = Sao.class_(Object, {
-        init: function(screen, attributes) {
-            this.screen = screen;
-            this.type = 'button';
-            this.attributes = attributes;
-        },
-        render: function(record) {
-            var button = new Sao.common.Button(this.attributes);
-            button.el.click(record, this.button_clicked.bind(this));
-            var fields = jQuery.map(this.screen.model.fields,
-                function(field, name) {
-                    if ((field.description.loading || 'eager') ==
-                        'eager') {
-                        return name;
-                    } else {
-                        return undefined;
-                    }
-                });
-            // Wait at least one eager field is loaded before evaluating states
-            record.load(fields[0]).done(function() {
-                button.set_state(record);
-            });
-            return button.el;
-        },
-        button_clicked: function(event) {
-            var record = event.data;
-            if (record != this.screen.current_record) {
-                return;
-            }
-            var states = record.expr_eval(this.attributes.states || {});
-            if (states.invisible || states.readonly) {
-                return;
-            }
-            this.screen.button(this.attributes);
-        }
-    });
-
-    Sao.View.editabletree_widget_get = function(type) {
-        switch (type) {
-            case 'char':
-            case 'text':
-            case 'url':
-            case 'email':
-            case 'callto':
-            case 'sip':
-                return Sao.View.EditableTree.Char;
-            case 'date':
-                return Sao.View.EditableTree.Date;
-            case 'time':
-                return Sao.View.EditableTree.Time;
-            case 'timedelta':
-                return Sao.View.EditableTree.TimeDelta;
-            case 'integer':
-            case 'biginteger':
-                return Sao.View.EditableTree.Integer;
-            case 'float':
-            case 'numeric':
-                return Sao.View.EditableTree.Float;
-            case 'selection':
-                return Sao.View.EditableTree.Selection;
-            case 'boolean':
-                return Sao.View.EditableTree.Boolean;
-            case 'many2one':
-                return Sao.View.EditableTree.Many2One;
-            case 'one2one':
-                return Sao.View.EditableTree.One2One;
-            case 'one2many':
-            case 'many2many':
-                return Sao.View.EditableTree.One2Many;
-            case 'binary':
-                return Sao.View.EditableTree.Binary;
-        }
-    };
-
-    Sao.View.EditableTree = {};
-
-    Sao.View.EditableTree.editable_mixin = function(widget) {
-        var key_press = function(event_) {
-            if ((event_.which == Sao.common.TAB_KEYCODE) ||
-                    (event_.which == Sao.common.UP_KEYCODE) ||
-                    (event_.which == Sao.common.DOWN_KEYCODE) ||
-                    (event_.which == Sao.common.ESC_KEYCODE) ||
-                    (event_.which == Sao.common.RETURN_KEYCODE)) {
-                this.focus_out();
-            }
-        };
-        widget.el.on('keydown', key_press.bind(widget));
-    };
-
-    Sao.View.EditableTree.Char = Sao.class_(Sao.View.Form.Char, {
-        class_: 'editabletree-char',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Char._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Date = Sao.class_(Sao.View.Form.Date, {
-        class_: 'editabletree-date',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Date._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Time = Sao.class_(Sao.View.Form.Time, {
-        class_: 'editabletree-time',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Time._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.TimeDelta = Sao.class_(Sao.View.Form.TimeDelta, {
-        class_: 'editabletree-timedelta',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.TimeDelta._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Integer = Sao.class_(Sao.View.Form.Integer, {
-        class_: 'editabletree-integer',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Integer._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Float = Sao.class_(Sao.View.Form.Float, {
-        class_: 'editabletree-float',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Float._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Selection = Sao.class_(Sao.View.Form.Selection, {
-        class_: 'editabletree-selection',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Selection._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Boolean = Sao.class_(Sao.View.Form.Boolean, {
-        class_: 'editabletree-boolean',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Boolean._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-    Sao.View.EditableTree.Many2One = Sao.class_(Sao.View.Form.Many2One, {
-        class_: 'editabletree-many2one',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Many2One._super.init.call(this, field_name,
-                model, attributes);
-            this.el.on('keydown', this.key_press.bind(this));
-        },
-        key_press: function(event_) {
-            if (event_.which == Sao.common.TAB_KEYCODE) {
-                this.focus_out();
-            } else {
-                Sao.View.EditableTree.Many2One._super.key_press.call(this,
-                    event_);
-            }
-        }
-    });
-
-    Sao.View.EditableTree.One2One = Sao.class_(Sao.View.Form.One2One, {
-        class_: 'editabletree-one2one',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.One2One._super.init.call(this, field_name,
-                model, attributes);
-            this.el.on('keydown', this.key_press.bind(this));
-        },
-        key_press: function(event_) {
-            if (event_.which == Sao.common.TAB_KEYCODE) {
-                this.focus_out();
-            } else {
-                Sao.View.EditableTree.One2One._super.key_press.call(this,
-                    event_);
-            }
-        }
-    });
-
-    Sao.View.EditableTree.One2Many = Sao.class_(Sao.View.EditableTree.Char, {
-        class_: 'editabletree-one2many',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.One2Many._super.init.call(this, field_name,
-                model, attributes);
-        },
-        display: function(record, field) {
-            if (record) {
-                this.el.val('(' + field.get_client(record).length + ')');
-            } else {
-                this.el.val('');
-            }
-        },
-        key_press: function(event_) {
-            if (event_.which == Sao.common.TAB_KEYCODE) {
-                this.focus_out();
-            }
-        },
-        set_value: function(record, field) {
-        }
-    });
-
-    Sao.View.EditableTree.Binary = Sao.class_(Sao.View.Form.Binary, {
-        class_: 'editabletree-binary',
-        init: function(field_name, model, attributes) {
-            Sao.View.EditableTree.Binary._super.init.call(this, field_name,
-                model, attributes);
-            Sao.View.EditableTree.editable_mixin(this);
-        }
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View.Graph = Sao.class_(Sao.View, {
-        init: function(screen, xml) {
-            Sao.View.Graph._super.init.call(this, screen, xml);
-            this.view_type = 'graph';
-            this.el = jQuery('<div/>', {
-                'class': 'graph'
-            });
-            this.widgets = {};
-            this.widget = this.parse(xml.children()[0]);
-            this.widgets.root = this.widget;
-            this.el.append(this.widget.el);
-        },
-        parse: function(node) {
-            var field, xfield = null, yfields = [], yattrs;
-            var child, node_child;
-            var i, len, j, c_len;
-
-            var get_attributes = function(node) {
-                var attributes = {}, attribute;
-                for (var i = 0, len = node.attributes.length; i < len; i++) {
-                    attribute = node.attributes[i];
-                    attributes[attribute.name] = attribute.value;
-                }
-                return attributes;
-            };
-
-            for (i=0, len=node.children.length; i < len; i++) {
-                child = node.children[i];
-                switch (child.tagName) {
-                    case 'x':
-                        for (j=0, c_len=child.children.length; j < c_len; j++) {
-                            xfield = get_attributes(child.children[j]);
-                            field = this.screen.model.fields[xfield.name];
-                            if (!(xfield.string || '')) {
-                                xfield.string = field.description.string;
-                            }
-                        }
-                        break;
-                    case 'y':
-                        for (j=0, c_len=child.children.length; j < c_len; j++) {
-                            yattrs = get_attributes(child.children[j]);
-                            if (!(yattrs.string || '') &&
-                                    (yattrs.name != '#')) {
-                                field = this.screen.model.fields[yattrs.name];
-                                yattrs.string = field.description.string;
-                            }
-                            yfields.push(yattrs);
-                        }
-                        break;
-                }
-            }
-
-            var Widget;
-            switch (this.attributes.type) {
-                case 'hbar':
-                    Widget = Sao.View.Graph.HorizontalBar;
-                    break;
-                case 'line':
-                    Widget = Sao.View.Graph.Line;
-                    break;
-                case 'pie':
-                    Widget = Sao.View.Graph.Pie;
-                    break;
-                default:
-                    Widget = Sao.View.Graph.VerticalBar;
-            }
-            return new Widget(this, xfield, yfields);
-        },
-        display: function() {
-            return this.widget.display(this.screen.group);
-        }
-    });
-
-    Sao.View.Graph.Chart = Sao.class_(Object, {
-        _chart_type: undefined,
-
-        init: function(view, xfield, yfields) {
-            this.view = view;
-            this.xfield = xfield;
-            this.yfields = yfields;
-            this.el = jQuery('<div/>');
-            this.el.uniqueId();
-        },
-        update_data: function(group) {
-            var data = {};
-            var record, yfield, key;
-            var i, len, j, y_len;
-
-            this.ids = {};
-            data.columns = [['labels']];
-            var key2columns = {};
-            var fields2load = [this.xfield.name];
-            for (i = 0, len = this.yfields.length; i < len; i++) {
-                yfield = this.yfields[i];
-                data.columns.push([yfield.string]);
-                key2columns[yfield.key || yfield.name] = i + 1;
-                fields2load.push(yfield.name);
-            }
-
-            var prms = [];
-            var set_data = function(index) {
-                return function () {
-                    record = group[index];
-                    var x = record.field_get(this.xfield.name);
-                    data.columns[0][index + 1] = x;
-                    this._add_id(x, record.id);
-
-                    var column;
-                    for (j = 0, y_len = this.yfields.length; j < y_len; j++) {
-                        yfield = this.yfields[j];
-                        key = yfield.key || yfield.name;
-                        column = data.columns[key2columns[key]];
-                        if (yfield.domain) {
-                            var ctx = jQuery.extend({},
-                                    Sao.session.current_session.context);
-                            ctx.context = ctx;
-                            ctx._user = Sao.session.current_session.user_id;
-                            for (var field in group.model.fields) {
-                                ctx[field] = record.field_get(field);
-                            }
-                            var decoder = new Sao.PYSON.Decoder(ctx);
-                            if (!decoder.decode(yfield.domain)) {
-                                column[index + 1] = 0;
-                                continue;
-                            }
-                        }
-                        if (yfield.name == '#') {
-                            column[index + 1] = 1;
-                        } else {
-                            var value = record.field_get(yfield.name);
-                            if (value && value.isTimeDelta) {
-                                value = value.asSeconds();
-                            }
-                            column[index + 1] = value || 0;
-                        }
-                    }
-                }.bind(this);
-            }.bind(this);
-            var load_field = function(record) {
-                return function(fname) {
-                    prms.push(record.load(fname));
-                };
-            };
-
-            var r_prms = [];
-            for (i = 0, len = group.length; i < len; i++) {
-                record = group[i];
-                fields2load.forEach(load_field(group[i]));
-
-                for (j = 0, y_len = data.columns.length; j < y_len; j++) {
-                    data.columns[j].push(undefined);
-                }
-                r_prms.push(
-                        jQuery.when.apply(jQuery, prms).then(set_data(i)));
-            }
-            return jQuery.when.apply(jQuery, r_prms).then(function() {
-                return data;
-            });
-        },
-        _add_id: function(key, id) {
-            // c3 do not use the moment instance but its date repr when calling
-            // onclick
-            var id_x = (key.isDate || key.isDateTime) ? key._d : key;
-            if (!(id_x in this.ids)) {
-                this.ids[id_x] = [];
-            }
-            this.ids[id_x].push(id);
-        },
-        display: function(group) {
-            var update_prm = this.update_data(group);
-            update_prm.done(function(data) {
-                c3.generate(this._c3_config(data));
-            }.bind(this));
-            return update_prm;
-        },
-        _c3_config: function(data) {
-            var c3_config = {};
-
-            c3_config.bindto = '#' + this.el.attr('id');
-            c3_config.data = data;
-            c3_config.data.type = this._chart_type;
-            c3_config.data.x = 'labels';
-            c3_config.data.onclick = this.action.bind(this);
-
-            var i, len;
-            var found, labels;
-            for (i = 0, len = data.columns.length; i < len; i++) {
-                labels = data.columns[i];
-                if (labels[0] == 'labels') {
-                    found = true;
-                    break;
-                }
-            }
-            if (found && (labels.length > 1) &&
-                    (labels[1] && (labels[1].isDateTime || labels[1].isDate)))
-            {
-                var format_func, date_format, time_format;
-                date_format = this.view.screen.context.date_format || '%x';
-                time_format = '%X';
-                if (labels[1].isDateTime) {
-                    format_func = function(dt) {
-                        return Sao.common.format_datetime(date_format,
-                                time_format, moment(dt));
-                    };
-                } else {
-                    format_func = function(dt) {
-                        return Sao.common.format_date(date_format, moment(dt));
-                    };
-                }
-                c3_config.axis = {
-                    x: {
-                        type: 'timeseries',
-                        tick: {
-                            format: format_func,
-                        }
-                    }
-                };
-            }
-            return c3_config;
-        },
-        action: function(data, element) {
-            var ids = this.ids[this._action_key(data)];
-            var ctx = jQuery.extend({}, this.view.screen.context);
-            delete ctx.active_ids;
-            delete ctx.active_id;
-            Sao.Action.exec_keyword('graph_open', {
-                model: this.view.screen.model_name,
-                id: ids[0],
-                ids: ids
-            }, ctx, false);
-        },
-        _action_key: function(data) {
-            return data.x;
-        }
-    });
-
-    Sao.View.Graph.VerticalBar = Sao.class_(Sao.View.Graph.Chart, {
-        _chart_type: 'bar'
-    });
-
-    Sao.View.Graph.HorizontalBar = Sao.class_(Sao.View.Graph.Chart, {
-        _chart_type: 'bar',
-        _c3_config: function(data) {
-            var config = Sao.View.Graph.HorizontalBar._super._c3_config
-                .call(this, data);
-            config.axis.rotated = true;
-        }
-    });
-
-    Sao.View.Graph.Line = Sao.class_(Sao.View.Graph.Chart, {
-        _chart_type: 'line'
-    });
-
-    Sao.View.Graph.Pie = Sao.class_(Sao.View.Graph.Chart, {
-        _chart_type: 'pie',
-        _c3_config: function(data) {
-            var config = Sao.View.Graph.Pie._super._c3_config.call(this, data);
-            var pie_columns = [];
-            var i, len;
-            var labels, values;
-
-            for (i = 0, len = data.columns.length; i < len; i++) {
-                if (data.columns[i][0] == 'labels') {
-                    labels = data.columns[i].slice(1);
-                } else {
-                    values = data.columns[i].slice(1);
-                }
-            }
-
-            // Pie chart do not support axis definition.
-            delete config.axis;
-            delete config.data.x;
-            var format_func, date_format, datetime_format;
-            if ((labels.length > 0) &&
-                    (labels[0].isDateTime || labels[0].isDate)) {
-                date_format = this.view.screen.context.date_format || '%x';
-                datetime_format = date_format + ' %X';
-                if (labels[1].isDateTime) {
-                    format_func = function(dt) {
-                        return Sao.common.format_datetime(datetime_format, dt);
-                    };
-                } else {
-                    format_func = function(dt) {
-                        return Sao.common.format_date(date_format, dt);
-                    };
-                }
-            }
-            var label;
-            for (i = 0, len = labels.length; i < len; i++) {
-                label = labels[i];
-                if (format_func) {
-                    label = format_func(label);
-                }
-                pie_columns.push([label, values[i]]);
-            }
-
-            config.data.columns = pie_columns;
-            return config;
-        },
-        _add_id: function(key, id) {
-            var id_x = key;
-            if (key.isDateTime || key.isDate) {
-                var date_format = this.view.screen.context.date_format || '%x';
-                var datetime_format = date_format + ' %X';
-                if (key.isDateTime) {
-                    id_x = Sao.common.format_datetime(datetime_format, key);
-                } else {
-                    id_x = Sao.common.format_date(date_format, key);
-                }
-            }
-            if (!(id_x in this.ids)) {
-                this.ids[id_x] = [];
-            }
-            this.ids[id_x].push(id);
-        },
-        _action_key: function(data) {
-            return data.id;
-        }
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View.Calendar = Sao.class_(Sao.View, {
-        init: function(screen, xml) {
-            Sao.View.Graph._super.init.call(this, screen, xml);
-            this.view_type = 'calendar';
-            this.el = jQuery('<div/>', {
-                'class': 'calendar'
-            });
-            // TODO
-            Sao.common.warning.run(
-                    Sao.i18n.gettext('Calendar view not yet implemented'),
-                    Sao.i18n.gettext('Warning'));
-        },
-        display: function() {
-            return jQuery.when();
-        }
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Action = {
-        report_blob_url: undefined
-    };
-
-    Sao.Action.exec_action = function(action, data, context) {
-        if (context === undefined) {
-            context = {};
-        } else {
-            context = jQuery.extend({}, context);
-        }
-        var session = Sao.Session.current_session;
-        if (!('date_format' in context)) {
-            if (session.context.locale && session.context.locale.date) {
-                context.date_format = session.context.locale.date;
-            }
-        }
-        if (data === undefined) {
-            data = {};
-        } else {
-            data = jQuery.extend({}, data);
-        }
-        data.action_id = action.id;
-        var params = {};
-        switch (action.type) {
-            case 'ir.action.act_window':
-                params.view_ids = false;
-                params.mode = null;
-                if (!jQuery.isEmptyObject(action.views)) {
-                    params.view_ids = [];
-                    params.mode = [];
-                    action.views.forEach(function(x) {
-                        params.view_ids.push(x[0]);
-                        params.mode.push(x[1]);
-                    });
-                } else if (!jQuery.isEmptyObject(action.view_id)) {
-                    params.view_ids = [action.view_id[0]];
-                }
-
-                if (action.pyson_domain === undefined) {
-                    action.pyson_domain = '[]';
-                }
-                var ctx = {
-                    active_model: data.model || null,
-                    active_id: data.id || null,
-                    active_ids: data.ids
-                };
-                ctx = jQuery.extend(ctx, session.context);
-                var eval_ctx = jQuery.extend({}, ctx);
-                eval_ctx._user = session.user_id;
-                params.context = new Sao.PYSON.Decoder(eval_ctx).decode(
-                        action.pyson_context || '{}');
-                ctx = jQuery.extend(ctx, params.context);
-                ctx = jQuery.extend(ctx, context);
-                params.context = jQuery.extend(params.context, context);
-
-                var domain_context = jQuery.extend({}, ctx);
-                domain_context.context = ctx;
-                domain_context._user = session.user_id;
-                params.domain = new Sao.PYSON.Decoder(domain_context).decode(
-                        action.pyson_domain);
-
-                var search_context = jQuery.extend({}, ctx);
-                search_context.context = ctx;
-                search_context._user = session.user_id;
-                params.search_value = new Sao.PYSON.Decoder(search_context)
-                    .decode(action.pyson_search_value || '[]');
-
-                var tab_domain_context = jQuery.extend({}, ctx);
-                tab_domain_context.context = ctx;
-                tab_domain_context._user = session.user_id;
-                var decoder = new Sao.PYSON.Decoder(tab_domain_context);
-                params.tab_domain = [];
-                action.domains.forEach(function(element, index) {
-                    params.tab_domain.push(
-                        [element[0], decoder.decode(element[1])]);
-                });
-                params.name = false;
-                if (action.window_name) {
-                    params.name = action.name;
-                }
-                params.model = action.res_model || data.res_model;
-                params.res_id = action.res_id || data.res_id;
-                params.limit = action.limit;
-                params.icon = action['icon.rec_name'] || '';
-                Sao.Tab.create(params);
-                return;
-            case 'ir.action.wizard':
-                params.action = action.wiz_name;
-                params.data = data;
-                params.name = action.name;
-                params.context = context;
-                params.window = action.window;
-                Sao.Wizard.create(params);
-                return;
-            case 'ir.action.report':
-                params.name = action.report_name;
-                params.data = data;
-                params.direct_print = action.direct_print;
-                params.email_print = action.email_print;
-                params.email = action.email;
-                params.context = context;
-                Sao.Action.exec_report(params);
-                return;
-            case 'ir.action.url':
-                window.open(action.url, '_blank');
-                return;
-        }
-    };
-
-    Sao.Action.exec_keyword = function(keyword, data, context, warning,
-            alwaysask)
-    {
-        if (warning === undefined) {
-            warning = true;
-        }
-        if (alwaysask === undefined) {
-            alwaysask = false;
-        }
-        var actions = [];
-        var model_id = data.id;
-        var args = {
-            'method': 'model.' + 'ir.action.keyword.get_keyword',
-            'params': [keyword, [data.model, model_id], {}]
-        };
-        var prm = Sao.rpc(args, Sao.Session.current_session);
-        var exec_action = function(actions) {
-            var keyact = {};
-            for (var i in actions) {
-                var action = actions[i];
-                keyact[action.name.replace(/_/g, '')] = action;
-            }
-            var prm = Sao.common.selection(
-                    Sao.i18n.gettext('Select your action'),
-                    keyact, alwaysask);
-            return prm.then(function(action) {
-                Sao.Action.exec_action(action, data, context);
-            }, function() {
-                if (jQuery.isEmptyObject(keyact) && warning) {
-                    alert(Sao.i18n.gettext('No action defined!'));
-                }
-            });
-        };
-        return prm.pipe(exec_action);
-    };
-
-    Sao.Action.exec_report = function(attributes) {
-        if (!attributes.context) {
-            attributes.context = {};
-        }
-        if (!attributes.email) {
-            attributes.email = {};
-        }
-        var data = jQuery.extend({}, attributes.data);
-        var context = jQuery.extend({}, Sao.Session.current_session.context);
-        jQuery.extend(context, attributes.context);
-        context.direct_print = attributes.direct_print;
-        context.email_print = attributes.email_print;
-        context.email = attributes.email;
-
-        var prm = Sao.rpc({
-            'method': 'report.' + attributes.name + '.execute',
-            'params': [data.ids || [], data, context]
-        }, Sao.Session.current_session);
-        prm.done(function(result) {
-            var report_type = result[0];
-            var data = result[1];
-            var print = result[2];
-            var name = result[3];
-
-            // TODO direct print
-            var blob = new Blob([data],
-                {type: Sao.common.guess_mimetype(report_type)});
-            var blob_url = window.URL.createObjectURL(blob);
-            if (Sao.Action.report_blob_url) {
-                window.URL.revokeObjectURL(Sao.Action.report_blob_url);
-            }
-            Sao.Action.report_blob_url = blob_url;
-            window.open(blob_url);
-        });
-    };
-
-    Sao.Action.execute = function(id, data, type, context) {
-        if (!type) {
-            Sao.rpc({
-                'method': 'model.ir.action.read',
-                'params': [[id], ['type'], context]
-            }, Sao.Session.current_session).done(function(result) {
-                Sao.Action.execute(id, data, result[0].type, context);
-            });
-        } else {
-            Sao.rpc({
-                'method': 'model.' + type + '.search_read',
-                'params': [[['action', '=', id]], 0, 1, null, null, context]
-            }, Sao.Session.current_session).done(function(result) {
-                Sao.Action.exec_action(result[0], data, context);
-            });
-        }
-    };
-
-    Sao.Action.evaluate = function(action, atype, record) {
-        action = jQuery.extend({}, action);
-        var email = {};
-        if ('pyson_email' in action) {
-            email = record.expr_eval(action.pyson_email);
-            if (jQuery.isEmptyObject(email)) {
-                email = {};
-            }
-        }
-        if (!('subject' in email)) {
-            email.subject = action.name.replace(/_/g, '');
-        }
-        action.email = email;
-        return action;
-    };
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.common = {};
-
-    Sao.common.BACKSPACE_KEYCODE = 8;
-    Sao.common.TAB_KEYCODE = 9;
-    Sao.common.RETURN_KEYCODE = 13;
-    Sao.common.ESC_KEYCODE = 27;
-    Sao.common.UP_KEYCODE = 38;
-    Sao.common.DOWN_KEYCODE = 40;
-    Sao.common.DELETE_KEYCODE = 46;
-    Sao.common.F2_KEYCODE = 113;
-    Sao.common.F3_KEYCODE = 114;
-
-    Sao.common.SELECTION_NONE = 1;
-    Sao.common.SELECTION_SINGLE = 2;
-    Sao.common.SELECTION_MULTIPLE = 3;
-
-    Sao.common.BIG_IMAGE_SIZE = Math.pow(10, 6);
-
-    Sao.common.compare = function(arr1, arr2) {
-        if (arr1.length != arr2.length) {
-            return false;
-        }
-        for (var i = 0; i < arr1.length; i++) {
-            if (arr1[i] instanceof Array && arr2[i] instanceof Array) {
-                if (!Sao.common.compare(arr1[i], arr2[i])) {
-                    return false;
-                }
-            } else if (arr1[i] != arr2[i]) {
-                return false;
-            }
-        }
-        return true;
-    };
-
-    Sao.common.contains = function(array1, array2) {
-        for (var i = 0; i < array1.length; i++) {
-            if (Sao.common.compare(array1[i], array2)) {
-                return true;
-            }
-        }
-        return false;
-    };
-
-    // Find the intersection of two arrays.
-    // The arrays must be sorted.
-    Sao.common.intersect = function(a, b) {
-        var ai = 0, bi = 0;
-        var result = [];
-        while (ai < a.length && bi < b.length) {
-            if (a[ai] < b[bi]) {
-                ai++;
-            } else if (a[ai] > b[bi]) {
-                bi++;
-            } else {
-                result.push(a[ai]);
-                ai++;
-                bi++;
-            }
-        }
-        return result;
-    };
-
-    // Handle click and Return press event
-    // If one, the handler is executed at most once for both events
-    Sao.common.click_press = function(func, one) {
-        return function handler(evt) {
-            if (evt.type != 'keypress' ||
-                    evt.which == Sao.common.RETURN_KEYCODE) {
-                if (one) {
-                    jQuery(this).off('click keypress', null, handler);
-                }
-                return func(evt);
-            }
-        };
-    };
-
-    // Cartesian product
-    Sao.common.product = function(array, repeat) {
-        repeat = repeat || 1;
-        var pools = [];
-        var i = 0;
-        while (i < repeat) {
-            pools = pools.concat(array);
-            i++;
-        }
-        var result = [[]];
-        pools.forEach(function(pool) {
-            var tmp = [];
-            result.forEach(function(x) {
-                pool.forEach(function(y) {
-                    tmp.push(x.concat([y]));
-                });
-            });
-            result = tmp;
-        });
-        return result;
-    };
-
-    Sao.common.selection = function(title, values, alwaysask) {
-        if (alwaysask === undefined) {
-            alwaysask = false;
-        }
-        var prm = jQuery.Deferred();
-        if (jQuery.isEmptyObject(values)) {
-            prm.fail();
-            return prm;
-        }
-        var keys = Object.keys(values).sort();
-        if ((keys.length == 1) && (!alwaysask)) {
-            var key = keys[0];
-            prm.resolve(values[key]);
-            return prm;
-        }
-        var dialog = new Sao.Dialog(
-                title || Sao.i18n.gettext('Your selection:'),
-                'selection-dialog');
-
-        keys.forEach(function(k, i) {
-            jQuery('<div/>', {
-                'class': 'checkbox'
-            }).append(jQuery('<label/>')
-                .append(jQuery('<input/>', {
-                    'type': 'radio',
-                    'name': 'selection',
-                    'value': i
-                }))
-                .append(' ' + k))
-            .appendTo(dialog.body);
-        });
-        dialog.body.find('input').first().prop('checked', true);
-
-        jQuery('<button/>', {
-            'class': 'btn btn-link',
-            'type': 'button'
-        }).append(Sao.i18n.gettext('Cancel')).click(function() {
-            dialog.modal.modal('hide');
-            prm.fail();
-        }).appendTo(dialog.footer);
-        jQuery('<button/>', {
-            'class': 'btn btn-primary',
-            'type': 'button'
-        }).append(Sao.i18n.gettext('OK')).click(function() {
-            var i = dialog.body.find('input:checked').attr('value');
-            dialog.modal.modal('hide');
-            prm.resolve(values[keys[i]]);
-        }).appendTo(dialog.footer);
-        dialog.modal.on('hidden.bs.modal', function(e) {
-            jQuery(this).remove();
-        });
-        dialog.modal.modal('show');
-        return prm;
-    };
-
-    Sao.common.moment_format = function(format) {
-        return format
-            .replace('%a', 'ddd')
-            .replace('%A', 'dddd')
-            .replace('%w', 'd')
-            .replace('%d', 'DD')
-            .replace('%b', 'MMM')
-            .replace('%B', 'MMMM')
-            .replace('%m', 'MM')
-            .replace('%y', 'YY')
-            .replace('%Y', 'YYYY')
-            .replace('%H', 'HH')
-            .replace('%I', 'hh')
-            .replace('%p', 'A')
-            .replace('%M', 'mm')
-            .replace('%S', 'ss')
-            .replace('%f', 'SSS')
-            .replace('%z', 'ZZ')
-            .replace('%Z', 'zz')
-            .replace('%j', 'DDDD')
-            .replace('%U', 'ww')
-            .replace('%W', 'WW')
-            .replace('%c', 'llll')
-            .replace('%x', 'L')
-            .replace('%X', 'LTS')
-            .replace('%', '%%')
-            ;
-    };
-
-    Sao.common.date_format = function(format) {
-        if (jQuery.isEmptyObject(format) && Sao.Session.current_session) {
-            var context = Sao.Session.current_session.context;
-            if (context.locale && context.locale.date) {
-                format = context.locale.date;
-            }
-        }
-        if (format) {
-            return Sao.common.moment_format(format);
-        }
-        return '%Y-%m-%d';
-    };
-
-    Sao.common.format_time = function(format, date) {
-        if (!date) {
-            return '';
-        }
-        return date.format(Sao.common.moment_format(format));
-    };
-
-    Sao.common.parse_time = function(format, value) {
-        if (jQuery.isEmptyObject(value)) {
-            return null;
-        }
-        var getNumber = function(pattern) {
-            var i = format.indexOf(pattern);
-            if (~i) {
-                var number = parseInt(value.slice(i, i + pattern.length), 10);
-                if (!isNaN(number)) {
-                    return number;
-                }
-            }
-            return 0;
-        };
-        return Sao.Time(getNumber('%H'), getNumber('%M'), getNumber('%S'),
-                getNumber('%f'));
-    };
-
-    Sao.common.format_date = function(date_format, date) {
-        if (!date) {
-            return '';
-        }
-        return date.format(Sao.common.moment_format(date_format));
-    };
-
-    Sao.common.parse_date = function(date_format, value) {
-        var date = moment(value,
-               Sao.common.moment_format(date_format));
-        if (date.isValid()) {
-            date = Sao.Date(date.year(), date.month(), date.date());
-        } else {
-            date = null;
-        }
-        return date;
-    };
-
-    Sao.common.format_datetime = function(date_format, time_format, date) {
-        if (!date) {
-            return '';
-        }
-        return date.format(
-                Sao.common.moment_format(date_format + ' ' + time_format));
-    };
-
-    Sao.common.parse_datetime = function(date_format, time_format, value) {
-        var date = moment(value,
-                Sao.common.moment_format(date_format + ' ' + time_format));
-        if (date.isValid()) {
-            date = Sao.DateTime(date.year(), date.month(), date.date(),
-                    date.hour(), date.minute(), date.second(),
-                    date.millisecond());
-        } else {
-            date = null;
-        }
-        return date;
-    };
-
-    Sao.common.timedelta = {};
-    Sao.common.timedelta.DEFAULT_CONVERTER = {
-        's': 1
-    };
-    Sao.common.timedelta.DEFAULT_CONVERTER.m =
-        Sao.common.timedelta.DEFAULT_CONVERTER.s * 60;
-    Sao.common.timedelta.DEFAULT_CONVERTER.h =
-        Sao.common.timedelta.DEFAULT_CONVERTER.m * 60;
-    Sao.common.timedelta.DEFAULT_CONVERTER.d =
-        Sao.common.timedelta.DEFAULT_CONVERTER.h * 24;
-    Sao.common.timedelta.DEFAULT_CONVERTER.w =
-        Sao.common.timedelta.DEFAULT_CONVERTER.d * 7;
-    Sao.common.timedelta.DEFAULT_CONVERTER.M =
-        Sao.common.timedelta.DEFAULT_CONVERTER.w * 4;
-    Sao.common.timedelta.DEFAULT_CONVERTER.Y =
-        Sao.common.timedelta.DEFAULT_CONVERTER.d * 365;
-    Sao.common.timedelta._get_separator = function() {
-        return {
-            Y: Sao.i18n.gettext('Y'),
-            M: Sao.i18n.gettext('M'),
-            w: Sao.i18n.gettext('w'),
-            d: Sao.i18n.gettext('d'),
-            h: Sao.i18n.gettext('h'),
-            m: Sao.i18n.gettext('m'),
-            s: Sao.i18n.gettext('s')
-        };
-    };
-    Sao.common.timedelta.format = function(value, converter) {
-        if (!value) {
-            return '';
-        }
-        if (!converter) {
-            converter = Sao.common.timedelta.DEFAULT_CONVERTER;
-        }
-        var text = [];
-        value = value.asSeconds();
-        var sign = '';
-        if (value < 0) {
-            sign = '-';
-        }
-        value = Math.abs(value);
-        converter = Object.keys(converter).map(function(key) {
-            return [key, converter[key]];
-        });
-        converter.sort(function(first, second) {
-            return second[1] - first[1];
-        });
-        var values = [];
-        var k, v;
-        for (var i = 0; i < converter.length; i++) {
-            k = converter[i][0];
-            v = converter[i][1];
-            var part = Math.floor(value / v);
-            value -= part * v;
-            values.push(part);
-        }
-        for (i = 0; i < converter.length - 3; i++) {
-            k = converter[i][0];
-            v = values[i];
-            if (v) {
-                text.push(v + Sao.common.timedelta._get_separator()[k]);
-            }
-        }
-        if (jQuery(values.slice(-3)).is(function(i, v) { return v; }) ||
-                jQuery.isEmptyObject(text)) {
-            var time = values.slice(-3, -1);
-            time = ('00' + time[0]).slice(-2) + ':' + ('00' + time[1]).slice(-2);
-            if (values.slice(-1)[0] || value) {
-                time += ':' + ('00' + values.slice(-1)[0]).slice(-2);
-            }
-            text.push(time);
-        }
-        text = sign + text.reduce(function(p, c) {
-            if (p) {
-                return p + ' ' + c;
-            } else {
-                return c;
-            }
-        });
-        if (value) {
-            if (!jQuery(values.slice(-3)).is(function(i, v) { return v; })) {
-                // Add space if no time
-                text += ' ';
-            }
-            text += ('' + value.toFixed(6)).slice(1);
-        }
-        return text;
-    };
-    Sao.common.timedelta.parse = function(text, converter) {
-        if (!text) {
-            return null;
-        }
-        if (!converter) {
-            converter = Sao.common.timedelta.DEFAULT_CONVERTER;
-        }
-        var separators = Sao.common.timedelta._get_separator();
-        var separator;
-        for (var k in separators) {
-            separator = separators[k];
-            text = text.replace(separator, separator + ' ');
-        }
-
-        var seconds = 0;
-        var sec;
-        var parts = text.split(' ');
-        for (var i = 0; i < parts.length; i++) {
-            var part = parts[i];
-            if (part.contains(':')) {
-                var subparts = part.split(':');
-                var subconverter = [
-                    converter.h, converter.m, converter.s];
-                for (var j = 0;
-                        j < Math.min(subparts.length, subconverter.length);
-                        j ++) {
-                    var t = subparts[j];
-                    var v = subconverter[j];
-                    sec = Math.abs(Number(t)) * v;
-                    if (!isNaN(sec)) {
-                        seconds += sec;
-                    }
-                }
-            } else {
-                var found = false;
-                for (var key in separators) {
-                    separator =separators[key];
-                    if (part.endsWith(separator)) {
-                        part = part.slice(0, -separator.length);
-                        sec = Math.abs(parseInt(part, 10)) * converter[key];
-                        if (!isNaN(sec)) {
-                            seconds += sec;
-                        }
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found) {
-                    sec = Math.abs(Number(part));
-                    if (!isNaN(sec)) {
-                        seconds += sec;
-                    }
-                }
-            }
-        }
-        if (text.contains('-')) {
-            seconds *= -1;
-        }
-        return Sao.TimeDelta(null, seconds);
-    };
-
-    Sao.common.ModelAccess = Sao.class_(Object, {
-        init: function() {
-            this.batchnum = 100;
-            this._access = {};
-        },
-        load_models: function(refresh) {
-            var prm = jQuery.Deferred();
-            if (!refresh) {
-                this._access = {};
-            }
-            Sao.rpc({
-                'method': 'model.ir.model.list_models',
-                'params': [{}]
-            }, Sao.Session.current_session).then(function(models) {
-                var deferreds = [];
-                var update_access = function(access) {
-                    this._access = jQuery.extend(this._access, access);
-                };
-                for (var i = 0; i < models.length; i += this.batchnum) {
-                    var to_load = models.slice(i, i + this.batchnum);
-                    deferreds.push(Sao.rpc({
-                        'method': 'model.ir.model.access.get_access',
-                        'params': [to_load, {}]
-                    }, Sao.Session.current_session)
-                        .then(update_access.bind(this)));
-                }
-                jQuery.when.apply(jQuery, deferreds).then(
-                    prm.resolve, prm.reject);
-            }.bind(this));
-            return prm;
-        },
-        get: function(model) {
-            return this._access[model];
-        }
-    });
-    Sao.common.MODELACCESS = new Sao.common.ModelAccess();
-
-    Sao.common.ModelHistory = Sao.class_(Object, {
-        init: function() {
-            this._models = [];
-        },
-        load_history: function() {
-            this._models = [];
-            return Sao.rpc({
-                'method': 'model.ir.model.list_history',
-                'params': [{}]
-            }, Sao.Session.current_session).then(function(models) {
-                this._models = models;
-            }.bind(this));
-        },
-        contains: function(model) {
-            return ~this._models.indexOf(model);
-        }
-    });
-    Sao.common.MODELHISTORY = new Sao.common.ModelHistory();
-
-    Sao.common.ViewSearch = Sao.class_(Object, {
-        load_searches: function() {
-            this.searches = {};
-            return Sao.rpc({
-                'method': 'model.ir.ui.view_search.get_search',
-                'params': [{}]
-            }, Sao.Session.current_session).then(function(searches) {
-                this.searches = searches;
-            }.bind(this));
-        },
-        get: function(model) {
-            return this.searches[model] || [];
-        },
-        add: function(model, name, domain) {
-            return Sao.rpc({
-                'method': 'model.ir.ui.view_search.create',
-                'params': [[{
-                    'model': model,
-                    'name': name,
-                    'domain': new Sao.PYSON.Encoder().encode(domain)
-                }], {}]
-            }, Sao.Session.current_session).then(function(ids) {
-                var id = ids[0];
-                if (this.searches[model] === undefined) {
-                    this.searches[model] = [];
-                }
-                this.searches[model].push([id, name, domain]);
-            }.bind(this));
-        },
-        remove: function(model, id) {
-            return Sao.rpc({
-                'method': 'model.ir.ui.view_search.delete',
-                'params': [[id], {}]
-            }, Sao.Session.current_session).then(function() {
-                for (var i = 0; i < this.searches[model].length; i++) {
-                    var domain = this.searches[model][i];
-                    if (domain[0] === id) {
-                        this.searches[model].splice(i, 1);
-                        break;
-                    }
-                }
-            }.bind(this));
-        }
-    });
-    Sao.common.VIEW_SEARCH = new Sao.common.ViewSearch();
-
-    Sao.common.humanize = function(size) {
-        var sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
-        for (var i =0, len = sizes.length; i < len; i++) {
-            if (size < 1000) {
-                return size.toPrecision(4) + ' ' + sizes[i];
-            }
-            size /= 1000;
-        }
-    };
-
-    Sao.common.EvalEnvironment = function(parent_, eval_type) {
-        if (eval_type === undefined)
-            eval_type = 'eval';
-        var environment;
-        if (eval_type == 'eval') {
-            environment = parent_.get_eval();
-        } else {
-            environment = {};
-            for (var key in parent_.model.fields) {
-                var field = parent_.model.fields[key];
-                environment[key] = field.get_on_change_value(parent_);
-            }
-        }
-        environment.id = parent_.id;
-        if (parent_.group.parent)
-            Object.defineProperty(environment, '_parent_' +
-                    parent_.group.parent_name, {
-                'enumerable': true,
-                'get': function() {
-                    return Sao.common.EvalEnvironment(parent_.group.parent,
-                        eval_type);
-                }
-            });
-        environment.get = function(item, default_) {
-            if (this.hasOwnProperty(item))
-                return this[item];
-            return default_;
-        };
-
-        return environment;
-    };
-
-    Sao.common.selection_mixin = {};
-    Sao.common.selection_mixin.init = function() {
-        this.selection = null;
-        this.inactive_selection = [];
-        this._last_domain = null;
-        this._values2selection = {};
-        this._domain_cache = {};
-        if (this.nullable_widget === undefined) {
-            this.nullable_widget = true;
-        }
-    };
-    Sao.common.selection_mixin.init_selection = function(value, callback) {
-        if (!value) {
-            value = {};
-            (this.attributes.selection_change_with || []).forEach(function(e) {
-                value[e] = null;
-            });
-        }
-        var key = JSON.stringify(value);
-        var selection = this.attributes.selection || [];
-        var prm;
-        var prepare_selection = function(selection) {
-            selection = jQuery.extend([], selection);
-            if (this.attributes.sort === undefined || this.attributes.sort) {
-                selection.sort(function(a, b) {
-                    return a[1].localeCompare(b[1]);
-                });
-            }
-            this.selection = jQuery.extend([], selection);
-            if (callback) callback(this.selection);
-        };
-        if (!(selection instanceof Array) &&
-                !(key in this._values2selection)) {
-            if (!jQuery.isEmptyObject(this.attributes.selection_change_with)) {
-                prm = this.model.execute(selection, [value]);
-            } else {
-                prm = this.model.execute(selection, []);
-            }
-            prm = prm.then(function(selection) {
-                this._values2selection[key] = selection;
-                return selection;
-            }.bind(this));
-            prm = prm.then(prepare_selection.bind(this));
-        } else {
-            if (key in this._values2selection) {
-                selection = this._values2selection[key];
-            }
-            prepare_selection.call(this, selection);
-            prm = jQuery.when();
-        }
-        this.inactive_selection = [];
-        this._selection_prm = prm;
-    };
-    Sao.common.selection_mixin.update_selection = function(record, field,
-            callback) {
-        var _update_selection = function() {
-            if (!field) {
-                if (callback) {
-                    callback(this.selection);
-                }
-                return;
-            }
-            var domain = field.get_domain(record);
-            if (field.description.type == 'reference') {
-                // The domain on reference field is not only based on the
-                // selection so the selection can not be filtered.
-                domain = [];
-            }
-            if (!('relation' in this.attributes)) {
-                var change_with = this.attributes.selection_change_with || [];
-                var value = record._get_on_change_args(change_with);
-                delete value.id;
-                Sao.common.selection_mixin.init_selection.call(this, value,
-                        function() {
-                            Sao.common.selection_mixin.filter_selection.call(
-                                    this, domain, record, field);
-                            if (callback) {
-                                callback(this.selection);
-                            }
-                        }.bind(this));
-            } else {
-                var context = field.get_context(record);
-                var jdomain = JSON.stringify([domain, context]);
-                if (jdomain in this._domain_cache) {
-                    this.selection = this._domain_cache[jdomain];
-                    this._last_domain = [domain, context];
-                }
-                if ((this._last_domain !== null) &&
-                        Sao.common.compare(domain, this._last_domain[0]) &&
-                        (JSON.stringify(context) ==
-                         JSON.stringify(this._last_domain[1]))) {
-                    if (callback) {
-                        callback(this.selection);
-                    }
-                    return;
-                }
-                var prm = Sao.rpc({
-                    'method': 'model.' + this.attributes.relation +
-                        '.search_read',
-                    'params': [domain, 0, null, null, ['rec_name'], context]
-                }, record.model.session);
-                prm.done(function(result) {
-                    var selection = [];
-                    result.forEach(function(x) {
-                        selection.push([x.id, x.rec_name]);
-                    });
-                    if (this.nullable_widget) {
-                        selection.push([null, '']);
-                    }
-                    this._last_domain = domain;
-                    this._domain_cache[jdomain] = selection;
-                    this.selection = jQuery.extend([], selection);
-                    if (callback) {
-                        callback(this.selection);
-                    }
-                }.bind(this));
-                prm.fail(function() {
-                    this._last_domain = null;
-                    this.selection = [];
-                    if (callback) {
-                        callback(this.selection);
-                    }
-                }.bind(this));
-            }
-        };
-        this._selection_prm.done(_update_selection.bind(this));
-    };
-    Sao.common.selection_mixin.filter_selection = function(
-            domain, record, field) {
-        if (jQuery.isEmptyObject(domain)) {
-            return;
-        }
-        var inversion = new Sao.common.DomainInversion();
-        this.selection = this.selection.filter(function(value) {
-            var context = {};
-            context[this.field_name] = value[0];
-            return inversion.eval_domain(domain, context);
-        }.bind(this));
-    };
-    Sao.common.selection_mixin.get_inactive_selection = function(value) {
-        if (!this.attributes.relation) {
-            return jQuery.when([]);
-        }
-        for (var i = 0, len = this.inactive_selection.length; i < len; i++) {
-            if (value == this.inactive_selection[i][0]) {
-                return jQuery.when(this.inactive_selection[i]);
-            }
-        }
-        var prm = Sao.rpc({
-            'method': 'model.' + this.attributes.relation + '.read',
-            'params': [[value], ['rec_name'], {}]
-        }, Sao.Session.current_session);
-        return prm.then(function(result) {
-            this.inactive_selection.push([result[0].id, result[0].rec_name]);
-            return [result[0].id, result[0].rec_name];
-        }.bind(this));
-    };
-
-    Sao.common.Button = Sao.class_(Object, {
-        init: function(attributes) {
-            this.attributes = attributes;
-            this.el = jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            });
-            this.icon = jQuery('<img/>', {
-                'class': 'icon',
-                'aria-hidden': true
-            }).appendTo(this.el);
-            this.icon.hide();
-            this.el.append(attributes.string || '');
-            this.set_icon(attributes.icon);
-        },
-        set_icon: function(icon_name) {
-            if (!icon_name) {
-                this.icon.attr('src', '');
-                this.icon.hide();
-                return;
-            }
-            var prm = Sao.common.ICONFACTORY.register_icon(icon_name);
-            prm.done(function(url) {
-                this.icon.attr('src', url);
-                this.icon.show();
-            }.bind(this));
-        },
-        set_state: function(record) {
-            var states;
-            if (record) {
-                states = record.expr_eval(this.attributes.states || {});
-                if (record.group.get_readonly() || record.readonly()) {
-                    states.readonly = true;
-                }
-            } else {
-                states = {};
-            }
-            if (states.invisible) {
-                this.el.hide();
-            } else {
-                this.el.show();
-            }
-            this.el.prop('disabled', states.readonly);
-            this.set_icon(states.icon || this.attributes.icon);
-            if (((this.attributes.type === undefined) ||
-                        (this.attributes.type === 'class')) && (record)) {
-                var parent = record.group.parent;
-                while (parent) {
-                    if (parent.has_changed()) {
-                        this.el.prop('disabled', false);
-                        break;
-                    }
-                    parent = parent.group.parent;
-                }
-            }
-        }
-    });
-
-    Sao.common.udlex = Sao.class_(Object, {
-        init: function(instream) {
-
-            var Stream = Sao.class_(Object, {
-                init: function(stream) {
-                    this.stream = stream.split('');
-                    this.i = 0;
-                },
-                read: function(length) {
-                    if (length === undefined) {
-                        length = 1;
-                    }
-                    if (this.i >= this.stream.length) {
-                        return null;
-                    }
-                    var value = this.stream
-                        .slice(this.i, this.i + length).join();
-                    this.i += length;
-                    return value;
-                }
-            });
-            this.instream = new Stream(instream);
-            this.eof = null;
-            this.commenters = '';
-            this.nowordchars = [':', '>', '<', '=', '!', '"', ';', '(', ')'];
-            this.whitespace = ' \t\r\n';
-            this.whitespace_split = false;
-            this.quotes = '"';
-            this.escape = '\\';
-            this.escapedquotes = '"';
-            this.state = ' ';
-            this.pushback = [];
-            this.token = '';
-        },
-        get_token: function() {
-            if (this.pushback.length > 0) {
-                return this.pushback.shift();
-            }
-            var raw = this.read_token();
-            return raw;
-        },
-        read_token: function() {
-            var quoted = false;
-            var escapedstate = ' ';
-            while (true) {
-                var nextchar = this.instream.read(1);
-                if (this.state === null) {
-                    this.token = '';  // past en of file
-                    break;
-                } else if (this.state == ' ') {
-                    if (!nextchar) {
-                        this.state = null;  // end of file
-                        break;
-                    } else if (this.whitespace.contains(nextchar)) {
-                        if (this.token || quoted) {
-                            break;  // emit current token
-                        } else {
-                            continue;
-                        }
-                    } else if (this.commenters.contains(nextchar)) {
-                        // TODO readline
-                    } else if (this.escape.contains(nextchar)) {
-                        escapedstate = 'a';
-                        this.state = nextchar;
-                    } else if (!~this.nowordchars.indexOf(nextchar)) {
-                        this.token = nextchar;
-                        this.state = 'a';
-                    } else if (this.quotes.contains(nextchar)) {
-                        this.state = nextchar;
-                    } else if (this.whitespace_split) {
-                        this.token = nextchar;
-                        this.state = 'a';
-                    } else {
-                        this.token = nextchar;
-                        if (this.token || quoted) {
-                            break;  // emit current token
-                        } else {
-                            continue;
-                        }
-                    }
-                } else if (this.quotes.contains(this.state)) {
-                    quoted = true;
-                    if (!nextchar) {  // end of file
-                        throw 'no closing quotation';
-                    }
-                    if (nextchar == this.state) {
-                        this.state = 'a';
-                    } else if (this.escape.contains(nextchar) &&
-                        this.escapedquotes.contains(this.state)) {
-                        escapedstate = this.state;
-                        this.state = nextchar;
-                    } else {
-                        this.token = this.token + nextchar;
-                    }
-                } else if (this.escape.contains(this.state)) {
-                    if (!nextchar) {  // end of file
-                        throw 'no escaped character';
-                    }
-                    if (this.quotes.contains(escapedstate) &&
-                        (nextchar != this.state) &&
-                        (nextchar != escapedstate)) {
-                        this.token = this.token + this.state;
-                    }
-                    this.token = this.token + nextchar;
-                    this.state = escapedstate;
-                } else if (this.state == 'a') {
-                    if (!nextchar) {
-                        this.state = null;  // end of file
-                        break;
-                    } else if (this.whitespace.contains(nextchar)) {
-                        this.state = ' ';
-                        if (this.token || quoted) {
-                            break;  // emit current token
-                        } else {
-                            continue;
-                        }
-                    } else if (this.commenters.contains(nextchar)) {
-                        // TODO
-                    } else if (this.quotes.contains(nextchar)) {
-                        this.state = nextchar;
-                    } else if (this.escape.contains(nextchar)) {
-                        escapedstate = 'a';
-                        this.state = nextchar;
-                    } else if ((!~this.nowordchars.indexOf(nextchar)) ||
-                            this.quotes.contains(nextchar) ||
-                            this.whitespace_split) {
-                        this.token = this.token + nextchar;
-                    } else {
-                        this.pushback.unshift(nextchar);
-                        this.state = ' ';
-                        if (this.token) {
-                            break;  // emit current token
-                        } else {
-                            continue;
-                        }
-                    }
-                }
-            }
-            var result = this.token;
-            this.token = '';
-            if (!quoted && result === '') {
-                result = null;
-            }
-            return result;
-        },
-        next: function() {
-            var token = this.get_token();
-            if (token == this.eof) {
-                return null;
-            }
-            return token;
-        }
-    });
-
-    Sao.common.DomainParser = Sao.class_(Object, {
-        OPERATORS: ['!=', '<=', '>=', '=', '!', '<', '>'],
-        init: function(fields, context) {
-            this.fields = {};
-            this.strings = {};
-            for (var name in fields) {
-                var field = fields[name];
-                if (field.searchable || (field.searchable === undefined)) {
-                    this.fields[name] = field;
-                    this.strings[field.string.toLowerCase()] = field;
-                }
-            }
-            this.context = context;
-        },
-        parse: function(input) {
-            try {
-                var lex = new Sao.common.udlex(input);
-                var tokens = [];
-                while (true) {
-                    var token = lex.next();
-                    if (token === null) {
-                        break;
-                    }
-                    tokens.push(token);
-                }
-                tokens = this.group_operator(tokens);
-                tokens = this.parenthesize(tokens);
-                tokens = this.group(tokens);
-                tokens = this.operatorize(tokens, 'or');
-                tokens = this.operatorize(tokens, 'and');
-                tokens = this.parse_clause(tokens);
-                return this.simplify(tokens);
-            } catch (e) {
-                if (e == 'no closing quotation') {
-                    return this.parse(input + '"');
-                }
-                throw e;
-            }
-        },
-        stringable: function(domain) {
-            var stringable_ = function(clause) {
-                if (!clause) {
-                    return true;
-                }
-                var is_array = function(e) {
-                    return e instanceof Array;
-                };
-                if ((~['AND', 'OR'].indexOf(clause[0]) ||
-                            (is_array(clause[0]))) &&
-                        clause.slice(1).every(is_array)) {
-                    return this.stringable(clause);
-                }
-                if ((clause[0] in this.fields) || clause[0] == 'rec_name') {
-                    return true;
-                }
-                return false;
-            }.bind(this);
-            if (!domain) {
-                return true;
-            }
-            if (~['AND', 'OR'].indexOf(domain[0])) {
-                domain = domain.slice(1);
-            }
-            return domain.every(stringable_);
-        },
-        string: function(domain) {
-
-            var string = function(clause) {
-                if (jQuery.isEmptyObject(clause)) {
-                    return '';
-                }
-                if ((typeof clause[0] != 'string') ||
-                        ~['AND', 'OR'].indexOf(clause[0])) {
-                    return '(' + this.string(clause) + ')';
-                }
-                var escaped;
-                var name = clause[0];
-                var operator = clause[1];
-                var value = clause[2];
-                if (name.endsWith('.rec_name')) {
-                    name = name.slice(0, -9);
-                }
-                if (!(name in this.fields)) {
-                    escaped = value.replace('%%', '__');
-                    if (escaped.startsWith('%') && escaped.endsWith('%')) {
-                        value = value.slice(1, -1);
-                    }
-                    return this.quote(value);
-                }
-                var field = this.fields[name];
-                var target = null;
-                if (clause.length > 3) {
-                    target = clause[3];
-                }
-                if (operator.contains('ilike')) {
-                    escaped = value.replace('%%', '__');
-                    if (escaped.startsWith('%') && escaped.endsWith('%')) {
-                        value = value.slice(1, -1);
-                    } else if (!escaped.contains('%')) {
-                        if (operator == 'ilike') {
-                            operator = '=';
-                        } else {
-                            operator = '!';
-                        }
-                        value = value.replace('%%', '%');
-                    }
-                }
-                var def_operator = this.default_operator(field);
-                if ((def_operator == operator.trim()) ||
-                        (operator.contains(def_operator) &&
-                         (operator.contains('not') ||
-                          operator.contains('!')))) {
-                    operator = operator.replace(def_operator, '')
-                        .replace('not', '!').trim();
-                }
-                if (operator.endsWith('in')) {
-                    if (operator == 'not in') {
-                        operator = '!';
-                    } else {
-                        operator = '';
-                    }
-                }
-                var formatted_value = this.format_value(field, value, target);
-                if (~this.OPERATORS.indexOf(operator) &&
-                        ~['char', 'text', 'selection']
-                        .indexOf(field.type) &&
-                        (value === '')) {
-                    formatted_value = '""';
-                }
-                return (this.quote(field.string) + ': ' +
-                        operator + formatted_value);
-            };
-            string = string.bind(this);
-
-            if (jQuery.isEmptyObject(domain)) {
-                return '';
-            }
-            var nary = ' ';
-            if ((domain[0] == 'AND') || (domain[0] == 'OR')) {
-                if (domain[0] == 'OR') {
-                    nary = ' or ';
-                }
-                domain = domain.slice(1);
-            }
-            return domain.map(string).join(nary);
-        },
-        completion: function(input) {
-            var results = [];
-            var domain = this.parse(input);
-            var closing = 0;
-            var i, len;
-            for (i=input.length; i>0; i--) {
-                if (input[i] == ')' || input[i] == ' ') {
-                    break;
-                }
-                if (input[i] == ')') {
-                    closing += 1;
-                }
-            }
-            var endings = this.ending_clause(domain);
-            var ending = endings[0];
-            var deep_ending = endings[1];
-            var deep = deep_ending - closing;
-            var string_domain = this.string(domain);
-
-            if (deep > 0) {
-                string_domain = string_domain.substring(0,
-                        string_domain.length - deep);
-            }
-            if (string_domain != input) {
-                results.push(string_domain);
-            }
-
-            var pslice = function(string, depth) {
-                if (depth > 0) {
-                    return string.substring(0, depth);
-                }
-                return string;
-            };
-            var complete, complete_string;
-            if (ending !== null && closing === 0) {
-                var completes = this.complete(ending);
-                for (i=0, len=completes.length; i < len; i++) {
-                    complete = completes[i];
-                    complete_string = this.string(
-                            this.replace_ending_clause(domain, complete));
-                    results.push(pslice(complete_string, deep));
-                }
-            }
-            if (input.length > 0) {
-                if (input.substr(input.length - 1, 1) != ' ') {
-                    return results;
-                }
-                if (input.length >= 2 ||
-                        input.substr(input.length - 2, 1) == ':') {
-                    return results;
-                }
-            }
-            var field, operator, value;
-            for (var key in this.strings) {
-                field = this.strings[key];
-                operator = this.default_operator(field);
-                value = '';
-                if ((operator == 'ilike') || (operator == 'not ilike')) {
-                    value = this.likify(value);
-                }
-                new_domain = this.append_ending_clause(domain,
-                        [field.name, operator, value], deep);
-                new_domain_string = this.string(new_domain);
-                results.push(pslice(new_domain_string, deep));
-            }
-            return results;
-        },
-        complete: function(clause) {
-            var results = [];
-            var name, operator, value, target;
-            if (clause.length == 1) {
-                name = clause[0];
-            } else if (clause.length == 3) {
-                name = clause[0];
-                operator = clause[1];
-                value = clause[2];
-            } else {
-                name = clause[0];
-                operator = clause[1];
-                value = clause[2];
-                target = clause[3];
-                if (name.endsWith('.rec_name')) {
-                    name = name.substring(0, name.length - 9);
-                }
-            }
-            var escaped;
-            if (name == "rec_name") {
-                if (operator == "ilike") {
-                    escaped = value.replace(/%%/g, '__');
-                    if (escaped.startsWith('%') || escaped.endsWith('%')) {
-                        value = escaped.substring(1, escaped.length - 1);
-                    } else if (~escaped.indexOf('%')) {
-                        value = value.replace(/%%/g, '%');
-                    }
-                    operator = null;
-                }
-                name = value;
-                value = '';
-            }
-            var field;
-            if (!(name.toLowerCase() in this.strings) &&
-                    !(name in this.fields)) {
-                for (var key in this.strings) {
-                    field = this.strings[key];
-                    if (field.string.toLowerCase()
-                            .startsWith(name.toLowerCase())) {
-                        operator = this.default_operator(field);
-                        value = '';
-                        if (operator == 'ilike') {
-                            value = this.likify(value);
-                        }
-                        results.push([field.name, operator, value]);
-                    }
-                }
-                return results;
-            }
-            if (name in this.fields) {
-                field = this.fields[name];
-            } else {
-                field = this.strings[name.toLowerCase()];
-            }
-            if (operator === null) {
-                operator = this.default_operator(field);
-                value = '';
-                if ((operator == 'ilike') || (operator == 'not ilike')) {
-                    value = this.likify(value);
-                }
-                results.push([field.name, operator, value]);
-            } else {
-                var completes = this.complete_value(field, value);
-                for (var i=0, len=completes.length; i < len; i++) {
-                    results.push([field.name, operator, completes[i]]);
-                }
-            }
-            return results;
-        },
-        is_leaf: function(element) {
-            return ((element instanceof Array) && element.clause);
-        },
-        ending_clause: function(domain, depth) {
-            if (depth === undefined) {
-                depth = 0;
-            }
-            if (domain.length === 0) {
-                return [null, depth];
-            }
-            var last_element = domain[domain.length - 1];
-            if (!this.is_leaf(last_element)) {
-                return this.ending_clause(last_element, depth + 1);
-            }
-            return [last_element, depth];
-        },
-        replace_ending_clause: function(domain, clause) {
-            var results = [];
-            var i, len;
-            for (i = 0, len=domain.length - 1; i < len; i++) {
-                results.push(domain[i]);
-            }
-            if (!this.is_leaf(domain[i])) {
-                results = results.concat(this.replace_ending_clause(domain[i],
-                            clause));
-            } else {
-                results.push(clause);
-            }
-            return results;
-        },
-        append_ending_clause: function(domain, clause, depth) {
-            if (domain.length === 0) {
-                return [clause];
-            }
-            var results = domain.slice(0, -1);
-            var last_element = domain[domain.length - 1];
-            if (!this.is_leaf(last_element)) {
-                results.push(this.append_ending_clause(last_element, clause,
-                            depth - 1));
-            } else {
-                results.push(last_element);
-                if (depth === 0) {
-                    results.push(clause);
-                }
-            }
-            return results;
-        },
-        complete_value: function(field, value) {
-            var complete_boolean = function() {
-                return value ? [true] : [false];
-            };
-
-            var complete_selection = function() {
-                var results = [];
-                var test_value = value !== null ? value : '';
-                if (value instanceof Array) {
-                    test_value = value[value.length - 1];
-                }
-                test_value = test_value.replace(/^%*|%*$/g, '');
-                var i, len, svalue, test;
-                for (i=0, len=field.selection.length; i<len; i++) {
-                    svalue = field.selection[i][0];
-                    test = field.selection[i][1].toLowerCase();
-                    if (test.startsWith(test_value.toLowerCase())) {
-                        if (value instanceof Array) {
-                            results.push(value.slice(0, -1).concat([svalue]));
-                        } else {
-                            results.push(svalue);
-                        }
-                    }
-                }
-                return results;
-            };
-
-            var complete_reference = function() {
-                var results = [];
-                var test_value = value !== null ? value : '';
-                if (value instanceof Array) {
-                    test_value = value[value.length - 1];
-                }
-                test_value = test_value.replace(/^%*|%*$/g, '');
-                var i, len, svalue, test;
-                for (i=0, len=field.selection.length; i<len; i++) {
-                    svalue = field.selection[i][0];
-                    test = field.selection[i][1].toLowerCase();
-                    if (test.startsWith(test_value.toLowerCase())) {
-                        if (value instanceof Array) {
-                            results.push(value.slice(0, -1).concat([svalue]));
-                        } else {
-                            results.push(this.likify(svalue));
-                        }
-                    }
-                }
-                return results;
-            };
-
-            var complete_datetime = function() {
-                return [Sao.Date(), Sao.DateTime().utc()];
-            };
-
-            var complete_date = function() {
-                return [Sao.Date()];
-            };
-
-            var complete_time = function() {
-                return [Sao.Time()];
-            };
-
-            var completes = {
-                'boolean': complete_boolean,
-                'selection': complete_selection,
-                'reference': complete_reference,
-                'datetime': complete_datetime,
-                'date': complete_date,
-                'time': complete_time
-            };
-
-            if (field.type in completes) {
-                return completes[field.type]();
-            }
-            return [];
-        },
-        group_operator: function(tokens) {
-            var cur = tokens[0];
-            var nex = null;
-            var result = [];
-            tokens.slice(1).forEach(function(nex) {
-                if ((nex == '=') && cur &&
-                    ~this.OPERATORS.indexOf(cur + nex)) {
-                    result.push(cur + nex);
-                    cur = null;
-                } else {
-                    if (cur !== null) {
-                        result.push(cur);
-                    }
-                    cur = nex;
-                }
-            }.bind(this));
-            if (cur !== null) {
-                result.push(cur);
-            }
-            return result;
-        },
-        parenthesize: function(tokens) {
-            var result = [];
-            var current = result;
-            var parent = [];
-            tokens.forEach(function(token, i) {
-                if (current === undefined) {
-                    return;
-                }
-                if (token == '(') {
-                    parent.push(current);
-                    current = current[current.push([]) - 1];
-                } else if (token == ')') {
-                    current = parent.pop();
-                } else {
-                    current.push(token);
-                }
-            });
-            return result;
-        },
-        group: function(tokens) {
-            var result = [];
-
-            var _group = function(parts) {
-                var result = [];
-                var push_result = function(part) {
-                    result.push([part]);
-                };
-                var i = parts.indexOf(':');
-                if (!~i) {
-                    parts.forEach(push_result);
-                    result.forEach(function (e) {
-                        e.clause = true;
-                    });
-                    return result;
-                }
-                var sub_group = function(name, lvalue) {
-                    return function(part) {
-                        if (!jQuery.isEmptyObject(name)) {
-                            var clause;
-                            if (!jQuery.isEmptyObject(lvalue)) {
-                                if (part[0] !== null) {
-                                    lvalue.push(part[0]);
-                                }
-                                clause = name.concat([lvalue]);
-                                clause.clause = true;
-                                result.push(clause);
-                            } else {
-                                clause = name.concat(part);
-                                clause.clause = true;
-                                result.push(clause);
-                            }
-                            name.splice(0, name.length);
-                        } else {
-                            result.push(part);
-                        }
-                    };
-                };
-                for (var j = 0; j < i; j++) {
-                    var name = parts.slice(j, i).join(' ');
-                    if (name.toLowerCase() in this.strings) {
-                        if (!jQuery.isEmptyObject(parts.slice(0, j))) {
-                            parts.slice(0, j).forEach(push_result);
-                        } else {
-                            push_result(null);
-                        }
-                        name = [name];
-                        if (((i + 1) < parts.length) &&
-                                (~this.OPERATORS.indexOf(parts[i + 1]))) {
-                            name = name.concat([parts[i + 1]]);
-                            i += 1;
-                        } else {
-                            name = name.concat([null]);
-                        }
-                        var lvalue = [];
-                        while ((i + 2) < parts.length) {
-                            if (parts[i + 2] == ';') {
-                                lvalue.push(parts[i + 1]);
-                                i += 2;
-                            } else {
-                                break;
-                            }
-                        }
-                        _group(parts.slice(i + 1)).forEach(
-                                sub_group(name, lvalue));
-                        if (!jQuery.isEmptyObject(name)) {
-                            var clause;
-                            if (!jQuery.isEmptyObject(lvalue)) {
-                                clause = name.concat([lvalue]);
-                                clause.clause = true;
-                                result.push(clause);
-                            } else {
-                                clause = name.concat([null]);
-                                clause.clause = true;
-                                result.push(clause);
-                            }
-                        }
-                        break;
-                    }
-                }
-                return result;
-            };
-            _group = _group.bind(this);
-
-            var parts = [];
-            tokens.forEach(function(token) {
-                if (this.is_generator(token)) {
-                    _group(parts).forEach(function(group) {
-                        if (!Sao.common.compare(group, [null])) {
-                            result.push(group);
-                        }
-                    });
-                    parts = [];
-                    result.push(this.group(token));
-                } else {
-                    parts.push(token);
-                }
-            }.bind(this));
-            _group(parts).forEach(function(group) {
-                if (!Sao.common.compare(group, [null])) {
-                    result.push(group);
-                }
-            });
-            return result;
-        },
-        is_generator: function(value) {
-            return (value instanceof Array) && (value.clause === undefined);
-        },
-        operatorize: function(tokens, operator) {
-            var result = [];
-            operator = operator || 'or';
-            tokens = jQuery.extend([], tokens);
-            var test = function(value) {
-                if (value instanceof Array) {
-                    return Sao.common.compare(value, [operator]);
-                } else {
-                    return value == operator;
-                }
-            };
-            var cur = tokens.shift();
-            while (test(cur)) {
-                cur = tokens.shift();
-            }
-            if (cur === undefined) {
-                return result;
-            }
-            if (this.is_generator(cur)) {
-                cur = this.operatorize(cur, operator);
-            }
-            var nex = null;
-            while (!jQuery.isEmptyObject(tokens)) {
-                nex = tokens.shift();
-                if ((this.is_generator(nex)) && !test(nex)) {
-                    nex = this.operatorize(nex, operator);
-                }
-                if (test(nex)) {
-                    nex = tokens.shift();
-                    while (test(nex)) {
-                        nex = tokens.shift();
-                    }
-                    if (this.is_generator(nex)) {
-                        nex = this.operatorize(nex, operator);
-                    }
-                    if (nex !== undefined) {
-                        cur = [operator.toUpperCase(), cur, nex];
-                    } else {
-                        if (!test(cur)) {
-                            result.push([operator.toUpperCase(), cur]);
-                            cur = null;
-                        }
-                    }
-                    nex = null;
-                } else {
-                    if (!test(cur)) {
-                        result.push(cur);
-                    }
-                    cur = nex;
-                }
-            }
-            if (jQuery.isEmptyObject(tokens)) {
-                if ((nex !== null) && !test(nex)) {
-                    result.push(nex);
-                } else if ((cur !== null) && !test(cur)) {
-                    result.push(cur);
-                }
-            }
-            return result;
-        },
-        _clausify: function(e) {
-            e.clause = true;
-            return e;
-        },
-        parse_clause: function(tokens) {
-            var result = [];
-            tokens.forEach(function(clause) {
-                if (this.is_generator(clause)) {
-                    result.concat(this.parse_clause(clause));
-                } else if ((clause == 'OR') || (clause == 'AND')) {
-                    result.push(clause);
-                } else if ((clause.length == 1) &&
-                    !(clause[0] instanceof Array)) {
-                    result.push(this._clausify(['rec_name', 'ilike',
-                                this.likify(clause[0])]));
-                } else if ((clause.length == 3) &&
-                    (clause[0].toLowerCase() in this.strings)) {
-                    var name = clause[0];
-                    var operator = clause[1];
-                    var value = clause[2];
-                    var field = this.strings[clause[0].toLowerCase()];
-
-                    var target = null;
-                    if (field.type == 'reference') {
-                        var split = this.split_target_value(field, value);
-                        target = split[0];
-                        value = split[1];
-                    }
-
-                    if (operator === null) {
-                        operator = this.default_operator(field);
-                    }
-                    if (value instanceof Array) {
-                        if (operator == '!') {
-                            operator = 'not in';
-                        } else {
-                            operator = 'in';
-                        }
-                    }
-                    if (operator == '!') {
-                        operator = this.negate_operator(
-                                this.default_operator(field));
-                    }
-                    if (~['integer', 'float', 'numeric', 'datetime', 'date',
-                            'time'].indexOf(field.type)) {
-                        if (value && value.contains('..')) {
-                            var values = value.split('..', 2);
-                            var lvalue = this.convert_value(field, values[0]);
-                            var rvalue = this.convert_value(field, values[1]);
-                            result.push([
-                                    this._clausify([field.name, '>=', lvalue]),
-                                    this._clausify([field.name, '<=', rvalue])
-                                    ]);
-                            return;
-                        }
-                    }
-                    if (value instanceof Array) {
-                        value = value.map(function(v) {
-                            return this.convert_value(field, v);
-                        }.bind(this));
-                    } else {
-                        value = this.convert_value(field, value);
-                    }
-                    if (operator.contains('like')) {
-                        value = this.likify(value);
-                    }
-                    if (target) {
-                        result.push(this._clausify([field.name + '.rec_name',
-                                    operator, value, target]));
-                    } else {
-                        result.push(this._clausify(
-                                    [field.name, operator, value]));
-                    }
-                }
-            }.bind(this));
-            return result;
-        },
-        likify: function(value) {
-            if (!value) {
-                return '%';
-            }
-            var escaped = value.replace('%%', '__');
-            if (escaped.contains('%')) {
-                return value;
-            } else {
-                return '%' + value + '%';
-            }
-        },
-        quote: function(value) {
-            if (typeof value != 'string') {
-                return value;
-            }
-            if (value.contains('\\')) {
-                value = value.replace(new RegExp('\\\\', 'g'), '\\\\');
-            }
-            if (value.contains('"')) {
-                value = value.replace(new RegExp('"', 'g'), '\\"');
-            }
-            var tests = [':', ' ', '(', ')'].concat(this.OPERATORS);
-            for (var i = 0; i < tests.length; i++) {
-                var test = tests[i];
-                if (value.contains(test)) {
-                    return '"' + value + '"';
-                }
-            }
-            return value;
-        },
-        default_operator: function(field) {
-            if (~['char', 'text', 'many2one', 'many2many', 'one2many',
-                    'reference'].indexOf(field.type)) {
-                return 'ilike';
-            } else {
-                return '=';
-            }
-        },
-        negate_operator: function(operator) {
-            switch (operator) {
-                case 'ilike':
-                    return 'not ilike';
-                case '=':
-                    return '!=';
-                case 'in':
-                    return 'not in';
-            }
-        },
-        time_format: function(field) {
-            return new Sao.PYSON.Decoder({}).decode(field.format);
-        },
-        split_target_value: function(field, value) {
-            var target = null;
-            if (typeof value == 'string') {
-                for (var i = 0; i < field.selection.length; i++) {
-                    var selection = field.selection[i];
-                    var key = selection[0];
-                    var text = selection[1];
-                    if (value.toLowerCase().startsWith(
-                                text.toLowerCase() + ',')) {
-                        target = key;
-                        value = value.slice(text.length + 1);
-                        break;
-                    }
-                }
-            }
-            return [target, value];
-        },
-        convert_value: function(field, value) {
-            var convert_selection = function() {
-                if (typeof value == 'string') {
-                    for (var i = 0; i < field.selection.length; i++) {
-                        var selection = field.selection[i];
-                        var key = selection[0];
-                        var text = selection[1];
-                        if (value.toLowerCase() == text.toLowerCase()) {
-                            return key;
-                        }
-                    }
-                }
-                return value;
-            };
-
-            var converts = {
-                'boolean': function() {
-                    if (typeof value == 'string') {
-                        return [Sao.i18n.gettext('y'),
-                            Sao.i18n.gettext('yes'),
-                            Sao.i18n.gettext('true'),
-                            Sao.i18n.gettext('t'),
-                            '1'].some(
-                                function(test) {
-                                    return test.toLowerCase().startsWith(
-                                        value.toLowerCase());
-                                });
-                    } else {
-                        return Boolean(value);
-                    }
-                },
-                'float': function() {
-                    var result = Number(value);
-                    if (isNaN(result) || value === '' || value === null) {
-                        return null;
-                    } else {
-                        return result;
-                    }
-                },
-                'integer': function() {
-                    var result = parseInt(value, 10);
-                    if (isNaN(result)) {
-                        return null;
-                    } else {
-                        return result;
-                    }
-                },
-                'numeric': function() {
-                    var result = new Sao.Decimal(value);
-                    if (isNaN(result.valueOf()) ||
-                            value === '' || value === null) {
-                        return null;
-                    } else {
-                        return result;
-                    }
-                },
-                'selection': convert_selection,
-                'reference': convert_selection,
-                'datetime': function() {
-                    var result = Sao.common.parse_datetime(
-                            Sao.common.date_format(),
-                            this.time_format(field),
-                            value);
-                    if (!result) {
-                        result = Sao.common.parse_date(
-                                Sao.common.date_format(),
-                                value);
-                    }
-                    return result;
-                }.bind(this),
-                'date': function() {
-                    return Sao.common.parse_date(
-                            Sao.common.date_format(),
-                            value);
-                },
-                'time': function() {
-                    try {
-                        return Sao.common.parse_time(this.time_format(field),
-                                value);
-                    } catch (e) {
-                        return null;
-                    }
-                }.bind(this),
-                'timedelta': function() {
-                    var converter = null;
-                    if (field.converter) {
-                        converter = this.context[field.converter];
-                    }
-                    return Sao.common.timedelta.parse(value, converter);
-                }.bind(this),
-                'many2one': function() {
-                    if (value === '') {
-                        return null;
-                    } else {
-                        return value;
-                    }
-                }
-            };
-            var func = converts[field.type];
-            if (func) {
-                return func();
-            } else {
-                return value;
-            }
-        },
-        format_value: function(field, value, target) {
-            if (target === undefined) {
-                target = null;
-            }
-            var format_float = function() {
-                if (!value && value !== 0 && value !== new Sao.Decimal(0)) {
-                    return '';
-                }
-                var digit = String(value).split('.')[1];
-                if (digit) {
-                    digit = digit.length;
-                } else {
-                    digit = 0;
-                }
-                return value.toFixed(digit);
-            };
-            var format_selection = function() {
-                for (var i = 0; i < field.selection.length; i++) {
-                    if (field.selection[i][0] == value) {
-                        return field.selection[i][1];
-                    }
-                }
-                return value || '';
-            };
-
-            var format_reference = function() {
-                if (!target) {
-                    return format_selection();
-                }
-                for (var i = 0; i < field.selection.length; i++) {
-                    if (field.selection[i][0] == target) {
-                        target = field.selection[i][1];
-                        break;
-                    }
-                }
-                return target + ',' + value;
-            };
-
-            var converts = {
-                'boolean': function() {
-                    if (value) {
-                        return Sao.i18n.gettext('True');
-                    } else {
-                        return Sao.i18n.gettext('False');
-                    }
-                },
-                'integer': function() {
-                    if (value || value === 0) {
-                        return '' + parseInt(value, 10);
-                    } else {
-                        return '';
-                    }
-                },
-                'float': format_float,
-                'numeric': format_float,
-                'selection': format_selection,
-                'reference': format_reference,
-                'datetime': function() {
-                    if (!value) {
-                        return '';
-                    }
-                    if (value.isDate ||
-                            !(value.hour() ||
-                                value.minute() ||
-                                value.second())) {
-                        return Sao.common.format_date(
-                                Sao.common.date_format(),
-                                value);
-                    }
-                    return Sao.common.format_datetime(
-                            Sao.common.date_format(),
-                            this.time_format(field),
-                            value);
-                }.bind(this),
-                'date': function() {
-                    return Sao.common.format_date(
-                            Sao.common.date_format(),
-                            value);
-                },
-                'time': function() {
-                    if (!value) {
-                        return '';
-                    }
-                    return Sao.common.format_time(
-                            this.time_format(field),
-                            value);
-                }.bind(this),
-                'timedelta': function() {
-                    if (!value || !value.valueOf()) {
-                        return '';
-                    }
-                    var converter = null;
-                    if (field.converter) {
-                        converter = this.context[field.converter];
-                    }
-                    return Sao.common.timedelta.format(value, converter);
-                }.bind(this),
-                'many2one': function() {
-                    if (value === null) {
-                        return '';
-                    } else {
-                        return value;
-                    }
-                }
-            };
-            if (value instanceof Array) {
-                return value.map(function(v) {
-                    return this.format_value(field, v);
-                }.bind(this)).join(';');
-            } else {
-                var func = converts[field.type];
-                if (func) {
-                    return this.quote(func(value));
-                } else if (value === null) {
-                    return '';
-                } else {
-                    return this.quote(value);
-                }
-            }
-        },
-        simplify: function(value) {
-            if ((value instanceof Array) && !this.is_leaf(value)) {
-                if ((value.length == 1) && (value[0] instanceof Array) &&
-                        ((value[0][0] == 'AND') || (value[0][0] == 'OR') ||
-                         (value[0][0] instanceof Array))) {
-                    return this.simplify(value[0]);
-                } else if ((value.length == 2) &&
-                        ((value[0] == 'AND') || (value[0] == 'OR')) &&
-                        (value[1] instanceof Array)) {
-                    return this.simplify(value[1]);
-                } else if ((value.length == 3) &&
-                        ((value[0] == 'AND') || (value[0] == 'OR')) &&
-                        (value[1] instanceof Array) &&
-                        (value[0] == value[1][0])) {
-                    value = this.simplify(value[1]).concat([value[2]]);
-                }
-                return value.map(this.simplify.bind(this));
-            }
-            return value;
-        }
-    });
-
-    Sao.common.DomainInversion = Sao.class_(Object, {
-        and: function(a, b) {return a && b;},
-        or: function(a, b) {return a || b;},
-        OPERATORS: {
-            '=': function(a, b) {
-                if ((a instanceof Array) && (b instanceof Array)) {
-                    return Sao.common.compare(a, b);
-                } else {
-                    return (a === b);
-                }
-            },
-            '>': function(a, b) {return (a > b);},
-            '<': function(a, b) {return (a < b);},
-            '<=': function(a, b) {return (a <= b);},
-            '>=': function(a, b) {return (a >= b);},
-            '!=': function(a, b) {
-                if ((a instanceof Array) && (b instanceof Array)) {
-                    return !Sao.common.compare(a, b);
-                } else {
-                    return (a !== b);
-                }
-            },
-            'in': function(a, b) {
-                return Sao.common.DomainInversion.in_(a, b);
-            },
-            'not in': function(a, b) {
-                return !Sao.common.DomainInversion.in_(a, b);
-            },
-            // Those operators are not supported (yet ?)
-            'like': function() {return true;},
-            'ilike': function() {return true;},
-            'not like': function() {return true;},
-            'not ilike': function() {return true;},
-            'child_of': function() {return true;},
-            'not child_of': function() {return true;}
-        },
-        locale_part: function(expression, field_name, locale_name) {
-            if (locale_name === undefined) {
-                locale_name = 'id';
-            }
-            if (expression === field_name) {
-                return locale_name;
-            }
-            if (expression.contains('.')) {
-                return expression.split('.').slice(1).join('.');
-            }
-            return expression;
-        },
-        is_leaf: function(expression) {
-            return ((expression instanceof Array) &&
-                (expression.length > 2) &&
-                (typeof expression[1] == 'string'));
-        },
-        eval_leaf: function(part, context, boolop) {
-            if (boolop === undefined) {
-                boolop = this.and;
-            }
-            var field = part[0];
-            var operand = part[1];
-            var value = part[2];
-            if (field.contains('.')) {
-                // In the case where the leaf concerns a m2o then having a
-                // value in the evaluation context is deemed suffisant
-                return Boolean(context[field.split('.')[0]]);
-            }
-            if ((operand == '=') &&
-                    (context[field] === null || context[field] === undefined) &&
-                    (boolop === this.and)) {
-                // We should consider that other domain inversion will set a
-                // correct value to this field
-                return true;
-            }
-            var context_field = context[field];
-            if ((context_field && context_field._isAMomentObject) && !value) {
-                if (context_field.isDateTime) {
-                    value = Sao.DateTime.min;
-                } else {
-                    value = Sao.Date.min;
-                }
-            }
-            if ((value && value._isAMomentObject) && !context_field) {
-                if (value.isDateTime) {
-                    context_field = Sao.DateTime.min;
-                } else {
-                    context_field = Sao.Date.min;
-                }
-            }
-            if ((typeof context_field == 'string') &&
-                    (value instanceof Array) && value.length == 2) {
-                value = value.join(',');
-            } else if ((context_field instanceof Array) &&
-                    (typeof value == 'string') && context_field.length == 2) {
-                context_field = context_field.join(',');
-            }
-            if (~['=', '!='].indexOf(operand) &&
-                    context_field instanceof Array &&
-                    typeof value == 'number') {
-                operand = {
-                    '=': 'in',
-                    '!=': 'not in'
-                }[operand];
-            }
-            if (operand in this.OPERATORS) {
-                return this.OPERATORS[operand](context_field, value);
-            } else {
-                return true;
-            }
-        },
-        inverse_leaf: function(domain) {
-            if (~['AND', 'OR'].indexOf(domain)) {
-                return domain;
-            } else if (this.is_leaf(domain)) {
-                if (domain[1].contains('child_of')) {
-                    if (domain.length == 3) {
-                        return domain;
-                    } else {
-                        return [domain[3]].concat(domain.slice(1));
-                    }
-                }
-                return domain;
-            } else {
-                return domain.map(this.inverse_leaf.bind(this));
-            }
-        },
-        filter_leaf: function(domain, field, model) {
-            if (~['AND', 'OR'].indexOf(domain)) {
-                return domain;
-            } else if (this.is_leaf(domain)) {
-                if (domain[0].startsWith(field) && (domain.length > 3)) {
-                    if (domain[3] !== model) {
-                        return ['id', '=', null];
-                    }
-                }
-                return domain;
-            } else {
-                return domain.map(function(d) {
-                    return this.filter_leaf(d, field, model);
-                }.bind(this));
-            }
-        },
-        eval_domain: function(domain, context, boolop) {
-            if (boolop === undefined) {
-                boolop = this.and;
-            }
-            if (this.is_leaf(domain)) {
-                return this.eval_leaf(domain, context, boolop);
-            } else if (jQuery.isEmptyObject(domain) && boolop == this.and) {
-                return true;
-            } else if (jQuery.isEmptyObject(domain) && boolop == this.or) {
-                return false;
-            } else if (domain[0] == 'AND') {
-                return this.eval_domain(domain.slice(1), context);
-            } else if (domain[0] == 'OR') {
-                return this.eval_domain(domain.slice(1), context, this.or);
-            } else {
-                return boolop(this.eval_domain(domain[0], context),
-                        this.eval_domain(domain.slice(1), context, boolop));
-            }
-        },
-        localize_domain: function(domain, field_name, strip_target) {
-            if (~['AND', 'OR', true, false].indexOf(domain)) {
-                return domain;
-            } else if (this.is_leaf(domain)) {
-                if (domain[1].contains('child_of')) {
-                    if (domain.length == 3) {
-                        return domain;
-                    } else {
-                        return [domain[3]].concat(domain.slice(1, -1));
-                    }
-                }
-                var local_name = 'id';
-                if (typeof domain[2] == 'string') {
-                    local_name = 'rec_name';
-                }
-                var n = strip_target ? 3 : 4;
-                return [this.locale_part(domain[0], field_name, local_name)]
-                    .concat(domain.slice(1, n)).concat(domain.slice(4));
-            } else {
-                return domain.map(function(e) {
-                    return this.localize_domain(e, field_name, strip_target);
-                }.bind(this));
-            }
-        },
-        simplify: function(domain) {
-            if (this.is_leaf(domain)) {
-                return domain;
-            } else if (~['OR', 'AND'].indexOf(domain)) {
-                return domain;
-            } else if ((domain instanceof Array) && (domain.length == 1) &&
-                    (~['OR', 'AND'].indexOf(domain[0]))) {
-                return [];
-            } else if ((domain instanceof Array) && (domain.length == 1) &&
-                    (!this.is_leaf(domain[0]))) {
-                return this.simplify(domain[0]);
-            } else if ((domain instanceof Array) && (domain.length == 2) &&
-                    ~['AND', 'OR'].indexOf(domain[0])) {
-                return [this.simplify(domain[1])];
-            } else {
-                return domain.map(this.simplify.bind(this));
-            }
-        },
-        merge: function(domain, domoperator) {
-            if (jQuery.isEmptyObject(domain) ||
-                    ~['AND', 'OR'].indexOf(domain)) {
-                return [];
-            }
-            var domain_type = domain[0] == 'OR' ? 'OR' : 'AND';
-            if (this.is_leaf(domain)) {
-                return [domain];
-            } else if (domoperator === undefined) {
-                return [domain_type].concat([].concat.apply([],
-                        domain.map(function(e) {
-                            return this.merge(e, domain_type);
-                        }.bind(this))));
-            } else if (domain_type == domoperator) {
-                return [].concat.apply([], domain.map(function(e) {
-                    return this.merge(e, domain_type);
-                }.bind(this)));
-            } else {
-                // without setting the domoperator
-                return [this.merge(domain)];
-            }
-        },
-        concat: function(domains, domoperator) {
-            var result = [];
-            if (domoperator) {
-                result.push(domoperator);
-            }
-            domains.forEach(function append(domain) {
-                if (!jQuery.isEmptyObject(domain)) {
-                    result.push(domain);
-                }
-            });
-            return this.simplify(this.merge(result));
-        },
-        unique_value: function(domain) {
-            if ((domain instanceof Array) &&
-                    (domain.length == 1) &&
-                    !domain[0][0].contains('.') &&
-                    (domain[0][1] == '=')) {
-                return [true, domain[0][1], domain[0][2]];
-            } else {
-                return [false, null, null];
-            }
-        },
-        parse: function(domain) {
-            var And = Sao.common.DomainInversion.And;
-            var Or = Sao.common.DomainInversion.Or;
-            if (this.is_leaf(domain)) {
-                return domain;
-            } else if (jQuery.isEmptyObject(domain)) {
-                return new And([]);
-            } else if (domain[0] === 'OR') {
-                return new Or(domain.slice(1));
-            } else {
-                var begin = 0;
-                if (domain[0] === 'AND') {
-                    begin = 1;
-                }
-                return new And(domain.slice(begin));
-            }
-        },
-        domain_inversion: function(domain, symbol, context) {
-            if (context === undefined) {
-                context = {};
-            }
-            var expression = this.parse(domain);
-            if (!~expression.variables.indexOf(symbol)) {
-                return true;
-            }
-            return expression.inverse(symbol, context);
-        }
-    });
-    Sao.common.DomainInversion.in_ = function(a, b) {
-        if (a instanceof Array) {
-            if (b instanceof Array) {
-                for (var i = 0, len = a.length; i < len; i++) {
-                    if (~b.indexOf(a[i])) {
-                        return true;
-                    }
-                }
-                return false;
-            } else {
-                return Boolean(~a.indexOf(b));
-            }
-        } else {
-            return Boolean(~b.indexOf(a));
-        }
-    };
-    Sao.common.DomainInversion.And = Sao.class_(Object, {
-        init: function(expressions) {
-            this.domain_inversion = new Sao.common.DomainInversion();
-            this.branches = expressions.map(this.domain_inversion.parse.bind(
-                    this.domain_inversion));
-            this.variables = [];
-            for (var i = 0, len = this.branches.length; i < len; i++) {
-                var expression = this.branches[i];
-                if (this.domain_inversion.is_leaf(expression)) {
-                    this.variables.push(this.base(expression[0]));
-                } else if (expression instanceof
-                    Sao.common.DomainInversion.And) {
-                    this.variables = this.variables.concat(
-                        expression.variables);
-                }
-            }
-        },
-        base: function(expression) {
-            if (!expression.contains('.')) {
-                return expression;
-            } else {
-                return expression.split('.')[0];
-            }
-        },
-        inverse: function(symbol, context) {
-            var DomainInversion = Sao.common.DomainInversion;
-            var result = [];
-            for (var i = 0, len = this.branches.length; i < len; i++) {
-                var part = this.branches[i];
-                if (part instanceof DomainInversion.And) {
-                    var part_inversion = part.inverse(symbol, context);
-                    var evaluated = typeof part_inversion == 'boolean';
-                    if (!evaluated) {
-                        result.push(part_inversion);
-                    } else if (part_inversion) {
-                        continue;
-                    } else {
-                        return false;
-                    }
-                } else if (this.domain_inversion.is_leaf(part) &&
-                        (this.base(part[0]) === symbol)) {
-                    result.push(part);
-                } else {
-                    var field = part[0];
-                    if ((!(field in context)) ||
-                            ((field in context) &&
-                             this.domain_inversion.eval_leaf(part, context,
-                                 this.domain_inversion.and))) {
-                        result.push(true);
-                    } else {
-                        return false;
-                    }
-                }
-            }
-            result = result.filter(function(e) {
-                return e !== true;
-            });
-            if (jQuery.isEmptyObject(result)) {
-                return true;
-            } else {
-                return this.domain_inversion.simplify(result);
-            }
-        }
-    });
-    Sao.common.DomainInversion.Or = Sao.class_(Sao.common.DomainInversion.And, {
-        inverse: function(symbol, context) {
-            var DomainInversion = Sao.common.DomainInversion;
-            var result = [];
-            if (!~this.variables.indexOf(symbol) &&
-                !jQuery.isEmptyObject(this.variables.filter(function(e) {
-                    return !(e in context);
-                }))) {
-                // In this case we don't know anything about this OR part, we
-                // consider it to be True (because people will have the
-                // constraint on this part later).
-                return true;
-            }
-            for (var i = 0, len = this.branches.length; i < len; i++) {
-                var part = this.branches[i];
-                if (part instanceof DomainInversion.And) {
-                    var part_inversion = part.inverse(symbol, context);
-                    var evaluated = typeof part_inversion == 'boolean';
-                    if (!~this.variables.indexOf(symbol)) {
-                        if (evaluated && part_inversion) {
-                            return true;
-                        }
-                        continue;
-                    }
-                    if (!evaluated) {
-                        result.push(part_inversion);
-                    } else if (part_inversion) {
-                        return true;
-                    } else {
-                        continue;
-                    }
-                } else if (this.domain_inversion.is_leaf(part) &&
-                        (this.base(part[0]) == symbol)) {
-                    result.push(part);
-                } else {
-                    var field = part[0];
-                    field = this.base(field);
-                    if ((field in context) &&
-                            this.domain_inversion.eval_leaf(part, context,
-                                this.domain_inversion.or)) {
-                        return true;
-                    } else if ((field in context) &&
-                            !this.domain_inversion.eval_leaf(part, context,
-                                this.domain_inversion.or)) {
-                        result.push(false);
-                    }
-                }
-            }
-            result = result.filter(function(e) {
-                return e !== false;
-            });
-            if (jQuery.isEmptyObject(result)) {
-                return false;
-            } else {
-                return this.domain_inversion.simplify(['OR'].concat(result));
-            }
-        }
-    });
-
-    Sao.common.guess_mimetype = function(filename) {
-        if (/.*odt$/.test(filename)) {
-            return 'application/vnd.oasis.opendocument.text';
-        } else if (/.*ods$/.test(filename)) {
-            return 'application/vnd.oasis.opendocument.spreadsheet';
-        } else if (/.*pdf$/.test(filename)) {
-            return 'application/pdf';
-        } else if (/.*docx$/.test(filename)) {
-            return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
-        } else if (/.*doc/.test(filename)) {
-            return 'application/msword';
-        } else if (/.*xlsx$/.test(filename)) {
-            return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
-        } else if (/.*xls/.test(filename)) {
-            return 'application/vnd.ms-excel';
-        } else {
-            return 'application/octet-binary';
-        }
-    };
-
-    Sao.common.LOCAL_ICONS = [
-        'tryton-attachment-hi',
-        'tryton-attachment',
-        'tryton-bookmark',
-        'tryton-cancel',
-        'tryton-clear',
-        'tryton-close',
-        'tryton-connect',
-        'tryton-copy',
-        'tryton-delete',
-        'tryton-dialog-error',
-        'tryton-dialog-information',
-        'tryton-dialog-warning',
-        'tryton-disconnect',
-        'tryton-executable',
-        'tryton-find-replace',
-        'tryton-find',
-        'tryton-folder-new',
-        'tryton-fullscreen',
-        'tryton-go-home',
-        'tryton-go-jump',
-        'tryton-go-next',
-        'tryton-go-previous',
-        'tryton-help',
-        'tryton-icon',
-        'tryton-list-add',
-        'tryton-list-remove',
-        'tryton-locale',
-        'tryton-lock',
-        'tryton-log-out',
-        'tryton-mail-message-new',
-        'tryton-mail-message',
-        'tryton-new',
-        'tryton-ok',
-        'tryton-open',
-        'tryton-preferences-system-session',
-        'tryton-preferences-system',
-        'tryton-preferences',
-        'tryton-print-email',
-        'tryton-print-open',
-        'tryton-print',
-        'tryton-refresh',
-        'tryton-save-as',
-        'tryton-save',
-        'tryton-star',
-        'tryton-start-here',
-        'tryton-system-file-manager',
-        'tryton-system',
-        'tryton-text-background',
-        'tryton-text-foreground',
-        'tryton-text-markup',
-        'tryton-undo',
-        'tryton-unstar',
-        'tryton-web-browser'
-    ];
-
-    Sao.common.IconFactory = Sao.class_(Object, {
-        batchnum: 10,
-        name2id: {},
-        loaded_icons: {},
-        tryton_icons: [],
-        register_prm: jQuery.when(),
-        load_icons: function(refresh) {
-            refresh = refresh || false;
-            if (!refresh) {
-                for (var icon_name in this.load_icons) {
-                    if (!this.load_icons.hasOwnProperty(icon_name)) {
-                        continue;
-                    }
-                    window.URL.revokeObjectURL(this.load_icons[icon_name]);
-                }
-            }
-
-            var icon_model = new Sao.Model('ir.ui.icon');
-            return icon_model.execute('list_icons', [], {})
-            .then(function(icons) {
-                if (!refresh) {
-                    this.name2id = {};
-                    this.loaded_icons = {};
-                }
-                this.tryton_icons = [];
-
-                var icon_id, icon_name;
-                for (var i=0, len=icons.length; i < len; i++) {
-                    icon_id = icons[i][0];
-                    icon_name = icons[i][1];
-                    if (refresh && (icon_name in this.loaded_icons)) {
-                        continue;
-                    }
-                    this.tryton_icons.push([icon_id, icon_name]);
-                    this.name2id[icon_name] = icon_id;
-                }
-            }.bind(this));
-        },
-        register_icon: function(icon_name) {
-            if (!icon_name) {
-                return jQuery.when('');
-            } else if ((icon_name in this.loaded_icons) ||
-                    ~Sao.common.LOCAL_ICONS.indexOf(icon_name)) {
-                return jQuery.when(this.get_icon_url(icon_name));
-            }
-            if (this.register_prm.state() == 'pending') {
-                var waiting_prm = jQuery.Deferred();
-                this.register_prm.then(function() {
-                    this.register_icon(icon_name).then(
-                        waiting_prm.resolve, waiting_prm.reject);
-                }.bind(this));
-                return waiting_prm;
-            }
-            var loaded_prm;
-            if (!(icon_name in this.name2id)) {
-                loaded_prm = this.load_icons(true);
-            } else {
-                loaded_prm = jQuery.when();
-            }
-
-            var icon_model = new Sao.Model('ir.ui.icon');
-            this.register_prm = loaded_prm.then(function () {
-                var find_array = function(array) {
-                    var idx, l;
-                    for (idx=0, l=this.tryton_icons.length; idx < l; idx++) {
-                        var icon = this.tryton_icons[idx];
-                        if (Sao.common.compare(icon, array)) {
-                            break;
-                        }
-                    }
-                    return idx;
-                }.bind(this);
-                var idx = find_array([this.name2id[icon_name], icon_name]);
-                var from = Math.round(idx - this.batchnum / 2);
-                from = (from < 0) ? 0 : from;
-                var to = Math.round(idx + this.batchnum / 2);
-                var ids = [];
-                this.tryton_icons.slice(from, to).forEach(function(e) {
-                    ids.push(e[0]);
-                });
-
-                var read_prm = icon_model.execute('read',
-                    [ids, ['name', 'icon']], {});
-                return read_prm.then(function(icons) {
-                    icons.forEach(function(icon) {
-                        var img_url;
-                        if (navigator.userAgent.match(/firefox/i)) {
-                            // Fixefox doesn't support SVG inside Blob
-                            // https://bugzilla.mozilla.org/show_bug.cgi?id=841920
-                            // Temporary use the embeded base64 version which
-                            // will be replaced later by the URL of the png object
-                            img_url = 'data:image/svg+xml;base64,' +
-                                window.btoa(unescape(encodeURIComponent(icon.icon)));
-                            var image = new Image();
-                            image.src = img_url;
-                            image.onload = function() {
-                                var canvas = document.createElement('canvas');
-                                canvas.width = image.width;
-                                canvas.height = image.height;
-                                var context = canvas.getContext('2d');
-                                context.drawImage(image, 0, 0);
-                                canvas.toBlob(function(blob) {
-                                    var old_img_url = img_url;
-                                    img_url =  window.URL.createObjectURL(blob);
-                                    this.loaded_icons[icon.name] = img_url;
-                                    jQuery(document).find('img').each(function(i, el) {
-                                        if (el.src == old_img_url) {
-                                            el.src = img_url;
-                                        }
-                                    });
-                                    canvas.remove();
-                                }.bind(this), 'image/png');
-                            }.bind(this);
-                        } else {
-                            var blob = new Blob([icon.icon],
-                                {type: 'image/svg+xml'});
-                            img_url = window.URL.createObjectURL(blob);
-                        }
-                        this.loaded_icons[icon.name] = img_url;
-                        delete this.name2id[icon.name];
-                        this.tryton_icons.splice(
-                            find_array([icon.id, icon.name]), 1);
-                    }.bind(this));
-                    return this.get_icon_url(icon_name);
-                }.bind(this));
-            }.bind(this));
-            return this.register_prm;
-        },
-        get_icon_url: function(icon_name) {
-            if (icon_name in this.loaded_icons) {
-                return this.loaded_icons[icon_name];
-            }
-            return "images/" + icon_name + ".svg";
-        }
-    });
-
-    Sao.common.ICONFACTORY = new Sao.common.IconFactory();
-
-    Sao.common.UniqueDialog = Sao.class_(Object, {
-        init: function() {
-            this.running = false;
-        },
-        build_dialog: function() {
-            var dialog = new Sao.Dialog('', this.class_);
-            return dialog;
-        },
-        run: function() {
-            if (this.running) {
-                return;
-            }
-            var args = Array.prototype.slice.call(arguments);
-            var prm = jQuery.Deferred();
-            args.push(prm);
-            var dialog = this.build_dialog.apply(this, args);
-            this.running = true;
-            dialog.modal.modal('show');
-            return prm;
-        },
-        close: function(dialog) {
-            dialog.modal.on('hidden.bs.modal', function(event) {
-                jQuery(this).remove();
-            });
-            dialog.modal.modal('hide');
-            this.running = false;
-        }
-    });
-
-    Sao.common.MessageDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'message-dialog',
-        build_dialog: function(message, icon, prm) {
-            var dialog = Sao.common.MessageDialog._super.build_dialog.call(
-                this);
-            dialog.header.remove();
-            dialog.body.append(jQuery('<div/>', {
-                'class': 'alert alert-info',
-                role: 'alert'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon ' + icon,
-                'aria-hidden': true
-            })).append(jQuery('<span/>', {
-                'class': 'sr-only'
-            }).append(Sao.i18n.gettext('Message: '))
-            ).append(jQuery('<span/>')
-                .append(message)
-                .css('white-space', 'pre-wrap')));
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('OK')).click(function() {
-                this.close(dialog);
-                prm.resolve('ok');
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        },
-        run: function(message, icon) {
-            return Sao.common.MessageDialog._super.run.call(
-                    this, message, icon || 'glyphicon-info-sign');
-        }
-    });
-    Sao.common.message = new Sao.common.MessageDialog();
-
-    Sao.common.WarningDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'warning-dialog',
-        build_dialog: function(message, title, prm) {
-            var dialog = Sao.common.WarningDialog._super.build_dialog.call(
-                this);
-            dialog.add_title(title);
-            dialog.body.append(jQuery('<div/>', {
-                'class': 'alert alert-warning',
-                role: 'alert'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-alert',
-                'aria-hidden': true
-            })).append(jQuery('<span/>', {
-                'class': 'sr-only'
-            }).append(Sao.i18n.gettext('Warning: '))
-            ).append(jQuery('<span/>')
-                .append(message)
-                .css('white-space', 'pre-wrap')));
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('OK')).click(function() {
-                this.close(dialog);
-                prm.resolve('ok');
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.warning = new Sao.common.WarningDialog();
-
-    Sao.common.UserWarningDialog = Sao.class_(Sao.common.WarningDialog, {
-        class_: 'user-warning-dialog',
-        always: false,
-        _set_always: function() {
-            this.always = jQuery(this).prop('checked');
-        },
-        build_dialog: function(message, title, prm) {
-            var dialog = Sao.common.UserWarningDialog._super.build_dialog.call(
-                this, message, title, prm);
-            dialog.body.append(jQuery('<div/>')
-                .append(jQuery('<input/>', {
-                    'type': 'checkbox'
-                }).change(this._set_always.bind(this)))
-                .append(jQuery('<span/>')
-                    .text(Sao.i18n.gettext('Always ignore this warning.')))
-                );
-            dialog.body.append(jQuery('<p/>')
-                    .text(Sao.i18n.gettext('Do you want to proceed?')));
-            dialog.footer.children().remove();
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('No')).click(function() {
-                this.close(dialog);
-                prm.reject();
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Yes')).click(function() {
-                this.close(dialog);
-                if (this.always) {
-                    prm.resolve('always');
-                }
-                prm.resolve('ok');
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.userwarning = new Sao.common.UserWarningDialog();
-
-    Sao.common.ConfirmationDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'confirmation-dialog',
-        build_dialog: function(message) {
-            var dialog = Sao.common.ConfirmationDialog._super.build_dialog.call(
-                this);
-            dialog.header.remove();
-            dialog.body.append(jQuery('<div/>', {
-                'class': 'alert alert-info',
-                role: 'alert'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-info-sign',
-                'aria-hidden': true
-            })).append(jQuery('<span/>', {
-                'class': 'sr-only'
-            }).append(Sao.i18n.gettext('Confirmation: '))
-            ).append(jQuery('<span/>')
-                .append(message)
-                .css('white-space', 'pre-wrap')));
-            return dialog;
-        }
-    });
-
-    Sao.common.SurDialog = Sao.class_(Sao.common.ConfirmationDialog, {
-        build_dialog: function(message, prm) {
-            var dialog = Sao.common.SurDialog._super.build_dialog.call(
-                this, message);
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.close(dialog);
-                prm.reject();
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('OK')).click(function() {
-                this.close(dialog);
-                prm.resolve();
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.sur = new Sao.common.SurDialog();
-
-    Sao.common.Sur3BDialog = Sao.class_(Sao.common.ConfirmationDialog, {
-        build_dialog: function(message, prm) {
-            var dialog = Sao.common.SurDialog._super.build_dialog.call(
-                this, message);
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.close(dialog);
-                prm.resolve('cancel');
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('No')).click(function() {
-                this.close(dialog);
-                prm.resolve('ko');
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Yes')).click(function() {
-                this.close(dialog);
-                prm.resolve('ok');
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.sur_3b = new Sao.common.Sur3BDialog();
-
-    Sao.common.AskDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'ask-dialog',
-        run: function() {
-            var args = Array.prototype.slice.call(arguments);
-            if (args.length == 1) {
-                args.push(true);
-            }
-            return Sao.common.AskDialog._super.run.apply(this, args);
-        },
-        build_dialog: function(question, visibility, prm) {
-            var dialog = Sao.common.AskDialog._super.build_dialog.call(this);
-            dialog.header.remove();
-            var entry = jQuery('<input/>', {
-                'class': 'form-control',
-                'type': visibility ? 'input' : 'password',
-                'id': 'ask-dialog-entry'
-            });
-            var body = dialog.find('.modal-body');
-            body.append(jQuery('<div/>', {
-                'class': 'form-group'
-            }).append(jQuery('<label/>', {
-                'for': 'ask-dialog-entry'
-            }).append(question)).append(entry));
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.close(dialog);
-                prm.reject();
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('OK')).click(function() {
-                this.close(dialog);
-                prm.resolve(entry.val());
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.ask = new Sao.common.AskDialog();
-
-    Sao.common.ConcurrencyDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'ask-dialog',
-        build_dialog: function(model, record_id, context, prm) {
-            var dialog = Sao.common.ConcurrencyDialog._super.build_dialog.call(
-                this);
-            dialog.modal.find('.modal-dialog'
-                ).removeClass('modal-sm').addClass('modal-lg');
-            dialog.add_title(Sao.i18n.gettext('Concurrency Exception'));
-            dialog.body.append(jQuery('<div/>', {
-                'class': 'alert alert-warning',
-                role: 'alert'
-            }).append(jQuery('<p/>')
-                .append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-info-sign',
-                    'aria-hidden': true
-                })).append(jQuery('<span/>', {
-                    'class': 'sr-only'
-                }).append(Sao.i18n.gettext('Write Concurrency Warning: '))
-                ).append(Sao.i18n.gettext('This record has been modified ' +
-                'while you were editing it.')))
-                .append(jQuery('<p/>').text(Sao.i18n.gettext('Choose:')))
-                .append(jQuery('<ul/>')
-                    .append(jQuery('<li/>')
-                        .text(Sao.i18n.gettext('"Cancel" to cancel saving;')))
-                    .append(jQuery('<li/>')
-                        .text(Sao.i18n.gettext(
-                                '"Compare" to see the modified version;')))
-                    .append(jQuery('<li/>')
-                        .text(Sao.i18n.gettext(
-                                '"Write Anyway" to save your current version.'))))
-                );
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.close(dialog);
-                prm.reject();
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Compare')).click(function() {
-                this.close(dialog);
-                Sao.Tab.create({
-                    'model': model,
-                    'res_id': record_id,
-                    'domain': [['id', '=', record_id]],
-                    'context': context,
-                    'mode': ['form', 'tree']
-                });
-                prm.reject();
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Write Anyway')).click(function() {
-                this.close(dialog);
-                prm.resolve();
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.concurrency = new Sao.common.ConcurrencyDialog();
-
-    Sao.common.ErrorDialog = Sao.class_(Sao.common.UniqueDialog, {
-        class_: 'error-dialog',
-        build_dialog: function(title, details, prm) {
-            var dialog = Sao.common.ConcurrencyDialog._super.build_dialog.call(
-                this);
-            dialog.modal.find('.modal-dialog'
-                ).removeClass('modal-sm').addClass('modal-lg');
-            dialog.add_title(Sao.i18n.gettext('Application Error'));
-            dialog.body.append(jQuery('<div/>', {
-                'class': 'alert alert-warning',
-                role: 'alert'
-            }).append(jQuery('<span/>', {
-                'class': 'glyphicon glyphicon-alert',
-                'aria-hidden': true
-            })).append(jQuery('<span/>', {
-                'class': 'sr-only'
-            }).append(Sao.i18n.gettext('Warning: '))
-            ).append(jQuery('<p/>')
-                .append(jQuery('<pre/>')
-                    .text(details)))
-                .append(jQuery('<p/>')
-                    .append(jQuery('<a/>', {
-                        'class': 'btn btn-link',
-                        href: Sao.config.roundup.url,
-                        target: '_blank'
-                    }).text(Sao.i18n.gettext('Report Bug')))));
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Close')).click(function() {
-                this.close(dialog);
-                prm.resolve();
-            }.bind(this)).appendTo(dialog.footer);
-            return dialog;
-        }
-    });
-    Sao.common.error = new Sao.common.ErrorDialog();
-
-    Sao.common.Processing = Sao.class_(Object, {
-        queries: 0,
-        timeout: 500,
-        init: function() {
-            this.el = jQuery('<div/>', {
-                'id': 'processing',
-                'class': 'text-center'
-            });
-            this.el.append(jQuery('<span/>', {
-                'class': 'label label-info',
-                'text': 'Processing...'
-            }));
-            this.el.hide();
-            jQuery(function() {
-                this.el.appendTo('body');
-            }.bind(this));
-        },
-        show: function() {
-            return window.setTimeout(function() {
-                this.queries += 1;
-                this.el.show();
-            }.bind(this), this.timeout);
-        },
-        hide: function(timeoutID) {
-            window.clearTimeout(timeoutID);
-            if (this.queries > 0) {
-                this.queries -= 1;
-            }
-            if (this.queries <= 0) {
-                this.queries = 0;
-                this.el.hide();
-            }
-        }
-    });
-    Sao.common.processing = new Sao.common.Processing();
-
-    Sao.common.InputCompletion = Sao.class_(Object, {
-        init: function(el, source, match_selected, format) {
-            if (!el.is('input')) {
-                el.addClass('dropdown');
-                this.dropdown = el;
-            } else {
-                el.wrap('<div class="dropdown"/>');
-                this.dropdown = el.parent();
-            }
-            this.input = el.find('input').add(el.filter('input')).first();
-            this.input.attr('autocomplete', 'off');
-            // bootstrap requires an element with data-toggle
-            jQuery('<span/>', {
-                'data-toggle': 'dropdown'
-            }).appendTo(this.dropdown);
-            this.menu = jQuery('<ul/>', {
-                'class': 'dropdown-menu',
-                'role': 'listbox'
-            }).appendTo(this.dropdown);
-            this.separator = jQuery('<li/>', {
-                'role': 'separator',
-                'class': 'divider'
-            }).appendTo(this.menu);
-            this.separator.hide();
-
-            this.source = source;
-            this.match_selected = match_selected;
-            this.format = format;
-            this.action_activated = null;
-
-            this._search_text = null;
-
-            this.input.on('input', function() {
-                window.setTimeout(this._input.bind(this), 300,
-                        this.input.val());
-            }.bind(this));
-            this.input.keydown(function(evt) {
-                if (evt.which == Sao.common.ESC_KEYCODE) {
-                    if (this.dropdown.hasClass('open')) {
-                        this.menu.dropdown('toggle');
-                    }
-                } else if (evt.which == Sao.common.RETURN_KEYCODE) {
-                    if (!this.dropdown.hasClass('open')) {
-                        this.menu.dropdown('toggle');
-                    }
-                }
-            }.bind(this));
-            // We must set the overflow of the treeview containing the input to
-            // visible to prevent vertical scrollbar inherited from the auto
-            // overflow-x
-            // (see http://www.w3.org/TR/css-overflow-3/#overflow-properties)
-            this.dropdown.on('hide.bs.dropdown', function() {
-                this.input.focus();
-                this.input.closest('.treeview').css('overflow', '');
-            }.bind(this));
-            this.dropdown.on('show.bs.dropdown', function() {
-                this.input.closest('.treeview').css('overflow', 'visible');
-            }.bind(this));
-        },
-        set_actions: function(actions, action_activated) {
-            if (action_activated !== undefined) {
-                this.action_activated = action_activated;
-            }
-            this.menu.find('li.action').remove();
-            if (jQuery.isEmptyObject(actions)) {
-                this.separator.hide();
-                return;
-            }
-            this.separator.show();
-            actions.forEach(function(action) {
-                var action_id = action[0];
-                var content = action[1];
-                jQuery('<li/>', {
-                    'class': 'action action-' + action_id
-                }).append(jQuery('<a/>', {
-                    'href': '#'
-                }).append(this._format_action(content)))
-                .click(function() {
-                    if (this.action_activated) {
-                        this.action_activated(action_id);
-                    }
-                    this.input.val('');
-                }.bind(this))
-                .appendTo(this.menu);
-            }, this);
-        },
-        _format: function(content) {
-            if (this.format) {
-                return this.format(content);
-            }
-            return content;
-        },
-        _format_action: function(content) {
-            if (this.format_action) {
-                return this.format_action(content);
-            }
-            return content;
-        },
-        _input: function(text) {
-            if (text != this.input.val()) {
-                return;
-            }
-            var prm;
-            if (this.source instanceof Array) {
-                prm = jQuery.when(source.filter(function(value) {
-                    return value.toLowerCase().startsWith(text.toLowerCase());
-                }));
-            } else {
-                prm = this.source(text);
-            }
-            prm.then(function(values) {
-                if (text != this.input.val()) {
-                    return;
-                }
-                this._set_selection(values);
-            }.bind(this));
-        },
-        _set_selection: function(values) {
-            if (values === undefined) {
-                values = [];
-            }
-            this.menu.find('li.completion').remove();
-            values.reverse().forEach(function(value) {
-                jQuery('<li/>', {
-                    'class': 'completion'
-                }).append(jQuery('<a/>', {
-                    'href': '#'
-                }).append(this._format(value)))
-                .click(function() {
-                    if (this.match_selected) {
-                        this.match_selected(value);
-                    }
-                    this.input.focus();
-                }.bind(this)).prependTo(this.menu);
-            }, this);
-            if (!this.input.val()) {
-                if (this.dropdown.hasClass('open')) {
-                    this.menu.dropdown('toggle');
-                }
-            } else {
-                if (!this.dropdown.hasClass('open')) {
-                    this.menu.dropdown('toggle');
-                }
-            }
-        }
-    });
-
-    Sao.common.get_completion = function(el, source,
-            match_selected, action_activated) {
-        var format = function(content) {
-            return content.rec_name;
-        };
-        var completion = new Sao.common.InputCompletion(
-                el, source, match_selected, format);
-        completion.set_actions([
-                ['search', Sao.i18n.gettext('Search...')],
-                ['create', Sao.i18n.gettext('Create..')]],
-                action_activated);
-    };
-
-    Sao.common.update_completion = function(
-            entry, record, field, model, domain) {
-        var search_text = entry.val();
-        if (!search_text || !model) {
-            return jQuery.when();
-        }
-        if (domain === undefined) {
-            domain = field.get_domain(record);
-        }
-        var context = field.get_context(record);
-        domain = [['rec_name', 'ilike', '%' + search_text + '%'], domain];
-
-        var sao_model = new Sao.Model(model);
-        return sao_model.execute('search_read',
-                [domain, 0, Sao.config.limit, null, ['rec_name']], context);
-    };
-
-    Sao.common.Paned = Sao.class_(Object, {
-        init: function(orientation) {
-            var row;
-            this._orientation = orientation;
-            this.el = jQuery('<div/>');
-            if (orientation == 'horizontal') {
-                row = jQuery('<div/>', {
-                    'class': 'row'
-                }).appendTo(this.el);
-                this.child1 = jQuery('<div/>', {
-                    'class': 'col-md-6'
-                }).appendTo(row);
-                this.child2 = jQuery('<div/>', {
-                    'class': 'col-md-6'
-                }).appendTo(row);
-            } else if (orientation == 'vertical') {
-                this.child1 = jQuery('<div/>', {
-                    'class': 'row'
-                }).appendTo(this.el);
-                this.child2 = jQuery('<div/>', {
-                    'class': 'row'
-                }).appendTo(this.el);
-            }
-        },
-        get_child1: function() {
-            return this.child1;
-        },
-        get_child2: function() {
-            return this.child2;
-        }
-    });
-
-    Sao.common.get_focus_chain = function(element) {
-        var elements = element.find('input', 'textarea');
-        elements.sort(function(a, b) {
-            if (('tabindex' in a.attributes) && ('tabindex' in b.attributes)) {
-                var a_tabindex = parseInt(a.attributes.tabindex.value);
-                var b_tabindex = parseInt(b.attributes.tabindex.value);
-                return a_tabindex - b_tabindex;
-            } else if ('tabindex' in a.attributes) {
-                return -1;
-            } else if ('tabindex' in b.attributes) {
-                return 1;
-            } else {
-                return 0;
-            }
-        });
-        return elements;
-    };
-
-    Sao.common.find_focusable_child = function(element) {
-        var i, len, children, focusable;
-
-        if (!element.is(':visible')) {
-            return null;
-        }
-        if (~['input', 'select', 'textarea'].indexOf(
-                    element[0].tagName.toLowerCase())) {
-            return element;
-        }
-
-        children = Sao.common.get_focus_chain(element);
-        for (i = 0, len = children.length; i < len; i++) {
-            focusable = Sao.common.find_focusable_child(jQuery(children[i]));
-            if (focusable) {
-                return focusable;
-            }
-        }
-    };
-
-    Sao.common.find_first_focus_widget = function(ancestor, widgets) {
-        var i, j;
-        var children, commons, is_common;
-
-        if (widgets.length == 1) {
-            return jQuery(widgets[0]);
-        }
-        children = Sao.common.get_focus_chain(ancestor);
-        for (i = 0; i < children.length; i++) {
-            commons = [];
-            for (j = 0; j < widgets.length; j++) {
-                is_common = jQuery(widgets[j]).closest(children[i]).length > 0;
-                if (is_common) {
-                    commons.push(widgets[j]);
-                }
-            }
-            if (commons.length > 0) {
-                return Sao.common.find_first_focus_widget(jQuery(children[i]),
-                        commons);
-            }
-        }
-    };
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Window = {};
-
-    Sao.Window.InfoBar = Sao.class_(Object, {
-        init: function() {
-            this.text = jQuery('<span/>');
-            this.text.css('white-space', 'pre-wrap');
-            this.el = jQuery('<div/>', {
-                'class': 'alert',
-                'role': 'alert'
-            }).append(jQuery('<button/>', {
-                'type': 'button',
-                'class': 'close',
-                'aria-label': Sao.i18n.gettext('Close')
-            }).append(jQuery('<span/>', {
-                'aria-hidden': true
-            }).append('×')).click(function() {
-                this.el.hide();
-            }.bind(this))).append(this.text);
-            this.el.hide();
-        },
-        message: function(message, type) {
-            if (message) {
-                this.el.removeClass(
-                        'alert-success alert-info alert-warning alert-danger');
-                this.el.addClass('alert-' + (type || 'info'));
-                this.text.text(message);
-                this.el.show();
-            } else {
-                this.el.hide();
-            }
-        }
-    });
-
-    Sao.Window.Form = Sao.class_(Object, {
-        init: function(screen, callback, kwargs) {
-            kwargs = kwargs || {};
-            this.screen = screen;
-            this.callback = callback;
-            this.many = kwargs.many || 0;
-            this.domain = kwargs.domain || null;
-            this.context = kwargs.context || null;
-            this.save_current = kwargs.save_current;
-            this.prev_view = screen.current_view;
-            this.screen.screen_container.alternate_view = true;
-            this.info_bar = new Sao.Window.InfoBar();
-            var view_type = kwargs.view_type || 'form';
-
-            var form_prm = jQuery.when();
-            var screen_views = [];
-            for (var i = 0, len = this.screen.views.length; i < len; i++) {
-                screen_views.push(this.screen.views[i].view_type);
-            }
-            if (!~screen_views.indexOf(view_type) &&
-                !~this.screen.view_to_load.indexOf(view_type)) {
-                form_prm = this.screen.add_view_id(null, view_type);
-            }
-
-            var switch_prm = form_prm.then(function() {
-                return this.screen.switch_view(view_type).done(function() {
-                    if (kwargs.new_) {
-                        this.screen.new_();
-                    }
-                }.bind(this));
-            }.bind(this));
-            var dialog = new Sao.Dialog('', '', 'lg');
-            this.el = dialog.modal;
-
-            dialog.body.append(this.info_bar.el);
-
-            var readonly = (this.screen.attributes.readonly ||
-                    this.screen.group.get_readonly());
-
-            if (view_type == 'form') {
-                dialog.footer.append(jQuery('<button/>', {
-                    'class': 'btn btn-link',
-                    'type': 'button'
-                }).append(!kwargs.new_ && this.screen.current_record.id < 0 ?
-                    Sao.i18n.gettext('Delete') : Sao.i18n.gettext('Cancel'))
-                        .click(function() {
-                            this.response('RESPONSE_CANCEL');
-                        }.bind(this)));
-            }
-
-            if (kwargs.new_ && this.many) {
-                dialog.footer.append(jQuery('<button/>', {
-                    'class': 'btn btn-default',
-                    'type': 'button'
-                }).append(Sao.i18n.gettext('New')).click(function() {
-                    this.response('RESPONSE_ACCEPT');
-                }.bind(this)));
-            }
-
-            if (this.save_current) {
-                dialog.footer.append(jQuery('<button/>', {
-                    'class': 'btn btn-primary',
-                    'type': 'submit'
-                }).append(Sao.i18n.gettext('Save')));
-            } else {
-                dialog.footer.append(jQuery('<button/>', {
-                    'class': 'btn btn-primary',
-                    'type': 'submit'
-                }).append(Sao.i18n.gettext('OK')));
-            }
-            dialog.content.submit(function(e) {
-                this.response('RESPONSE_OK');
-                e.preventDefault();
-            }.bind(this));
-
-            if (view_type == 'tree') {
-                var menu = jQuery('<div/>').appendTo(dialog.body);
-                var group = jQuery('<div/>', {
-                    'class': 'input-group input-group-sm'
-                }).appendTo(menu);
-
-                this.wid_text = jQuery('<input/>', {
-                    type: 'input'
-                }).appendTo(menu);
-                this.wid_text.hide();
-
-                var buttons = jQuery('<div/>', {
-                    'class': 'input-group-btn'
-                }).appendTo(group);
-                var access = Sao.common.MODELACCESS.get(this.screen.model_name);
-                if (this.domain) {
-                    this.wid_text.show();
-
-                    this.but_add = jQuery('<button/>', {
-                        'class': 'btn btn-default btn-sm',
-                        'type': 'button',
-                        'aria-label': Sao.i18n.gettext('Add')
-                    }).append(jQuery('<span/>', {
-                        'class': 'glyphicon glyphicon-plus'
-                    })).appendTo(buttons);
-                    this.but_add.click(this.add.bind(this));
-                    this.but_add.prop('disabled', !access.read || readonly);
-
-                    this.but_remove = jQuery('<button/>', {
-                        'class': 'btn btn-default btn-sm',
-                        'type': 'button',
-                        'aria-label': Sao.i18n.gettext('Remove')
-                    }).append(jQuery('<span/>', {
-                        'class': 'glyphicon glyphicon-minus'
-                    })).appendTo(buttons);
-                    this.but_remove.click(this.remove.bind(this));
-                    this.but_remove.prop('disabled', !access.read || readonly);
-                }
-
-                this.but_new = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('New')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-edit'
-                })).appendTo(buttons);
-                this.but_new.click(this.new_.bind(this));
-                this.but_new.prop('disabled', !access.create || readonly);
-
-                this.but_del = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('Delete')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-trash'
-                })).appendTo(buttons);
-                this.but_del.click(this.delete_.bind(this));
-                this.but_del.prop('disabled', !access['delete'] || readonly);
-
-                this.but_undel = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('Undelete')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-repeat'
-                })).appendTo(buttons);
-                this.but_undel.click(this.undelete.bind(this));
-                this.but_undel.prop('disabled', !access['delete'] || readonly);
-
-                this.but_previous = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('Previous')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-chevron-left'
-                })).appendTo(buttons);
-                this.but_previous.click(this.previous.bind(this));
-
-                this.label = jQuery('<span/>', {
-                    'class': 'btn'
-                }).appendTo(buttons);
-                this.label.text('(0, 0)');
-
-                this.but_next = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('Next')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-chevron-right'
-                })).appendTo(buttons);
-                this.but_next.click(this.next.bind(this));
-
-                this.but_switch = jQuery('<button/>', {
-                    'class': 'btn btn-default btn-sm',
-                    'type': 'button',
-                    'aria-label': Sao.i18n.gettext('Switch')
-                }).append(jQuery('<span/>', {
-                    'class': 'glyphicon glyphicon-list-alt'
-                })).appendTo(buttons);
-                this.but_switch.click(this.switch_.bind(this));
-            }
-
-
-            switch_prm.done(function() {
-                dialog.add_title(this.screen.current_view.attributes.string);
-                dialog.body.append(this.screen.screen_container.alternate_viewport);
-                this.el.modal('show');
-            }.bind(this));
-            this.el.on('shown.bs.modal', function(event) {
-                this.screen.display().done(function() {
-                    this.screen.set_cursor();
-                }.bind(this));
-            }.bind(this));
-            this.el.on('hidden.bs.modal', function(event) {
-                jQuery(this).remove();
-            });
-
-        },
-        add: function() {
-            var domain = jQuery.extend([], this.domain);
-            var model_name = this.screen.model_name;
-            var value = this.wid_text.val();
-
-            var callback = function(result) {
-                var prm = jQuery.when();
-                if (!jQuery.isEmptyObject(result)) {
-                    var ids = [];
-                    for (var i = 0, len = result.length; i < len; i++) {
-                        ids.push(result[i][0]);
-                    }
-                    this.screen.group.load(ids, true);
-                    prm = this.screen.display();
-                }
-                prm.done(function() {
-                    this.screen.set_cursor();
-                }.bind(this));
-                this.entry.val('');
-            }.bind(this);
-            var parser = new Sao.common.DomainParser();
-            var win = new Sao.Window.Search(model_name, callback, {
-                sel_multi: true,
-                context: this.context,
-                domain: domain,
-                search_filter: parser.quote(value)
-            });
-        },
-        remove: function() {
-            this.screen.remove(false, true, false);
-        },
-        new_: function() {
-            this.screen.new_();
-        },
-        delete_: function() {
-            this.screen.remove(false, false, false);
-        },
-        undelete: function() {
-            this.screen.unremove();
-        },
-        previous: function() {
-            this.screen.display_previous();
-        },
-        next: function() {
-            this.screen.display_next();
-        },
-        switch_: function() {
-            this.screen.switch_view();
-        },
-        response: function(response_id) {
-            var result;
-            this.screen.current_view.set_value();
-            var readonly = this.screen.group.get_readonly();
-            if (~['RESPONSE_OK', 'RESPONSE_ACCEPT'].indexOf(response_id) &&
-                    !readonly &&
-                    this.screen.current_record) {
-                this.screen.current_record.validate().then(function(validate) {
-                    if (validate && this.screen.attributes.pre_validate) {
-                        return this.screen.current_record.pre_validate();
-                    }
-                    return validate;
-                }.bind(this)).then(function(validate) {
-                    var closing_prm = jQuery.Deferred();
-                    if (validate && this.save_current) {
-                        this.screen.save_current().then(closing_prm.resolve,
-                            closing_prm.reject);
-                    } else if (validate &&
-                            this.screen.current_view.view_type == 'form') {
-                        var view = this.screen.current_view;
-                        var validate_prms = [];
-                        for (var name in view.widgets) {
-                            var widget = view.widgets[name];
-                            if (widget.screen &&
-                                widget.screen.attributes.pre_validate) {
-                                var record = widget.screen.current_record;
-                                if (record) {
-                                    validate_prms.push(record.pre_validate());
-                                }
-                            }
-                        }
-                        jQuery.when.apply(jQuery, validate_prms).then(
-                            closing_prm.resolve, closing_prm.reject);
-                    } else if (!validate) {
-                        this.info_bar.message(
-                            this.screen.invalid_message(), 'danger');
-                        closing_prm.reject();
-                    } else {
-                        this.info_bar.message();
-                        closing_prm.resolve();
-                    }
-
-                    closing_prm.fail(function() {
-                        this.screen.display().done(function() {
-                            this.screen.set_cursor();
-                        }.bind(this));
-                    }.bind(this));
-
-                    // TODO Add support for many
-                    closing_prm.done(function() {
-                        if (response_id == 'RESPONSE_ACCEPT') {
-                            this.screen.new_();
-                            this.screen.current_view.display().done(function() {
-                                this.screen.set_cursor();
-                            }.bind(this));
-                            this.many -= 1;
-                            if (this.many === 0) {
-                                this.but_new.prop('disabled', true);
-                            }
-                        } else {
-                            result = true;
-                            this.callback(result);
-                            this.destroy();
-                        }
-                    }.bind(this));
-                }.bind(this));
-                return;
-            }
-
-            if (response_id == 'RESPONSE_CANCEL' &&
-                    !readonly &&
-                    this.screen.current_record) {
-                result = false;
-                if ((this.screen.current_record.id < 0) || this.save_current) {
-                    this.screen.group.remove(this.screen.current_record, true);
-                } else if (this.screen.current_record.has_changed()) {
-                    this.screen.current_record.cancel();
-                    this.screen.current_record.reload().always(function() {
-                        this.callback(result);
-                        this.destroy();
-                    }.bind(this));
-                    return;
-                }
-            } else {
-                result = response_id != 'RESPONSE_CANCEL';
-            }
-            this.callback(result);
-            this.destroy();
-        },
-        destroy: function() {
-            this.screen.screen_container.alternate_view = false;
-            this.screen.screen_container.alternate_viewport.children()
-                .detach();
-            if (this.prev_view) {
-                // Empty when opening from Many2One
-                this.screen.switch_view(this.prev_view.view_type);
-            }
-            this.el.modal('hide');
-        }
-    });
-
-    Sao.Window.Attachment = Sao.class_(Sao.Window.Form, {
-        init: function(record, callback) {
-            this.resource = record.model.name + ',' + record.id;
-            this.attachment_callback = callback;
-            var context = jQuery.extend({}, record.get_context());
-            context.resource = this.resource;
-            var screen = new Sao.Screen('ir.attachment', {
-                domain: [['resource', '=', this.resource]],
-                mode: ['tree', 'form'],
-                context: context,
-                exclude_field: 'resource'
-            });
-            screen.switch_view().done(function() {
-                screen.search_filter();
-                screen.group.parent = record;
-            });
-            Sao.Window.Attachment._super.init.call(this, screen, this.callback,
-                {view_type: 'tree'});
-        },
-        callback: function(result) {
-            if (result) {
-                this.screen.group.save();
-            }
-            if (this.attachment_callback) {
-                this.attachment_callback();
-            }
-        }
-    });
-
-    Sao.Window.Search = Sao.class_(Object, {
-        init: function(model, callback, kwargs) {
-            kwargs = kwargs || {};
-            var views_preload = kwargs.views_preload || {};
-            this.model_name = model;
-            this.domain = kwargs.domain || [];
-            this.context = kwargs.context || {};
-            this.sel_multi = kwargs.sel_multi;
-            this.callback = callback;
-            var dialog = new Sao.Dialog('Search', '', 'lg');
-            this.el = dialog.modal;
-
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.response('RESPONSE_CANCEL');
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-default',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Find')).click(function() {
-                this.response('RESPONSE_APPLY');
-            }.bind(this)).appendTo(dialog.footer);
-            if (kwargs.new_ && Sao.common.MODELACCESS.get(model).create) {
-                jQuery('<button/>', {
-                    'class': 'btn btn-default',
-                    'type': 'button'
-                }).append(Sao.i18n.gettext('New')).click(function() {
-                    this.response('RESPONSE_ACCEPT');
-                }.bind(this)).appendTo(dialog.footer);
-            }
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'submit'
-            }).append(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
-            dialog.content.submit(function(e) {
-                this.response('RESPONSE_OK');
-                e.preventDefault();
-            }.bind(this));
-
-            this.screen = new Sao.Screen(model, {
-                mode: ['tree'],
-                context: this.context,
-                domain: this.domain,
-                view_ids: kwargs.view_ids,
-                views_preload: views_preload,
-                row_activate: this.activate.bind(this)
-            });
-            this.screen.load_next_view().done(function() {
-                this.screen.switch_view().done(function() {
-                    dialog.body.append(this.screen.screen_container.el);
-                    this.el.modal('show');
-                    this.screen.display();
-                    if (kwargs.search_filter !== undefined) {
-                        this.screen.search_filter(kwargs.search_filter);
-                    }
-                }.bind(this));
-            }.bind(this));
-            this.el.on('hidden.bs.modal', function(event) {
-                jQuery(this).remove();
-            });
-        },
-        activate: function() {
-            this.response('RESPONSE_OK');
-        },
-        response: function(response_id) {
-            var records;
-            var value = [];
-            if (response_id == 'RESPONSE_OK') {
-                records = this.screen.current_view.selected_records();
-            } else if (response_id == 'RESPONSE_APPLY') {
-                this.screen.search_filter();
-                return;
-            } else if (response_id == 'RESPONSE_ACCEPT') {
-                var screen = new Sao.Screen(this.model_name, {
-                    domain: this.domain,
-                    context: this.context,
-                    mode: ['form']
-                });
-
-                var callback = function(result) {
-                    if (result) {
-                        screen.save_current().then(function() {
-                            var record = screen.current_record;
-                            this.callback([[record.id,
-                                record._values.rec_name || '']]);
-                        }.bind(this), function() {
-                            this.callback(null);
-                        }.bind(this));
-                    } else {
-                        this.callback(null);
-                    }
-                };
-                this.el.modal('hide');
-                new Sao.Window.Form(screen, callback.bind(this), {
-                    new_: true
-                });
-                return;
-            }
-            if (records) {
-                var index, record;
-                for (index in records) {
-                    record = records[index];
-                    value.push([record.id, record._values.rec_name || '']);
-                }
-            }
-            this.callback(value);
-            this.el.modal('hide');
-        }
-    });
-
-    Sao.Window.Preferences = Sao.class_(Object, {
-        init: function(callback) {
-            this.callback = callback;
-            var dialog = new Sao.Dialog('Preferences', '', 'lg');
-            this.el = dialog.modal;
-
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.response('RESPONSE_CANCEL');
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'submit'
-            }).append(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
-            dialog.content.submit(function(e) {
-                this.response('RESPONSE_OK');
-                e.preventDefault();
-            }.bind(this));
-
-            this.screen = new Sao.Screen('res.user', {
-                mode: []
-            });
-            // Reset readonly set automaticly by MODELACCESS
-            this.screen.attributes.readonly = false;
-            this.screen.group.set_readonly(false);
-            this.screen.group.skip_model_access = true;
-
-            var set_view = function(view) {
-                this.screen.add_view(view);
-                this.screen.switch_view().done(function() {
-                    this.screen.new_(false);
-                    this.screen.model.execute('get_preferences', [false], {})
-                    .then(set_preferences.bind(this), this.destroy);
-                }.bind(this));
-            };
-            var set_preferences = function(preferences) {
-                this.screen.current_record.set(preferences);
-                this.screen.current_record.id =
-                    this.screen.model.session.user_id;
-                this.screen.current_record.validate(null, true).then(
-                        function() {
-                            this.screen.display(true);
-                        }.bind(this));
-                dialog.body.append(this.screen.screen_container.el);
-                this.el.modal('show');
-            };
-            this.el.on('hidden.bs.modal', function(event) {
-                jQuery(this).remove();
-            });
-
-            this.screen.model.execute('get_preferences_fields_view', [], {})
-                .then(set_view.bind(this), this.destroy);
-        },
-        response: function(response_id) {
-            var end = function() {
-                this.destroy();
-                this.callback();
-            }.bind(this);
-            var prm = jQuery.when();
-            if (response_id == 'RESPONSE_OK') {
-                prm = this.screen.current_record.validate()
-                    .then(function(validate) {
-                        if (validate) {
-                            var values = jQuery.extend({}, this.screen.get());
-                            var set_preferences = function(password) {
-                                return this.screen.model.execute(
-                                    'set_preferences', [values, password], {});
-                            }.bind(this);
-                            if ('password' in values) {
-                                return Sao.common.ask.run(
-                                    'Current Password:', false)
-                                    .then(function(password) {
-                                        return set_preferences(password);
-                                    });
-                            } else {
-                                return set_preferences(false);
-                            }
-                        }
-                    }.bind(this));
-            }
-            prm.done(end);
-        },
-        destroy: function() {
-            this.el.modal('hide');
-        }
-    });
-
-    Sao.Window.Revision = Sao.class_(Object, {
-        init: function(revisions, callback) {
-            this.callback = callback;
-            var dialog = new Sao.Dialog(
-                    Sao.i18n.gettext('Revision'), '', 'lg');
-            this.el = dialog.modal;
-
-            jQuery('<button/>', {
-                'class': 'btn btn-link',
-                'type': 'button'
-            }).append(Sao.i18n.gettext('Cancel')).click(function() {
-                this.response('RESPONSE_CANCEL');
-            }.bind(this)).appendTo(dialog.footer);
-            jQuery('<button/>', {
-                'class': 'btn btn-primary',
-                'type': 'submit'
-            }).append(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
-            dialog.content.submit(function(e) {
-                this.response('RESPONSE_OK');
-                e.preventDefault();
-            }.bind(this));
-
-            var group = jQuery('<div/>', {
-                'class': 'form-group'
-            }).appendTo(dialog.body);
-            jQuery('<label/>', {
-                'for': 'revision',
-                'text': 'Revision'
-            }).appendTo(group);
-            this.select = jQuery('<select/>', {
-                'class': 'form-control',
-                id: 'revision',
-                'placeholder': Sao.i18n.gettext('Revision')
-            }).appendTo(group);
-            var date_format = Sao.common.date_format();
-            var time_format = '%H:%M:%S.%f';
-            this.select.append(jQuery('<option/>', {
-                value: null,
-                text: ''
-            }));
-            revisions.forEach(function(revision) {
-                var name = revision[2];
-                revision = revision[0];
-                this.select.append(jQuery('<option/>', {
-                    value: revision.valueOf(),
-                    text: Sao.common.format_datetime(
-                        date_format, time_format, revision) + ' ' + name
-                }));
-            }.bind(this));
-            this.el.modal('show');
-            this.el.on('hidden.bs.modal', function(event) {
-                jQuery(this).remove();
-            });
-        },
-        response: function(response_id) {
-            var revision = null;
-            if (response_id == 'RESPONSE_OK') {
-                revision = this.select.val();
-                if (revision) {
-                    revision = Sao.DateTime(parseInt(revision, 10));
-                }
-            }
-            this.el.modal('hide');
-            this.callback(revision);
-        }
-    });
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.Wizard = Sao.class_(Object, {
-        init: function(name) {
-            this.widget = jQuery('<div/>', {
-                'class': 'wizard'
-            });
-            this.name = name;
-            this.action_id = null;
-            this.id = null;
-            this.ids = null;
-            this.action = null;
-            this.context = null;
-            this.states = {};
-            this.session_id = null;
-            this.start_state = null;
-            this.end_state = null;
-            this.screen = null;
-            this.screen_state = null;
-            this.state = null;
-            this.session = Sao.Session.current_session;
-            this.__processing = false;
-            this.__waiting_response = false;
-            this.info_bar = new Sao.Window.InfoBar();
-        },
-        run: function(attributes) {
-            this.action = attributes.action;
-            this.action_id = attributes.data.action_id;
-            this.id = attributes.data.id;
-            this.ids = attributes.data.ids;
-            this.model = attributes.data.model;
-            this.context = attributes.context;
-            Sao.rpc({
-                'method': 'wizard.' + this.action + '.create',
-                'params': [this.session.context]
-            }, this.session).then(function(result) {
-                this.session_id = result[0];
-                this.start_state = this.state = result[1];
-                this.end_state = result[2];
-                this.process();
-            }.bind(this));
-        },
-        process: function() {
-            if (this.__processing || this.__waiting_response) {
-                return;
-            }
-            var process = function() {
-                if (this.state == this.end_state) {
-                    this.end();
-                    return;
-                }
-                var ctx = jQuery.extend({}, this.context);
-                ctx.active_id = this.id;
-                ctx.active_ids = this.ids;
-                ctx.active_model = this.model;
-                ctx.action_id = this.action_id;
-                var data = {};
-                if (this.screen) {
-                    data[this.screen_state] = this.screen.get_on_change_value();
-                }
-                Sao.rpc({
-                    'method': 'wizard.' + this.action + '.execute',
-                    'params': [this.session_id, data, this.state, ctx]
-                }, this.session).then(function(result) {
-                    if (result.view) {
-                        this.clean();
-                        var view = result.view;
-                        this.update(view.fields_view, view.defaults,
-                            view.buttons);
-                        this.screen_state = view.state;
-                        this.__waiting_response = true;
-                    } else {
-                        this.state = this.end_state;
-                    }
-
-                    var execute_actions = function execute_actions() {
-                        if (result.actions) {
-                            result.actions.forEach(function(action) {
-                                Sao.Action.exec_action(action[0], action[1],
-                                    jQuery.extend({}, this.context));
-                            }.bind(this));
-                        }
-                    }.bind(this);
-
-                    if (this.state == this.end_state) {
-                        this.end().then(execute_actions);
-                    } else {
-                        execute_actions();
-                    }
-                    this.__processing = false;
-                }.bind(this), function(result) {
-                    // TODO end for server error.
-                    this.__processing = false;
-                }.bind(this));
-            };
-            process.call(this);
-        },
-        destroy: function() {
-            // TODO
-        },
-        end: function() {
-            return Sao.rpc({
-                'method': 'wizard.' + this.action + '.delete',
-                'params': [this.session_id, this.session.context]
-            }, this.session);
-        },
-        clean: function() {
-            this.widget.children().remove();
-            this.states = {};
-        },
-        response: function(state) {
-            this.__waiting_response = false;
-            this.screen.current_view.set_value();
-            return this.screen.current_record.validate().then(function(validate) {
-                if ((!validate) && state != this.end_state) {
-                    this.screen.display(true);
-                    this.info_bar.message(
-                        this.screen.invalid_message(), 'danger');
-                    return;
-                }
-                this.info_bar.message();
-                this.state = state;
-                this.process();
-            }.bind(this));
-        },
-        _get_button: function(definition) {
-            var button = new Sao.common.Button(definition);
-            this.states[definition.state] = button;
-            if (definition.default) {
-                button.el.addClass('btn-primary');
-            } else if (definition.state == this.end_state) {
-                button.el.addClass('btn-link');
-            }
-            return button;
-        },
-        update: function(view, defaults, buttons) {
-            buttons.forEach(function(button) {
-                this._get_button(button);
-            }.bind(this));
-            this.screen = new Sao.Screen(view.model,
-                    {mode: [], context: this.context});
-            this.screen.add_view(view);
-            this.screen.switch_view();
-            // TODO record-modified
-            // TODO title
-            // TODO toolbar
-            this.widget.append(this.screen.screen_container.el);
-
-            this.screen.new_(false);
-            this.screen.current_record.set_default(defaults);
-            this.screen.set_cursor();
-        }
-    });
-
-    Sao.Wizard.create = function(attributes) {
-        var win;
-        if (attributes.window) {
-            win = new Sao.Wizard.Form(attributes.name);
-            var tab = new Sao.Tab.Wizard(win);
-            Sao.Tab.add(tab);
-        } else {
-            win = new Sao.Wizard.Dialog(attributes.name);
-        }
-        win.run(attributes);
-    };
-
-    Sao.Wizard.Form = Sao.class_(Sao.Wizard, {
-        init: function(name) {
-            Sao.Wizard.Form._super.init.call(this);
-            this.tab = null;  // Filled by Sao.Tab.Wizard
-            this.name = name;
-
-            this.form = jQuery('<div/>', {
-                'class': 'wizard-form',
-            }).append(this.widget);
-            this.footer = jQuery('<div/>', {
-                'class': 'modal-footer'
-            }).appendTo(this.form);
-        },
-        clean: function() {
-            Sao.Wizard.Form._super.clean.call(this);
-            this.footer.children().remove();
-        },
-        _get_button: function(definition) {
-            var button = Sao.Wizard.Form._super._get_button.call(this,
-                definition);
-            this.footer.append(button.el);
-            button.el.click(function() {
-                this.response(definition.state);
-            }.bind(this));
-            return button;
-        },
-        end: function() {
-            return Sao.Wizard.Form._super.end.call(this).always(function() {
-                return this.tab.close();
-            }.bind(this));
-        }
-    });
-
-    Sao.Wizard.Dialog = Sao.class_(Sao.Wizard, { // TODO nomodal
-        init: function(name) {
-            if (!name) {
-                name = Sao.i18n.gettext('Wizard');
-            }
-            Sao.Wizard.Dialog._super.init.call(this);
-            var dialog = new Sao.Dialog(name, 'wizard-dialog', 'lg');
-            this.dialog = dialog.modal;
-            this.content = dialog.content;
-            this.footer = dialog.footer;
-            dialog.body.append(this.info_bar.el).append(this.widget);
-        },
-        clean: function() {
-            Sao.Wizard.Dialog._super.clean.call(this);
-            this.footer.children().remove();
-        },
-        _get_button: function(definition) {
-            var button = Sao.Wizard.Dialog._super._get_button.call(this,
-                    definition);
-            this.footer.append(button.el);
-            if (definition['default']) {
-                this.content.unbind('submit');
-                this.content.submit(function(e) {
-                    this.response(definition.state);
-                    e.preventDefault();
-                }.bind(this));
-                button.el.attr('type', 'submit');
-            } else {
-                button.el.click(function() {
-                    this.response(definition.state);
-                }.bind(this));
-            }
-            return button;
-        },
-        update: function(view, defaults, buttons) {
-            this.content.unbind('submit');
-            Sao.Wizard.Dialog._super.update.call(this, view, defaults,
-                    buttons);
-            this.dialog.modal('show');
-        },
-        destroy: function(action) {
-            Sao.Wizard.Dialog._super.destroy.call(this);
-            this.dialog.on('hidden.bsd.modal', function(event) {
-                jQuery(this).remove();
-            });
-            this.dialog.modal('hide');
-            var dialog = jQuery('.wizard-dialog').filter(':visible')[0];
-            var is_menu = false;
-            var screen;
-            if (!dialog) {
-                dialog = Sao.Tab.tabs.get_current();
-                if (dialog) {
-                    if (dialog.screen &&
-                           dialog.screen.model_name != this.model) {
-                        is_menu = true;
-                        screen = Sao.main_menu_screen;
-                    }
-                } else {
-                    is_menu = true;
-                    screen = Sao.main_menu_screen;
-                }
-            }
-            if (dialog && dialog.screen) {
-                screen = dialog.screen;
-            }
-            if (screen) {
-                if (screen.current_record && !is_menu) {
-                    var ids;
-                    if (screen.model_name == this.model) {
-                        ids = this.ids;
-                    } else {
-                        // Wizard run form a children record so reload parent
-                        // record
-                        ids = [screen.current_record.id];
-                    }
-                    screen.reload(ids, true);
-                }
-                if (action) {
-                    screen.client_action(action);
-                }
-            }
-        },
-        end: function() {
-            return Sao.Wizard.Dialog._super.end.call(this).then(
-                    this.destroy.bind(this));
-        },
-        show: function() {
-            this.dialog.modal('show');
-        },
-        hide: function() {
-            this.dialog.modal('hide');
-        },
-        state_changed: function() {
-            this.process();
-        }
-    });
-
-}());
-
-/* This file is part of Tryton.  The COPYRIGHT file at the top level of
-   this repository contains the full copyright notices and license terms. */
-(function() {
-    'use strict';
-
-    Sao.View.Board = Sao.class_(Object, {
-        init: function(view_xml, context) {
-            var attributes, attribute, node, actions_prms;
-
-            this.context = context;
-            this.widgets = [];
-            this.actions = [];
-            this.el = jQuery('<div/>', {
-                'class': 'board'
-            });
-
-            attributes = {};
-            node = view_xml.children()[0];
-            for (var i = 0, len = node.attributes.length; i < len; i++) {
-                attribute = node.attributes[i];
-                attributes[attribute.name] = attribute.value;
-            }
-            this.attributes = attributes;
-            this.el.append(this.parse(node).el);
-
-            actions_prms = [];
-            for (i = 0, len = this.actions.length; i < len; i++) {
-                actions_prms.push(this.actions[i].action_prm);
-            }
-            this.actions_prms = jQuery.when.apply(jQuery, actions_prms);
-        },
-        _parse_node: function(child, container, attributes) {
-            switch (child.tagName) {
-                case 'image':
-                    break;
-                case 'separator':
-                    this._parse_separator(child, container, attributes);
-                    break;
-                case 'label':
-                    this._parse_label(child, container, attributes);
-                    break;
-                case 'newline':
-                    container.add_row();
-                    break;
-                case 'notebook':
-                    this._parse_notebook(child, container, attributes);
-                    break;
-                case 'page':
-                    this._parse_page(child, container, attributes);
-                    break;
-                case 'group':
-                    this._parse_group(child, container, attributes);
-                    break;
-                case 'hpaned':
-                    this._parse_pane(child, container, attributes,
-                            'horizontal');
-                    break;
-                case 'vpaned':
-                    this._parse_pane(child, container, attributes,
-                            'vertical');
-                    break;
-                case 'child':
-                    this._parse_child(child, container, attributes);
-                    break;
-                case 'action':
-                    this._parse_action(child, container, attributes);
-                    break;
-            }
-        },
-        parse: function(node, container) {
-            var _parse;
-            if (!container) {
-                container = new Sao.View.Form.Container(
-                        Number(node.getAttribute('col') || 4));
-            }
-            _parse = function(index, child) {
-                var attributes, attribute;
-                var i, len;
-                attributes = {};
-                for (i = 0, len = child.attributes.length; i < len; i++) {
-                    attribute = child.attributes[i];
-                    attributes[attribute.name] = attribute.value;
-                }
-                ['yexpand', 'yfill', 'xexpand', 'xfill', 'colspan',
-                 'position'].forEach(function(name) {
-                     if (attributes[name]) {
-                         attributes[name] = Number(attributes[name]);
-                     }
-                });
-                this._parse_node(child, container, attributes);
-            };
-            jQuery(node).children().each(_parse.bind(this));
-            return container;
-        },
-        _parse_separator: function(node, container, attributes) {
-            var text, separator;
-            text = attributes.string;
-            separator = new Sao.view.Form.Separator(text, attributes);
-            container.add(attributes, separator);
-        },
-        _parse_label: function(node, container, attributes) {
-            var text, label;
-            text = attributes.string;
-            if (!text) {
-                container.add(attributes);
-                return;
-            }
-            label = new Sao.View.Form.Label(text, attributes);
-            container.add(attributes, label);
-        },
-        _parse_notebook: function(node, container, attributes) {
-            var notebook;
-            if (attributes.yexpand === undefined) {
-                attributes.yexpand = true;
-            }
-            if (attributes.yfill === undefined) {
-                attributes.yfill = true;
-            }
-            notebook = new Sao.View.Form.Notebook(attributes);
-            container.add(attributes, notebook);
-            this.parse(node, container);
-        },
-        _parse_page: function(node, container, attributes) {
-            var text;
-            text = attributes.string;
-            page = this.parse(node, container);
-            page = new Sao.View.Form.Page(container.add(page.el, text),
-                    attributes);
-        },
-        _parse_group: function(node, container, attributes) {
-            var group;
-            group = new Sao.View.Form.Group(attributes);
-            container.add(attributes, group);
-        },
-        _parse_pane: function(node, container, attributes, orientation) {
-            var paned;
-            if (attributes.yexpand === undefined) {
-                attributes.yexpand = true;
-            }
-            if (attributes.yfill === undefined) {
-                attributes.yfill = true;
-            }
-            paned = new Sao.common.Paned(orientation);
-            container.add(attributes, paned);
-            this.parse(node, paned);
-        },
-        _parse_child: function(node, paned, attributes) {
-            var container, child1, child2;
-            container = this.parse(node);
-            child1 = paned.get_child1();
-            if (child1.children().length > 0) {
-                child2 = paned.get_child2();
-                child2.append(container.el);
-            } else {
-                child1.append(container.el);
-            }
-        },
-        _parse_action: function(node, container, attributes) {
-            var action;
-            if (attributes.yexpand === undefined) {
-                attributes.yexpand = true;
-            }
-            if (attributes.yfill === undefined) {
-                attributes.yfill = true;
-            }
-            action = new Sao.View.Board.Action(attributes, this.context);
-            this.actions.push(action);
-            container.add(attributes, action);
-        },
-        reload: function() {
-            for (var i = 0; i < this.actions.length; i++) {
-                this.actions[i].display();
-            }
-        }
-    });
-
-    Sao.View.Board.Action = Sao.class_(Object, {
-        init: function(attributes, context) {
-            var model, action_prm, act_window;
-            var decoder, search_context, search_value;
-
-            this.name = attributes.name;
-            this.context = context || {};
-
-            act_window = new Sao.Model('ir.action.act_window');
-            this.action_prm = act_window.execute('get', [this.name],
-                    this.context);
-            this.action_prm.done(function(action) {
-                var i, len;
-                var view_ids, decoder, search_context;
-                var screen_attributes, action_modes;
-
-                this.action = action;
-                this.action.mode = [];
-                view_ids = [];
-                if ((this.action.views || []).length > 0) {
-                    for (i = 0, len = this.action.views.length; i < len; i++) {
-                        view_ids.push(this.action.views[i][0]);
-                        this.action.mode.push(this.action.views[i][1]);
-                    }
-                } else if (this.action.view_id !== undefined) {
-                    view_ids = [this.action.view_id[0]];
-                }
-
-                if ('mode' in attributes) {
-                    this.action.mode = attributes.mode;
-                }
-
-                if (!('pyson_domain' in this.action)) {
-                    this.action.pyson_domain = '[]';
-                }
-
-                jQuery.extend(this.context,
-                        Sao.Session.current_session.context);
-                this.context._user = Sao.Session.current_session.user_id;
-                decoder = new Sao.PYSON.Decoder(this.context);
-                jQuery.extend(this.context,
-                        decoder.decode(this.action.pyson_context || '{}'));
-                decoder = new Sao.PYSON.Decoder(this.context);
-                jQuery.extend(this.context,
-                        decoder.decode(this.action.pyson_context || '{}'));
-
-                this.domain = [];
-                this.update_domain([]);
-
-                search_context = jQuery.extend({}, this.context);
-                search_context.context = this.context;
-                search_context._user = Sao.Session.current_session.user_id;
-                decoder = new Sao.PYSON.Decoder(search_context);
-                search_value = decoder.decode(
-                        this.action.pyson_search_value || '{}');
-
-                screen_attributes = {
-                    mode: this.action.mode,
-                    context: this.context,
-                    view_ids: view_ids,
-                    domain: this.domain,
-                    search_value: search_value,
-                    row_activate: this.row_activate.bind(this),
-                };
-                this.screen = new Sao.Screen(this.action.res_model,
-                        screen_attributes);
-
-                if (attributes.string) {
-                    this.title.html(attributes.string);
-                } else if (this.action.window_name !== undefined) {
-                    this.title.html(this.action.name);
-                } else {
-                    this.title.html(this.screen.current_view.title);
-                }
-                this.screen.switch_view().done(function() {
-                    this.body.append(this.screen.screen_container.el);
-                    this.screen.search_filter();
-                }.bind(this));
-            }.bind(this));
-            this.el = jQuery('<div/>', {
-                'class': 'board-action panel panel-default',
-            });
-            this.title = jQuery('<div/>', {
-                'class': 'panel-heading',
-            });
-            this.el.append(this.title);
-            this.body = jQuery('<div/>', {
-                'class': 'panel-body',
-            });
-            this.el.append(this.body);
-        },
-        row_activate: function() {
-            var record_ids, win;
-
-            if (!this.screen.current_record) {
-                return;
-            }
-
-            if (this.screen.current_view.view_type == 'tree' &&
-                    ('keyword_open' in this.screen.current_view.attributes)) {
-                record_ids = this.screen.current_view.selected_records().map(
-                        function(record) { return record.id; });
-                Sao.Action.exec_keyword('tree_open', {
-                    model: this.screen.model_name,
-                    id: this.screen.current_record.id,
-                    ids: record_ids
-                }, jQuery.extend({}, this.context), false);
-            } else {
-                win = new Sao.Window.Form(this.screen, function(result) {
-                    if (result) {
-                        this.screen.current_record.save();
-                    } else {
-                        this.screen.current_record.cancel();
-                    }
-                }.bind(this));
-            }
-        },
-        set_value: function() {
-        },
-        display: function() {
-            this.screen.search_filter(this.screen.screen_container.get_text());
-        },
-        get_active: function() {
-            if (this.screen && this.screen.current_record) {
-                return Sao.common.EvalEnvironment(this.screen.current_record);
-            }
-        },
-        update_domain: function(actions) {
-            var i, len;
-            var active, domain_ctx, decoder, new_domain;
-
-            domain_ctx = jQuery.extend({}, this.context);
-            domain_ctx.context = domain_ctx;
-            domain_ctx._user = Sao.Session.current_session.user_id;
-            for (i = 0, len = actions.length; i < len; i++) {
-                active = actions[i].get_active();
-                if (active) {
-                    domain_ctx[actions[i].name] = active;
-                }
-            }
-            decoder = new Sao.PYSON.Decoder(domain_ctx);
-            new_domain = decoder.decode(this.action.pyson_domain);
-            if (Sao.common.compare(this.domain, new_domain)) {
-                return;
-            }
-            this.domain.splice(0, this.domain.length);
-            jQuery.extend(this.domain, new_domain);
-            if (this.screen) {
-                this.display();
-            }
-        }
-    });
-}());
diff --git a/dist/tryton-sao.js b/dist/tryton-sao.js
index 7d5052d..981c285 100644
--- a/dist/tryton-sao.js
+++ b/dist/tryton-sao.js
@@ -192,9 +192,7 @@ var Sao = {};
                 if ('' === key) {
                     continue;
                 }
-                if ('' !== data[key][1]) {
-                    data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
-                }
+                data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
             }
             Sao.i18n.loadJSON(data);
         });
diff --git a/dist/tryton-sao.min.js b/dist/tryton-sao.min.js
index 3f7a2e9..49d9aa4 100644
--- a/dist/tryton-sao.min.js
+++ b/dist/tryton-sao.min.js
@@ -1,12 +1,12 @@
-/*! tryton-sao-3.8.0 | GPL-3
+/*! tryton-sao-3.8.1 | GPL-3
 This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. */
-var Sao={};!function(){"use strict";function a(){var a=1040;jQuery(".modal.in").each(function(b){var c=jQuery(this);a++,c.css("zIndex",a),c.next(".modal-backdrop.in").addClass("hidden").css("zIndex",a-1)}),jQuery(".modal.in:visible:last").focus().next(".modal-backdrop.in").removeClass("hidden")}"contains"in String.prototype||(String.prototype.contains=function(a,b){return-1!==String.prototype.indexOf.call(this,a,b)}),String.prototype.startsWith||Object.defineProperty(String.prototype,"startsWith",{enumerable:!1,configurable:!1,writable:!1,value:function(a,b){return b=b||0,this.indexOf(a,b)===b}}),String.prototype.endsWith||Object.defineProperty(String.prototype,"endsWith",{enumerable:!1,configurable:!1,writable:!1,value:function(a,b){b=b||this.length,b-=a.length;var c=this.lastIndexOf(a);return-1!==c&&c===b}}),Array.prototype.some||(Array.prototype.some=function(a){if(null===this)throw new TypeError;var b,c,d=Object(this),e=d.length>>>0;if("function"!=typeof a)throw new TypeError;for(b=arguments[1],c=0;e>c;c++)if(c in d&&a.call(b,d[c],c,d))return!0;return!1});try{document.execCommand("styleWithCSS",!1,!1)}catch(b){}try{document.execCommand("useCSS",!1,!0)}catch(b){}jQuery.fn.extend({uniqueId:function(){var a=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})}}()}),Sao.class_=function(a,b){var c=function(){if(!(this instanceof c))throw new Error("Constructor function requires new operator");this.Class=c,this.init&&this.init.apply(this,arguments)};if(c.prototype=Object.create(a.prototype),c._super=a.prototype,b)for(var d in b)c.prototype[d]=b[d];return c},Sao.Decimal=Number,Sao.Date=function(a,b,c){var d=moment();return d.year(a),d.month(b),d.date(c),d.set({hour:0,minute:0,second:0,millisecond:0}),d.isDate=!0,d},Sao.Date.min=moment(new Date(-864e13)),Sao.Date.min.set({hour:0,minute:0,second:0,millisecond:0}),Sao.Date.min.isDate=!0,Sao.Date.max=moment(new Date(864e13)),Sao.Date.max.set({hour:0,minute:0,second:0,millisecond:0}),Sao.Date.max.isDate=!0,Sao.DateTime=function(a,b,c,d,e,f,g,h){var i=moment();return h&&i.utc(),i.year(a),i.month(b),i.date(c),i.hour(d||0),i.minute(e||0),i.second(f||0),i.milliseconds(g||0),i.isDateTime=!0,i.local(),i},Sao.DateTime.combine=function(a,b){var c=a.clone();return c.set({hour:b.hour(),minute:b.minute(),second:b.second(),millisecond:b.millisecond()}),c},Sao.DateTime.min=moment(new Date(-864e13)).local(),Sao.DateTime.min.isDateTime=!0,Sao.DateTime.max=moment(new Date(864e13)).local(),Sao.DateTime.max.isDateTime=!0,Sao.Time=function(a,b,c,d){var e=moment({hour:a,minute:b,second:c,millisecond:d||0});return e.isTime=!0,e},Sao.TimeDelta=function(a,b,c,d,e,f){var g=moment.duration({days:a,seconds:b,milliseconds:c,minutes:d,hours:e,weeks:f});return g.isTimeDelta=!0,g},Sao.config={},Sao.config.limit=1e3,Sao.config.display_size=20,Sao.config.roundup={},Sao.config.roundup.url="http://bugs.tryton.org/roundup/",Sao.i18n=i18n(),Sao.i18n.setlang=function(a){return Sao.i18n.setLocale(a),jQuery.getJSON("locale/"+a+".json",function(b){b[""].language||(b[""].language=a),b[""]["plural-forms"]||(b[""]["plural-forms"]="nplurals=2; plural=(n!=1);");for(var c in b)""!==c&&""!==b[c][1]&&(b[c]=2==b[c].length?b[c][1]:b[c].slice(1));Sao.i18n.loadJSON(b)})},Sao.i18n.setlang((navigator.language||navigator.browserLanguge||navigator.userLanguage||"en_US").replace("-","_")),Sao.get_preferences=function(){var a=Sao.Session.current_session;return a.reload_context().then(function(){return Sao.rpc({method:"model.res.user.get_preferences",params:[!1,{}]},a).then(function(a){var b=[];return b.push(Sao.common.MODELACCESS.load_models()),b.push(Sao.common.ICONFACTORY.load_icons()),b.push(Sao.common.MODELHISTORY.load_history()),b.push(Sao.common.VIEW_SEARCH.load_searches()),jQuery.when.apply(jQuery,b).then(function(){(a.actions||[]).forEach(function(a){Sao.Action.execute(a,{},null,{})});var b="Tryton";jQuery.isEmptyObject(a.status_bar)||(b+=" - "+a.status_bar),document.title=b;var c=a.language!=Sao.i18n.getLocale(),d=jQuery.Deferred();return Sao.i18n.setlang(a.language).always(function(){c&&Sao.user_menu(a),d.resolve(a)}),d})})})},Sao.login=function(){Sao.Session.get_credentials().then(function(a){return Sao.Session.current_session=a,a.reload_context()}).then(Sao.get_preferences).then(function(a){Sao.menu(a),Sao.user_menu(a)})},Sao.logout=function(){var a=Sao.Session.current_session;Sao.Tab.tabs.close(!0).done(function(){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#menu").children().remove(),document.title="Tryton",a.do_logout().always(Sao.login)})},Sao.preferences=function(){Sao.Tab.tabs.close(!0).done(function(){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#menu").children().remove(),new Sao.Window.Preferences(function(){Sao.get_preferences().then(function(a){Sao.menu(a),Sao.user_menu(a)})})})},Sao.user_menu=function(a){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#user-preferences").append(jQuery("<a/>",{href:"#"}).click(Sao.preferences).append(a.status_bar)),jQuery("#user-logout").append(jQuery("<a/>",{href:"#"}).click(Sao.logout).append(Sao.i18n.gettext("Logout")))},Sao.menu=function(a){var b=new Sao.PYSON.Decoder,c=b.decode(a.pyson_menu),d=!1;jQuery.isEmptyObject(c.views)?c.view_id&&(d=[c.view_id[0]]):d=c.views.map(function(a){return a[0]}),b=new Sao.PYSON.Decoder(Sao.Session.current_session.context);var e=b.decode(c.pyson_context||"{}"),f=b.decode(c.pyson_domain),g=new Sao.Tab.Form(c.res_model,{mode:["tree"],view_ids:d,domain:f,context:e,selection_mode:Sao.common.SELECTION_NONE});Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(g),1),g.view_prm.done(function(){Sao.main_menu_screen=g.screen;var a=g.screen.current_view;a.table.find("thead").hide(),jQuery("#menu").children().remove();var b=new Sao.GlobalSearch;jQuery("#menu").append(b.el),jQuery("#menu").append(g.screen.screen_container.content_box.detach())})},Sao.main_menu_screen=null,Sao.Dialog=Sao.class_(Object,{init:function(a,b,c){c=c||"sm",this.modal=jQuery("<div/>",{"class":b+" modal fade",role:"dialog"}),this.content=jQuery("<form/>",{"class":"modal-content"}).appendTo(jQuery("<div/>",{"class":"modal-dialog modal-"+c}).appendTo(this.modal)),this.header=jQuery("<div/>",{"class":"modal-header"}).appendTo(this.content),a&&this.add_title(a),this.body=jQuery("<div/>",{"class":"modal-body"}).appendTo(this.content),this.footer=jQuery("<div/>",{"class":"modal-footer"}).appendTo(this.content)},add_title:function(a){this.header.append(jQuery("<h4/>",{"class":"modal-title"}).append(a))}}),Sao.GlobalSearch=Sao.class_(Object,{init:function(){this.el=jQuery("<div/>",{"class":"global-search-container"}),this.search_entry=jQuery("<input>",{"class":"form-control",placeholder:Sao.i18n.gettext("Search...")}),this.el.append(this.search_entry);new Sao.common.InputCompletion(this.search_entry,this.update.bind(this),this.match_selected.bind(this),this.format.bind(this))},format:function(a){var b=jQuery("<div/>"),c=jQuery("<img/>",{"class":"global-search-icon"}).appendTo(b);return Sao.common.ICONFACTORY.register_icon(a.icon).then(function(a){c.attr("src",a)}),jQuery("<span/>",{"class":"global-search-text"}).text(a.record_name).appendTo(b),b},update:function(a){var b=new Sao.Model("ir.model");return b.execute("global_search",[a,Sao.config.limit,Sao.main_menu_screen.model_name],Sao.main_menu_screen.context).then(function(a){for(var b=[],c=0,d=a.length;d>c;c++)b.push({model:a[c][1],model_name:a[c][2],record_id:a[c][3],record_name:a[c][4],icon:a[c][5]});return b}.bind(this))},match_selected:function(a){if(a.model==Sao.main_menu_screen.model_name)Sao.Action.exec_keyword("tree_open",{model:a.model,id:a.record_id,ids:[a.record_id]},Sao.main_menu_screen.context);else{var b={model:a.model,res_id:a.record_id,mode:["form","tree"]};Sao.Tab.create(b)}this.search_entry.val("")}}),jQuery(document).on("show.bs.modal",".modal",function(a){jQuery(this).appendTo(jQuery("body"))}).on("shown.bs.modal",".modal.in",function(b){a()}).on("hidden.bs.modal",".modal",function(b){a()})}(),function(){"use strict";Sao.rpc=function(a,b){var c=jQuery.Deferred();b||(b=new Sao.Session);var d=jQuery.extend([],a.params);d.push(jQuery.extend({},b.context,d.pop()));var e=Sao.common.processing.show(),f=jQuery.ajax({contentType:"application/json",data:JSON.stringify(Sao.rpc.prepareObject({method:a.method,params:[b.user_id,b.session].concat(d)})),dataType:"json",url:"/"+(b.database||""),type:"post",complete:[function(){Sao.common.processing.hide(e)}]}),g=function(d){if(null===d)Sao.common.warning.run("",Sao.i18n.gettext("Unable to reach the server")),c.reject();else if(d.error){var e,f,g;if("UserWarning"==d.error[0])return e=d.error[1],f=d.error[2],g=d.error[3],void Sao.common.userwarning.run(g,f).done(function(d){return~["always","ok"].indexOf(d)?void Sao.rpc({method:"model.res.user.warning.create",params:[[{user:b.user_id,name:e,always:"always"==d}],{}]},b).done(function(){Sao.rpc(a,b).then(c.resolve,c.reject)}):void c.reject()});if("UserError"==d.error[0])return f=d.error[1],g=d.error[2],void Sao.common.warning.run(f,g).always(c.reject);if("ConcurrencyException"==d.error[0]){if(a.method.startsWith("model.")&&(a.method.endsWith(".write")||a.method.endsWith(".delete"))){var h=a.method.split(".").slice(1,-1).join(".");return void Sao.common.concurrency.run(h,a.params[0][0],a.params.slice(-1)[0]).then(function(){delete a.params.slice(-1)[0]._timestamp,Sao.rpc(a,b).then(c.resolve,c.reject)},c.reject)}return void Sao.common.message.run("Concurrency Exception","glyphicon-alert").always(c.reject)}if("NotLogged"==d.error[0])return void Sao.Session.renew(b).then(function(){Sao.rpc(a,b).then(c.resolve,c.reject)},c.reject);Sao.common.error.run(d.error[0],d.error[1]),c.reject()}else c.resolve(d.result)},h=function(a,b,d){Sao.common.error.run(b,d),c.reject()};return f.success(g),f.error(h),c.promise()},Sao.rpc.convertJSONObject=function(a,b,c){if(a instanceof Array)for(var d=0,e=a.length;e>d;d++)Sao.rpc.convertJSONObject(a[d],d,a);else if("string"!=typeof a&&"number"!=typeof a&&null!==a)if(a&&a.__class__){switch(a.__class__){case"datetime":a=Sao.DateTime(a.year,a.month-1,a.day,a.hour,a.minute,a.second,a.microsecond/1e3,!0);break;case"date":a=Sao.Date(a.year,a.month-1,a.day);break;case"time":a=Sao.Time(a.hour,a.minute,a.second,a.microsecond/1e3);break;case"timedelta":a=Sao.TimeDelta(null,a.seconds);break;case"bytes":for(var f=atob(a.base64.replace(/\s/g,"")),g=new ArrayBuffer(f.length),h=new Uint8Array(g),i=0;i<f.length;i++)h[i]=f.charCodeAt(i);a=h;break;case"Decimal":a=new Sao.Decimal(a.decimal)}c&&(c[b]=a)}else for(var j in a)Sao.rpc.convertJSONObject(a[j],j,a);return c||a},Sao.rpc.prepareObject=function(a,b,c){if(a instanceof Array)for(var d=0,e=a.length;e>d;d++)Sao.rpc.prepareObject(a[d],d,a);else if("string"!=typeof a&&"number"!=typeof a&&null!==a&&void 0!==a)if(a.isDate)a={__class__:"date",year:a.year(),month:a.month()+1,day:a.date()},c&&(c[b]=a);else if(a.isDateTime)a=a.clone(),a={__class__:"datetime",year:a.utc().year(),month:a.utc().month()+1,day:a.utc().date(),hour:a.utc().hour(),minute:a.utc().minute(),second:a.utc().second(),microsecond:1e3*a.utc().millisecond()},c&&(c[b]=a);else if(a.isTime)a={__class__:"time",hour:a.hour(),minute:a.minute(),second:a.second(),microsecond:1e3*a.millisecond()},c&&(c[b]=a);else if(a.isTimeDelta)a={__class__:"timedelta",seconds:a.asSeconds()},c&&(c[b]=a);else if(a instanceof Sao.Decimal)a={__class__:"Decimal",decimal:a.toString()},c&&(c[b]=a);else if(a instanceof Uint8Array)a={__class__:"bytes",base64:btoa(String.fromCharCode.apply(null,a))},c&&(c[b]=a);else for(var f in a)Sao.rpc.prepareObject(a[f],f,a);return c||a},jQuery.ajaxSetup({converters:{"text json":function(a){return Sao.rpc.convertJSONObject(jQuery.parseJSON(a))}}})}(),function(){"use strict";Sao.PYSON={},Sao.PYSON.PYSON=Sao.class_(Object,{init:function(){},pyson:function(){throw"NotImplementedError"},types:function(){throw"NotImplementedError"}}),Sao.PYSON.PYSON.eval_=function(a,b){throw"NotImplementedError"},Sao.PYSON.Encoder=Sao.class_(Object,{encode:function(a){return JSON.stringify(a,function(a,b){return b instanceof Sao.PYSON.PYSON?b.pyson():b._isAMomentObject?b.isDate?Sao.PYSON.Date(b.getFullYear(),b.getMonth(),b.getDate()).pyson():Sao.PYSON.DateTime(b.getFullYear(),b.getMonth(),b.getDate(),b.getHours(),b.getMinutes(),b.getSeconds(),b.getMilliseconds()).pyson():b})}}),Sao.PYSON.Decoder=Sao.class_(Object,{init:function(a){this.__context=a||{}},decode:function(a){var b=function(a,b){if("object"==typeof b&&null!==b){var c=Sao.PYSON[b.__class__];if(c)return c.eval_(b,this.__context)}return b};return JSON.parse(a,b.bind(this))}}),Sao.PYSON.Eval=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){void 0===b&&(b=""),Sao.PYSON.Eval._super.init.call(this),this._value=a,this._default=b},pyson:function(){return{__class__:"Eval",v:this._value,d:this._default}},types:function(){return this._default instanceof Sao.PYSON.PYSON?this._default.types():[typeof this._default]}}),Sao.PYSON.Eval.eval_=function(a,b){return a.v in b?b[a.v]:a.d},Sao.PYSON.Not=Sao.class_(Sao.PYSON.PYSON,{init:function(a){if(Sao.PYSON.Not._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["boolean"]).length||jQuery(["boolean"]).not(a.types()).length)throw"value must be boolean"}else if("boolean"!=typeof a)throw"value must be boolean";this._value=a},pyson:function(){return{__class__:"Not",v:this._value}},types:function(){return["boolean"]}}),Sao.PYSON.Not.eval_=function(a,b){return!a.v},Sao.PYSON.Bool=Sao.class_(Sao.PYSON.PYSON,{init:function(a){Sao.PYSON.Bool._super.init.call(this),this._value=a},pyson:function(){return{__class__:"Bool",v:this._value}},types:function(){return["boolean"]}}),Sao.PYSON.Bool.eval_=function(a,b){return a.v instanceof Object?!jQuery.isEmptyObject(a.v):Boolean(a.v)},Sao.PYSON.And=Sao.class_(Sao.PYSON.PYSON,{init:function(a){void 0===a&&(a=[]),Sao.PYSON.And._super.init.call(this);for(var b=0,c=a.length;c>b;b++){var d=a[b];if(d instanceof Sao.PYSON.PYSON){if(jQuery(d.types()).not(["boolean"]).length||jQuery(["boolean"]).not(d.types()).length)throw"statement must be boolean"}else if("boolean"!=typeof d)throw"statement must be boolean"}if(a.length<2)throw"must have at least 2 statements";this._statements=a},pyson:function(){return{__class__:"And",s:this._statements}},types:function(){return["boolean"]}}),Sao.PYSON.And.eval_=function(a,b){for(var c=!0,d=0,e=a.s.length;e>d;d++){var f=a.s[d];c=c&&f}return c},Sao.PYSON.Or=Sao.class_(Sao.PYSON.And,{pyson:function(){var a=Sao.PYSON.Or._super.pyson.call(this);return a.__class__="Or",a}}),Sao.PYSON.Or.eval_=function(a,b){for(var c=!1,d=0,e=a.s.length;e>d;d++){var f=a.s[d];c=c||f}return c},Sao.PYSON.Equal=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){Sao.PYSON.Equal._super.init.call(this);var c,d;if(c=a instanceof Sao.PYSON.PYSON?a.types():[typeof a],d=b instanceof Sao.PYSON.PYSON?b.types():[typeof b],jQuery(c).not(d).length||jQuery(d).not(c).length)throw"statements must have the same type";this._statement1=a,this._statement2=b},pyson:function(){return{__class__:"Equal",s1:this._statement1,s2:this._statement2}},types:function(){return["boolean"]}}),Sao.PYSON.Equal.eval_=function(a,b){return a.s1==a.s2},Sao.PYSON.Greater=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){Sao.PYSON.Greater._super.init.call(this);for(var d=[a,b],e=0;2>e;e++){var f=d[e];if(f instanceof Sao.PYSON.PYSON){if(jQuery(f).not(["number"]).length)throw"statement must be an integer or a float"}else if("number"!=typeof f)throw"statement must be an integer or a float"}if(void 0===c&&(c=!1),c instanceof Sao.PYSON.PYSON){if(jQuery(c.types()).not(["boolean"]).length||jQuery(["boolean"]).not(c.types()).length)throw"equal must be boolean"}else if("boolean"!=typeof c)throw"equal must be boolean";this._statement1=a,this._statement2=b,this._equal=c},pyson:function(){return{__class__:"Greater",s1:this._statement1,s2:this._statement2,e:this._equal}},types:function(){return["boolean"]}}),Sao.PYSON.Greater._convert=function(a){return a=jQuery.extend({},a),a.s1=Number(a.s1),a.s2=Number(a.s2),a},Sao.PYSON.Greater.eval_=function(a,b){return a=Sao.PYSON.Greater._convert(a),a.e?a.s1>=a.s2:a.s1>a.s2},Sao.PYSON.Less=Sao.class_(Sao.PYSON.Greater,{pyson:function(){var a=Sao.PYSON.Less._super.pyson.call(this);return a.__class__="Less",a}}),Sao.PYSON.Less._convert=Sao.PYSON.Greater._convert,Sao.PYSON.Less.eval_=function(a,b){return a=Sao.PYSON.Less._convert(a),a.e?a.s1<=a.s2:a.s1<a.s2},Sao.PYSON.If=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){if(Sao.PYSON.If._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["boolean"]).length||jQuery(["boolean"]).not(a.types()).length)throw"condition must be boolean"}else if("boolean"!=typeof a)throw"condition must be boolean";var d,e;if(d=b instanceof Sao.PYSON.PYSON?b.types():[typeof b],void 0===c&&(c=null),e=c instanceof Sao.PYSON.PYSON?c.types():[typeof c],jQuery(d).not(e).length||jQuery(e).not(d).length)throw"then and else statements must be the same type";this._condition=a,this._then_statement=b,this._else_statement=c},pyson:function(){return{__class__:"If",c:this._condition,t:this._then_statement,e:this._else_statement}},types:function(){return this._then_statement instanceof Sao.PYSON.PYSON?this._then_statement.types():[typeof this._then_statement]}}),Sao.PYSON.If.eval_=function(a,b){return a.c?a.t:a.e},Sao.PYSON.Get=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){if(Sao.PYSON.Get._super.init.call(this),void 0===c&&(c=null),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["object"]).length||jQuery(["object"]).not(a.types()).length)throw"obj must be a dict"}else if(!(a instanceof Object))throw"obj must be a dict";if(this._obj=a,b instanceof Sao.PYSON.PYSON){if(jQuery(b.types()).not(["string"]).length||jQuery(["string"]).not(b.types()).length)throw"key must be a string"}else if("string"!=typeof b)throw"key must be a string";this._key=b,this._default=c},pyson:function(){return{__class__:"Get",v:this._obj,k:this._key,d:this._default}},types:function(){return this._default instanceof Sao.PYSON.PYSON?this._default.types():[typeof this._default]}}),Sao.PYSON.Get.eval_=function(a,b){return a.k in a.v?a.v[a.k]:a.d},Sao.PYSON.In=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){if(Sao.PYSON.In._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["string","number"]).length)throw"key must be a string or a number"}else if(!~["string","number"].indexOf(typeof a))throw"key must be a string or a number";if(b instanceof Sao.PYSON.PYSON){if(jQuery(b.types()).not(["object"]).length||jQuery(["object"]).not(b.types()).length)throw"obj must be a dict or a list"}else if(!(b instanceof Object))throw"obj must be a dict or a list";this._key=a,this._obj=b},pyson:function(){return{__class__:"In",k:this._key,v:this._obj}},types:function(){return["boolean"]}}),Sao.PYSON.In.eval_=function(a,b){return a.v.indexOf?Boolean(~a.v.indexOf(a.k)):!!a.v[a.k]},Sao.PYSON.Date=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c,d,e,f){Sao.PYSON.Date._super.init.call(this),void 0===a&&(a=null),void 0===b&&(b=null),void 0===c&&(c=null),void 0===d&&(d=0),void 0===e&&(e=0),void 0===f&&(f=0),this._test(a,"year"),this._test(b,"month"),this._test(c,"day"),this._test(d,"delta_years"),this._test(f,"delta_days"),this._test(e,"delta_months"),this._year=a,this._month=b,this._day=c,this._delta_years=d,this._delta_months=e,this._delta_days=f},pyson:function(){return{__class__:"Date",y:this._year,M:this._month,d:this._day,dy:this._delta_years,dM:this._delta_months,dd:this._delta_days}},types:function(){return["object"]},_test:function(a,b){if(a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["number","object"]).length)throw b+" must be an integer or None"}else if("number"!=typeof a&&null!==a)throw b+" must be an integer or None"}}),Sao.PYSON.Date.eval_=function(a,b){var c=Sao.Date(a.y,a.M&&a.M-1,a.d);return a.dy&&c.add(a.dy,"y"),a.dM&&c.add(a.dM,"M"),a.dd&&c.add(a.dd,"d"),c},Sao.PYSON.DateTime=Sao.class_(Sao.PYSON.Date,{init:function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){Sao.PYSON.DateTime._super.init.call(this,a,b,c,h,i,j),void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),void 0===g&&(g=null),void 0===k&&(k=0),void 0===l&&(l=0),void 0===m&&(m=0),void 0===n&&(n=0),this._test(d,"hour"),this._test(e,"minute"),this._test(f,"second"),this._test(g,"microsecond"),this._test(k,"delta_hours"),this._test(l,"delta_minutes"),this._test(m,"delta_seconds"),this._test(n,"delta_microseconds"),this._hour=d,this._minute=e,this._second=f,this._microsecond=g,this._delta_hours=k,this._delta_minutes=l,this._delta_seconds=m,this._delta_microseconds=n},pyson:function(){var a=Sao.PYSON.DateTime._super.pyson.call(this);return a.__class__="DateTime",a.h=this._hour,a.m=this._minute,a.s=this._second,a.ms=this._microsecond,a.dh=this._delta_hours,a.dm=this._delta_minutes,a.ds=this._delta_seconds,a.dms=this._delta_microseconds,a}}),Sao.PYSON.DateTime.eval_=function(a,b){var c=Sao.DateTime(a.y,a.M&&a.M-1,a.d,a.h,a.m,a.s,a.ms&&a.ms/1e3);return a.dy&&c.add(a.dy,"y"),a.dM&&c.add(a.dM,"M"),a.dd&&c.add(a.dd,"d"),a.dh&&c.add(a.dh,"h"),a.dm&&c.add(a.dm,"m"),a.ds&&c.add(a.ds,"s"),a.dms&&c.add(a.dms/1e3,"ms"),c},Sao.PYSON.Len=Sao.class_(Sao.PYSON.PYSON,{init:function(a){if(Sao.PYSON.Len._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["object","string"]).length||jQuery(["object","string"]).not(a.types()).length)throw"value must be an object or a string"}else if("object"!=typeof a&&"string"!=typeof a)throw"value must be an object or a string";this._value=a},pyson:function(){return{__class__:"Len",v:this._value}},types:function(){return["integer"]}}),Sao.PYSON.Len.eval_=function(a,b){return"object"==typeof a.v?Object.keys(a.v).length:a.v.length}}(),function(){"use strict";Sao.Session=Sao.class_(Object,{init:function(a,b){this.user_id=null,this.session=null,this.prm=jQuery.when(),this.database=a,this.login=b,this.context={},Sao.Session.current_session||(Sao.Session.current_session=this)},do_login:function(a,b){var c=jQuery.Deferred(),d=Sao.common.processing.show(),e={method:"common.db.login",params:[a,b]},f=jQuery.ajax({contentType:"application/json",data:JSON.stringify(e),dataType:"json",url:"/"+this.database,type:"post",complete:[function(){Sao.common.processing.hide(d)}]}),g=function(a){null===a?(Sao.common.warning.run("",Sao.i18n.gettext("Unable to reach the server.")),c.reject()):a.error?(Sao.common.error.run(a.error[0],a.error[1]),c.reject()):a.result?(this.user_id=a.result[0],this.session=a.result[1],c.resolve()):(this.user_id=null,this.session=null,c.reject())};return f.success(g.bind(this)),f.error(c.reject),c.promise()},do_logout:function(){if(!this.user_id||!this.session)return jQuery.when();var a={method:"common.db.logout",params:[]},b=Sao.rpc(a,this);return this.database=null,this.login=null,this.user_id=null,this.session=null,b},reload_context:function(){var a={method:"model.res.user.get_preferences",params:[!0,{}]},b=jQuery.extend({},this);b.context={};var c=Sao.rpc(a,b);return c.then(function(a){this.context=a}.bind(this))}}),Sao.Session.login_dialog=function(){var a=new Sao.Dialog(Sao.i18n.gettext("Login"),"lg");return a.database_select=jQuery("<select/>",{"class":"form-control",id:"login-database"}).hide(),a.database_input=jQuery("<input/>",{"class":"form-control",id:"login-database"}).hide(),a.login_input=jQuery("<input/>",{"class":"form-control",id:"login-login",placeholder:Sao.i18n.gettext("Login")}),a.password_input=jQuery("<input/>",{"class":"form-control",type:"password",id:"login-password",placeholder:Sao.i18n.gettext("Password")}),a.body.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-database"}).append(Sao.i18n.gettext("Database"))).append(a.database_select).append(a.database_input)).append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-login"}).append(Sao.i18n.gettext("Login"))).append(a.login_input)).append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-password"}).append(Sao.i18n.gettext("Password"))).append(a.password_input)),a.button=jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Login")).appendTo(a.footer),a},Sao.Session.get_credentials=function(){var a=jQuery.Deferred(),b=window.location.hash.replace(/^(#(!|))/,"")||null,c=Sao.Session.login_dialog(),d=function(){return c.modal.find("input,select").filter(":visible").filter(function(){return!jQuery(this).val()})},e=function(){var b=c.login_input.val(),e=c.password_input.val(),f=f||c.database_select.val()||c.database_input.val();if(c.modal.find(".has-error").removeClass("has-error"),!(b&&e&&f))return void d().closest(".form-group").addClass("has-error");c.button.focus(),c.button.prop("disabled",!0);var g=new Sao.Session(f,b);g.do_login(b,e).then(function(){a.resolve(g),c.modal.remove()},function(){c.button.prop("disabled",!1),c.password_input.val(""),d().closest(".form-group").addClass("has-error"),d().first().focus()})};return c.modal.modal({backdrop:!1,keyboard:!1}),c.modal.find("form").unbind().submit(function(a){e(),a.preventDefault()}),jQuery.when(Sao.DB.list()).then(function(a){var e;jQuery.isEmptyObject(a)?e=c.database_input:(e=c.database_select,a.forEach(function(a){e.append(jQuery("<option/>",{value:a,text:a}))})),e.show(),e.val(b||""),d().first().focus()}),a.promise()},Sao.Session.password_dialog=function(){var a=new Sao.Dialog(Sao.i18n.gettext("Password"),"lg");return a.password_input=jQuery("<input/>",{"class":"form-control",type:"password",id:"password-password",placeholder:Sao.i18n.gettext("Password")}),a.body.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"for":"password-password"}).append(Sao.i18n.gettext("Password"))).append(a.password_input)),a.button=jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(a.footer),a},Sao.Session.renew=function(a){if("pending"==a.prm.state())return a.prm;var b=jQuery.Deferred();a.prm=b.promise();var c=Sao.Session.password_dialog();if(!a.login)return b.reject(),a.prm;var d=function(){var d=c.password_input.val();c.button.focus(),c.button.prop("disabled",!0),a.do_login(a.login,d).then(function(){b.resolve(),c.modal.remove()},function(){c.button.prop("disabled",!1),c.password_input.val("").focus()})};return c.modal.modal({backdrop:!1,keyboard:!1}),c.modal.on("shown.bs.modal",function(){c.password_input.focus()}),c.modal.find("form").unbind().submit(function(a){d(),a.preventDefault()}),c.modal.modal("show"),a.prm},Sao.Session.current_session=null,Sao.DB={},Sao.DB.list=function(){var a=Sao.common.processing.show();return jQuery.ajax({contentType:"application/json",data:JSON.stringify({method:"common.db.list",params:[null,null]}),dataType:"json",url:"/",type:"post",complete:[function(){Sao.common.processing.hide(a)}]}).then(function(a){return a.result})}}(),function(){"use strict";Sao.Model=Sao.class_(Object,{init:function(a,b){b=b||{},this.name=a,this.session=Sao.Session.current_session,this.fields={}},add_fields:function(a){for(var b in a)if(a.hasOwnProperty(b)&&!(b in this.fields)){var c=a[b],d=Sao.field.get(c.type);this.fields[b]=new d(c)}},execute:function(a,b,c){void 0===c&&(c={});var d={method:"model."+this.name+"."+a,params:b.concat(c)};return Sao.rpc(d,this.session)},find:function(a,b,c,d,e){b||(b=0);var f=this,g=this.execute("search",[a,b,c,d],e),h=function(a){return Sao.Group(f,e,a.map(function(a){return new Sao.Record(f,a)}))};return g.pipe(h)},delete_:function(a){if(jQuery.isEmptyObject(a))return jQuery.when();var b=a[0],c=b.group.root_group();console.assert(a.every(function(a){return a.model.name==b.model.name}),"records not from the same model"),console.assert(a.every(function(a){return a.group.root_group()==b.group.root_group()}),"records not from the same root group"),a=a.filter(function(a){return a.id>=0});var d={};d._timestamp={},a.forEach(function(a){jQuery.extend(d._timestamp,a.get_timestamp())});var e=a.map(function(a){return a.id});return c.on_write_ids(e).then(function(a){return a=a.filter(function(b){return!~a.indexOf(b)}),this.execute("delete",[e],d).then(function(){c.reload(a)})}.bind(this))},copy:function(a,b){if(jQuery.isEmptyObject(a))return jQuery.when();var c=a.map(function(a){return a.id});return this.execute("copy",[c,{}],b)}}),Sao.Group=function(a,b,c){return c.prm=jQuery.when(),c.model=a,c._context=b,c.on_write=[],c.parent=void 0,c.screens=[],c.parent_name="",c.children=[],c.child_name="",c.parent_datetime_field=void 0,c.record_removed=[],c.record_deleted=[],c.__readonly=!1,c.skip_model_access=!1,c.forEach(function(a,b,c){a.group=c}),c.get_readonly=function(){return this.context._datetime||!Sao.common.MODELACCESS.get(this.model.name).write&&!this.skip_model_access?!0:this.__readonly},c.set_readonly=function(a){this.__readonly=a},c.load=function(a,b){var c,d,e=[];for(c=0,d=a.length;d>c;c++){var f=a[c],g=this.get(f);g||(g=new Sao.Record(this.model,f),g.group=this,this.push(g)),e.push(g)}var h,i=[];for(c=0,d=this.record_removed.length;d>c;c++)h=this.record_removed[c],~a.indexOf(h.id)||i.push(h);this.record_removed=i;var j=[];for(c=0,d=this.record_deleted.length;d>c;c++)h=this.record_deleted[c],~a.indexOf(h.id)||j.push(h);this.record_deleted=j,e.length&&b&&this.changed()},c.get=function(a){for(var b=0,c=this.length;c>b;b++){var d=this[b];if(d.id==a)return d}},c.new_=function(a,b){var c=new Sao.Record(this.model,b);return c.group=this,a&&c.default_get(),c},c.add=function(a,b){(void 0===b||-1==b)&&(b=this.length),a.group!=this&&(a.group=this),this.splice(b,0,a);for(var c in this.record_removed)c.id==a.id&&this.record_removed.splice(this.record_removed.indexOf(c),1);for(var d in this.record_deleted)d.id==a.id&&this.record_deleted.splice(this.record_deleted.indexOf(d),1);if(a._changed.id=!0,this.changed(),this.parent&&this.model.fields[this.parent_name]){var e=this.model.fields[this.parent_name];if(e instanceof Sao.field.Many2One||e instanceof Sao.field.Reference){var f=[this.parent.id,""];e instanceof Sao.field.Reference&&(f=[this.parent.model.name,f]),e.set_client(a,f)}}return a},c.remove=function(a,b,c,d){void 0===c&&(c=!0);this.indexOf(a);a.id>=0&&(b?(~this.record_deleted.indexOf(a)&&this.record_deleted.splice(this.record_deleted.indexOf(a),1),this.record_removed.push(a)):(~this.record_removed.indexOf(a)&&this.record_removed.splice(this.record_removed.indexOf(a),1),this.record_deleted.push(a))),a.group.parent&&(a.group.parent._changed.id=!0),c&&(a._changed.id=!0),(!a.group.parent||a.id<0||d)&&this._remove(a),a.group.changed(),a.group.root_group().screens.forEach(function(a){a.display()})},c._remove=function(a){var b=this.indexOf(a);this.splice(b,1)},c.unremove=function(a){this.record_removed.splice(this.record_removed.indexOf(a),1),this.record_deleted.splice(this.record_deleted.indexOf(a),1),a.group.changed(),a.group.root_group().screens.forEach(function(a){a.display()})},c.changed=function(){if(!this.parent)return jQuery.when();this.parent._changed[this.child_name]=!0;var a=jQuery.Deferred(),b=this.parent.model.fields[this.child_name].changed(this.parent);return b?b.then(function(){this.parent.validate(null,!0).done(function(){this.parent.group.changed().done(a.resolve)}.bind(this))}.bind(this)):a.resolve(),a},c.root_group=function(){for(var a=this,b=this.parent;b;)a=b.group,b=b.parent;return a},c.save=function(){var a=[];return this.forEach(function(b){a.push(b.save())}),jQuery.isEmptyObject(this.record_deleted)||a.push(this.model.delete_(this.record_deleted)),jQuery.when.apply(jQuery,a)},c.written=function(a){return"number"==typeof a&&(a=[a]),this.on_write_ids(a).then(function(b){b=b.filter(function(b){return!~a.indexOf(b)}),this.root_group().reload(b)}.bind(this))},c.reload=function(a){this.children.forEach(function(b){b.reload(a)}),a.forEach(function(a){
-var b=this.get(a);b&&jQuery.isEmptyObject(b._changed)&&b.cancel()}.bind(this))},c.on_write_ids=function(a){var b=[],c=[];return this.on_write.forEach(function(d){var e=this.model.execute(d,[a],this._context).then(function(a){jQuery.extend(c,a)});b.push(e)}.bind(this)),jQuery.when.apply(jQuery,b).then(function(){return c.filter(function(a,b,c){return b==c.indexOf(a)})})},c.set_parent=function(a){this.parent=a,a&&a.model.name==this.model.name&&this.parent.group.children.push(this)},c.destroy=function(){if(this.parent){var a=this.parent.group.children.indexOf(this);~a&&this.parent.group.children.splice(a,1)}this.parent=null},c.domain=function(){var a=[];if(this.screens.forEach(function(b){b.attributes.domain&&a.push(b.attributes.domain)}),this.parent&&this.child_name){var b=this.parent.model.fields[this.child_name];return[a,b.get_domain(this.parent)]}return a},c.context=function(){var a=jQuery.extend({},this._context);if(this.parent&&(jQuery.extend(a,this.parent.get_context()),this.child_name in this.parent.model.fields)){var b=this.parent.model.fields[this.child_name];jQuery.extend(a,b.get_context(this.parent))}return jQuery.extend(a,this._context),this.parent_datetime_field&&(a._datetime=this.parent.get_eval()[this.parent_datetime_field]),a},c.set_context=function(a){this._context=jQuery.extend({},a)},c.clean4inversion=function(a){if(jQuery.isEmptyObject(a))return[];var b=new Sao.common.DomainInversion,c=a[0],d=a.slice(1);if(~["AND","OR"].indexOf(c));else if(b.is_leaf(c)){var e=c[0];e in this.model.fields&&this.model.fields[e].description.readonly&&(c=[])}else c=this.clean4inversion(c);return[c].concat(this.clean4inversion(d))},c.domain4inversion=function(){var a=this.domain();return this.__domain4inversion&&Sao.common.compare(this.__domain4inversion[0],a)||(this.__domain4inversion=[a,this.clean4inversion(a)]),this.__domain4inversion[1]},c.get_by_path=function(a){a=jQuery.extend([],a);var b=null,c=this,d=function(){if(jQuery.isEmptyObject(a))return b;var e=a[0][0],f=a[0][1];return a.splice(0,1),b=c.get(f),b?e?b.load(e).then(function(){return c=b._values[e],c?d():null}):d():null};return jQuery.when().then(d)},c},Sao.Record=Sao.class_(Object,{id_counter:-1,init:function(a,b){this.model=a,this.group=Sao.Group(a,{},[]),this.id=b||Sao.Record.prototype.id_counter--,this._values={},this._changed={},this._loaded={},this.fields={},this._timestamp=null,this.attachment_count=-1,this.state_attrs={},this.autocompletion={},this.exception=!1},has_changed:function(){return!jQuery.isEmptyObject(this._changed)},save:function(a){void 0===a&&(a=!1);var b=this.get_context(),c=jQuery.when(),d=this.get();if(this.id<0||!jQuery.isEmptyObject(d)){if(this.id<0){c=this.model.execute("create",[[d]],b);var e=function(a){this.id=a[0]};c.done(e.bind(this))}else jQuery.isEmptyObject(d)||(b._timestamp=this.get_timestamp(),c=this.model.execute("write",[[this.id],d],b));c=c.done(function(){return this.cancel(),a?this.reload():void 0}.bind(this)),this.group&&(c=c.done(function(){return this.group.written(this.id)}.bind(this)))}return this.group.parent&&(delete this.group.parent._changed[this.group.child_name],c=c.done(function(){return this.group.parent.save(a)}.bind(this))),c},reload:function(a){if(this.id<0)return jQuery.when();if(a){var b=a.map(function(a){return this.load(a)}.bind(this));return jQuery.when.apply(jQuery,b)}return this.load("*")},load:function(a){var b,c;if(this.id<0||a in this._loaded)return jQuery.when();if("pending"==this.group.prm.state())return c=jQuery.Deferred(),this.group.prm.then(function(){this.load(a).then(c.resolve,c.reject)}.bind(this)),c;var d={};d[this.id]=this;var e;if("*"==a){e="eager";for(b in this.model.fields)if(this.model.fields.hasOwnProperty(b)){var f=this.model.fields[b].description.loading||"eager";if("eager"!=f){e="lazy";break}}}else e=this.model.fields[a].description.loading||"eager";var g=[];if("eager"==e)for(b in this.model.fields)this.model.fields.hasOwnProperty(b)&&"eager"==(this.model.fields[b].description.loading||"eager")&&g.push(b);else g=Object.keys(this.model.fields);g=g.filter(function(a,b,c){return!(a in this._loaded)}.bind(this));var h=g.slice(),i=["many2one","one2one","reference"];for(var j in g){b=g[j];var k=this.model.fields[b].description;~i.indexOf(k.type)&&h.push(b+".rec_name")}~g.indexOf("rec_name")||h.push("rec_name"),h.push("_timestamp");var l=jQuery.extend({},this.get_context());if("eager"==e){var m,n,o=parseInt(Sao.config.limit/h.length,10),p=function(b){return!(a in b._loaded)&&b.id>=0},q=function(a){return p(a)&&void 0===d[a.id]&&(a.group===this.group||JSON.stringify(a.get_context())===JSON.stringify(l))}.bind(this);this.group.parent&&this.group.parent.model.name==this.model.name?(m=[],m=m.concat.apply(m,this.group.parent.group.children),n=q):(m=this.group,n=p);var r=m.indexOf(this);if(~r)for(var s=m.length,t=1;Object.keys(d).length<o&&(r-t>=0||s>r+t)&&2*o>t;){var u;r-t>=0&&(u=m[r-t],n(u)&&(d[u.id]=u)),s>r+t&&(u=m[r+t],n(u)&&(d[u.id]=u)),t++}}for(b in this.model.fields)this.model.fields.hasOwnProperty(b)&&"binary"==this.model.fields[b].description.type&&~h.indexOf(b,h)&&(l[this.model.name+"."+b]="size");c=this.model.execute("read",[Object.keys(d).map(function(a){return parseInt(a,10)}),h],l);var v=function(a,b){void 0===b&&(b=!1);var c={};a.forEach(function(a,b,d){c[a.id]=a});for(var e in d)if(d.hasOwnProperty(e)){var f=d[e];f.exception||(f.exception=b);var g=c[e];if(f&&g){for(var h in this._changed)this._changed.hasOwnProperty(h)&&delete g[h];f.set(g)}}}.bind(this),w=function(){var a,b=[];for(var c in d){a={id:c};for(var e in h)a[h[e]]=null;b.push(a)}v(b,!0)};return this.group.prm=c.then(v,w),this.group.prm},set:function(a){var b,c,d={};for(b in a)if(a.hasOwnProperty(b))if(c=a[b],"_timestamp"!=b)if(b in this.model.fields){if(this.model.fields[b]instanceof Sao.field.One2Many&&(d[b]=c),this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference){var e=b+".rec_name";a.hasOwnProperty(e)?this._values[e]=a[e]:this._values.hasOwnProperty(e)&&delete this._values[e]}this.model.fields[b].set(this,c),this._loaded[b]=!0}else"rec_name"==b&&(this._values[b]=c);else this._timestamp||(this._timestamp=c);for(b in d)c=d[b],this.model.fields[b].set(this,c),this._loaded[b]=!0},get:function(){var a={};for(var b in this.model.fields)if(this.model.fields.hasOwnProperty(b)){var c=this.model.fields[b];c.description.readonly||void 0===this._changed[b]&&this.id>=0||(a[b]=c.get(this))}return a},invalid_fields:function(){var a={};for(var b in this.model.fields){var c=this.model.fields[b],d=c.get_state_attrs(this).invalid;d&&(a[b]=d)}return a},get_context:function(){return this.group.context()},field_get:function(a){return this.model.fields[a].get(this)},field_set:function(a,b){this.model.fields[a].set(this,b)},field_get_client:function(a){return this.model.fields[a].get_client(this)},field_set_client:function(a,b,c){this.model.fields[a].set_client(this,b,c)},default_get:function(){var a=[];if(!jQuery.isEmptyObject(this.model.fields)){var b=this.model.execute("default_get",[Object.keys(this.model.fields)],this.get_context()),c=function(a){if(this.group.parent&&this.group.parent_name in this.group.model.fields){var b=this.group.model.fields[this.group.parent_name];b instanceof Sao.field.Reference?a[this.group.parent_name]=[this.group.parent.model.name,this.group.parent.id]:b.description.relation==this.group.parent.model.name&&(a[this.group.parent_name]=this.group.parent.id)}return a}.bind(this);b=b.pipe(c).then(function(a){return this.set_default(a),a}.bind(this)),a.push(b)}for(var d in this.model.fields){var e=this.model.fields[d];e.description.autocomplete&&e.description.autocomplete.length>0&&a.push(this.do_autocomplete(d))}return jQuery.when.apply(a)},set_default:function(a){var b=[];for(var c in a)if(a.hasOwnProperty(c)){var d=a[c];if(c in this.model.fields){if(this.model.fields[c]instanceof Sao.field.Many2One||this.model.fields[c]instanceof Sao.field.Reference){var e=c+".rec_name";a.hasOwnProperty(e)?this._values[e]=a[e]:this._values.hasOwnProperty(e)&&delete this._values[e]}this.model.fields[c].set_default(this,d),this._loaded[c]=!0,b.push(c)}}this.on_change(b),this.on_change_with(b),this.validate(null,!0).then(function(){this.group.root_group().screens.forEach(function(a){a.display()})}.bind(this))},get_timestamp:function(){var a={};a[this.model.name+","+this.id]=this._timestamp;for(var b in this.model.fields)this.model.fields.hasOwnProperty(b)&&b in this._loaded&&jQuery.extend(a,this.model.fields[b].get_timestamp(this));return a},get_eval:function(){var a={};for(var b in this.model.fields)!this.model.fields.hasOwnProperty(b)&&this.id>=0||(a[b]=this.model.fields[b].get_eval(this));return a},get_on_change_value:function(a){var b={};for(var c in this.model.fields)a&&~a.indexOf(c)||(!(this.id>=0)||this._loaded[c]&&this._changed[c])&&(b[c]=this.model.fields[c].get_on_change_value(this));return b.id=this.id,b},_get_on_change_args:function(a){var b={},c=Sao.common.EvalEnvironment(this,"on_change");return a.forEach(function(a){var d=c;a.split(".").forEach(function(a){void 0!==d&&(d=d[a])}),b[a]=d}),b.id=this.id,b},on_change:function(a){var b={};if(a.forEach(function(a){var c=this.model.fields[a].description.on_change;jQuery.isEmptyObject(c)||(b=jQuery.extend(b,this._get_on_change_args(c)))}.bind(this)),jQuery.isEmptyObject(b))return jQuery.when();var c=this.model.execute("on_change",[b,a],this.get_context());return c.then(function(a){a.forEach(this.set_on_change.bind(this))}.bind(this))},on_change_with:function(a){var b,c,d={},e={},f={};for(b in this.model.fields)if(this.model.fields.hasOwnProperty(b)&&(c=this.model.fields[b].description.on_change_with,!jQuery.isEmptyObject(c))){for(var g=0;g<a.length&&!~c.indexOf(a[g]);g++);g>=a.length||(jQuery.isEmptyObject(Sao.common.intersect(Object.keys(d).sort(),c.sort()))?(d[b]=!0,e=jQuery.extend(e,this._get_on_change_args(c)),(this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference)&&delete this._values[b+".rec_name"]):f[b]=!0)}var h,i=[];jQuery.isEmptyObject(d)||(h=this.model.execute("on_change_with",[e,Object.keys(d)],this.get_context()),i.push(h.then(this.set_on_change.bind(this))));var j=function(a){return function(b){this.model.fields[a].set_on_change(this,b)}};for(b in f)f.hasOwnProperty(b)&&(c=this.model.fields[b].description.on_change_with,e=this._get_on_change_args(c),h=this.model.execute("on_change_with_"+b,[e],this.get_context()),i.push(h.then(j(b).bind(this))));return jQuery.when.apply(jQuery,i)},set_on_change:function(a){var b,c;for(b in a)if(a.hasOwnProperty(b)&&(c=a[b],b in this.model.fields)){if(this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference){var d=b+".rec_name";a.hasOwnProperty(d)?this._values[d]=a[d]:this._values.hasOwnProperty(d)&&delete this._values[d]}this.model.fields[b].set_on_change(this,c)}},autocomplete_with:function(a){var b=[];for(var c in this.model.fields){var d=this.model.fields[c],e=d.description.autocomplete||[];~e.indexOf(a)&&b.push(this.do_autocomplete(c))}return jQuery.when.apply(b)},do_autocomplete:function(a){this.autocompletion[a]=[];var b=this.model.fields[a],c=b.description.autocomplete,d=this._get_on_change_args(c),e=this.model.execute("autocomplete_"+a,[d],this.get_context());return e.then(function(b){this.autocompletion[a]=b}.bind(this))},expr_eval:function(a){if("string"!=typeof a)return a;var b=jQuery.extend({},this.get_context());if(b.context=jQuery.extend({},this.model.session.context,b),jQuery.extend(b,this.get_eval()),b.active_model=this.model.name,b.active_id=this.id,b._user=this.model.session.user_id,this.group.parent&&this.group.parent_name){var c=Sao.common.EvalEnvironment(this.group.parent);b["_parent_"+this.group.parent_name]=c}return new Sao.PYSON.Decoder(b).decode(a)},rec_name:function(){var a=this.model.execute("read",[[this.id],["rec_name"]],this.get_context());return a.then(function(a){return a[0].rec_name})},validate:function(a,b,c,d){var e=function(){var d=!0,e=[];this.group.screens.forEach(function(a){a.exclude_field&&e.push(a.exclude_field)});for(var f in this.model.fields)if(this.model.fields.hasOwnProperty(f)){var g=this.model.fields[f];(!a||~a.indexOf(f))&&(g.get_state_attrs(this).readonly||~e.indexOf(f)||g.validate(this,b,c)||(d=!1))}return d}.bind(this);return d?e():this._check_load(a).then(e)},pre_validate:function(){if(jQuery.isEmptyObject(this._changed))return jQuery.Deferred().resolve(!0);var a=this._get_on_change_args(Object.keys(this._changed));return this.model.execute("pre_validate",[a],this.get_context()).then(function(){return!0},function(){return!1})},cancel:function(){this._loaded={},this._changed={},this._timestamp=null},_check_load:function(a){return this.get_loaded(a)?jQuery.when():this.reload(a)},get_loaded:function(a){if(!jQuery.isEmptyObject(a)){var b=!0;return a.forEach(function(a){!(a in this._loaded)|!(a in this._changed)&&(b=!1)}.bind(this)),b}return Sao.common.compare(Object.keys(this.model.fields).sort(),Object.keys(this._loaded).sort())},root_parent:function(){for(var a=this;!a.group.parent;)a=a.group.parent;return a},get_path:function(a){for(var b=[],c=this,d="";c&&(b.push([d,c.id]),c.group!==a);)d=c.group.child_name,c=c.group.parent;return b.reverse(),b},deleted:function(){return Boolean(~this.group.record_deleted.indexOf(this))},removed:function(){return Boolean(~this.group.record_removed.indexOf(this))},readonly:function(){return this.deleted()||this.removed()||this.exception},set_field_context:function(){for(var a in this.model.fields)if(this.model.fields.hasOwnProperty(a)){var b=this.model.fields[a],c=this._values[a];if(c&&c.set_context){var d=b.description.context;d&&c.set_context(this.expr_eval(d))}}},get_attachment_count:function(a){var b=jQuery.Deferred();return this.id<0?(b.resolve(0),b):(this.attachment_count<0||a?b=Sao.rpc({method:"model.ir.attachment.search_count",params:[[["resource","=",this.model.name+","+this.id]],this.get_context()]},this.model.session).then(function(a){return this.attachment_count=a,a}.bind(this)):b.resolve(this.attachment_count),b)}}),Sao.field={},Sao.field.get=function(a){switch(a){case"char":return Sao.field.Char;case"selection":return Sao.field.Selection;case"datetime":return Sao.field.DateTime;case"date":return Sao.field.Date;case"time":return Sao.field.Time;case"timedelta":return Sao.field.TimeDelta;case"float":return Sao.field.Float;case"numeric":return Sao.field.Numeric;case"integer":return Sao.field.Integer;case"boolean":return Sao.field.Boolean;case"many2one":return Sao.field.Many2One;case"one2one":return Sao.field.One2One;case"one2many":return Sao.field.One2Many;case"many2many":return Sao.field.Many2Many;case"reference":return Sao.field.Reference;case"binary":return Sao.field.Binary;case"dict":return Sao.field.Dict;default:return Sao.field.Char}},Sao.field.Field=Sao.class_(Object,{_default:null,init:function(a){this.description=a,this.name=a.name},set:function(a,b){a._values[this.name]=b},get:function(a){var b=a._values[this.name];return void 0===b&&(b=this._default),b},set_client:function(a,b,c){var d=this.get(a);this.set(a,b),JSON.stringify(d)!=JSON.stringify(this.get(a))?(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){a.group.changed().done(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})})):c&&(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})}))},get_client:function(a){return this.get(a)},set_default:function(a,b){a._values[this.name]=b,a._changed[this.name]=!0},set_on_change:function(a,b){a._values[this.name]=b,a._changed[this.name]=!0},changed:function(a){var b=[];return this.get_state_attrs(a).readonly?jQuery.when():(b.push(a.on_change([this.name])),b.push(a.on_change_with([this.name])),b.push(a.autocomplete_with(this.name)),a.set_field_context(),jQuery.when.apply(jQuery,b))},get_timestamp:function(a){return{}},get_context:function(a){var b=jQuery.extend({},a.get_context());return a.group.parent&&jQuery.extend(b,a.group.parent.get_context()),jQuery.extend(b,a.expr_eval(this.description.context||{})),b},get_domains:function(a,b){var c=new Sao.common.DomainInversion,d=c.domain_inversion([a.group.domain4inversion(),b||[]],this.name,Sao.common.EvalEnvironment(a));"boolean"!=typeof d||d?"boolean"==typeof d&&d&&(d=[]):d=[["id","=",null]];var e=a.expr_eval(this.description.domain||[]);return[d,e]},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(c),d])},validation_domains:function(a,b){var c=new Sao.common.DomainInversion;return c.concat(this.get_domains(a,b))},get_eval:function(a){return this.get(a)},get_on_change_value:function(a){return this.get_eval(a)},set_state:function(a,b){void 0===b&&(b=["readonly","required","invisible"]);var c=a.expr_eval(this.description.states||{});b.forEach(function(b){"readonly"==b&&this.description.readonly||(void 0!==c[b]?this.get_state_attrs(a)[b]=c[b]:void 0!==this.description[b]&&(this.get_state_attrs(a)[b]=this.description[b]))}.bind(this)),(a.group.get_readonly()||this.get_state_attrs(a).domain_readonly)&&(this.get_state_attrs(a).readonly=!0)},get_state_attrs:function(a){return this.name in a.state_attrs||(a.state_attrs[this.name]=jQuery.extend({},this.description)),(a.group.get_readonly()||a.readonly())&&(a.state_attrs[this.name].readonly=!0),a.state_attrs[this.name]},check_required:function(a){var b=this.get_state_attrs(a);return 1!=b.required||this.get(a)||1==b.readonly?!0:!1},validate:function(a,b,c){if(this.description.readonly)return!0;var d=!1;this.get_state_attrs(a).domain_readonly=!1;var e=new Sao.common.DomainInversion,f=e.simplify(this.validation_domains(a,c));if(b||this.check_required(a)||(d="required"),"boolean"==typeof f)f||(d="domain");else if(Sao.common.compare(f,[["id","=",null]]))d="domain";else{var g=e.unique_value(f),h=g[0],i=g[1],j=g[2];if(h){j===!1&&(j=null);var k,l=!0;k=e.merge(jQuery.isEmptyObject(a.group.domain())?f:a.group.domain());var m="AND"==k[0];if(i.contains(".")){var n=i.split(".",1)[0],o=i.split(".",1)[1],p=[];m&&e.localize_domain(k.slice(1)).forEach(function(a){p.push(a)}),"id"==o&&~p.indexOf(n)||(l=!1)}l&&!c&&(this.set_client(a,j),this.get_state_attrs(a).domain_readonly=m)}e.eval_domain(f,Sao.common.EvalEnvironment(a))||(d=f)}return this.get_state_attrs(a).invalid=d,!d}}),Sao.field.Char=Sao.class_(Sao.field.Field,{_default:"",get:function(a){return Sao.field.Char._super.get.call(this,a)||this._default}}),Sao.field.Selection=Sao.class_(Sao.field.Field,{_default:null}),Sao.field.DateTime=Sao.class_(Sao.field.Field,{_default:null,time_format:function(a){return a.expr_eval(this.description.format)},set_client:function(a,b,c){var d;b&&(b.isTime?(d=this.get(a),b=d?Sao.DateTime.combine(d,b):null):b.isDate&&(d=this.get(a),d&&(b=Sao.DateTime.combine(b,d)))),Sao.field.DateTime._super.set_client.call(this,a,b,c)},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)}}),Sao.field.Date=Sao.class_(Sao.field.Field,{_default:null,set_client:function(a,b,c){b&&!b.isDate&&(b.isDate=!0,b.isDateTime=!1),Sao.field.Date._super.set_client.call(this,a,b,c)},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)}}),Sao.field.Time=Sao.class_(Sao.field.Field,{_default:null,time_format:function(a){return a.expr_eval(this.description.format)},set_client:function(a,b,c){b&&(b.isDate||b.isDateTime)&&(b=Sao.Time(b.hour(),b.minute(),b.second(),b.millisecond())),Sao.field.Time._super.set_client.call(this,a,b,c)}}),Sao.field.TimeDelta=Sao.class_(Sao.field.Field,{_default:null,converter:function(a){return a.model.session.context[this.description.converter]},set_client:function(a,b,c){"string"==typeof b&&(b=Sao.common.timedelta.parse(b,this.converter(a))),Sao.field.TimeDelta._super.set_client.call(this,a,b,c)},get_client:function(a){var b=Sao.field.TimeDelta._super.get_client.call(this,a);return Sao.common.timedelta.format(b,this.converter(a))}}),Sao.field.Float=Sao.class_(Sao.field.Field,{_default:null,digits:function(a,b){void 0===b&&(b=1);var c=a.expr_eval(this.description.digits);if(c&&c.every(function(a){return null!==a})){var d=Math.round(Math.log(Math.abs(b))/Math.LN10);return[c[0]+d,c[1]-d]}},check_required:function(a){var b=this.get_state_attrs(a);return 1==b.required&&null===this.get(a)&&1!=b.readonly?!1:!0},convert:function(a){return a?(a=Number(a),isNaN(a)&&(a=this._default),a):null},apply_factor:function(a,b){return null!==a&&(a/=b),a},set_client:function(a,b,c,d){void 0===d&&(d=1),b=this.apply_factor(this.convert(b),d),Sao.field.Float._super.set_client.call(this,a,b,c)},get_client:function(a,b){void 0===b&&(b=1);var c=this.get(a);if(null!==c){var d=this.digits(a,b);return d?(c*b).toFixed(d[1]):""+c*b}return""}}),Sao.field.Numeric=Sao.class_(Sao.field.Float,{convert:function(a){return a?(a=new Sao.Decimal(a),isNaN(a.valueOf())&&(a=this._default),a):null},apply_factor:function(a,b){return a=Sao.field.Numeric._super.apply_factor(a,b),new Sao.Decimal(a)}}),Sao.field.Integer=Sao.class_(Sao.field.Float,{convert:function(a){return a=parseInt(a,10),isNaN(a)&&(a=this._default),a}}),Sao.field.Boolean=Sao.class_(Sao.field.Field,{_default:!1,set_client:function(a,b,c){b=Boolean(b),Sao.field.Boolean._super.set_client.call(this,a,b,c)},get:function(a){return Boolean(a._values[this.name])},get_client:function(a){return Boolean(a._values[this.name])}}),Sao.field.Many2One=Sao.class_(Sao.field.Field,{_default:null,get_client:function(a){var b=a._values[this.name+".rec_name"];return void 0===b&&(this.set(a,this.get(a)),b=a._values[this.name+".rec_name"]||""),b},set:function(a,b){var c=a._values[this.name+".rec_name"]||"",d=function(b){a._values[this.name+".rec_name"]=b[0].rec_name};if(!c&&b>=0&&null!==b){var e=a.model.fields[this.name].description.relation;Sao.rpc({method:"model."+e+".read",params:[[b],["rec_name"],a.get_context()]},a.model.session).done(d.bind(this))}else d.call(this,[{rec_name:c}]);a._values[this.name]=b},set_client:function(a,b,c){var d;b instanceof Array?(d=b[1],b=b[0]):d=b==this.get(a)?a._values[this.name+".rec_name"]||"":"",a._values[this.name+".rec_name"]=d,Sao.field.Many2One._super.set_client.call(this,a,b,c)},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(e.inverse_leaf(c),this.name),d])},get_on_change_value:function(a){return a.group.parent_name==this.name&&a.group.parent?a.group.parent.get_on_change_value([a.group.child_name]):Sao.field.Many2One._super.get_on_change_value.call(this,a)}}),Sao.field.One2One=Sao.class_(Sao.field.Many2One,{}),Sao.field.One2Many=Sao.class_(Sao.field.Field,{init:function(a){Sao.field.One2Many._super.init.call(this,a)},_default:null,_set_value:function(a,b,c){this._set_default_value(a);var d,e=a._values[this.name];d=b instanceof Array&&!isNaN(parseInt(b[0],10))?"list ids":"list values";var f=jQuery.when();if("list values"==d&&!jQuery.isEmptyObject(b)){var g=this.get_context(a),h={};for(var i in b)if(b.hasOwnProperty(i))for(var j in i)i.hasOwnProperty(j)&&(h[j]=!0);if(!jQuery.isEmptyObject(h)){var k={method:"model."+this.description.relation+".fields_get",params:[Object.keys(h),g]};f=Sao.rpc(k,a.model.session)}}var l=function(f){if(jQuery.isEmptyObject(f)||e.model.add_fields(f),a._values[this.name]=e,"list ids"==d){for(var g=0,h=e.length;h>g;g++){var i=e[g];e.remove(i,!0)}e.load(b)}else for(var j in b)if(b.hasOwnProperty(j)){var k=e.new_(!1);c?(k.set_default(j),e.add(k)):(k.id*=1,k.set(j),e.push(k))}};return f.pipe(l.bind(this))},set:function(a,b,c){void 0===c&&(c=!1);var d,e=a._values[this.name];return void 0!==e?(d=e.model,e.destroy()):d=a.model.name==this.description.relation?a.model:new Sao.Model(this.description.relation),a._values[this.name]=void 0,this._set_default_value(a,d),this._set_value(a,b,c)},get:function(a){var b=a._values[this.name];if(void 0===b)return[];for(var c=b.record_removed,d=b.record_deleted,e=[],f=this.description.relation_field||"",g=[],h=[],i=[],j=0,k=b.length;k>j;j++){var l=b[j];if(!~c.indexOf(l)&&!~d.indexOf(l)){var m;l.id>=0?(m=l.get(),delete m[f],l.has_changed()&&!jQuery.isEmptyObject(m)&&(i.push([l.id]),i.push(m)),g.push(l.id)):(m=l.get(),delete m[f],h.push(m))}}return jQuery.isEmptyObject(g)||e.push(["add",g]),jQuery.isEmptyObject(h)||e.push(["create",h]),jQuery.isEmptyObject(i)||e.push(["write"].concat(i)),jQuery.isEmptyObject(c)||e.push(["remove",c.map(function(a){return a.id})]),jQuery.isEmptyObject(d)||e.push(["delete",d.map(function(a){return a.id})]),e},set_client:function(a,b,c){null===b&&(b=[]),"number"==typeof b&&(b=[b]);var d=this.get_eval(a);this._set_value(a,b),Sao.common.compare(d.sort(),b.sort())?c&&(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})):(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){a.group.changed().done(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})}))},get_client:function(a){return this._set_default_value(a),a._values[this.name]},set_default:function(a,b){a._values[this.name];this.set(a,b,!0),a._changed[this.name]=!0},set_on_change:function(a,b){if(a._changed[this.name]=!0,this._set_default_value(a),b instanceof Array)return void this._set_value(a,b);var c=jQuery.when();if(b.add||b.update){var d=this.get_context(a),e=a._values[this.name].model.fields,f={},g=[];if(b.add)for(var h=0;h<b.add.length;h++)g.push(b.add[h][1]);if([g,b.update].forEach(function(a){jQuery.isEmptyObject(a)||a.forEach(function(a){Object.keys(a).forEach(function(a){a in e||"id"==a||(f[a]=!0)})})}),!jQuery.isEmptyObject(f)){var i={method:"model."+this.description.relation+".fields_get",params:[Object.keys(f),d]};c=Sao.rpc(i,a.model.session)}}var j=[],k=a._values[this.name];k.forEach(function(a){a.id||j.push(a)}),b.remove&&b.remove.forEach(function(a){var b=k.get(a);b&&j.push(b)}.bind(this)),j.forEach(function(a){k.remove(a,!1,!0,!1)}.bind(this)),(b.add||b.update)&&c.then(function(a){k.model.add_fields(a),b.add&&b.add.forEach(function(a){var b=a[0],c=a[1],d=k.new_(!1);k.add(d,b),d.set_on_change(c)}),b.update&&b.update.forEach(function(a){if(a.id){var b=k.get(a.id);b&&b.set_on_change(a)}})}.bind(this))},_set_default_value:function(a,b){if(void 0===a._values[this.name]){b||(b=new Sao.Model(this.description.relation)),a.model.name==this.description.relation&&(b=a.model);var c=a.expr_eval(this.description.context||{}),d=Sao.Group(b,c,[]);d.set_parent(a),d.parent_name=this.description.relation_field,d.child_name=this.name,a._values[this.name]=d}},get_timestamp:function(a){var b=a._values[this.name];if(void 0===b)return{};for(var c,d={},e=0,f=b.length;f>e;e++)c=b[e],jQuery.extend(d,c.get_timestamp());return d},get_eval:function(a){var b=[],c=a._values[this.name];if(void 0===c)return b;for(var d=c.record_removed,e=c.record_deleted,f=0,g=a._values[this.name].length;g>f;f++){var h=c[f];~d.indexOf(h)||~e.indexOf(h)||b.push(h.id)}return b},get_on_change_value:function(a){var b=[],c=a._values[this.name];if(void 0===c)return b;for(var d=0,e=a._values[this.name].length;e>d;d++){var f=c[d];f.deleted()&&f.removed()||b.push(f.get_on_change_value([this.description.relation_field||""]))}return b},get_removed_ids:function(a){return a._values[this.name].record_removed.map(function(a){return a.id})},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(e.inverse_leaf(c),this.name),d])},validation_domains:function(a,b){return this.get_domains(a,b)[0]},validate:function(a,b,c){if(this.description.readonly)return!0;var d=!1,e=new Sao.common.DomainInversion,f=e.localize_domain(e.domain_inversion(a.group.clean4inversion(c||[]),this.name,Sao.common.EvalEnvironment(a)),this.name);"boolean"==typeof f&&(f=f?[]:[["id","=",null]]);for(var g=0,h=(a._values[this.name]||[]).length;h>g;g++){var i=a._values[this.name][g];!i.get_loaded()&&i.id>=0&&!c||i.validate(null,b,f,!0)||(d="children")}var j=Sao.field.One2Many._super.validate.call(this,a,b,c);return j&&d?(this.get_state_attrs(a).invalid=d,!1):j},set_state:function(a,b){this._set_default_value(a),Sao.field.One2Many._super.set_state.call(this,a,b),a._values[this.name].readonly=this.get_state_attrs(a).readonly}}),Sao.field.Many2Many=Sao.class_(Sao.field.One2Many,{get_on_change_value:function(a){return this.get_eval(a)}}),Sao.field.Reference=Sao.class_(Sao.field.Field,{_default:null,get_client:function(a){if(a._values[this.name]){var b=a._values[this.name][0],c=a._values[this.name+".rec_name"]||"";return[b,c]}return null},get:function(a){return a._values[this.name]&&a._values[this.name][0]&&a._values[this.name][1]>=-1?a._values[this.name].join(","):null},set_client:function(a,b,c){if(b){"string"==typeof b&&(b=b.split(","));var d,e=b[0],f=b[1];f instanceof Array?(d=f[1],f=f[0]):(f&&!isNaN(parseInt(f,10))&&(f=parseInt(f,10)),d=[e,f].join(",")==this.get(a)?a._values[this.name+".rec_name"]||"":""),a._values[this.name+".rec_name"]=d,b=[e,f]}Sao.field.Reference._super.set_client.call(this,a,b,c)},set:function(a,b){if(!b)return void(a._values[this.name]=this._default);var c,d;"string"==typeof b?(c=b.split(",")[0],d=b.split(",")[1],d?isNaN(parseInt(d,10))||(d=parseInt(d,10)):d=null):(c=b[0],d=b[1]);var e=a._values[this.name+".rec_name"]||"",f=function(b){a._values[this.name+".rec_name"]=b}.bind(this);c&&null!==d&&d>=0?!e&&d>=0&&Sao.rpc({method:"model."+c+".read",params:[[d],["rec_name"],a.get_context()]},a.model.session).done(function(a){f(a[0].rec_name)}):e=c?"":d,a._values[this.name]=[c,d],f(e)},get_on_change_value:function(a){return a.group.parent_name==this.name&&a.group.parent?[a.group.parent.model.name,a.group.parent.get_on_change_value([a.group.child_name])]:Sao.field.Reference._super.get_on_change_value.call(this,a)},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=null;a._values[this.name]&&(b=a._values[this.name][0]);var c=this.get_domains(a),d=c[0],e=c[1],f=new Sao.common.DomainInversion;return f.concat([f.localize_domain(f.filter_leaf(d,this.name,b),!0),e])}}),Sao.field.Binary=Sao.class_(Sao.field.Field,{_default:null,get_size:function(a){var b=a._values[this.name]||0;return b instanceof Uint8Array?b.length:b},get_data:function(a){var b=jQuery.when(),c=a._values[this.name]||0;if(!(c instanceof Uint8Array)){if(a.id<0)return b;var d=a.get_context();return b=a.model.execute("read",[[a.id],[this.name]],d),b.done(function(a){return a[0][this.name]}.bind(this)),b}}}),Sao.field.Dict=Sao.class_(Sao.field.Field,{_default:{},get:function(a){return Sao.field.Dict._super.get.call(this,a)||this._default},get_client:function(a){return Sao.field.Dict._super.get_client.call(this,a)||this._default},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=new Sao.common.DomainInversion,c=this.get_domains(a),d=c[0],e=c[1];return b.concat([b.localize_domain(b.inverse_leaf(d)),e])},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)},time_format:function(a){return"%X"}})}(),function(){"use strict";Sao.Tab=Sao.class_(Object,{init:function(){Sao.Tab.tabs.push(this),this.buttons={},this.id="tab-"+Sao.Tab.counter++,this.name_el=jQuery("<span/>")},create_tabcontent:function(){this.el=jQuery("<div/>",{"class":this.class_});var a=this.create_toolbar().appendTo(this.el);this.title=a.find("a.navbar-brand"),this.info_bar&&this.el.append(this.info_bar.el)},set_menu:function(a){this.menu_def().forEach(function(b){var c=b[0],d=b[1],e=b[2],f=jQuery("<li/>",{role:"presentation"}).appendTo(a),g=jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(jQuery("<span/>",{
-"class":"glyphicon "+c,"aria-hidden":"true"})).append(" "+d).appendTo(f);e?g.click(function(){this[e]()}.bind(this)):f.addClass("disabled")}.bind(this))},create_toolbar:function(){var a=jQuery('<nav class="navbar navbar-default toolbar" role="navigation"><div class="container-fluid"><div class="navbar-header"><a class="navbar-brand" href="#"></a><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-'+this.id+'"><span class="sr-only">'+Sao.i18n.gettext("Toggle navigation")+'</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></div><div class="collapse navbar-collapse" id="navbar-'+this.id+'"><ul class="nav navbar-nav"><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><span class="visible-xs">'+Sao.i18n.gettext("Menu")+'</span><span class="caret"></span></a><ul class="dropdown-menu" role="menu"></ul></li></ul></div></div></nav>'),b=jQuery("<div/>",{"class":"nav-wrapper"}).append(a);this.set_menu(a.find('ul[role*="menu"]'));var c=function(b){var c=jQuery("<li/>",{role:"presentation"}).appendTo(a.find(".navbar-collapse > ul"));this.buttons[b[0]]=jQuery("<a/>",{role:"menuitem",href:"#",id:b[0]}).append(jQuery("<span/>",{"class":"glyphicon "+b[1],"aria-hidden":"true"})).append(jQuery("<span/>",{"class":"hidden-sm"}).append(" "+b[2])).appendTo(c),b[4]?this.buttons[b[0]].click(this[b[4]].bind(this)):c.addClass("disabled")};this.toolbar_def().forEach(c.bind(this));var d=jQuery("#tabs");return a.affix({target:d.parent(),offset:{top:function(){return d.find(".nav-tabs").height()}}}),a.on("affix.bs.affix",function(){b.height(a.height())}),a.on("affix-top.bs.affix affix-bottom.bs.affix",function(){b.height("")}),a.on("affixed.bs.affix",function(){Sao.Tab.affix_set_with(a)}),a.on("affixed-top.bs.affix affixed-bottom.bs.affix",function(){Sao.Tab.affix_unset_width(a)}),b},close:function(){var a=jQuery("#tabs"),b=a.find("#nav-"+this.id),c=a.find("#"+this.id);return a.find('a[href="#'+this.id+'"]').tab("show"),this._close_allowed().then(function(){var d=b.next();d.length||(d=b.prev()),b.remove(),c.remove(),Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(this),1),d&&d.find("a").tab("show"),a.trigger("ready")}.bind(this))},_close_allowed:function(){return jQuery.when()},set_name:function(a){this.name_el.text(a)}}),Sao.Tab.affix_set_with=function(a){var b=jQuery(a.parent()).width();a.css("width",b)},Sao.Tab.affix_unset_width=function(a){a.css("width","")},jQuery(window).resize(function(){jQuery(".toolbar").each(function(a,b){b=jQuery(b),b.affix("checkPosition"),b.hasClass("affix")?Sao.Tab.affix_set_with(b):Sao.Tab.affix_unset_width(b)})}),Sao.Tab.counter=0,Sao.Tab.tabs=[],Sao.Tab.tabs.close=function(a){if(a&&Sao.Tab.tabs.length)return Sao.common.sur.run(Sao.i18n.gettext("The following action requires to close all tabs.\nDo you want to continue?")).then(function(){return Sao.Tab.tabs.close(!1)});if(Sao.Tab.tabs.length){var b=Sao.Tab.tabs[0];return b.close().then(function(){return~Sao.Tab.tabs.indexOf(b)?jQuery.Deferred().reject():Sao.Tab.tabs.close()})}return Sao.main_menu_screen?Sao.main_menu_screen.save_tree_state().then(function(){Sao.main_menu_screen=null}):jQuery.when()},Sao.Tab.tabs.get_current=function(){var a=jQuery("#tabs > ul"),b=a.find("li").index(a.find("li.active"));return Sao.Tab.tabs[b]},Sao.Tab.tabs.close_current=function(){var a=this.get_current();a.close()},Sao.Tab.create=function(a){void 0===a.context&&(a.context={});var b;b=a.model?new Sao.Tab.Form(a.model,a):new Sao.Tab.Board(a),Sao.Tab.add(b)},Sao.Tab.add=function(a){var b=jQuery("#tabs"),c=jQuery("<a/>",{"aria-controls":a.id,role:"tab","data-toggle":"tab",href:"#"+a.id}).append(jQuery("<button/>",{"class":"close"}).append(jQuery("<span/>",{"aria-hidden":!0}).append("×")).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Close"))).click(function(){a.close()})).append(a.name_el);jQuery("<li/>",{role:"presentation",id:"nav-"+a.id}).append(c).appendTo(b.find("> .nav-tabs")),jQuery("<div/>",{role:"tabpanel","class":"tab-pane",id:a.id}).html(a.el).appendTo(b.find("> .tab-content")),c.on("shown.bs.tab",function(){Sao.View.resize(a.el)}),c.tab("show"),b.trigger("ready")},Sao.Tab.Form=Sao.class_(Sao.Tab,{class_:"tab-form",init:function(a,b){Sao.Tab.Form._super.init.call(this);var c=new Sao.Screen(a,b);c.tab=this,this.screen=c,this.attributes=jQuery.extend({},b),this.info_bar=new Sao.Window.InfoBar,this.create_tabcontent();var d=Sao.common.MODELACCESS.get(a);[["new","create"],["save","write"]].forEach(function(a){var b=a[0],c=a[1];this.buttons[b].prop("disabled",!d[c])}.bind(this)),this.view_prm=this.screen.switch_view().done(function(){this.set_name(b.name||this.screen.current_view.attributes.string),this.el.append(c.screen_container.el),b.res_id?(c.group.load([b.res_id]),c.set_current_record(c.group.get(b.res_id)),c.display()):("form"==c.current_view.view_type&&c.new_(),~["tree","graph","calendar"].indexOf(c.current_view.view_type)&&c.search_filter()),this.update_revision()}.bind(this))},toolbar_def:function(){return[["new","glyphicon-edit",Sao.i18n.gettext("New"),Sao.i18n.gettext("Create a new record"),"new_"],["save","glyphicon-save",Sao.i18n.gettext("Save"),Sao.i18n.gettext("Save this record"),"save"],["switch","glyphicon-list-alt",Sao.i18n.gettext("Switch"),Sao.i18n.gettext("Switch view"),"switch_"],["reload","glyphicon-refresh",Sao.i18n.gettext("Reload"),Sao.i18n.gettext("Reload"),"reload"],["previous","glyphicon-chevron-left",Sao.i18n.gettext("Previous"),Sao.i18n.gettext("Previous Record"),"previous"],["next","glyphicon-chevron-right",Sao.i18n.gettext("Next"),Sao.i18n.gettext("Next Record"),"next"],["attach","glyphicon-paperclip",Sao.i18n.gettext("Attachment"),Sao.i18n.gettext("Add an attachment to the record"),"attach"]]},menu_def:function(){return[["glyphicon-edit",Sao.i18n.gettext("New"),"new_"],["glyphicon-save",Sao.i18n.gettext("Save"),"save"],["glyphicon-list-alt",Sao.i18n.gettext("Switch"),"switch_"],["glyphicon-refresh",Sao.i18n.gettext("Reload/Undo"),"reload"],["glyphicon-duplicate",Sao.i18n.gettext("Duplicate"),"copy"],["glyphicon-trash",Sao.i18n.gettext("Delete"),"delete_"],["glyphicon-chevron-left",Sao.i18n.gettext("Previous"),"previous"],["glyphicon-chevron-right",Sao.i18n.gettext("Next"),"next"],["glyphicon-search",Sao.i18n.gettext("Search"),"search"],["glyphicon-time",Sao.i18n.gettext("View Logs..."),"logs"],["glyphicon-time",Sao.i18n.gettext("Show revisions..."),Sao.common.MODELHISTORY.contains(this.screen.model_name)?"revision":null],["glyphicon-remove",Sao.i18n.gettext("Close Tab"),"close"],["glyphicon-paperclip",Sao.i18n.gettext("Attachment"),"attach"],["glyphicon-cog",Sao.i18n.gettext("Action"),"action"],["glyphicon-share-alt",Sao.i18n.gettext("Relate"),"relate"],["glyphicon-print",Sao.i18n.gettext("Print"),"print"]]},create_toolbar:function(){var a=Sao.Tab.Form._super.create_toolbar.call(this),b=this.screen,c=this.buttons,d=b.model.execute("view_toolbar_get",[],b.context);return d.done(function(d){[["action","glyphicon-cog",Sao.i18n.gettext("Action"),Sao.i18n.gettext("Launch action")],["relate","glyphicon-share-alt",Sao.i18n.gettext("Relate"),Sao.i18n.gettext("Open related records")],["print","glyphicon-print",Sao.i18n.gettext("Print"),Sao.i18n.gettext("Print report")]].forEach(function(e){var f=jQuery("<li/>",{"class":"dropdown"}).append(jQuery("<a/>",{href:"#",id:e[0],"class":"dropdown-toggle","data-toggle":"dropdown",role:"button","aria-expanded":"false"}).append(jQuery("<span/>",{"class":"glyphicon "+e[1],"aria-hidden":"true"})).append(jQuery("<span/>",{"class":"hidden-sm"}).append(" "+e[2]+" ")).append(jQuery("<span/>",{"class":"caret"}))).append(jQuery("<ul/>",{"class":"dropdown-menu",role:"menu","aria-labelledby":e[0]})).appendTo(a.find(".navbar-collapse > ul"));c[e[0]]=f;var g=f.find('ul[role*="menu"]');"action"==e[0]&&f.find("a").click(function(){g.find(".action_button").remove();var a=b.get_buttons();a.forEach(function(a){jQuery("<li/>",{role:"presentation","class":"action_button"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(a.attributes.string||"")).click(function(){b.button(a.attributes)}).appendTo(g)})}),d[e[0]].forEach(function(a){jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(a.name)).click(function(){b.save_current().then(function(){var c=jQuery.extend({},a),d=null;b.current_record&&(d=b.current_record.id);var f=b.current_view.selected_records().map(function(a){return a.id});c=Sao.Action.evaluate(c,e[0],b.current_record);var g={model:b.model_name,id:d,ids:f};Sao.Action.exec_action(c,g,b.context)})}).appendTo(g)})})}),a},_close_allowed:function(){return this.modified_save()},modified_save:function(){return this.screen.save_tree_state(),this.screen.current_view.set_value(),this.screen.modified()?Sao.common.sur_3b.run(Sao.i18n.gettext("This record has been modified\ndo you want to save it?")).then(function(a){switch(a){case"ok":return this.save();case"ko":return this.reload(!1);default:return jQuery.Deferred().reject()}}.bind(this)):jQuery.when()},new_:function(){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.modified_save().done(function(){this.screen.new_().then(function(){this.info_bar.message()}.bind(this))}.bind(this))},save:function(){return Sao.common.MODELACCESS.get(this.screen.model_name).write?this.screen.save_current().then(function(){this.info_bar.message(Sao.i18n.gettext("Record saved."),"info")}.bind(this),function(){this.info_bar.message(this.screen.invalid_message(),"danger")}.bind(this)):jQuery.when()},switch_:function(){this.modified_save().done(function(){this.screen.switch_view()}.bind(this))},reload:function(a){void 0===a&&(a=!0);var b=function(){return this.screen.cancel_current().then(function(){return this.screen.save_tree_state(!1),"form"!=this.screen.current_view.view_type&&this.screen.search_filter(this.screen.screen_container.search_entry.val()),this.screen.display().then(function(){this.info_bar.message()}.bind(this))}.bind(this))}.bind(this);return a?this.modified_save().then(b):b()},copy:function(){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.modified_save().done(function(){this.screen.copy().then(function(){this.info_bar.message(Sao.i18n.gettext("Working now on the duplicated record(s)."),"info")}.bind(this))}.bind(this))},delete_:function(){if(Sao.common.MODELACCESS.get(this.screen.model_name)["delete"]){var a;a=Sao.i18n.gettext("form"==this.screen.current_view.view_type?"Are you sure to remove this record?":"Are you sure to remove those records?"),Sao.common.sur.run(a).done(function(){this.screen.remove(!0,!1,!0).then(function(){this.info_bar.message(Sao.i18n.gettext("Records removed."),"info")}.bind(this),function(){this.info_bar.message(Sao.i18n.gettext("Records not removed."),"danger")}.bind(this))}.bind(this))}},previous:function(){this.modified_save().done(function(){this.screen.display_previous(),this.info_bar.message()}.bind(this))},next:function(){this.modified_save().done(function(){this.screen.display_next(),this.info_bar.message()}.bind(this))},search:function(){var a=this.screen.screen_container.search_entry;a.is(":visible")&&window.setTimeout(function(){a.focus()},0)},logs:function(){var a=this.screen.current_record;if(!a||a.id<0)return void this.info_bar.message(Sao.i18n.gettext("You have to select one record."),"info");var b=[["id",Sao.i18n.gettext("ID:")],["create_uid.rec_name",Sao.i18n.gettext("Creation User:")],["create_date",Sao.i18n.gettext("Creation Date:")],["write_uid.rec_name",Sao.i18n.gettext("Latest Modification by:")],["write_date",Sao.i18n.gettext("Latest Modification Date:")]];this.screen.model.execute("read",[[a.id],b.map(function(a){return a[0]})],this.screen.context).then(function(a){a=a[0];var c="";b.forEach(function(b){var d=b[0],e=b[1],f=a[d]||"/";a[d]&&~["create_date","write_date"].indexOf(d)&&(f=Sao.common.format_datetime(Sao.common.date_format(),"%H:%M:%S",f)),c+=e+" "+f+"\n"}),c+=Sao.i18n.gettext("Model: ")+this.screen.model.name,Sao.common.message.run(c)}.bind(this))},revision:function(){var a=null;this.screen.current_record&&(a=this.screen.current_record.id);var b=function(b){return function(c){c&&c.setMilliseconds(c.getMilliseconds()+1),"form"==this.screen.current_view.view_type&&c<b[b.length-1][0]&&(c=b[b.length-1][0]),c!=this.screen.context._datetime&&(this.screen.context._datetime=c,"form"!=this.screen.current_view.view_type?this.screen.search_filter(this.screen.screen_container.search_entry.val()):this.screen.new_group([a]),this.screen.display(!0),this.update_revision())}.bind(this)}.bind(this);this.modified_save().done(function(){var a=this.screen.current_view.selected_records().map(function(a){return a.id});this.screen.model.execute("history_revisions",[a],this.screen.context).then(function(a){new Sao.Window.Revision(a,b(a))})}.bind(this))},update_revision:function(){var a,b=this.screen.context._datetime;if(b){var c=Sao.common.date_format(),d="%H:%M:%S.%f";b=Sao.common.format_datetime(c,d,b),a=this.name_el.text()+" @ "+b}else a=this.name_el.text();this.title.html(a),["new","save"].forEach(function(a){this.buttons[a].prop("disabled",b)}.bind(this))},attach:function(){var a=this.screen.current_record;!a||a.id<0||new Sao.Window.Attachment(a,function(){this.update_attachment_count(!0)}.bind(this))},update_attachment_count:function(a){var b=this.screen.current_record;b?b.get_attachment_count(a).always(this.attachment_count.bind(this)):this.attachment_count(0)},attachment_count:function(a){var b=Sao.i18n.gettext("Attachment(%1)",a);this.buttons.attach.text(b);var c=this.screen.get_id();this.buttons.attach.prop("disabled",0>c||null===c)},record_message:function(){this.info_bar.message()},action:function(){window.setTimeout(function(){this.buttons.action.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))},relate:function(){window.setTimeout(function(){this.buttons.relate.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))},print:function(){window.setTimeout(function(){this.buttons.print.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))}}),Sao.Tab.Board=Sao.class_(Sao.Tab,{class_:"tab-board",init:function(a){var b,c;Sao.Tab.Board._super.init.call(this),this.model=a.model,this.view_id=a.view_ids.length>0?a.view_ids[0]:null,this.context=a.context,this.name=a.name,this.dialogs=[],this.board=null,b=new Sao.Model("ir.ui.view"),c=b.execute("read",[[this.view_id],["arch"]],this.context),c.done(function(a){var b;b=jQuery(jQuery.parseXML(a[0].arch)),this.board=new Sao.View.Board(b,this.context),this.board.actions_prms.done(function(){var a,b,c;for(a=0,b=this.board.actions.length;b>a;a++)c=this.board.actions[a],c.screen.tab=this}.bind(this)),this.el.append(this.board.el)}.bind(this)),this.create_tabcontent(),this.set_name(this.name),this.title.html(this.name_el.text())},toolbar_def:function(){return[["reload","glyphicon-refresh",Sao.i18n.gettext("Reload"),Sao.i18n.gettext("Reload"),"reload"]]},menu_def:function(){return[["glyphicon-refresh",Sao.i18n.gettext("Reload/Undo"),"reload"]]},reload:function(){this.board.reload()},record_message:function(){var a,b,c;for(b=this.board.actions.length,a=0,b=b;b>a;a++)c=this.board.actions[a],c.update_domain(this.board.actions)},attachment_count:function(){}}),Sao.Tab.Wizard=Sao.class_(Sao.Tab,{class_:"tab-wizard",init:function(a){Sao.Tab.Wizard._super.init.call(this),this.wizard=a,this.set_name(a.name),a.tab=this,this.create_tabcontent(),this.title.html(this.name_el.text()),this.el.append(a.form)},create_toolbar:function(){return jQuery("<span/>")},_close_allowed:function(){var a=this.wizard,b=jQuery.when();a.state!==a.end_state&&a.end_state in a.states&&(b=a.response(a.end_state));var c=jQuery.Deferred();return b.always(function(){a.state===a.end_state?c.resolve():c.reject()}),c.promise()}})}(),function(){"use strict";Sao.ScreenContainer=Sao.class_(Object,{init:function(a){this.alternate_viewport=jQuery("<div/>",{"class":"screen-container"}),this.alternate_view=!1,this.search_modal=null,this.search_form=null,this.tab_domain=a||[],this.el=jQuery("<div/>",{"class":"screen-container"}),this.filter_box=jQuery("<div/>",{"class":"row filter-box"}),this.el.append(this.filter_box),this.filter_button=jQuery("<button/>",{type:"button","class":"btn btn-default"}).append(Sao.i18n.gettext("Filters")),this.filter_button.click(this.search_box.bind(this)),this.search_entry=jQuery("<input/>",{"class":"form-control",placeholder:Sao.i18n.gettext("Search")}),this.search_list=jQuery("<datalist/>"),this.search_list.uniqueId(),this.search_entry.attr("list",this.search_list.attr("id")),this.search_entry.keypress(this.key_press.bind(this)),this.search_entry.on("input",this.update.bind(this)),this.but_bookmark=jQuery("<button/>",{type:"button","class":"btn btn-default dropdown-toggle","data-toggle":"dropdown","aria-expanded":!1,"aria-label":Sao.i18n.gettext("Bookmarks"),id:"bookmarks"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-bookmark","aria-hidden":!0}));var b=jQuery("<ul/>",{"class":"dropdown-menu",role:"menu","aria-labelledby":"bookmarks"});if(this.but_bookmark.click(function(){b.children().remove();for(var a=this.bookmarks(),c=0;c<a.length;c++){var d=a[c][1],e=a[c][2];jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(d).click(e,this.bookmark_activate.bind(this))).appendTo(b)}}.bind(this)),this.but_star=jQuery("<button/>",{"class":"btn btn-default"}).append(jQuery("<span/>",{"class":"glyphicon","aria-hidden":!0})).click(this.star_click.bind(this)),this.set_star(),jQuery("<div/>",{"class":"input-group"}).append(jQuery("<span/>",{"class":"input-group-btn"}).append(this.filter_button)).append(this.search_entry).append(this.search_list).append(jQuery("<span/>",{"class":"input-group-btn"}).append(this.but_star).append(this.but_bookmark).append(b)).appendTo(jQuery("<div/>",{"class":"col-md-8"}).appendTo(this.filter_box)),this.but_prev=jQuery("<button/>",{type:"button","class":"btn btn-default","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-menu-left","aria-hidden":!0})),this.but_prev.click(this.search_prev.bind(this)),this.but_next=jQuery("<button/>",{type:"button","class":"btn btn-default","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-menu-right","aria-hidden":!0})),this.but_next.click(this.search_next.bind(this)),jQuery("<div/>",{"class":"btn-group",role:"group"}).append(this.but_prev).append(this.but_next).appendTo(jQuery("<div/>",{"class":"col-md-4"}).appendTo(this.filter_box)),this.content_box=jQuery("<div/>",{"class":"content-box"}),jQuery.isEmptyObject(this.tab_domain))this.tab=null;else{this.tab=jQuery("<div/>",{"class":"tab-domain"}).appendTo(this.el);var c=jQuery("<ul/>",{"class":"nav nav-tabs",role:"tablist"}).appendTo(this.tab);jQuery("<div/>",{"class":"tab-content"}).appendTo(this.tab);this.tab_domain.forEach(function(a,b){var d=a[0];jQuery("<li/>",{role:"presentation",id:"nav-"+b}).append(jQuery("<a/>",{"aria-controls":b,role:"tab","data-toggle":"tab",href:"#"+b}).append(d)).appendTo(c)}.bind(this)),c.find("a:first").tab("show");var d=this;c.find("a").click(function(a){a.preventDefault(),jQuery(this).tab("show"),d.do_search()})}this.el.append(this.content_box)},set_text:function(a){this.search_entry.val(a),this.bookmark_match()},update:function(){var a=this.screen.domain_parser().completion(this.get_text());this.search_list.children().remove(),a.forEach(function(a){jQuery("<option/>",{value:a.trim()}).appendTo(this.search_list)},this)},set_star:function(a){var b=this.but_star.children("span.glyphicon");a?(b.removeClass("glyphicon-star-empty"),b.addClass("glyphicon-star"),this.but_star.attr("aria-label",Sao.i18n.gettext("Remove this bookmark"))):(b.removeClass("glyphicon-star"),b.addClass("glyphicon-star-empty"),this.but_star.attr("aria-label",Sao.i18n.gettext("Bookmark this filter")))},get_star:function(){var a=this.but_star.children("span.glyphicon");return a.hasClass("glyphicon-star")},star_click:function(){var a=this.get_star(),b=this.screen.model_name,c=function(){this.bookmark_match(),this.but_bookmark.prop("disabled",jQuery.isEmptyObject(this.bookmarks()))}.bind(this);if(a){var d=this.bookmark_match();Sao.common.VIEW_SEARCH.remove(b,d).then(function(){c()})}else{var e=this.get_text();if(!e)return;Sao.common.ask.run(Sao.i18n.gettext("Bookmark Name:")).then(function(a){if(a){var d=this.screen.domain_parser().parse(e);Sao.common.VIEW_SEARCH.add(b,a,d).then(function(){c()}),this.set_text(this.screen.domain_parser().string(d))}}.bind(this))}},bookmarks:function(){var a=Sao.common.VIEW_SEARCH.get(this.screen.model_name);return a.filter(function(a){return this.screen.domain_parser().stringable(a[2])}.bind(this))},bookmark_activate:function(a){var b=a.data;this.set_text(this.screen.domain_parser().string(b)),this.do_search()},bookmark_match:function(){var a=this.get_text(),b=this.screen.domain_parser().parse(a);this.but_star.prop("disabled",!a);for(var c=(this.get_star(),this.bookmarks()),d=0;d<c.length;d++){var e=c[d][0],f=(c[d][1],c[d][2]),g=this.screen.domain_parser().string(f);if(g===a||Sao.common.compare(f,b))return this.set_star(!0),e}this.set_star(!1)},search_prev:function(){this.screen.search_prev(this.get_text())},search_next:function(){this.screen.search_next(this.get_text())},get_tab_domain:function(){if(!this.tab)return[];var a=this.tab.find("li").index(this.tab.find("li.active"));return this.tab_domain[a][1]},do_search:function(){return this.screen.search_filter(this.get_text())},key_press:function(a){return a.which==Sao.common.RETURN_KEYCODE?(this.do_search(),!1):void window.setTimeout(function(){this.bookmark_match()}.bind(this))},set_screen:function(a){this.screen=a,this.but_bookmark.prop("disabled",jQuery.isEmptyObject(this.bookmarks())),this.bookmark_match()},show_filter:function(){this.filter_box.show(),this.tab&&this.tab.show()},hide_filter:function(){this.filter_box.hide(),this.tab&&this.tab.hide()},set:function(a){this.alternate_view?(this.alternate_viewport.children().detach(),this.alternate_viewport.append(a)):(this.content_box.children().detach(),this.content_box.append(a))},get_text:function(){return this.search_entry.val()},search_box:function(){var a=this.screen.domain_parser(),b=function(){this.search_modal.modal("hide");for(var b="",c=a.quote.bind(a),d=0;d<this.search_form.fields.length;d++){var e,f=this.search_form.fields[d][0],g=this.search_form.fields[d][1];switch(g.type){case"selection":case"date":case"datetime":case"time":e=g.get_value(c);break;default:e=c(g.val())}e&&(b+=c(f)+": "+e+" ")}this.set_text(b),this.do_search().then(function(){this.last_search_text=this.get_text()}.bind(this))}.bind(this);if(!this.search_modal){var c=new Sao.Dialog(Sao.i18n.gettext("Filters"),"","lg");this.search_modal=c.modal,this.search_form=c.content,this.search_form.addClass("form-horizontal"),this.search_form.submit(b);var d,e=[];for(var f in a.fields)d=a.fields[f],(d.searchable||void 0===d.searchable)&&e.push(d);var g=function(a){return function(b){jQuery("<option/>",{value:b,text:b}).appendTo(a)}},h="filter-"+this.screen.model_name+"-";this.search_form.fields=[];for(var i=0;i<e.length;i++){d=e[i];var j,k,l=jQuery("<div/>",{"class":"form-group form-group-sm"}).append(jQuery("<label/>",{"class":"col-sm-4 control-label","for":h+d.name,text:d.string})).appendTo(c.body);switch(d.type){case"boolean":k=j=jQuery("<select/>",{"class":"form-control input-sm",id:h+d.name}),["",Sao.i18n.gettext("True"),Sao.i18n.gettext("False")].forEach(g(j));break;case"selection":k=new Sao.ScreenContainer.Selection(d.selection,h+d.name),j=k.el;break;case"date":case"datetime":case"time":var m,n=Sao.common.date_format();if("date"==d.type)m=n;else{var o=new Sao.PYSON.Decoder({}).decode(d.format);o=Sao.common.moment_format(o),"time"==d.type?m=o:"datetime"==d.type&&(m=n+" "+o)}k=new Sao.ScreenContainer.DateTimes(m,h+d.name),j=k.el;break;default:k=j=jQuery("<input/>",{"class":"form-control input-sm",type:"text",placeholder:d.string,id:h+d.name})}jQuery("<div/>",{"class":"col-sm-8"}).append(j).appendTo(l),this.search_form.fields.push([d.string,k])}jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Find")).click(b).appendTo(c.footer)}if(this.search_modal.modal("show"),this.last_search_text.trim()!==this.get_text().trim()){for(var p=0;p<this.search_form.fields.length;p++){var q=this.search_form.fields[p][1];switch(q.type){case"selection":q.set_value([]);break;case"date":case"datetime":case"time":q.set_value(null,null);break;default:q.val("")}}this.search_form.fields[0][1].focus()}}}),Sao.ScreenContainer.DateTimes=Sao.class_(Object,{type:"date",init:function(a,b){this.el=jQuery("<div/>",{"class":"row",id:b});var c=function(c){var d=jQuery("<div/>",{"class":"input-group input-group-sm"});return jQuery("<input/>",{"class":"form-control input-sm",type:"text",placeholder:c,id:b+"-from"}).appendTo(d),jQuery("<span/>",{"class":"input-group-btn"}).append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"}))).appendTo(d),d.datetimepicker(),d.data("DateTimePicker").format(a),d};this.from=c("From").appendTo(jQuery("<div/>",{"class":"col-md-5"}).appendTo(this.el)),jQuery("<p/>",{"class":"text-center"}).append("..").appendTo(jQuery("<div/>",{"class":"col-md-1"}).appendTo(this.el)),this.to=c("To").appendTo(jQuery("<div/>",{"class":"col-md-5"}).appendTo(this.el))},_get_value:function(a){return a.find("input").val()},get_value:function(a){var b=this._get_value(this.from),c=this._get_value(this.to);return b&&c?b!==c?a(b)+".."+a(c):a(b):b?">="+a(b):c?"<="+a(c):void 0},set_value:function(a,b){this.from.data("DateTimePicker").date(a),this.to.data("DateTimePicker").date(b)}}),Sao.ScreenContainer.Selection=Sao.class_(Object,{type:"selection",init:function(a,b){this.el=jQuery("<select/>",{"class":"form-control input-sm",multiple:!0,id:b}),a.forEach(function(a){jQuery("<option/>",{value:a[1],text:a[1]}).appendTo(this.el)}.bind(this))},get_value:function(a){var b=this.el.val();return b&&(b=b.reduce(function(b,c){return b&&(b+=";"),b+a(c)})),b},set_value:function(a){this.el.val(a)}}),Sao.Screen=Sao.class_(Object,{init:function(a,b){this.model_name=a,this.model=new Sao.Model(a,b),this.attributes=jQuery.extend({},b),this.attributes.limit=this.attributes.limit||Sao.config.limit,this.view_ids=jQuery.extend([],b.view_ids),this.view_to_load=jQuery.extend([],b.mode||["tree","form"]),this.views=[],this.views_preload=b.views_preload||{},this.exclude_field=b.exclude_field,this.context=b.context||{},this.new_group(),this.current_view=null,this.current_record=null,this.domain=b.domain||[],this.size_limit=null,this.limit=b.limit||Sao.config.limit,this.offset=0,Sao.common.MODELACCESS.get(a).write||(this.attributes.readonly=!0),this.search_count=0,this.screen_container=new Sao.ScreenContainer(b.tab_domain),b.row_activate?this.row_activate=b.row_activate:this.row_activate=this.default_row_activate,this.tree_states={},this.tree_states_done=[],this.fields_view_tree={},this._domain_parser={},this.pre_validate=!1,this.tab=null},load_next_view:function(){if(!jQuery.isEmptyObject(this.view_to_load)){var a;jQuery.isEmptyObject(this.view_ids)||(a=this.view_ids.shift());var b=this.view_to_load.shift();return this.add_view_id(a,b)}return jQuery.when()},add_view_id:function(a,b){var c;if(a&&this.views_preload[String(a)])c=this.views_preload[String(a)];else{if(a||!this.views_preload[b]){var d=this.model.execute("fields_view_get",[a,b],this.context);return d.pipe(this.add_view.bind(this))}c=this.views_preload[b]}return this.add_view(c),jQuery.when()},add_view:function(a){var b=a.arch,c=a.fields,d=a.view_id,e=jQuery(jQuery.parseXML(b));"tree"==e.children().prop("tagName")&&(this.fields_view_tree[d]=a);var f="eager";"form"==e.children().prop("tagName")&&(f="lazy");for(var g in c)g in this.model.fields&&"eager"!=f?c[g].loading=this.model.fields[g].description.loading:c[g].loading=f;this.model.add_fields(c);var h=Sao.View.parse(this,e,a.field_childs);return h.view_id=d,this.views.push(h),h},number_of_views:function(){return this.views.length+this.view_to_load.length},switch_view:function(a){if(this.current_view){if(!this.group.parent&&this.modified())return jQuery.when();this.current_view.set_value(),this.current_record&&!~this.current_record.group.indexOf(this.current_record)&&(this.current_record=null);var b=this.current_view.get_fields();if(this.current_record&&this.current_view.editable&&!this.current_record.validate(b,!1,!1,!0))return this.screen_container.set(this.current_view.el),this.current_view.display().done(function(){this.set_cursor()}.bind(this))}var c=function(){if(!a||!this.current_view||this.current_view.view_type!=a)for(var b=function(){return this.current_view=this.views[this.views.length-1],c()}.bind(this),d=0;d<this.number_of_views();d++){if(this.view_to_load.length)return a||(a=this.view_to_load[0]),this.load_next_view().then(b);if(this.current_view=this.views[(this.views.indexOf(this.current_view)+1)%this.views.length],!a)break;if(this.current_view.view_type==a)break}return this.screen_container.set(this.current_view.el),this.display().done(function(){this.set_cursor()}.bind(this))}.bind(this);return c()},search_filter:function(a){var b=[],c=this.domain_parser();c&&!this.group.parent?(b=a||""===a?c.parse(a):this.attributes.search_value,this.screen_container.set_text(c.string(b))):b=[["id","in",this.group.map(function(a){return a.id})]],b=!jQuery.isEmptyObject(b)&&this.attributes.domain?["AND",b,this.attributes.domain]:this.attributes.domain||[];var d=this.screen_container.get_tab_domain();jQuery.isEmptyObject(d)||(b=["AND",b,d]);var e=this.model.find(b,this.offset,this.limit,this.attributes.order,this.context),f=this.model.execute("search_count",[b],this.context);return f.done(function(a){this.search_count=a}.bind(this)),e.done(this.set_group.bind(this)),e.done(this.display.bind(this)),jQuery.when(e,f).done(function(a,b){this.screen_container.but_next.prop("disabled",!(a.length==this.limit&&b>this.limit+this.offset))}.bind(this)),this.screen_container.but_prev.prop("disabled",this.offset<=0),e},set_group:function(a){this.group&&(jQuery.extend(a.model.fields,this.group.model.fields),this.group.screens.splice(this.group.screens.indexOf(this),1),jQuery.extend(a.on_write,this.group.on_write),a.on_write=a.on_write.filter(function(a,b,c){return b==c.indexOf(a)})),a.screens.push(this),this.tree_states_done=[],this.group=a,this.model=a.model,this.set_current_record(jQuery.isEmptyObject(a)?null:a[0])},new_group:function(a){var b=new Sao.Group(this.model,this.context,[]);b.set_readonly(this.attributes.readonly||!1),a&&b.load(a),this.set_group(b)},set_current_record:function(a){this.current_record=a,this.tab&&(a?a.get_attachment_count().always(this.tab.attachment_count.bind(this.tab)):this.tab.attachment_count(0),this.tab.record_message())},display:function(a){var b=[];if(this.views){var c=this.search_active(~["tree","graph","calendar"].indexOf(this.current_view.view_type));b.push(c);for(var d=0;d<this.views.length;d++)this.views[d]&&b.push(this.views[d].display())}return jQuery.when.apply(jQuery,b).then(function(){this.set_tree_state(),this.set_current_record(this.current_record),a&&this.set_cursor(!1,!1)}.bind(this))},display_next:function(){var a=this.current_view;if(a.set_value(),this.set_cursor(!1,!1),~["tree","form"].indexOf(a.view_type)&&this.current_record&&this.current_record.group){for(var b=this.current_record.group,c=this.current_record;b;){var d=b.indexOf(c);if(d<b.length-1){c=b[d+1];break}if(!b.parent||c.group.model_name!=b.parent.group.model_name)break;c=b.parent,b=b.parent.group}this.set_current_record(c)}else this.set_current_record(this.group[0]);this.set_cursor(!1,!1),a.display()},display_previous:function(){var a=this.current_view;
-if(a.set_value(),this.set_cursor(!1,!1),~["tree","form"].indexOf(a.view_type)&&this.current_record&&this.current_record.group){for(var b=this.current_record.group,c=this.current_record;b;){var d=b.indexOf(c);if(d>0){c=b[d-1];break}if(!b.parent||c.group.model_name!=b.parent.group.model_name)break;c=b.parent,b=b.parent.group}this.set_current_record(c)}else this.set_current_record(this.group[0]);this.set_cursor(!1,!1),a.display()},default_row_activate:function(){"tree"==this.current_view.view_type&&this.current_view.attributes.keyword_open?Sao.Action.exec_keyword("tree_open",{model:this.model_name,id:this.get_id(),ids:[this.get_id()]},jQuery.extend({},this.context),!1):this.switch_view("form")},get_id:function(){return this.current_record?this.current_record.id:void 0},new_:function(a){void 0===a&&(a=!0);var b=jQuery.when();return this.current_view&&("tree"==this.current_view.view_type&&!this.current_view.editable||"graph"==this.current_view.view_type)&&(b=this.switch_view("form")),b.then(function(){var b;b=this.current_record?this.current_record.group:this.group;var c=b.new_(a);return b.add(c,this.new_model_position()),this.set_current_record(c),this.display().done(function(){this.set_cursor(!0,!0)}.bind(this)),c}.bind(this))},new_model_position:function(){var a=-1;return this.current_view&&"tree"==this.current_view.view_type&&"top"==this.current_view.attributes.editable&&(a=0),a},set_on_write:function(a){a&&(~this.group.on_write.indexOf(a)||this.group.on_write.push(a))},cancel_current:function(){var a=[];return this.current_record&&(this.current_record.cancel(),this.current_record.id<0&&a.push(this.remove())),jQuery.when.apply(jQuery,a)},save_current:function(){var a=this.current_record;if(!a){if("tree"!=this.current_view.view_type||jQuery.isEmptyObject(this.group))return jQuery.when();this.set_current_record(this.group[0])}this.current_view.set_value();var b=this.current_view.get_fields(),c=a.get_path(this.group),d=jQuery.Deferred();"tree"==this.current_view.view_type?d=this.group.save():a.validate(b).then(function(b){b?a.save().then(d.resolve,d.reject):(this.current_view.display().done(this.set_cursor.bind(this)),d.reject())}.bind(this));var e=jQuery.Deferred();return d=d.then(function(){return c&&a.id&&c.splice(-1,1,[c[c.length-1][0],a.id]),this.group.get_by_path(c).then(function(a){this.set_current_record(a)}.bind(this))}.bind(this)),d.then(function(){this.display().always(e.resolve)}.bind(this),function(){this.display().always(e.reject)}.bind(this)),e.promise()},set_cursor:function(a,b){this.current_view&&~["tree","form"].indexOf(this.current_view.view_type)&&this.current_view.set_cursor(a,b)},modified:function(){var a=function(a){return a.has_changed()||a.id<0};if("tree"!=this.current_view.view_type){if(this.current_record&&a(this.current_record))return!0}else if(this.group.some(a))return!0;return!1},unremove:function(){var a=this.current_view.selected_records();a.forEach(function(a){a.group.unremove(a)})},remove:function(a,b,c){var d=null;if("form"==this.current_view.view_type&&this.current_record?d=[this.current_record]:"tree"==this.current_view.view_type&&(d=this.current_view.selected_records()),!jQuery.isEmptyObject(d)){var e=jQuery.when();a&&(e=this.model.delete_(d));var f=d[0],g=f.group,h=g.indexOf(f),i=f.get_path(this.group);return e.then(function(){d.forEach(function(a){a.group.remove(a,b,!0,c)});var e=[];if(a&&d.forEach(function(a){a.group.parent&&e.push(a.group.parent.save(!1)),~a.group.record_deleted.indexOf(a)&&a.group.record_deleted.splice(a.group.record_deleted.indexOf(a),1),~a.group.record_removed.indexOf(a)&&a.group.record_removed.splice(a.group.record_removed.indexOf(a),1)}),h>0){var f=g[h-1];i.splice(-1,1,[i[i.length-1][0],f.id])}else i.splice(-1,1);return jQuery.isEmptyObject(i)?this.group.length&&this.set_current_record(this.group[0]):e.push(this.group.get_by_path(i).then(function(a){this.set_current_record(a)}.bind(this))),jQuery.when.apply(jQuery,e).then(function(){this.display().done(function(){this.set_cursor()}.bind(this))}.bind(this))}.bind(this))}},copy:function(){var a=jQuery.Deferred(),b=this.current_view.selected_records();return this.model.copy(b,this.context).then(function(b){this.group.load(b),jQuery.isEmptyObject(b)||this.set_current_record(this.group.get(b[0])),this.display().always(a.resolve)}.bind(this),a.reject),a.promise()},search_active:function(a){return a&&!this.group.parent?(this.screen_container.set_screen(this),this.screen_container.show_filter()):this.screen_container.hide_filter(),jQuery.when()},domain_parser:function(){var a,b;if(a=this.current_view?this.current_view.view_id:null,a in this._domain_parser)return this._domain_parser[a];a in this.fields_view_tree?b=this.fields_view_tree[a]:(this.model.execute("fields_view_get",[!1,"tree"],this.context).then(function(b){this.fields_view_tree[a]=b}.bind(this)),b={},b.fields={});var c=jQuery.extend({},b.fields),d=function(a){return function(b){a.selection=b}};for(var e in c){var f=c[e];("selection"==f.type||"reference"==f.type)&&(f.selection instanceof Array||this.get_selection(f).then(d))}if("arch"in b){var g=jQuery(jQuery.parseXML(b.arch)),h=g.find("tree").children().filter(function(a){return"field"==a.tagName}).map(function(a){return a.getAttribute("name")}),i={};h.each(function(a){i[a]=c[a]})}[["id",Sao.i18n.gettext("ID"),"integer"],["create_uid",Sao.i18n.gettext("Creation User"),"many2one"],["create_date",Sao.i18n.gettext("Creation Date"),"datetime"],["write_uid",Sao.i18n.gettext("Modification User"),"many2one"],["write_date",Sao.i18n.gettext("Modification Date"),"datetime"]].forEach(function(a){var b=a[0],d=a[1],e=a[2];b in c||(c[b]={string:d,name:b,type:e},"datetime"==e&&(c[b].format='"%H:%M:%S"'))}),"id"in c||(c.id={string:Sao.i18n.gettext("ID"),name:"id",type:"integer"});var j=jQuery.extend({},this.model.session.context,this.context),k=new Sao.common.DomainParser(c,j);return this._domain_parser[a]=k,k},get_selection:function(a){var b,c=a.selection_change_with;if(jQuery.isEmptyObject(c))b=this.model.execute(a.selection,[]);else{var d={};c.forEach(function(a){d[a]=null}),b=this.model.execute(a.selection,[d])}return b.then(function(a){return a.sort(function(a,b){return a[1].localeCompare(b[1])})})},search_prev:function(a){this.offset-=this.limit,this.search_filter(a)},search_next:function(a){this.offset+=this.limit,this.search_filter(a)},invalid_message:function(a){a||(a=this.current_record);var b={};for(var c in a.model.fields){var d=a.model.fields[c];b[c]=d.description}var e=new Sao.common.DomainParser(b),f=[],g=a.invalid_fields();return Object.keys(g).sort().forEach(function(b){var c=g[b],d=a.model.fields[b].description.string;"required"==c||Sao.common.compare(c,[[b,"!=",null]])?f.push(Sao.i18n.gettext('"%1" is required',d)):"domain"==c?f.push(Sao.i18n.gettext('"%1" is not valid according to its domain',d)):"children"==c?f.push(Sao.i18n.gettext('The values of "%1" are not valid',d)):e.stringable(c)?f.push(e.string(c)):f.push(Sao.i18n.gettext('"%1" is not valid according to its domain'),d)}),f.length>5&&(f.splice(5,f.length),f.push("...")),f.join("\n")},get:function(){return this.current_record?(this.current_view.set_value(),this.current_record.get()):null},get_on_change_value:function(){return this.current_record?(this.current_view.set_value(),this.current_record.get_on_change_value()):null},reload:function(a,b){this.group.reload(a),b&&this.group.written(a),this.group.parent&&this.group.parent.root_parent().reload(),this.display()},get_buttons:function(){var a=this.current_view.selected_records();if(jQuery.isEmptyObject(a))return[];var b=this.current_view.get_buttons();return a.forEach(function(a){b=b.filter(function(b){if(a.group.get_readonly()||a.readonly())return!1;if("instance"===b.attributes.type)return!1;var c=a.expr_eval(b.attributes.states||{});return!(c.invisible||c.readonly)})}),b},button:function(a){var b,c=function(a){this.reload(b,!0),"string"==typeof a?this.client_action(a):a&&Sao.Action.execute(a,{model:this.model_name,id:i.id,ids:b},null,this.context)},d=this.current_view.selected_records();this.current_view.set_value();for(var e=this.current_view.get_fields(),f=[],g=function(a){return function(){this.display(!0),a.validate(e)}.bind(this)}.bind(this),h=0;h<d.length;h++){var i=d[h],j=i.expr_eval(a.states||{}).pre_validate||[];f.push(i.validate(e,!1,j))}jQuery.when.apply(jQuery,f).then(function(){for(var e,f=0;f<d.length;f++){e=d[f];var h=arguments[f];if(!h)return void Sao.common.warning.run(this.invalid_message(e),Sao.i18n.gettext("Pre-validation")).then(g(e))}if(e=this.current_record,"instance"===a.type){var i=e.expr_eval(a.change||[]),j=e._get_on_change_args(i);e.model.execute(a.name,[j],this.context).then(function(a){e.set_on_change(a),e.group.root_group().screens.forEach(function(a){a.display()})})}else{var k=jQuery.extend({},this.context);for(k._timestamp={},f=0;f<d.length;f++)e=d[f],jQuery.extend(k._timestamp,e.get_timestamp());e.save(!1).done(function(){b=d.map(function(a){return a.id}),e.model.execute(a.name,[b],k).then(c.bind(this))}.bind(this))}}.bind(this))},client_action:function(a){var b=Sao.common.MODELACCESS.get(this.model_name);if("new"==a)b.create&&this.new_();else if("delete"==a)b["delete"]&&this.remove(!this.group.parent,!1,!this.group.parent);else if("remove"==a)b.write&&b.read&&this.group.parent&&this.remove(!1,!0,!1);else if("copy"==a)b.create&&this.copy();else if("next"==a)this.display_next();else if("previous"==a)this.display_previous();else if("close"==a)Sao.Tab.close_current();else if(a.startsWith("switch")){var c=a.split(" ")[1];this.switch_view(c)}else"reload"==a?~["tree","graph","calendar"].indexOf(this.current_view.view_type)&&!this.group.parent&&this.search_filter():"reload menu"==a?Sao.get_preferences().then(function(a){Sao.menu(a)}):"reload context"==a&&Sao.get_preferences()},save_tree_state:function(a){var b,c=[];a=void 0===a?!0:a;var d,e,f,g,h,i,j=this.group.parent?this.group.parent.id:null,k=this.group.parent?this.group.parent._timestamp:null;for(d=0,e=this.views.length;e>d;d++)if(f=this.views[d],"form"==f.view_type){for(var l in f.widgets)if(f.widgets.hasOwnProperty(l))for(g=f.widgets[l],h=0,i=g.length;i>h;h++)g[h].screen&&(b=g[h].screen.save_tree_state(a),c.push(b));1==this.views.length&&this.current_record&&(j in this.tree_states||(this.tree_states[j]={}),this.tree_states[j][f.children_field||null]=[k,[],[[this.current_record.id]]])}else if("tree"==f.view_type){var m=f.get_expanded_paths(),n=f.get_selected_paths();if(j in this.tree_states||(this.tree_states[j]={}),this.tree_states[j][f.children_field||null]=[k,m,n],a&&f.attributes.tree_state){var o=new Sao.Model("ir.ui.view_tree_state");b=o.execute("set",[this.model_name,this.get_tree_domain(j),f.children_field,JSON.stringify(m),JSON.stringify(n)],{}),c.push(b)}}return jQuery.when.apply(jQuery,c)},get_tree_domain:function(a){var b;return b=a?(this.domain||[]).concat([[this.exclude_field,"=",a]]):this.domain,JSON.stringify(Sao.rpc.prepareObject(b))},set_tree_state:function(){var a,b,c,d,e,f=this.current_view;~["tree","form"].indexOf(f.view_type)&&(~this.tree_states_done.indexOf(f)||("form"!=f.view_type||jQuery.isEmptyObject(this.tree_states_done))&&(a=this.group.parent?this.group.parent.id:null,b=parent?parent._timestamp:null,a in this.tree_states||(this.tree_states[a]={}),c=this.tree_states[a][f.children_field||null],c&&b!=c[0]&&(c=void 0),void 0===c?(e=new Sao.Model("ir.ui.view_tree_state"),d=e.execute("get",[this.model_name,this.get_tree_domain(a),f.children_field],{}).then(function(a){return[b,JSON.parse(a[0]),JSON.parse(a[1])]})):d=jQuery.when(c),d.done(function(b){var c,d,e;if(this.tree_states[a][f.children_field||null]=b,c=b[1],d=b[2],"tree"==f.view_type)f.display(d,c);else if(!jQuery.isEmptyObject(d)){for(var g=0;g<d[0].length;g++){var h=this.group.get(d[0][g]);if(!h)break;e=h}e&&e!=this.current_record&&(this.set_current_record(e),f.display())}}.bind(this)),this.tree_states_done.push(f)))}})}(),function(){"use strict";Sao.View=Sao.class_(Object,{init:function(a,b){this.screen=a,this.view_type=null,this.el=null,this.view_id=null,this.fields={};var c=b.children()[0].attributes;this.attributes={};for(var d=0,e=c.length;e>d;d++){var f=c[d];this.attributes[f.name]=f.value}a.set_on_write(this.attributes.on_write)},set_value:function(){},get_fields:function(){return Object.keys(this.fields)},selected_records:function(){return[]},get_buttons:function(){return[]}}),Sao.View.idpath2path=function(a,b){var c,d=[];if(!b)return[];for(var e=0,f=a.rows.length;f>e;e++)if(a.rows[e].record.id==b[0]){d.push(e),c=Sao.View.idpath2path(a.rows[e],b.slice(1,b.length)),d=d.concat(c);break}return d},Sao.View.parse=function(a,b,c){switch(b.children().prop("tagName")){case"tree":return new Sao.View.Tree(a,b,c);case"form":return new Sao.View.Form(a,b);case"graph":return new Sao.View.Graph(a,b);case"calendar":return new Sao.View.Calendar(a,b)}},Sao.View.resize=function(a){a||(a=jQuery(document)),a.find(".treeview").each(function(){var a=jQuery(this);a.css("width","100%"),a.children(".tree").css("table-layout","fixed")}),a.find(".treeview").each(function(){var a=jQuery(this);a.width()&&(a.css("width",a.width()),a.children(".tree").css("table-layout","auto"))})},jQuery(window).resize(function(){Sao.View.resize()})}(),function(){"use strict";Sao.View.Form=Sao.class_(Sao.View,{editable:!0,init:function(a,b){Sao.View.Form._super.init.call(this,a,b),this.view_type="form",this.el=jQuery("<div/>",{"class":"form"}),this.widgets={},this.widget_id=0,this.state_widgets=[],this.containers=[],this.notebooks=[];var c=b.children()[0],d=this.parse(a.model,c);this.el.append(d.el)},_parse_node:function(a,b,c,d,e){var f;switch(b.tagName){case"image":this._parse_image(a,b,c,d);break;case"separator":this._parse_separator(a,b,c,d);break;case"label":f=this._parse_label(a,b,c,d),d.name&&f&&(e[d.name]=f);break;case"newline":c.add_row();break;case"button":this._parse_button(b,c,d);break;case"notebook":this._parse_notebook(a,b,c,d);break;case"page":this._parse_page(a,b,c,d);break;case"field":if(f=this._parse_field(a,b,c,d),d.name in e&&f&&f.labelled){var g=e[d.name];g.el.uniqueId(),f.labelled.uniqueId(),f.labelled.attr("aria-labelledby",g.el.attr("id")),g.el.attr("for",f.labelled.attr("id"))}break;case"group":this._parse_group(a,b,c,d);break;case"hpaned":this._parse_paned(a,b,c,d,"horizontal");break;case"vpaned":this._parse_paned(a,b,c,d,"vertical");break;case"child":this._parse_child(a,b,c,d)}},parse:function(a,b,c){void 0===c&&(c=new Sao.View.Form.Container(Number(b.getAttribute("col")||4)),this.containers.push(c));var d={},e=function(b,e){for(var f={},g=0,h=e.attributes.length;h>g;g++){var i=e.attributes[g];f[i.name]=i.value}["readonly","invisible"].forEach(function(a){f[a]&&(f[a]=1==f[a])}),["yexpand","yfill","xexpand","xfill","colspan"].forEach(function(a){f[a]&&(f[a]=Number(f[a]))}),this._parse_node(a,e,c,f,d)};return jQuery(b).children().each(e.bind(this)),c},_parse_image:function(a,b,c,d){var e=new Sao.View.Form.Image_(d);this.state_widgets.push(e),c.add(d,e)},_parse_separator:function(a,b,c,d){var e=d.name,f=d.string;e in a.fields&&(!d.states&&e in a.fields&&(d.states=a.fields[e].description.states),f||(f=a.fields[e].description.string));var g=new Sao.View.Form.Separator(f,d);this.state_widgets.push(g),c.add(d,g)},_parse_label:function(a,b,c,d){var e=d.name,f=d.string;if(void 0===d.xexpand&&(d.xexpand=0),e in a.fields){if(e==this.screen.exclude_field)return void c.add(d);!d.states&&e in a.fields&&(d.states=a.fields[e].description.states),f||(f=a.fields[e].description.string+": "),void 0===d.xalign&&(d.xalign=1)}var g;return f&&(g=new Sao.View.Form.Label(f,d),this.state_widgets.push(g)),c.add(d,g),g},_parse_button:function(a,b,c){var d=new Sao.common.Button(c);this.state_widgets.push(d),b.add(c,d),d.el.click(d,this.button_clicked.bind(this))},_parse_notebook:function(a,b,c,d){void 0===d.colspan&&(d.colspan=4);var e=new Sao.View.Form.Notebook(d);this.notebooks.push(e),this.state_widgets.push(e),c.add(d,e),this.parse(a,b,e)},_parse_page:function(a,b,c,d){d.string;if(d.name in a.fields){var e=a.fields[d.name];if(d.name==this.screen.exclude_field)return;["states","string"].forEach(function(a){void 0===d[a]&&void 0!==e.description[a]&&(d[a]=e.description[a])})}var f=this.parse(a,b);f=new Sao.View.Form.Page(c.add(f.el,d.string),d),this.state_widgets.push(f)},_parse_field:function(a,b,c,d){var e=d.name;if(!(e in a.fields)||e==this.screen.exclude_field)return void c.add(d);d.widget||(d.widget=a.fields[e].description.type);var f=["relation","domain","selection","help","relation_field","string","views","add_remove","sort","context","size","filename","autocomplete","translate","create","delete","selection_change_with","schema_model"];for(var g in f){var h=f[g];h in a.fields[e].description&&null===b.getAttribute(h)&&(d[h]=a.fields[e].description[h])}var i=Sao.View.form_widget_get(d.widget);if(!i)return void c.add(d);var j=new i(e,a,d);return j.position=this.widget_id+=1,j.view=this,c.add(d,j),void 0===this.widgets[e]&&(this.widgets[e]=[]),this.widgets[e].push(j),this.fields[e]=!0,j},_parse_group:function(a,b,c,d){var e=new Sao.View.Form.Group(d);e.add(this.parse(a,b)),this.state_widgets.push(e),c.add(d,e)},_parse_paned:function(a,b,c,d,e){void 0===d.yexpand&&(d.yexpand=!0),void 0===d.yfill&&(d.yfill=!0);var f=new Sao.common.Paned(e);c.add(d,f),this.parse(a,b,f)},_parse_child:function(a,b,c,d){var e,f=this.parse(a,b);e=c.get_child1().children().length?c.get_child2():c.get_child1(),e.append(f.el)},get_buttons:function(){var a=[];for(var b in this.state_widgets){var c=this.state_widgets[b];c instanceof Sao.common.Button&&a.push(c)}return a},display:function(){var a,b,c=this.screen.current_record,d={};if(c){var e=[];for(b in c.model.fields)a=c.model.fields[b],e.push([b,a.description.loading||"eager"]);e.sort(function(a,b){return a[1].localeCompare(b[1])}),e.forEach(function(a){var b=a[0];d[b]=c.load(b)})}var f=function(a,b,c){var e=jQuery.when();c in d&&(e=d[c]),e.done(function(){b.set_state(a)})},g=function(a,b,c){return function(e){var f=jQuery.when();c in d&&(f=d[c]),f.done(function(){e.display(a,b)})}};for(b in this.widgets){var h=this.widgets[b];a=null,c&&(a=c.model.fields[b]),a&&f(c,a,b),h.forEach(g(c,a,b))}return jQuery.when.apply(jQuery,jQuery.map(d,function(a){return a})).done(function(){var a;for(a in this.state_widgets){var b=this.state_widgets[a];b.set_state(c)}for(a in this.containers){var d=this.containers[a];d.resize()}Sao.View.resize(this.el)}.bind(this))},set_value:function(){var a=this.screen.current_record;if(a){var b=function(b){b.set_value(a,this)};for(var c in this.widgets)if(c in a.model.fields){var d=this.widgets[c],e=a.model.fields[c];d.forEach(b,e)}}},button_clicked:function(a){var b=a.data;this.screen.button(b.attributes)},selected_records:function(){return this.screen.current_record?[this.screen.current_record]:[]},set_cursor:function(a,b){var c,d,e,f,g,h,i,j,k,l,m=jQuery(document.activeElement),n=m.closest(this.el)>0;if(b||n){if(b)for(c=0;c<this.notebooks.length;c++)g=this.notebooks[c],g.set_current_page(0);this.attributes.cursor in this.widgets?f=Sao.common.find_focusable_child(this.widgets[this.attributes.cursor][0].el):(h=Sao.common.find_focusable_child(this.el),h&&h.focus())}var o=this.screen.current_record;if(o){var p=[],q=this.el.find(".has-error"),r=o.invalid_fields();for(d in r)for(i=this.widgets[d],c=0;c<q.length;c++)for(j=jQuery(q[c]),e=0;e<i.length;e++)if(j.closest(i[e].el).length>0){p.push(j);break}p.length>0&&(f=Sao.common.find_first_focus_widget(this.el,p))}if(f){for(c=0;c<this.notebooks.length;c++)for(g=this.notebooks[c],k=g.get_n_pages(),e=0;k>e;e++)if(h=g.get_nth_page(e),l=jQuery(f).closest(h).length>0){g.set_current_page(e);break}jQuery(f).find("input,select,textarea").focus()}}}),Sao.View.Form.Container=Sao.class_(Object,{init:function(a){void 0===a&&(a=4),this.col=a,this.el=jQuery("<table/>",{"class":"form-container responsive responsive-noheader"}),this.add_row()},add_row:function(){this.el.append(jQuery("<tr/>"))},rows:function(){return this.el.children().children("tr")},row:function(){return this.rows().last()},add:function(a,b){var c=a.colspan;void 0===c&&(c=1);var d=a.xfill;void 0===d&&(d=1);var e=a.xexpand;void 0===e&&(e=1);var f=0,g=this.row();g.children().map(function(a,b){f+=Number(jQuery(b).attr("colspan")||1)}),f+c>this.col&&(this.add_row(),g=this.row());var h;b&&(h=b.el);var i=jQuery("<td/>",{colspan:c,"class":b?b.class_||"":""}).append(h);g.append(i),b&&(a.yfill&&i.css("vertical-align","top"),void 0!==a.xalign&&i.css("text-align",a.xalign>=.5?"right":"left"),e&&(i.addClass("xexpand"),i.css("width","100%")),d&&(i.addClass("xfill"),e&&h.css("width","100%")),a.help&&(b.el.data("toggle","tooltip"),b.el.attr("title",a.help),b.el.tooltip()))},resize:function(){var a,b,c=this.rows().toArray(),d=[],e=this.col,f=!1,g=function(b){b=jQuery(b);var c=[];return a=0,b.children().map(function(){var b=jQuery(this),d=Math.min(Number(b.attr("colspan")),e);b.hasClass("xexpand")&&!jQuery.isEmptyObject(b.children())&&"none"!=b.children(":not(.tooltip)").css("display")&&c.push([b,a]),a+=d}),c};c.sort(function(a,b){if(a=g(a),b=g(b),a.length==b.length){var c=function(a,b){var c=b[0],d=Math.min(Number(c.attr("colspan")),e);return a+d};return a.reduce(c,0)-b.reduce(c,0)}return b.length-a.length}),c.forEach(function(c){c=jQuery(c);var h=g(c),i=100/h.length;h.forEach(function(c){var f=c[0];a=c[1];var g=Math.min(Number(f.attr("colspan")),e),h=0;for(b=0;g>b;b++)h+=d[a+b]||0;for(b=0;g>b;b++)if(h){if(h>i){var j=h-i;d[a+b]&&(d[a+b]-=j/(h/d[a+b]))}}else d[a+b]=i/g}),jQuery.isEmptyObject(h)||(f=!0)}),c.forEach(function(c){c=jQuery(c),a=0,c.children().map(function(){var c=jQuery(this),f=Math.min(Number(c.attr("colspan")),e);if(c.hasClass("xexpand")&&"none"!=c.children(":not(.tooltip)").css("display")){var g=0;for(b=0;f>b;b++)g+=d[a+b]||0;c.css("width",g+"%")}else c.css("width","");"none"==c.children().css("display")?c.css("visibility","collapse"):c.css("visibility","visible"),a+=f})}),f?this.el.css("width","100%"):this.el.css("width","")}}),Sao.View.Form.StateWidget=Sao.class_(Object,{init:function(a){this.attributes=a},set_state:function(a){var b;b=a?a.expr_eval(this.attributes.states||{}):{};var c=b.invisible;void 0===c&&(c=this.attributes.invisible),c?this.hide():this.show()},show:function(){this.el.show()},hide:function(){this.el.hide()}}),Sao.View.Form.Separator=Sao.class_(Sao.View.Form.StateWidget,{init:function(a,b){Sao.View.Form.Separator._super.init.call(this,b),this.el=jQuery("<div/>",{"class":"form-separator"}),a&&this.el.append(jQuery("<label/>",{text:a})),this.el.append(jQuery("<hr/>"))}}),Sao.View.Form.Label=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-label",init:function(a,b){Sao.View.Form.Label._super.init.call(this,b),this.el=jQuery("<label/>",{text:a,"class":this.class_+" form-label"})},set_state:function(a){Sao.View.Form.Label._super.set_state.call(this,a);var b;if(this.attributes.name&&a&&(b=a.model.fields[this.attributes.name]),void 0===this.attributes.string&&b){var c="";a&&(c=b.get_client(a)||""),this.el.val(c)}var d;d=a?a.expr_eval(this.attributes.states||{}):{},b&&b.description.required||d.required?this.el.addClass("required"):this.el.removeClass("required"),b&&b.description.readonly||d.readonly?(this.el.removeClass("editable"),this.el.removeClass("required")):this.el.addClass("editable")}}),Sao.View.Form.Notebook=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-notebook",init:function(a){Sao.View.Form.Notebook._super.init.call(this,a),this.el=jQuery("<div/>",{"class":this.class_}),this.nav=jQuery("<ul/>",{"class":"nav nav-tabs",role:"tablist"}).appendTo(this.el),this.panes=jQuery("<div/>",{"class":"tab-content"}).appendTo(this.el),this.selected=!1},add:function(a,b){var c=jQuery("<div/>",{role:"tabpanel","class":"tab-pane"}).uniqueId(),d=c.attr("id"),e=jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{"aria-controls":d,role:"tab","data-toggle":"tab",href:"#"+d}).append(b).on("shown.bs.tab",function(){Sao.View.resize(a)})).appendTo(this.nav);return c.html(a).appendTo(this.panes),this.selected||(e.addClass("active"),c.addClass("active"),this.selected=!0),e},set_current_page:function(a){var b=this.nav.find('li[role="presentation"]:eq('+a+") a");b.tab("show")},get_n_pages:function(){return this.nav.find("li[role='presentation']").length},get_nth_page:function(a){return jQuery(this.panes.find("div[role='tabpanel']")[a])}}),Sao.View.Form.Page=Sao.class_(Sao.View.Form.StateWidget,{init:function(a,b){Sao.View.Form.Page._super.init.call(this,b),this.el=a}}),Sao.View.Form.Group=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-group_",init:function(a){Sao.View.Form.Group._super.init.call(this,a),this.el=jQuery("<div/>",{"class":this.class_})},add:function(a){this.el.append(a.el)}}),Sao.View.Form.Image_=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-image_",init:function(a){Sao.View.Form.Image_._super.init.call(this,a),this.el=jQuery("<div/>",{class_:this.class_}),this.img=jQuery("<img/>",{"class":"center-block"}).appendTo(this.el),Sao.common.ICONFACTORY.register_icon(a.name).done(function(a){this.img.attr("src",a)}.bind(this))}}),Sao.View.form_widget_get=function(a){switch(a){case"char":return Sao.View.Form.Char;case"password":return Sao.View.Form.Password;case"date":return Sao.View.Form.Date;case"datetime":return Sao.View.Form.DateTime;case"time":return Sao.View.Form.Time;case"timedelta":return Sao.View.Form.TimeDelta;case"integer":case"biginteger":return Sao.View.Form.Integer;case"float":case"numeric":return Sao.View.Form.Float;case"selection":return Sao.View.Form.Selection;case"boolean":return Sao.View.Form.Boolean;case"text":return Sao.View.Form.Text;case"richtext":return Sao.View.Form.RichText;case"many2one":return Sao.View.Form.Many2One;case"one2one":return Sao.View.Form.One2One;case"reference":return Sao.View.Form.Reference;case"one2many":return Sao.View.Form.One2Many;case"many2many":return Sao.View.Form.Many2Many;case"binary":return Sao.View.Form.Binary;case"multiselection":return Sao.View.Form.MultiSelection;case"image":return Sao.View.Form.Image;case"url":return Sao.View.Form.URL;case"email":return Sao.View.Form.Email;case"callto":return Sao.View.Form.CallTo;case"sip":return Sao.View.Form.SIP;case"progressbar":return Sao.View.Form.ProgressBar;case"dict":return Sao.View.Form.Dict}},Sao.View.Form.Widget=Sao.class_(Object,{init:function(a,b,c){this.field_name=a,this.model=b,this.view=null,this.attributes=c,this.el=null,this.position=0,this.visible=!0,this.labelled=null},display:function(a,b){var c=this.attributes.readonly,d=this.attributes.invisible;if(!b)return void 0===c&&(c=!0),void 0===d&&(d=!1),this.set_readonly(c),void this.set_invisible(d);var e=b.get_state_attrs(a);void 0===c&&(c=e.readonly,void 0===c&&(c=!1)),this.view.screen.attributes.readonly&&(c=!0),this.set_readonly(c);var f=e.invalid;!c&&f?this.el.addClass("has-error"):this.el.removeClass("has-error"),void 0===d&&(d=b.get_state_attrs(a).invisible,void 0===d&&(d=!1)),this.set_invisible(d)},record:function(){return this.view&&this.view.screen?this.view.screen.current_record:void 0},field:function(){var a=this.record();return a?a.model.fields[this.field_name]:void 0},focus_out:function(){this.field()&&this.visible&&this.set_value(this.record(),this.field())},set_value:function(a,b){},set_readonly:function(a){this.el.prop("disabled",a)},set_invisible:function(a){this.visible=!a,a?this.el.hide():this.el.show()}}),Sao.View.Form.Char=Sao.class_(Sao.View.Form.Widget,{class_:"form-char",init:function(a,b,c){Sao.View.Form.Char._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.group=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el),this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.group),c.autocomplete&&(this.datalist=jQuery("<datalist/>").appendTo(this.el),this.datalist.uniqueId(),this.input.attr("list",this.datalist.attr("id"))),this.el.change(this.focus_out.bind(this)),c.size||this.group.css("width","100%")},display:function(a,b){if(Sao.View.Form.Char._super.display.call(this,a,b),this.datalist){this.datalist.children().remove();var c=[];a&&(c=a.autocompletion[this.field_name]||[]),c.forEach(function(a){jQuery("<option/>",{value:a}).appendTo(this.datalist)}.bind(this))}var d="",e="100%";if(a&&(d=a.expr_eval(this.attributes.size),d>0&&(e=null)),this.input.attr("maxlength",d),this.input.attr("size",d),this.group.css("width",e),a){var f=a.field_get_client(this.field_name);this.input.val(f||"")}else this.input.val("")},set_value:function(a,b){b.set_client(a,this.input.val())},set_readonly:function(a){this.input.prop("readonly",a)},focus:function(){this.input.focus()}}),Sao.View.Form.Password=Sao.class_(Sao.View.Form.Char,{class_:"form-password",init:function(a,b,c){Sao.View.Form.Password._super.init.call(this,a,b,c),this.input.prop("type","password")}}),Sao.View.Form.Date=Sao.class_(Sao.View.Form.Widget,{class_:"form-date",_width:"12em",init:function(a,b,c){Sao.View.Form.Date._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.date=this.labelled=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el),this.input=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.date),jQuery("<span/>",{"class":"input-group-btn"}).append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"}))).appendTo(this.date),this.date.datetimepicker(),this.date.css("width",this._width),this.date.on("dp.change",this.focus_out.bind(this))},get_format:function(a,b){return b.date_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isDate=!0),c},display:function(a,b){a&&b&&this.date.data("DateTimePicker").format(Sao.common.moment_format(this.get_format(a,b))),Sao.View.Form.Date._super.display.call(this,a,b);var c;c=a?b.get_client(a):null,this.date.data("DateTimePicker").date(c)},focus:function(){this.input.focus()},set_value:function(a,b){b.set_client(a,this.get_value(a,b))},set_readonly:function(a){this.date.find("button").prop("disabled",a),this.date.find("input").prop("readonly",a)}}),Sao.View.Form.DateTime=Sao.class_(Sao.View.Form.Date,{class_:"form-datetime",_width:"25em",get_format:function(a,b){return b.date_format(a)+" "+b.time_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isDateTime=!0),c}}),Sao.View.Form.Time=Sao.class_(Sao.View.Form.Date,{class_:"form-time",_width:"10em",get_format:function(a,b){return b.time_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isTime=!0),c}}),Sao.View.Form.TimeDelta=Sao.class_(Sao.View.Form.Widget,{class_:"form-timedelta",init:function(a,b,c){Sao.View.Form.TimeDelta._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.el),this.el.change(this.focus_out.bind(this))},display:function(a,b){if(Sao.View.Form.TimeDelta._super.display.call(this,a,b),a){var c=a.field_get_client(this.field_name);this.input.val(c||"")}else this.input.val("")},focus:function(){this.input.focus()},set_value:function(a,b){b.set_client(a,this.input.val())},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Integer=Sao.class_(Sao.View.Form.Char,{class_:"form-integer",init:function(a,b,c){Sao.View.Form.Integer._super.init.call(this,a,b,c),this.input.attr("type","text"),this.input.attr("width",8),this.group.css("width",""),this.factor=Number(c.factor||1)},set_value:function(a,b){b.set_client(a,this.input.val(),void 0,this.factor)},display:function(a,b){if(Sao.View.Form.Char._super.display.call(this,a,b),a){var c=a.model.fields[this.field_name].get_client(a,this.factor);this.input.val(c)}else this.input.val("")}}),Sao.View.Form.Float=Sao.class_(Sao.View.Form.Integer,{class_:"form-float"}),Sao.View.Form.Selection=Sao.class_(Sao.View.Form.Widget,{
-class_:"form-selection",init:function(a,b,c){Sao.View.Form.Selection._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.select=this.labelled=jQuery("<select/>",{"class":"form-control input-sm"}),this.el.append(this.select),this.select.change(this.focus_out.bind(this)),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a,this.set_selection.bind(this))},update_selection:function(a,b,c){Sao.common.selection_mixin.update_selection.call(this,a,b,function(a){this.set_selection(a),c&&c()}.bind(this))},set_selection:function(a){var b=this.select;b.empty(),a.forEach(function(a){b.append(jQuery("<option/>",{value:JSON.stringify(a[0]),text:a[1]}))})},display_update_selection:function(a,b){this.update_selection(a,b,function(){if(!b)return void this.select.val("");for(var c,d=b.get(a),e=!1,f=0,g=this.selection.length;g>f;f++)if(this.selection[f][0]===d){e=!0;break}e?c=jQuery.when():(c=Sao.common.selection_mixin.get_inactive_selection.call(this,d),c.done(function(a){this.select.append(jQuery("<option/>",{value:JSON.stringify(a[0]),text:a[1],disabled:!0}))}.bind(this))),c.done(function(){this.select.val(JSON.stringify(d))}.bind(this))}.bind(this))},display:function(a,b){Sao.View.Form.Selection._super.display.call(this,a,b),this.display_update_selection(a,b)},focus:function(){this.select.focus()},value_get:function(){return JSON.parse(this.select.val())},set_value:function(a,b){var c=this.value_get();b.set_client(a,c)},set_readonly:function(a){this.select.prop("disabled",a)}}),Sao.View.Form.Boolean=Sao.class_(Sao.View.Form.Widget,{class_:"form-boolean",init:function(a,b,c){Sao.View.Form.Boolean._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<input/>",{type:"checkbox","class":"form-control input-sm"}).appendTo(this.el),this.input.change(this.focus_out.bind(this))},display:function(a,b){Sao.View.Form.Boolean._super.display.call(this,a,b),a?this.input.prop("checked",a.field_get(this.field_name)):this.input.prop("checked",!1)},focus:function(){this.input.focus()},set_value:function(a,b){var c=this.input.prop("checked");b.set_client(a,c)},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Text=Sao.class_(Sao.View.Form.Widget,{class_:"form-text",init:function(a,b,c){Sao.View.Form.Text._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<textarea/>",{"class":"form-control input-sm"}).appendTo(this.el),this.input.change(this.focus_out.bind(this))},display:function(a,b){if(Sao.View.Form.Text._super.display.call(this,a,b),a){var c=a.field_get_client(this.field_name);this.input.val(c)}else this.input.val("")},focus:function(){this.input.focus()},set_value:function(a,b){var c=this.input.val()||"";b.set_client(a,c)},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.RichText=Sao.class_(Sao.View.Form.Widget,{class_:"form-richtext",init:function(a,b,c){var d,e,f;Sao.View.Form.RichText._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.toolbar=jQuery("<div/>",{"class":"btn-toolbar",role:"toolbar"}).appendTo(jQuery("<div/>",{"class":"panel-heading"}).appendTo(this.el));var g=function(a){document.execCommand(a.data)},h=function(a){var b=jQuery("<div/>",{"class":"btn-group",role:"group"}).appendTo(this.toolbar);for(d in a)e=a[d],f=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-"+e.icon})).appendTo(b),f.click(e.command,g)}.bind(this);h([{icon:"bold",command:"bold"},{icon:"italic",command:"italic"},{icon:"text-color",command:"underline"}]);var i=[{heading:Sao.i18n.gettext("Font"),options:["Normal","Serif","Sans","Monospace"],command:"fontname"},{heading:Sao.i18n.gettext("Size"),options:[1,2,3,4,5,6,7],command:"fontsize"}],j=function(a,b){return function(c){a.append(jQuery("<li/>").append(jQuery("<a/>",{href:"#"}).append(c).click(function(){document.execCommand(b.command,!1,c)})))}};for(d in i){e=i[d];var k=jQuery("<div/>",{"class":"btn-group",role:"group"}).appendTo(this.toolbar);f=jQuery("<button/>",{"class":"btn btn-default dropdown-toggle","data-toggle":"dropdown","aria-expanded":!1,"aria-haspopup":!0}).append(e.heading).append(jQuery("<span/>",{"class":"caret"})).appendTo(k);var l=jQuery("<ul/>",{"class":"dropdown-menu"}).appendTo(k);e.options.forEach(j(l,e))}h([{icon:"align-left",command:"justifyLeft"},{icon:"align-center",command:"justifyCenter"},{icon:"align-right",command:"justifyRight"},{icon:"align-justify",command:"justifyFull"}]),[["foreColor","#000000"]].forEach(function(a){var b=a[0],c=a[1];jQuery("<input/>",{"class":"btn btn-default",type:"color"}).appendTo(this.toolbar).change(function(){document.execCommand(b,!1,jQuery(this).val())}).focusin(function(){document.execCommand(b,!1,jQuery(this).val())}).val(c)}.bind(this)),this.input=this.labelled=jQuery("<div/>",{"class":"richtext",contenteditable:!0}).appendTo(jQuery("<div/>",{"class":"panel-body"}).appendTo(this.el)),this.el.focusout(this.focus_out.bind(this))},focus_out:function(){window.setTimeout(function(){0===this.el.find(":focus").length&&Sao.View.Form.RichText._super.focus_out.call(this)}.bind(this),0)},display:function(a,b){Sao.View.Form.RichText._super.display.call(this,a,b);var c="";a&&(c=a.field_get_client(this.field_name)),this.input.html(c)},focus:function(){this.input.focus()},set_value:function(a,b){this.input.find("div").each(function(a,b){if(b=jQuery(b),b.css("text-align")){var c=b.css("text-align").split("-").pop();b.attr("align",c),b.css("text-align","")}"start"==b.attr("align")&&b.attr("align","left")});var c=this.input.html()||"";b.set_client(a,c)},set_readonly:function(a){this.input.prop("contenteditable",!a),this.toolbar.find("button,select").prop("disabled",a)}}),Sao.View.Form.Many2One=Sao.class_(Sao.View.Form.Widget,{class_:"form-many2one",init:function(a,b,c){Sao.View.Form.Many2One._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_});var d=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.entry=this.labelled=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(d),this.entry.on("keydown",this.key_press.bind(this)),c.completion&&"1"!=c.completion||(Sao.common.get_completion(d,this._update_completion.bind(this),this._completion_match_selected.bind(this),this._completion_action_activated.bind(this)),this.wid_completion=!0);var e=jQuery("<span/>",{"class":"input-group-btn"}).appendTo(d);this.but_open=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-search"})).appendTo(e),this.but_open.click(this.edit.bind(this)),this.el.change(this.focus_out.bind(this)),this._readonly=!1},get_screen:function(){var a=this.field().get_domain(this.record()),b=this.field().get_context(this.record());return new Sao.Screen(this.get_model(),{context:b,domain:a,mode:["form"],view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views,readonly:this._readonly})},set_text:function(a){jQuery.isEmptyObject(a)&&(a=""),this.entry.val(a)},get_text:function(){var a=this.record();return a?a.field_get_client(this.field_name):""},focus_out:function(){(this.attributes.completion&&"1"!=this.attributes.completion||!this.el.find(".dropdown").hasClass("open"))&&Sao.View.Form.Many2One._super.focus_out.call(this)},set_value:function(a,b){b.get_client(a)!=this.entry.val()&&(b.set_client(a,this.value_from_id(null,"")),this.entry.val(""))},display:function(a,b){var c=this.record();if(!c||!a||c.id==a.id){var d;if(Sao.View.Form.Many2One._super.display.call(this,a,b),this._set_button_sensitive(),this._set_completion(),!a)return void this.entry.val("");this.set_text(b.get_client(a)),d=b.get(a),this.but_open.button(this.has_target(d)?{icons:{primary:"glyphicon-folder-open"}}:{icons:{primary:"glyphicon-search"}})}},focus:function(){this.entry.focus()},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){this.entry.prop("readonly",this._readonly),this.but_open.prop("disabled",this._readonly||!this.read_access())},get_access:function(a){var b=this.get_model();return b?Sao.common.MODELACCESS.get(b)[a]:!0},read_access:function(){return this.get_access("read")},create_access:function(){return this.attributes.create&&this.get_access("create")},id_from_value:function(a){return a},value_from_id:function(a,b){return void 0===b&&(b=""),[a,b]},get_model:function(){return this.attributes.relation},has_target:function(a){return void 0!==a&&null!==a},edit:function(a){var b=this.get_model();if(b&&Sao.common.MODELACCESS.get(b).read){var c,d,e=this.record(),f=e.field_get(this.field_name);if(b&&this.has_target(f)){var g=this.get_screen(),h=this.id_from_value(e.field_get(this.field_name));g.new_group([h]),d=function(a){if(a){var b=g.current_record.rec_name();b.done(function(a){var b=this.value_from_id(g.current_record.id,a);this.record().field_set_client(this.field_name,b,!0)}.bind(this))}},c=new Sao.Window.Form(g,d.bind(this),{save_current:!0})}else if(b){var i=this.field().get_domain(e),j=this.field().get_context(e),k=this.entry.val();d=function(a){if(!jQuery.isEmptyObject(a)){var b=this.value_from_id(a[0][0],a[0][1]);this.record().field_set_client(this.field_name,b,!0)}};var l=new Sao.common.DomainParser;c=new Sao.Window.Search(b,d.bind(this),{sel_multi:!1,context:j,domain:i,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.create_access(),search_filter:l.quote(k)})}}},new_:function(a){var b=this.get_model();if(b&&Sao.common.MODELACCESS.get(b).create){var c=this.get_screen(),d=function(a){if(a){var b=c.current_record.rec_name();b.done(function(a){var b=this.value_from_id(c.current_record.id,a);this.record().field_set_client(this.field_name,b)}.bind(this))}};new Sao.Window.Form(c,d.bind(this),{new_:!0,save_current:!0})}},key_press:function(a){var b=!this.entry.prop("readonly"),c=[Sao.common.TAB_KEYCODE],d=[Sao.common.BACKSPACE_KEYCODE,Sao.common.DELETE_KEYCODE];if(this.wid_completion||c.push(Sao.common.RETURN_KEYCODE),a.which==Sao.common.F3_KEYCODE&&b&&this.create_access())this.new_(),a.preventDefault();else if(a.which==Sao.common.F2_KEYCODE&&this.read_access())this.edit(),a.preventDefault();else if(~c.indexOf(a.which)&&b){if((!this.attributes.completion||"1"==this.attributes.completion)&&this.el.find(".dropdown").hasClass("open"))return;this.activate()}else if(this.has_target(this.record().field_get(this.field_name))&&b){var e=this.get_text();(e!=this.entry.val()||~d.indexOf(a.which))&&(this.entry.val(""),this.record().field_set_client(this.field_name,this.value_from_id(null,"")))}},activate:function(){var a=this.get_model();if(a&&Sao.common.MODELACCESS.get(a).read){var b=this.record(),c=b.field_get(this.field_name);new Sao.Model(a);if(a&&!this.has_target(c)){var d=this.entry.val();if(!this._readonly&&(d||this.field().get_state_attrs(this.record()).required)){var e=this.field().get_domain(b),f=this.field().get_context(b),g=function(a){if(jQuery.isEmptyObject(a))this.entry.val("");else{var b=this.value_from_id(a[0][0],a[0][1]);this.record().field_set_client(this.field_name,b,!0)}},h=new Sao.common.DomainParser;new Sao.Window.Search(a,g.bind(this),{sel_multi:!1,context:f,domain:e,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.create_access(),search_filter:h.quote(d)})}}}},_set_completion:function(){var a=this.el.find(".action-search");this.read_access()?a.removeClass("disabled"):a.addClass("disabled");var b=this.el.find(".action-create");this.create_access()?b.removeClass("disabled"):b.addClass("disabled")},_update_completion:function(a){var b=this.record();if(b){var c=this.field(),d=c.get(b);if(this.has_target(d)){var e=this.id_from_value(d);if(void 0!==e&&e>0)return jQuery.when()}var f=this.get_model();return Sao.common.update_completion(this.entry,b,c,f)}},_completion_match_selected:function(a){this.record().field_set_client(this.field_name,this.value_from_id(a.id,a.rec_name),!0)},_completion_action_activated:function(a){"search"==a?this.edit():"create"==a&&this.new_()}}),Sao.View.Form.One2One=Sao.class_(Sao.View.Form.Many2One,{class_:"form-one2one"}),Sao.View.Form.Reference=Sao.class_(Sao.View.Form.Many2One,{class_:"form-reference",init:function(a,b,c){Sao.View.Form.Reference._super.init.call(this,a,b,c),this.el.addClass("form-inline"),this.select=jQuery("<select/>",{"class":"form-control input-sm","aria-label":c.string}),this.el.prepend(jQuery("<span/>").text("-")),this.el.prepend(this.select),this.select.change(this.select_changed.bind(this)),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a,this.set_selection.bind(this))},update_selection:function(a,b,c){Sao.common.selection_mixin.update_selection.call(this,a,b,function(a){this.set_selection(a),c&&c()}.bind(this))},set_selection:function(a){var b=this.select;b.empty(),a.forEach(function(a){b.append(jQuery("<option/>",{value:a[0],text:a[1]}))})},id_from_value:function(a){return parseInt(a.split(",")[1],10)},value_from_id:function(a,b){return b||(b=""),[this.get_model(),[a,b]]},get_text:function(){var a=this.record();return a?a.field_get_client(this.field_name)[1]:""},get_model:function(){return this.select.val()},has_target:function(a){if(null===a)return!1;var b=a.split(",")[0];return a=a.split(",")[1],jQuery.isEmptyObject(a)?a=null:(a=parseInt(a,10),isNaN(a)&&(a=null)),b==this.get_model()&&a>=0},_set_button_sensitive:function(){Sao.View.Form.Reference._super._set_button_sensitive.call(this),this.select.prop("disabled",this.entry.prop("readonly"))},select_changed:function(){this.entry.val("");var a,b=this.get_model();a=b?[b,[-1,""]]:["",""],this.record().field_set_client(this.field_name,a)},set_value:function(a,b){var c;if(this.get_model()){c=b.get_client(a,this.field_name);var d,e;c instanceof Array?(d=c[0],e=c[1]):(d="",e=""),(d!=this.get_model()||e!=this.entry.val())&&(b.set_client(a,null),this.entry.val(""))}else c=this.entry.val(),jQuery.isEmptyObject(c)?b.set_client(a,null):b.set_client(a,["",c])},set_text:function(a){var b;a?(b=a[0],a=a[1]):(b=null,a=null),Sao.View.Form.Reference._super.set_text.call(this,a),this.select.val(b?b:"")},display:function(a,b){this.update_selection(a,b,function(){Sao.View.Form.Reference._super.display.call(this,a,b)}.bind(this))},set_readonly:function(a){Sao.View.Form.Reference._super.set_readonly.call(this,a),this.select.prop("disabled",a)}}),Sao.View.Form.One2Many=Sao.class_(Sao.View.Form.Widget,{class_:"form-one2many",init:function(a,b,c){Sao.View.Form.One2Many._super.init.call(this,a,b,c),this._readonly=!0,this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.menu=jQuery("<div/>",{"class":this.class_+"-menu panel-heading"}),this.el.append(this.menu);var d=jQuery("<label/>",{"class":this.class_+"-string",text:c.string});this.menu.append(d),d.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",d.attr("id")),d.attr("for",this.el.attr("id"));var e=jQuery("<div/>",{"class":this.class_+"-toolbar"});this.menu.append(e);var f=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(e);this.wid_text=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(f),this.wid_text.hide();var g=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(f);c.add_remove&&(this.wid_text.show(),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(g),this.but_add.click(this.add.bind(this)),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(g),this.but_remove.click(this.remove.bind(this))),this.but_new=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("New")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-edit"})).appendTo(g),this.but_new.click(this.new_.bind(this)),this.but_open=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Open")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-folder-open"})).appendTo(g),this.but_open.click(this.open.bind(this)),this.but_del=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Delete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-trash"})).appendTo(g),this.but_del.click(this.delete_.bind(this)),this.but_undel=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Undelete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-repeat"})).appendTo(g),this.but_undel.click(this.undelete.bind(this)),this.but_previous=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-left"})).appendTo(g),this.but_previous.click(this.previous.bind(this)),this.label=jQuery("<span/>",{"class":"btn"}).appendTo(g),this.label.text("(0, 0)"),this.but_next=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-right"})).appendTo(g),this.but_next.click(this.next.bind(this)),this.but_switch=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Switch")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-list-alt"})).appendTo(g),this.but_switch.click(this.switch_.bind(this)),this.content=jQuery("<div/>",{"class":this.class_+"-content panel-body"}),this.el.append(this.content);var h=(c.mode||"tree,form").split(",");this.screen=new Sao.Screen(c.relation,{mode:h,view_ids:(c.view_ids||"").split(","),views_preload:c.views||{},row_activate:this.activate.bind(this),readonly:c.readonly||!1,exclude_field:c.relation_field||null,pre_validate:c.pre_validate}),this.screen.pre_validate=1==c.pre_validate,this.prm=this.screen.switch_view(h[0]).done(function(){this.content.append(this.screen.screen_container.el)}.bind(this)),this.but_switch.prop("disabled",this.screen.number_of_views()<=0)},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){var a,b,c=Sao.common.MODELACCESS.get(this.screen.model_name),d=this.record(),e=this.field();if(d&&e){var f=d.expr_eval(this.attributes.size);b=e.get_eval(d),a=void 0!==f&&null!==f&&b>=f>=0}else b=null,a=!1;var g=this.attributes.create;void 0===g&&(g=!0),this.but_new.prop("disabled",this._readonly||!g||a||!c.create);var h=this.attributes["delete"];void 0===h&&(h=!0),this.but_del.prop("disabled",this._readonly||!h||!c["delete"]),this.but_undel.prop("disabled",this._readonly||a),this.but_open.prop("disabled",!c.read),this.attributes.add_remove&&(this.wid_text.prop("disabled",this._readonly),this.but_add.prop("disabled",this._readonly||a||!c.write||!c.read),this.but_remove.prop("disabled",this._readonly||!c.write||!c.read))},display:function(a,b){Sao.View.Form.One2Many._super.display.call(this,a,b),this._set_button_sensitive(),this.prm.done(function(){if(a){if(void 0===b)return this.screen.new_group(),this.screen.set_current_record(null),this.screen.group.parent=null,void this.screen.display();var c=a.field_get_client(this.field_name);c!=this.screen.group&&(this.screen.set_group(c),"tree"==this.screen.current_view.view_type&&this.screen.current_view.editable&&this.screen.set_current_record(null));var d=!1,e=[],f=null;a&&(d=b.get_state_attrs(a).readonly,e=b.get_domain(a),f=a.expr_eval(this.attributes.size)),Sao.common.compare(this.screen.domain,e)||(this.screen.domain=e),this.screen.group.set_readonly(d),this.screen.size_limit=f,this.screen.display()}}.bind(this))},focus:function(){this.wid_text.is(":visible")&&this.wid_text.focus()},activate:function(a){this.edit()},add:function(a){var b=Sao.common.MODELACCESS.get(this.screen.model_name);if(b.write&&b.read){this.view.set_value();var c=this.field().get_domain(this.record()),d=this.field().get_context(this.record());c=[c,this.record().expr_eval(this.attributes.add_remove)];var e=this.field().get_removed_ids(this.record());c=["OR",c,["id","in",e]];var f=this.wid_text.val(),g=function(a){var b=jQuery.when();if(!jQuery.isEmptyObject(a)){var c,d,e=[];for(c=0,d=a.length;d>c;c++)e.push(a[c][0]);this.screen.group.load(e,!0),b=this.screen.display()}b.done(function(){this.screen.set_cursor()}.bind(this)),this.wid_text.val("")}.bind(this),h=new Sao.common.DomainParser;new Sao.Window.Search(this.attributes.relation,g,{sel_multi:!0,context:d,domain:c,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:!this.but_new.prop("disabled"),search_filter:h.quote(f)})}},remove:function(a){var b=Sao.common.MODELACCESS.get(this.screen.model_name);b.write&&b.read&&this.screen.remove(!1,!0,!1)},new_:function(a){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.validate().done(function(){this.attributes.product?this.new_product():this.new_single()}.bind(this))},new_single:function(){var a=jQuery.extend({},this.field().get_context(this.record()));if("form"==this.screen.current_view.type||this.screen.current_view.editable)this.screen.new_(),this.screen.current_view.el.prop("disabled",!1);else{var b=this.record(),c=b.expr_eval(this.attributes.size)||-1;c-=this.field().get_eval(b);new Sao.Window.Form(this.screen,function(){},{new_:!0,many:c,context:a})}},new_product:function(){var a=this.attributes.product.split(","),b={},c=this.screen;c.new_(!1).then(function(d){d.default_get().then(function(e){d.set_default(e);var f=function(){if(jQuery.isEmptyObject(a))return g();var e=c.model.fields[a.pop()],h=e.description.relation;h||f();var i=e.get_domain(d),j=e.get_context(d),k=function(a){jQuery.isEmptyObject(a)||(b[e.name]=a),f()};new Sao.Window.Search(h,k,{sel_multi:!0,context:j,domain:i,search_filter:""})},g=function(){if(jQuery.isEmptyObject(b))return void c.group.remove(d,!0);var a=Object.keys(b),f=a.map(function(a){return b[a]});Sao.common.product(f).forEach(function(b){var f=function(c){var d=jQuery.extend({},e);a.forEach(function(a,c){d[a]=b[c][0],d[a+".rec_name"]=b[c][1]}),c.set_default(d)};d?(f(d),d=null):c.new_(!1).then(f)})};f()})})},open:function(a){this.edit()},delete_:function(a){Sao.common.MODELACCESS.get(this.screen.model_name)["delete"]&&this.screen.remove(!1,!1,!1)},undelete:function(a){this.screen.unremove()},previous:function(a){this.validate().done(function(){this.screen.display_previous()}.bind(this))},next:function(a){this.validate().done(function(){this.screen.display_next()}.bind(this))},switch_:function(a){this.screen.switch_view()},edit:function(){Sao.common.MODELACCESS.get(this.screen.model_name).read&&this.validate().done(function(){var a=this.screen.current_record;if(a){new Sao.Window.Form(this.screen,function(){})}}.bind(this))},validate:function(){var a=jQuery.Deferred();this.view.set_value();var b=this.screen.current_record;if(b){var c=this.screen.current_view.get_fields();b.validate(c).then(function(c){return c?this.screen.pre_validate?b.pre_validate().then(function(b){return b?void a.resolve():void a.reject()}):void a.resolve():(this.screen.display(!0),void a.reject())}.bind(this))}else a.resolve();return a},set_value:function(a,b){this.screen.save_tree_state()}}),Sao.View.Form.Many2Many=Sao.class_(Sao.View.Form.Widget,{class_:"form-many2many",init:function(a,b,c){Sao.View.Form.Many2Many._super.init.call(this,a,b,c),this._readonly=!0,this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.menu=jQuery("<div/>",{"class":this.class_+"-menu panel-heading"}),this.el.append(this.menu);var d=jQuery("<label/>",{"class":this.class_+"-string",text:c.string});this.menu.append(d),d.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",d.attr("id")),d.attr("for",this.el.attr("id"));var e=jQuery("<div/>",{"class":this.class_+"-toolbar"});this.menu.append(e);var f=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(e);this.entry=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(f),this.entry.on("keydown",this.key_press.bind(this));var g=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(f);this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(g),this.but_add.click(this.add.bind(this)),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(g),this.but_remove.click(this.remove.bind(this)),this.content=jQuery("<div/>",{"class":this.class_+"-content panel-body"}),this.el.append(this.content),this.screen=new Sao.Screen(c.relation,{mode:["tree"],view_ids:(c.view_ids||"").split(","),views_preload:c.views||{},row_activate:this.activate.bind(this)}),this.prm=this.screen.switch_view("tree").done(function(){this.content.append(this.screen.screen_container.el)}.bind(this))},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){var a=!1;this.record()&&this.field(),this.entry.prop("disabled",this._readonly),this.but_add.prop("disabled",this._readonly||a),this.but_remove.prop("disabled",this._readonly)},display:function(a,b){Sao.View.Form.Many2Many._super.display.call(this,a,b),this.prm.done(function(){if(a){if(void 0===b)return this.screen.new_group(),this.screen.set_current_record(null),this.screen.group.parent=null,void this.screen.display();var c=a.field_get_client(this.field_name);c!=this.screen.group&&this.screen.set_group(c),this.screen.display()}}.bind(this))},focus:function(){this.entry.focus()},activate:function(){this.edit()},add:function(){var a=this.field().get_domain(this.record()),b=this.field().get_context(this.record()),c=this.entry.val(),d=function(a){if(!jQuery.isEmptyObject(a)){var b,c,d=[];for(b=0,c=a.length;c>b;b++)d.push(a[b][0]);this.screen.group.load(d,!0),this.screen.display()}this.entry.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(this.attributes.relation,d,{sel_multi:!0,context:b,domain:a,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.attributes.create,search_filter:e.quote(c)})},remove:function(){this.screen.remove(!1,!0,!1)},key_press:function(a){var b=[Sao.common.TAB_KEYCODE];this.wid_completion||b.push(Sao.common.RETURN_KEYCODE),a.which==Sao.common.F3_KEYCODE?(this.new_(),a.preventDefault()):a.which==Sao.common.F2_KEYCODE?(this.add(),a.preventDefault()):~b.indexOf(a.which)&&this.entry.val()&&this.add()},edit:function(){if(!jQuery.isEmptyObject(this.screen.current_record)){var a=this.field().get_domain(this.record()),b=this.record().expr_eval(this.attributes.add_remove);jQuery.isEmptyObject(b)||(a=[a,b]);var c=this.field().get_context(this.record()),d=new Sao.Screen(this.attributes.relation,{domain:a,view_ids:(this.attributes.view_ids||"").split(","),mode:["form"],views_preload:this.attributes.views,readonly:this.attributes.readonly||!1,context:c});d.new_group([this.screen.current_record.id]);var e=function(a){a&&d.current_record.save().done(function(){this.screen.current_record.cancel()}.bind(this))}.bind(this);d.switch_view().done(function(){new Sao.Window.Form(d,e)})}},new_:function(){var a=this.field().get_domain(this.record()),b=this.record().expr_eval(this.attributes.add_remove);jQuery.isEmptyObject(b)||(a=[a,b]);var c=this.field().get_context(this.record()),d=new Sao.Screen(this.attributes.relation,{domain:a,view_ids:(this.attributes.view_ids||"").split(","),mode:["form"],views_preload:this.attributes.views,context:c}),e=function(a){if(a){var b=d.current_record;this.screen.group.load([b.id],!0)}this.entry.val("")}.bind(this);d.switch_view().done(function(){new Sao.Window.Form(d,e,{new_:!0,save_current:!0})})}}),Sao.View.Form.BinaryMixin=Sao.class_(Sao.View.Form.Widget,{init:function(a,b,c){Sao.View.Form.BinaryMixin._super.init.call(this,a,b,c),this.filename=c.filename||null},toolbar:function(a){var b=jQuery("<div/>",{"class":a,role:"group"});return this.but_select=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-search"})).appendTo(b),this.but_select.click(this.select.bind(this)),this.filename&&(this.but_open=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-folder-open"})).appendTo(b),this.but_open.click(this.open.bind(this))),this.but_save_as=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-save"})).appendTo(b),this.but_save_as.click(this.save_as.bind(this)),this.but_clear=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-erase"})).appendTo(b),this.but_clear.click(this.clear.bind(this)),b},filename_field:function(){var a=this.record();return a?a.model.fields[this.filename]:void 0},select:function(){var a=this.record(),b=function(){d.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),d.modal.modal("hide")},c=function(){var c=new FileReader;c.onload=function(b){var d=new Uint8Array(c.result);this.field().set_client(a,d)}.bind(this),c.onloadend=function(a){b()};var d=e[0].files[0];c.readAsArrayBuffer(d),this.filename&&this.filename_field().set_client(a,d.name)}.bind(this),d=new Sao.Dialog(Sao.i18n.gettext("Select"),"file-dialog");d.footer.append(jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(b)).append(jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).click(c)),d.content.submit(function(a){c(),a.preventDefault()});var e=jQuery("<input/>",{type:"file"}).appendTo(d.body);d.modal.modal("show")},open:function(){this.save_as()},save_as:function(){var a=this.field(),b=this.record();a.get_data(b).done(function(a){var b=new Blob([a[0].binary],{type:"application/octet-binary"}),c=window.URL.createObjectURL(b);this.blob_url&&window.URL.revokeObjectURL(this.blob_url),this.blob_url=c,window.open(c)}.bind(this))},clear:function(){this.field().set_client(this.record(),null)}}),Sao.View.Form.Binary=Sao.class_(Sao.View.Form.BinaryMixin,{class_:"form-binary",blob_url:"",init:function(a,b,c){Sao.View.Form.Binary._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.filename&&c.filename_visible&&(this.text=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(this.el),this.text.change(this.focus_out.bind(this)),this.text.on("keydown",this.key_press.bind(this)));var d=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.size=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(d),this.toolbar("input-group-btn").appendTo(d)},display:function(a,b){if(Sao.View.Form.Binary._super.display.call(this,a,b),!b)return this.size.val(""),this.filename&&this.but_open.button("disable"),this.text&&this.text.val(""),void this.but_save_as.button("disable");var c,d=b.get_size(a);c=d?"enable":"disable",this.filename&&(this.text&&this.text.val(this.filename_field().get(a)||""),this.but_open.button(c)),this.size.val(Sao.common.humanize(d)),this.but_save_as.button(c)},key_press:function(a){var b=!this.wid_text.prop("readonly");a.which==Sao.common.F3_KEYCODE&&b?(this.new_(),a.preventDefault()):a.which==Sao.common.F2_KEYCODE&&(this.open(),a.preventDefault())},set_value:function(a,b){this.text&&this.filename_field().set_client(a,this.text.val()||"")},set_readonly:function(a){a?(this.but_select.hide(),this.but_clear.hide()):(this.but_select.show(),this.but_clear.show()),this.wid_text&&this.wid_text.prop("readonly",a);
-}}),Sao.View.Form.MultiSelection=Sao.class_(Sao.View.Form.Selection,{class_:"form-multiselection",init:function(a,b,c){this.nullable_widget=!1,Sao.View.Form.MultiSelection._super.init.call(this,a,b,c),this.select.prop("multiple",!0)},display_update_selection:function(a,b){var c,d,e;this.update_selection(a,b,function(){if(b){var f=[],g=a.field_get_client(this.field_name);for(c=0,d=g.length;d>c;c++)e=g[c],~g.record_removed.indexOf(e)||~g.record_deleted.indexOf(e)||f.push(e.id);this.el.val(f)}}.bind(this))},set_value:function(a,b){var c=this.el.val();c?c=c.map(function(a){return parseInt(a,10)}):(c=[],b.set_client(a,c))}}),Sao.View.Form.Image=Sao.class_(Sao.View.Form.BinaryMixin,{class_:"form-image",init:function(a,b,c){Sao.View.Form.Image._super.init.call(this,a,b,c),this.height=parseInt(c.height||100,10),this.width=parseInt(c.width||300,10),this.el=jQuery("<div/>"),this.image=jQuery("<img/>",{"class":"center-block"}).appendTo(this.el),this.image.css("max-height",this.height),this.image.css("max-width",this.width),this.image.css("height","auto"),this.image.css("width","auto");var d=this.toolbar("btn-group");c.readonly||jQuery("<div/>",{"class":"text-center"}).append(d).appendTo(this.el),this.update_img()},set_readonly:function(a){[this.but_select,this.but_open,this.but_save_as,this.but_clear].forEach(function(b){b&&b.prop("disable",a)})},clear:function(){Sao.View.Form.Image._super.clear.call(this),this.update_img()},update_img:function(){var a,b=this.record();b&&(a=b.field_get_client(this.field_name)),a=a?a>Sao.common.BIG_IMAGE_SIZE?jQuery.when(null):b.model.fields[this.field_name].get_data(b):jQuery.when(null),a.done(function(a){var b,c;a?(c=new Blob([a[0][this.field_name]]),b=window.URL.createObjectURL(c)):b=null,this.image.attr("src",b)}.bind(this))},display:function(a,b){Sao.View.Form.Image._super.display.call(this,a,b),this.update_img()}}),Sao.View.Form.URL=Sao.class_(Sao.View.Form.Char,{class_:"form-url",init:function(a,b,c){Sao.View.Form.URL._super.init.call(this,a,b,c),this.button=jQuery("<a/>",{"class":"btn btn-default",target:"_new"}).appendTo(jQuery("<span/>",{"class":"input-group-btn"}).appendTo(this.group)),this.icon=jQuery("<img/>").appendTo(this.button),this.set_icon()},display:function(a,b){Sao.View.Form.URL._super.display.call(this,a,b);var c="";if(a&&(c=a.field_get_client(this.field_name)),this.set_url(c),a&this.attributes.icon){var d,e=this.attributes.icon;d=e in a.model.fields?a.field_get_client(e):e,this.set_icon(d)}},set_icon:function(a){a=a||"tryton-web-browser",Sao.common.ICONFACTORY.register_icon(a).done(function(a){this.icon.attr("src",a)}.bind(this))},set_url:function(a){this.button.attr("href",a)},set_readonly:function(a){Sao.View.Form.URL._super.set_readonly.call(this,a),a?(this.input.hide(),this.button.removeClass("btn-default"),this.button.addClass("btn-link")):(this.input.show(),this.button.removeClass("btn-link"),this.button.addClass("btn-default"))}}),Sao.View.Form.Email=Sao.class_(Sao.View.Form.URL,{class_:"form-email",set_url:function(a){Sao.View.Form.Email._super.set_url.call(this,"mailto:"+a)}}),Sao.View.Form.CallTo=Sao.class_(Sao.View.Form.URL,{class_:"form-callto",set_url:function(a){Sao.View.Form.CallTo._super.set_url.call(this,"callto:"+a)}}),Sao.View.Form.SIP=Sao.class_(Sao.View.Form.URL,{class_:"form-sip",set_url:function(a){Sao.View.Form.SIP._super.set_url.call(this,"sip:"+a)}}),Sao.View.Form.ProgressBar=Sao.class_(Sao.View.Form.Widget,{class_:"form-char",init:function(a,b,c){Sao.View.Form.ProgressBar._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_+" progress"}),this.progressbar=jQuery("<div/>",{"class":"progress-bar",role:"progressbar","aria-valuemin":0,"aria-valuemax":100}).appendTo(this.el),this.progressbar.css("min-width: 2em")},display:function(a,b){Sao.View.Form.ProgressBar._super.display.call(this,a,b);var c,d;b?(c=b.get(a),d=b.get_client(a,100),d&&(d=Sao.i18n.gettext("%1%",d))):(c=0,d=""),this.progressbar.attr("aria-valuenow",100*c),this.progressbar.css("width",100*c+"%"),this.progressbar.text(d)}}),Sao.View.Form.Dict=Sao.class_(Sao.View.Form.Widget,{class_:"form-dict",init:function(a,b,c){Sao.View.Form.Dict._super.init.call(this,a,b,c),this.schema_model=new Sao.Model(c.schema_model),this.keys={},this.fields={},this.rows={},this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"});var d=jQuery("<div/>",{"class":this.class_+"-heading panel-heading"}).appendTo(this.el),e=jQuery("<label/>",{"class":this.class_+"-string",text:c.string}).appendTo(d);e.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",e.attr("id")),e.attr("for",this.el.attr("id"));var f=jQuery("<div/>",{"class":this.class_+"-body panel-body"}).appendTo(this.el);this.container=jQuery("<div/>",{"class":this.class_+"-container"}).appendTo(f);var g=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(jQuery("<div>",{"class":"col-md-12"}).appendTo(jQuery("<div/>",{"class":"row"}).appendTo(jQuery("<div/>",{"class":"container-fluid"}).appendTo(f))));this.wid_text=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(g),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(jQuery("<div/>",{"class":"input-group-btn"}).appendTo(g)),this.but_add.click(this.add.bind(this)),this._readonly=!1,this._record_id=null},add:function(){var a=this.field().get_context(this.record()),b=this.wid_text.val(),c=this.field().get_domain(this.record()),d=function(a){if(!jQuery.isEmptyObject(a)){var b=a.map(function(a){return a[0]});this.add_new_keys(b)}this.wid_text.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(this.schema_model.name,d,{sel_multi:!0,context:a,domain:c,new_:!1,search_filter:e.quote(b)})},add_new_keys:function(a){var b=this.field().get_context(this.record());this.schema_model.execute("get_keys",[a],b).then(function(a){var b=!1;a.forEach(function(a){this.fields[a.name]||(this.keys[a.name]=a,this.add_line(a.name),b||(this.fields[a.name].input.focus(),b=!0))}.bind(this))}.bind(this))},remove:function(a,b){void 0===b&&(b=!0),delete this.fields[a],this.rows[a].remove(),delete this.rows[a],b&&this.set_value(this.record(),this.field())},set_value:function(a,b){b.set_client(a,this.get_value())},get_value:function(){var a={};for(var b in this.fields){var c=this.fields[b];a[b]=c.get_value()}return a},set_readonly:function(a){this._readonly=a,this._set_button_sensitive();for(var b in this.fields){var c=this.fields[b];c.set_readonly(a)}this.wid_text.prop("disabled",a)},_set_button_sensitive:function(){var a=this.attributes.create;void 0===a&&(a=!0);var b=this.attributes["delete"];void 0===b&&(b=!0),this.but_add.prop("disabled",this._readonly||!a);for(var c in this.fields){var d=this.fields[c].button;d.prop("disabled",this._readonly||!b)}},add_line:function(a){var b,c;this.fields[a]=b=new(this.get_entries(this.keys[a].type_))(a,this),this.rows[a]=c=jQuery("<div/>",{"class":"row"});var d=this.keys[a].string+Sao.i18n.gettext(":"),e=jQuery("<label/>",{text:d}).appendTo(jQuery("<div/>",{"class":"dict-label col-md-4"}).appendTo(c));b.el.addClass("col-md-8").appendTo(c),e.uniqueId(),b.labelled.uniqueId(),b.labelled.attr("aria-labelledby",e.attr("id")),e.attr("for",b.labelled.attr("id")),b.button.click(function(){this.remove(a,!0)}.bind(this)),c.appendTo(this.container)},add_keys:function(a){var b=this.field().get_context(this.record()),c=this.field().get_domain(this.record()),d=Math.min(10,Sao.config.limit);a=jQuery.extend([],a);for(var e=function(a){return this.schema_model.execute("get_keys",[a],b).then(f)}.bind(this),f=function(a){for(var b=0,c=a.length;c>b;b++){var d=a[b];this.keys[d.name]=d}}.bind(this),g=[];a.length>0;){var h=a.splice(0,d);g.push(this.schema_model.execute("search",[[["name","in",h],c],0,Sao.config.limit,null],b).then(e))}return jQuery.when.apply(jQuery,g)},display:function(a,b){if(Sao.View.Form.Dict._super.display.call(this,a,b),b){var c,d=a?a.id:null;if(d!=this._record_id){for(c in this.fields)this.remove(c,!1);this._record_id=d}var e,f=b.get_client(a),g=Object.keys(f).filter(function(a){return!this.keys[a]}.bind(this));e=jQuery.isEmptyObject(g)?jQuery.when():this.add_keys(g),e.then(function(){var a,b,c,d=Object.keys(f).sort();for(a=0,b=d.length;b>a;a++){c=d[a];var e=f[c];if(this.keys[c]){this.fields[c]||this.add_line(c);var g=this.fields[c];g.set_value(e),g.set_readonly(this._readonly)}}var h=Object.keys(this.fields).filter(function(a){return!f[a]});for(a=0,b=h.length;b>a;a++)c=h[a],this.remove(c,!1)}.bind(this)),this._set_button_sensitive()}},get_entries:function(a){switch(a){case"char":return Sao.View.Form.Dict.Entry;case"boolean":return Sao.View.Form.Dict.Boolean;case"selection":return Sao.View.Form.Dict.Selection;case"integer":return Sao.View.Form.Dict.Integer;case"float":return Sao.View.Form.Dict.Float;case"numeric":return Sao.View.Form.Dict.Numeric;case"date":return Sao.View.Form.Dict.Date;case"datetime":return Sao.View.Form.Dict.DateTime}}}),Sao.View.Form.Dict.Entry=Sao.class_(Object,{class_:"dict-char",init:function(a,b){this.name=a,this.definition=b.keys[a],this.parent_widget=b,this.create_widget()},create_widget:function(){this.el=jQuery("<div/>",{"class":this.class_});var a=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(a),this.button=jQuery("<button/>",{"class":"btn btn-default",type:"button","arial-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(jQuery("<div/>",{"class":"input-group-btn"}).appendTo(a)),this.el.change(this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){return this.input.val()},set_value:function(a){this.input.val(a||"")},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Dict.Boolean=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-boolean",create_widget:function(){Sao.View.Form.Dict.Boolean._super.create_widget.call(this),this.input.attr("type","checkbox"),this.input.change(this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){return this.input.prop("checked")},set_value:function(a){this.input.prop("checked",a)}}),Sao.View.Form.Dict.Selection=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-selection",create_widget:function(){Sao.View.Form.Dict.Selection._super.create_widget.call(this);var a=jQuery("<select/>",{"class":"form-control input-sm"});a.change(this.parent_widget.focus_out.bind(this.parent_widget)),this.input.replaceWith(a),this.input=this.labelled=a;var b=jQuery.extend([],this.definition.selection);b.splice(0,0,[null,""]),b.forEach(function(b){a.append(jQuery("<option/>",{value:JSON.stringify(b[0]),text:b[1]}))})},get_value:function(){return JSON.parse(this.input.val())},set_value:function(a){this.input.val(JSON.stringify(a))}}),Sao.View.Form.Dict.Integer=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-integer",get_value:function(){var a=parseInt(this.input.val(),10);return isNaN(a)?null:a}}),Sao.View.Form.Dict.Float=Sao.class_(Sao.View.Form.Dict.Integer,{class_:"dict-float",get_value:function(){var a=Number(this.input.val());return isNaN(a)?null:a}}),Sao.View.Form.Dict.Numeric=Sao.class_(Sao.View.Form.Dict.Float,{class_:"dict-numeric",get_value:function(){var a=new Sao.Decimal(this.input.val());return isNaN(a.valueOf())?null:a}}),Sao.View.Form.Dict.Date=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-date",format:"%x",create_widget:function(){Sao.View.Form.Dict.Date._super.create_widget.call(this);var a=this.button.parent();jQuery("<button/>",{"class":"datepickerbutton btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"})).prependTo(a),this.input.datetimepicker({format:Sao.common.moment_format(this.format)}),this.input.on("dp.change",this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){var a=this.input.data("DateTimePicker").date();return a&&(a.isDate=!0),a},set_value:function(a){this.date.data("DateTimePicker").date(a)}}),Sao.View.Form.Dict.DateTime=Sao.class_(Sao.View.Form.Dict.Date,{class_:"dict-datetime",format:"%x %X",get_value:function(){var a=this.input.data("DateTimePicker").date();return a&&(a.isDateTime=!0),a}})}(),function(){"use strict";Sao.View.tree_column_get=function(a){switch(a){case"char":case"text":case"binary":return Sao.View.Tree.CharColumn;case"many2one":return Sao.View.Tree.Many2OneColumn;case"one2one":return Sao.View.Tree.One2OneColumn;case"date":return Sao.View.Tree.DateColumn;case"time":return Sao.View.Tree.TimeColumn;case"timedelta":return Sao.View.Tree.TimeDeltaColumn;case"one2many":return Sao.View.Tree.One2ManyColumn;case"many2many":return Sao.View.Tree.Many2ManyColumn;case"selection":return Sao.View.Tree.SelectionColumn;case"reference":return Sao.View.Tree.ReferenceColumn;case"float":case"numeric":return Sao.View.Tree.FloatColumn;case"integer":case"biginteger":return Sao.View.Tree.IntegerColumn;case"boolean":return Sao.View.Tree.BooleanColumn;case"image":return Sao.View.Tree.ImageColumn;case"url":case"email":case"callto":case"sip":return Sao.View.Tree.URLColumn;case"progressbar":return Sao.View.Tree.ProgressBar}},Sao.View.Tree=Sao.class_(Sao.View,{init:function(a,b,c){Sao.View.Tree._super.init.call(this,a,b),this.view_type="tree",this.selection_mode=a.attributes.selection_mode||Sao.common.SELECTION_SINGLE,this.el=jQuery("<div/>",{"class":"treeview responsive"}),this.expanded={},this.children_field=c,this.editable=Boolean(this.attributes.editable),this.columns=[],this.create_columns(a.model,b),this.rows=[],this.table=jQuery("<table/>",{"class":"tree table table-hover table-striped"}),this.columns.filter(function(a){return!a.attributes.tree_invisible}).length>1&&(this.table.addClass("responsive"),this.table.addClass("responsive-header")),this.el.append(this.table);var d=jQuery("<thead/>");this.table.append(d);var e=jQuery("<tr/>");if(this.selection_mode!=Sao.common.SELECTION_NONE){var f=jQuery("<th/>",{"class":"selection"});this.selection=jQuery("<input/>",{type:"checkbox","class":"selection"}),this.selection.change(this.selection_changed.bind(this)),f.append(this.selection),e.append(f)}d.append(e),this.columns.forEach(function(a){f=jQuery("<th/>");var b=jQuery("<label/>").text(a.attributes.string);this.editable&&(a.attributes.required&&b.addClass("required"),a.attributes.readonly||b.addClass("editable")),e.append(f.append(b)),a.header=f},this),this.tbody=jQuery("<tbody/>"),this.table.append(this.tbody);var g=jQuery("<div/>",{"class":"treefooter"});this.more=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("More")).click(function(){this.display_size+=Sao.config.display_size,this.display()}.bind(this)),g.append(this.more),this.more.hide(),this.display_size=Sao.config.display_size,this.el.append(g)},create_columns:function(a,b){b.find("tree").children().each(function(b,c){for(var d,e,f={},g=0,h=c.attributes.length;h>g;g++)e=c.attributes[g],f[e.name]=e.value;if(["readonly","tree_invisible","expand","completion"].forEach(function(a){f[a]&&(f[a]=1==f[a])}),"field"==c.tagName){var i=f.name;f.widget||(f.widget=a.fields[i].description.type);var j=["relation","domain","selection","relation_field","string","views","invisible","add_remove","sort","context","filename","autocomplete","translate","create","delete","selection_change_with","schema_model","required","readonly"];for(g in j){var k=j[g];k in a.fields[i].description&&null===c.getAttribute(k)&&(f[k]=a.fields[i].description[k])}var l=Sao.View.tree_column_get(f.widget);d=new l(a,f);~["url","email","callto","sip"].indexOf(f.widget)&&d.prefixes.push(new Sao.View.Tree.Affix(f,f.widget)),"icon"in f&&d.prefixes.push(new Sao.View.Tree.Affix(f));var m,n,o=c.childNodes;for(g=0;g<o.length;g++){for(m=o[g],n={},g=0,h=m.attributes.length;h>g;g++)e=m.attributes[g],n[e.name]=e.value;n.name||(n.name=i),"prefix"==m.tagName?d.prefixes.push(new Sao.View.Tree.Affix(n)):d.suffixes.push(new Sao.View.Tree.Affix(n))}this.fields[i]=!0}else"button"==c.tagName&&(d=new Sao.View.Tree.ButtonColumn(this.screen,f));this.columns.push(d)}.bind(this))},get_buttons:function(){var a=[];return this.columns.forEach(function(b){b instanceof Sao.View.Tree.ButtonColumn&&a.push(b)}),a},display:function(a,b){var c=this.screen.current_record;if(!a)if(a=this.get_selected_paths(),c){var d=c.get_path(this.screen.group);d=d.map(function(a){return a[1]}),Sao.common.contains(a,d)||(a=[d])}else c||(a=[]);b=b||[],(this.screen.group.length!=this.rows.length||!Sao.common.compare(this.screen.group,this.rows.map(function(a){return a.record}))||this.children_field)&&this.construct(a,b);var e=[];jQuery.isEmptyObject(this.screen.domain)||e.push(this.screen.domain);var f=this.screen.screen_container.get_tab_domain();jQuery.isEmptyObject(f)||e.push(f);var g=new Sao.common.DomainInversion;return e=g.simplify(e),this.columns.forEach(function(a){var b=a.attributes.name;if(b)if(a.attributes.tree_invisible)a.header.hide();else if(b===this.screen.exclude_field)a.header.hide();else{var c=g.domain_inversion(e,b);"boolean"!=typeof c&&(c=g.simplify(c));var d=g.unique_value(c)[0];d&&jQuery.isEmptyObject(this.children_field)?a.header.hide():a.header.show()}}.bind(this)),this.redraw(a,b),jQuery.when()},construct:function(a,b){this.rows=[],this.tbody.empty();var c=function(c,d,e){var f;f=this.editable?Sao.View.Tree.RowEditable:Sao.View.Tree.Row;var g=new f(this,c,d);this.rows.push(g),g.construct(a,b)};this.screen.group.slice(0,this.display_size).forEach(c.bind(this)),this.display_size>=this.screen.group.length?this.more.hide():this.more.show()},redraw:function(a,b){var c=function(c,d,e){this.rows[d].redraw(a,b)};this.screen.group.slice(0,this.display_size).forEach(c.bind(this))},switch_:function(a){this.screen.row_activate()},select_changed:function(a){var b=this.screen.current_record;if(this.screen.set_current_record(a),this.editable&&b){var c=function(){this.screen.set_current_record(b),this.set_cursor()}.bind(this);this.screen.group.parent||b===a?b!==a&&this.screen.attributes.pre_validate&&b.pre_validate().then(function(a){a||c()}):b.validate(this.get_fields()).then(function(a){a?b.save().fail(c):c()})}},selected_records:function(){if(this.selection_mode==Sao.common.SELECTION_NONE)return[];var a=[],b=function(c){c.is_selected()&&a.push(c.record),c.rows.forEach(b)};return this.rows.forEach(b),this.selection.prop("checked")&&!this.selection.prop("indeterminate")&&this.screen.group.slice(this.rows.length).forEach(function(b){a.push(b)}),a},selection_changed:function(){var a=this.selection.prop("checked"),b=function(c){c.set_selection(a),c.rows.forEach(b)};this.rows.forEach(b),this.select_changed(a&&this.rows[0]?this.rows[0].record:null)},update_selection:function(){if(!this.selection.prop("checked")){var a=this.selected_records();this.selection.prop("indeterminate",!1),jQuery.isEmptyObject(a)?this.selection.prop("checked",!1):a.length==this.tbody.children().length&&this.display_size>=this.screen.group.length?this.selection.prop("checked",!0):(this.selection.prop("indeterminate",!0),this.selection.prop("checked",!0))}},get_selected_paths:function(){function a(c,d){var e,f,g,h;for(e=0,g=c.rows.length;g>e;e++)f=c.rows[e],h=d.concat([f.record.id]),f.is_selected()&&b.push(h),a(f,h)}var b=[];return a(this,[]),b},get_expanded_paths:function(a,b){var c,d,e,f,g;void 0===a&&(a=[]),void 0===b&&(b=[]),d=[],e=this.find_row(a),f=e?e.rows:this.rows;for(var h=0,i=this.n_children(e);i>h;h++)g=a.concat([h]),e=f[h],e&&e.is_expanded()&&(c=b.concat(e.record.id),d.push(c),d=d.concat(this.get_expanded_paths(g,c)));return d},find_row:function(a){for(var b,c=null,d=this.rows,e=0,f=a.length;f>e;e++){if(b=a[e],!d||b>=d.length)return null;if(c=d[b],d=c.rows,!this.children_field)break}return c},n_children:function(a){return a&&this.children_field?a.record._values[this.children_field].length:this.rows.length},set_cursor:function(a,b){var c,d,e,f,g,h,i,j;if(this.screen.current_record){for(d=null,c=0;c<this.rows.length;c++)if(e=this.rows[c].record_to_path(this.screen.current_record)){e.unshift(c),d=e;break}f=null,d?(h=d[0],i=d.slice(1),i.length>0&&this.rows[h].expand_to_path(i),f=this.find_row(d)):this.rows.length>0&&(f=this.rows[0]),f&&(g=f.next_column(null,a),j=f._get_column_td(g),this.editable?(j.triggerHandler("click"),a?j.triggerHandler("click"):j.find(":input,[tabindex=0]").focus()):j.find(":input,[tabindex=0]").focus())}}}),Sao.View.Tree.Row=Sao.class_(Object,{init:function(a,b,c,d){this.tree=a,this.rows=[],this.record=b,this.parent_=d,this.children_field=a.children_field,this.expander=null;var e=[];d&&(e=jQuery.extend([],d.path.split("."))),e.push(c),this.path=e.join("."),this.el=jQuery("<tr/>")},is_expanded:function(){return this.path in this.tree.expanded},get_last_child:function(){return this.children_field&&this.is_expanded()&&!jQuery.isEmptyObject(this.rows)?this.rows[this.rows.length-1].get_last_child():this},get_id_path:function(){return this.parent_?this.parent_.get_id_path().concat([this.record.id]):[this.record.id]},build_widgets:function(){var a=jQuery("<table/>");a.css("width","100%");var b=jQuery("<tr/>");return a.append(b),[a,b]},construct:function(a,b){a=a||[],b=b||[];for(var c=this.el[0];c.firstChild;)c.removeChild(c.firstChild);var d;this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(d=jQuery("<td/>"),this.el.append(d),this.selection=jQuery("<input/>",{type:"checkbox","class":"selection"}),this.selection.change(this.selection_changed.bind(this)),d.append(this.selection));for(var e=this.path.split(".").length,f=0;f<this.tree.columns.length;f++){var g=this.tree.columns[f];d=jQuery("<td/>",{"data-title":g.attributes.string}).append(jQuery("<div/>",{"aria-hidden":!0})),d.css("overflow","hidden"),d.on("click keypress",{column:f,td:d},Sao.common.click_press(this.select_row.bind(this),!0)),this.tree.editable||d.dblclick(this.switch_row.bind(this));var h=this.build_widgets(),i=h[0],j=h[1];if(d.append(i),0===f&&this.children_field){var k="glyphicon-plus";(this.is_expanded()||~b.indexOf(this.record.id))&&(k="glyphicon-minus"),this.expander=jQuery("<span/>",{"class":"glyphicon "+k,tabindex:0}),this.expander.html(" "),this.expander.css("margin-left",e-1+"em"),this.expander.css("float","left"),this.expander.on("click keypress",Sao.common.click_press(this.toggle_row.bind(this))),j.append(jQuery("<td/>",{"class":"expander"}).append(this.expander).css("width",1))}var l;if(g.prefixes)for(l=0;l<g.prefixes.length;l++){g.prefixes[l];j.append(jQuery("<td/>",{"class":"prefix"}).css("width",1))}if(j.append(jQuery("<td/>",{"class":"widget"})),g.suffixes)for(l=0;l<g.suffixes.length;l++){g.suffixes[l];j.append(jQuery("<td/>",{"class":"suffix"}).css("width",1))}this.el.append(d)}if(this.parent_){var m=this.parent_.get_last_child();m.el.after(this.el)}else this.tree.tbody.append(this.el);var n=this.get_id_path();(this.is_expanded()||Sao.common.contains(b,n))&&(this.tree.expanded[this.path]=this,this.expand_children(a,b))},_get_column_td:function(a,b){b=b||this.el;var c=0;return this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(c+=1),jQuery(b.children()[a+c])},redraw:function(a,b){a=a||[],b=b||[];for(var c=function(){jQuery.isEmptyObject(this.record.field_get(this.children_field))&&this.expander.css("visibility","hidden")},d=0;d<this.tree.columns.length;d++){0===d&&this.children_field&&this.record.load(this.children_field).done(c.bind(this));var e=this.tree.columns[d],f=this._get_column_td(d),g=f.find("tr");if(e.prefixes)for(var h=0;h<e.prefixes.length;h++){var i=e.prefixes[h];jQuery(g.children(".prefix")[h]).html(i.render(this.record))}if(jQuery(g.children(".widget")).html(e.render(this.record)),e.suffixes)for(var j=0;j<e.suffixes.length;j++){var k=e.suffixes[j];jQuery(g.children(".suffix")[j]).html(k.render(this.record))}e.attributes.tree_invisible||"none"==e.header.css("display")?f.hide():f.show()}var l=this.get_id_path();if(this.set_selection(Sao.common.contains(a,l)),this.is_expanded()||Sao.common.contains(b,l)){if(this.tree.expanded[this.path]=this,!this.record._values[this.children_field]||this.record._values[this.children_field].length>0&&0===this.rows.length)this.expand_children(a,b);else{var m;for(d=0;d<this.rows.length;d++)m=this.rows[d],m.redraw(a,b)}this.expander&&this.update_expander(!0)}else this.expander&&this.update_expander(!1);this.record.deleted()||this.record.removed()?this.el.css("text-decoration","line-through"):this.el.css("text-decoration","inherit")},toggle_row:function(){return this.is_expanded()?(this.update_expander(!1),delete this.tree.expanded[this.path],this.collapse_children()):(this.update_expander(!0),this.tree.expanded[this.path]=this,this.expand_children()),!1},update_expander:function(a){a?(this.expander.removeClass("glyphicon-plus"),this.expander.addClass("glyphicon-minus")):(this.expander.removeClass("glyphicon-minus"),this.expander.addClass("glyphicon-plus"))},collapse_children:function(){this.rows.forEach(function(a,b,c){a.collapse_children();var d=a.el[0];d.parentNode.removeChild(d)}),this.rows=[]},expand_children:function(a,b){var c=function(){if(jQuery.isEmptyObject(this.rows)){var c=function(c,d,e){var f=new this.Class(this.tree,c,d,this);f.construct(a,b),f.redraw(a,b),this.rows.push(f)},d=this.record.field_get_client(this.children_field);d.forEach(c.bind(this))}};return this.record.load(this.children_field).done(c.bind(this))},switch_row:function(){window.getSelection?window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges():document.selection&&document.selection.empty(),(this.tree.selection_mode==Sao.common.SELECTION_NONE||(this.set_selection(!0),this.selection_changed(),this.is_selected()))&&this.tree.switch_(this.path)},select_row:function(a){this.tree.selection_mode==Sao.common.SELECTION_NONE?(this.tree.select_changed(this.record),this.switch_row()):(a.ctrlKey||this.tree.selection_mode!=Sao.common.SELECTION_SINGLE||this.tree.rows.forEach(function(a){a.set_selection(!1)}.bind(this)),this.set_selection(!this.is_selected()),this.selection_changed());var b=a.data.td,c=a.data.column;b.on("click keypress",{column:c,td:b},Sao.common.click_press(this.select_row.bind(this),!0))},is_selected:function(){return this.tree.selection_mode==Sao.common.SELECTION_NONE?!1:this.selection.prop("checked")},set_selection:function(a){this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(this.selection.prop("checked",a),a||this.tree.selection.prop("checked",!1))},selection_changed:function(){var a=this.is_selected();this.set_selection(a),this.tree.select_changed(a?this.record:this.tree.selected_records()[0]||null),this.tree.update_selection()},record_to_path:function(a){var b,c;if(a==this.record)return[];for(b=0;b<this.rows.length;b++)if(c=this.rows[b].record_to_path(a))return c.unshift(b),c},expand_to_path:function(a){var b,c;b=a[0],c=a.slice(1),c.length>0&&this.rows[b].expand_children().done(function(){this.rows[b].expand_to_path(c)}.bind(this))},next_column:function(a,b,c){var d,e,f,g;for(c=c||1,null===a&&c>0?a=-1:null===a&&(a=0),f=0,d=0;d<this.tree.columns.length&&(f=(a+c*(d+1))%this.tree.columns.length,0>f&&(f+=this.tree.columns.length),a=this.tree.columns[f],g=a.field.get_state_attrs(this.record),e=b?a.attributes.readonly||g.readonly:!1,g.invisible||e);d++);return f}}),Sao.View.Tree.RowEditable=Sao.class_(Sao.View.Tree.Row,{init:function(a,b,c,d){Sao.View.Tree.RowEditable._super.init.call(this,a,b,c,d),this.edited_column=null},redraw:function(a,b){var c,d,e,f,g;for(Sao.View.Tree.RowEditable._super.redraw.call(this,a,b),c=0;c<this.tree.columns.length;c++)e=this._get_column_td(c),d=e.find("tr"),f=jQuery(d.children(".widget-editable")).data("widget"),f&&(g=this.record.model.fields[f.field_name],f.display(this.record,g))},select_row:function(a){function b(a,d){var e,f;for(e=0;e<a.length;e++)f=a[e],f.is_selected()&&(c=f),f!=d&&f.set_selection(!1),b(f.rows,d)}var c,d,e,f,g;b(this.tree.rows,this),this.selection_changed();var h;h=c&&c!=this&&!this.tree.screen.group.parent?c.record.save():jQuery.when(),h.done(function(){if(c&&null!==c.edited_column){d=c.get_active_td(),d.on("click keypress",{td:d,column:c.edited_column},Sao.common.click_press(c.select_row.bind(c),!0));var b=this.tree.columns[c.edited_column];c.get_static_el().html(b.render(c.record)).show(),c.empty_editable_el()}if(this.is_selected()){this.edited_column=a.data.column,e=this.get_active_td();var h=this.tree.columns[this.edited_column].attributes,i=Sao.View.editabletree_widget_get(h.widget);g=new i(h.name,this.tree.screen.model,h),g.view=this.tree,g.el.on("focusin",function(){jQuery(this).parents(".treeview td").css("overflow","visible")}),g.el.on("focusout",function(){jQuery(this).parents(".treeview td").css("overflow","hidden")});var j=this.get_editable_el();j.append(g.el),j.data("widget",g),g.el.on("keydown",this.key_press.bind(this)),f=this.record.model.fields[g.field_name],g.display(this.record,f),this.get_static_el().hide(),this.get_editable_el().show(),g.focus()}else{this.set_selection(!0),this.selection_changed();var k=a.data.td,l=a.data.column;k.on("click keypress",{column:l,td:k},Sao.common.click_press(this.select_row.bind(this),!0))}}.bind(this))},get_static_el:function(){var a=this.get_active_td();return a.find(".widget")},get_editable_el:function(){var a=this.get_active_td(),b=a.find(".widget-editable");return b.length||(b=jQuery("<td/>",{"class":"widget-editable"}).insertAfter(a.find(".widget"))),b},empty_editable_el:function(){var a;a=this.get_editable_el(),a.empty(),a.data("widget",null),this.edited_column=null},get_active_td:function(){return this._get_column_td(this.edited_column)},key_press:function(a){var b,c,d,e,f;if(a.which==Sao.common.TAB_KEYCODE||a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE||a.which==Sao.common.ESC_KEYCODE||a.which==Sao.common.RETURN_KEYCODE){var g=this.tree.columns[this.edited_column];if(g.field.validate(this.record))if(a.which==Sao.common.TAB_KEYCODE){var h=1;a.shiftKey&&(h=-1),a.preventDefault(),d=this.next_column(this.edited_column,!0,h),window.setTimeout(function(){var a=this._get_column_td(d);a.triggerHandler("click",{column:d,td:a})}.bind(this),0)}else if(a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE)f=a.which==Sao.common.UP_KEYCODE?this.el.prev("tr"):this.el.next("tr"),c=this.edited_column,this.record.validate(this.tree.get_fields()).then(function(a){if(a){if(this.tree.screen.attributes.pre_validate)return this.record.pre_validate().fail(function(){f=null});if(!this.tree.screen.model.parent)return this.record.save().fail(function(){f=null})}else{f=null;var b=this.record.invalid_fields();for(e=0;e<this.tree.columns.length;e++){var d=this.tree.columns[e];d.attributes.name in b&&(c=e)}}}.bind(this)).then(function(){window.setTimeout(function(){this._get_column_td(c,f).trigger("click").trigger("click")}.bind(this),0)}.bind(this));else if(a.which==Sao.common.ESC_KEYCODE)this.get_static_el().show(),b=this.get_active_td(),b.on("click keypress",{column:this.edited_column,td:b},Sao.common.click_press(this.select_row.bind(this),!0)),this.empty_editable_el();else if(a.which==Sao.common.RETURN_KEYCODE){var i=function(a){var b=this._get_column_td(this.edited_column,a);b.triggerHandler("click"),b.triggerHandler("click")}.bind(this);f="bottom"==this.tree.attributes.editable?this.el.next("tr"):this.el.prev("tr"),f.length?i(f):this.tree.screen.new_().done(function(){var a,b=this.tree.tbody.children("tr");a="bottom"==this.tree.attributes.editable?b.last():b.first(),i(a)}.bind(this))}}}}),Sao.View.Tree.Affix=Sao.class_(Object,{init:function(a,b){this.attributes=a,this.protocol=b||null,this.icon=a.icon,this.protocol&&!this.icon&&(this.icon="tryton-web-browser")},get_cell:function(){var a;return this.protocol?(a=jQuery("<a/>",{target:"_new"}),a.append(jQuery("<img/>")),a.click({cell:a},this.clicked.bind(this))):this.icon?a=jQuery("<img/>"):(a=jQuery("<span/>"),
-a.attr("tabindex",0)),a.addClass("column-affix"),a},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){var c,d,e=a.model.fields[this.attributes.name],f=e.get_state_attrs(a).invisible;if(f?b.hide():b.show(),this.protocol){if(c=e.get(a),!jQuery.isEmptyObject(c))switch(this.protocol){case"email":c="mailto:"+c;break;case"callto":c="callto:"+c;break;case"sip":c="sip:"+c}b.attr("src",c)}if(this.icon){if(this.icon in a.model.fields){var g=a.model.fields[this.icon];c=g.get_client(a)}else c=this.icon;d=Sao.common.ICONFACTORY.register_icon(c),d.done(function(a){var c;c=b.children("img").length?b.children("img"):b,c.attr("src",a)}.bind(this))}else c=this.attributes.string||"",c||(c=e.get_client(a)||""),b.text(c)}.bind(this)),b},clicked:function(a){a.preventDefault(),window.open(a.data.cell.attr("src"),"_blank")}}),Sao.View.Tree.CharColumn=Sao.class_(Object,{class_:"column-char",init:function(a,b){this.type="field",this.model=a,this.field=a.fields[b.name],this.attributes=b,this.prefixes=[],this.suffixes=[],this.header=null},get_cell:function(){var a=jQuery("<div/>",{"class":this.class_,tabindex:0});return a},update_text:function(a,b){a.text(this.field.get_client(b))},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){this.update_text(b,a),this.field.set_state(a);var c=this.field.get_state_attrs(a);c.invisible?b.hide():b.show()}.bind(this)),b}}),Sao.View.Tree.IntegerColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-integer",init:function(a,b){Sao.View.Tree.IntegerColumn._super.init.call(this,a,b),this.factor=Number(b.factor||1)},get_cell:function(){return Sao.View.Tree.IntegerColumn._super.get_cell.call(this)},update_text:function(a,b){a.text(this.field.get_client(b,this.factor))}}),Sao.View.Tree.FloatColumn=Sao.class_(Sao.View.Tree.IntegerColumn,{class_:"column-float"}),Sao.View.Tree.BooleanColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-boolean",get_cell:function(){return jQuery("<input/>",{type:"checkbox",disabled:!0,"class":this.class_,tabindex:0})},update_text:function(a,b){a.prop("checked",this.field.get(b))}}),Sao.View.Tree.Many2OneColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-many2one"}),Sao.View.Tree.One2OneColumn=Sao.class_(Sao.View.Tree.Many2OneColumn,{class_:"column-one2one"}),Sao.View.Tree.SelectionColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-selection",init:function(a,b){Sao.View.Tree.SelectionColumn._super.init.call(this,a,b),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a)},update_selection:function(a,b){Sao.common.selection_mixin.update_selection.call(this,a,this.field,b)},update_text:function(a,b){this.update_selection(b,function(){for(var c,d,e=this.field.get(b),f=!1,g=0,h=this.selection.length;h>g;g++)if(this.selection[g][0]===e){f=!0,d=this.selection[g][1];break}c=f?jQuery.when(d):Sao.common.selection_mixin.get_inactive_selection.call(this,e).then(function(a){return a[1]}),c.done(function(b){a.text(b)}.bind(this))}.bind(this))}}),Sao.View.Tree.ReferenceColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-reference",init:function(a,b){Sao.View.Tree.ReferenceColumn._super.init.call(this,a,b),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a)},update_selection:function(a,b){Sao.common.selection_mixin.update_selection.call(this,a,this.field,b)},update_text:function(a,b){this.update_selection(b,function(){var c,d,e=this.field.get_client(b);e?(c=e[0],d=e[1]):(c="",d=""),a.text(c?this.selection[c]||c+","+d:d)}.bind(this))}}),Sao.View.Tree.DateColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-date",update_text:function(a,b){var c=this.field.get_client(b),d=this.field.date_format(b);a.text(Sao.common.format_date(d,c))}}),Sao.View.Tree.TimeColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-time",update_text:function(a,b){var c=this.field.get_client(b);a.text(Sao.common.format_time(this.field.time_format(b),c))}}),Sao.View.Tree.TimeDeltaColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-timedelta"}),Sao.View.Tree.One2ManyColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-one2many",update_text:function(a,b){a.text("( "+this.field.get_client(b).length+" )")}}),Sao.View.Tree.Many2ManyColumn=Sao.class_(Sao.View.Tree.One2ManyColumn,{class_:"column-many2many"}),Sao.View.Tree.ImageColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-image",get_cell:function(){var a=jQuery("<img/>",{"class":this.class_,tabindex:0});return a.css("width","100%"),a},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){var c=this.field.get_client(a);c=c?c>Sao.common.BIG_IMAGE_SIZE?jQuery.when(null):this.field.get_data(a):jQuery.when(null),c.done(function(a){var c,d;a?(d=new Blob([a[0][this.field.name]]),c=window.URL.createObjectURL(d)):c=null,b.attr("src",c)}.bind(this))}.bind(this)),b}}),Sao.View.Tree.URLColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-url",render:function(a){var b=Sao.View.Tree.URLColumn._super.render.call(this,a);this.field.set_state(a);var c=this.field.get_state_attrs(a);c.readonly?b.hide():b.show()}}),Sao.View.Tree.ProgressBar=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-progressbar",get_cell:function(){var a=jQuery("<div/>",{"class":this.class_+" progress",tabindex:0}),b=jQuery("<div/>",{"class":"progress-bar",role:"progressbar","aria-valuemin":0,"aria-valuemax":100}).appendTo(a);return b.css("min-width: 2em"),a},update_text:function(a,b){var c=this.field.get_client(b,100);c&&(c=Sao.i18n.gettext("%1%",c));var d=this.field.get(b)||0,e=a.find(".progress-bar");e.attr("aria-valuenow",100*d),e.css("width",100*d+"%"),e.text(c)}}),Sao.View.Tree.ButtonColumn=Sao.class_(Object,{init:function(a,b){this.screen=a,this.type="button",this.attributes=b},render:function(a){var b=new Sao.common.Button(this.attributes);b.el.click(a,this.button_clicked.bind(this));var c=jQuery.map(this.screen.model.fields,function(a,b){return"eager"==(a.description.loading||"eager")?b:void 0});return a.load(c[0]).done(function(){b.set_state(a)}),b.el},button_clicked:function(a){var b=a.data;if(b==this.screen.current_record){var c=b.expr_eval(this.attributes.states||{});c.invisible||c.readonly||this.screen.button(this.attributes)}}}),Sao.View.editabletree_widget_get=function(a){switch(a){case"char":case"text":case"url":case"email":case"callto":case"sip":return Sao.View.EditableTree.Char;case"date":return Sao.View.EditableTree.Date;case"time":return Sao.View.EditableTree.Time;case"timedelta":return Sao.View.EditableTree.TimeDelta;case"integer":case"biginteger":return Sao.View.EditableTree.Integer;case"float":case"numeric":return Sao.View.EditableTree.Float;case"selection":return Sao.View.EditableTree.Selection;case"boolean":return Sao.View.EditableTree.Boolean;case"many2one":return Sao.View.EditableTree.Many2One;case"one2one":return Sao.View.EditableTree.One2One;case"one2many":case"many2many":return Sao.View.EditableTree.One2Many;case"binary":return Sao.View.EditableTree.Binary}},Sao.View.EditableTree={},Sao.View.EditableTree.editable_mixin=function(a){var b=function(a){(a.which==Sao.common.TAB_KEYCODE||a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE||a.which==Sao.common.ESC_KEYCODE||a.which==Sao.common.RETURN_KEYCODE)&&this.focus_out()};a.el.on("keydown",b.bind(a))},Sao.View.EditableTree.Char=Sao.class_(Sao.View.Form.Char,{class_:"editabletree-char",init:function(a,b,c){Sao.View.EditableTree.Char._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Date=Sao.class_(Sao.View.Form.Date,{class_:"editabletree-date",init:function(a,b,c){Sao.View.EditableTree.Date._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Time=Sao.class_(Sao.View.Form.Time,{class_:"editabletree-time",init:function(a,b,c){Sao.View.EditableTree.Time._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.TimeDelta=Sao.class_(Sao.View.Form.TimeDelta,{class_:"editabletree-timedelta",init:function(a,b,c){Sao.View.EditableTree.TimeDelta._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Integer=Sao.class_(Sao.View.Form.Integer,{class_:"editabletree-integer",init:function(a,b,c){Sao.View.EditableTree.Integer._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Float=Sao.class_(Sao.View.Form.Float,{class_:"editabletree-float",init:function(a,b,c){Sao.View.EditableTree.Float._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Selection=Sao.class_(Sao.View.Form.Selection,{class_:"editabletree-selection",init:function(a,b,c){Sao.View.EditableTree.Selection._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Boolean=Sao.class_(Sao.View.Form.Boolean,{class_:"editabletree-boolean",init:function(a,b,c){Sao.View.EditableTree.Boolean._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Many2One=Sao.class_(Sao.View.Form.Many2One,{class_:"editabletree-many2one",init:function(a,b,c){Sao.View.EditableTree.Many2One._super.init.call(this,a,b,c),this.el.on("keydown",this.key_press.bind(this))},key_press:function(a){a.which==Sao.common.TAB_KEYCODE?this.focus_out():Sao.View.EditableTree.Many2One._super.key_press.call(this,a)}}),Sao.View.EditableTree.One2One=Sao.class_(Sao.View.Form.One2One,{class_:"editabletree-one2one",init:function(a,b,c){Sao.View.EditableTree.One2One._super.init.call(this,a,b,c),this.el.on("keydown",this.key_press.bind(this))},key_press:function(a){a.which==Sao.common.TAB_KEYCODE?this.focus_out():Sao.View.EditableTree.One2One._super.key_press.call(this,a)}}),Sao.View.EditableTree.One2Many=Sao.class_(Sao.View.EditableTree.Char,{class_:"editabletree-one2many",init:function(a,b,c){Sao.View.EditableTree.One2Many._super.init.call(this,a,b,c)},display:function(a,b){this.el.val(a?"("+b.get_client(a).length+")":"")},key_press:function(a){a.which==Sao.common.TAB_KEYCODE&&this.focus_out()},set_value:function(a,b){}}),Sao.View.EditableTree.Binary=Sao.class_(Sao.View.Form.Binary,{class_:"editabletree-binary",init:function(a,b,c){Sao.View.EditableTree.Binary._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}})}(),function(){"use strict";Sao.View.Graph=Sao.class_(Sao.View,{init:function(a,b){Sao.View.Graph._super.init.call(this,a,b),this.view_type="graph",this.el=jQuery("<div/>",{"class":"graph"}),this.widgets={},this.widget=this.parse(b.children()[0]),this.widgets.root=this.widget,this.el.append(this.widget.el)},parse:function(a){var b,c,d,e,f,g,h,i=null,j=[],k=function(a){for(var b,c={},d=0,e=a.attributes.length;e>d;d++)b=a.attributes[d],c[b.name]=b.value;return c};for(e=0,f=a.children.length;f>e;e++)switch(d=a.children[e],d.tagName){case"x":for(g=0,h=d.children.length;h>g;g++)i=k(d.children[g]),b=this.screen.model.fields[i.name],i.string||(i.string=b.description.string);break;case"y":for(g=0,h=d.children.length;h>g;g++)c=k(d.children[g]),c.string||"#"==c.name||(b=this.screen.model.fields[c.name],c.string=b.description.string),j.push(c)}var l;switch(this.attributes.type){case"hbar":l=Sao.View.Graph.HorizontalBar;break;case"line":l=Sao.View.Graph.Line;break;case"pie":l=Sao.View.Graph.Pie;break;default:l=Sao.View.Graph.VerticalBar}return new l(this,i,j)},display:function(){return this.widget.display(this.screen.group)}}),Sao.View.Graph.Chart=Sao.class_(Object,{_chart_type:void 0,init:function(a,b,c){this.view=a,this.xfield=b,this.yfields=c,this.el=jQuery("<div/>"),this.el.uniqueId()},update_data:function(a){var b,c,d,e,f,g,h,i={};this.ids={},i.columns=[["labels"]];var j={},k=[this.xfield.name];for(e=0,f=this.yfields.length;f>e;e++)c=this.yfields[e],i.columns.push([c.string]),j[c.key||c.name]=e+1,k.push(c.name);var l=[],m=function(e){return function(){b=a[e];var f=b.field_get(this.xfield.name);i.columns[0][e+1]=f,this._add_id(f,b.id);var k;for(g=0,h=this.yfields.length;h>g;g++){if(c=this.yfields[g],d=c.key||c.name,k=i.columns[j[d]],c.domain){var l=jQuery.extend({},Sao.session.current_session.context);l.context=l,l._user=Sao.session.current_session.user_id;for(var m in a.model.fields)l[m]=b.field_get(m);var n=new Sao.PYSON.Decoder(l);if(!n.decode(c.domain)){k[e+1]=0;continue}}if("#"==c.name)k[e+1]=1;else{var o=b.field_get(c.name);o&&o.isTimeDelta&&(o=o.asSeconds()),k[e+1]=o||0}}}.bind(this)}.bind(this),n=function(a){return function(b){l.push(a.load(b))}},o=[];for(e=0,f=a.length;f>e;e++){for(b=a[e],k.forEach(n(a[e])),g=0,h=i.columns.length;h>g;g++)i.columns[g].push(void 0);o.push(jQuery.when.apply(jQuery,l).then(m(e)))}return jQuery.when.apply(jQuery,o).then(function(){return i})},_add_id:function(a,b){var c=a.isDate||a.isDateTime?a._d:a;c in this.ids||(this.ids[c]=[]),this.ids[c].push(b)},display:function(a){var b=this.update_data(a);return b.done(function(a){c3.generate(this._c3_config(a))}.bind(this)),b},_c3_config:function(a){var b={};b.bindto="#"+this.el.attr("id"),b.data=a,b.data.type=this._chart_type,b.data.x="labels",b.data.onclick=this.action.bind(this);var c,d,e,f;for(c=0,d=a.columns.length;d>c;c++)if(f=a.columns[c],"labels"==f[0]){e=!0;break}if(e&&f.length>1&&f[1]&&(f[1].isDateTime||f[1].isDate)){var g,h,i;h=this.view.screen.context.date_format||"%x",i="%X",g=f[1].isDateTime?function(a){return Sao.common.format_datetime(h,i,moment(a))}:function(a){return Sao.common.format_date(h,moment(a))},b.axis={x:{type:"timeseries",tick:{format:g}}}}return b},action:function(a,b){var c=this.ids[this._action_key(a)],d=jQuery.extend({},this.view.screen.context);delete d.active_ids,delete d.active_id,Sao.Action.exec_keyword("graph_open",{model:this.view.screen.model_name,id:c[0],ids:c},d,!1)},_action_key:function(a){return a.x}}),Sao.View.Graph.VerticalBar=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"bar"}),Sao.View.Graph.HorizontalBar=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"bar",_c3_config:function(a){var b=Sao.View.Graph.HorizontalBar._super._c3_config.call(this,a);b.axis.rotated=!0}}),Sao.View.Graph.Line=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"line"}),Sao.View.Graph.Pie=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"pie",_c3_config:function(a){var b,c,d,e,f=Sao.View.Graph.Pie._super._c3_config.call(this,a),g=[];for(b=0,c=a.columns.length;c>b;b++)"labels"==a.columns[b][0]?d=a.columns[b].slice(1):e=a.columns[b].slice(1);delete f.axis,delete f.data.x;var h,i,j;d.length>0&&(d[0].isDateTime||d[0].isDate)&&(i=this.view.screen.context.date_format||"%x",j=i+" %X",h=d[1].isDateTime?function(a){return Sao.common.format_datetime(j,a)}:function(a){return Sao.common.format_date(i,a)});var k;for(b=0,c=d.length;c>b;b++)k=d[b],h&&(k=h(k)),g.push([k,e[b]]);return f.data.columns=g,f},_add_id:function(a,b){var c=a;if(a.isDateTime||a.isDate){var d=this.view.screen.context.date_format||"%x",e=d+" %X";c=a.isDateTime?Sao.common.format_datetime(e,a):Sao.common.format_date(d,a)}c in this.ids||(this.ids[c]=[]),this.ids[c].push(b)},_action_key:function(a){return a.id}})}(),function(){"use strict";Sao.View.Calendar=Sao.class_(Sao.View,{init:function(a,b){Sao.View.Graph._super.init.call(this,a,b),this.view_type="calendar",this.el=jQuery("<div/>",{"class":"calendar"}),Sao.common.warning.run(Sao.i18n.gettext("Calendar view not yet implemented"),Sao.i18n.gettext("Warning"))},display:function(){return jQuery.when()}})}(),function(){"use strict";Sao.Action={report_blob_url:void 0},Sao.Action.exec_action=function(a,b,c){c=void 0===c?{}:jQuery.extend({},c);var d=Sao.Session.current_session;"date_format"in c||d.context.locale&&d.context.locale.date&&(c.date_format=d.context.locale.date),b=void 0===b?{}:jQuery.extend({},b),b.action_id=a.id;var e={};switch(a.type){case"ir.action.act_window":e.view_ids=!1,e.mode=null,jQuery.isEmptyObject(a.views)?jQuery.isEmptyObject(a.view_id)||(e.view_ids=[a.view_id[0]]):(e.view_ids=[],e.mode=[],a.views.forEach(function(a){e.view_ids.push(a[0]),e.mode.push(a[1])})),void 0===a.pyson_domain&&(a.pyson_domain="[]");var f={active_model:b.model||null,active_id:b.id||null,active_ids:b.ids};f=jQuery.extend(f,d.context);var g=jQuery.extend({},f);g._user=d.user_id,e.context=new Sao.PYSON.Decoder(g).decode(a.pyson_context||"{}"),f=jQuery.extend(f,e.context),f=jQuery.extend(f,c),e.context=jQuery.extend(e.context,c);var h=jQuery.extend({},f);h.context=f,h._user=d.user_id,e.domain=new Sao.PYSON.Decoder(h).decode(a.pyson_domain);var i=jQuery.extend({},f);i.context=f,i._user=d.user_id,e.search_value=new Sao.PYSON.Decoder(i).decode(a.pyson_search_value||"[]");var j=jQuery.extend({},f);j.context=f,j._user=d.user_id;var k=new Sao.PYSON.Decoder(j);return e.tab_domain=[],a.domains.forEach(function(a,b){e.tab_domain.push([a[0],k.decode(a[1])])}),e.name=!1,a.window_name&&(e.name=a.name),e.model=a.res_model||b.res_model,e.res_id=a.res_id||b.res_id,e.limit=a.limit,e.icon=a["icon.rec_name"]||"",void Sao.Tab.create(e);case"ir.action.wizard":return e.action=a.wiz_name,e.data=b,e.name=a.name,e.context=c,e.window=a.window,void Sao.Wizard.create(e);case"ir.action.report":return e.name=a.report_name,e.data=b,e.direct_print=a.direct_print,e.email_print=a.email_print,e.email=a.email,e.context=c,void Sao.Action.exec_report(e);case"ir.action.url":return void window.open(a.url,"_blank")}},Sao.Action.exec_keyword=function(a,b,c,d,e){void 0===d&&(d=!0),void 0===e&&(e=!1);var f=b.id,g={method:"model.ir.action.keyword.get_keyword",params:[a,[b.model,f],{}]},h=Sao.rpc(g,Sao.Session.current_session),i=function(a){var f={};for(var g in a){var h=a[g];f[h.name.replace(/_/g,"")]=h}var i=Sao.common.selection(Sao.i18n.gettext("Select your action"),f,e);return i.then(function(a){Sao.Action.exec_action(a,b,c)},function(){jQuery.isEmptyObject(f)&&d&&alert(Sao.i18n.gettext("No action defined!"))})};return h.pipe(i)},Sao.Action.exec_report=function(a){a.context||(a.context={}),a.email||(a.email={});var b=jQuery.extend({},a.data),c=jQuery.extend({},Sao.Session.current_session.context);jQuery.extend(c,a.context),c.direct_print=a.direct_print,c.email_print=a.email_print,c.email=a.email;var d=Sao.rpc({method:"report."+a.name+".execute",params:[b.ids||[],b,c]},Sao.Session.current_session);d.done(function(a){var b=a[0],c=a[1],d=(a[2],a[3],new Blob([c],{type:Sao.common.guess_mimetype(b)})),e=window.URL.createObjectURL(d);Sao.Action.report_blob_url&&window.URL.revokeObjectURL(Sao.Action.report_blob_url),Sao.Action.report_blob_url=e,window.open(e)})},Sao.Action.execute=function(a,b,c,d){c?Sao.rpc({method:"model."+c+".search_read",params:[[["action","=",a]],0,1,null,null,d]},Sao.Session.current_session).done(function(a){Sao.Action.exec_action(a[0],b,d)}):Sao.rpc({method:"model.ir.action.read",params:[[a],["type"],d]},Sao.Session.current_session).done(function(c){Sao.Action.execute(a,b,c[0].type,d)})},Sao.Action.evaluate=function(a,b,c){a=jQuery.extend({},a);var d={};return"pyson_email"in a&&(d=c.expr_eval(a.pyson_email),jQuery.isEmptyObject(d)&&(d={})),"subject"in d||(d.subject=a.name.replace(/_/g,"")),a.email=d,a}}(),function(){"use strict";Sao.common={},Sao.common.BACKSPACE_KEYCODE=8,Sao.common.TAB_KEYCODE=9,Sao.common.RETURN_KEYCODE=13,Sao.common.ESC_KEYCODE=27,Sao.common.UP_KEYCODE=38,Sao.common.DOWN_KEYCODE=40,Sao.common.DELETE_KEYCODE=46,Sao.common.F2_KEYCODE=113,Sao.common.F3_KEYCODE=114,Sao.common.SELECTION_NONE=1,Sao.common.SELECTION_SINGLE=2,Sao.common.SELECTION_MULTIPLE=3,Sao.common.BIG_IMAGE_SIZE=Math.pow(10,6),Sao.common.compare=function(a,b){if(a.length!=b.length)return!1;for(var c=0;c<a.length;c++)if(a[c]instanceof Array&&b[c]instanceof Array){if(!Sao.common.compare(a[c],b[c]))return!1}else if(a[c]!=b[c])return!1;return!0},Sao.common.contains=function(a,b){for(var c=0;c<a.length;c++)if(Sao.common.compare(a[c],b))return!0;return!1},Sao.common.intersect=function(a,b){for(var c=0,d=0,e=[];c<a.length&&d<b.length;)a[c]<b[d]?c++:a[c]>b[d]?d++:(e.push(a[c]),c++,d++);return e},Sao.common.click_press=function(a,b){return function c(d){return"keypress"!=d.type||d.which==Sao.common.RETURN_KEYCODE?(b&&jQuery(this).off("click keypress",null,c),a(d)):void 0}},Sao.common.product=function(a,b){b=b||1;for(var c=[],d=0;b>d;)c=c.concat(a),d++;var e=[[]];return c.forEach(function(a){var b=[];e.forEach(function(c){a.forEach(function(a){b.push(c.concat([a]))})}),e=b}),e},Sao.common.selection=function(a,b,c){void 0===c&&(c=!1);var d=jQuery.Deferred();if(jQuery.isEmptyObject(b))return d.fail(),d;var e=Object.keys(b).sort();if(1==e.length&&!c){var f=e[0];return d.resolve(b[f]),d}var g=new Sao.Dialog(a||Sao.i18n.gettext("Your selection:"),"selection-dialog");return e.forEach(function(a,b){jQuery("<div/>",{"class":"checkbox"}).append(jQuery("<label/>").append(jQuery("<input/>",{type:"radio",name:"selection",value:b})).append(" "+a)).appendTo(g.body)}),g.body.find("input").first().prop("checked",!0),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){g.modal.modal("hide"),d.fail()}).appendTo(g.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){var a=g.body.find("input:checked").attr("value");g.modal.modal("hide"),d.resolve(b[e[a]])}).appendTo(g.footer),g.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),g.modal.modal("show"),d},Sao.common.moment_format=function(a){return a.replace("%a","ddd").replace("%A","dddd").replace("%w","d").replace("%d","DD").replace("%b","MMM").replace("%B","MMMM").replace("%m","MM").replace("%y","YY").replace("%Y","YYYY").replace("%H","HH").replace("%I","hh").replace("%p","A").replace("%M","mm").replace("%S","ss").replace("%f","SSS").replace("%z","ZZ").replace("%Z","zz").replace("%j","DDDD").replace("%U","ww").replace("%W","WW").replace("%c","llll").replace("%x","L").replace("%X","LTS").replace("%","%%")},Sao.common.date_format=function(a){if(jQuery.isEmptyObject(a)&&Sao.Session.current_session){var b=Sao.Session.current_session.context;b.locale&&b.locale.date&&(a=b.locale.date)}return a?Sao.common.moment_format(a):"%Y-%m-%d"},Sao.common.format_time=function(a,b){return b?b.format(Sao.common.moment_format(a)):""},Sao.common.parse_time=function(a,b){if(jQuery.isEmptyObject(b))return null;var c=function(c){var d=a.indexOf(c);if(~d){var e=parseInt(b.slice(d,d+c.length),10);if(!isNaN(e))return e}return 0};return Sao.Time(c("%H"),c("%M"),c("%S"),c("%f"))},Sao.common.format_date=function(a,b){return b?b.format(Sao.common.moment_format(a)):""},Sao.common.parse_date=function(a,b){var c=moment(b,Sao.common.moment_format(a));return c=c.isValid()?Sao.Date(c.year(),c.month(),c.date()):null},Sao.common.format_datetime=function(a,b,c){return c?c.format(Sao.common.moment_format(a+" "+b)):""},Sao.common.parse_datetime=function(a,b,c){var d=moment(c,Sao.common.moment_format(a+" "+b));return d=d.isValid()?Sao.DateTime(d.year(),d.month(),d.date(),d.hour(),d.minute(),d.second(),d.millisecond()):null},Sao.common.timedelta={},Sao.common.timedelta.DEFAULT_CONVERTER={s:1},Sao.common.timedelta.DEFAULT_CONVERTER.m=60*Sao.common.timedelta.DEFAULT_CONVERTER.s,Sao.common.timedelta.DEFAULT_CONVERTER.h=60*Sao.common.timedelta.DEFAULT_CONVERTER.m,Sao.common.timedelta.DEFAULT_CONVERTER.d=24*Sao.common.timedelta.DEFAULT_CONVERTER.h,Sao.common.timedelta.DEFAULT_CONVERTER.w=7*Sao.common.timedelta.DEFAULT_CONVERTER.d,Sao.common.timedelta.DEFAULT_CONVERTER.M=4*Sao.common.timedelta.DEFAULT_CONVERTER.w,Sao.common.timedelta.DEFAULT_CONVERTER.Y=365*Sao.common.timedelta.DEFAULT_CONVERTER.d,Sao.common.timedelta._get_separator=function(){return{Y:Sao.i18n.gettext("Y"),M:Sao.i18n.gettext("M"),w:Sao.i18n.gettext("w"),d:Sao.i18n.gettext("d"),h:Sao.i18n.gettext("h"),m:Sao.i18n.gettext("m"),s:Sao.i18n.gettext("s")}},Sao.common.timedelta.format=function(a,b){if(!a)return"";b||(b=Sao.common.timedelta.DEFAULT_CONVERTER);var c=[];a=a.asSeconds();var d="";0>a&&(d="-"),a=Math.abs(a),b=Object.keys(b).map(function(a){return[a,b[a]]}),b.sort(function(a,b){return b[1]-a[1]});for(var e,f,g=[],h=0;h<b.length;h++){e=b[h][0],f=b[h][1];var i=Math.floor(a/f);a-=i*f,g.push(i)}for(h=0;h<b.length-3;h++)e=b[h][0],f=g[h],f&&c.push(f+Sao.common.timedelta._get_separator()[e]);if(jQuery(g.slice(-3)).is(function(a,b){return b})||jQuery.isEmptyObject(c)){var j=g.slice(-3,-1);j=("00"+j[0]).slice(-2)+":"+("00"+j[1]).slice(-2),(g.slice(-1)[0]||a)&&(j+=":"+("00"+g.slice(-1)[0]).slice(-2)),c.push(j)}return c=d+c.reduce(function(a,b){return a?a+" "+b:b}),a&&(jQuery(g.slice(-3)).is(function(a,b){return b})||(c+=" "),c+=(""+a.toFixed(6)).slice(1)),c},Sao.common.timedelta.parse=function(a,b){if(!a)return null;b||(b=Sao.common.timedelta.DEFAULT_CONVERTER);var c,d=Sao.common.timedelta._get_separator();for(var e in d)c=d[e],a=a.replace(c,c+" ");for(var f,g=0,h=a.split(" "),i=0;i<h.length;i++){var j=h[i];if(j.contains(":"))for(var k=j.split(":"),l=[b.h,b.m,b.s],m=0;m<Math.min(k.length,l.length);m++){var n=k[m],o=l[m];f=Math.abs(Number(n))*o,isNaN(f)||(g+=f)}else{var p=!1;for(var q in d)if(c=d[q],j.endsWith(c)){j=j.slice(0,-c.length),f=Math.abs(parseInt(j,10))*b[q],isNaN(f)||(g+=f),p=!0;break}p||(f=Math.abs(Number(j)),isNaN(f)||(g+=f))}}return a.contains("-")&&(g*=-1),Sao.TimeDelta(null,g)},Sao.common.ModelAccess=Sao.class_(Object,{init:function(){this.batchnum=100,this._access={}},load_models:function(a){var b=jQuery.Deferred();return a||(this._access={}),Sao.rpc({method:"model.ir.model.list_models",params:[{}]},Sao.Session.current_session).then(function(a){for(var c=[],d=function(a){this._access=jQuery.extend(this._access,a)},e=0;e<a.length;e+=this.batchnum){var f=a.slice(e,e+this.batchnum);c.push(Sao.rpc({method:"model.ir.model.access.get_access",params:[f,{}]},Sao.Session.current_session).then(d.bind(this)))}jQuery.when.apply(jQuery,c).then(b.resolve,b.reject)}.bind(this)),b},get:function(a){return this._access[a]}}),Sao.common.MODELACCESS=new Sao.common.ModelAccess,Sao.common.ModelHistory=Sao.class_(Object,{init:function(){this._models=[]},load_history:function(){return this._models=[],Sao.rpc({method:"model.ir.model.list_history",params:[{}]},Sao.Session.current_session).then(function(a){this._models=a}.bind(this))},contains:function(a){return~this._models.indexOf(a)}}),Sao.common.MODELHISTORY=new Sao.common.ModelHistory,Sao.common.ViewSearch=Sao.class_(Object,{load_searches:function(){return this.searches={},Sao.rpc({method:"model.ir.ui.view_search.get_search",params:[{}]},Sao.Session.current_session).then(function(a){this.searches=a}.bind(this))},get:function(a){return this.searches[a]||[]},add:function(a,b,c){return Sao.rpc({method:"model.ir.ui.view_search.create",params:[[{model:a,name:b,domain:(new Sao.PYSON.Encoder).encode(c)}],{}]},Sao.Session.current_session).then(function(d){var e=d[0];void 0===this.searches[a]&&(this.searches[a]=[]),this.searches[a].push([e,b,c])}.bind(this))},remove:function(a,b){return Sao.rpc({method:"model.ir.ui.view_search.delete",params:[[b],{}]},Sao.Session.current_session).then(function(){for(var c=0;c<this.searches[a].length;c++){var d=this.searches[a][c];if(d[0]===b){this.searches[a].splice(c,1);break}}}.bind(this))}}),Sao.common.VIEW_SEARCH=new Sao.common.ViewSearch,Sao.common.humanize=function(a){for(var b=["bytes","KB","MB","GB","TB","PB"],c=0,d=b.length;d>c;c++){if(1e3>a)return a.toPrecision(4)+" "+b[c];a/=1e3}},Sao.common.EvalEnvironment=function(a,b){void 0===b&&(b="eval");var c;if("eval"==b)c=a.get_eval();else{c={};for(var d in a.model.fields){var e=a.model.fields[d];c[d]=e.get_on_change_value(a)}}return c.id=a.id,a.group.parent&&Object.defineProperty(c,"_parent_"+a.group.parent_name,{enumerable:!0,get:function(){return Sao.common.EvalEnvironment(a.group.parent,b)}}),c.get=function(a,b){return this.hasOwnProperty(a)?this[a]:b},c},Sao.common.selection_mixin={},Sao.common.selection_mixin.init=function(){this.selection=null,this.inactive_selection=[],this._last_domain=null,this._values2selection={},this._domain_cache={},void 0===this.nullable_widget&&(this.nullable_widget=!0)},Sao.common.selection_mixin.init_selection=function(a,b){a||(a={},(this.attributes.selection_change_with||[]).forEach(function(b){a[b]=null}));var c,d=JSON.stringify(a),e=this.attributes.selection||[],f=function(a){a=jQuery.extend([],a),(void 0===this.attributes.sort||this.attributes.sort)&&a.sort(function(a,b){return a[1].localeCompare(b[1])}),this.selection=jQuery.extend([],a),b&&b(this.selection)};e instanceof Array||d in this._values2selection?(d in this._values2selection&&(e=this._values2selection[d]),f.call(this,e),c=jQuery.when()):(c=jQuery.isEmptyObject(this.attributes.selection_change_with)?this.model.execute(e,[]):this.model.execute(e,[a]),c=c.then(function(a){return this._values2selection[d]=a,a}.bind(this)),c=c.then(f.bind(this))),this.inactive_selection=[],this._selection_prm=c},Sao.common.selection_mixin.update_selection=function(a,b,c){var d=function(){if(!b)return void(c&&c(this.selection));var d=b.get_domain(a);if("reference"==b.description.type&&(d=[]),"relation"in this.attributes){var e=b.get_context(a),f=JSON.stringify([d,e]);if(f in this._domain_cache&&(this.selection=this._domain_cache[f],this._last_domain=[d,e]),null!==this._last_domain&&Sao.common.compare(d,this._last_domain[0])&&JSON.stringify(e)==JSON.stringify(this._last_domain[1]))return void(c&&c(this.selection));var g=Sao.rpc({method:"model."+this.attributes.relation+".search_read",params:[d,0,null,null,["rec_name"],e]},a.model.session);g.done(function(a){var b=[];a.forEach(function(a){b.push([a.id,a.rec_name])}),this.nullable_widget&&b.push([null,""]),this._last_domain=d,this._domain_cache[f]=b,this.selection=jQuery.extend([],b),c&&c(this.selection)}.bind(this)),g.fail(function(){this._last_domain=null,this.selection=[],c&&c(this.selection)}.bind(this))}else{var h=this.attributes.selection_change_with||[],i=a._get_on_change_args(h);delete i.id,Sao.common.selection_mixin.init_selection.call(this,i,function(){Sao.common.selection_mixin.filter_selection.call(this,d,a,b),c&&c(this.selection)}.bind(this))}};this._selection_prm.done(d.bind(this))},Sao.common.selection_mixin.filter_selection=function(a,b,c){if(!jQuery.isEmptyObject(a)){var d=new Sao.common.DomainInversion;this.selection=this.selection.filter(function(b){var c={};return c[this.field_name]=b[0],d.eval_domain(a,c)}.bind(this))}},Sao.common.selection_mixin.get_inactive_selection=function(a){if(!this.attributes.relation)return jQuery.when([]);for(var b=0,c=this.inactive_selection.length;c>b;b++)if(a==this.inactive_selection[b][0])return jQuery.when(this.inactive_selection[b]);var d=Sao.rpc({method:"model."+this.attributes.relation+".read",params:[[a],["rec_name"],{}]},Sao.Session.current_session);return d.then(function(a){return this.inactive_selection.push([a[0].id,a[0].rec_name]),[a[0].id,a[0].rec_name]}.bind(this))},Sao.common.Button=Sao.class_(Object,{init:function(a){this.attributes=a,this.el=jQuery("<button/>",{"class":"btn btn-default",type:"button"}),this.icon=jQuery("<img/>",{"class":"icon","aria-hidden":!0}).appendTo(this.el),this.icon.hide(),this.el.append(a.string||""),this.set_icon(a.icon)},set_icon:function(a){if(!a)return this.icon.attr("src",""),void this.icon.hide();var b=Sao.common.ICONFACTORY.register_icon(a);b.done(function(a){this.icon.attr("src",a),this.icon.show()}.bind(this))},set_state:function(a){var b;if(a?(b=a.expr_eval(this.attributes.states||{}),(a.group.get_readonly()||a.readonly())&&(b.readonly=!0)):b={},b.invisible?this.el.hide():this.el.show(),this.el.prop("disabled",b.readonly),this.set_icon(b.icon||this.attributes.icon),(void 0===this.attributes.type||"class"===this.attributes.type)&&a)for(var c=a.group.parent;c;){if(c.has_changed()){this.el.prop("disabled",!1);break}c=c.group.parent}}}),Sao.common.udlex=Sao.class_(Object,{init:function(a){var b=Sao.class_(Object,{init:function(a){
-this.stream=a.split(""),this.i=0},read:function(a){if(void 0===a&&(a=1),this.i>=this.stream.length)return null;var b=this.stream.slice(this.i,this.i+a).join();return this.i+=a,b}});this.instream=new b(a),this.eof=null,this.commenters="",this.nowordchars=[":",">","<","=","!",'"',";","(",")"],this.whitespace=" 	\r\n",this.whitespace_split=!1,this.quotes='"',this.escape="\\",this.escapedquotes='"',this.state=" ",this.pushback=[],this.token=""},get_token:function(){if(this.pushback.length>0)return this.pushback.shift();var a=this.read_token();return a},read_token:function(){for(var a=!1,b=" ";;){var c=this.instream.read(1);if(null===this.state){this.token="";break}if(" "==this.state){if(!c){this.state=null;break}if(this.whitespace.contains(c)){if(this.token||a)break;continue}if(this.commenters.contains(c));else if(this.escape.contains(c))b="a",this.state=c;else if(~this.nowordchars.indexOf(c))if(this.quotes.contains(c))this.state=c;else{if(!this.whitespace_split){if(this.token=c,this.token||a)break;continue}this.token=c,this.state="a"}else this.token=c,this.state="a"}else if(this.quotes.contains(this.state)){if(a=!0,!c)throw"no closing quotation";c==this.state?this.state="a":this.escape.contains(c)&&this.escapedquotes.contains(this.state)?(b=this.state,this.state=c):this.token=this.token+c}else if(this.escape.contains(this.state)){if(!c)throw"no escaped character";this.quotes.contains(b)&&c!=this.state&&c!=b&&(this.token=this.token+this.state),this.token=this.token+c,this.state=b}else if("a"==this.state){if(!c){this.state=null;break}if(this.whitespace.contains(c)){if(this.state=" ",this.token||a)break;continue}if(this.commenters.contains(c));else if(this.quotes.contains(c))this.state=c;else if(this.escape.contains(c))b="a",this.state=c;else{if(~this.nowordchars.indexOf(c)&&!this.quotes.contains(c)&&!this.whitespace_split){if(this.pushback.unshift(c),this.state=" ",this.token)break;continue}this.token=this.token+c}}}var d=this.token;return this.token="",a||""!==d||(d=null),d},next:function(){var a=this.get_token();return a==this.eof?null:a}}),Sao.common.DomainParser=Sao.class_(Object,{OPERATORS:["!=","<=",">=","=","!","<",">"],init:function(a,b){this.fields={},this.strings={};for(var c in a){var d=a[c];(d.searchable||void 0===d.searchable)&&(this.fields[c]=d,this.strings[d.string.toLowerCase()]=d)}this.context=b},parse:function(a){try{for(var b=new Sao.common.udlex(a),c=[];;){var d=b.next();if(null===d)break;c.push(d)}return c=this.group_operator(c),c=this.parenthesize(c),c=this.group(c),c=this.operatorize(c,"or"),c=this.operatorize(c,"and"),c=this.parse_clause(c),this.simplify(c)}catch(e){if("no closing quotation"==e)return this.parse(a+'"');throw e}},stringable:function(a){var b=function(a){if(!a)return!0;var b=function(a){return a instanceof Array};return(~["AND","OR"].indexOf(a[0])||b(a[0]))&&a.slice(1).every(b)?this.stringable(a):a[0]in this.fields||"rec_name"==a[0]?!0:!1}.bind(this);return a?(~["AND","OR"].indexOf(a[0])&&(a=a.slice(1)),a.every(b)):!0},string:function(a){var b=function(a){if(jQuery.isEmptyObject(a))return"";if("string"!=typeof a[0]||~["AND","OR"].indexOf(a[0]))return"("+this.string(a)+")";var b,c=a[0],d=a[1],e=a[2];if(c.endsWith(".rec_name")&&(c=c.slice(0,-9)),!(c in this.fields))return b=e.replace("%%","__"),b.startsWith("%")&&b.endsWith("%")&&(e=e.slice(1,-1)),this.quote(e);var f=this.fields[c],g=null;a.length>3&&(g=a[3]),d.contains("ilike")&&(b=e.replace("%%","__"),b.startsWith("%")&&b.endsWith("%")?e=e.slice(1,-1):b.contains("%")||(d="ilike"==d?"=":"!",e=e.replace("%%","%")));var h=this.default_operator(f);(h==d.trim()||d.contains(h)&&(d.contains("not")||d.contains("!")))&&(d=d.replace(h,"").replace("not","!").trim()),d.endsWith("in")&&(d="not in"==d?"!":"");var i=this.format_value(f,e,g);return~this.OPERATORS.indexOf(d)&&~["char","text","selection"].indexOf(f.type)&&""===e&&(i='""'),this.quote(f.string)+": "+d+i};if(b=b.bind(this),jQuery.isEmptyObject(a))return"";var c=" ";return("AND"==a[0]||"OR"==a[0])&&("OR"==a[0]&&(c=" or "),a=a.slice(1)),a.map(b).join(c)},completion:function(a){var b,c,d=[],e=this.parse(a),f=0;for(b=a.length;b>0&&(")"!=a[b]&&" "!=a[b]);b--)")"==a[b]&&(f+=1);var g=this.ending_clause(e),h=g[0],i=g[1],j=i-f,k=this.string(e);j>0&&(k=k.substring(0,k.length-j)),k!=a&&d.push(k);var l,m,n=function(a,b){return b>0?a.substring(0,b):a};if(null!==h&&0===f){var o=this.complete(h);for(b=0,c=o.length;c>b;b++)l=o[b],m=this.string(this.replace_ending_clause(e,l)),d.push(n(m,j))}if(a.length>0){if(" "!=a.substr(a.length-1,1))return d;if(a.length>=2||":"==a.substr(a.length-2,1))return d}var p,q,r;for(var s in this.strings)p=this.strings[s],q=this.default_operator(p),r="",("ilike"==q||"not ilike"==q)&&(r=this.likify(r)),new_domain=this.append_ending_clause(e,[p.name,q,r],j),new_domain_string=this.string(new_domain),d.push(n(new_domain_string,j));return d},complete:function(a){var b,c,d,e,f=[];1==a.length?b=a[0]:3==a.length?(b=a[0],c=a[1],d=a[2]):(b=a[0],c=a[1],d=a[2],e=a[3],b.endsWith(".rec_name")&&(b=b.substring(0,b.length-9)));var g;"rec_name"==b&&("ilike"==c&&(g=d.replace(/%%/g,"__"),g.startsWith("%")||g.endsWith("%")?d=g.substring(1,g.length-1):~g.indexOf("%")&&(d=d.replace(/%%/g,"%")),c=null),b=d,d="");var h;if(!(b.toLowerCase()in this.strings||b in this.fields)){for(var i in this.strings)h=this.strings[i],h.string.toLowerCase().startsWith(b.toLowerCase())&&(c=this.default_operator(h),d="","ilike"==c&&(d=this.likify(d)),f.push([h.name,c,d]));return f}if(h=b in this.fields?this.fields[b]:this.strings[b.toLowerCase()],null===c)c=this.default_operator(h),d="",("ilike"==c||"not ilike"==c)&&(d=this.likify(d)),f.push([h.name,c,d]);else for(var j=this.complete_value(h,d),k=0,l=j.length;l>k;k++)f.push([h.name,c,j[k]]);return f},is_leaf:function(a){return a instanceof Array&&a.clause},ending_clause:function(a,b){if(void 0===b&&(b=0),0===a.length)return[null,b];var c=a[a.length-1];return this.is_leaf(c)?[c,b]:this.ending_clause(c,b+1)},replace_ending_clause:function(a,b){var c,d,e=[];for(c=0,d=a.length-1;d>c;c++)e.push(a[c]);return this.is_leaf(a[c])?e.push(b):e=e.concat(this.replace_ending_clause(a[c],b)),e},append_ending_clause:function(a,b,c){if(0===a.length)return[b];var d=a.slice(0,-1),e=a[a.length-1];return this.is_leaf(e)?(d.push(e),0===c&&d.push(b)):d.push(this.append_ending_clause(e,b,c-1)),d},complete_value:function(a,b){var c=function(){return b?[!0]:[!1]},d=function(){var c=[],d=null!==b?b:"";b instanceof Array&&(d=b[b.length-1]),d=d.replace(/^%*|%*$/g,"");var e,f,g,h;for(e=0,f=a.selection.length;f>e;e++)g=a.selection[e][0],h=a.selection[e][1].toLowerCase(),h.startsWith(d.toLowerCase())&&c.push(b instanceof Array?b.slice(0,-1).concat([g]):g);return c},e=function(){var c=[],d=null!==b?b:"";b instanceof Array&&(d=b[b.length-1]),d=d.replace(/^%*|%*$/g,"");var e,f,g,h;for(e=0,f=a.selection.length;f>e;e++)g=a.selection[e][0],h=a.selection[e][1].toLowerCase(),h.startsWith(d.toLowerCase())&&c.push(b instanceof Array?b.slice(0,-1).concat([g]):this.likify(g));return c},f=function(){return[Sao.Date(),Sao.DateTime().utc()]},g=function(){return[Sao.Date()]},h=function(){return[Sao.Time()]},i={"boolean":c,selection:d,reference:e,datetime:f,date:g,time:h};return a.type in i?i[a.type]():[]},group_operator:function(a){var b=a[0],c=[];return a.slice(1).forEach(function(a){"="==a&&b&&~this.OPERATORS.indexOf(b+a)?(c.push(b+a),b=null):(null!==b&&c.push(b),b=a)}.bind(this)),null!==b&&c.push(b),c},parenthesize:function(a){var b=[],c=b,d=[];return a.forEach(function(a,b){void 0!==c&&("("==a?(d.push(c),c=c[c.push([])-1]):")"==a?c=d.pop():c.push(a))}),b},group:function(a){var b=[],c=function(a){var b=[],d=function(a){b.push([a])},e=a.indexOf(":");if(!~e)return a.forEach(d),b.forEach(function(a){a.clause=!0}),b;for(var f=function(a,c){return function(d){if(jQuery.isEmptyObject(a))b.push(d);else{var e;jQuery.isEmptyObject(c)?(e=a.concat(d),e.clause=!0,b.push(e)):(null!==d[0]&&c.push(d[0]),e=a.concat([c]),e.clause=!0,b.push(e)),a.splice(0,a.length)}}},g=0;e>g;g++){var h=a.slice(g,e).join(" ");if(h.toLowerCase()in this.strings){jQuery.isEmptyObject(a.slice(0,g))?d(null):a.slice(0,g).forEach(d),h=[h],e+1<a.length&&~this.OPERATORS.indexOf(a[e+1])?(h=h.concat([a[e+1]]),e+=1):h=h.concat([null]);for(var i=[];e+2<a.length&&";"==a[e+2];)i.push(a[e+1]),e+=2;if(c(a.slice(e+1)).forEach(f(h,i)),!jQuery.isEmptyObject(h)){var j;jQuery.isEmptyObject(i)?(j=h.concat([null]),j.clause=!0,b.push(j)):(j=h.concat([i]),j.clause=!0,b.push(j))}break}}return b};c=c.bind(this);var d=[];return a.forEach(function(a){this.is_generator(a)?(c(d).forEach(function(a){Sao.common.compare(a,[null])||b.push(a)}),d=[],b.push(this.group(a))):d.push(a)}.bind(this)),c(d).forEach(function(a){Sao.common.compare(a,[null])||b.push(a)}),b},is_generator:function(a){return a instanceof Array&&void 0===a.clause},operatorize:function(a,b){var c=[];b=b||"or",a=jQuery.extend([],a);for(var d=function(a){return a instanceof Array?Sao.common.compare(a,[b]):a==b},e=a.shift();d(e);)e=a.shift();if(void 0===e)return c;this.is_generator(e)&&(e=this.operatorize(e,b));for(var f=null;!jQuery.isEmptyObject(a);)if(f=a.shift(),this.is_generator(f)&&!d(f)&&(f=this.operatorize(f,b)),d(f)){for(f=a.shift();d(f);)f=a.shift();this.is_generator(f)&&(f=this.operatorize(f,b)),void 0!==f?e=[b.toUpperCase(),e,f]:d(e)||(c.push([b.toUpperCase(),e]),e=null),f=null}else d(e)||c.push(e),e=f;return jQuery.isEmptyObject(a)&&(null===f||d(f)?null===e||d(e)||c.push(e):c.push(f)),c},_clausify:function(a){return a.clause=!0,a},parse_clause:function(a){var b=[];return a.forEach(function(a){if(this.is_generator(a))b.concat(this.parse_clause(a));else if("OR"==a||"AND"==a)b.push(a);else if(1!=a.length||a[0]instanceof Array){if(3==a.length&&a[0].toLowerCase()in this.strings){var c=(a[0],a[1]),d=a[2],e=this.strings[a[0].toLowerCase()],f=null;if("reference"==e.type){var g=this.split_target_value(e,d);f=g[0],d=g[1]}if(null===c&&(c=this.default_operator(e)),d instanceof Array&&(c="!"==c?"not in":"in"),"!"==c&&(c=this.negate_operator(this.default_operator(e))),~["integer","float","numeric","datetime","date","time"].indexOf(e.type)&&d&&d.contains("..")){var h=d.split("..",2),i=this.convert_value(e,h[0]),j=this.convert_value(e,h[1]);return void b.push([this._clausify([e.name,">=",i]),this._clausify([e.name,"<=",j])])}d=d instanceof Array?d.map(function(a){return this.convert_value(e,a)}.bind(this)):this.convert_value(e,d),c.contains("like")&&(d=this.likify(d)),b.push(f?this._clausify([e.name+".rec_name",c,d,f]):this._clausify([e.name,c,d]))}}else b.push(this._clausify(["rec_name","ilike",this.likify(a[0])]))}.bind(this)),b},likify:function(a){if(!a)return"%";var b=a.replace("%%","__");return b.contains("%")?a:"%"+a+"%"},quote:function(a){if("string"!=typeof a)return a;a.contains("\\")&&(a=a.replace(new RegExp("\\\\","g"),"\\\\")),a.contains('"')&&(a=a.replace(new RegExp('"',"g"),'\\"'));for(var b=[":"," ","(",")"].concat(this.OPERATORS),c=0;c<b.length;c++){var d=b[c];if(a.contains(d))return'"'+a+'"'}return a},default_operator:function(a){return~["char","text","many2one","many2many","one2many","reference"].indexOf(a.type)?"ilike":"="},negate_operator:function(a){switch(a){case"ilike":return"not ilike";case"=":return"!=";case"in":return"not in"}},time_format:function(a){return new Sao.PYSON.Decoder({}).decode(a.format)},split_target_value:function(a,b){var c=null;if("string"==typeof b)for(var d=0;d<a.selection.length;d++){var e=a.selection[d],f=e[0],g=e[1];if(b.toLowerCase().startsWith(g.toLowerCase()+",")){c=f,b=b.slice(g.length+1);break}}return[c,b]},convert_value:function(a,b){var c=function(){if("string"==typeof b)for(var c=0;c<a.selection.length;c++){var d=a.selection[c],e=d[0],f=d[1];if(b.toLowerCase()==f.toLowerCase())return e}return b},d={"boolean":function(){return"string"==typeof b?[Sao.i18n.gettext("y"),Sao.i18n.gettext("yes"),Sao.i18n.gettext("true"),Sao.i18n.gettext("t"),"1"].some(function(a){return a.toLowerCase().startsWith(b.toLowerCase())}):Boolean(b)},"float":function(){var a=Number(b);return isNaN(a)||""===b||null===b?null:a},integer:function(){var a=parseInt(b,10);return isNaN(a)?null:a},numeric:function(){var a=new Sao.Decimal(b);return isNaN(a.valueOf())||""===b||null===b?null:a},selection:c,reference:c,datetime:function(){var c=Sao.common.parse_datetime(Sao.common.date_format(),this.time_format(a),b);return c||(c=Sao.common.parse_date(Sao.common.date_format(),b)),c}.bind(this),date:function(){return Sao.common.parse_date(Sao.common.date_format(),b)},time:function(){try{return Sao.common.parse_time(this.time_format(a),b)}catch(c){return null}}.bind(this),timedelta:function(){var c=null;return a.converter&&(c=this.context[a.converter]),Sao.common.timedelta.parse(b,c)}.bind(this),many2one:function(){return""===b?null:b}},e=d[a.type];return e?e():b},format_value:function(a,b,c){void 0===c&&(c=null);var d=function(){if(!b&&0!==b&&b!==new Sao.Decimal(0))return"";var a=String(b).split(".")[1];return a=a?a.length:0,b.toFixed(a)},e=function(){for(var c=0;c<a.selection.length;c++)if(a.selection[c][0]==b)return a.selection[c][1];return b||""},f=function(){if(!c)return e();for(var d=0;d<a.selection.length;d++)if(a.selection[d][0]==c){c=a.selection[d][1];break}return c+","+b},g={"boolean":function(){return Sao.i18n.gettext(b?"True":"False")},integer:function(){return b||0===b?""+parseInt(b,10):""},"float":d,numeric:d,selection:e,reference:f,datetime:function(){return b?b.isDate||!(b.hour()||b.minute()||b.second())?Sao.common.format_date(Sao.common.date_format(),b):Sao.common.format_datetime(Sao.common.date_format(),this.time_format(a),b):""}.bind(this),date:function(){return Sao.common.format_date(Sao.common.date_format(),b)},time:function(){return b?Sao.common.format_time(this.time_format(a),b):""}.bind(this),timedelta:function(){if(!b||!b.valueOf())return"";var c=null;return a.converter&&(c=this.context[a.converter]),Sao.common.timedelta.format(b,c)}.bind(this),many2one:function(){return null===b?"":b}};if(b instanceof Array)return b.map(function(b){return this.format_value(a,b)}.bind(this)).join(";");var h=g[a.type];return h?this.quote(h(b)):null===b?"":this.quote(b)},simplify:function(a){return a instanceof Array&&!this.is_leaf(a)?1==a.length&&a[0]instanceof Array&&("AND"==a[0][0]||"OR"==a[0][0]||a[0][0]instanceof Array)?this.simplify(a[0]):2==a.length&&("AND"==a[0]||"OR"==a[0])&&a[1]instanceof Array?this.simplify(a[1]):(3==a.length&&("AND"==a[0]||"OR"==a[0])&&a[1]instanceof Array&&a[0]==a[1][0]&&(a=this.simplify(a[1]).concat([a[2]])),a.map(this.simplify.bind(this))):a}}),Sao.common.DomainInversion=Sao.class_(Object,{and:function(a,b){return a&&b},or:function(a,b){return a||b},OPERATORS:{"=":function(a,b){return a instanceof Array&&b instanceof Array?Sao.common.compare(a,b):a===b},">":function(a,b){return a>b},"<":function(a,b){return b>a},"<=":function(a,b){return b>=a},">=":function(a,b){return a>=b},"!=":function(a,b){return a instanceof Array&&b instanceof Array?!Sao.common.compare(a,b):a!==b},"in":function(a,b){return Sao.common.DomainInversion.in_(a,b)},"not in":function(a,b){return!Sao.common.DomainInversion.in_(a,b)},like:function(){return!0},ilike:function(){return!0},"not like":function(){return!0},"not ilike":function(){return!0},child_of:function(){return!0},"not child_of":function(){return!0}},locale_part:function(a,b,c){return void 0===c&&(c="id"),a===b?c:a.contains(".")?a.split(".").slice(1).join("."):a},is_leaf:function(a){return a instanceof Array&&a.length>2&&"string"==typeof a[1]},eval_leaf:function(a,b,c){void 0===c&&(c=this.and);var d=a[0],e=a[1],f=a[2];if(d.contains("."))return Boolean(b[d.split(".")[0]]);if("="==e&&(null===b[d]||void 0===b[d])&&c===this.and)return!0;var g=b[d];return g&&g._isAMomentObject&&!f&&(f=g.isDateTime?Sao.DateTime.min:Sao.Date.min),f&&f._isAMomentObject&&!g&&(g=f.isDateTime?Sao.DateTime.min:Sao.Date.min),"string"==typeof g&&f instanceof Array&&2==f.length?f=f.join(","):g instanceof Array&&"string"==typeof f&&2==g.length&&(g=g.join(",")),~["=","!="].indexOf(e)&&g instanceof Array&&"number"==typeof f&&(e={"=":"in","!=":"not in"}[e]),e in this.OPERATORS?this.OPERATORS[e](g,f):!0},inverse_leaf:function(a){return~["AND","OR"].indexOf(a)?a:this.is_leaf(a)?a[1].contains("child_of")?3==a.length?a:[a[3]].concat(a.slice(1)):a:a.map(this.inverse_leaf.bind(this))},filter_leaf:function(a,b,c){return~["AND","OR"].indexOf(a)?a:this.is_leaf(a)?a[0].startsWith(b)&&a.length>3&&a[3]!==c?["id","=",null]:a:a.map(function(a){return this.filter_leaf(a,b,c)}.bind(this))},eval_domain:function(a,b,c){return void 0===c&&(c=this.and),this.is_leaf(a)?this.eval_leaf(a,b,c):jQuery.isEmptyObject(a)&&c==this.and?!0:jQuery.isEmptyObject(a)&&c==this.or?!1:"AND"==a[0]?this.eval_domain(a.slice(1),b):"OR"==a[0]?this.eval_domain(a.slice(1),b,this.or):c(this.eval_domain(a[0],b),this.eval_domain(a.slice(1),b,c))},localize_domain:function(a,b,c){if(~["AND","OR",!0,!1].indexOf(a))return a;if(this.is_leaf(a)){if(a[1].contains("child_of"))return 3==a.length?a:[a[3]].concat(a.slice(1,-1));var d="id";"string"==typeof a[2]&&(d="rec_name");var e=c?3:4;return[this.locale_part(a[0],b,d)].concat(a.slice(1,e)).concat(a.slice(4))}return a.map(function(a){return this.localize_domain(a,b,c)}.bind(this))},simplify:function(a){return this.is_leaf(a)?a:~["OR","AND"].indexOf(a)?a:a instanceof Array&&1==a.length&&~["OR","AND"].indexOf(a[0])?[]:a instanceof Array&&1==a.length&&!this.is_leaf(a[0])?this.simplify(a[0]):a instanceof Array&&2==a.length&&~["AND","OR"].indexOf(a[0])?[this.simplify(a[1])]:a.map(this.simplify.bind(this))},merge:function(a,b){if(jQuery.isEmptyObject(a)||~["AND","OR"].indexOf(a))return[];var c="OR"==a[0]?"OR":"AND";return this.is_leaf(a)?[a]:void 0===b?[c].concat([].concat.apply([],a.map(function(a){return this.merge(a,c)}.bind(this)))):c==b?[].concat.apply([],a.map(function(a){return this.merge(a,c)}.bind(this))):[this.merge(a)]},concat:function(a,b){var c=[];return b&&c.push(b),a.forEach(function(a){jQuery.isEmptyObject(a)||c.push(a)}),this.simplify(this.merge(c))},unique_value:function(a){return a instanceof Array&&1==a.length&&!a[0][0].contains(".")&&"="==a[0][1]?[!0,a[0][1],a[0][2]]:[!1,null,null]},parse:function(a){var b=Sao.common.DomainInversion.And,c=Sao.common.DomainInversion.Or;if(this.is_leaf(a))return a;if(jQuery.isEmptyObject(a))return new b([]);if("OR"===a[0])return new c(a.slice(1));var d=0;return"AND"===a[0]&&(d=1),new b(a.slice(d))},domain_inversion:function(a,b,c){void 0===c&&(c={});var d=this.parse(a);return~d.variables.indexOf(b)?d.inverse(b,c):!0}}),Sao.common.DomainInversion.in_=function(a,b){if(a instanceof Array){if(b instanceof Array){for(var c=0,d=a.length;d>c;c++)if(~b.indexOf(a[c]))return!0;return!1}return Boolean(~a.indexOf(b))}return Boolean(~b.indexOf(a))},Sao.common.DomainInversion.And=Sao.class_(Object,{init:function(a){this.domain_inversion=new Sao.common.DomainInversion,this.branches=a.map(this.domain_inversion.parse.bind(this.domain_inversion)),this.variables=[];for(var b=0,c=this.branches.length;c>b;b++){var d=this.branches[b];this.domain_inversion.is_leaf(d)?this.variables.push(this.base(d[0])):d instanceof Sao.common.DomainInversion.And&&(this.variables=this.variables.concat(d.variables))}},base:function(a){return a.contains(".")?a.split(".")[0]:a},inverse:function(a,b){for(var c=Sao.common.DomainInversion,d=[],e=0,f=this.branches.length;f>e;e++){var g=this.branches[e];if(g instanceof c.And){var h=g.inverse(a,b),i="boolean"==typeof h;if(i){if(h)continue;return!1}d.push(h)}else if(this.domain_inversion.is_leaf(g)&&this.base(g[0])===a)d.push(g);else{var j=g[0];if(j in b&&!(j in b&&this.domain_inversion.eval_leaf(g,b,this.domain_inversion.and)))return!1;d.push(!0)}}return d=d.filter(function(a){return a!==!0}),jQuery.isEmptyObject(d)?!0:this.domain_inversion.simplify(d)}}),Sao.common.DomainInversion.Or=Sao.class_(Sao.common.DomainInversion.And,{inverse:function(a,b){var c=Sao.common.DomainInversion,d=[];if(!~this.variables.indexOf(a)&&!jQuery.isEmptyObject(this.variables.filter(function(a){return!(a in b)})))return!0;for(var e=0,f=this.branches.length;f>e;e++){var g=this.branches[e];if(g instanceof c.And){var h=g.inverse(a,b),i="boolean"==typeof h;if(!~this.variables.indexOf(a)){if(i&&h)return!0;continue}if(i){if(h)return!0;continue}d.push(h)}else if(this.domain_inversion.is_leaf(g)&&this.base(g[0])==a)d.push(g);else{var j=g[0];if(j=this.base(j),j in b&&this.domain_inversion.eval_leaf(g,b,this.domain_inversion.or))return!0;j in b&&!this.domain_inversion.eval_leaf(g,b,this.domain_inversion.or)&&d.push(!1)}}return d=d.filter(function(a){return a!==!1}),jQuery.isEmptyObject(d)?!1:this.domain_inversion.simplify(["OR"].concat(d))}}),Sao.common.guess_mimetype=function(a){return/.*odt$/.test(a)?"application/vnd.oasis.opendocument.text":/.*ods$/.test(a)?"application/vnd.oasis.opendocument.spreadsheet":/.*pdf$/.test(a)?"application/pdf":/.*docx$/.test(a)?"application/vnd.openxmlformats-officedocument.wordprocessingml.document":/.*doc/.test(a)?"application/msword":/.*xlsx$/.test(a)?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":/.*xls/.test(a)?"application/vnd.ms-excel":"application/octet-binary"},Sao.common.LOCAL_ICONS=["tryton-attachment-hi","tryton-attachment","tryton-bookmark","tryton-cancel","tryton-clear","tryton-close","tryton-connect","tryton-copy","tryton-delete","tryton-dialog-error","tryton-dialog-information","tryton-dialog-warning","tryton-disconnect","tryton-executable","tryton-find-replace","tryton-find","tryton-folder-new","tryton-fullscreen","tryton-go-home","tryton-go-jump","tryton-go-next","tryton-go-previous","tryton-help","tryton-icon","tryton-list-add","tryton-list-remove","tryton-locale","tryton-lock","tryton-log-out","tryton-mail-message-new","tryton-mail-message","tryton-new","tryton-ok","tryton-open","tryton-preferences-system-session","tryton-preferences-system","tryton-preferences","tryton-print-email","tryton-print-open","tryton-print","tryton-refresh","tryton-save-as","tryton-save","tryton-star","tryton-start-here","tryton-system-file-manager","tryton-system","tryton-text-background","tryton-text-foreground","tryton-text-markup","tryton-undo","tryton-unstar","tryton-web-browser"],Sao.common.IconFactory=Sao.class_(Object,{batchnum:10,name2id:{},loaded_icons:{},tryton_icons:[],register_prm:jQuery.when(),load_icons:function(a){if(a=a||!1,!a)for(var b in this.load_icons)this.load_icons.hasOwnProperty(b)&&window.URL.revokeObjectURL(this.load_icons[b]);var c=new Sao.Model("ir.ui.icon");return c.execute("list_icons",[],{}).then(function(b){a||(this.name2id={},this.loaded_icons={}),this.tryton_icons=[];for(var c,d,e=0,f=b.length;f>e;e++)c=b[e][0],d=b[e][1],a&&d in this.loaded_icons||(this.tryton_icons.push([c,d]),this.name2id[d]=c)}.bind(this))},register_icon:function(a){if(!a)return jQuery.when("");if(a in this.loaded_icons||~Sao.common.LOCAL_ICONS.indexOf(a))return jQuery.when(this.get_icon_url(a));if("pending"==this.register_prm.state()){var b=jQuery.Deferred();return this.register_prm.then(function(){this.register_icon(a).then(b.resolve,b.reject)}.bind(this)),b}var c;c=a in this.name2id?jQuery.when():this.load_icons(!0);var d=new Sao.Model("ir.ui.icon");return this.register_prm=c.then(function(){var b=function(a){var b,c;for(b=0,c=this.tryton_icons.length;c>b;b++){var d=this.tryton_icons[b];if(Sao.common.compare(d,a))break}return b}.bind(this),c=b([this.name2id[a],a]),e=Math.round(c-this.batchnum/2);e=0>e?0:e;var f=Math.round(c+this.batchnum/2),g=[];this.tryton_icons.slice(e,f).forEach(function(a){g.push(a[0])});var h=d.execute("read",[g,["name","icon"]],{});return h.then(function(c){return c.forEach(function(a){var c;if(navigator.userAgent.match(/firefox/i)){c="data:image/svg+xml;base64,"+window.btoa(unescape(encodeURIComponent(a.icon)));var d=new Image;d.src=c,d.onload=function(){var b=document.createElement("canvas");b.width=d.width,b.height=d.height;var e=b.getContext("2d");e.drawImage(d,0,0),b.toBlob(function(d){var e=c;c=window.URL.createObjectURL(d),this.loaded_icons[a.name]=c,jQuery(document).find("img").each(function(a,b){b.src==e&&(b.src=c)}),b.remove()}.bind(this),"image/png")}.bind(this)}else{var e=new Blob([a.icon],{type:"image/svg+xml"});c=window.URL.createObjectURL(e)}this.loaded_icons[a.name]=c,delete this.name2id[a.name],this.tryton_icons.splice(b([a.id,a.name]),1)}.bind(this)),this.get_icon_url(a)}.bind(this))}.bind(this)),this.register_prm},get_icon_url:function(a){return a in this.loaded_icons?this.loaded_icons[a]:"images/"+a+".svg"}}),Sao.common.ICONFACTORY=new Sao.common.IconFactory,Sao.common.UniqueDialog=Sao.class_(Object,{init:function(){this.running=!1},build_dialog:function(){var a=new Sao.Dialog("",this.class_);return a},run:function(){if(!this.running){var a=Array.prototype.slice.call(arguments),b=jQuery.Deferred();a.push(b);var c=this.build_dialog.apply(this,a);return this.running=!0,c.modal.modal("show"),b}},close:function(a){a.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),a.modal.modal("hide"),this.running=!1}}),Sao.common.MessageDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"message-dialog",build_dialog:function(a,b,c){var d=Sao.common.MessageDialog._super.build_dialog.call(this);return d.header.remove(),d.body.append(jQuery("<div/>",{"class":"alert alert-info",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon "+b,"aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Message: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve("ok")}.bind(this)).appendTo(d.footer),d},run:function(a,b){return Sao.common.MessageDialog._super.run.call(this,a,b||"glyphicon-info-sign")}}),Sao.common.message=new Sao.common.MessageDialog,Sao.common.WarningDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"warning-dialog",build_dialog:function(a,b,c){var d=Sao.common.WarningDialog._super.build_dialog.call(this);return d.add_title(b),d.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-alert","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Warning: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve("ok")}.bind(this)).appendTo(d.footer),d}}),Sao.common.warning=new Sao.common.WarningDialog,Sao.common.UserWarningDialog=Sao.class_(Sao.common.WarningDialog,{class_:"user-warning-dialog",always:!1,_set_always:function(){this.always=jQuery(this).prop("checked")},build_dialog:function(a,b,c){var d=Sao.common.UserWarningDialog._super.build_dialog.call(this,a,b,c);return d.body.append(jQuery("<div/>").append(jQuery("<input/>",{type:"checkbox"}).change(this._set_always.bind(this))).append(jQuery("<span/>").text(Sao.i18n.gettext("Always ignore this warning.")))),d.body.append(jQuery("<p/>").text(Sao.i18n.gettext("Do you want to proceed?"))),d.footer.children().remove(),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("No")).click(function(){this.close(d),c.reject()}.bind(this)).appendTo(d.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Yes")).click(function(){this.close(d),this.always&&c.resolve("always"),c.resolve("ok")}.bind(this)).appendTo(d.footer),d}}),Sao.common.userwarning=new Sao.common.UserWarningDialog,Sao.common.ConfirmationDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"confirmation-dialog",build_dialog:function(a){var b=Sao.common.ConfirmationDialog._super.build_dialog.call(this);return b.header.remove(),b.body.append(jQuery("<div/>",{"class":"alert alert-info",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-info-sign","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Confirmation: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),b}}),Sao.common.SurDialog=Sao.class_(Sao.common.ConfirmationDialog,{build_dialog:function(a,b){var c=Sao.common.SurDialog._super.build_dialog.call(this,a);return jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(c),b.reject()}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(c),b.resolve()}.bind(this)).appendTo(c.footer),c}}),Sao.common.sur=new Sao.common.SurDialog,Sao.common.Sur3BDialog=Sao.class_(Sao.common.ConfirmationDialog,{build_dialog:function(a,b){var c=Sao.common.SurDialog._super.build_dialog.call(this,a);return jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(c),b.resolve("cancel")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("No")).click(function(){this.close(c),b.resolve("ko")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Yes")).click(function(){this.close(c),b.resolve("ok")}.bind(this)).appendTo(c.footer),c}}),Sao.common.sur_3b=new Sao.common.Sur3BDialog,Sao.common.AskDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"ask-dialog",run:function(){var a=Array.prototype.slice.call(arguments);return 1==a.length&&a.push(!0),Sao.common.AskDialog._super.run.apply(this,a)},build_dialog:function(a,b,c){var d=Sao.common.AskDialog._super.build_dialog.call(this);d.header.remove();var e=jQuery("<input/>",{"class":"form-control",type:b?"input":"password",id:"ask-dialog-entry"}),f=d.find(".modal-body");return f.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"for":"ask-dialog-entry"}).append(a)).append(e)),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(d),c.reject()}.bind(this)).appendTo(d.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve(e.val())}.bind(this)).appendTo(d.footer),d}}),Sao.common.ask=new Sao.common.AskDialog,Sao.common.ConcurrencyDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"ask-dialog",build_dialog:function(a,b,c,d){var e=Sao.common.ConcurrencyDialog._super.build_dialog.call(this);return e.modal.find(".modal-dialog").removeClass("modal-sm").addClass("modal-lg"),e.add_title(Sao.i18n.gettext("Concurrency Exception")),e.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<p/>").append(jQuery("<span/>",{"class":"glyphicon glyphicon-info-sign","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Write Concurrency Warning: "))).append(Sao.i18n.gettext("This record has been modified while you were editing it."))).append(jQuery("<p/>").text(Sao.i18n.gettext("Choose:"))).append(jQuery("<ul/>").append(jQuery("<li/>").text(Sao.i18n.gettext('"Cancel" to cancel saving;'))).append(jQuery("<li/>").text(Sao.i18n.gettext('"Compare" to see the modified version;'))).append(jQuery("<li/>").text(Sao.i18n.gettext('"Write Anyway" to save your current version.'))))),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(e),d.reject()}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Compare")).click(function(){this.close(e),Sao.Tab.create({model:a,res_id:b,domain:[["id","=",b]],context:c,mode:["form","tree"]}),d.reject()}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Write Anyway")).click(function(){this.close(e),d.resolve()}.bind(this)).appendTo(e.footer),
-e}}),Sao.common.concurrency=new Sao.common.ConcurrencyDialog,Sao.common.ErrorDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"error-dialog",build_dialog:function(a,b,c){var d=Sao.common.ConcurrencyDialog._super.build_dialog.call(this);return d.modal.find(".modal-dialog").removeClass("modal-sm").addClass("modal-lg"),d.add_title(Sao.i18n.gettext("Application Error")),d.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-alert","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Warning: "))).append(jQuery("<p/>").append(jQuery("<pre/>").text(b))).append(jQuery("<p/>").append(jQuery("<a/>",{"class":"btn btn-link",href:Sao.config.roundup.url,target:"_blank"}).text(Sao.i18n.gettext("Report Bug"))))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Close")).click(function(){this.close(d),c.resolve()}.bind(this)).appendTo(d.footer),d}}),Sao.common.error=new Sao.common.ErrorDialog,Sao.common.Processing=Sao.class_(Object,{queries:0,timeout:500,init:function(){this.el=jQuery("<div/>",{id:"processing","class":"text-center"}),this.el.append(jQuery("<span/>",{"class":"label label-info",text:"Processing..."})),this.el.hide(),jQuery(function(){this.el.appendTo("body")}.bind(this))},show:function(){return window.setTimeout(function(){this.queries+=1,this.el.show()}.bind(this),this.timeout)},hide:function(a){window.clearTimeout(a),this.queries>0&&(this.queries-=1),this.queries<=0&&(this.queries=0,this.el.hide())}}),Sao.common.processing=new Sao.common.Processing,Sao.common.InputCompletion=Sao.class_(Object,{init:function(a,b,c,d){a.is("input")?(a.wrap('<div class="dropdown"/>'),this.dropdown=a.parent()):(a.addClass("dropdown"),this.dropdown=a),this.input=a.find("input").add(a.filter("input")).first(),this.input.attr("autocomplete","off"),jQuery("<span/>",{"data-toggle":"dropdown"}).appendTo(this.dropdown),this.menu=jQuery("<ul/>",{"class":"dropdown-menu",role:"listbox"}).appendTo(this.dropdown),this.separator=jQuery("<li/>",{role:"separator","class":"divider"}).appendTo(this.menu),this.separator.hide(),this.source=b,this.match_selected=c,this.format=d,this.action_activated=null,this._search_text=null,this.input.on("input",function(){window.setTimeout(this._input.bind(this),300,this.input.val())}.bind(this)),this.input.keydown(function(a){a.which==Sao.common.ESC_KEYCODE?this.dropdown.hasClass("open")&&this.menu.dropdown("toggle"):a.which==Sao.common.RETURN_KEYCODE&&(this.dropdown.hasClass("open")||this.menu.dropdown("toggle"))}.bind(this)),this.dropdown.on("hide.bs.dropdown",function(){this.input.focus(),this.input.closest(".treeview").css("overflow","")}.bind(this)),this.dropdown.on("show.bs.dropdown",function(){this.input.closest(".treeview").css("overflow","visible")}.bind(this))},set_actions:function(a,b){return void 0!==b&&(this.action_activated=b),this.menu.find("li.action").remove(),jQuery.isEmptyObject(a)?void this.separator.hide():(this.separator.show(),void a.forEach(function(a){var b=a[0],c=a[1];jQuery("<li/>",{"class":"action action-"+b}).append(jQuery("<a/>",{href:"#"}).append(this._format_action(c))).click(function(){this.action_activated&&this.action_activated(b),this.input.val("")}.bind(this)).appendTo(this.menu)},this))},_format:function(a){return this.format?this.format(a):a},_format_action:function(a){return this.format_action?this.format_action(a):a},_input:function(a){if(a==this.input.val()){var b;b=this.source instanceof Array?jQuery.when(source.filter(function(b){return b.toLowerCase().startsWith(a.toLowerCase())})):this.source(a),b.then(function(b){a==this.input.val()&&this._set_selection(b)}.bind(this))}},_set_selection:function(a){void 0===a&&(a=[]),this.menu.find("li.completion").remove(),a.reverse().forEach(function(a){jQuery("<li/>",{"class":"completion"}).append(jQuery("<a/>",{href:"#"}).append(this._format(a))).click(function(){this.match_selected&&this.match_selected(a),this.input.focus()}.bind(this)).prependTo(this.menu)},this),this.input.val()?this.dropdown.hasClass("open")||this.menu.dropdown("toggle"):this.dropdown.hasClass("open")&&this.menu.dropdown("toggle")}}),Sao.common.get_completion=function(a,b,c,d){var e=function(a){return a.rec_name},f=new Sao.common.InputCompletion(a,b,c,e);f.set_actions([["search",Sao.i18n.gettext("Search...")],["create",Sao.i18n.gettext("Create..")]],d)},Sao.common.update_completion=function(a,b,c,d,e){var f=a.val();if(!f||!d)return jQuery.when();void 0===e&&(e=c.get_domain(b));var g=c.get_context(b);e=[["rec_name","ilike","%"+f+"%"],e];var h=new Sao.Model(d);return h.execute("search_read",[e,0,Sao.config.limit,null,["rec_name"]],g)},Sao.common.Paned=Sao.class_(Object,{init:function(a){var b;this._orientation=a,this.el=jQuery("<div/>"),"horizontal"==a?(b=jQuery("<div/>",{"class":"row"}).appendTo(this.el),this.child1=jQuery("<div/>",{"class":"col-md-6"}).appendTo(b),this.child2=jQuery("<div/>",{"class":"col-md-6"}).appendTo(b)):"vertical"==a&&(this.child1=jQuery("<div/>",{"class":"row"}).appendTo(this.el),this.child2=jQuery("<div/>",{"class":"row"}).appendTo(this.el))},get_child1:function(){return this.child1},get_child2:function(){return this.child2}}),Sao.common.get_focus_chain=function(a){var b=a.find("input","textarea");return b.sort(function(a,b){if("tabindex"in a.attributes&&"tabindex"in b.attributes){var c=parseInt(a.attributes.tabindex.value),d=parseInt(b.attributes.tabindex.value);return c-d}return"tabindex"in a.attributes?-1:"tabindex"in b.attributes?1:0}),b},Sao.common.find_focusable_child=function(a){var b,c,d,e;if(!a.is(":visible"))return null;if(~["input","select","textarea"].indexOf(a[0].tagName.toLowerCase()))return a;for(d=Sao.common.get_focus_chain(a),b=0,c=d.length;c>b;b++)if(e=Sao.common.find_focusable_child(jQuery(d[b])))return e},Sao.common.find_first_focus_widget=function(a,b){var c,d,e,f,g;if(1==b.length)return jQuery(b[0]);for(e=Sao.common.get_focus_chain(a),c=0;c<e.length;c++){for(f=[],d=0;d<b.length;d++)g=jQuery(b[d]).closest(e[c]).length>0,g&&f.push(b[d]);if(f.length>0)return Sao.common.find_first_focus_widget(jQuery(e[c]),f)}}}(),function(){"use strict";Sao.Window={},Sao.Window.InfoBar=Sao.class_(Object,{init:function(){this.text=jQuery("<span/>"),this.text.css("white-space","pre-wrap"),this.el=jQuery("<div/>",{"class":"alert",role:"alert"}).append(jQuery("<button/>",{type:"button","class":"close","aria-label":Sao.i18n.gettext("Close")}).append(jQuery("<span/>",{"aria-hidden":!0}).append("×")).click(function(){this.el.hide()}.bind(this))).append(this.text),this.el.hide()},message:function(a,b){a?(this.el.removeClass("alert-success alert-info alert-warning alert-danger"),this.el.addClass("alert-"+(b||"info")),this.text.text(a),this.el.show()):this.el.hide()}}),Sao.Window.Form=Sao.class_(Object,{init:function(a,b,c){c=c||{},this.screen=a,this.callback=b,this.many=c.many||0,this.domain=c.domain||null,this.context=c.context||null,this.save_current=c.save_current,this.prev_view=a.current_view,this.screen.screen_container.alternate_view=!0,this.info_bar=new Sao.Window.InfoBar;for(var d=c.view_type||"form",e=jQuery.when(),f=[],g=0,h=this.screen.views.length;h>g;g++)f.push(this.screen.views[g].view_type);~f.indexOf(d)||~this.screen.view_to_load.indexOf(d)||(e=this.screen.add_view_id(null,d));var i=e.then(function(){return this.screen.switch_view(d).done(function(){c.new_&&this.screen.new_()}.bind(this))}.bind(this)),j=new Sao.Dialog("","","lg");this.el=j.modal,j.body.append(this.info_bar.el);var k=this.screen.attributes.readonly||this.screen.group.get_readonly();if("form"==d&&j.footer.append(jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext(!c.new_&&this.screen.current_record.id<0?"Delete":"Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this))),c.new_&&this.many&&j.footer.append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("New")).click(function(){this.response("RESPONSE_ACCEPT")}.bind(this))),j.footer.append(this.save_current?jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Save")):jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK"))),j.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),"tree"==d){var l=jQuery("<div/>").appendTo(j.body),m=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(l);this.wid_text=jQuery("<input/>",{type:"input"}).appendTo(l),this.wid_text.hide();var n=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(m),o=Sao.common.MODELACCESS.get(this.screen.model_name);this.domain&&(this.wid_text.show(),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(n),this.but_add.click(this.add.bind(this)),this.but_add.prop("disabled",!o.read||k),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(n),this.but_remove.click(this.remove.bind(this)),this.but_remove.prop("disabled",!o.read||k)),this.but_new=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("New")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-edit"})).appendTo(n),this.but_new.click(this.new_.bind(this)),this.but_new.prop("disabled",!o.create||k),this.but_del=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Delete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-trash"})).appendTo(n),this.but_del.click(this.delete_.bind(this)),this.but_del.prop("disabled",!o["delete"]||k),this.but_undel=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Undelete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-repeat"})).appendTo(n),this.but_undel.click(this.undelete.bind(this)),this.but_undel.prop("disabled",!o["delete"]||k),this.but_previous=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-left"})).appendTo(n),this.but_previous.click(this.previous.bind(this)),this.label=jQuery("<span/>",{"class":"btn"}).appendTo(n),this.label.text("(0, 0)"),this.but_next=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-right"})).appendTo(n),this.but_next.click(this.next.bind(this)),this.but_switch=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Switch")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-list-alt"})).appendTo(n),this.but_switch.click(this.switch_.bind(this))}i.done(function(){j.add_title(this.screen.current_view.attributes.string),j.body.append(this.screen.screen_container.alternate_viewport),this.el.modal("show")}.bind(this)),this.el.on("shown.bs.modal",function(a){this.screen.display().done(function(){this.screen.set_cursor()}.bind(this))}.bind(this)),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},add:function(){var a=jQuery.extend([],this.domain),b=this.screen.model_name,c=this.wid_text.val(),d=function(a){var b=jQuery.when();if(!jQuery.isEmptyObject(a)){for(var c=[],d=0,e=a.length;e>d;d++)c.push(a[d][0]);this.screen.group.load(c,!0),b=this.screen.display()}b.done(function(){this.screen.set_cursor()}.bind(this)),this.entry.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(b,d,{sel_multi:!0,context:this.context,domain:a,search_filter:e.quote(c)})},remove:function(){this.screen.remove(!1,!0,!1)},new_:function(){this.screen.new_()},delete_:function(){this.screen.remove(!1,!1,!1)},undelete:function(){this.screen.unremove()},previous:function(){this.screen.display_previous()},next:function(){this.screen.display_next()},switch_:function(){this.screen.switch_view()},response:function(a){var b;this.screen.current_view.set_value();var c=this.screen.group.get_readonly();if(~["RESPONSE_OK","RESPONSE_ACCEPT"].indexOf(a)&&!c&&this.screen.current_record)return void this.screen.current_record.validate().then(function(a){return a&&this.screen.attributes.pre_validate?this.screen.current_record.pre_validate():a}.bind(this)).then(function(c){var d=jQuery.Deferred();if(c&&this.save_current)this.screen.save_current().then(d.resolve,d.reject);else if(c&&"form"==this.screen.current_view.view_type){var e=this.screen.current_view,f=[];for(var g in e.widgets){var h=e.widgets[g];if(h.screen&&h.screen.attributes.pre_validate){var i=h.screen.current_record;i&&f.push(i.pre_validate())}}jQuery.when.apply(jQuery,f).then(d.resolve,d.reject)}else c?(this.info_bar.message(),d.resolve()):(this.info_bar.message(this.screen.invalid_message(),"danger"),d.reject());d.fail(function(){this.screen.display().done(function(){this.screen.set_cursor()}.bind(this))}.bind(this)),d.done(function(){"RESPONSE_ACCEPT"==a?(this.screen.new_(),this.screen.current_view.display().done(function(){this.screen.set_cursor()}.bind(this)),this.many-=1,0===this.many&&this.but_new.prop("disabled",!0)):(b=!0,this.callback(b),this.destroy())}.bind(this))}.bind(this));if("RESPONSE_CANCEL"==a&&!c&&this.screen.current_record){if(b=!1,this.screen.current_record.id<0||this.save_current)this.screen.group.remove(this.screen.current_record,!0);else if(this.screen.current_record.has_changed())return this.screen.current_record.cancel(),void this.screen.current_record.reload().always(function(){this.callback(b),this.destroy()}.bind(this))}else b="RESPONSE_CANCEL"!=a;this.callback(b),this.destroy()},destroy:function(){this.screen.screen_container.alternate_view=!1,this.screen.screen_container.alternate_viewport.children().detach(),this.prev_view&&this.screen.switch_view(this.prev_view.view_type),this.el.modal("hide")}}),Sao.Window.Attachment=Sao.class_(Sao.Window.Form,{init:function(a,b){this.resource=a.model.name+","+a.id,this.attachment_callback=b;var c=jQuery.extend({},a.get_context());c.resource=this.resource;var d=new Sao.Screen("ir.attachment",{domain:[["resource","=",this.resource]],mode:["tree","form"],context:c,exclude_field:"resource"});d.switch_view().done(function(){d.search_filter(),d.group.parent=a}),Sao.Window.Attachment._super.init.call(this,d,this.callback,{view_type:"tree"})},callback:function(a){a&&this.screen.group.save(),this.attachment_callback&&this.attachment_callback()}}),Sao.Window.Search=Sao.class_(Object,{init:function(a,b,c){c=c||{};var d=c.views_preload||{};this.model_name=a,this.domain=c.domain||[],this.context=c.context||{},this.sel_multi=c.sel_multi,this.callback=b;var e=new Sao.Dialog("Search","","lg");this.el=e.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Find")).click(function(){this.response("RESPONSE_APPLY")}.bind(this)).appendTo(e.footer),c.new_&&Sao.common.MODELACCESS.get(a).create&&jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("New")).click(function(){this.response("RESPONSE_ACCEPT")}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(e.footer),e.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),this.screen=new Sao.Screen(a,{mode:["tree"],context:this.context,domain:this.domain,view_ids:c.view_ids,views_preload:d,row_activate:this.activate.bind(this)}),this.screen.load_next_view().done(function(){this.screen.switch_view().done(function(){e.body.append(this.screen.screen_container.el),this.el.modal("show"),this.screen.display(),void 0!==c.search_filter&&this.screen.search_filter(c.search_filter)}.bind(this))}.bind(this)),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},activate:function(){this.response("RESPONSE_OK")},response:function(a){var b,c=[];if("RESPONSE_OK"==a)b=this.screen.current_view.selected_records();else{if("RESPONSE_APPLY"==a)return void this.screen.search_filter();if("RESPONSE_ACCEPT"==a){var d=new Sao.Screen(this.model_name,{domain:this.domain,context:this.context,mode:["form"]}),e=function(a){a?d.save_current().then(function(){var a=d.current_record;this.callback([[a.id,a._values.rec_name||""]])}.bind(this),function(){this.callback(null)}.bind(this)):this.callback(null)};return this.el.modal("hide"),void new Sao.Window.Form(d,e.bind(this),{new_:!0})}}if(b){var f,g;for(f in b)g=b[f],c.push([g.id,g._values.rec_name||""])}this.callback(c),this.el.modal("hide")}}),Sao.Window.Preferences=Sao.class_(Object,{init:function(a){this.callback=a;var b=new Sao.Dialog("Preferences","","lg");this.el=b.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(b.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(b.footer),b.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),this.screen=new Sao.Screen("res.user",{mode:[]}),this.screen.attributes.readonly=!1,this.screen.group.set_readonly(!1),this.screen.group.skip_model_access=!0;var c=function(a){this.screen.add_view(a),this.screen.switch_view().done(function(){this.screen.new_(!1),this.screen.model.execute("get_preferences",[!1],{}).then(d.bind(this),this.destroy)}.bind(this))},d=function(a){this.screen.current_record.set(a),this.screen.current_record.id=this.screen.model.session.user_id,this.screen.current_record.validate(null,!0).then(function(){this.screen.display(!0)}.bind(this)),b.body.append(this.screen.screen_container.el),this.el.modal("show")};this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()}),this.screen.model.execute("get_preferences_fields_view",[],{}).then(c.bind(this),this.destroy)},response:function(a){var b=function(){this.destroy(),this.callback()}.bind(this),c=jQuery.when();"RESPONSE_OK"==a&&(c=this.screen.current_record.validate().then(function(a){if(a){var b=jQuery.extend({},this.screen.get()),c=function(a){return this.screen.model.execute("set_preferences",[b,a],{})}.bind(this);return"password"in b?Sao.common.ask.run("Current Password:",!1).then(function(a){return c(a)}):c(!1)}}.bind(this))),c.done(b)},destroy:function(){this.el.modal("hide")}}),Sao.Window.Revision=Sao.class_(Object,{init:function(a,b){this.callback=b;var c=new Sao.Dialog(Sao.i18n.gettext("Revision"),"","lg");this.el=c.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(c.footer),c.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this));var d=jQuery("<div/>",{"class":"form-group"}).appendTo(c.body);jQuery("<label/>",{"for":"revision",text:"Revision"}).appendTo(d),this.select=jQuery("<select/>",{"class":"form-control",id:"revision",placeholder:Sao.i18n.gettext("Revision")}).appendTo(d);var e=Sao.common.date_format(),f="%H:%M:%S.%f";this.select.append(jQuery("<option/>",{value:null,text:""})),a.forEach(function(a){var b=a[2];a=a[0],this.select.append(jQuery("<option/>",{value:a.valueOf(),text:Sao.common.format_datetime(e,f,a)+" "+b}))}.bind(this)),this.el.modal("show"),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},response:function(a){var b=null;"RESPONSE_OK"==a&&(b=this.select.val(),b&&(b=Sao.DateTime(parseInt(b,10)))),this.el.modal("hide"),this.callback(b)}})}(),function(){"use strict";Sao.Wizard=Sao.class_(Object,{init:function(a){this.widget=jQuery("<div/>",{"class":"wizard"}),this.name=a,this.action_id=null,this.id=null,this.ids=null,this.action=null,this.context=null,this.states={},this.session_id=null,this.start_state=null,this.end_state=null,this.screen=null,this.screen_state=null,this.state=null,this.session=Sao.Session.current_session,this.__processing=!1,this.__waiting_response=!1,this.info_bar=new Sao.Window.InfoBar},run:function(a){this.action=a.action,this.action_id=a.data.action_id,this.id=a.data.id,this.ids=a.data.ids,this.model=a.data.model,this.context=a.context,Sao.rpc({method:"wizard."+this.action+".create",params:[this.session.context]},this.session).then(function(a){this.session_id=a[0],this.start_state=this.state=a[1],this.end_state=a[2],this.process()}.bind(this))},process:function(){if(!this.__processing&&!this.__waiting_response){var a=function(){if(this.state==this.end_state)return void this.end();var a=jQuery.extend({},this.context);a.active_id=this.id,a.active_ids=this.ids,a.active_model=this.model,a.action_id=this.action_id;var b={};this.screen&&(b[this.screen_state]=this.screen.get_on_change_value()),Sao.rpc({method:"wizard."+this.action+".execute",params:[this.session_id,b,this.state,a]},this.session).then(function(a){if(a.view){this.clean();var b=a.view;this.update(b.fields_view,b.defaults,b.buttons),this.screen_state=b.state,this.__waiting_response=!0}else this.state=this.end_state;var c=function(){a.actions&&a.actions.forEach(function(a){Sao.Action.exec_action(a[0],a[1],jQuery.extend({},this.context))}.bind(this))}.bind(this);this.state==this.end_state?this.end().then(c):c(),this.__processing=!1}.bind(this),function(a){this.__processing=!1}.bind(this))};a.call(this)}},destroy:function(){},end:function(){return Sao.rpc({method:"wizard."+this.action+".delete",params:[this.session_id,this.session.context]},this.session)},clean:function(){this.widget.children().remove(),this.states={}},response:function(a){return this.__waiting_response=!1,this.screen.current_view.set_value(),this.screen.current_record.validate().then(function(b){return b||a==this.end_state?(this.info_bar.message(),this.state=a,void this.process()):(this.screen.display(!0),void this.info_bar.message(this.screen.invalid_message(),"danger"))}.bind(this))},_get_button:function(a){var b=new Sao.common.Button(a);return this.states[a.state]=b,a["default"]?b.el.addClass("btn-primary"):a.state==this.end_state&&b.el.addClass("btn-link"),b},update:function(a,b,c){c.forEach(function(a){this._get_button(a)}.bind(this)),this.screen=new Sao.Screen(a.model,{mode:[],context:this.context}),this.screen.add_view(a),this.screen.switch_view(),this.widget.append(this.screen.screen_container.el),this.screen.new_(!1),this.screen.current_record.set_default(b),this.screen.set_cursor()}}),Sao.Wizard.create=function(a){var b;if(a.window){b=new Sao.Wizard.Form(a.name);var c=new Sao.Tab.Wizard(b);Sao.Tab.add(c)}else b=new Sao.Wizard.Dialog(a.name);b.run(a)},Sao.Wizard.Form=Sao.class_(Sao.Wizard,{init:function(a){Sao.Wizard.Form._super.init.call(this),this.tab=null,this.name=a,this.form=jQuery("<div/>",{"class":"wizard-form"}).append(this.widget),this.footer=jQuery("<div/>",{"class":"modal-footer"}).appendTo(this.form)},clean:function(){Sao.Wizard.Form._super.clean.call(this),this.footer.children().remove()},_get_button:function(a){var b=Sao.Wizard.Form._super._get_button.call(this,a);return this.footer.append(b.el),b.el.click(function(){this.response(a.state)}.bind(this)),b},end:function(){return Sao.Wizard.Form._super.end.call(this).always(function(){return this.tab.close()}.bind(this))}}),Sao.Wizard.Dialog=Sao.class_(Sao.Wizard,{init:function(a){a||(a=Sao.i18n.gettext("Wizard")),Sao.Wizard.Dialog._super.init.call(this);var b=new Sao.Dialog(a,"wizard-dialog","lg");this.dialog=b.modal,this.content=b.content,this.footer=b.footer,b.body.append(this.info_bar.el).append(this.widget)},clean:function(){Sao.Wizard.Dialog._super.clean.call(this),this.footer.children().remove()},_get_button:function(a){var b=Sao.Wizard.Dialog._super._get_button.call(this,a);return this.footer.append(b.el),a["default"]?(this.content.unbind("submit"),this.content.submit(function(b){this.response(a.state),b.preventDefault()}.bind(this)),b.el.attr("type","submit")):b.el.click(function(){this.response(a.state)}.bind(this)),b},update:function(a,b,c){this.content.unbind("submit"),Sao.Wizard.Dialog._super.update.call(this,a,b,c),this.dialog.modal("show")},destroy:function(a){Sao.Wizard.Dialog._super.destroy.call(this),this.dialog.on("hidden.bsd.modal",function(a){jQuery(this).remove()}),this.dialog.modal("hide");var b,c=jQuery(".wizard-dialog").filter(":visible")[0],d=!1;if(c||(c=Sao.Tab.tabs.get_current(),c?c.screen&&c.screen.model_name!=this.model&&(d=!0,b=Sao.main_menu_screen):(d=!0,b=Sao.main_menu_screen)),c&&c.screen&&(b=c.screen),b){if(b.current_record&&!d){var e;e=b.model_name==this.model?this.ids:[b.current_record.id],b.reload(e,!0)}a&&b.client_action(a)}},end:function(){return Sao.Wizard.Dialog._super.end.call(this).then(this.destroy.bind(this))},show:function(){this.dialog.modal("show")},hide:function(){this.dialog.modal("hide")},state_changed:function(){this.process()}})}(),function(){"use strict";Sao.View.Board=Sao.class_(Object,{init:function(a,b){var c,d,e,f;this.context=b,this.widgets=[],this.actions=[],this.el=jQuery("<div/>",{"class":"board"}),c={},e=a.children()[0];for(var g=0,h=e.attributes.length;h>g;g++)d=e.attributes[g],c[d.name]=d.value;for(this.attributes=c,this.el.append(this.parse(e).el),f=[],g=0,h=this.actions.length;h>g;g++)f.push(this.actions[g].action_prm);this.actions_prms=jQuery.when.apply(jQuery,f)},_parse_node:function(a,b,c){switch(a.tagName){case"image":break;case"separator":this._parse_separator(a,b,c);break;case"label":this._parse_label(a,b,c);break;case"newline":b.add_row();break;case"notebook":this._parse_notebook(a,b,c);break;case"page":this._parse_page(a,b,c);break;case"group":this._parse_group(a,b,c);break;case"hpaned":this._parse_pane(a,b,c,"horizontal");break;case"vpaned":this._parse_pane(a,b,c,"vertical");break;case"child":this._parse_child(a,b,c);break;case"action":this._parse_action(a,b,c)}},parse:function(a,b){var c;return b||(b=new Sao.View.Form.Container(Number(a.getAttribute("col")||4))),c=function(a,c){var d,e,f,g;for(d={},f=0,g=c.attributes.length;g>f;f++)e=c.attributes[f],d[e.name]=e.value;["yexpand","yfill","xexpand","xfill","colspan","position"].forEach(function(a){d[a]&&(d[a]=Number(d[a]))}),this._parse_node(c,b,d)},jQuery(a).children().each(c.bind(this)),b},_parse_separator:function(a,b,c){var d,e;d=c.string,e=new Sao.view.Form.Separator(d,c),b.add(c,e)},_parse_label:function(a,b,c){var d,e;return(d=c.string)?(e=new Sao.View.Form.Label(d,c),void b.add(c,e)):void b.add(c)},_parse_notebook:function(a,b,c){var d;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),d=new Sao.View.Form.Notebook(c),b.add(c,d),this.parse(a,b)},_parse_page:function(a,b,c){var d;d=c.string,page=this.parse(a,b),page=new Sao.View.Form.Page(b.add(page.el,d),c)},_parse_group:function(a,b,c){var d;d=new Sao.View.Form.Group(c),b.add(c,d)},_parse_pane:function(a,b,c,d){var e;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),e=new Sao.common.Paned(d),b.add(c,e),this.parse(a,e)},_parse_child:function(a,b,c){var d,e,f;d=this.parse(a),e=b.get_child1(),e.children().length>0?(f=b.get_child2(),f.append(d.el)):e.append(d.el)},_parse_action:function(a,b,c){var d;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),d=new Sao.View.Board.Action(c,this.context),this.actions.push(d),b.add(c,d)},reload:function(){for(var a=0;a<this.actions.length;a++)this.actions[a].display()}}),Sao.View.Board.Action=Sao.class_(Object,{init:function(a,b){var c,d;this.name=a.name,this.context=b||{},c=new Sao.Model("ir.action.act_window"),this.action_prm=c.execute("get",[this.name],this.context),this.action_prm.done(function(b){var c,e,f,g,h,i;if(this.action=b,this.action.mode=[],f=[],(this.action.views||[]).length>0)for(c=0,e=this.action.views.length;e>c;c++)f.push(this.action.views[c][0]),this.action.mode.push(this.action.views[c][1]);else void 0!==this.action.view_id&&(f=[this.action.view_id[0]]);"mode"in a&&(this.action.mode=a.mode),"pyson_domain"in this.action||(this.action.pyson_domain="[]"),jQuery.extend(this.context,Sao.Session.current_session.context),this.context._user=Sao.Session.current_session.user_id,g=new Sao.PYSON.Decoder(this.context),jQuery.extend(this.context,g.decode(this.action.pyson_context||"{}")),g=new Sao.PYSON.Decoder(this.context),jQuery.extend(this.context,g.decode(this.action.pyson_context||"{}")),this.domain=[],this.update_domain([]),h=jQuery.extend({},this.context),h.context=this.context,h._user=Sao.Session.current_session.user_id,g=new Sao.PYSON.Decoder(h),d=g.decode(this.action.pyson_search_value||"{}"),i={mode:this.action.mode,context:this.context,view_ids:f,domain:this.domain,search_value:d,row_activate:this.row_activate.bind(this)},this.screen=new Sao.Screen(this.action.res_model,i),this.title.html(a.string?a.string:void 0!==this.action.window_name?this.action.name:this.screen.current_view.title),this.screen.switch_view().done(function(){this.body.append(this.screen.screen_container.el),this.screen.search_filter()}.bind(this))}.bind(this)),this.el=jQuery("<div/>",{"class":"board-action panel panel-default"}),this.title=jQuery("<div/>",{"class":"panel-heading"}),this.el.append(this.title),this.body=jQuery("<div/>",{"class":"panel-body"}),this.el.append(this.body)},row_activate:function(){var a,b;this.screen.current_record&&("tree"==this.screen.current_view.view_type&&"keyword_open"in this.screen.current_view.attributes?(a=this.screen.current_view.selected_records().map(function(a){return a.id}),Sao.Action.exec_keyword("tree_open",{model:this.screen.model_name,id:this.screen.current_record.id,ids:a},jQuery.extend({},this.context),!1)):b=new Sao.Window.Form(this.screen,function(a){a?this.screen.current_record.save():this.screen.current_record.cancel()}.bind(this)))},set_value:function(){},display:function(){this.screen.search_filter(this.screen.screen_container.get_text())},get_active:function(){return this.screen&&this.screen.current_record?Sao.common.EvalEnvironment(this.screen.current_record):void 0},update_domain:function(a){var b,c,d,e,f,g;for(e=jQuery.extend({},this.context),e.context=e,e._user=Sao.Session.current_session.user_id,b=0,c=a.length;c>b;b++)d=a[b].get_active(),d&&(e[a[b].name]=d);f=new Sao.PYSON.Decoder(e),g=f.decode(this.action.pyson_domain),Sao.common.compare(this.domain,g)||(this.domain.splice(0,this.domain.length),jQuery.extend(this.domain,g),this.screen&&this.display())}})}();
\ No newline at end of file
+var Sao={};!function(){"use strict";function a(){var a=1040;jQuery(".modal.in").each(function(b){var c=jQuery(this);a++,c.css("zIndex",a),c.next(".modal-backdrop.in").addClass("hidden").css("zIndex",a-1)}),jQuery(".modal.in:visible:last").focus().next(".modal-backdrop.in").removeClass("hidden")}"contains"in String.prototype||(String.prototype.contains=function(a,b){return-1!==String.prototype.indexOf.call(this,a,b)}),String.prototype.startsWith||Object.defineProperty(String.prototype,"startsWith",{enumerable:!1,configurable:!1,writable:!1,value:function(a,b){return b=b||0,this.indexOf(a,b)===b}}),String.prototype.endsWith||Object.defineProperty(String.prototype,"endsWith",{enumerable:!1,configurable:!1,writable:!1,value:function(a,b){b=b||this.length,b-=a.length;var c=this.lastIndexOf(a);return-1!==c&&c===b}}),Array.prototype.some||(Array.prototype.some=function(a){if(null===this)throw new TypeError;var b,c,d=Object(this),e=d.length>>>0;if("function"!=typeof a)throw new TypeError;for(b=arguments[1],c=0;e>c;c++)if(c in d&&a.call(b,d[c],c,d))return!0;return!1});try{document.execCommand("styleWithCSS",!1,!1)}catch(b){}try{document.execCommand("useCSS",!1,!0)}catch(b){}jQuery.fn.extend({uniqueId:function(){var a=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})}}()}),Sao.class_=function(a,b){var c=function(){if(!(this instanceof c))throw new Error("Constructor function requires new operator");this.Class=c,this.init&&this.init.apply(this,arguments)};if(c.prototype=Object.create(a.prototype),c._super=a.prototype,b)for(var d in b)c.prototype[d]=b[d];return c},Sao.Decimal=Number,Sao.Date=function(a,b,c){var d=moment();return d.year(a),d.month(b),d.date(c),d.set({hour:0,minute:0,second:0,millisecond:0}),d.isDate=!0,d},Sao.Date.min=moment(new Date(-864e13)),Sao.Date.min.set({hour:0,minute:0,second:0,millisecond:0}),Sao.Date.min.isDate=!0,Sao.Date.max=moment(new Date(864e13)),Sao.Date.max.set({hour:0,minute:0,second:0,millisecond:0}),Sao.Date.max.isDate=!0,Sao.DateTime=function(a,b,c,d,e,f,g,h){var i=moment();return h&&i.utc(),i.year(a),i.month(b),i.date(c),i.hour(d||0),i.minute(e||0),i.second(f||0),i.milliseconds(g||0),i.isDateTime=!0,i.local(),i},Sao.DateTime.combine=function(a,b){var c=a.clone();return c.set({hour:b.hour(),minute:b.minute(),second:b.second(),millisecond:b.millisecond()}),c},Sao.DateTime.min=moment(new Date(-864e13)).local(),Sao.DateTime.min.isDateTime=!0,Sao.DateTime.max=moment(new Date(864e13)).local(),Sao.DateTime.max.isDateTime=!0,Sao.Time=function(a,b,c,d){var e=moment({hour:a,minute:b,second:c,millisecond:d||0});return e.isTime=!0,e},Sao.TimeDelta=function(a,b,c,d,e,f){var g=moment.duration({days:a,seconds:b,milliseconds:c,minutes:d,hours:e,weeks:f});return g.isTimeDelta=!0,g},Sao.config={},Sao.config.limit=1e3,Sao.config.display_size=20,Sao.config.roundup={},Sao.config.roundup.url="http://bugs.tryton.org/roundup/",Sao.i18n=i18n(),Sao.i18n.setlang=function(a){return Sao.i18n.setLocale(a),jQuery.getJSON("locale/"+a+".json",function(b){b[""].language||(b[""].language=a),b[""]["plural-forms"]||(b[""]["plural-forms"]="nplurals=2; plural=(n!=1);");for(var c in b)""!==c&&(b[c]=2==b[c].length?b[c][1]:b[c].slice(1));Sao.i18n.loadJSON(b)})},Sao.i18n.setlang((navigator.language||navigator.browserLanguge||navigator.userLanguage||"en_US").replace("-","_")),Sao.get_preferences=function(){var a=Sao.Session.current_session;return a.reload_context().then(function(){return Sao.rpc({method:"model.res.user.get_preferences",params:[!1,{}]},a).then(function(a){var b=[];return b.push(Sao.common.MODELACCESS.load_models()),b.push(Sao.common.ICONFACTORY.load_icons()),b.push(Sao.common.MODELHISTORY.load_history()),b.push(Sao.common.VIEW_SEARCH.load_searches()),jQuery.when.apply(jQuery,b).then(function(){(a.actions||[]).forEach(function(a){Sao.Action.execute(a,{},null,{})});var b="Tryton";jQuery.isEmptyObject(a.status_bar)||(b+=" - "+a.status_bar),document.title=b;var c=a.language!=Sao.i18n.getLocale(),d=jQuery.Deferred();return Sao.i18n.setlang(a.language).always(function(){c&&Sao.user_menu(a),d.resolve(a)}),d})})})},Sao.login=function(){Sao.Session.get_credentials().then(function(a){return Sao.Session.current_session=a,a.reload_context()}).then(Sao.get_preferences).then(function(a){Sao.menu(a),Sao.user_menu(a)})},Sao.logout=function(){var a=Sao.Session.current_session;Sao.Tab.tabs.close(!0).done(function(){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#menu").children().remove(),document.title="Tryton",a.do_logout().always(Sao.login)})},Sao.preferences=function(){Sao.Tab.tabs.close(!0).done(function(){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#menu").children().remove(),new Sao.Window.Preferences(function(){Sao.get_preferences().then(function(a){Sao.menu(a),Sao.user_menu(a)})})})},Sao.user_menu=function(a){jQuery("#user-preferences").children().remove(),jQuery("#user-logout").children().remove(),jQuery("#user-preferences").append(jQuery("<a/>",{href:"#"}).click(Sao.preferences).append(a.status_bar)),jQuery("#user-logout").append(jQuery("<a/>",{href:"#"}).click(Sao.logout).append(Sao.i18n.gettext("Logout")))},Sao.menu=function(a){var b=new Sao.PYSON.Decoder,c=b.decode(a.pyson_menu),d=!1;jQuery.isEmptyObject(c.views)?c.view_id&&(d=[c.view_id[0]]):d=c.views.map(function(a){return a[0]}),b=new Sao.PYSON.Decoder(Sao.Session.current_session.context);var e=b.decode(c.pyson_context||"{}"),f=b.decode(c.pyson_domain),g=new Sao.Tab.Form(c.res_model,{mode:["tree"],view_ids:d,domain:f,context:e,selection_mode:Sao.common.SELECTION_NONE});Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(g),1),g.view_prm.done(function(){Sao.main_menu_screen=g.screen;var a=g.screen.current_view;a.table.find("thead").hide(),jQuery("#menu").children().remove();var b=new Sao.GlobalSearch;jQuery("#menu").append(b.el),jQuery("#menu").append(g.screen.screen_container.content_box.detach())})},Sao.main_menu_screen=null,Sao.Dialog=Sao.class_(Object,{init:function(a,b,c){c=c||"sm",this.modal=jQuery("<div/>",{"class":b+" modal fade",role:"dialog"}),this.content=jQuery("<form/>",{"class":"modal-content"}).appendTo(jQuery("<div/>",{"class":"modal-dialog modal-"+c}).appendTo(this.modal)),this.header=jQuery("<div/>",{"class":"modal-header"}).appendTo(this.content),a&&this.add_title(a),this.body=jQuery("<div/>",{"class":"modal-body"}).appendTo(this.content),this.footer=jQuery("<div/>",{"class":"modal-footer"}).appendTo(this.content)},add_title:function(a){this.header.append(jQuery("<h4/>",{"class":"modal-title"}).append(a))}}),Sao.GlobalSearch=Sao.class_(Object,{init:function(){this.el=jQuery("<div/>",{"class":"global-search-container"}),this.search_entry=jQuery("<input>",{"class":"form-control",placeholder:Sao.i18n.gettext("Search...")}),this.el.append(this.search_entry);new Sao.common.InputCompletion(this.search_entry,this.update.bind(this),this.match_selected.bind(this),this.format.bind(this))},format:function(a){var b=jQuery("<div/>"),c=jQuery("<img/>",{"class":"global-search-icon"}).appendTo(b);return Sao.common.ICONFACTORY.register_icon(a.icon).then(function(a){c.attr("src",a)}),jQuery("<span/>",{"class":"global-search-text"}).text(a.record_name).appendTo(b),b},update:function(a){var b=new Sao.Model("ir.model");return b.execute("global_search",[a,Sao.config.limit,Sao.main_menu_screen.model_name],Sao.main_menu_screen.context).then(function(a){for(var b=[],c=0,d=a.length;d>c;c++)b.push({model:a[c][1],model_name:a[c][2],record_id:a[c][3],record_name:a[c][4],icon:a[c][5]});return b}.bind(this))},match_selected:function(a){if(a.model==Sao.main_menu_screen.model_name)Sao.Action.exec_keyword("tree_open",{model:a.model,id:a.record_id,ids:[a.record_id]},Sao.main_menu_screen.context);else{var b={model:a.model,res_id:a.record_id,mode:["form","tree"]};Sao.Tab.create(b)}this.search_entry.val("")}}),jQuery(document).on("show.bs.modal",".modal",function(a){jQuery(this).appendTo(jQuery("body"))}).on("shown.bs.modal",".modal.in",function(b){a()}).on("hidden.bs.modal",".modal",function(b){a()})}(),function(){"use strict";Sao.rpc=function(a,b){var c=jQuery.Deferred();b||(b=new Sao.Session);var d=jQuery.extend([],a.params);d.push(jQuery.extend({},b.context,d.pop()));var e=Sao.common.processing.show(),f=jQuery.ajax({contentType:"application/json",data:JSON.stringify(Sao.rpc.prepareObject({method:a.method,params:[b.user_id,b.session].concat(d)})),dataType:"json",url:"/"+(b.database||""),type:"post",complete:[function(){Sao.common.processing.hide(e)}]}),g=function(d){if(null===d)Sao.common.warning.run("",Sao.i18n.gettext("Unable to reach the server")),c.reject();else if(d.error){var e,f,g;if("UserWarning"==d.error[0])return e=d.error[1],f=d.error[2],g=d.error[3],void Sao.common.userwarning.run(g,f).done(function(d){return~["always","ok"].indexOf(d)?void Sao.rpc({method:"model.res.user.warning.create",params:[[{user:b.user_id,name:e,always:"always"==d}],{}]},b).done(function(){Sao.rpc(a,b).then(c.resolve,c.reject)}):void c.reject()});if("UserError"==d.error[0])return f=d.error[1],g=d.error[2],void Sao.common.warning.run(f,g).always(c.reject);if("ConcurrencyException"==d.error[0]){if(a.method.startsWith("model.")&&(a.method.endsWith(".write")||a.method.endsWith(".delete"))){var h=a.method.split(".").slice(1,-1).join(".");return void Sao.common.concurrency.run(h,a.params[0][0],a.params.slice(-1)[0]).then(function(){delete a.params.slice(-1)[0]._timestamp,Sao.rpc(a,b).then(c.resolve,c.reject)},c.reject)}return void Sao.common.message.run("Concurrency Exception","glyphicon-alert").always(c.reject)}if("NotLogged"==d.error[0])return void Sao.Session.renew(b).then(function(){Sao.rpc(a,b).then(c.resolve,c.reject)},c.reject);Sao.common.error.run(d.error[0],d.error[1]),c.reject()}else c.resolve(d.result)},h=function(a,b,d){Sao.common.error.run(b,d),c.reject()};return f.success(g),f.error(h),c.promise()},Sao.rpc.convertJSONObject=function(a,b,c){if(a instanceof Array)for(var d=0,e=a.length;e>d;d++)Sao.rpc.convertJSONObject(a[d],d,a);else if("string"!=typeof a&&"number"!=typeof a&&null!==a)if(a&&a.__class__){switch(a.__class__){case"datetime":a=Sao.DateTime(a.year,a.month-1,a.day,a.hour,a.minute,a.second,a.microsecond/1e3,!0);break;case"date":a=Sao.Date(a.year,a.month-1,a.day);break;case"time":a=Sao.Time(a.hour,a.minute,a.second,a.microsecond/1e3);break;case"timedelta":a=Sao.TimeDelta(null,a.seconds);break;case"bytes":for(var f=atob(a.base64.replace(/\s/g,"")),g=new ArrayBuffer(f.length),h=new Uint8Array(g),i=0;i<f.length;i++)h[i]=f.charCodeAt(i);a=h;break;case"Decimal":a=new Sao.Decimal(a.decimal)}c&&(c[b]=a)}else for(var j in a)Sao.rpc.convertJSONObject(a[j],j,a);return c||a},Sao.rpc.prepareObject=function(a,b,c){if(a instanceof Array)for(var d=0,e=a.length;e>d;d++)Sao.rpc.prepareObject(a[d],d,a);else if("string"!=typeof a&&"number"!=typeof a&&null!==a&&void 0!==a)if(a.isDate)a={__class__:"date",year:a.year(),month:a.month()+1,day:a.date()},c&&(c[b]=a);else if(a.isDateTime)a=a.clone(),a={__class__:"datetime",year:a.utc().year(),month:a.utc().month()+1,day:a.utc().date(),hour:a.utc().hour(),minute:a.utc().minute(),second:a.utc().second(),microsecond:1e3*a.utc().millisecond()},c&&(c[b]=a);else if(a.isTime)a={__class__:"time",hour:a.hour(),minute:a.minute(),second:a.second(),microsecond:1e3*a.millisecond()},c&&(c[b]=a);else if(a.isTimeDelta)a={__class__:"timedelta",seconds:a.asSeconds()},c&&(c[b]=a);else if(a instanceof Sao.Decimal)a={__class__:"Decimal",decimal:a.toString()},c&&(c[b]=a);else if(a instanceof Uint8Array)a={__class__:"bytes",base64:btoa(String.fromCharCode.apply(null,a))},c&&(c[b]=a);else for(var f in a)Sao.rpc.prepareObject(a[f],f,a);return c||a},jQuery.ajaxSetup({converters:{"text json":function(a){return Sao.rpc.convertJSONObject(jQuery.parseJSON(a))}}})}(),function(){"use strict";Sao.PYSON={},Sao.PYSON.PYSON=Sao.class_(Object,{init:function(){},pyson:function(){throw"NotImplementedError"},types:function(){throw"NotImplementedError"}}),Sao.PYSON.PYSON.eval_=function(a,b){throw"NotImplementedError"},Sao.PYSON.Encoder=Sao.class_(Object,{encode:function(a){return JSON.stringify(a,function(a,b){return b instanceof Sao.PYSON.PYSON?b.pyson():b._isAMomentObject?b.isDate?Sao.PYSON.Date(b.getFullYear(),b.getMonth(),b.getDate()).pyson():Sao.PYSON.DateTime(b.getFullYear(),b.getMonth(),b.getDate(),b.getHours(),b.getMinutes(),b.getSeconds(),b.getMilliseconds()).pyson():b})}}),Sao.PYSON.Decoder=Sao.class_(Object,{init:function(a){this.__context=a||{}},decode:function(a){var b=function(a,b){if("object"==typeof b&&null!==b){var c=Sao.PYSON[b.__class__];if(c)return c.eval_(b,this.__context)}return b};return JSON.parse(a,b.bind(this))}}),Sao.PYSON.Eval=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){void 0===b&&(b=""),Sao.PYSON.Eval._super.init.call(this),this._value=a,this._default=b},pyson:function(){return{__class__:"Eval",v:this._value,d:this._default}},types:function(){return this._default instanceof Sao.PYSON.PYSON?this._default.types():[typeof this._default]}}),Sao.PYSON.Eval.eval_=function(a,b){return a.v in b?b[a.v]:a.d},Sao.PYSON.Not=Sao.class_(Sao.PYSON.PYSON,{init:function(a){if(Sao.PYSON.Not._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["boolean"]).length||jQuery(["boolean"]).not(a.types()).length)throw"value must be boolean"}else if("boolean"!=typeof a)throw"value must be boolean";this._value=a},pyson:function(){return{__class__:"Not",v:this._value}},types:function(){return["boolean"]}}),Sao.PYSON.Not.eval_=function(a,b){return!a.v},Sao.PYSON.Bool=Sao.class_(Sao.PYSON.PYSON,{init:function(a){Sao.PYSON.Bool._super.init.call(this),this._value=a},pyson:function(){return{__class__:"Bool",v:this._value}},types:function(){return["boolean"]}}),Sao.PYSON.Bool.eval_=function(a,b){return a.v instanceof Object?!jQuery.isEmptyObject(a.v):Boolean(a.v)},Sao.PYSON.And=Sao.class_(Sao.PYSON.PYSON,{init:function(a){void 0===a&&(a=[]),Sao.PYSON.And._super.init.call(this);for(var b=0,c=a.length;c>b;b++){var d=a[b];if(d instanceof Sao.PYSON.PYSON){if(jQuery(d.types()).not(["boolean"]).length||jQuery(["boolean"]).not(d.types()).length)throw"statement must be boolean"}else if("boolean"!=typeof d)throw"statement must be boolean"}if(a.length<2)throw"must have at least 2 statements";this._statements=a},pyson:function(){return{__class__:"And",s:this._statements}},types:function(){return["boolean"]}}),Sao.PYSON.And.eval_=function(a,b){for(var c=!0,d=0,e=a.s.length;e>d;d++){var f=a.s[d];c=c&&f}return c},Sao.PYSON.Or=Sao.class_(Sao.PYSON.And,{pyson:function(){var a=Sao.PYSON.Or._super.pyson.call(this);return a.__class__="Or",a}}),Sao.PYSON.Or.eval_=function(a,b){for(var c=!1,d=0,e=a.s.length;e>d;d++){var f=a.s[d];c=c||f}return c},Sao.PYSON.Equal=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){Sao.PYSON.Equal._super.init.call(this);var c,d;if(c=a instanceof Sao.PYSON.PYSON?a.types():[typeof a],d=b instanceof Sao.PYSON.PYSON?b.types():[typeof b],jQuery(c).not(d).length||jQuery(d).not(c).length)throw"statements must have the same type";this._statement1=a,this._statement2=b},pyson:function(){return{__class__:"Equal",s1:this._statement1,s2:this._statement2}},types:function(){return["boolean"]}}),Sao.PYSON.Equal.eval_=function(a,b){return a.s1==a.s2},Sao.PYSON.Greater=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){Sao.PYSON.Greater._super.init.call(this);for(var d=[a,b],e=0;2>e;e++){var f=d[e];if(f instanceof Sao.PYSON.PYSON){if(jQuery(f).not(["number"]).length)throw"statement must be an integer or a float"}else if("number"!=typeof f)throw"statement must be an integer or a float"}if(void 0===c&&(c=!1),c instanceof Sao.PYSON.PYSON){if(jQuery(c.types()).not(["boolean"]).length||jQuery(["boolean"]).not(c.types()).length)throw"equal must be boolean"}else if("boolean"!=typeof c)throw"equal must be boolean";this._statement1=a,this._statement2=b,this._equal=c},pyson:function(){return{__class__:"Greater",s1:this._statement1,s2:this._statement2,e:this._equal}},types:function(){return["boolean"]}}),Sao.PYSON.Greater._convert=function(a){return a=jQuery.extend({},a),a.s1=Number(a.s1),a.s2=Number(a.s2),a},Sao.PYSON.Greater.eval_=function(a,b){return a=Sao.PYSON.Greater._convert(a),a.e?a.s1>=a.s2:a.s1>a.s2},Sao.PYSON.Less=Sao.class_(Sao.PYSON.Greater,{pyson:function(){var a=Sao.PYSON.Less._super.pyson.call(this);return a.__class__="Less",a}}),Sao.PYSON.Less._convert=Sao.PYSON.Greater._convert,Sao.PYSON.Less.eval_=function(a,b){return a=Sao.PYSON.Less._convert(a),a.e?a.s1<=a.s2:a.s1<a.s2},Sao.PYSON.If=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){if(Sao.PYSON.If._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["boolean"]).length||jQuery(["boolean"]).not(a.types()).length)throw"condition must be boolean"}else if("boolean"!=typeof a)throw"condition must be boolean";var d,e;if(d=b instanceof Sao.PYSON.PYSON?b.types():[typeof b],void 0===c&&(c=null),e=c instanceof Sao.PYSON.PYSON?c.types():[typeof c],jQuery(d).not(e).length||jQuery(e).not(d).length)throw"then and else statements must be the same type";this._condition=a,this._then_statement=b,this._else_statement=c},pyson:function(){return{__class__:"If",c:this._condition,t:this._then_statement,e:this._else_statement}},types:function(){return this._then_statement instanceof Sao.PYSON.PYSON?this._then_statement.types():[typeof this._then_statement]}}),Sao.PYSON.If.eval_=function(a,b){return a.c?a.t:a.e},Sao.PYSON.Get=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c){if(Sao.PYSON.Get._super.init.call(this),void 0===c&&(c=null),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["object"]).length||jQuery(["object"]).not(a.types()).length)throw"obj must be a dict"}else if(!(a instanceof Object))throw"obj must be a dict";if(this._obj=a,b instanceof Sao.PYSON.PYSON){if(jQuery(b.types()).not(["string"]).length||jQuery(["string"]).not(b.types()).length)throw"key must be a string"}else if("string"!=typeof b)throw"key must be a string";this._key=b,this._default=c},pyson:function(){return{__class__:"Get",v:this._obj,k:this._key,d:this._default}},types:function(){return this._default instanceof Sao.PYSON.PYSON?this._default.types():[typeof this._default]}}),Sao.PYSON.Get.eval_=function(a,b){return a.k in a.v?a.v[a.k]:a.d},Sao.PYSON.In=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b){if(Sao.PYSON.In._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["string","number"]).length)throw"key must be a string or a number"}else if(!~["string","number"].indexOf(typeof a))throw"key must be a string or a number";if(b instanceof Sao.PYSON.PYSON){if(jQuery(b.types()).not(["object"]).length||jQuery(["object"]).not(b.types()).length)throw"obj must be a dict or a list"}else if(!(b instanceof Object))throw"obj must be a dict or a list";this._key=a,this._obj=b},pyson:function(){return{__class__:"In",k:this._key,v:this._obj}},types:function(){return["boolean"]}}),Sao.PYSON.In.eval_=function(a,b){return a.v.indexOf?Boolean(~a.v.indexOf(a.k)):!!a.v[a.k]},Sao.PYSON.Date=Sao.class_(Sao.PYSON.PYSON,{init:function(a,b,c,d,e,f){Sao.PYSON.Date._super.init.call(this),void 0===a&&(a=null),void 0===b&&(b=null),void 0===c&&(c=null),void 0===d&&(d=0),void 0===e&&(e=0),void 0===f&&(f=0),this._test(a,"year"),this._test(b,"month"),this._test(c,"day"),this._test(d,"delta_years"),this._test(f,"delta_days"),this._test(e,"delta_months"),this._year=a,this._month=b,this._day=c,this._delta_years=d,this._delta_months=e,this._delta_days=f},pyson:function(){return{__class__:"Date",y:this._year,M:this._month,d:this._day,dy:this._delta_years,dM:this._delta_months,dd:this._delta_days}},types:function(){return["object"]},_test:function(a,b){if(a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["number","object"]).length)throw b+" must be an integer or None"}else if("number"!=typeof a&&null!==a)throw b+" must be an integer or None"}}),Sao.PYSON.Date.eval_=function(a,b){var c=Sao.Date(a.y,a.M&&a.M-1,a.d);return a.dy&&c.add(a.dy,"y"),a.dM&&c.add(a.dM,"M"),a.dd&&c.add(a.dd,"d"),c},Sao.PYSON.DateTime=Sao.class_(Sao.PYSON.Date,{init:function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){Sao.PYSON.DateTime._super.init.call(this,a,b,c,h,i,j),void 0===d&&(d=null),void 0===e&&(e=null),void 0===f&&(f=null),void 0===g&&(g=null),void 0===k&&(k=0),void 0===l&&(l=0),void 0===m&&(m=0),void 0===n&&(n=0),this._test(d,"hour"),this._test(e,"minute"),this._test(f,"second"),this._test(g,"microsecond"),this._test(k,"delta_hours"),this._test(l,"delta_minutes"),this._test(m,"delta_seconds"),this._test(n,"delta_microseconds"),this._hour=d,this._minute=e,this._second=f,this._microsecond=g,this._delta_hours=k,this._delta_minutes=l,this._delta_seconds=m,this._delta_microseconds=n},pyson:function(){var a=Sao.PYSON.DateTime._super.pyson.call(this);return a.__class__="DateTime",a.h=this._hour,a.m=this._minute,a.s=this._second,a.ms=this._microsecond,a.dh=this._delta_hours,a.dm=this._delta_minutes,a.ds=this._delta_seconds,a.dms=this._delta_microseconds,a}}),Sao.PYSON.DateTime.eval_=function(a,b){var c=Sao.DateTime(a.y,a.M&&a.M-1,a.d,a.h,a.m,a.s,a.ms&&a.ms/1e3);return a.dy&&c.add(a.dy,"y"),a.dM&&c.add(a.dM,"M"),a.dd&&c.add(a.dd,"d"),a.dh&&c.add(a.dh,"h"),a.dm&&c.add(a.dm,"m"),a.ds&&c.add(a.ds,"s"),a.dms&&c.add(a.dms/1e3,"ms"),c},Sao.PYSON.Len=Sao.class_(Sao.PYSON.PYSON,{init:function(a){if(Sao.PYSON.Len._super.init.call(this),a instanceof Sao.PYSON.PYSON){if(jQuery(a.types()).not(["object","string"]).length||jQuery(["object","string"]).not(a.types()).length)throw"value must be an object or a string"}else if("object"!=typeof a&&"string"!=typeof a)throw"value must be an object or a string";this._value=a},pyson:function(){return{__class__:"Len",v:this._value}},types:function(){return["integer"]}}),Sao.PYSON.Len.eval_=function(a,b){return"object"==typeof a.v?Object.keys(a.v).length:a.v.length}}(),function(){"use strict";Sao.Session=Sao.class_(Object,{init:function(a,b){this.user_id=null,this.session=null,this.prm=jQuery.when(),this.database=a,this.login=b,this.context={},Sao.Session.current_session||(Sao.Session.current_session=this)},do_login:function(a,b){var c=jQuery.Deferred(),d=Sao.common.processing.show(),e={method:"common.db.login",params:[a,b]},f=jQuery.ajax({contentType:"application/json",data:JSON.stringify(e),dataType:"json",url:"/"+this.database,type:"post",complete:[function(){Sao.common.processing.hide(d)}]}),g=function(a){null===a?(Sao.common.warning.run("",Sao.i18n.gettext("Unable to reach the server.")),c.reject()):a.error?(Sao.common.error.run(a.error[0],a.error[1]),c.reject()):a.result?(this.user_id=a.result[0],this.session=a.result[1],c.resolve()):(this.user_id=null,this.session=null,c.reject())};return f.success(g.bind(this)),f.error(c.reject),c.promise()},do_logout:function(){if(!this.user_id||!this.session)return jQuery.when();var a={method:"common.db.logout",params:[]},b=Sao.rpc(a,this);return this.database=null,this.login=null,this.user_id=null,this.session=null,b},reload_context:function(){var a={method:"model.res.user.get_preferences",params:[!0,{}]},b=jQuery.extend({},this);b.context={};var c=Sao.rpc(a,b);return c.then(function(a){this.context=a}.bind(this))}}),Sao.Session.login_dialog=function(){var a=new Sao.Dialog(Sao.i18n.gettext("Login"),"lg");return a.database_select=jQuery("<select/>",{"class":"form-control",id:"login-database"}).hide(),a.database_input=jQuery("<input/>",{"class":"form-control",id:"login-database"}).hide(),a.login_input=jQuery("<input/>",{"class":"form-control",id:"login-login",placeholder:Sao.i18n.gettext("Login")}),a.password_input=jQuery("<input/>",{"class":"form-control",type:"password",id:"login-password",placeholder:Sao.i18n.gettext("Password")}),a.body.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-database"}).append(Sao.i18n.gettext("Database"))).append(a.database_select).append(a.database_input)).append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-login"}).append(Sao.i18n.gettext("Login"))).append(a.login_input)).append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"class":"control-label","for":"login-password"}).append(Sao.i18n.gettext("Password"))).append(a.password_input)),a.button=jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Login")).appendTo(a.footer),a},Sao.Session.get_credentials=function(){var a=jQuery.Deferred(),b=window.location.hash.replace(/^(#(!|))/,"")||null,c=Sao.Session.login_dialog(),d=function(){return c.modal.find("input,select").filter(":visible").filter(function(){return!jQuery(this).val()})},e=function(){var b=c.login_input.val(),e=c.password_input.val(),f=f||c.database_select.val()||c.database_input.val();if(c.modal.find(".has-error").removeClass("has-error"),!(b&&e&&f))return void d().closest(".form-group").addClass("has-error");c.button.focus(),c.button.prop("disabled",!0);var g=new Sao.Session(f,b);g.do_login(b,e).then(function(){a.resolve(g),c.modal.remove()},function(){c.button.prop("disabled",!1),c.password_input.val(""),d().closest(".form-group").addClass("has-error"),d().first().focus()})};return c.modal.modal({backdrop:!1,keyboard:!1}),c.modal.find("form").unbind().submit(function(a){e(),a.preventDefault()}),jQuery.when(Sao.DB.list()).then(function(a){var e;jQuery.isEmptyObject(a)?e=c.database_input:(e=c.database_select,a.forEach(function(a){e.append(jQuery("<option/>",{value:a,text:a}))})),e.show(),e.val(b||""),d().first().focus()}),a.promise()},Sao.Session.password_dialog=function(){var a=new Sao.Dialog(Sao.i18n.gettext("Password"),"lg");return a.password_input=jQuery("<input/>",{"class":"form-control",type:"password",id:"password-password",placeholder:Sao.i18n.gettext("Password")}),a.body.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"for":"password-password"}).append(Sao.i18n.gettext("Password"))).append(a.password_input)),a.button=jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(a.footer),a},Sao.Session.renew=function(a){if("pending"==a.prm.state())return a.prm;var b=jQuery.Deferred();a.prm=b.promise();var c=Sao.Session.password_dialog();if(!a.login)return b.reject(),a.prm;var d=function(){var d=c.password_input.val();c.button.focus(),c.button.prop("disabled",!0),a.do_login(a.login,d).then(function(){b.resolve(),c.modal.remove()},function(){c.button.prop("disabled",!1),c.password_input.val("").focus()})};return c.modal.modal({backdrop:!1,keyboard:!1}),c.modal.on("shown.bs.modal",function(){c.password_input.focus()}),c.modal.find("form").unbind().submit(function(a){d(),a.preventDefault()}),c.modal.modal("show"),a.prm},Sao.Session.current_session=null,Sao.DB={},Sao.DB.list=function(){var a=Sao.common.processing.show();return jQuery.ajax({contentType:"application/json",data:JSON.stringify({method:"common.db.list",params:[null,null]}),dataType:"json",url:"/",type:"post",complete:[function(){Sao.common.processing.hide(a)}]}).then(function(a){return a.result})}}(),function(){"use strict";Sao.Model=Sao.class_(Object,{init:function(a,b){b=b||{},this.name=a,this.session=Sao.Session.current_session,this.fields={}},add_fields:function(a){for(var b in a)if(a.hasOwnProperty(b)&&!(b in this.fields)){var c=a[b],d=Sao.field.get(c.type);this.fields[b]=new d(c)}},execute:function(a,b,c){void 0===c&&(c={});var d={method:"model."+this.name+"."+a,params:b.concat(c)};return Sao.rpc(d,this.session)},find:function(a,b,c,d,e){b||(b=0);var f=this,g=this.execute("search",[a,b,c,d],e),h=function(a){return Sao.Group(f,e,a.map(function(a){return new Sao.Record(f,a)}))};return g.pipe(h)},delete_:function(a){if(jQuery.isEmptyObject(a))return jQuery.when();var b=a[0],c=b.group.root_group();console.assert(a.every(function(a){return a.model.name==b.model.name}),"records not from the same model"),console.assert(a.every(function(a){return a.group.root_group()==b.group.root_group()}),"records not from the same root group"),a=a.filter(function(a){return a.id>=0});var d={};d._timestamp={},a.forEach(function(a){jQuery.extend(d._timestamp,a.get_timestamp())});var e=a.map(function(a){return a.id});return c.on_write_ids(e).then(function(a){return a=a.filter(function(b){return!~a.indexOf(b)}),this.execute("delete",[e],d).then(function(){c.reload(a)})}.bind(this))},copy:function(a,b){if(jQuery.isEmptyObject(a))return jQuery.when();var c=a.map(function(a){return a.id});return this.execute("copy",[c,{}],b)}}),Sao.Group=function(a,b,c){return c.prm=jQuery.when(),c.model=a,c._context=b,c.on_write=[],c.parent=void 0,c.screens=[],c.parent_name="",c.children=[],c.child_name="",c.parent_datetime_field=void 0,c.record_removed=[],c.record_deleted=[],c.__readonly=!1,c.skip_model_access=!1,c.forEach(function(a,b,c){a.group=c}),c.get_readonly=function(){return this.context._datetime||!Sao.common.MODELACCESS.get(this.model.name).write&&!this.skip_model_access?!0:this.__readonly},c.set_readonly=function(a){this.__readonly=a},c.load=function(a,b){var c,d,e=[];for(c=0,d=a.length;d>c;c++){var f=a[c],g=this.get(f);g||(g=new Sao.Record(this.model,f),g.group=this,this.push(g)),e.push(g)}var h,i=[];for(c=0,d=this.record_removed.length;d>c;c++)h=this.record_removed[c],~a.indexOf(h.id)||i.push(h);this.record_removed=i;var j=[];for(c=0,d=this.record_deleted.length;d>c;c++)h=this.record_deleted[c],~a.indexOf(h.id)||j.push(h);this.record_deleted=j,e.length&&b&&this.changed()},c.get=function(a){for(var b=0,c=this.length;c>b;b++){var d=this[b];if(d.id==a)return d}},c.new_=function(a,b){var c=new Sao.Record(this.model,b);return c.group=this,a&&c.default_get(),c},c.add=function(a,b){(void 0===b||-1==b)&&(b=this.length),a.group!=this&&(a.group=this),this.splice(b,0,a);for(var c in this.record_removed)c.id==a.id&&this.record_removed.splice(this.record_removed.indexOf(c),1);for(var d in this.record_deleted)d.id==a.id&&this.record_deleted.splice(this.record_deleted.indexOf(d),1);if(a._changed.id=!0,this.changed(),this.parent&&this.model.fields[this.parent_name]){var e=this.model.fields[this.parent_name];if(e instanceof Sao.field.Many2One||e instanceof Sao.field.Reference){var f=[this.parent.id,""];e instanceof Sao.field.Reference&&(f=[this.parent.model.name,f]),e.set_client(a,f)}}return a},c.remove=function(a,b,c,d){void 0===c&&(c=!0);this.indexOf(a);a.id>=0&&(b?(~this.record_deleted.indexOf(a)&&this.record_deleted.splice(this.record_deleted.indexOf(a),1),this.record_removed.push(a)):(~this.record_removed.indexOf(a)&&this.record_removed.splice(this.record_removed.indexOf(a),1),this.record_deleted.push(a))),a.group.parent&&(a.group.parent._changed.id=!0),c&&(a._changed.id=!0),(!a.group.parent||a.id<0||d)&&this._remove(a),a.group.changed(),a.group.root_group().screens.forEach(function(a){a.display()})},c._remove=function(a){var b=this.indexOf(a);this.splice(b,1)},c.unremove=function(a){this.record_removed.splice(this.record_removed.indexOf(a),1),this.record_deleted.splice(this.record_deleted.indexOf(a),1),a.group.changed(),a.group.root_group().screens.forEach(function(a){a.display()})},c.changed=function(){if(!this.parent)return jQuery.when();this.parent._changed[this.child_name]=!0;var a=jQuery.Deferred(),b=this.parent.model.fields[this.child_name].changed(this.parent);return b?b.then(function(){this.parent.validate(null,!0).done(function(){this.parent.group.changed().done(a.resolve)}.bind(this))}.bind(this)):a.resolve(),a},c.root_group=function(){for(var a=this,b=this.parent;b;)a=b.group,b=b.parent;return a},c.save=function(){var a=[];return this.forEach(function(b){a.push(b.save())}),jQuery.isEmptyObject(this.record_deleted)||a.push(this.model.delete_(this.record_deleted)),jQuery.when.apply(jQuery,a)},c.written=function(a){return"number"==typeof a&&(a=[a]),this.on_write_ids(a).then(function(b){b=b.filter(function(b){return!~a.indexOf(b)}),this.root_group().reload(b)}.bind(this))},c.reload=function(a){this.children.forEach(function(b){b.reload(a)}),a.forEach(function(a){
+var b=this.get(a);b&&jQuery.isEmptyObject(b._changed)&&b.cancel()}.bind(this))},c.on_write_ids=function(a){var b=[],c=[];return this.on_write.forEach(function(d){var e=this.model.execute(d,[a],this._context).then(function(a){jQuery.extend(c,a)});b.push(e)}.bind(this)),jQuery.when.apply(jQuery,b).then(function(){return c.filter(function(a,b,c){return b==c.indexOf(a)})})},c.set_parent=function(a){this.parent=a,a&&a.model.name==this.model.name&&this.parent.group.children.push(this)},c.destroy=function(){if(this.parent){var a=this.parent.group.children.indexOf(this);~a&&this.parent.group.children.splice(a,1)}this.parent=null},c.domain=function(){var a=[];if(this.screens.forEach(function(b){b.attributes.domain&&a.push(b.attributes.domain)}),this.parent&&this.child_name){var b=this.parent.model.fields[this.child_name];return[a,b.get_domain(this.parent)]}return a},c.context=function(){var a=jQuery.extend({},this._context);if(this.parent&&(jQuery.extend(a,this.parent.get_context()),this.child_name in this.parent.model.fields)){var b=this.parent.model.fields[this.child_name];jQuery.extend(a,b.get_context(this.parent))}return jQuery.extend(a,this._context),this.parent_datetime_field&&(a._datetime=this.parent.get_eval()[this.parent_datetime_field]),a},c.set_context=function(a){this._context=jQuery.extend({},a)},c.clean4inversion=function(a){if(jQuery.isEmptyObject(a))return[];var b=new Sao.common.DomainInversion,c=a[0],d=a.slice(1);if(~["AND","OR"].indexOf(c));else if(b.is_leaf(c)){var e=c[0];e in this.model.fields&&this.model.fields[e].description.readonly&&(c=[])}else c=this.clean4inversion(c);return[c].concat(this.clean4inversion(d))},c.domain4inversion=function(){var a=this.domain();return this.__domain4inversion&&Sao.common.compare(this.__domain4inversion[0],a)||(this.__domain4inversion=[a,this.clean4inversion(a)]),this.__domain4inversion[1]},c.get_by_path=function(a){a=jQuery.extend([],a);var b=null,c=this,d=function(){if(jQuery.isEmptyObject(a))return b;var e=a[0][0],f=a[0][1];return a.splice(0,1),b=c.get(f),b?e?b.load(e).then(function(){return c=b._values[e],c?d():null}):d():null};return jQuery.when().then(d)},c},Sao.Record=Sao.class_(Object,{id_counter:-1,init:function(a,b){this.model=a,this.group=Sao.Group(a,{},[]),this.id=b||Sao.Record.prototype.id_counter--,this._values={},this._changed={},this._loaded={},this.fields={},this._timestamp=null,this.attachment_count=-1,this.state_attrs={},this.autocompletion={},this.exception=!1},has_changed:function(){return!jQuery.isEmptyObject(this._changed)},save:function(a){void 0===a&&(a=!1);var b=this.get_context(),c=jQuery.when(),d=this.get();if(this.id<0||!jQuery.isEmptyObject(d)){if(this.id<0){c=this.model.execute("create",[[d]],b);var e=function(a){this.id=a[0]};c.done(e.bind(this))}else jQuery.isEmptyObject(d)||(b._timestamp=this.get_timestamp(),c=this.model.execute("write",[[this.id],d],b));c=c.done(function(){return this.cancel(),a?this.reload():void 0}.bind(this)),this.group&&(c=c.done(function(){return this.group.written(this.id)}.bind(this)))}return this.group.parent&&(delete this.group.parent._changed[this.group.child_name],c=c.done(function(){return this.group.parent.save(a)}.bind(this))),c},reload:function(a){if(this.id<0)return jQuery.when();if(a){var b=a.map(function(a){return this.load(a)}.bind(this));return jQuery.when.apply(jQuery,b)}return this.load("*")},load:function(a){var b,c;if(this.id<0||a in this._loaded)return jQuery.when();if("pending"==this.group.prm.state())return c=jQuery.Deferred(),this.group.prm.then(function(){this.load(a).then(c.resolve,c.reject)}.bind(this)),c;var d={};d[this.id]=this;var e;if("*"==a){e="eager";for(b in this.model.fields)if(this.model.fields.hasOwnProperty(b)){var f=this.model.fields[b].description.loading||"eager";if("eager"!=f){e="lazy";break}}}else e=this.model.fields[a].description.loading||"eager";var g=[];if("eager"==e)for(b in this.model.fields)this.model.fields.hasOwnProperty(b)&&"eager"==(this.model.fields[b].description.loading||"eager")&&g.push(b);else g=Object.keys(this.model.fields);g=g.filter(function(a,b,c){return!(a in this._loaded)}.bind(this));var h=g.slice(),i=["many2one","one2one","reference"];for(var j in g){b=g[j];var k=this.model.fields[b].description;~i.indexOf(k.type)&&h.push(b+".rec_name")}~g.indexOf("rec_name")||h.push("rec_name"),h.push("_timestamp");var l=jQuery.extend({},this.get_context());if("eager"==e){var m,n,o=parseInt(Sao.config.limit/h.length,10),p=function(b){return!(a in b._loaded)&&b.id>=0},q=function(a){return p(a)&&void 0===d[a.id]&&(a.group===this.group||JSON.stringify(a.get_context())===JSON.stringify(l))}.bind(this);this.group.parent&&this.group.parent.model.name==this.model.name?(m=[],m=m.concat.apply(m,this.group.parent.group.children),n=q):(m=this.group,n=p);var r=m.indexOf(this);if(~r)for(var s=m.length,t=1;Object.keys(d).length<o&&(r-t>=0||s>r+t)&&2*o>t;){var u;r-t>=0&&(u=m[r-t],n(u)&&(d[u.id]=u)),s>r+t&&(u=m[r+t],n(u)&&(d[u.id]=u)),t++}}for(b in this.model.fields)this.model.fields.hasOwnProperty(b)&&"binary"==this.model.fields[b].description.type&&~h.indexOf(b,h)&&(l[this.model.name+"."+b]="size");c=this.model.execute("read",[Object.keys(d).map(function(a){return parseInt(a,10)}),h],l);var v=function(a,b){void 0===b&&(b=!1);var c={};a.forEach(function(a,b,d){c[a.id]=a});for(var e in d)if(d.hasOwnProperty(e)){var f=d[e];f.exception||(f.exception=b);var g=c[e];if(f&&g){for(var h in this._changed)this._changed.hasOwnProperty(h)&&delete g[h];f.set(g)}}}.bind(this),w=function(){var a,b=[];for(var c in d){a={id:c};for(var e in h)a[h[e]]=null;b.push(a)}v(b,!0)};return this.group.prm=c.then(v,w),this.group.prm},set:function(a){var b,c,d={};for(b in a)if(a.hasOwnProperty(b))if(c=a[b],"_timestamp"!=b)if(b in this.model.fields){if(this.model.fields[b]instanceof Sao.field.One2Many&&(d[b]=c),this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference){var e=b+".rec_name";a.hasOwnProperty(e)?this._values[e]=a[e]:this._values.hasOwnProperty(e)&&delete this._values[e]}this.model.fields[b].set(this,c),this._loaded[b]=!0}else"rec_name"==b&&(this._values[b]=c);else this._timestamp||(this._timestamp=c);for(b in d)c=d[b],this.model.fields[b].set(this,c),this._loaded[b]=!0},get:function(){var a={};for(var b in this.model.fields)if(this.model.fields.hasOwnProperty(b)){var c=this.model.fields[b];c.description.readonly||void 0===this._changed[b]&&this.id>=0||(a[b]=c.get(this))}return a},invalid_fields:function(){var a={};for(var b in this.model.fields){var c=this.model.fields[b],d=c.get_state_attrs(this).invalid;d&&(a[b]=d)}return a},get_context:function(){return this.group.context()},field_get:function(a){return this.model.fields[a].get(this)},field_set:function(a,b){this.model.fields[a].set(this,b)},field_get_client:function(a){return this.model.fields[a].get_client(this)},field_set_client:function(a,b,c){this.model.fields[a].set_client(this,b,c)},default_get:function(){var a=[];if(!jQuery.isEmptyObject(this.model.fields)){var b=this.model.execute("default_get",[Object.keys(this.model.fields)],this.get_context()),c=function(a){if(this.group.parent&&this.group.parent_name in this.group.model.fields){var b=this.group.model.fields[this.group.parent_name];b instanceof Sao.field.Reference?a[this.group.parent_name]=[this.group.parent.model.name,this.group.parent.id]:b.description.relation==this.group.parent.model.name&&(a[this.group.parent_name]=this.group.parent.id)}return a}.bind(this);b=b.pipe(c).then(function(a){return this.set_default(a),a}.bind(this)),a.push(b)}for(var d in this.model.fields){var e=this.model.fields[d];e.description.autocomplete&&e.description.autocomplete.length>0&&a.push(this.do_autocomplete(d))}return jQuery.when.apply(a)},set_default:function(a){var b=[];for(var c in a)if(a.hasOwnProperty(c)){var d=a[c];if(c in this.model.fields){if(this.model.fields[c]instanceof Sao.field.Many2One||this.model.fields[c]instanceof Sao.field.Reference){var e=c+".rec_name";a.hasOwnProperty(e)?this._values[e]=a[e]:this._values.hasOwnProperty(e)&&delete this._values[e]}this.model.fields[c].set_default(this,d),this._loaded[c]=!0,b.push(c)}}this.on_change(b),this.on_change_with(b),this.validate(null,!0).then(function(){this.group.root_group().screens.forEach(function(a){a.display()})}.bind(this))},get_timestamp:function(){var a={};a[this.model.name+","+this.id]=this._timestamp;for(var b in this.model.fields)this.model.fields.hasOwnProperty(b)&&b in this._loaded&&jQuery.extend(a,this.model.fields[b].get_timestamp(this));return a},get_eval:function(){var a={};for(var b in this.model.fields)!this.model.fields.hasOwnProperty(b)&&this.id>=0||(a[b]=this.model.fields[b].get_eval(this));return a},get_on_change_value:function(a){var b={};for(var c in this.model.fields)a&&~a.indexOf(c)||(!(this.id>=0)||this._loaded[c]&&this._changed[c])&&(b[c]=this.model.fields[c].get_on_change_value(this));return b.id=this.id,b},_get_on_change_args:function(a){var b={},c=Sao.common.EvalEnvironment(this,"on_change");return a.forEach(function(a){var d=c;a.split(".").forEach(function(a){void 0!==d&&(d=d[a])}),b[a]=d}),b.id=this.id,b},on_change:function(a){var b={};if(a.forEach(function(a){var c=this.model.fields[a].description.on_change;jQuery.isEmptyObject(c)||(b=jQuery.extend(b,this._get_on_change_args(c)))}.bind(this)),jQuery.isEmptyObject(b))return jQuery.when();var c=this.model.execute("on_change",[b,a],this.get_context());return c.then(function(a){a.forEach(this.set_on_change.bind(this))}.bind(this))},on_change_with:function(a){var b,c,d={},e={},f={};for(b in this.model.fields)if(this.model.fields.hasOwnProperty(b)&&(c=this.model.fields[b].description.on_change_with,!jQuery.isEmptyObject(c))){for(var g=0;g<a.length&&!~c.indexOf(a[g]);g++);g>=a.length||(jQuery.isEmptyObject(Sao.common.intersect(Object.keys(d).sort(),c.sort()))?(d[b]=!0,e=jQuery.extend(e,this._get_on_change_args(c)),(this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference)&&delete this._values[b+".rec_name"]):f[b]=!0)}var h,i=[];jQuery.isEmptyObject(d)||(h=this.model.execute("on_change_with",[e,Object.keys(d)],this.get_context()),i.push(h.then(this.set_on_change.bind(this))));var j=function(a){return function(b){this.model.fields[a].set_on_change(this,b)}};for(b in f)f.hasOwnProperty(b)&&(c=this.model.fields[b].description.on_change_with,e=this._get_on_change_args(c),h=this.model.execute("on_change_with_"+b,[e],this.get_context()),i.push(h.then(j(b).bind(this))));return jQuery.when.apply(jQuery,i)},set_on_change:function(a){var b,c;for(b in a)if(a.hasOwnProperty(b)&&(c=a[b],b in this.model.fields)){if(this.model.fields[b]instanceof Sao.field.Many2One||this.model.fields[b]instanceof Sao.field.Reference){var d=b+".rec_name";a.hasOwnProperty(d)?this._values[d]=a[d]:this._values.hasOwnProperty(d)&&delete this._values[d]}this.model.fields[b].set_on_change(this,c)}},autocomplete_with:function(a){var b=[];for(var c in this.model.fields){var d=this.model.fields[c],e=d.description.autocomplete||[];~e.indexOf(a)&&b.push(this.do_autocomplete(c))}return jQuery.when.apply(b)},do_autocomplete:function(a){this.autocompletion[a]=[];var b=this.model.fields[a],c=b.description.autocomplete,d=this._get_on_change_args(c),e=this.model.execute("autocomplete_"+a,[d],this.get_context());return e.then(function(b){this.autocompletion[a]=b}.bind(this))},expr_eval:function(a){if("string"!=typeof a)return a;var b=jQuery.extend({},this.get_context());if(b.context=jQuery.extend({},this.model.session.context,b),jQuery.extend(b,this.get_eval()),b.active_model=this.model.name,b.active_id=this.id,b._user=this.model.session.user_id,this.group.parent&&this.group.parent_name){var c=Sao.common.EvalEnvironment(this.group.parent);b["_parent_"+this.group.parent_name]=c}return new Sao.PYSON.Decoder(b).decode(a)},rec_name:function(){var a=this.model.execute("read",[[this.id],["rec_name"]],this.get_context());return a.then(function(a){return a[0].rec_name})},validate:function(a,b,c,d){var e=function(){var d=!0,e=[];this.group.screens.forEach(function(a){a.exclude_field&&e.push(a.exclude_field)});for(var f in this.model.fields)if(this.model.fields.hasOwnProperty(f)){var g=this.model.fields[f];(!a||~a.indexOf(f))&&(g.get_state_attrs(this).readonly||~e.indexOf(f)||g.validate(this,b,c)||(d=!1))}return d}.bind(this);return d?e():this._check_load(a).then(e)},pre_validate:function(){if(jQuery.isEmptyObject(this._changed))return jQuery.Deferred().resolve(!0);var a=this._get_on_change_args(Object.keys(this._changed));return this.model.execute("pre_validate",[a],this.get_context()).then(function(){return!0},function(){return!1})},cancel:function(){this._loaded={},this._changed={},this._timestamp=null},_check_load:function(a){return this.get_loaded(a)?jQuery.when():this.reload(a)},get_loaded:function(a){if(!jQuery.isEmptyObject(a)){var b=!0;return a.forEach(function(a){!(a in this._loaded)|!(a in this._changed)&&(b=!1)}.bind(this)),b}return Sao.common.compare(Object.keys(this.model.fields).sort(),Object.keys(this._loaded).sort())},root_parent:function(){for(var a=this;!a.group.parent;)a=a.group.parent;return a},get_path:function(a){for(var b=[],c=this,d="";c&&(b.push([d,c.id]),c.group!==a);)d=c.group.child_name,c=c.group.parent;return b.reverse(),b},deleted:function(){return Boolean(~this.group.record_deleted.indexOf(this))},removed:function(){return Boolean(~this.group.record_removed.indexOf(this))},readonly:function(){return this.deleted()||this.removed()||this.exception},set_field_context:function(){for(var a in this.model.fields)if(this.model.fields.hasOwnProperty(a)){var b=this.model.fields[a],c=this._values[a];if(c&&c.set_context){var d=b.description.context;d&&c.set_context(this.expr_eval(d))}}},get_attachment_count:function(a){var b=jQuery.Deferred();return this.id<0?(b.resolve(0),b):(this.attachment_count<0||a?b=Sao.rpc({method:"model.ir.attachment.search_count",params:[[["resource","=",this.model.name+","+this.id]],this.get_context()]},this.model.session).then(function(a){return this.attachment_count=a,a}.bind(this)):b.resolve(this.attachment_count),b)}}),Sao.field={},Sao.field.get=function(a){switch(a){case"char":return Sao.field.Char;case"selection":return Sao.field.Selection;case"datetime":return Sao.field.DateTime;case"date":return Sao.field.Date;case"time":return Sao.field.Time;case"timedelta":return Sao.field.TimeDelta;case"float":return Sao.field.Float;case"numeric":return Sao.field.Numeric;case"integer":return Sao.field.Integer;case"boolean":return Sao.field.Boolean;case"many2one":return Sao.field.Many2One;case"one2one":return Sao.field.One2One;case"one2many":return Sao.field.One2Many;case"many2many":return Sao.field.Many2Many;case"reference":return Sao.field.Reference;case"binary":return Sao.field.Binary;case"dict":return Sao.field.Dict;default:return Sao.field.Char}},Sao.field.Field=Sao.class_(Object,{_default:null,init:function(a){this.description=a,this.name=a.name},set:function(a,b){a._values[this.name]=b},get:function(a){var b=a._values[this.name];return void 0===b&&(b=this._default),b},set_client:function(a,b,c){var d=this.get(a);this.set(a,b),JSON.stringify(d)!=JSON.stringify(this.get(a))?(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){a.group.changed().done(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})})):c&&(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})}))},get_client:function(a){return this.get(a)},set_default:function(a,b){a._values[this.name]=b,a._changed[this.name]=!0},set_on_change:function(a,b){a._values[this.name]=b,a._changed[this.name]=!0},changed:function(a){var b=[];return this.get_state_attrs(a).readonly?jQuery.when():(b.push(a.on_change([this.name])),b.push(a.on_change_with([this.name])),b.push(a.autocomplete_with(this.name)),a.set_field_context(),jQuery.when.apply(jQuery,b))},get_timestamp:function(a){return{}},get_context:function(a){var b=jQuery.extend({},a.get_context());return a.group.parent&&jQuery.extend(b,a.group.parent.get_context()),jQuery.extend(b,a.expr_eval(this.description.context||{})),b},get_domains:function(a,b){var c=new Sao.common.DomainInversion,d=c.domain_inversion([a.group.domain4inversion(),b||[]],this.name,Sao.common.EvalEnvironment(a));"boolean"!=typeof d||d?"boolean"==typeof d&&d&&(d=[]):d=[["id","=",null]];var e=a.expr_eval(this.description.domain||[]);return[d,e]},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(c),d])},validation_domains:function(a,b){var c=new Sao.common.DomainInversion;return c.concat(this.get_domains(a,b))},get_eval:function(a){return this.get(a)},get_on_change_value:function(a){return this.get_eval(a)},set_state:function(a,b){void 0===b&&(b=["readonly","required","invisible"]);var c=a.expr_eval(this.description.states||{});b.forEach(function(b){"readonly"==b&&this.description.readonly||(void 0!==c[b]?this.get_state_attrs(a)[b]=c[b]:void 0!==this.description[b]&&(this.get_state_attrs(a)[b]=this.description[b]))}.bind(this)),(a.group.get_readonly()||this.get_state_attrs(a).domain_readonly)&&(this.get_state_attrs(a).readonly=!0)},get_state_attrs:function(a){return this.name in a.state_attrs||(a.state_attrs[this.name]=jQuery.extend({},this.description)),(a.group.get_readonly()||a.readonly())&&(a.state_attrs[this.name].readonly=!0),a.state_attrs[this.name]},check_required:function(a){var b=this.get_state_attrs(a);return 1!=b.required||this.get(a)||1==b.readonly?!0:!1},validate:function(a,b,c){if(this.description.readonly)return!0;var d=!1;this.get_state_attrs(a).domain_readonly=!1;var e=new Sao.common.DomainInversion,f=e.simplify(this.validation_domains(a,c));if(b||this.check_required(a)||(d="required"),"boolean"==typeof f)f||(d="domain");else if(Sao.common.compare(f,[["id","=",null]]))d="domain";else{var g=e.unique_value(f),h=g[0],i=g[1],j=g[2];if(h){j===!1&&(j=null);var k,l=!0;k=jQuery.isEmptyObject(a.group.domain())?e.merge(f):e.merge(a.group.domain());var m="AND"==k[0];if(i.contains(".")){var n=i.split(".",1)[0],o=i.split(".",1)[1],p=[];m&&e.localize_domain(k.slice(1)).forEach(function(a){p.push(a)}),"id"==o&&~p.indexOf(n)||(l=!1)}l&&!c&&(this.set_client(a,j),this.get_state_attrs(a).domain_readonly=m)}e.eval_domain(f,Sao.common.EvalEnvironment(a))||(d=f)}return this.get_state_attrs(a).invalid=d,!d}}),Sao.field.Char=Sao.class_(Sao.field.Field,{_default:"",get:function(a){return Sao.field.Char._super.get.call(this,a)||this._default}}),Sao.field.Selection=Sao.class_(Sao.field.Field,{_default:null}),Sao.field.DateTime=Sao.class_(Sao.field.Field,{_default:null,time_format:function(a){return a.expr_eval(this.description.format)},set_client:function(a,b,c){var d;b&&(b.isTime?(d=this.get(a),b=d?Sao.DateTime.combine(d,b):null):b.isDate&&(d=this.get(a),d&&(b=Sao.DateTime.combine(b,d)))),Sao.field.DateTime._super.set_client.call(this,a,b,c)},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)}}),Sao.field.Date=Sao.class_(Sao.field.Field,{_default:null,set_client:function(a,b,c){b&&!b.isDate&&(b.isDate=!0,b.isDateTime=!1),Sao.field.Date._super.set_client.call(this,a,b,c)},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)}}),Sao.field.Time=Sao.class_(Sao.field.Field,{_default:null,time_format:function(a){return a.expr_eval(this.description.format)},set_client:function(a,b,c){b&&(b.isDate||b.isDateTime)&&(b=Sao.Time(b.hour(),b.minute(),b.second(),b.millisecond())),Sao.field.Time._super.set_client.call(this,a,b,c)}}),Sao.field.TimeDelta=Sao.class_(Sao.field.Field,{_default:null,converter:function(a){return a.model.session.context[this.description.converter]},set_client:function(a,b,c){"string"==typeof b&&(b=Sao.common.timedelta.parse(b,this.converter(a))),Sao.field.TimeDelta._super.set_client.call(this,a,b,c)},get_client:function(a){var b=Sao.field.TimeDelta._super.get_client.call(this,a);return Sao.common.timedelta.format(b,this.converter(a))}}),Sao.field.Float=Sao.class_(Sao.field.Field,{_default:null,digits:function(a,b){void 0===b&&(b=1);var c=a.expr_eval(this.description.digits);if(c&&c.every(function(a){return null!==a})){var d=Math.round(Math.log(Math.abs(b))/Math.LN10);return[c[0]+d,c[1]-d]}},check_required:function(a){var b=this.get_state_attrs(a);return 1==b.required&&null===this.get(a)&&1!=b.readonly?!1:!0},convert:function(a){return a?(a=Number(a),isNaN(a)&&(a=this._default),a):null},apply_factor:function(a,b){return null!==a&&(a/=b),a},set_client:function(a,b,c,d){void 0===d&&(d=1),b=this.apply_factor(this.convert(b),d),Sao.field.Float._super.set_client.call(this,a,b,c)},get_client:function(a,b){void 0===b&&(b=1);var c=this.get(a);if(null!==c){var d=this.digits(a,b);return d?(c*b).toFixed(d[1]):""+c*b}return""}}),Sao.field.Numeric=Sao.class_(Sao.field.Float,{convert:function(a){return a?(a=new Sao.Decimal(a),isNaN(a.valueOf())&&(a=this._default),a):null},apply_factor:function(a,b){return a=Sao.field.Numeric._super.apply_factor(a,b),new Sao.Decimal(a)}}),Sao.field.Integer=Sao.class_(Sao.field.Float,{convert:function(a){return a=parseInt(a,10),isNaN(a)&&(a=this._default),a}}),Sao.field.Boolean=Sao.class_(Sao.field.Field,{_default:!1,set_client:function(a,b,c){b=Boolean(b),Sao.field.Boolean._super.set_client.call(this,a,b,c)},get:function(a){return Boolean(a._values[this.name])},get_client:function(a){return Boolean(a._values[this.name])}}),Sao.field.Many2One=Sao.class_(Sao.field.Field,{_default:null,get_client:function(a){var b=a._values[this.name+".rec_name"];return void 0===b&&(this.set(a,this.get(a)),b=a._values[this.name+".rec_name"]||""),b},set:function(a,b){var c=a._values[this.name+".rec_name"]||"",d=function(b){a._values[this.name+".rec_name"]=b[0].rec_name};if(!c&&b>=0&&null!==b){var e=a.model.fields[this.name].description.relation;Sao.rpc({method:"model."+e+".read",params:[[b],["rec_name"],a.get_context()]},a.model.session).done(d.bind(this))}else d.call(this,[{rec_name:c}]);a._values[this.name]=b},set_client:function(a,b,c){var d;b instanceof Array?(d=b[1],b=b[0]):d=b==this.get(a)?a._values[this.name+".rec_name"]||"":"",a._values[this.name+".rec_name"]=d,Sao.field.Many2One._super.set_client.call(this,a,b,c)},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(e.inverse_leaf(c),this.name),d])},get_on_change_value:function(a){return a.group.parent_name==this.name&&a.group.parent?a.group.parent.get_on_change_value([a.group.child_name]):Sao.field.Many2One._super.get_on_change_value.call(this,a)}}),Sao.field.One2One=Sao.class_(Sao.field.Many2One,{}),Sao.field.One2Many=Sao.class_(Sao.field.Field,{init:function(a){Sao.field.One2Many._super.init.call(this,a)},_default:null,_set_value:function(a,b,c){this._set_default_value(a);var d,e=a._values[this.name];d=b instanceof Array&&!isNaN(parseInt(b[0],10))?"list ids":"list values";var f=jQuery.when();if("list values"==d&&!jQuery.isEmptyObject(b)){var g=this.get_context(a),h={};for(var i in b)if(b.hasOwnProperty(i))for(var j in i)i.hasOwnProperty(j)&&(h[j]=!0);if(!jQuery.isEmptyObject(h)){var k={method:"model."+this.description.relation+".fields_get",params:[Object.keys(h),g]};f=Sao.rpc(k,a.model.session)}}var l=function(f){if(jQuery.isEmptyObject(f)||e.model.add_fields(f),a._values[this.name]=e,"list ids"==d){for(var g=0,h=e.length;h>g;g++){var i=e[g];e.remove(i,!0)}e.load(b)}else for(var j in b)if(b.hasOwnProperty(j)){var k=e.new_(!1);c?(k.set_default(j),e.add(k)):(k.id*=1,k.set(j),e.push(k))}};return f.pipe(l.bind(this))},set:function(a,b,c){void 0===c&&(c=!1);var d,e=a._values[this.name];return void 0!==e?(d=e.model,e.destroy()):d=a.model.name==this.description.relation?a.model:new Sao.Model(this.description.relation),a._values[this.name]=void 0,this._set_default_value(a,d),this._set_value(a,b,c)},get:function(a){var b=a._values[this.name];if(void 0===b)return[];for(var c=b.record_removed,d=b.record_deleted,e=[],f=this.description.relation_field||"",g=[],h=[],i=[],j=0,k=b.length;k>j;j++){var l=b[j];if(!~c.indexOf(l)&&!~d.indexOf(l)){var m;l.id>=0?(m=l.get(),delete m[f],l.has_changed()&&!jQuery.isEmptyObject(m)&&(i.push([l.id]),i.push(m)),g.push(l.id)):(m=l.get(),delete m[f],h.push(m))}}return jQuery.isEmptyObject(g)||e.push(["add",g]),jQuery.isEmptyObject(h)||e.push(["create",h]),jQuery.isEmptyObject(i)||e.push(["write"].concat(i)),jQuery.isEmptyObject(c)||e.push(["remove",c.map(function(a){return a.id})]),jQuery.isEmptyObject(d)||e.push(["delete",d.map(function(a){return a.id})]),e},set_client:function(a,b,c){null===b&&(b=[]),"number"==typeof b&&(b=[b]);var d=this.get_eval(a);this._set_value(a,b),Sao.common.compare(d.sort(),b.sort())?c&&(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})):(a._changed[this.name]=!0,this.changed(a).done(function(){a.validate(null,!0).then(function(){a.group.changed().done(function(){var b=a.group.root_group();b.screens.forEach(function(a){a.display()})})})}))},get_client:function(a){return this._set_default_value(a),a._values[this.name]},set_default:function(a,b){a._values[this.name];this.set(a,b,!0),a._changed[this.name]=!0},set_on_change:function(a,b){if(a._changed[this.name]=!0,this._set_default_value(a),b instanceof Array)return void this._set_value(a,b);var c=jQuery.when();if(b.add||b.update){var d=this.get_context(a),e=a._values[this.name].model.fields,f={},g=[];if(b.add)for(var h=0;h<b.add.length;h++)g.push(b.add[h][1]);if([g,b.update].forEach(function(a){jQuery.isEmptyObject(a)||a.forEach(function(a){Object.keys(a).forEach(function(a){a in e||"id"==a||(f[a]=!0)})})}),!jQuery.isEmptyObject(f)){var i={method:"model."+this.description.relation+".fields_get",params:[Object.keys(f),d]};c=Sao.rpc(i,a.model.session)}}var j=[],k=a._values[this.name];k.forEach(function(a){a.id||j.push(a)}),b.remove&&b.remove.forEach(function(a){var b=k.get(a);b&&j.push(b)}.bind(this)),j.forEach(function(a){k.remove(a,!1,!0,!1)}.bind(this)),(b.add||b.update)&&c.then(function(a){k.model.add_fields(a),b.add&&b.add.forEach(function(a){var b=a[0],c=a[1],d=k.new_(!1);k.add(d,b),d.set_on_change(c)}),b.update&&b.update.forEach(function(a){if(a.id){var b=k.get(a.id);b&&b.set_on_change(a)}})}.bind(this))},_set_default_value:function(a,b){if(void 0===a._values[this.name]){b||(b=new Sao.Model(this.description.relation)),a.model.name==this.description.relation&&(b=a.model);var c=a.expr_eval(this.description.context||{}),d=Sao.Group(b,c,[]);d.set_parent(a),d.parent_name=this.description.relation_field,d.child_name=this.name,a._values[this.name]=d}},get_timestamp:function(a){var b=a._values[this.name];if(void 0===b)return{};for(var c,d={},e=0,f=b.length;f>e;e++)c=b[e],jQuery.extend(d,c.get_timestamp());return d},get_eval:function(a){var b=[],c=a._values[this.name];if(void 0===c)return b;for(var d=c.record_removed,e=c.record_deleted,f=0,g=a._values[this.name].length;g>f;f++){var h=c[f];~d.indexOf(h)||~e.indexOf(h)||b.push(h.id)}return b},get_on_change_value:function(a){var b=[],c=a._values[this.name];if(void 0===c)return b;for(var d=0,e=a._values[this.name].length;e>d;d++){var f=c[d];f.deleted()&&f.removed()||b.push(f.get_on_change_value([this.description.relation_field||""]))}return b},get_removed_ids:function(a){return a._values[this.name].record_removed.map(function(a){return a.id})},get_domain:function(a){var b=this.get_domains(a),c=b[0],d=b[1],e=new Sao.common.DomainInversion;return e.concat([e.localize_domain(e.inverse_leaf(c),this.name),d])},validation_domains:function(a,b){return this.get_domains(a,b)[0]},validate:function(a,b,c){if(this.description.readonly)return!0;var d=!1,e=new Sao.common.DomainInversion,f=e.localize_domain(e.domain_inversion(a.group.clean4inversion(c||[]),this.name,Sao.common.EvalEnvironment(a)),this.name);"boolean"==typeof f&&(f=f?[]:[["id","=",null]]);for(var g=0,h=(a._values[this.name]||[]).length;h>g;g++){var i=a._values[this.name][g];!i.get_loaded()&&i.id>=0&&!c||i.validate(null,b,f,!0)||(d="children")}var j=Sao.field.One2Many._super.validate.call(this,a,b,c);return j&&d?(this.get_state_attrs(a).invalid=d,!1):j},set_state:function(a,b){this._set_default_value(a),Sao.field.One2Many._super.set_state.call(this,a,b),a._values[this.name].readonly=this.get_state_attrs(a).readonly}}),Sao.field.Many2Many=Sao.class_(Sao.field.One2Many,{get_on_change_value:function(a){return this.get_eval(a)}}),Sao.field.Reference=Sao.class_(Sao.field.Field,{_default:null,get_client:function(a){if(a._values[this.name]){var b=a._values[this.name][0],c=a._values[this.name+".rec_name"]||"";return[b,c]}return null},get:function(a){return a._values[this.name]&&a._values[this.name][0]&&a._values[this.name][1]>=-1?a._values[this.name].join(","):null},set_client:function(a,b,c){if(b){"string"==typeof b&&(b=b.split(","));var d,e=b[0],f=b[1];f instanceof Array?(d=f[1],f=f[0]):(f&&!isNaN(parseInt(f,10))&&(f=parseInt(f,10)),d=[e,f].join(",")==this.get(a)?a._values[this.name+".rec_name"]||"":""),a._values[this.name+".rec_name"]=d,b=[e,f]}Sao.field.Reference._super.set_client.call(this,a,b,c)},set:function(a,b){if(!b)return void(a._values[this.name]=this._default);var c,d;"string"==typeof b?(c=b.split(",")[0],d=b.split(",")[1],d?isNaN(parseInt(d,10))||(d=parseInt(d,10)):d=null):(c=b[0],d=b[1]);var e=a._values[this.name+".rec_name"]||"",f=function(b){a._values[this.name+".rec_name"]=b}.bind(this);c&&null!==d&&d>=0?!e&&d>=0&&Sao.rpc({method:"model."+c+".read",params:[[d],["rec_name"],a.get_context()]},a.model.session).done(function(a){f(a[0].rec_name)}):e=c?"":d,a._values[this.name]=[c,d],f(e)},get_on_change_value:function(a){return a.group.parent_name==this.name&&a.group.parent?[a.group.parent.model.name,a.group.parent.get_on_change_value([a.group.child_name])]:Sao.field.Reference._super.get_on_change_value.call(this,a)},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=null;a._values[this.name]&&(b=a._values[this.name][0]);var c=this.get_domains(a),d=c[0],e=c[1],f=new Sao.common.DomainInversion;return f.concat([f.localize_domain(f.filter_leaf(d,this.name,b),!0),e])}}),Sao.field.Binary=Sao.class_(Sao.field.Field,{_default:null,get_size:function(a){var b=a._values[this.name]||0;return b instanceof Uint8Array?b.length:b},get_data:function(a){var b=jQuery.when(),c=a._values[this.name]||0;if(!(c instanceof Uint8Array)){if(a.id<0)return b;var d=a.get_context();return b=a.model.execute("read",[[a.id],[this.name]],d),b.done(function(a){return a[0][this.name]}.bind(this)),b}}}),Sao.field.Dict=Sao.class_(Sao.field.Field,{_default:{},get:function(a){return Sao.field.Dict._super.get.call(this,a)||this._default},get_client:function(a){return Sao.field.Dict._super.get_client.call(this,a)||this._default},validation_domains:function(a,b){return this.get_domains(a,b)[0]},get_domain:function(a){var b=new Sao.common.DomainInversion,c=this.get_domains(a),d=c[0],e=c[1];return b.concat([b.localize_domain(b.inverse_leaf(d)),e])},date_format:function(a){var b=this.get_context(a);return Sao.common.date_format(b.date_format)},time_format:function(a){return"%X"}})}(),function(){"use strict";Sao.Tab=Sao.class_(Object,{init:function(){Sao.Tab.tabs.push(this),this.buttons={},this.id="tab-"+Sao.Tab.counter++,this.name_el=jQuery("<span/>")},create_tabcontent:function(){this.el=jQuery("<div/>",{"class":this.class_});var a=this.create_toolbar().appendTo(this.el);this.title=a.find("a.navbar-brand"),this.info_bar&&this.el.append(this.info_bar.el)},set_menu:function(a){this.menu_def().forEach(function(b){var c=b[0],d=b[1],e=b[2],f=jQuery("<li/>",{role:"presentation"}).appendTo(a),g=jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1
+}).append(jQuery("<span/>",{"class":"glyphicon "+c,"aria-hidden":"true"})).append(" "+d).appendTo(f);e?g.click(function(){this[e]()}.bind(this)):f.addClass("disabled")}.bind(this))},create_toolbar:function(){var a=jQuery('<nav class="navbar navbar-default toolbar" role="navigation"><div class="container-fluid"><div class="navbar-header"><a class="navbar-brand" href="#"></a><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-'+this.id+'"><span class="sr-only">'+Sao.i18n.gettext("Toggle navigation")+'</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></div><div class="collapse navbar-collapse" id="navbar-'+this.id+'"><ul class="nav navbar-nav"><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><span class="visible-xs">'+Sao.i18n.gettext("Menu")+'</span><span class="caret"></span></a><ul class="dropdown-menu" role="menu"></ul></li></ul></div></div></nav>'),b=jQuery("<div/>",{"class":"nav-wrapper"}).append(a);this.set_menu(a.find('ul[role*="menu"]'));var c=function(b){var c=jQuery("<li/>",{role:"presentation"}).appendTo(a.find(".navbar-collapse > ul"));this.buttons[b[0]]=jQuery("<a/>",{role:"menuitem",href:"#",id:b[0]}).append(jQuery("<span/>",{"class":"glyphicon "+b[1],"aria-hidden":"true"})).append(jQuery("<span/>",{"class":"hidden-sm"}).append(" "+b[2])).appendTo(c),b[4]?this.buttons[b[0]].click(this[b[4]].bind(this)):c.addClass("disabled")};this.toolbar_def().forEach(c.bind(this));var d=jQuery("#tabs");return a.affix({target:d.parent(),offset:{top:function(){return d.find(".nav-tabs").height()}}}),a.on("affix.bs.affix",function(){b.height(a.height())}),a.on("affix-top.bs.affix affix-bottom.bs.affix",function(){b.height("")}),a.on("affixed.bs.affix",function(){Sao.Tab.affix_set_with(a)}),a.on("affixed-top.bs.affix affixed-bottom.bs.affix",function(){Sao.Tab.affix_unset_width(a)}),b},close:function(){var a=jQuery("#tabs"),b=a.find("#nav-"+this.id),c=a.find("#"+this.id);return a.find('a[href="#'+this.id+'"]').tab("show"),this._close_allowed().then(function(){var d=b.next();d.length||(d=b.prev()),b.remove(),c.remove(),Sao.Tab.tabs.splice(Sao.Tab.tabs.indexOf(this),1),d&&d.find("a").tab("show"),a.trigger("ready")}.bind(this))},_close_allowed:function(){return jQuery.when()},set_name:function(a){this.name_el.text(a)}}),Sao.Tab.affix_set_with=function(a){var b=jQuery(a.parent()).width();a.css("width",b)},Sao.Tab.affix_unset_width=function(a){a.css("width","")},jQuery(window).resize(function(){jQuery(".toolbar").each(function(a,b){b=jQuery(b),b.affix("checkPosition"),b.hasClass("affix")?Sao.Tab.affix_set_with(b):Sao.Tab.affix_unset_width(b)})}),Sao.Tab.counter=0,Sao.Tab.tabs=[],Sao.Tab.tabs.close=function(a){if(a&&Sao.Tab.tabs.length)return Sao.common.sur.run(Sao.i18n.gettext("The following action requires to close all tabs.\nDo you want to continue?")).then(function(){return Sao.Tab.tabs.close(!1)});if(Sao.Tab.tabs.length){var b=Sao.Tab.tabs[0];return b.close().then(function(){return~Sao.Tab.tabs.indexOf(b)?jQuery.Deferred().reject():Sao.Tab.tabs.close()})}return Sao.main_menu_screen?Sao.main_menu_screen.save_tree_state().then(function(){Sao.main_menu_screen=null}):jQuery.when()},Sao.Tab.tabs.get_current=function(){var a=jQuery("#tabs > ul"),b=a.find("li").index(a.find("li.active"));return Sao.Tab.tabs[b]},Sao.Tab.tabs.close_current=function(){var a=this.get_current();a.close()},Sao.Tab.create=function(a){void 0===a.context&&(a.context={});var b;b=a.model?new Sao.Tab.Form(a.model,a):new Sao.Tab.Board(a),Sao.Tab.add(b)},Sao.Tab.add=function(a){var b=jQuery("#tabs"),c=jQuery("<a/>",{"aria-controls":a.id,role:"tab","data-toggle":"tab",href:"#"+a.id}).append(jQuery("<button/>",{"class":"close"}).append(jQuery("<span/>",{"aria-hidden":!0}).append("×")).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Close"))).click(function(){a.close()})).append(a.name_el);jQuery("<li/>",{role:"presentation",id:"nav-"+a.id}).append(c).appendTo(b.find("> .nav-tabs")),jQuery("<div/>",{role:"tabpanel","class":"tab-pane",id:a.id}).html(a.el).appendTo(b.find("> .tab-content")),c.on("shown.bs.tab",function(){Sao.View.resize(a.el)}),c.tab("show"),b.trigger("ready")},Sao.Tab.Form=Sao.class_(Sao.Tab,{class_:"tab-form",init:function(a,b){Sao.Tab.Form._super.init.call(this);var c=new Sao.Screen(a,b);c.tab=this,this.screen=c,this.attributes=jQuery.extend({},b),this.info_bar=new Sao.Window.InfoBar,this.create_tabcontent();var d=Sao.common.MODELACCESS.get(a);[["new","create"],["save","write"]].forEach(function(a){var b=a[0],c=a[1];this.buttons[b].prop("disabled",!d[c])}.bind(this)),this.view_prm=this.screen.switch_view().done(function(){this.set_name(b.name||this.screen.current_view.attributes.string),this.el.append(c.screen_container.el),b.res_id?(c.group.load([b.res_id]),c.set_current_record(c.group.get(b.res_id)),c.display()):("form"==c.current_view.view_type&&c.new_(),~["tree","graph","calendar"].indexOf(c.current_view.view_type)&&c.search_filter()),this.update_revision()}.bind(this))},toolbar_def:function(){return[["new","glyphicon-edit",Sao.i18n.gettext("New"),Sao.i18n.gettext("Create a new record"),"new_"],["save","glyphicon-save",Sao.i18n.gettext("Save"),Sao.i18n.gettext("Save this record"),"save"],["switch","glyphicon-list-alt",Sao.i18n.gettext("Switch"),Sao.i18n.gettext("Switch view"),"switch_"],["reload","glyphicon-refresh",Sao.i18n.gettext("Reload"),Sao.i18n.gettext("Reload"),"reload"],["previous","glyphicon-chevron-left",Sao.i18n.gettext("Previous"),Sao.i18n.gettext("Previous Record"),"previous"],["next","glyphicon-chevron-right",Sao.i18n.gettext("Next"),Sao.i18n.gettext("Next Record"),"next"],["attach","glyphicon-paperclip",Sao.i18n.gettext("Attachment"),Sao.i18n.gettext("Add an attachment to the record"),"attach"]]},menu_def:function(){return[["glyphicon-edit",Sao.i18n.gettext("New"),"new_"],["glyphicon-save",Sao.i18n.gettext("Save"),"save"],["glyphicon-list-alt",Sao.i18n.gettext("Switch"),"switch_"],["glyphicon-refresh",Sao.i18n.gettext("Reload/Undo"),"reload"],["glyphicon-duplicate",Sao.i18n.gettext("Duplicate"),"copy"],["glyphicon-trash",Sao.i18n.gettext("Delete"),"delete_"],["glyphicon-chevron-left",Sao.i18n.gettext("Previous"),"previous"],["glyphicon-chevron-right",Sao.i18n.gettext("Next"),"next"],["glyphicon-search",Sao.i18n.gettext("Search"),"search"],["glyphicon-time",Sao.i18n.gettext("View Logs..."),"logs"],["glyphicon-time",Sao.i18n.gettext("Show revisions..."),Sao.common.MODELHISTORY.contains(this.screen.model_name)?"revision":null],["glyphicon-remove",Sao.i18n.gettext("Close Tab"),"close"],["glyphicon-paperclip",Sao.i18n.gettext("Attachment"),"attach"],["glyphicon-cog",Sao.i18n.gettext("Action"),"action"],["glyphicon-share-alt",Sao.i18n.gettext("Relate"),"relate"],["glyphicon-print",Sao.i18n.gettext("Print"),"print"]]},create_toolbar:function(){var a=Sao.Tab.Form._super.create_toolbar.call(this),b=this.screen,c=this.buttons,d=b.model.execute("view_toolbar_get",[],b.context);return d.done(function(d){[["action","glyphicon-cog",Sao.i18n.gettext("Action"),Sao.i18n.gettext("Launch action")],["relate","glyphicon-share-alt",Sao.i18n.gettext("Relate"),Sao.i18n.gettext("Open related records")],["print","glyphicon-print",Sao.i18n.gettext("Print"),Sao.i18n.gettext("Print report")]].forEach(function(e){var f=jQuery("<li/>",{"class":"dropdown"}).append(jQuery("<a/>",{href:"#",id:e[0],"class":"dropdown-toggle","data-toggle":"dropdown",role:"button","aria-expanded":"false"}).append(jQuery("<span/>",{"class":"glyphicon "+e[1],"aria-hidden":"true"})).append(jQuery("<span/>",{"class":"hidden-sm"}).append(" "+e[2]+" ")).append(jQuery("<span/>",{"class":"caret"}))).append(jQuery("<ul/>",{"class":"dropdown-menu",role:"menu","aria-labelledby":e[0]})).appendTo(a.find(".navbar-collapse > ul"));c[e[0]]=f;var g=f.find('ul[role*="menu"]');"action"==e[0]&&f.find("a").click(function(){g.find(".action_button").remove();var a=b.get_buttons();a.forEach(function(a){jQuery("<li/>",{role:"presentation","class":"action_button"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(a.attributes.string||"")).click(function(){b.button(a.attributes)}).appendTo(g)})}),d[e[0]].forEach(function(a){jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(a.name)).click(function(){b.save_current().then(function(){var c=jQuery.extend({},a),d=null;b.current_record&&(d=b.current_record.id);var f=b.current_view.selected_records().map(function(a){return a.id});c=Sao.Action.evaluate(c,e[0],b.current_record);var g={model:b.model_name,id:d,ids:f};Sao.Action.exec_action(c,g,b.context)})}).appendTo(g)})})}),a},_close_allowed:function(){return this.modified_save()},modified_save:function(){return this.screen.save_tree_state(),this.screen.current_view.set_value(),this.screen.modified()?Sao.common.sur_3b.run(Sao.i18n.gettext("This record has been modified\ndo you want to save it?")).then(function(a){switch(a){case"ok":return this.save();case"ko":return this.reload(!1);default:return jQuery.Deferred().reject()}}.bind(this)):jQuery.when()},new_:function(){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.modified_save().done(function(){this.screen.new_().then(function(){this.info_bar.message()}.bind(this))}.bind(this))},save:function(){return Sao.common.MODELACCESS.get(this.screen.model_name).write?this.screen.save_current().then(function(){this.info_bar.message(Sao.i18n.gettext("Record saved."),"info")}.bind(this),function(){this.info_bar.message(this.screen.invalid_message(),"danger")}.bind(this)):jQuery.when()},switch_:function(){this.modified_save().done(function(){this.screen.switch_view()}.bind(this))},reload:function(a){void 0===a&&(a=!0);var b=function(){return this.screen.cancel_current().then(function(){return this.screen.save_tree_state(!1),"form"!=this.screen.current_view.view_type&&this.screen.search_filter(this.screen.screen_container.search_entry.val()),this.screen.display().then(function(){this.info_bar.message()}.bind(this))}.bind(this))}.bind(this);return a?this.modified_save().then(b):b()},copy:function(){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.modified_save().done(function(){this.screen.copy().then(function(){this.info_bar.message(Sao.i18n.gettext("Working now on the duplicated record(s)."),"info")}.bind(this))}.bind(this))},delete_:function(){if(Sao.common.MODELACCESS.get(this.screen.model_name)["delete"]){var a;a="form"==this.screen.current_view.view_type?Sao.i18n.gettext("Are you sure to remove this record?"):Sao.i18n.gettext("Are you sure to remove those records?"),Sao.common.sur.run(a).done(function(){this.screen.remove(!0,!1,!0).then(function(){this.info_bar.message(Sao.i18n.gettext("Records removed."),"info")}.bind(this),function(){this.info_bar.message(Sao.i18n.gettext("Records not removed."),"danger")}.bind(this))}.bind(this))}},previous:function(){this.modified_save().done(function(){this.screen.display_previous(),this.info_bar.message()}.bind(this))},next:function(){this.modified_save().done(function(){this.screen.display_next(),this.info_bar.message()}.bind(this))},search:function(){var a=this.screen.screen_container.search_entry;a.is(":visible")&&window.setTimeout(function(){a.focus()},0)},logs:function(){var a=this.screen.current_record;if(!a||a.id<0)return void this.info_bar.message(Sao.i18n.gettext("You have to select one record."),"info");var b=[["id",Sao.i18n.gettext("ID:")],["create_uid.rec_name",Sao.i18n.gettext("Creation User:")],["create_date",Sao.i18n.gettext("Creation Date:")],["write_uid.rec_name",Sao.i18n.gettext("Latest Modification by:")],["write_date",Sao.i18n.gettext("Latest Modification Date:")]];this.screen.model.execute("read",[[a.id],b.map(function(a){return a[0]})],this.screen.context).then(function(a){a=a[0];var c="";b.forEach(function(b){var d=b[0],e=b[1],f=a[d]||"/";a[d]&&~["create_date","write_date"].indexOf(d)&&(f=Sao.common.format_datetime(Sao.common.date_format(),"%H:%M:%S",f)),c+=e+" "+f+"\n"}),c+=Sao.i18n.gettext("Model: ")+this.screen.model.name,Sao.common.message.run(c)}.bind(this))},revision:function(){var a=null;this.screen.current_record&&(a=this.screen.current_record.id);var b=function(b){return function(c){c&&c.setMilliseconds(c.getMilliseconds()+1),"form"==this.screen.current_view.view_type&&c<b[b.length-1][0]&&(c=b[b.length-1][0]),c!=this.screen.context._datetime&&(this.screen.context._datetime=c,"form"!=this.screen.current_view.view_type?this.screen.search_filter(this.screen.screen_container.search_entry.val()):this.screen.new_group([a]),this.screen.display(!0),this.update_revision())}.bind(this)}.bind(this);this.modified_save().done(function(){var a=this.screen.current_view.selected_records().map(function(a){return a.id});this.screen.model.execute("history_revisions",[a],this.screen.context).then(function(a){new Sao.Window.Revision(a,b(a))})}.bind(this))},update_revision:function(){var a,b=this.screen.context._datetime;if(b){var c=Sao.common.date_format(),d="%H:%M:%S.%f";b=Sao.common.format_datetime(c,d,b),a=this.name_el.text()+" @ "+b}else a=this.name_el.text();this.title.html(a),["new","save"].forEach(function(a){this.buttons[a].prop("disabled",b)}.bind(this))},attach:function(){var a=this.screen.current_record;!a||a.id<0||new Sao.Window.Attachment(a,function(){this.update_attachment_count(!0)}.bind(this))},update_attachment_count:function(a){var b=this.screen.current_record;b?b.get_attachment_count(a).always(this.attachment_count.bind(this)):this.attachment_count(0)},attachment_count:function(a){var b=Sao.i18n.gettext("Attachment(%1)",a);this.buttons.attach.text(b);var c=this.screen.get_id();this.buttons.attach.prop("disabled",0>c||null===c)},record_message:function(){this.info_bar.message()},action:function(){window.setTimeout(function(){this.buttons.action.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))},relate:function(){window.setTimeout(function(){this.buttons.relate.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))},print:function(){window.setTimeout(function(){this.buttons.print.find("ul.dropdown-menu").dropdown("toggle")}.bind(this))}}),Sao.Tab.Board=Sao.class_(Sao.Tab,{class_:"tab-board",init:function(a){var b,c;Sao.Tab.Board._super.init.call(this),this.model=a.model,this.view_id=a.view_ids.length>0?a.view_ids[0]:null,this.context=a.context,this.name=a.name,this.dialogs=[],this.board=null,b=new Sao.Model("ir.ui.view"),c=b.execute("read",[[this.view_id],["arch"]],this.context),c.done(function(a){var b;b=jQuery(jQuery.parseXML(a[0].arch)),this.board=new Sao.View.Board(b,this.context),this.board.actions_prms.done(function(){var a,b,c;for(a=0,b=this.board.actions.length;b>a;a++)c=this.board.actions[a],c.screen.tab=this}.bind(this)),this.el.append(this.board.el)}.bind(this)),this.create_tabcontent(),this.set_name(this.name),this.title.html(this.name_el.text())},toolbar_def:function(){return[["reload","glyphicon-refresh",Sao.i18n.gettext("Reload"),Sao.i18n.gettext("Reload"),"reload"]]},menu_def:function(){return[["glyphicon-refresh",Sao.i18n.gettext("Reload/Undo"),"reload"]]},reload:function(){this.board.reload()},record_message:function(){var a,b,c;for(b=this.board.actions.length,a=0,b=b;b>a;a++)c=this.board.actions[a],c.update_domain(this.board.actions)},attachment_count:function(){}}),Sao.Tab.Wizard=Sao.class_(Sao.Tab,{class_:"tab-wizard",init:function(a){Sao.Tab.Wizard._super.init.call(this),this.wizard=a,this.set_name(a.name),a.tab=this,this.create_tabcontent(),this.title.html(this.name_el.text()),this.el.append(a.form)},create_toolbar:function(){return jQuery("<span/>")},_close_allowed:function(){var a=this.wizard,b=jQuery.when();a.state!==a.end_state&&a.end_state in a.states&&(b=a.response(a.end_state));var c=jQuery.Deferred();return b.always(function(){a.state===a.end_state?c.resolve():c.reject()}),c.promise()}})}(),function(){"use strict";Sao.ScreenContainer=Sao.class_(Object,{init:function(a){this.alternate_viewport=jQuery("<div/>",{"class":"screen-container"}),this.alternate_view=!1,this.search_modal=null,this.search_form=null,this.tab_domain=a||[],this.el=jQuery("<div/>",{"class":"screen-container"}),this.filter_box=jQuery("<div/>",{"class":"row filter-box"}),this.el.append(this.filter_box),this.filter_button=jQuery("<button/>",{type:"button","class":"btn btn-default"}).append(Sao.i18n.gettext("Filters")),this.filter_button.click(this.search_box.bind(this)),this.search_entry=jQuery("<input/>",{"class":"form-control",placeholder:Sao.i18n.gettext("Search")}),this.search_list=jQuery("<datalist/>"),this.search_list.uniqueId(),this.search_entry.attr("list",this.search_list.attr("id")),this.search_entry.keypress(this.key_press.bind(this)),this.search_entry.on("input",this.update.bind(this)),this.but_bookmark=jQuery("<button/>",{type:"button","class":"btn btn-default dropdown-toggle","data-toggle":"dropdown","aria-expanded":!1,"aria-label":Sao.i18n.gettext("Bookmarks"),id:"bookmarks"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-bookmark","aria-hidden":!0}));var b=jQuery("<ul/>",{"class":"dropdown-menu",role:"menu","aria-labelledby":"bookmarks"});if(this.but_bookmark.click(function(){b.children().remove();for(var a=this.bookmarks(),c=0;c<a.length;c++){var d=a[c][1],e=a[c][2];jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{role:"menuitem",href:"#",tabindex:-1}).append(d).click(e,this.bookmark_activate.bind(this))).appendTo(b)}}.bind(this)),this.but_star=jQuery("<button/>",{"class":"btn btn-default"}).append(jQuery("<span/>",{"class":"glyphicon","aria-hidden":!0})).click(this.star_click.bind(this)),this.set_star(),jQuery("<div/>",{"class":"input-group"}).append(jQuery("<span/>",{"class":"input-group-btn"}).append(this.filter_button)).append(this.search_entry).append(this.search_list).append(jQuery("<span/>",{"class":"input-group-btn"}).append(this.but_star).append(this.but_bookmark).append(b)).appendTo(jQuery("<div/>",{"class":"col-md-8"}).appendTo(this.filter_box)),this.but_prev=jQuery("<button/>",{type:"button","class":"btn btn-default","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-menu-left","aria-hidden":!0})),this.but_prev.click(this.search_prev.bind(this)),this.but_next=jQuery("<button/>",{type:"button","class":"btn btn-default","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-menu-right","aria-hidden":!0})),this.but_next.click(this.search_next.bind(this)),jQuery("<div/>",{"class":"btn-group",role:"group"}).append(this.but_prev).append(this.but_next).appendTo(jQuery("<div/>",{"class":"col-md-4"}).appendTo(this.filter_box)),this.content_box=jQuery("<div/>",{"class":"content-box"}),jQuery.isEmptyObject(this.tab_domain))this.tab=null;else{this.tab=jQuery("<div/>",{"class":"tab-domain"}).appendTo(this.el);var c=jQuery("<ul/>",{"class":"nav nav-tabs",role:"tablist"}).appendTo(this.tab);jQuery("<div/>",{"class":"tab-content"}).appendTo(this.tab);this.tab_domain.forEach(function(a,b){var d=a[0];jQuery("<li/>",{role:"presentation",id:"nav-"+b}).append(jQuery("<a/>",{"aria-controls":b,role:"tab","data-toggle":"tab",href:"#"+b}).append(d)).appendTo(c)}.bind(this)),c.find("a:first").tab("show");var d=this;c.find("a").click(function(a){a.preventDefault(),jQuery(this).tab("show"),d.do_search()})}this.el.append(this.content_box)},set_text:function(a){this.search_entry.val(a),this.bookmark_match()},update:function(){var a=this.screen.domain_parser().completion(this.get_text());this.search_list.children().remove(),a.forEach(function(a){jQuery("<option/>",{value:a.trim()}).appendTo(this.search_list)},this)},set_star:function(a){var b=this.but_star.children("span.glyphicon");a?(b.removeClass("glyphicon-star-empty"),b.addClass("glyphicon-star"),this.but_star.attr("aria-label",Sao.i18n.gettext("Remove this bookmark"))):(b.removeClass("glyphicon-star"),b.addClass("glyphicon-star-empty"),this.but_star.attr("aria-label",Sao.i18n.gettext("Bookmark this filter")))},get_star:function(){var a=this.but_star.children("span.glyphicon");return a.hasClass("glyphicon-star")},star_click:function(){var a=this.get_star(),b=this.screen.model_name,c=function(){this.bookmark_match(),this.but_bookmark.prop("disabled",jQuery.isEmptyObject(this.bookmarks()))}.bind(this);if(a){var d=this.bookmark_match();Sao.common.VIEW_SEARCH.remove(b,d).then(function(){c()})}else{var e=this.get_text();if(!e)return;Sao.common.ask.run(Sao.i18n.gettext("Bookmark Name:")).then(function(a){if(a){var d=this.screen.domain_parser().parse(e);Sao.common.VIEW_SEARCH.add(b,a,d).then(function(){c()}),this.set_text(this.screen.domain_parser().string(d))}}.bind(this))}},bookmarks:function(){var a=Sao.common.VIEW_SEARCH.get(this.screen.model_name);return a.filter(function(a){return this.screen.domain_parser().stringable(a[2])}.bind(this))},bookmark_activate:function(a){var b=a.data;this.set_text(this.screen.domain_parser().string(b)),this.do_search()},bookmark_match:function(){var a=this.get_text(),b=this.screen.domain_parser().parse(a);this.but_star.prop("disabled",!a);for(var c=(this.get_star(),this.bookmarks()),d=0;d<c.length;d++){var e=c[d][0],f=(c[d][1],c[d][2]),g=this.screen.domain_parser().string(f);if(g===a||Sao.common.compare(f,b))return this.set_star(!0),e}this.set_star(!1)},search_prev:function(){this.screen.search_prev(this.get_text())},search_next:function(){this.screen.search_next(this.get_text())},get_tab_domain:function(){if(!this.tab)return[];var a=this.tab.find("li").index(this.tab.find("li.active"));return this.tab_domain[a][1]},do_search:function(){return this.screen.search_filter(this.get_text())},key_press:function(a){return a.which==Sao.common.RETURN_KEYCODE?(this.do_search(),!1):void window.setTimeout(function(){this.bookmark_match()}.bind(this))},set_screen:function(a){this.screen=a,this.but_bookmark.prop("disabled",jQuery.isEmptyObject(this.bookmarks())),this.bookmark_match()},show_filter:function(){this.filter_box.show(),this.tab&&this.tab.show()},hide_filter:function(){this.filter_box.hide(),this.tab&&this.tab.hide()},set:function(a){this.alternate_view?(this.alternate_viewport.children().detach(),this.alternate_viewport.append(a)):(this.content_box.children().detach(),this.content_box.append(a))},get_text:function(){return this.search_entry.val()},search_box:function(){var a=this.screen.domain_parser(),b=function(){this.search_modal.modal("hide");for(var b="",c=a.quote.bind(a),d=0;d<this.search_form.fields.length;d++){var e,f=this.search_form.fields[d][0],g=this.search_form.fields[d][1];switch(g.type){case"selection":case"date":case"datetime":case"time":e=g.get_value(c);break;default:e=c(g.val())}e&&(b+=c(f)+": "+e+" ")}this.set_text(b),this.do_search().then(function(){this.last_search_text=this.get_text()}.bind(this))}.bind(this);if(!this.search_modal){var c=new Sao.Dialog(Sao.i18n.gettext("Filters"),"","lg");this.search_modal=c.modal,this.search_form=c.content,this.search_form.addClass("form-horizontal"),this.search_form.submit(b);var d,e=[];for(var f in a.fields)d=a.fields[f],(d.searchable||void 0===d.searchable)&&e.push(d);var g=function(a){return function(b){jQuery("<option/>",{value:b,text:b}).appendTo(a)}},h="filter-"+this.screen.model_name+"-";this.search_form.fields=[];for(var i=0;i<e.length;i++){d=e[i];var j,k,l=jQuery("<div/>",{"class":"form-group form-group-sm"}).append(jQuery("<label/>",{"class":"col-sm-4 control-label","for":h+d.name,text:d.string})).appendTo(c.body);switch(d.type){case"boolean":k=j=jQuery("<select/>",{"class":"form-control input-sm",id:h+d.name}),["",Sao.i18n.gettext("True"),Sao.i18n.gettext("False")].forEach(g(j));break;case"selection":k=new Sao.ScreenContainer.Selection(d.selection,h+d.name),j=k.el;break;case"date":case"datetime":case"time":var m,n=Sao.common.date_format();if("date"==d.type)m=n;else{var o=new Sao.PYSON.Decoder({}).decode(d.format);o=Sao.common.moment_format(o),"time"==d.type?m=o:"datetime"==d.type&&(m=n+" "+o)}k=new Sao.ScreenContainer.DateTimes(m,h+d.name),j=k.el;break;default:k=j=jQuery("<input/>",{"class":"form-control input-sm",type:"text",placeholder:d.string,id:h+d.name})}jQuery("<div/>",{"class":"col-sm-8"}).append(j).appendTo(l),this.search_form.fields.push([d.string,k])}jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Find")).click(b).appendTo(c.footer)}if(this.search_modal.modal("show"),this.last_search_text.trim()!==this.get_text().trim()){for(var p=0;p<this.search_form.fields.length;p++){var q=this.search_form.fields[p][1];switch(q.type){case"selection":q.set_value([]);break;case"date":case"datetime":case"time":q.set_value(null,null);break;default:q.val("")}}this.search_form.fields[0][1].focus()}}}),Sao.ScreenContainer.DateTimes=Sao.class_(Object,{type:"date",init:function(a,b){this.el=jQuery("<div/>",{"class":"row",id:b});var c=function(c){var d=jQuery("<div/>",{"class":"input-group input-group-sm"});return jQuery("<input/>",{"class":"form-control input-sm",type:"text",placeholder:c,id:b+"-from"}).appendTo(d),jQuery("<span/>",{"class":"input-group-btn"}).append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"}))).appendTo(d),d.datetimepicker(),d.data("DateTimePicker").format(a),d};this.from=c("From").appendTo(jQuery("<div/>",{"class":"col-md-5"}).appendTo(this.el)),jQuery("<p/>",{"class":"text-center"}).append("..").appendTo(jQuery("<div/>",{"class":"col-md-1"}).appendTo(this.el)),this.to=c("To").appendTo(jQuery("<div/>",{"class":"col-md-5"}).appendTo(this.el))},_get_value:function(a){return a.find("input").val()},get_value:function(a){var b=this._get_value(this.from),c=this._get_value(this.to);return b&&c?b!==c?a(b)+".."+a(c):a(b):b?">="+a(b):c?"<="+a(c):void 0},set_value:function(a,b){this.from.data("DateTimePicker").date(a),this.to.data("DateTimePicker").date(b)}}),Sao.ScreenContainer.Selection=Sao.class_(Object,{type:"selection",init:function(a,b){this.el=jQuery("<select/>",{"class":"form-control input-sm",multiple:!0,id:b}),a.forEach(function(a){jQuery("<option/>",{value:a[1],text:a[1]}).appendTo(this.el)}.bind(this))},get_value:function(a){var b=this.el.val();return b&&(b=b.reduce(function(b,c){return b&&(b+=";"),b+a(c)})),b},set_value:function(a){this.el.val(a)}}),Sao.Screen=Sao.class_(Object,{init:function(a,b){this.model_name=a,this.model=new Sao.Model(a,b),this.attributes=jQuery.extend({},b),this.attributes.limit=this.attributes.limit||Sao.config.limit,this.view_ids=jQuery.extend([],b.view_ids),this.view_to_load=jQuery.extend([],b.mode||["tree","form"]),this.views=[],this.views_preload=b.views_preload||{},this.exclude_field=b.exclude_field,this.context=b.context||{},this.new_group(),this.current_view=null,this.current_record=null,this.domain=b.domain||[],this.size_limit=null,this.limit=b.limit||Sao.config.limit,this.offset=0,Sao.common.MODELACCESS.get(a).write||(this.attributes.readonly=!0),this.search_count=0,this.screen_container=new Sao.ScreenContainer(b.tab_domain),b.row_activate?this.row_activate=b.row_activate:this.row_activate=this.default_row_activate,this.tree_states={},this.tree_states_done=[],this.fields_view_tree={},this._domain_parser={},this.pre_validate=!1,this.tab=null},load_next_view:function(){if(!jQuery.isEmptyObject(this.view_to_load)){var a;jQuery.isEmptyObject(this.view_ids)||(a=this.view_ids.shift());var b=this.view_to_load.shift();return this.add_view_id(a,b)}return jQuery.when()},add_view_id:function(a,b){var c;if(a&&this.views_preload[String(a)])c=this.views_preload[String(a)];else{if(a||!this.views_preload[b]){var d=this.model.execute("fields_view_get",[a,b],this.context);return d.pipe(this.add_view.bind(this))}c=this.views_preload[b]}return this.add_view(c),jQuery.when()},add_view:function(a){var b=a.arch,c=a.fields,d=a.view_id,e=jQuery(jQuery.parseXML(b));"tree"==e.children().prop("tagName")&&(this.fields_view_tree[d]=a);var f="eager";"form"==e.children().prop("tagName")&&(f="lazy");for(var g in c)g in this.model.fields&&"eager"!=f?c[g].loading=this.model.fields[g].description.loading:c[g].loading=f;this.model.add_fields(c);var h=Sao.View.parse(this,e,a.field_childs);return h.view_id=d,this.views.push(h),h},number_of_views:function(){return this.views.length+this.view_to_load.length},switch_view:function(a){if(this.current_view){if(!this.group.parent&&this.modified())return jQuery.when();this.current_view.set_value(),this.current_record&&!~this.current_record.group.indexOf(this.current_record)&&(this.current_record=null);var b=this.current_view.get_fields();if(this.current_record&&this.current_view.editable&&!this.current_record.validate(b,!1,!1,!0))return this.screen_container.set(this.current_view.el),this.current_view.display().done(function(){this.set_cursor()}.bind(this))}var c=function(){if(!a||!this.current_view||this.current_view.view_type!=a)for(var b=function(){return this.current_view=this.views[this.views.length-1],c()}.bind(this),d=0;d<this.number_of_views();d++){if(this.view_to_load.length)return a||(a=this.view_to_load[0]),this.load_next_view().then(b);if(this.current_view=this.views[(this.views.indexOf(this.current_view)+1)%this.views.length],!a)break;if(this.current_view.view_type==a)break}return this.screen_container.set(this.current_view.el),this.display().done(function(){this.set_cursor()}.bind(this))}.bind(this);return c()},search_filter:function(a){var b=[],c=this.domain_parser();c&&!this.group.parent?(b=a||""===a?c.parse(a):this.attributes.search_value,this.screen_container.set_text(c.string(b))):b=[["id","in",this.group.map(function(a){return a.id})]],b=!jQuery.isEmptyObject(b)&&this.attributes.domain?["AND",b,this.attributes.domain]:this.attributes.domain||[];var d=this.screen_container.get_tab_domain();jQuery.isEmptyObject(d)||(b=["AND",b,d]);var e=this.model.find(b,this.offset,this.limit,this.attributes.order,this.context),f=this.model.execute("search_count",[b],this.context);return f.done(function(a){this.search_count=a}.bind(this)),e.done(this.set_group.bind(this)),e.done(this.display.bind(this)),jQuery.when(e,f).done(function(a,b){this.screen_container.but_next.prop("disabled",!(a.length==this.limit&&b>this.limit+this.offset))}.bind(this)),this.screen_container.but_prev.prop("disabled",this.offset<=0),e},set_group:function(a){this.group&&(jQuery.extend(a.model.fields,this.group.model.fields),this.group.screens.splice(this.group.screens.indexOf(this),1),jQuery.extend(a.on_write,this.group.on_write),a.on_write=a.on_write.filter(function(a,b,c){return b==c.indexOf(a)})),a.screens.push(this),this.tree_states_done=[],this.group=a,this.model=a.model,jQuery.isEmptyObject(a)?this.set_current_record(null):this.set_current_record(a[0])},new_group:function(a){var b=new Sao.Group(this.model,this.context,[]);b.set_readonly(this.attributes.readonly||!1),a&&b.load(a),this.set_group(b)},set_current_record:function(a){this.current_record=a,this.tab&&(a?a.get_attachment_count().always(this.tab.attachment_count.bind(this.tab)):this.tab.attachment_count(0),this.tab.record_message())},display:function(a){var b=[];if(this.views){var c=this.search_active(~["tree","graph","calendar"].indexOf(this.current_view.view_type));b.push(c);for(var d=0;d<this.views.length;d++)this.views[d]&&b.push(this.views[d].display())}return jQuery.when.apply(jQuery,b).then(function(){this.set_tree_state(),this.set_current_record(this.current_record),a&&this.set_cursor(!1,!1)}.bind(this))},display_next:function(){var a=this.current_view;if(a.set_value(),this.set_cursor(!1,!1),~["tree","form"].indexOf(a.view_type)&&this.current_record&&this.current_record.group){for(var b=this.current_record.group,c=this.current_record;b;){var d=b.indexOf(c);if(d<b.length-1){c=b[d+1];break}if(!b.parent||c.group.model_name!=b.parent.group.model_name)break;c=b.parent,b=b.parent.group}this.set_current_record(c)}else this.set_current_record(this.group[0]);
+this.set_cursor(!1,!1),a.display()},display_previous:function(){var a=this.current_view;if(a.set_value(),this.set_cursor(!1,!1),~["tree","form"].indexOf(a.view_type)&&this.current_record&&this.current_record.group){for(var b=this.current_record.group,c=this.current_record;b;){var d=b.indexOf(c);if(d>0){c=b[d-1];break}if(!b.parent||c.group.model_name!=b.parent.group.model_name)break;c=b.parent,b=b.parent.group}this.set_current_record(c)}else this.set_current_record(this.group[0]);this.set_cursor(!1,!1),a.display()},default_row_activate:function(){"tree"==this.current_view.view_type&&this.current_view.attributes.keyword_open?Sao.Action.exec_keyword("tree_open",{model:this.model_name,id:this.get_id(),ids:[this.get_id()]},jQuery.extend({},this.context),!1):this.switch_view("form")},get_id:function(){return this.current_record?this.current_record.id:void 0},new_:function(a){void 0===a&&(a=!0);var b=jQuery.when();return this.current_view&&("tree"==this.current_view.view_type&&!this.current_view.editable||"graph"==this.current_view.view_type)&&(b=this.switch_view("form")),b.then(function(){var b;b=this.current_record?this.current_record.group:this.group;var c=b.new_(a);return b.add(c,this.new_model_position()),this.set_current_record(c),this.display().done(function(){this.set_cursor(!0,!0)}.bind(this)),c}.bind(this))},new_model_position:function(){var a=-1;return this.current_view&&"tree"==this.current_view.view_type&&"top"==this.current_view.attributes.editable&&(a=0),a},set_on_write:function(a){a&&(~this.group.on_write.indexOf(a)||this.group.on_write.push(a))},cancel_current:function(){var a=[];return this.current_record&&(this.current_record.cancel(),this.current_record.id<0&&a.push(this.remove())),jQuery.when.apply(jQuery,a)},save_current:function(){var a=this.current_record;if(!a){if("tree"!=this.current_view.view_type||jQuery.isEmptyObject(this.group))return jQuery.when();this.set_current_record(this.group[0])}this.current_view.set_value();var b=this.current_view.get_fields(),c=a.get_path(this.group),d=jQuery.Deferred();"tree"==this.current_view.view_type?d=this.group.save():a.validate(b).then(function(b){b?a.save().then(d.resolve,d.reject):(this.current_view.display().done(this.set_cursor.bind(this)),d.reject())}.bind(this));var e=jQuery.Deferred();return d=d.then(function(){return c&&a.id&&c.splice(-1,1,[c[c.length-1][0],a.id]),this.group.get_by_path(c).then(function(a){this.set_current_record(a)}.bind(this))}.bind(this)),d.then(function(){this.display().always(e.resolve)}.bind(this),function(){this.display().always(e.reject)}.bind(this)),e.promise()},set_cursor:function(a,b){this.current_view&&~["tree","form"].indexOf(this.current_view.view_type)&&this.current_view.set_cursor(a,b)},modified:function(){var a=function(a){return a.has_changed()||a.id<0};if("tree"!=this.current_view.view_type){if(this.current_record&&a(this.current_record))return!0}else if(this.group.some(a))return!0;return!1},unremove:function(){var a=this.current_view.selected_records();a.forEach(function(a){a.group.unremove(a)})},remove:function(a,b,c){var d=null;if("form"==this.current_view.view_type&&this.current_record?d=[this.current_record]:"tree"==this.current_view.view_type&&(d=this.current_view.selected_records()),!jQuery.isEmptyObject(d)){var e=jQuery.when();a&&(e=this.model.delete_(d));var f=d[0],g=f.group,h=g.indexOf(f),i=f.get_path(this.group);return e.then(function(){d.forEach(function(a){a.group.remove(a,b,!0,c)});var e=[];if(a&&d.forEach(function(a){a.group.parent&&e.push(a.group.parent.save(!1)),~a.group.record_deleted.indexOf(a)&&a.group.record_deleted.splice(a.group.record_deleted.indexOf(a),1),~a.group.record_removed.indexOf(a)&&a.group.record_removed.splice(a.group.record_removed.indexOf(a),1)}),h>0){var f=g[h-1];i.splice(-1,1,[i[i.length-1][0],f.id])}else i.splice(-1,1);return jQuery.isEmptyObject(i)?this.group.length&&this.set_current_record(this.group[0]):e.push(this.group.get_by_path(i).then(function(a){this.set_current_record(a)}.bind(this))),jQuery.when.apply(jQuery,e).then(function(){this.display().done(function(){this.set_cursor()}.bind(this))}.bind(this))}.bind(this))}},copy:function(){var a=jQuery.Deferred(),b=this.current_view.selected_records();return this.model.copy(b,this.context).then(function(b){this.group.load(b),jQuery.isEmptyObject(b)||this.set_current_record(this.group.get(b[0])),this.display().always(a.resolve)}.bind(this),a.reject),a.promise()},search_active:function(a){return a&&!this.group.parent?(this.screen_container.set_screen(this),this.screen_container.show_filter()):this.screen_container.hide_filter(),jQuery.when()},domain_parser:function(){var a,b;if(a=this.current_view?this.current_view.view_id:null,a in this._domain_parser)return this._domain_parser[a];a in this.fields_view_tree?b=this.fields_view_tree[a]:(this.model.execute("fields_view_get",[!1,"tree"],this.context).then(function(b){this.fields_view_tree[a]=b}.bind(this)),b={},b.fields={});var c=jQuery.extend({},b.fields),d=function(a){return function(b){a.selection=b}};for(var e in c){var f=c[e];("selection"==f.type||"reference"==f.type)&&(f.selection instanceof Array||this.get_selection(f).then(d))}if("arch"in b){var g=jQuery(jQuery.parseXML(b.arch)),h=g.find("tree").children().filter(function(a){return"field"==a.tagName}).map(function(a){return a.getAttribute("name")}),i={};h.each(function(a){i[a]=c[a]})}[["id",Sao.i18n.gettext("ID"),"integer"],["create_uid",Sao.i18n.gettext("Creation User"),"many2one"],["create_date",Sao.i18n.gettext("Creation Date"),"datetime"],["write_uid",Sao.i18n.gettext("Modification User"),"many2one"],["write_date",Sao.i18n.gettext("Modification Date"),"datetime"]].forEach(function(a){var b=a[0],d=a[1],e=a[2];b in c||(c[b]={string:d,name:b,type:e},"datetime"==e&&(c[b].format='"%H:%M:%S"'))}),"id"in c||(c.id={string:Sao.i18n.gettext("ID"),name:"id",type:"integer"});var j=jQuery.extend({},this.model.session.context,this.context),k=new Sao.common.DomainParser(c,j);return this._domain_parser[a]=k,k},get_selection:function(a){var b,c=a.selection_change_with;if(jQuery.isEmptyObject(c))b=this.model.execute(a.selection,[]);else{var d={};c.forEach(function(a){d[a]=null}),b=this.model.execute(a.selection,[d])}return b.then(function(a){return a.sort(function(a,b){return a[1].localeCompare(b[1])})})},search_prev:function(a){this.offset-=this.limit,this.search_filter(a)},search_next:function(a){this.offset+=this.limit,this.search_filter(a)},invalid_message:function(a){a||(a=this.current_record);var b={};for(var c in a.model.fields){var d=a.model.fields[c];b[c]=d.description}var e=new Sao.common.DomainParser(b),f=[],g=a.invalid_fields();return Object.keys(g).sort().forEach(function(b){var c=g[b],d=a.model.fields[b].description.string;"required"==c||Sao.common.compare(c,[[b,"!=",null]])?f.push(Sao.i18n.gettext('"%1" is required',d)):"domain"==c?f.push(Sao.i18n.gettext('"%1" is not valid according to its domain',d)):"children"==c?f.push(Sao.i18n.gettext('The values of "%1" are not valid',d)):e.stringable(c)?f.push(e.string(c)):f.push(Sao.i18n.gettext('"%1" is not valid according to its domain'),d)}),f.length>5&&(f.splice(5,f.length),f.push("...")),f.join("\n")},get:function(){return this.current_record?(this.current_view.set_value(),this.current_record.get()):null},get_on_change_value:function(){return this.current_record?(this.current_view.set_value(),this.current_record.get_on_change_value()):null},reload:function(a,b){this.group.reload(a),b&&this.group.written(a),this.group.parent&&this.group.parent.root_parent().reload(),this.display()},get_buttons:function(){var a=this.current_view.selected_records();if(jQuery.isEmptyObject(a))return[];var b=this.current_view.get_buttons();return a.forEach(function(a){b=b.filter(function(b){if(a.group.get_readonly()||a.readonly())return!1;if("instance"===b.attributes.type)return!1;var c=a.expr_eval(b.attributes.states||{});return!(c.invisible||c.readonly)})}),b},button:function(a){var b,c=function(a){this.reload(b,!0),"string"==typeof a?this.client_action(a):a&&Sao.Action.execute(a,{model:this.model_name,id:i.id,ids:b},null,this.context)},d=this.current_view.selected_records();this.current_view.set_value();for(var e=this.current_view.get_fields(),f=[],g=function(a){return function(){this.display(!0),a.validate(e)}.bind(this)}.bind(this),h=0;h<d.length;h++){var i=d[h],j=i.expr_eval(a.states||{}).pre_validate||[];f.push(i.validate(e,!1,j))}jQuery.when.apply(jQuery,f).then(function(){for(var e,f=0;f<d.length;f++){e=d[f];var h=arguments[f];if(!h)return void Sao.common.warning.run(this.invalid_message(e),Sao.i18n.gettext("Pre-validation")).then(g(e))}if(e=this.current_record,"instance"===a.type){var i=e.expr_eval(a.change||[]),j=e._get_on_change_args(i);e.model.execute(a.name,[j],this.context).then(function(a){e.set_on_change(a),e.group.root_group().screens.forEach(function(a){a.display()})})}else{var k=jQuery.extend({},this.context);for(k._timestamp={},f=0;f<d.length;f++)e=d[f],jQuery.extend(k._timestamp,e.get_timestamp());e.save(!1).done(function(){b=d.map(function(a){return a.id}),e.model.execute(a.name,[b],k).then(c.bind(this))}.bind(this))}}.bind(this))},client_action:function(a){var b=Sao.common.MODELACCESS.get(this.model_name);if("new"==a)b.create&&this.new_();else if("delete"==a)b["delete"]&&this.remove(!this.group.parent,!1,!this.group.parent);else if("remove"==a)b.write&&b.read&&this.group.parent&&this.remove(!1,!0,!1);else if("copy"==a)b.create&&this.copy();else if("next"==a)this.display_next();else if("previous"==a)this.display_previous();else if("close"==a)Sao.Tab.close_current();else if(a.startsWith("switch")){var c=a.split(" ")[1];this.switch_view(c)}else"reload"==a?~["tree","graph","calendar"].indexOf(this.current_view.view_type)&&!this.group.parent&&this.search_filter():"reload menu"==a?Sao.get_preferences().then(function(a){Sao.menu(a)}):"reload context"==a&&Sao.get_preferences()},save_tree_state:function(a){var b,c=[];a=void 0===a?!0:a;var d,e,f,g,h,i,j=this.group.parent?this.group.parent.id:null,k=this.group.parent?this.group.parent._timestamp:null;for(d=0,e=this.views.length;e>d;d++)if(f=this.views[d],"form"==f.view_type){for(var l in f.widgets)if(f.widgets.hasOwnProperty(l))for(g=f.widgets[l],h=0,i=g.length;i>h;h++)g[h].screen&&(b=g[h].screen.save_tree_state(a),c.push(b));1==this.views.length&&this.current_record&&(j in this.tree_states||(this.tree_states[j]={}),this.tree_states[j][f.children_field||null]=[k,[],[[this.current_record.id]]])}else if("tree"==f.view_type){var m=f.get_expanded_paths(),n=f.get_selected_paths();if(j in this.tree_states||(this.tree_states[j]={}),this.tree_states[j][f.children_field||null]=[k,m,n],a&&f.attributes.tree_state){var o=new Sao.Model("ir.ui.view_tree_state");b=o.execute("set",[this.model_name,this.get_tree_domain(j),f.children_field,JSON.stringify(m),JSON.stringify(n)],{}),c.push(b)}}return jQuery.when.apply(jQuery,c)},get_tree_domain:function(a){var b;return b=a?(this.domain||[]).concat([[this.exclude_field,"=",a]]):this.domain,JSON.stringify(Sao.rpc.prepareObject(b))},set_tree_state:function(){var a,b,c,d,e,f=this.current_view;~["tree","form"].indexOf(f.view_type)&&(~this.tree_states_done.indexOf(f)||("form"!=f.view_type||jQuery.isEmptyObject(this.tree_states_done))&&(a=this.group.parent?this.group.parent.id:null,b=parent?parent._timestamp:null,a in this.tree_states||(this.tree_states[a]={}),c=this.tree_states[a][f.children_field||null],c&&b!=c[0]&&(c=void 0),void 0===c?(e=new Sao.Model("ir.ui.view_tree_state"),d=e.execute("get",[this.model_name,this.get_tree_domain(a),f.children_field],{}).then(function(a){return[b,JSON.parse(a[0]),JSON.parse(a[1])]})):d=jQuery.when(c),d.done(function(b){var c,d,e;if(this.tree_states[a][f.children_field||null]=b,c=b[1],d=b[2],"tree"==f.view_type)f.display(d,c);else if(!jQuery.isEmptyObject(d)){for(var g=0;g<d[0].length;g++){var h=this.group.get(d[0][g]);if(!h)break;e=h}e&&e!=this.current_record&&(this.set_current_record(e),f.display())}}.bind(this)),this.tree_states_done.push(f)))}})}(),function(){"use strict";Sao.View=Sao.class_(Object,{init:function(a,b){this.screen=a,this.view_type=null,this.el=null,this.view_id=null,this.fields={};var c=b.children()[0].attributes;this.attributes={};for(var d=0,e=c.length;e>d;d++){var f=c[d];this.attributes[f.name]=f.value}a.set_on_write(this.attributes.on_write)},set_value:function(){},get_fields:function(){return Object.keys(this.fields)},selected_records:function(){return[]},get_buttons:function(){return[]}}),Sao.View.idpath2path=function(a,b){var c,d=[];if(!b)return[];for(var e=0,f=a.rows.length;f>e;e++)if(a.rows[e].record.id==b[0]){d.push(e),c=Sao.View.idpath2path(a.rows[e],b.slice(1,b.length)),d=d.concat(c);break}return d},Sao.View.parse=function(a,b,c){switch(b.children().prop("tagName")){case"tree":return new Sao.View.Tree(a,b,c);case"form":return new Sao.View.Form(a,b);case"graph":return new Sao.View.Graph(a,b);case"calendar":return new Sao.View.Calendar(a,b)}},Sao.View.resize=function(a){a||(a=jQuery(document)),a.find(".treeview").each(function(){var a=jQuery(this);a.css("width","100%"),a.children(".tree").css("table-layout","fixed")}),a.find(".treeview").each(function(){var a=jQuery(this);a.width()&&(a.css("width",a.width()),a.children(".tree").css("table-layout","auto"))})},jQuery(window).resize(function(){Sao.View.resize()})}(),function(){"use strict";Sao.View.Form=Sao.class_(Sao.View,{editable:!0,init:function(a,b){Sao.View.Form._super.init.call(this,a,b),this.view_type="form",this.el=jQuery("<div/>",{"class":"form"}),this.widgets={},this.widget_id=0,this.state_widgets=[],this.containers=[],this.notebooks=[];var c=b.children()[0],d=this.parse(a.model,c);this.el.append(d.el)},_parse_node:function(a,b,c,d,e){var f;switch(b.tagName){case"image":this._parse_image(a,b,c,d);break;case"separator":this._parse_separator(a,b,c,d);break;case"label":f=this._parse_label(a,b,c,d),d.name&&f&&(e[d.name]=f);break;case"newline":c.add_row();break;case"button":this._parse_button(b,c,d);break;case"notebook":this._parse_notebook(a,b,c,d);break;case"page":this._parse_page(a,b,c,d);break;case"field":if(f=this._parse_field(a,b,c,d),d.name in e&&f&&f.labelled){var g=e[d.name];g.el.uniqueId(),f.labelled.uniqueId(),f.labelled.attr("aria-labelledby",g.el.attr("id")),g.el.attr("for",f.labelled.attr("id"))}break;case"group":this._parse_group(a,b,c,d);break;case"hpaned":this._parse_paned(a,b,c,d,"horizontal");break;case"vpaned":this._parse_paned(a,b,c,d,"vertical");break;case"child":this._parse_child(a,b,c,d)}},parse:function(a,b,c){void 0===c&&(c=new Sao.View.Form.Container(Number(b.getAttribute("col")||4)),this.containers.push(c));var d={},e=function(b,e){for(var f={},g=0,h=e.attributes.length;h>g;g++){var i=e.attributes[g];f[i.name]=i.value}["readonly","invisible"].forEach(function(a){f[a]&&(f[a]=1==f[a])}),["yexpand","yfill","xexpand","xfill","colspan"].forEach(function(a){f[a]&&(f[a]=Number(f[a]))}),this._parse_node(a,e,c,f,d)};return jQuery(b).children().each(e.bind(this)),c},_parse_image:function(a,b,c,d){var e=new Sao.View.Form.Image_(d);this.state_widgets.push(e),c.add(d,e)},_parse_separator:function(a,b,c,d){var e=d.name,f=d.string;e in a.fields&&(!d.states&&e in a.fields&&(d.states=a.fields[e].description.states),f||(f=a.fields[e].description.string));var g=new Sao.View.Form.Separator(f,d);this.state_widgets.push(g),c.add(d,g)},_parse_label:function(a,b,c,d){var e=d.name,f=d.string;if(void 0===d.xexpand&&(d.xexpand=0),e in a.fields){if(e==this.screen.exclude_field)return void c.add(d);!d.states&&e in a.fields&&(d.states=a.fields[e].description.states),f||(f=a.fields[e].description.string+": "),void 0===d.xalign&&(d.xalign=1)}var g;return f&&(g=new Sao.View.Form.Label(f,d),this.state_widgets.push(g)),c.add(d,g),g},_parse_button:function(a,b,c){var d=new Sao.common.Button(c);this.state_widgets.push(d),b.add(c,d),d.el.click(d,this.button_clicked.bind(this))},_parse_notebook:function(a,b,c,d){void 0===d.colspan&&(d.colspan=4);var e=new Sao.View.Form.Notebook(d);this.notebooks.push(e),this.state_widgets.push(e),c.add(d,e),this.parse(a,b,e)},_parse_page:function(a,b,c,d){d.string;if(d.name in a.fields){var e=a.fields[d.name];if(d.name==this.screen.exclude_field)return;["states","string"].forEach(function(a){void 0===d[a]&&void 0!==e.description[a]&&(d[a]=e.description[a])})}var f=this.parse(a,b);f=new Sao.View.Form.Page(c.add(f.el,d.string),d),this.state_widgets.push(f)},_parse_field:function(a,b,c,d){var e=d.name;if(!(e in a.fields)||e==this.screen.exclude_field)return void c.add(d);d.widget||(d.widget=a.fields[e].description.type);var f=["relation","domain","selection","help","relation_field","string","views","add_remove","sort","context","size","filename","autocomplete","translate","create","delete","selection_change_with","schema_model"];for(var g in f){var h=f[g];h in a.fields[e].description&&null===b.getAttribute(h)&&(d[h]=a.fields[e].description[h])}var i=Sao.View.form_widget_get(d.widget);if(!i)return void c.add(d);var j=new i(e,a,d);return j.position=this.widget_id+=1,j.view=this,c.add(d,j),void 0===this.widgets[e]&&(this.widgets[e]=[]),this.widgets[e].push(j),this.fields[e]=!0,j},_parse_group:function(a,b,c,d){var e=new Sao.View.Form.Group(d);e.add(this.parse(a,b)),this.state_widgets.push(e),c.add(d,e)},_parse_paned:function(a,b,c,d,e){void 0===d.yexpand&&(d.yexpand=!0),void 0===d.yfill&&(d.yfill=!0);var f=new Sao.common.Paned(e);c.add(d,f),this.parse(a,b,f)},_parse_child:function(a,b,c,d){var e,f=this.parse(a,b);e=c.get_child1().children().length?c.get_child2():c.get_child1(),e.append(f.el)},get_buttons:function(){var a=[];for(var b in this.state_widgets){var c=this.state_widgets[b];c instanceof Sao.common.Button&&a.push(c)}return a},display:function(){var a,b,c=this.screen.current_record,d={};if(c){var e=[];for(b in c.model.fields)a=c.model.fields[b],e.push([b,a.description.loading||"eager"]);e.sort(function(a,b){return a[1].localeCompare(b[1])}),e.forEach(function(a){var b=a[0];d[b]=c.load(b)})}var f=function(a,b,c){var e=jQuery.when();c in d&&(e=d[c]),e.done(function(){b.set_state(a)})},g=function(a,b,c){return function(e){var f=jQuery.when();c in d&&(f=d[c]),f.done(function(){e.display(a,b)})}};for(b in this.widgets){var h=this.widgets[b];a=null,c&&(a=c.model.fields[b]),a&&f(c,a,b),h.forEach(g(c,a,b))}return jQuery.when.apply(jQuery,jQuery.map(d,function(a){return a})).done(function(){var a;for(a in this.state_widgets){var b=this.state_widgets[a];b.set_state(c)}for(a in this.containers){var d=this.containers[a];d.resize()}Sao.View.resize(this.el)}.bind(this))},set_value:function(){var a=this.screen.current_record;if(a){var b=function(b){b.set_value(a,this)};for(var c in this.widgets)if(c in a.model.fields){var d=this.widgets[c],e=a.model.fields[c];d.forEach(b,e)}}},button_clicked:function(a){var b=a.data;this.screen.button(b.attributes)},selected_records:function(){return this.screen.current_record?[this.screen.current_record]:[]},set_cursor:function(a,b){var c,d,e,f,g,h,i,j,k,l,m=jQuery(document.activeElement),n=m.closest(this.el)>0;if(b||n){if(b)for(c=0;c<this.notebooks.length;c++)g=this.notebooks[c],g.set_current_page(0);this.attributes.cursor in this.widgets?f=Sao.common.find_focusable_child(this.widgets[this.attributes.cursor][0].el):(h=Sao.common.find_focusable_child(this.el),h&&h.focus())}var o=this.screen.current_record;if(o){var p=[],q=this.el.find(".has-error"),r=o.invalid_fields();for(d in r)for(i=this.widgets[d],c=0;c<q.length;c++)for(j=jQuery(q[c]),e=0;e<i.length;e++)if(j.closest(i[e].el).length>0){p.push(j);break}p.length>0&&(f=Sao.common.find_first_focus_widget(this.el,p))}if(f){for(c=0;c<this.notebooks.length;c++)for(g=this.notebooks[c],k=g.get_n_pages(),e=0;k>e;e++)if(h=g.get_nth_page(e),l=jQuery(f).closest(h).length>0){g.set_current_page(e);break}jQuery(f).find("input,select,textarea").focus()}}}),Sao.View.Form.Container=Sao.class_(Object,{init:function(a){void 0===a&&(a=4),this.col=a,this.el=jQuery("<table/>",{"class":"form-container responsive responsive-noheader"}),this.add_row()},add_row:function(){this.el.append(jQuery("<tr/>"))},rows:function(){return this.el.children().children("tr")},row:function(){return this.rows().last()},add:function(a,b){var c=a.colspan;void 0===c&&(c=1);var d=a.xfill;void 0===d&&(d=1);var e=a.xexpand;void 0===e&&(e=1);var f=0,g=this.row();g.children().map(function(a,b){f+=Number(jQuery(b).attr("colspan")||1)}),f+c>this.col&&(this.add_row(),g=this.row());var h;b&&(h=b.el);var i=jQuery("<td/>",{colspan:c,"class":b?b.class_||"":""}).append(h);g.append(i),b&&(a.yfill&&i.css("vertical-align","top"),void 0!==a.xalign&&i.css("text-align",a.xalign>=.5?"right":"left"),e&&(i.addClass("xexpand"),i.css("width","100%")),d&&(i.addClass("xfill"),e&&h.css("width","100%")),a.help&&(b.el.data("toggle","tooltip"),b.el.attr("title",a.help),b.el.tooltip()))},resize:function(){var a,b,c=this.rows().toArray(),d=[],e=this.col,f=!1,g=function(b){b=jQuery(b);var c=[];return a=0,b.children().map(function(){var b=jQuery(this),d=Math.min(Number(b.attr("colspan")),e);b.hasClass("xexpand")&&!jQuery.isEmptyObject(b.children())&&"none"!=b.children(":not(.tooltip)").css("display")&&c.push([b,a]),a+=d}),c};c.sort(function(a,b){if(a=g(a),b=g(b),a.length==b.length){var c=function(a,b){var c=b[0],d=Math.min(Number(c.attr("colspan")),e);return a+d};return a.reduce(c,0)-b.reduce(c,0)}return b.length-a.length}),c.forEach(function(c){c=jQuery(c);var h=g(c),i=100/h.length;h.forEach(function(c){var f=c[0];a=c[1];var g=Math.min(Number(f.attr("colspan")),e),h=0;for(b=0;g>b;b++)h+=d[a+b]||0;for(b=0;g>b;b++)if(h){if(h>i){var j=h-i;d[a+b]&&(d[a+b]-=j/(h/d[a+b]))}}else d[a+b]=i/g}),jQuery.isEmptyObject(h)||(f=!0)}),c.forEach(function(c){c=jQuery(c),a=0,c.children().map(function(){var c=jQuery(this),f=Math.min(Number(c.attr("colspan")),e);if(c.hasClass("xexpand")&&"none"!=c.children(":not(.tooltip)").css("display")){var g=0;for(b=0;f>b;b++)g+=d[a+b]||0;c.css("width",g+"%")}else c.css("width","");"none"==c.children().css("display")?c.css("visibility","collapse"):c.css("visibility","visible"),a+=f})}),f?this.el.css("width","100%"):this.el.css("width","")}}),Sao.View.Form.StateWidget=Sao.class_(Object,{init:function(a){this.attributes=a},set_state:function(a){var b;b=a?a.expr_eval(this.attributes.states||{}):{};var c=b.invisible;void 0===c&&(c=this.attributes.invisible),c?this.hide():this.show()},show:function(){this.el.show()},hide:function(){this.el.hide()}}),Sao.View.Form.Separator=Sao.class_(Sao.View.Form.StateWidget,{init:function(a,b){Sao.View.Form.Separator._super.init.call(this,b),this.el=jQuery("<div/>",{"class":"form-separator"}),a&&this.el.append(jQuery("<label/>",{text:a})),this.el.append(jQuery("<hr/>"))}}),Sao.View.Form.Label=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-label",init:function(a,b){Sao.View.Form.Label._super.init.call(this,b),this.el=jQuery("<label/>",{text:a,"class":this.class_+" form-label"})},set_state:function(a){Sao.View.Form.Label._super.set_state.call(this,a);var b;if(this.attributes.name&&a&&(b=a.model.fields[this.attributes.name]),void 0===this.attributes.string&&b){var c="";a&&(c=b.get_client(a)||""),this.el.val(c)}var d;d=a?a.expr_eval(this.attributes.states||{}):{},b&&b.description.required||d.required?this.el.addClass("required"):this.el.removeClass("required"),b&&b.description.readonly||d.readonly?(this.el.removeClass("editable"),this.el.removeClass("required")):this.el.addClass("editable")}}),Sao.View.Form.Notebook=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-notebook",init:function(a){Sao.View.Form.Notebook._super.init.call(this,a),this.el=jQuery("<div/>",{"class":this.class_}),this.nav=jQuery("<ul/>",{"class":"nav nav-tabs",role:"tablist"}).appendTo(this.el),this.panes=jQuery("<div/>",{"class":"tab-content"}).appendTo(this.el),this.selected=!1},add:function(a,b){var c=jQuery("<div/>",{role:"tabpanel","class":"tab-pane"}).uniqueId(),d=c.attr("id"),e=jQuery("<li/>",{role:"presentation"}).append(jQuery("<a/>",{"aria-controls":d,role:"tab","data-toggle":"tab",href:"#"+d}).append(b).on("shown.bs.tab",function(){Sao.View.resize(a)})).appendTo(this.nav);return c.html(a).appendTo(this.panes),this.selected||(e.addClass("active"),c.addClass("active"),this.selected=!0),e},set_current_page:function(a){var b=this.nav.find('li[role="presentation"]:eq('+a+") a");b.tab("show")},get_n_pages:function(){return this.nav.find("li[role='presentation']").length},get_nth_page:function(a){return jQuery(this.panes.find("div[role='tabpanel']")[a])}}),Sao.View.Form.Page=Sao.class_(Sao.View.Form.StateWidget,{init:function(a,b){Sao.View.Form.Page._super.init.call(this,b),this.el=a}}),Sao.View.Form.Group=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-group_",init:function(a){Sao.View.Form.Group._super.init.call(this,a),this.el=jQuery("<div/>",{"class":this.class_})},add:function(a){this.el.append(a.el)}}),Sao.View.Form.Image_=Sao.class_(Sao.View.Form.StateWidget,{class_:"form-image_",init:function(a){Sao.View.Form.Image_._super.init.call(this,a),this.el=jQuery("<div/>",{class_:this.class_}),this.img=jQuery("<img/>",{"class":"center-block"}).appendTo(this.el),Sao.common.ICONFACTORY.register_icon(a.name).done(function(a){this.img.attr("src",a)}.bind(this))}}),Sao.View.form_widget_get=function(a){switch(a){case"char":return Sao.View.Form.Char;case"password":return Sao.View.Form.Password;case"date":return Sao.View.Form.Date;case"datetime":return Sao.View.Form.DateTime;case"time":return Sao.View.Form.Time;case"timedelta":return Sao.View.Form.TimeDelta;case"integer":case"biginteger":return Sao.View.Form.Integer;case"float":case"numeric":return Sao.View.Form.Float;case"selection":return Sao.View.Form.Selection;case"boolean":return Sao.View.Form.Boolean;case"text":return Sao.View.Form.Text;case"richtext":return Sao.View.Form.RichText;case"many2one":return Sao.View.Form.Many2One;case"one2one":return Sao.View.Form.One2One;case"reference":return Sao.View.Form.Reference;case"one2many":return Sao.View.Form.One2Many;case"many2many":return Sao.View.Form.Many2Many;case"binary":return Sao.View.Form.Binary;case"multiselection":return Sao.View.Form.MultiSelection;case"image":return Sao.View.Form.Image;case"url":return Sao.View.Form.URL;case"email":return Sao.View.Form.Email;case"callto":return Sao.View.Form.CallTo;case"sip":return Sao.View.Form.SIP;case"progressbar":return Sao.View.Form.ProgressBar;case"dict":return Sao.View.Form.Dict}},Sao.View.Form.Widget=Sao.class_(Object,{init:function(a,b,c){this.field_name=a,this.model=b,this.view=null,this.attributes=c,this.el=null,this.position=0,this.visible=!0,this.labelled=null},display:function(a,b){var c=this.attributes.readonly,d=this.attributes.invisible;if(!b)return void 0===c&&(c=!0),void 0===d&&(d=!1),this.set_readonly(c),void this.set_invisible(d);var e=b.get_state_attrs(a);void 0===c&&(c=e.readonly,void 0===c&&(c=!1)),this.view.screen.attributes.readonly&&(c=!0),this.set_readonly(c);var f=e.invalid;!c&&f?this.el.addClass("has-error"):this.el.removeClass("has-error"),void 0===d&&(d=b.get_state_attrs(a).invisible,void 0===d&&(d=!1)),this.set_invisible(d)},record:function(){return this.view&&this.view.screen?this.view.screen.current_record:void 0},field:function(){var a=this.record();return a?a.model.fields[this.field_name]:void 0},focus_out:function(){this.field()&&this.visible&&this.set_value(this.record(),this.field())},set_value:function(a,b){},set_readonly:function(a){this.el.prop("disabled",a)},set_invisible:function(a){this.visible=!a,a?this.el.hide():this.el.show()}}),Sao.View.Form.Char=Sao.class_(Sao.View.Form.Widget,{class_:"form-char",init:function(a,b,c){Sao.View.Form.Char._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.group=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el),this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.group),c.autocomplete&&(this.datalist=jQuery("<datalist/>").appendTo(this.el),this.datalist.uniqueId(),this.input.attr("list",this.datalist.attr("id"))),this.el.change(this.focus_out.bind(this)),c.size||this.group.css("width","100%")},display:function(a,b){if(Sao.View.Form.Char._super.display.call(this,a,b),this.datalist){this.datalist.children().remove();var c=[];a&&(c=a.autocompletion[this.field_name]||[]),c.forEach(function(a){jQuery("<option/>",{value:a}).appendTo(this.datalist)}.bind(this))}var d="",e="100%";if(a&&(d=a.expr_eval(this.attributes.size),d>0&&(e=null)),this.input.attr("maxlength",d),this.input.attr("size",d),this.group.css("width",e),a){var f=a.field_get_client(this.field_name);this.input.val(f||"")}else this.input.val("")},set_value:function(a,b){b.set_client(a,this.input.val())},set_readonly:function(a){this.input.prop("readonly",a)},focus:function(){this.input.focus()}}),Sao.View.Form.Password=Sao.class_(Sao.View.Form.Char,{class_:"form-password",init:function(a,b,c){Sao.View.Form.Password._super.init.call(this,a,b,c),this.input.prop("type","password")}}),Sao.View.Form.Date=Sao.class_(Sao.View.Form.Widget,{class_:"form-date",_width:"12em",init:function(a,b,c){Sao.View.Form.Date._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.date=this.labelled=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el),this.input=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.date),jQuery("<span/>",{"class":"input-group-btn"}).append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"}))).appendTo(this.date),this.date.datetimepicker(),this.date.css("width",this._width),this.date.on("dp.change",this.focus_out.bind(this))},get_format:function(a,b){return b.date_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isDate=!0),c},display:function(a,b){a&&b&&this.date.data("DateTimePicker").format(Sao.common.moment_format(this.get_format(a,b))),Sao.View.Form.Date._super.display.call(this,a,b);var c;c=a?b.get_client(a):null,this.date.data("DateTimePicker").date(c)},focus:function(){this.input.focus()},set_value:function(a,b){b.set_client(a,this.get_value(a,b))},set_readonly:function(a){this.date.find("button").prop("disabled",a),this.date.find("input").prop("readonly",a)}}),Sao.View.Form.DateTime=Sao.class_(Sao.View.Form.Date,{class_:"form-datetime",_width:"25em",get_format:function(a,b){return b.date_format(a)+" "+b.time_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isDateTime=!0),c}}),Sao.View.Form.Time=Sao.class_(Sao.View.Form.Date,{class_:"form-time",_width:"10em",get_format:function(a,b){return b.time_format(a)},get_value:function(a,b){var c=this.date.data("DateTimePicker").date();return c&&(c.isTime=!0),c}}),Sao.View.Form.TimeDelta=Sao.class_(Sao.View.Form.Widget,{class_:"form-timedelta",init:function(a,b,c){Sao.View.Form.TimeDelta._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(this.el),this.el.change(this.focus_out.bind(this))},display:function(a,b){if(Sao.View.Form.TimeDelta._super.display.call(this,a,b),a){var c=a.field_get_client(this.field_name);this.input.val(c||"")}else this.input.val("")},focus:function(){this.input.focus()},set_value:function(a,b){b.set_client(a,this.input.val())},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Integer=Sao.class_(Sao.View.Form.Char,{class_:"form-integer",init:function(a,b,c){Sao.View.Form.Integer._super.init.call(this,a,b,c),this.input.attr("type","text"),this.input.attr("width",8),this.group.css("width",""),this.factor=Number(c.factor||1)},set_value:function(a,b){b.set_client(a,this.input.val(),void 0,this.factor)},display:function(a,b){if(Sao.View.Form.Char._super.display.call(this,a,b),a){var c=a.model.fields[this.field_name].get_client(a,this.factor);this.input.val(c)}else this.input.val("")}}),Sao.View.Form.Float=Sao.class_(Sao.View.Form.Integer,{
+class_:"form-float"}),Sao.View.Form.Selection=Sao.class_(Sao.View.Form.Widget,{class_:"form-selection",init:function(a,b,c){Sao.View.Form.Selection._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.select=this.labelled=jQuery("<select/>",{"class":"form-control input-sm"}),this.el.append(this.select),this.select.change(this.focus_out.bind(this)),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a,this.set_selection.bind(this))},update_selection:function(a,b,c){Sao.common.selection_mixin.update_selection.call(this,a,b,function(a){this.set_selection(a),c&&c()}.bind(this))},set_selection:function(a){var b=this.select;b.empty(),a.forEach(function(a){b.append(jQuery("<option/>",{value:JSON.stringify(a[0]),text:a[1]}))})},display_update_selection:function(a,b){this.update_selection(a,b,function(){if(!b)return void this.select.val("");for(var c,d=b.get(a),e=!1,f=0,g=this.selection.length;g>f;f++)if(this.selection[f][0]===d){e=!0;break}e?c=jQuery.when():(c=Sao.common.selection_mixin.get_inactive_selection.call(this,d),c.done(function(a){this.select.append(jQuery("<option/>",{value:JSON.stringify(a[0]),text:a[1],disabled:!0}))}.bind(this))),c.done(function(){this.select.val(JSON.stringify(d))}.bind(this))}.bind(this))},display:function(a,b){Sao.View.Form.Selection._super.display.call(this,a,b),this.display_update_selection(a,b)},focus:function(){this.select.focus()},value_get:function(){return JSON.parse(this.select.val())},set_value:function(a,b){var c=this.value_get();b.set_client(a,c)},set_readonly:function(a){this.select.prop("disabled",a)}}),Sao.View.Form.Boolean=Sao.class_(Sao.View.Form.Widget,{class_:"form-boolean",init:function(a,b,c){Sao.View.Form.Boolean._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<input/>",{type:"checkbox","class":"form-control input-sm"}).appendTo(this.el),this.input.change(this.focus_out.bind(this))},display:function(a,b){Sao.View.Form.Boolean._super.display.call(this,a,b),a?this.input.prop("checked",a.field_get(this.field_name)):this.input.prop("checked",!1)},focus:function(){this.input.focus()},set_value:function(a,b){var c=this.input.prop("checked");b.set_client(a,c)},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Text=Sao.class_(Sao.View.Form.Widget,{class_:"form-text",init:function(a,b,c){Sao.View.Form.Text._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.input=this.labelled=jQuery("<textarea/>",{"class":"form-control input-sm"}).appendTo(this.el),this.input.change(this.focus_out.bind(this))},display:function(a,b){if(Sao.View.Form.Text._super.display.call(this,a,b),a){var c=a.field_get_client(this.field_name);this.input.val(c)}else this.input.val("")},focus:function(){this.input.focus()},set_value:function(a,b){var c=this.input.val()||"";b.set_client(a,c)},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.RichText=Sao.class_(Sao.View.Form.Widget,{class_:"form-richtext",init:function(a,b,c){var d,e,f;Sao.View.Form.RichText._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.toolbar=jQuery("<div/>",{"class":"btn-toolbar",role:"toolbar"}).appendTo(jQuery("<div/>",{"class":"panel-heading"}).appendTo(this.el));var g=function(a){document.execCommand(a.data)},h=function(a){var b=jQuery("<div/>",{"class":"btn-group",role:"group"}).appendTo(this.toolbar);for(d in a)e=a[d],f=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-"+e.icon})).appendTo(b),f.click(e.command,g)}.bind(this);h([{icon:"bold",command:"bold"},{icon:"italic",command:"italic"},{icon:"text-color",command:"underline"}]);var i=[{heading:Sao.i18n.gettext("Font"),options:["Normal","Serif","Sans","Monospace"],command:"fontname"},{heading:Sao.i18n.gettext("Size"),options:[1,2,3,4,5,6,7],command:"fontsize"}],j=function(a,b){return function(c){a.append(jQuery("<li/>").append(jQuery("<a/>",{href:"#"}).append(c).click(function(){document.execCommand(b.command,!1,c)})))}};for(d in i){e=i[d];var k=jQuery("<div/>",{"class":"btn-group",role:"group"}).appendTo(this.toolbar);f=jQuery("<button/>",{"class":"btn btn-default dropdown-toggle","data-toggle":"dropdown","aria-expanded":!1,"aria-haspopup":!0}).append(e.heading).append(jQuery("<span/>",{"class":"caret"})).appendTo(k);var l=jQuery("<ul/>",{"class":"dropdown-menu"}).appendTo(k);e.options.forEach(j(l,e))}h([{icon:"align-left",command:"justifyLeft"},{icon:"align-center",command:"justifyCenter"},{icon:"align-right",command:"justifyRight"},{icon:"align-justify",command:"justifyFull"}]),[["foreColor","#000000"]].forEach(function(a){var b=a[0],c=a[1];jQuery("<input/>",{"class":"btn btn-default",type:"color"}).appendTo(this.toolbar).change(function(){document.execCommand(b,!1,jQuery(this).val())}).focusin(function(){document.execCommand(b,!1,jQuery(this).val())}).val(c)}.bind(this)),this.input=this.labelled=jQuery("<div/>",{"class":"richtext",contenteditable:!0}).appendTo(jQuery("<div/>",{"class":"panel-body"}).appendTo(this.el)),this.el.focusout(this.focus_out.bind(this))},focus_out:function(){window.setTimeout(function(){0===this.el.find(":focus").length&&Sao.View.Form.RichText._super.focus_out.call(this)}.bind(this),0)},display:function(a,b){Sao.View.Form.RichText._super.display.call(this,a,b);var c="";a&&(c=a.field_get_client(this.field_name)),this.input.html(c)},focus:function(){this.input.focus()},set_value:function(a,b){this.input.find("div").each(function(a,b){if(b=jQuery(b),b.css("text-align")){var c=b.css("text-align").split("-").pop();b.attr("align",c),b.css("text-align","")}"start"==b.attr("align")&&b.attr("align","left")});var c=this.input.html()||"";b.set_client(a,c)},set_readonly:function(a){this.input.prop("contenteditable",!a),this.toolbar.find("button,select").prop("disabled",a)}}),Sao.View.Form.Many2One=Sao.class_(Sao.View.Form.Widget,{class_:"form-many2one",init:function(a,b,c){Sao.View.Form.Many2One._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_});var d=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.entry=this.labelled=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(d),this.entry.on("keydown",this.key_press.bind(this)),c.completion&&"1"!=c.completion||(Sao.common.get_completion(d,this._update_completion.bind(this),this._completion_match_selected.bind(this),this._completion_action_activated.bind(this)),this.wid_completion=!0);var e=jQuery("<span/>",{"class":"input-group-btn"}).appendTo(d);this.but_open=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-search"})).appendTo(e),this.but_open.click(this.edit.bind(this)),this.el.change(this.focus_out.bind(this)),this._readonly=!1},get_screen:function(){var a=this.field().get_domain(this.record()),b=this.field().get_context(this.record());return new Sao.Screen(this.get_model(),{context:b,domain:a,mode:["form"],view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views,readonly:this._readonly})},set_text:function(a){jQuery.isEmptyObject(a)&&(a=""),this.entry.val(a)},get_text:function(){var a=this.record();return a?a.field_get_client(this.field_name):""},focus_out:function(){(this.attributes.completion&&"1"!=this.attributes.completion||!this.el.find(".dropdown").hasClass("open"))&&Sao.View.Form.Many2One._super.focus_out.call(this)},set_value:function(a,b){b.get_client(a)!=this.entry.val()&&(b.set_client(a,this.value_from_id(null,"")),this.entry.val(""))},display:function(a,b){var c=this.record();if(!c||!a||c.id==a.id){var d;if(Sao.View.Form.Many2One._super.display.call(this,a,b),this._set_button_sensitive(),this._set_completion(),!a)return void this.entry.val("");this.set_text(b.get_client(a)),d=b.get(a),this.has_target(d)?this.but_open.button({icons:{primary:"glyphicon-folder-open"}}):this.but_open.button({icons:{primary:"glyphicon-search"}})}},focus:function(){this.entry.focus()},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){this.entry.prop("readonly",this._readonly),this.but_open.prop("disabled",this._readonly||!this.read_access())},get_access:function(a){var b=this.get_model();return b?Sao.common.MODELACCESS.get(b)[a]:!0},read_access:function(){return this.get_access("read")},create_access:function(){return this.attributes.create&&this.get_access("create")},id_from_value:function(a){return a},value_from_id:function(a,b){return void 0===b&&(b=""),[a,b]},get_model:function(){return this.attributes.relation},has_target:function(a){return void 0!==a&&null!==a},edit:function(a){var b=this.get_model();if(b&&Sao.common.MODELACCESS.get(b).read){var c,d,e=this.record(),f=e.field_get(this.field_name);if(b&&this.has_target(f)){var g=this.get_screen(),h=this.id_from_value(e.field_get(this.field_name));g.new_group([h]),d=function(a){if(a){var b=g.current_record.rec_name();b.done(function(a){var b=this.value_from_id(g.current_record.id,a);this.record().field_set_client(this.field_name,b,!0)}.bind(this))}},c=new Sao.Window.Form(g,d.bind(this),{save_current:!0})}else if(b){var i=this.field().get_domain(e),j=this.field().get_context(e),k=this.entry.val();d=function(a){if(!jQuery.isEmptyObject(a)){var b=this.value_from_id(a[0][0],a[0][1]);this.record().field_set_client(this.field_name,b,!0)}};var l=new Sao.common.DomainParser;c=new Sao.Window.Search(b,d.bind(this),{sel_multi:!1,context:j,domain:i,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.create_access(),search_filter:l.quote(k)})}}},new_:function(a){var b=this.get_model();if(b&&Sao.common.MODELACCESS.get(b).create){var c=this.get_screen(),d=function(a){if(a){var b=c.current_record.rec_name();b.done(function(a){var b=this.value_from_id(c.current_record.id,a);this.record().field_set_client(this.field_name,b)}.bind(this))}};new Sao.Window.Form(c,d.bind(this),{new_:!0,save_current:!0})}},key_press:function(a){var b=!this.entry.prop("readonly"),c=[Sao.common.TAB_KEYCODE],d=[Sao.common.BACKSPACE_KEYCODE,Sao.common.DELETE_KEYCODE];if(this.wid_completion||c.push(Sao.common.RETURN_KEYCODE),a.which==Sao.common.F3_KEYCODE&&b&&this.create_access())this.new_(),a.preventDefault();else if(a.which==Sao.common.F2_KEYCODE&&this.read_access())this.edit(),a.preventDefault();else if(~c.indexOf(a.which)&&b){if((!this.attributes.completion||"1"==this.attributes.completion)&&this.el.find(".dropdown").hasClass("open"))return;this.activate()}else if(this.has_target(this.record().field_get(this.field_name))&&b){var e=this.get_text();(e!=this.entry.val()||~d.indexOf(a.which))&&(this.entry.val(""),this.record().field_set_client(this.field_name,this.value_from_id(null,"")))}},activate:function(){var a=this.get_model();if(a&&Sao.common.MODELACCESS.get(a).read){var b=this.record(),c=b.field_get(this.field_name);new Sao.Model(a);if(a&&!this.has_target(c)){var d=this.entry.val();if(!this._readonly&&(d||this.field().get_state_attrs(this.record()).required)){var e=this.field().get_domain(b),f=this.field().get_context(b),g=function(a){if(jQuery.isEmptyObject(a))this.entry.val("");else{var b=this.value_from_id(a[0][0],a[0][1]);this.record().field_set_client(this.field_name,b,!0)}},h=new Sao.common.DomainParser;new Sao.Window.Search(a,g.bind(this),{sel_multi:!1,context:f,domain:e,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.create_access(),search_filter:h.quote(d)})}}}},_set_completion:function(){var a=this.el.find(".action-search");this.read_access()?a.removeClass("disabled"):a.addClass("disabled");var b=this.el.find(".action-create");this.create_access()?b.removeClass("disabled"):b.addClass("disabled")},_update_completion:function(a){var b=this.record();if(b){var c=this.field(),d=c.get(b);if(this.has_target(d)){var e=this.id_from_value(d);if(void 0!==e&&e>0)return jQuery.when()}var f=this.get_model();return Sao.common.update_completion(this.entry,b,c,f)}},_completion_match_selected:function(a){this.record().field_set_client(this.field_name,this.value_from_id(a.id,a.rec_name),!0)},_completion_action_activated:function(a){"search"==a?this.edit():"create"==a&&this.new_()}}),Sao.View.Form.One2One=Sao.class_(Sao.View.Form.Many2One,{class_:"form-one2one"}),Sao.View.Form.Reference=Sao.class_(Sao.View.Form.Many2One,{class_:"form-reference",init:function(a,b,c){Sao.View.Form.Reference._super.init.call(this,a,b,c),this.el.addClass("form-inline"),this.select=jQuery("<select/>",{"class":"form-control input-sm","aria-label":c.string}),this.el.prepend(jQuery("<span/>").text("-")),this.el.prepend(this.select),this.select.change(this.select_changed.bind(this)),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a,this.set_selection.bind(this))},update_selection:function(a,b,c){Sao.common.selection_mixin.update_selection.call(this,a,b,function(a){this.set_selection(a),c&&c()}.bind(this))},set_selection:function(a){var b=this.select;b.empty(),a.forEach(function(a){b.append(jQuery("<option/>",{value:a[0],text:a[1]}))})},id_from_value:function(a){return parseInt(a.split(",")[1],10)},value_from_id:function(a,b){return b||(b=""),[this.get_model(),[a,b]]},get_text:function(){var a=this.record();return a?a.field_get_client(this.field_name)[1]:""},get_model:function(){return this.select.val()},has_target:function(a){if(null===a)return!1;var b=a.split(",")[0];return a=a.split(",")[1],jQuery.isEmptyObject(a)?a=null:(a=parseInt(a,10),isNaN(a)&&(a=null)),b==this.get_model()&&a>=0},_set_button_sensitive:function(){Sao.View.Form.Reference._super._set_button_sensitive.call(this),this.select.prop("disabled",this.entry.prop("readonly"))},select_changed:function(){this.entry.val("");var a,b=this.get_model();a=b?[b,[-1,""]]:["",""],this.record().field_set_client(this.field_name,a)},set_value:function(a,b){var c;if(this.get_model()){c=b.get_client(a,this.field_name);var d,e;c instanceof Array?(d=c[0],e=c[1]):(d="",e=""),(d!=this.get_model()||e!=this.entry.val())&&(b.set_client(a,null),this.entry.val(""))}else c=this.entry.val(),jQuery.isEmptyObject(c)?b.set_client(a,null):b.set_client(a,["",c])},set_text:function(a){var b;a?(b=a[0],a=a[1]):(b=null,a=null),Sao.View.Form.Reference._super.set_text.call(this,a),b?this.select.val(b):this.select.val("")},display:function(a,b){this.update_selection(a,b,function(){Sao.View.Form.Reference._super.display.call(this,a,b)}.bind(this))},set_readonly:function(a){Sao.View.Form.Reference._super.set_readonly.call(this,a),this.select.prop("disabled",a)}}),Sao.View.Form.One2Many=Sao.class_(Sao.View.Form.Widget,{class_:"form-one2many",init:function(a,b,c){Sao.View.Form.One2Many._super.init.call(this,a,b,c),this._readonly=!0,this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.menu=jQuery("<div/>",{"class":this.class_+"-menu panel-heading"}),this.el.append(this.menu);var d=jQuery("<label/>",{"class":this.class_+"-string",text:c.string});this.menu.append(d),d.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",d.attr("id")),d.attr("for",this.el.attr("id"));var e=jQuery("<div/>",{"class":this.class_+"-toolbar"});this.menu.append(e);var f=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(e);this.wid_text=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(f),this.wid_text.hide();var g=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(f);c.add_remove&&(this.wid_text.show(),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(g),this.but_add.click(this.add.bind(this)),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(g),this.but_remove.click(this.remove.bind(this))),this.but_new=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("New")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-edit"})).appendTo(g),this.but_new.click(this.new_.bind(this)),this.but_open=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Open")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-folder-open"})).appendTo(g),this.but_open.click(this.open.bind(this)),this.but_del=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Delete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-trash"})).appendTo(g),this.but_del.click(this.delete_.bind(this)),this.but_undel=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Undelete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-repeat"})).appendTo(g),this.but_undel.click(this.undelete.bind(this)),this.but_previous=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-left"})).appendTo(g),this.but_previous.click(this.previous.bind(this)),this.label=jQuery("<span/>",{"class":"btn"}).appendTo(g),this.label.text("(0, 0)"),this.but_next=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-right"})).appendTo(g),this.but_next.click(this.next.bind(this)),this.but_switch=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Switch")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-list-alt"})).appendTo(g),this.but_switch.click(this.switch_.bind(this)),this.content=jQuery("<div/>",{"class":this.class_+"-content panel-body"}),this.el.append(this.content);var h=(c.mode||"tree,form").split(",");this.screen=new Sao.Screen(c.relation,{mode:h,view_ids:(c.view_ids||"").split(","),views_preload:c.views||{},row_activate:this.activate.bind(this),readonly:c.readonly||!1,exclude_field:c.relation_field||null,pre_validate:c.pre_validate}),this.screen.pre_validate=1==c.pre_validate,this.prm=this.screen.switch_view(h[0]).done(function(){this.content.append(this.screen.screen_container.el)}.bind(this)),this.but_switch.prop("disabled",this.screen.number_of_views()<=0)},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){var a,b,c=Sao.common.MODELACCESS.get(this.screen.model_name),d=this.record(),e=this.field();if(d&&e){var f=d.expr_eval(this.attributes.size);b=e.get_eval(d),a=void 0!==f&&null!==f&&b>=f>=0}else b=null,a=!1;var g=this.attributes.create;void 0===g&&(g=!0),this.but_new.prop("disabled",this._readonly||!g||a||!c.create);var h=this.attributes["delete"];void 0===h&&(h=!0),this.but_del.prop("disabled",this._readonly||!h||!c["delete"]),this.but_undel.prop("disabled",this._readonly||a),this.but_open.prop("disabled",!c.read),this.attributes.add_remove&&(this.wid_text.prop("disabled",this._readonly),this.but_add.prop("disabled",this._readonly||a||!c.write||!c.read),this.but_remove.prop("disabled",this._readonly||!c.write||!c.read))},display:function(a,b){Sao.View.Form.One2Many._super.display.call(this,a,b),this._set_button_sensitive(),this.prm.done(function(){if(a){if(void 0===b)return this.screen.new_group(),this.screen.set_current_record(null),this.screen.group.parent=null,void this.screen.display();var c=a.field_get_client(this.field_name);c!=this.screen.group&&(this.screen.set_group(c),"tree"==this.screen.current_view.view_type&&this.screen.current_view.editable&&this.screen.set_current_record(null));var d=!1,e=[],f=null;a&&(d=b.get_state_attrs(a).readonly,e=b.get_domain(a),f=a.expr_eval(this.attributes.size)),Sao.common.compare(this.screen.domain,e)||(this.screen.domain=e),this.screen.group.set_readonly(d),this.screen.size_limit=f,this.screen.display()}}.bind(this))},focus:function(){this.wid_text.is(":visible")&&this.wid_text.focus()},activate:function(a){this.edit()},add:function(a){var b=Sao.common.MODELACCESS.get(this.screen.model_name);if(b.write&&b.read){this.view.set_value();var c=this.field().get_domain(this.record()),d=this.field().get_context(this.record());c=[c,this.record().expr_eval(this.attributes.add_remove)];var e=this.field().get_removed_ids(this.record());c=["OR",c,["id","in",e]];var f=this.wid_text.val(),g=function(a){var b=jQuery.when();if(!jQuery.isEmptyObject(a)){var c,d,e=[];for(c=0,d=a.length;d>c;c++)e.push(a[c][0]);this.screen.group.load(e,!0),b=this.screen.display()}b.done(function(){this.screen.set_cursor()}.bind(this)),this.wid_text.val("")}.bind(this),h=new Sao.common.DomainParser;new Sao.Window.Search(this.attributes.relation,g,{sel_multi:!0,context:d,domain:c,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:!this.but_new.prop("disabled"),search_filter:h.quote(f)})}},remove:function(a){var b=Sao.common.MODELACCESS.get(this.screen.model_name);b.write&&b.read&&this.screen.remove(!1,!0,!1)},new_:function(a){Sao.common.MODELACCESS.get(this.screen.model_name).create&&this.validate().done(function(){this.attributes.product?this.new_product():this.new_single()}.bind(this))},new_single:function(){var a=jQuery.extend({},this.field().get_context(this.record()));if("form"==this.screen.current_view.type||this.screen.current_view.editable)this.screen.new_(),this.screen.current_view.el.prop("disabled",!1);else{var b=this.record(),c=b.expr_eval(this.attributes.size)||-1;c-=this.field().get_eval(b);new Sao.Window.Form(this.screen,function(){},{new_:!0,many:c,context:a})}},new_product:function(){var a=this.attributes.product.split(","),b={},c=this.screen;c.new_(!1).then(function(d){d.default_get().then(function(e){d.set_default(e);var f=function(){if(jQuery.isEmptyObject(a))return g();var e=c.model.fields[a.pop()],h=e.description.relation;h||f();var i=e.get_domain(d),j=e.get_context(d),k=function(a){jQuery.isEmptyObject(a)||(b[e.name]=a),f()};new Sao.Window.Search(h,k,{sel_multi:!0,context:j,domain:i,search_filter:""})},g=function(){if(jQuery.isEmptyObject(b))return void c.group.remove(d,!0);var a=Object.keys(b),f=a.map(function(a){return b[a]});Sao.common.product(f).forEach(function(b){var f=function(c){var d=jQuery.extend({},e);a.forEach(function(a,c){d[a]=b[c][0],d[a+".rec_name"]=b[c][1]}),c.set_default(d)};d?(f(d),d=null):c.new_(!1).then(f)})};f()})})},open:function(a){this.edit()},delete_:function(a){Sao.common.MODELACCESS.get(this.screen.model_name)["delete"]&&this.screen.remove(!1,!1,!1)},undelete:function(a){this.screen.unremove()},previous:function(a){this.validate().done(function(){this.screen.display_previous()}.bind(this))},next:function(a){this.validate().done(function(){this.screen.display_next()}.bind(this))},switch_:function(a){this.screen.switch_view()},edit:function(){Sao.common.MODELACCESS.get(this.screen.model_name).read&&this.validate().done(function(){var a=this.screen.current_record;if(a){new Sao.Window.Form(this.screen,function(){})}}.bind(this))},validate:function(){var a=jQuery.Deferred();this.view.set_value();var b=this.screen.current_record;if(b){var c=this.screen.current_view.get_fields();b.validate(c).then(function(c){return c?this.screen.pre_validate?b.pre_validate().then(function(b){return b?void a.resolve():void a.reject()}):void a.resolve():(this.screen.display(!0),void a.reject())}.bind(this))}else a.resolve();return a},set_value:function(a,b){this.screen.save_tree_state()}}),Sao.View.Form.Many2Many=Sao.class_(Sao.View.Form.Widget,{class_:"form-many2many",init:function(a,b,c){Sao.View.Form.Many2Many._super.init.call(this,a,b,c),this._readonly=!0,this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"}),this.menu=jQuery("<div/>",{"class":this.class_+"-menu panel-heading"}),this.el.append(this.menu);var d=jQuery("<label/>",{"class":this.class_+"-string",text:c.string});this.menu.append(d),d.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",d.attr("id")),d.attr("for",this.el.attr("id"));var e=jQuery("<div/>",{"class":this.class_+"-toolbar"});this.menu.append(e);var f=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(e);this.entry=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(f),this.entry.on("keydown",this.key_press.bind(this));var g=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(f);this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(g),this.but_add.click(this.add.bind(this)),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(g),this.but_remove.click(this.remove.bind(this)),this.content=jQuery("<div/>",{"class":this.class_+"-content panel-body"}),this.el.append(this.content),this.screen=new Sao.Screen(c.relation,{mode:["tree"],view_ids:(c.view_ids||"").split(","),views_preload:c.views||{},row_activate:this.activate.bind(this)}),this.prm=this.screen.switch_view("tree").done(function(){this.content.append(this.screen.screen_container.el)}.bind(this))},set_readonly:function(a){this._readonly=a,this._set_button_sensitive()},_set_button_sensitive:function(){var a=!1;this.record()&&this.field(),this.entry.prop("disabled",this._readonly),this.but_add.prop("disabled",this._readonly||a),this.but_remove.prop("disabled",this._readonly)},display:function(a,b){Sao.View.Form.Many2Many._super.display.call(this,a,b),this.prm.done(function(){if(a){if(void 0===b)return this.screen.new_group(),this.screen.set_current_record(null),this.screen.group.parent=null,void this.screen.display();var c=a.field_get_client(this.field_name);c!=this.screen.group&&this.screen.set_group(c),this.screen.display()}}.bind(this))},focus:function(){this.entry.focus()},activate:function(){this.edit()},add:function(){var a=this.field().get_domain(this.record()),b=this.field().get_context(this.record()),c=this.entry.val(),d=function(a){if(!jQuery.isEmptyObject(a)){var b,c,d=[];for(b=0,c=a.length;c>b;b++)d.push(a[b][0]);this.screen.group.load(d,!0),this.screen.display()}this.entry.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(this.attributes.relation,d,{sel_multi:!0,context:b,domain:a,view_ids:(this.attributes.view_ids||"").split(","),views_preload:this.attributes.views||{},new_:this.attributes.create,search_filter:e.quote(c)})},remove:function(){this.screen.remove(!1,!0,!1)},key_press:function(a){var b=[Sao.common.TAB_KEYCODE];this.wid_completion||b.push(Sao.common.RETURN_KEYCODE),a.which==Sao.common.F3_KEYCODE?(this.new_(),a.preventDefault()):a.which==Sao.common.F2_KEYCODE?(this.add(),a.preventDefault()):~b.indexOf(a.which)&&this.entry.val()&&this.add()},edit:function(){if(!jQuery.isEmptyObject(this.screen.current_record)){var a=this.field().get_domain(this.record()),b=this.record().expr_eval(this.attributes.add_remove);jQuery.isEmptyObject(b)||(a=[a,b]);var c=this.field().get_context(this.record()),d=new Sao.Screen(this.attributes.relation,{domain:a,view_ids:(this.attributes.view_ids||"").split(","),mode:["form"],views_preload:this.attributes.views,readonly:this.attributes.readonly||!1,context:c});d.new_group([this.screen.current_record.id]);var e=function(a){a&&d.current_record.save().done(function(){this.screen.current_record.cancel()}.bind(this))}.bind(this);d.switch_view().done(function(){new Sao.Window.Form(d,e)})}},new_:function(){var a=this.field().get_domain(this.record()),b=this.record().expr_eval(this.attributes.add_remove);jQuery.isEmptyObject(b)||(a=[a,b]);var c=this.field().get_context(this.record()),d=new Sao.Screen(this.attributes.relation,{domain:a,view_ids:(this.attributes.view_ids||"").split(","),mode:["form"],views_preload:this.attributes.views,context:c}),e=function(a){if(a){var b=d.current_record;this.screen.group.load([b.id],!0)}this.entry.val("")}.bind(this);d.switch_view().done(function(){new Sao.Window.Form(d,e,{new_:!0,save_current:!0})})}}),Sao.View.Form.BinaryMixin=Sao.class_(Sao.View.Form.Widget,{init:function(a,b,c){Sao.View.Form.BinaryMixin._super.init.call(this,a,b,c),this.filename=c.filename||null},toolbar:function(a){var b=jQuery("<div/>",{"class":a,role:"group"});return this.but_select=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-search"})).appendTo(b),this.but_select.click(this.select.bind(this)),this.filename&&(this.but_open=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-folder-open"})).appendTo(b),this.but_open.click(this.open.bind(this))),this.but_save_as=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-save"})).appendTo(b),this.but_save_as.click(this.save_as.bind(this)),this.but_clear=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-erase"})).appendTo(b),this.but_clear.click(this.clear.bind(this)),b},filename_field:function(){var a=this.record();return a?a.model.fields[this.filename]:void 0},select:function(){var a=this.record(),b=function(){d.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),d.modal.modal("hide")},c=function(){var c=new FileReader;c.onload=function(b){var d=new Uint8Array(c.result);this.field().set_client(a,d)}.bind(this),c.onloadend=function(a){b()};var d=e[0].files[0];c.readAsArrayBuffer(d),this.filename&&this.filename_field().set_client(a,d.name)}.bind(this),d=new Sao.Dialog(Sao.i18n.gettext("Select"),"file-dialog");d.footer.append(jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(b)).append(jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).click(c)),d.content.submit(function(a){c(),a.preventDefault()});var e=jQuery("<input/>",{type:"file"}).appendTo(d.body);d.modal.modal("show")},open:function(){this.save_as()},save_as:function(){var a=this.field(),b=this.record();a.get_data(b).done(function(a){var b=new Blob([a[0].binary],{type:"application/octet-binary"}),c=window.URL.createObjectURL(b);this.blob_url&&window.URL.revokeObjectURL(this.blob_url),this.blob_url=c,window.open(c)}.bind(this))},clear:function(){this.field().set_client(this.record(),null)}}),Sao.View.Form.Binary=Sao.class_(Sao.View.Form.BinaryMixin,{class_:"form-binary",blob_url:"",init:function(a,b,c){Sao.View.Form.Binary._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_}),this.filename&&c.filename_visible&&(this.text=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(this.el),this.text.change(this.focus_out.bind(this)),this.text.on("keydown",this.key_press.bind(this)));var d=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.size=jQuery("<input/>",{type:"input","class":"form-control input-sm"}).appendTo(d),this.toolbar("input-group-btn").appendTo(d)},display:function(a,b){if(Sao.View.Form.Binary._super.display.call(this,a,b),!b)return this.size.val(""),this.filename&&this.but_open.button("disable"),this.text&&this.text.val(""),void this.but_save_as.button("disable");var c,d=b.get_size(a);c=d?"enable":"disable",this.filename&&(this.text&&this.text.val(this.filename_field().get(a)||""),this.but_open.button(c)),this.size.val(Sao.common.humanize(d)),this.but_save_as.button(c)},key_press:function(a){var b=!this.wid_text.prop("readonly");a.which==Sao.common.F3_KEYCODE&&b?(this.new_(),a.preventDefault()):a.which==Sao.common.F2_KEYCODE&&(this.open(),a.preventDefault())},set_value:function(a,b){this.text&&this.filename_field().set_client(a,this.text.val()||"")},set_readonly:function(a){
+a?(this.but_select.hide(),this.but_clear.hide()):(this.but_select.show(),this.but_clear.show()),this.wid_text&&this.wid_text.prop("readonly",a)}}),Sao.View.Form.MultiSelection=Sao.class_(Sao.View.Form.Selection,{class_:"form-multiselection",init:function(a,b,c){this.nullable_widget=!1,Sao.View.Form.MultiSelection._super.init.call(this,a,b,c),this.select.prop("multiple",!0)},display_update_selection:function(a,b){var c,d,e;this.update_selection(a,b,function(){if(b){var f=[],g=a.field_get_client(this.field_name);for(c=0,d=g.length;d>c;c++)e=g[c],~g.record_removed.indexOf(e)||~g.record_deleted.indexOf(e)||f.push(e.id);this.el.val(f)}}.bind(this))},set_value:function(a,b){var c=this.el.val();c?c=c.map(function(a){return parseInt(a,10)}):(c=[],b.set_client(a,c))}}),Sao.View.Form.Image=Sao.class_(Sao.View.Form.BinaryMixin,{class_:"form-image",init:function(a,b,c){Sao.View.Form.Image._super.init.call(this,a,b,c),this.height=parseInt(c.height||100,10),this.width=parseInt(c.width||300,10),this.el=jQuery("<div/>"),this.image=jQuery("<img/>",{"class":"center-block"}).appendTo(this.el),this.image.css("max-height",this.height),this.image.css("max-width",this.width),this.image.css("height","auto"),this.image.css("width","auto");var d=this.toolbar("btn-group");c.readonly||jQuery("<div/>",{"class":"text-center"}).append(d).appendTo(this.el),this.update_img()},set_readonly:function(a){[this.but_select,this.but_open,this.but_save_as,this.but_clear].forEach(function(b){b&&b.prop("disable",a)})},clear:function(){Sao.View.Form.Image._super.clear.call(this),this.update_img()},update_img:function(){var a,b=this.record();b&&(a=b.field_get_client(this.field_name)),a=a?a>Sao.common.BIG_IMAGE_SIZE?jQuery.when(null):b.model.fields[this.field_name].get_data(b):jQuery.when(null),a.done(function(a){var b,c;a?(c=new Blob([a[0][this.field_name]]),b=window.URL.createObjectURL(c)):b=null,this.image.attr("src",b)}.bind(this))},display:function(a,b){Sao.View.Form.Image._super.display.call(this,a,b),this.update_img()}}),Sao.View.Form.URL=Sao.class_(Sao.View.Form.Char,{class_:"form-url",init:function(a,b,c){Sao.View.Form.URL._super.init.call(this,a,b,c),this.button=jQuery("<a/>",{"class":"btn btn-default",target:"_new"}).appendTo(jQuery("<span/>",{"class":"input-group-btn"}).appendTo(this.group)),this.icon=jQuery("<img/>").appendTo(this.button),this.set_icon()},display:function(a,b){Sao.View.Form.URL._super.display.call(this,a,b);var c="";if(a&&(c=a.field_get_client(this.field_name)),this.set_url(c),a&this.attributes.icon){var d,e=this.attributes.icon;d=e in a.model.fields?a.field_get_client(e):e,this.set_icon(d)}},set_icon:function(a){a=a||"tryton-web-browser",Sao.common.ICONFACTORY.register_icon(a).done(function(a){this.icon.attr("src",a)}.bind(this))},set_url:function(a){this.button.attr("href",a)},set_readonly:function(a){Sao.View.Form.URL._super.set_readonly.call(this,a),a?(this.input.hide(),this.button.removeClass("btn-default"),this.button.addClass("btn-link")):(this.input.show(),this.button.removeClass("btn-link"),this.button.addClass("btn-default"))}}),Sao.View.Form.Email=Sao.class_(Sao.View.Form.URL,{class_:"form-email",set_url:function(a){Sao.View.Form.Email._super.set_url.call(this,"mailto:"+a)}}),Sao.View.Form.CallTo=Sao.class_(Sao.View.Form.URL,{class_:"form-callto",set_url:function(a){Sao.View.Form.CallTo._super.set_url.call(this,"callto:"+a)}}),Sao.View.Form.SIP=Sao.class_(Sao.View.Form.URL,{class_:"form-sip",set_url:function(a){Sao.View.Form.SIP._super.set_url.call(this,"sip:"+a)}}),Sao.View.Form.ProgressBar=Sao.class_(Sao.View.Form.Widget,{class_:"form-char",init:function(a,b,c){Sao.View.Form.ProgressBar._super.init.call(this,a,b,c),this.el=jQuery("<div/>",{"class":this.class_+" progress"}),this.progressbar=jQuery("<div/>",{"class":"progress-bar",role:"progressbar","aria-valuemin":0,"aria-valuemax":100}).appendTo(this.el),this.progressbar.css("min-width: 2em")},display:function(a,b){Sao.View.Form.ProgressBar._super.display.call(this,a,b);var c,d;b?(c=b.get(a),d=b.get_client(a,100),d&&(d=Sao.i18n.gettext("%1%",d))):(c=0,d=""),this.progressbar.attr("aria-valuenow",100*c),this.progressbar.css("width",100*c+"%"),this.progressbar.text(d)}}),Sao.View.Form.Dict=Sao.class_(Sao.View.Form.Widget,{class_:"form-dict",init:function(a,b,c){Sao.View.Form.Dict._super.init.call(this,a,b,c),this.schema_model=new Sao.Model(c.schema_model),this.keys={},this.fields={},this.rows={},this.el=jQuery("<div/>",{"class":this.class_+" panel panel-default"});var d=jQuery("<div/>",{"class":this.class_+"-heading panel-heading"}).appendTo(this.el),e=jQuery("<label/>",{"class":this.class_+"-string",text:c.string}).appendTo(d);e.uniqueId(),this.el.uniqueId(),this.el.attr("aria-labelledby",e.attr("id")),e.attr("for",this.el.attr("id"));var f=jQuery("<div/>",{"class":this.class_+"-body panel-body"}).appendTo(this.el);this.container=jQuery("<div/>",{"class":this.class_+"-container"}).appendTo(f);var g=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(jQuery("<div>",{"class":"col-md-12"}).appendTo(jQuery("<div/>",{"class":"row"}).appendTo(jQuery("<div/>",{"class":"container-fluid"}).appendTo(f))));this.wid_text=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(g),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(jQuery("<div/>",{"class":"input-group-btn"}).appendTo(g)),this.but_add.click(this.add.bind(this)),this._readonly=!1,this._record_id=null},add:function(){var a=this.field().get_context(this.record()),b=this.wid_text.val(),c=this.field().get_domain(this.record()),d=function(a){if(!jQuery.isEmptyObject(a)){var b=a.map(function(a){return a[0]});this.add_new_keys(b)}this.wid_text.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(this.schema_model.name,d,{sel_multi:!0,context:a,domain:c,new_:!1,search_filter:e.quote(b)})},add_new_keys:function(a){var b=this.field().get_context(this.record());this.schema_model.execute("get_keys",[a],b).then(function(a){var b=!1;a.forEach(function(a){this.fields[a.name]||(this.keys[a.name]=a,this.add_line(a.name),b||(this.fields[a.name].input.focus(),b=!0))}.bind(this))}.bind(this))},remove:function(a,b){void 0===b&&(b=!0),delete this.fields[a],this.rows[a].remove(),delete this.rows[a],b&&this.set_value(this.record(),this.field())},set_value:function(a,b){b.set_client(a,this.get_value())},get_value:function(){var a={};for(var b in this.fields){var c=this.fields[b];a[b]=c.get_value()}return a},set_readonly:function(a){this._readonly=a,this._set_button_sensitive();for(var b in this.fields){var c=this.fields[b];c.set_readonly(a)}this.wid_text.prop("disabled",a)},_set_button_sensitive:function(){var a=this.attributes.create;void 0===a&&(a=!0);var b=this.attributes["delete"];void 0===b&&(b=!0),this.but_add.prop("disabled",this._readonly||!a);for(var c in this.fields){var d=this.fields[c].button;d.prop("disabled",this._readonly||!b)}},add_line:function(a){var b,c;this.fields[a]=b=new(this.get_entries(this.keys[a].type_))(a,this),this.rows[a]=c=jQuery("<div/>",{"class":"row"});var d=this.keys[a].string+Sao.i18n.gettext(":"),e=jQuery("<label/>",{text:d}).appendTo(jQuery("<div/>",{"class":"dict-label col-md-4"}).appendTo(c));b.el.addClass("col-md-8").appendTo(c),e.uniqueId(),b.labelled.uniqueId(),b.labelled.attr("aria-labelledby",e.attr("id")),e.attr("for",b.labelled.attr("id")),b.button.click(function(){this.remove(a,!0)}.bind(this)),c.appendTo(this.container)},add_keys:function(a){var b=this.field().get_context(this.record()),c=this.field().get_domain(this.record()),d=Math.min(10,Sao.config.limit);a=jQuery.extend([],a);for(var e=function(a){return this.schema_model.execute("get_keys",[a],b).then(f)}.bind(this),f=function(a){for(var b=0,c=a.length;c>b;b++){var d=a[b];this.keys[d.name]=d}}.bind(this),g=[];a.length>0;){var h=a.splice(0,d);g.push(this.schema_model.execute("search",[[["name","in",h],c],0,Sao.config.limit,null],b).then(e))}return jQuery.when.apply(jQuery,g)},display:function(a,b){if(Sao.View.Form.Dict._super.display.call(this,a,b),b){var c,d=a?a.id:null;if(d!=this._record_id){for(c in this.fields)this.remove(c,!1);this._record_id=d}var e,f=b.get_client(a),g=Object.keys(f).filter(function(a){return!this.keys[a]}.bind(this));e=jQuery.isEmptyObject(g)?jQuery.when():this.add_keys(g),e.then(function(){var a,b,c,d=Object.keys(f).sort();for(a=0,b=d.length;b>a;a++){c=d[a];var e=f[c];if(this.keys[c]){this.fields[c]||this.add_line(c);var g=this.fields[c];g.set_value(e),g.set_readonly(this._readonly)}}var h=Object.keys(this.fields).filter(function(a){return!f[a]});for(a=0,b=h.length;b>a;a++)c=h[a],this.remove(c,!1)}.bind(this)),this._set_button_sensitive()}},get_entries:function(a){switch(a){case"char":return Sao.View.Form.Dict.Entry;case"boolean":return Sao.View.Form.Dict.Boolean;case"selection":return Sao.View.Form.Dict.Selection;case"integer":return Sao.View.Form.Dict.Integer;case"float":return Sao.View.Form.Dict.Float;case"numeric":return Sao.View.Form.Dict.Numeric;case"date":return Sao.View.Form.Dict.Date;case"datetime":return Sao.View.Form.Dict.DateTime}}}),Sao.View.Form.Dict.Entry=Sao.class_(Object,{class_:"dict-char",init:function(a,b){this.name=a,this.definition=b.keys[a],this.parent_widget=b,this.create_widget()},create_widget:function(){this.el=jQuery("<div/>",{"class":this.class_});var a=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(this.el);this.input=this.labelled=jQuery("<input/>",{type:"text","class":"form-control input-sm"}).appendTo(a),this.button=jQuery("<button/>",{"class":"btn btn-default",type:"button","arial-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(jQuery("<div/>",{"class":"input-group-btn"}).appendTo(a)),this.el.change(this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){return this.input.val()},set_value:function(a){this.input.val(a||"")},set_readonly:function(a){this.input.prop("readonly",a)}}),Sao.View.Form.Dict.Boolean=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-boolean",create_widget:function(){Sao.View.Form.Dict.Boolean._super.create_widget.call(this),this.input.attr("type","checkbox"),this.input.change(this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){return this.input.prop("checked")},set_value:function(a){this.input.prop("checked",a)}}),Sao.View.Form.Dict.Selection=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-selection",create_widget:function(){Sao.View.Form.Dict.Selection._super.create_widget.call(this);var a=jQuery("<select/>",{"class":"form-control input-sm"});a.change(this.parent_widget.focus_out.bind(this.parent_widget)),this.input.replaceWith(a),this.input=this.labelled=a;var b=jQuery.extend([],this.definition.selection);b.splice(0,0,[null,""]),b.forEach(function(b){a.append(jQuery("<option/>",{value:JSON.stringify(b[0]),text:b[1]}))})},get_value:function(){return JSON.parse(this.input.val())},set_value:function(a){this.input.val(JSON.stringify(a))}}),Sao.View.Form.Dict.Integer=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-integer",get_value:function(){var a=parseInt(this.input.val(),10);return isNaN(a)?null:a}}),Sao.View.Form.Dict.Float=Sao.class_(Sao.View.Form.Dict.Integer,{class_:"dict-float",get_value:function(){var a=Number(this.input.val());return isNaN(a)?null:a}}),Sao.View.Form.Dict.Numeric=Sao.class_(Sao.View.Form.Dict.Float,{class_:"dict-numeric",get_value:function(){var a=new Sao.Decimal(this.input.val());return isNaN(a.valueOf())?null:a}}),Sao.View.Form.Dict.Date=Sao.class_(Sao.View.Form.Dict.Entry,{class_:"dict-date",format:"%x",create_widget:function(){Sao.View.Form.Dict.Date._super.create_widget.call(this);var a=this.button.parent();jQuery("<button/>",{"class":"datepickerbutton btn btn-default",type:"button"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-calendar"})).prependTo(a),this.input.datetimepicker({format:Sao.common.moment_format(this.format)}),this.input.on("dp.change",this.parent_widget.focus_out.bind(this.parent_widget))},get_value:function(){var a=this.input.data("DateTimePicker").date();return a&&(a.isDate=!0),a},set_value:function(a){this.date.data("DateTimePicker").date(a)}}),Sao.View.Form.Dict.DateTime=Sao.class_(Sao.View.Form.Dict.Date,{class_:"dict-datetime",format:"%x %X",get_value:function(){var a=this.input.data("DateTimePicker").date();return a&&(a.isDateTime=!0),a}})}(),function(){"use strict";Sao.View.tree_column_get=function(a){switch(a){case"char":case"text":case"binary":return Sao.View.Tree.CharColumn;case"many2one":return Sao.View.Tree.Many2OneColumn;case"one2one":return Sao.View.Tree.One2OneColumn;case"date":return Sao.View.Tree.DateColumn;case"time":return Sao.View.Tree.TimeColumn;case"timedelta":return Sao.View.Tree.TimeDeltaColumn;case"one2many":return Sao.View.Tree.One2ManyColumn;case"many2many":return Sao.View.Tree.Many2ManyColumn;case"selection":return Sao.View.Tree.SelectionColumn;case"reference":return Sao.View.Tree.ReferenceColumn;case"float":case"numeric":return Sao.View.Tree.FloatColumn;case"integer":case"biginteger":return Sao.View.Tree.IntegerColumn;case"boolean":return Sao.View.Tree.BooleanColumn;case"image":return Sao.View.Tree.ImageColumn;case"url":case"email":case"callto":case"sip":return Sao.View.Tree.URLColumn;case"progressbar":return Sao.View.Tree.ProgressBar}},Sao.View.Tree=Sao.class_(Sao.View,{init:function(a,b,c){Sao.View.Tree._super.init.call(this,a,b),this.view_type="tree",this.selection_mode=a.attributes.selection_mode||Sao.common.SELECTION_SINGLE,this.el=jQuery("<div/>",{"class":"treeview responsive"}),this.expanded={},this.children_field=c,this.editable=Boolean(this.attributes.editable),this.columns=[],this.create_columns(a.model,b),this.rows=[],this.table=jQuery("<table/>",{"class":"tree table table-hover table-striped"}),this.columns.filter(function(a){return!a.attributes.tree_invisible}).length>1&&(this.table.addClass("responsive"),this.table.addClass("responsive-header")),this.el.append(this.table);var d=jQuery("<thead/>");this.table.append(d);var e=jQuery("<tr/>");if(this.selection_mode!=Sao.common.SELECTION_NONE){var f=jQuery("<th/>",{"class":"selection"});this.selection=jQuery("<input/>",{type:"checkbox","class":"selection"}),this.selection.change(this.selection_changed.bind(this)),f.append(this.selection),e.append(f)}d.append(e),this.columns.forEach(function(a){f=jQuery("<th/>");var b=jQuery("<label/>").text(a.attributes.string);this.editable&&(a.attributes.required&&b.addClass("required"),a.attributes.readonly||b.addClass("editable")),e.append(f.append(b)),a.header=f},this),this.tbody=jQuery("<tbody/>"),this.table.append(this.tbody);var g=jQuery("<div/>",{"class":"treefooter"});this.more=jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("More")).click(function(){this.display_size+=Sao.config.display_size,this.display()}.bind(this)),g.append(this.more),this.more.hide(),this.display_size=Sao.config.display_size,this.el.append(g)},create_columns:function(a,b){b.find("tree").children().each(function(b,c){for(var d,e,f={},g=0,h=c.attributes.length;h>g;g++)e=c.attributes[g],f[e.name]=e.value;if(["readonly","tree_invisible","expand","completion"].forEach(function(a){f[a]&&(f[a]=1==f[a])}),"field"==c.tagName){var i=f.name;f.widget||(f.widget=a.fields[i].description.type);var j=["relation","domain","selection","relation_field","string","views","invisible","add_remove","sort","context","filename","autocomplete","translate","create","delete","selection_change_with","schema_model","required","readonly"];for(g in j){var k=j[g];k in a.fields[i].description&&null===c.getAttribute(k)&&(f[k]=a.fields[i].description[k])}var l=Sao.View.tree_column_get(f.widget);d=new l(a,f);~["url","email","callto","sip"].indexOf(f.widget)&&d.prefixes.push(new Sao.View.Tree.Affix(f,f.widget)),"icon"in f&&d.prefixes.push(new Sao.View.Tree.Affix(f));var m,n,o=c.childNodes;for(g=0;g<o.length;g++){for(m=o[g],n={},g=0,h=m.attributes.length;h>g;g++)e=m.attributes[g],n[e.name]=e.value;n.name||(n.name=i),"prefix"==m.tagName?d.prefixes.push(new Sao.View.Tree.Affix(n)):d.suffixes.push(new Sao.View.Tree.Affix(n))}this.fields[i]=!0}else"button"==c.tagName&&(d=new Sao.View.Tree.ButtonColumn(this.screen,f));this.columns.push(d)}.bind(this))},get_buttons:function(){var a=[];return this.columns.forEach(function(b){b instanceof Sao.View.Tree.ButtonColumn&&a.push(b)}),a},display:function(a,b){var c=this.screen.current_record;if(!a)if(a=this.get_selected_paths(),c){var d=c.get_path(this.screen.group);d=d.map(function(a){return a[1]}),Sao.common.contains(a,d)||(a=[d])}else c||(a=[]);b=b||[],(this.screen.group.length!=this.rows.length||!Sao.common.compare(this.screen.group,this.rows.map(function(a){return a.record}))||this.children_field)&&this.construct(a,b);var e=[];jQuery.isEmptyObject(this.screen.domain)||e.push(this.screen.domain);var f=this.screen.screen_container.get_tab_domain();jQuery.isEmptyObject(f)||e.push(f);var g=new Sao.common.DomainInversion;return e=g.simplify(e),this.columns.forEach(function(a){var b=a.attributes.name;if(b)if(a.attributes.tree_invisible)a.header.hide();else if(b===this.screen.exclude_field)a.header.hide();else{var c=g.domain_inversion(e,b);"boolean"!=typeof c&&(c=g.simplify(c));var d=g.unique_value(c)[0];d&&jQuery.isEmptyObject(this.children_field)?a.header.hide():a.header.show()}}.bind(this)),this.redraw(a,b),jQuery.when()},construct:function(a,b){this.rows=[],this.tbody.empty();var c=function(c,d,e){var f;f=this.editable?Sao.View.Tree.RowEditable:Sao.View.Tree.Row;var g=new f(this,c,d);this.rows.push(g),g.construct(a,b)};this.screen.group.slice(0,this.display_size).forEach(c.bind(this)),this.display_size>=this.screen.group.length?this.more.hide():this.more.show()},redraw:function(a,b){var c=function(c,d,e){this.rows[d].redraw(a,b)};this.screen.group.slice(0,this.display_size).forEach(c.bind(this))},switch_:function(a){this.screen.row_activate()},select_changed:function(a){var b=this.screen.current_record;if(this.screen.set_current_record(a),this.editable&&b){var c=function(){this.screen.set_current_record(b),this.set_cursor()}.bind(this);this.screen.group.parent||b===a?b!==a&&this.screen.attributes.pre_validate&&b.pre_validate().then(function(a){a||c()}):b.validate(this.get_fields()).then(function(a){a?b.save().fail(c):c()})}},selected_records:function(){if(this.selection_mode==Sao.common.SELECTION_NONE)return[];var a=[],b=function(c){c.is_selected()&&a.push(c.record),c.rows.forEach(b)};return this.rows.forEach(b),this.selection.prop("checked")&&!this.selection.prop("indeterminate")&&this.screen.group.slice(this.rows.length).forEach(function(b){a.push(b)}),a},selection_changed:function(){var a=this.selection.prop("checked"),b=function(c){c.set_selection(a),c.rows.forEach(b)};this.rows.forEach(b),a&&this.rows[0]?this.select_changed(this.rows[0].record):this.select_changed(null)},update_selection:function(){if(!this.selection.prop("checked")){var a=this.selected_records();this.selection.prop("indeterminate",!1),jQuery.isEmptyObject(a)?this.selection.prop("checked",!1):a.length==this.tbody.children().length&&this.display_size>=this.screen.group.length?this.selection.prop("checked",!0):(this.selection.prop("indeterminate",!0),this.selection.prop("checked",!0))}},get_selected_paths:function(){function a(c,d){var e,f,g,h;for(e=0,g=c.rows.length;g>e;e++)f=c.rows[e],h=d.concat([f.record.id]),f.is_selected()&&b.push(h),a(f,h)}var b=[];return a(this,[]),b},get_expanded_paths:function(a,b){var c,d,e,f,g;void 0===a&&(a=[]),void 0===b&&(b=[]),d=[],e=this.find_row(a),f=e?e.rows:this.rows;for(var h=0,i=this.n_children(e);i>h;h++)g=a.concat([h]),e=f[h],e&&e.is_expanded()&&(c=b.concat(e.record.id),d.push(c),d=d.concat(this.get_expanded_paths(g,c)));return d},find_row:function(a){for(var b,c=null,d=this.rows,e=0,f=a.length;f>e;e++){if(b=a[e],!d||b>=d.length)return null;if(c=d[b],d=c.rows,!this.children_field)break}return c},n_children:function(a){return a&&this.children_field?a.record._values[this.children_field].length:this.rows.length},set_cursor:function(a,b){var c,d,e,f,g,h,i,j;if(this.screen.current_record){for(d=null,c=0;c<this.rows.length;c++)if(e=this.rows[c].record_to_path(this.screen.current_record)){e.unshift(c),d=e;break}f=null,d?(h=d[0],i=d.slice(1),i.length>0&&this.rows[h].expand_to_path(i),f=this.find_row(d)):this.rows.length>0&&(f=this.rows[0]),f&&(g=f.next_column(null,a),j=f._get_column_td(g),this.editable?(j.triggerHandler("click"),a?j.triggerHandler("click"):j.find(":input,[tabindex=0]").focus()):j.find(":input,[tabindex=0]").focus())}}}),Sao.View.Tree.Row=Sao.class_(Object,{init:function(a,b,c,d){this.tree=a,this.rows=[],this.record=b,this.parent_=d,this.children_field=a.children_field,this.expander=null;var e=[];d&&(e=jQuery.extend([],d.path.split("."))),e.push(c),this.path=e.join("."),this.el=jQuery("<tr/>")},is_expanded:function(){return this.path in this.tree.expanded},get_last_child:function(){return this.children_field&&this.is_expanded()&&!jQuery.isEmptyObject(this.rows)?this.rows[this.rows.length-1].get_last_child():this},get_id_path:function(){return this.parent_?this.parent_.get_id_path().concat([this.record.id]):[this.record.id]},build_widgets:function(){var a=jQuery("<table/>");a.css("width","100%");var b=jQuery("<tr/>");return a.append(b),[a,b]},construct:function(a,b){a=a||[],b=b||[];for(var c=this.el[0];c.firstChild;)c.removeChild(c.firstChild);var d;this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(d=jQuery("<td/>"),this.el.append(d),this.selection=jQuery("<input/>",{type:"checkbox","class":"selection"}),this.selection.change(this.selection_changed.bind(this)),d.append(this.selection));for(var e=this.path.split(".").length,f=0;f<this.tree.columns.length;f++){var g=this.tree.columns[f];d=jQuery("<td/>",{"data-title":g.attributes.string}).append(jQuery("<div/>",{"aria-hidden":!0})),d.css("overflow","hidden"),d.on("click keypress",{column:f,td:d},Sao.common.click_press(this.select_row.bind(this),!0)),this.tree.editable||d.dblclick(this.switch_row.bind(this));var h=this.build_widgets(),i=h[0],j=h[1];if(d.append(i),0===f&&this.children_field){var k="glyphicon-plus";(this.is_expanded()||~b.indexOf(this.record.id))&&(k="glyphicon-minus"),this.expander=jQuery("<span/>",{"class":"glyphicon "+k,tabindex:0}),this.expander.html(" "),this.expander.css("margin-left",e-1+"em"),this.expander.css("float","left"),this.expander.on("click keypress",Sao.common.click_press(this.toggle_row.bind(this))),j.append(jQuery("<td/>",{"class":"expander"}).append(this.expander).css("width",1))}var l;if(g.prefixes)for(l=0;l<g.prefixes.length;l++){g.prefixes[l];j.append(jQuery("<td/>",{"class":"prefix"}).css("width",1))}if(j.append(jQuery("<td/>",{"class":"widget"})),g.suffixes)for(l=0;l<g.suffixes.length;l++){g.suffixes[l];j.append(jQuery("<td/>",{"class":"suffix"}).css("width",1))}this.el.append(d)}if(this.parent_){var m=this.parent_.get_last_child();m.el.after(this.el)}else this.tree.tbody.append(this.el);var n=this.get_id_path();(this.is_expanded()||Sao.common.contains(b,n))&&(this.tree.expanded[this.path]=this,this.expand_children(a,b))},_get_column_td:function(a,b){b=b||this.el;var c=0;return this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(c+=1),jQuery(b.children()[a+c])},redraw:function(a,b){a=a||[],b=b||[];for(var c=function(){jQuery.isEmptyObject(this.record.field_get(this.children_field))&&this.expander.css("visibility","hidden")},d=0;d<this.tree.columns.length;d++){0===d&&this.children_field&&this.record.load(this.children_field).done(c.bind(this));var e=this.tree.columns[d],f=this._get_column_td(d),g=f.find("tr");if(e.prefixes)for(var h=0;h<e.prefixes.length;h++){var i=e.prefixes[h];jQuery(g.children(".prefix")[h]).html(i.render(this.record))}if(jQuery(g.children(".widget")).html(e.render(this.record)),e.suffixes)for(var j=0;j<e.suffixes.length;j++){var k=e.suffixes[j];jQuery(g.children(".suffix")[j]).html(k.render(this.record))}e.attributes.tree_invisible||"none"==e.header.css("display")?f.hide():f.show()}var l=this.get_id_path();if(this.set_selection(Sao.common.contains(a,l)),this.is_expanded()||Sao.common.contains(b,l)){if(this.tree.expanded[this.path]=this,!this.record._values[this.children_field]||this.record._values[this.children_field].length>0&&0===this.rows.length)this.expand_children(a,b);else{var m;for(d=0;d<this.rows.length;d++)m=this.rows[d],m.redraw(a,b)}this.expander&&this.update_expander(!0)}else this.expander&&this.update_expander(!1);this.record.deleted()||this.record.removed()?this.el.css("text-decoration","line-through"):this.el.css("text-decoration","inherit")},toggle_row:function(){return this.is_expanded()?(this.update_expander(!1),delete this.tree.expanded[this.path],this.collapse_children()):(this.update_expander(!0),this.tree.expanded[this.path]=this,this.expand_children()),!1},update_expander:function(a){a?(this.expander.removeClass("glyphicon-plus"),this.expander.addClass("glyphicon-minus")):(this.expander.removeClass("glyphicon-minus"),this.expander.addClass("glyphicon-plus"))},collapse_children:function(){this.rows.forEach(function(a,b,c){a.collapse_children();var d=a.el[0];d.parentNode.removeChild(d)}),this.rows=[]},expand_children:function(a,b){var c=function(){if(jQuery.isEmptyObject(this.rows)){var c=function(c,d,e){var f=new this.Class(this.tree,c,d,this);f.construct(a,b),f.redraw(a,b),this.rows.push(f)},d=this.record.field_get_client(this.children_field);d.forEach(c.bind(this))}};return this.record.load(this.children_field).done(c.bind(this))},switch_row:function(){window.getSelection?window.getSelection().empty?window.getSelection().empty():window.getSelection().removeAllRanges&&window.getSelection().removeAllRanges():document.selection&&document.selection.empty(),(this.tree.selection_mode==Sao.common.SELECTION_NONE||(this.set_selection(!0),this.selection_changed(),this.is_selected()))&&this.tree.switch_(this.path)},select_row:function(a){this.tree.selection_mode==Sao.common.SELECTION_NONE?(this.tree.select_changed(this.record),this.switch_row()):(a.ctrlKey||this.tree.selection_mode!=Sao.common.SELECTION_SINGLE||this.tree.rows.forEach(function(a){a.set_selection(!1)}.bind(this)),this.set_selection(!this.is_selected()),this.selection_changed());var b=a.data.td,c=a.data.column;b.on("click keypress",{column:c,td:b},Sao.common.click_press(this.select_row.bind(this),!0))},is_selected:function(){return this.tree.selection_mode==Sao.common.SELECTION_NONE?!1:this.selection.prop("checked")},set_selection:function(a){this.tree.selection_mode!=Sao.common.SELECTION_NONE&&(this.selection.prop("checked",a),a||this.tree.selection.prop("checked",!1))},selection_changed:function(){var a=this.is_selected();this.set_selection(a),a?this.tree.select_changed(this.record):this.tree.select_changed(this.tree.selected_records()[0]||null),this.tree.update_selection()},record_to_path:function(a){var b,c;if(a==this.record)return[];for(b=0;b<this.rows.length;b++)if(c=this.rows[b].record_to_path(a))return c.unshift(b),c},expand_to_path:function(a){var b,c;b=a[0],c=a.slice(1),c.length>0&&this.rows[b].expand_children().done(function(){this.rows[b].expand_to_path(c)}.bind(this))},next_column:function(a,b,c){var d,e,f,g;for(c=c||1,null===a&&c>0?a=-1:null===a&&(a=0),f=0,d=0;d<this.tree.columns.length&&(f=(a+c*(d+1))%this.tree.columns.length,0>f&&(f+=this.tree.columns.length),a=this.tree.columns[f],g=a.field.get_state_attrs(this.record),e=b?a.attributes.readonly||g.readonly:!1,g.invisible||e);d++);return f}}),Sao.View.Tree.RowEditable=Sao.class_(Sao.View.Tree.Row,{init:function(a,b,c,d){Sao.View.Tree.RowEditable._super.init.call(this,a,b,c,d),this.edited_column=null},redraw:function(a,b){var c,d,e,f,g;for(Sao.View.Tree.RowEditable._super.redraw.call(this,a,b),c=0;c<this.tree.columns.length;c++)e=this._get_column_td(c),d=e.find("tr"),f=jQuery(d.children(".widget-editable")).data("widget"),f&&(g=this.record.model.fields[f.field_name],f.display(this.record,g))},select_row:function(a){function b(a,d){var e,f;for(e=0;e<a.length;e++)f=a[e],f.is_selected()&&(c=f),f!=d&&f.set_selection(!1),b(f.rows,d)}var c,d,e,f,g;b(this.tree.rows,this),this.selection_changed();var h;h=c&&c!=this&&!this.tree.screen.group.parent?c.record.save():jQuery.when(),h.done(function(){if(c&&null!==c.edited_column){d=c.get_active_td(),d.on("click keypress",{td:d,column:c.edited_column},Sao.common.click_press(c.select_row.bind(c),!0));var b=this.tree.columns[c.edited_column];c.get_static_el().html(b.render(c.record)).show(),c.empty_editable_el()}if(this.is_selected()){this.edited_column=a.data.column,e=this.get_active_td();var h=this.tree.columns[this.edited_column].attributes,i=Sao.View.editabletree_widget_get(h.widget);g=new i(h.name,this.tree.screen.model,h),g.view=this.tree,g.el.on("focusin",function(){jQuery(this).parents(".treeview td").css("overflow","visible")}),g.el.on("focusout",function(){jQuery(this).parents(".treeview td").css("overflow","hidden")});var j=this.get_editable_el();j.append(g.el),j.data("widget",g),g.el.on("keydown",this.key_press.bind(this)),f=this.record.model.fields[g.field_name],g.display(this.record,f),this.get_static_el().hide(),this.get_editable_el().show(),g.focus()}else{this.set_selection(!0),this.selection_changed();var k=a.data.td,l=a.data.column;k.on("click keypress",{column:l,td:k},Sao.common.click_press(this.select_row.bind(this),!0))}}.bind(this))},get_static_el:function(){var a=this.get_active_td();return a.find(".widget")},get_editable_el:function(){var a=this.get_active_td(),b=a.find(".widget-editable");return b.length||(b=jQuery("<td/>",{"class":"widget-editable"}).insertAfter(a.find(".widget"))),b},empty_editable_el:function(){var a;a=this.get_editable_el(),a.empty(),a.data("widget",null),this.edited_column=null},get_active_td:function(){return this._get_column_td(this.edited_column)},key_press:function(a){var b,c,d,e,f;if(a.which==Sao.common.TAB_KEYCODE||a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE||a.which==Sao.common.ESC_KEYCODE||a.which==Sao.common.RETURN_KEYCODE){var g=this.tree.columns[this.edited_column];if(g.field.validate(this.record))if(a.which==Sao.common.TAB_KEYCODE){var h=1;a.shiftKey&&(h=-1),a.preventDefault(),d=this.next_column(this.edited_column,!0,h),window.setTimeout(function(){var a=this._get_column_td(d);a.triggerHandler("click",{column:d,td:a})}.bind(this),0)}else if(a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE)f=a.which==Sao.common.UP_KEYCODE?this.el.prev("tr"):this.el.next("tr"),c=this.edited_column,this.record.validate(this.tree.get_fields()).then(function(a){if(a){if(this.tree.screen.attributes.pre_validate)return this.record.pre_validate().fail(function(){f=null});if(!this.tree.screen.model.parent)return this.record.save().fail(function(){f=null})}else{f=null;var b=this.record.invalid_fields();for(e=0;e<this.tree.columns.length;e++){var d=this.tree.columns[e];d.attributes.name in b&&(c=e)}}}.bind(this)).then(function(){window.setTimeout(function(){this._get_column_td(c,f).trigger("click").trigger("click")}.bind(this),0)}.bind(this));else if(a.which==Sao.common.ESC_KEYCODE)this.get_static_el().show(),b=this.get_active_td(),b.on("click keypress",{column:this.edited_column,td:b},Sao.common.click_press(this.select_row.bind(this),!0)),this.empty_editable_el();else if(a.which==Sao.common.RETURN_KEYCODE){var i=function(a){var b=this._get_column_td(this.edited_column,a);b.triggerHandler("click"),b.triggerHandler("click")}.bind(this);f="bottom"==this.tree.attributes.editable?this.el.next("tr"):this.el.prev("tr"),f.length?i(f):this.tree.screen.new_().done(function(){var a,b=this.tree.tbody.children("tr");a="bottom"==this.tree.attributes.editable?b.last():b.first(),i(a)}.bind(this))}}}}),Sao.View.Tree.Affix=Sao.class_(Object,{init:function(a,b){this.attributes=a,this.protocol=b||null,this.icon=a.icon,this.protocol&&!this.icon&&(this.icon="tryton-web-browser");
+},get_cell:function(){var a;return this.protocol?(a=jQuery("<a/>",{target:"_new"}),a.append(jQuery("<img/>")),a.click({cell:a},this.clicked.bind(this))):this.icon?a=jQuery("<img/>"):(a=jQuery("<span/>"),a.attr("tabindex",0)),a.addClass("column-affix"),a},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){var c,d,e=a.model.fields[this.attributes.name],f=e.get_state_attrs(a).invisible;if(f?b.hide():b.show(),this.protocol){if(c=e.get(a),!jQuery.isEmptyObject(c))switch(this.protocol){case"email":c="mailto:"+c;break;case"callto":c="callto:"+c;break;case"sip":c="sip:"+c}b.attr("src",c)}if(this.icon){if(this.icon in a.model.fields){var g=a.model.fields[this.icon];c=g.get_client(a)}else c=this.icon;d=Sao.common.ICONFACTORY.register_icon(c),d.done(function(a){var c;c=b.children("img").length?b.children("img"):b,c.attr("src",a)}.bind(this))}else c=this.attributes.string||"",c||(c=e.get_client(a)||""),b.text(c)}.bind(this)),b},clicked:function(a){a.preventDefault(),window.open(a.data.cell.attr("src"),"_blank")}}),Sao.View.Tree.CharColumn=Sao.class_(Object,{class_:"column-char",init:function(a,b){this.type="field",this.model=a,this.field=a.fields[b.name],this.attributes=b,this.prefixes=[],this.suffixes=[],this.header=null},get_cell:function(){var a=jQuery("<div/>",{"class":this.class_,tabindex:0});return a},update_text:function(a,b){a.text(this.field.get_client(b))},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){this.update_text(b,a),this.field.set_state(a);var c=this.field.get_state_attrs(a);c.invisible?b.hide():b.show()}.bind(this)),b}}),Sao.View.Tree.IntegerColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-integer",init:function(a,b){Sao.View.Tree.IntegerColumn._super.init.call(this,a,b),this.factor=Number(b.factor||1)},get_cell:function(){return Sao.View.Tree.IntegerColumn._super.get_cell.call(this)},update_text:function(a,b){a.text(this.field.get_client(b,this.factor))}}),Sao.View.Tree.FloatColumn=Sao.class_(Sao.View.Tree.IntegerColumn,{class_:"column-float"}),Sao.View.Tree.BooleanColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-boolean",get_cell:function(){return jQuery("<input/>",{type:"checkbox",disabled:!0,"class":this.class_,tabindex:0})},update_text:function(a,b){a.prop("checked",this.field.get(b))}}),Sao.View.Tree.Many2OneColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-many2one"}),Sao.View.Tree.One2OneColumn=Sao.class_(Sao.View.Tree.Many2OneColumn,{class_:"column-one2one"}),Sao.View.Tree.SelectionColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-selection",init:function(a,b){Sao.View.Tree.SelectionColumn._super.init.call(this,a,b),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a)},update_selection:function(a,b){Sao.common.selection_mixin.update_selection.call(this,a,this.field,b)},update_text:function(a,b){this.update_selection(b,function(){for(var c,d,e=this.field.get(b),f=!1,g=0,h=this.selection.length;h>g;g++)if(this.selection[g][0]===e){f=!0,d=this.selection[g][1];break}c=f?jQuery.when(d):Sao.common.selection_mixin.get_inactive_selection.call(this,e).then(function(a){return a[1]}),c.done(function(b){a.text(b)}.bind(this))}.bind(this))}}),Sao.View.Tree.ReferenceColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-reference",init:function(a,b){Sao.View.Tree.ReferenceColumn._super.init.call(this,a,b),Sao.common.selection_mixin.init.call(this),this.init_selection()},init_selection:function(a){Sao.common.selection_mixin.init_selection.call(this,a)},update_selection:function(a,b){Sao.common.selection_mixin.update_selection.call(this,a,this.field,b)},update_text:function(a,b){this.update_selection(b,function(){var c,d,e=this.field.get_client(b);e?(c=e[0],d=e[1]):(c="",d=""),c?a.text(this.selection[c]||c+","+d):a.text(d)}.bind(this))}}),Sao.View.Tree.DateColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-date",update_text:function(a,b){var c=this.field.get_client(b),d=this.field.date_format(b);a.text(Sao.common.format_date(d,c))}}),Sao.View.Tree.TimeColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-time",update_text:function(a,b){var c=this.field.get_client(b);a.text(Sao.common.format_time(this.field.time_format(b),c))}}),Sao.View.Tree.TimeDeltaColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-timedelta"}),Sao.View.Tree.One2ManyColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-one2many",update_text:function(a,b){a.text("( "+this.field.get_client(b).length+" )")}}),Sao.View.Tree.Many2ManyColumn=Sao.class_(Sao.View.Tree.One2ManyColumn,{class_:"column-many2many"}),Sao.View.Tree.ImageColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-image",get_cell:function(){var a=jQuery("<img/>",{"class":this.class_,tabindex:0});return a.css("width","100%"),a},render:function(a){var b=this.get_cell();return a.load(this.attributes.name).done(function(){var c=this.field.get_client(a);c=c?c>Sao.common.BIG_IMAGE_SIZE?jQuery.when(null):this.field.get_data(a):jQuery.when(null),c.done(function(a){var c,d;a?(d=new Blob([a[0][this.field.name]]),c=window.URL.createObjectURL(d)):c=null,b.attr("src",c)}.bind(this))}.bind(this)),b}}),Sao.View.Tree.URLColumn=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-url",render:function(a){var b=Sao.View.Tree.URLColumn._super.render.call(this,a);this.field.set_state(a);var c=this.field.get_state_attrs(a);c.readonly?b.hide():b.show()}}),Sao.View.Tree.ProgressBar=Sao.class_(Sao.View.Tree.CharColumn,{class_:"column-progressbar",get_cell:function(){var a=jQuery("<div/>",{"class":this.class_+" progress",tabindex:0}),b=jQuery("<div/>",{"class":"progress-bar",role:"progressbar","aria-valuemin":0,"aria-valuemax":100}).appendTo(a);return b.css("min-width: 2em"),a},update_text:function(a,b){var c=this.field.get_client(b,100);c&&(c=Sao.i18n.gettext("%1%",c));var d=this.field.get(b)||0,e=a.find(".progress-bar");e.attr("aria-valuenow",100*d),e.css("width",100*d+"%"),e.text(c)}}),Sao.View.Tree.ButtonColumn=Sao.class_(Object,{init:function(a,b){this.screen=a,this.type="button",this.attributes=b},render:function(a){var b=new Sao.common.Button(this.attributes);b.el.click(a,this.button_clicked.bind(this));var c=jQuery.map(this.screen.model.fields,function(a,b){return"eager"==(a.description.loading||"eager")?b:void 0});return a.load(c[0]).done(function(){b.set_state(a)}),b.el},button_clicked:function(a){var b=a.data;if(b==this.screen.current_record){var c=b.expr_eval(this.attributes.states||{});c.invisible||c.readonly||this.screen.button(this.attributes)}}}),Sao.View.editabletree_widget_get=function(a){switch(a){case"char":case"text":case"url":case"email":case"callto":case"sip":return Sao.View.EditableTree.Char;case"date":return Sao.View.EditableTree.Date;case"time":return Sao.View.EditableTree.Time;case"timedelta":return Sao.View.EditableTree.TimeDelta;case"integer":case"biginteger":return Sao.View.EditableTree.Integer;case"float":case"numeric":return Sao.View.EditableTree.Float;case"selection":return Sao.View.EditableTree.Selection;case"boolean":return Sao.View.EditableTree.Boolean;case"many2one":return Sao.View.EditableTree.Many2One;case"one2one":return Sao.View.EditableTree.One2One;case"one2many":case"many2many":return Sao.View.EditableTree.One2Many;case"binary":return Sao.View.EditableTree.Binary}},Sao.View.EditableTree={},Sao.View.EditableTree.editable_mixin=function(a){var b=function(a){(a.which==Sao.common.TAB_KEYCODE||a.which==Sao.common.UP_KEYCODE||a.which==Sao.common.DOWN_KEYCODE||a.which==Sao.common.ESC_KEYCODE||a.which==Sao.common.RETURN_KEYCODE)&&this.focus_out()};a.el.on("keydown",b.bind(a))},Sao.View.EditableTree.Char=Sao.class_(Sao.View.Form.Char,{class_:"editabletree-char",init:function(a,b,c){Sao.View.EditableTree.Char._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Date=Sao.class_(Sao.View.Form.Date,{class_:"editabletree-date",init:function(a,b,c){Sao.View.EditableTree.Date._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Time=Sao.class_(Sao.View.Form.Time,{class_:"editabletree-time",init:function(a,b,c){Sao.View.EditableTree.Time._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.TimeDelta=Sao.class_(Sao.View.Form.TimeDelta,{class_:"editabletree-timedelta",init:function(a,b,c){Sao.View.EditableTree.TimeDelta._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Integer=Sao.class_(Sao.View.Form.Integer,{class_:"editabletree-integer",init:function(a,b,c){Sao.View.EditableTree.Integer._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Float=Sao.class_(Sao.View.Form.Float,{class_:"editabletree-float",init:function(a,b,c){Sao.View.EditableTree.Float._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Selection=Sao.class_(Sao.View.Form.Selection,{class_:"editabletree-selection",init:function(a,b,c){Sao.View.EditableTree.Selection._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Boolean=Sao.class_(Sao.View.Form.Boolean,{class_:"editabletree-boolean",init:function(a,b,c){Sao.View.EditableTree.Boolean._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}}),Sao.View.EditableTree.Many2One=Sao.class_(Sao.View.Form.Many2One,{class_:"editabletree-many2one",init:function(a,b,c){Sao.View.EditableTree.Many2One._super.init.call(this,a,b,c),this.el.on("keydown",this.key_press.bind(this))},key_press:function(a){a.which==Sao.common.TAB_KEYCODE?this.focus_out():Sao.View.EditableTree.Many2One._super.key_press.call(this,a)}}),Sao.View.EditableTree.One2One=Sao.class_(Sao.View.Form.One2One,{class_:"editabletree-one2one",init:function(a,b,c){Sao.View.EditableTree.One2One._super.init.call(this,a,b,c),this.el.on("keydown",this.key_press.bind(this))},key_press:function(a){a.which==Sao.common.TAB_KEYCODE?this.focus_out():Sao.View.EditableTree.One2One._super.key_press.call(this,a)}}),Sao.View.EditableTree.One2Many=Sao.class_(Sao.View.EditableTree.Char,{class_:"editabletree-one2many",init:function(a,b,c){Sao.View.EditableTree.One2Many._super.init.call(this,a,b,c)},display:function(a,b){a?this.el.val("("+b.get_client(a).length+")"):this.el.val("")},key_press:function(a){a.which==Sao.common.TAB_KEYCODE&&this.focus_out()},set_value:function(a,b){}}),Sao.View.EditableTree.Binary=Sao.class_(Sao.View.Form.Binary,{class_:"editabletree-binary",init:function(a,b,c){Sao.View.EditableTree.Binary._super.init.call(this,a,b,c),Sao.View.EditableTree.editable_mixin(this)}})}(),function(){"use strict";Sao.View.Graph=Sao.class_(Sao.View,{init:function(a,b){Sao.View.Graph._super.init.call(this,a,b),this.view_type="graph",this.el=jQuery("<div/>",{"class":"graph"}),this.widgets={},this.widget=this.parse(b.children()[0]),this.widgets.root=this.widget,this.el.append(this.widget.el)},parse:function(a){var b,c,d,e,f,g,h,i=null,j=[],k=function(a){for(var b,c={},d=0,e=a.attributes.length;e>d;d++)b=a.attributes[d],c[b.name]=b.value;return c};for(e=0,f=a.children.length;f>e;e++)switch(d=a.children[e],d.tagName){case"x":for(g=0,h=d.children.length;h>g;g++)i=k(d.children[g]),b=this.screen.model.fields[i.name],i.string||(i.string=b.description.string);break;case"y":for(g=0,h=d.children.length;h>g;g++)c=k(d.children[g]),c.string||"#"==c.name||(b=this.screen.model.fields[c.name],c.string=b.description.string),j.push(c)}var l;switch(this.attributes.type){case"hbar":l=Sao.View.Graph.HorizontalBar;break;case"line":l=Sao.View.Graph.Line;break;case"pie":l=Sao.View.Graph.Pie;break;default:l=Sao.View.Graph.VerticalBar}return new l(this,i,j)},display:function(){return this.widget.display(this.screen.group)}}),Sao.View.Graph.Chart=Sao.class_(Object,{_chart_type:void 0,init:function(a,b,c){this.view=a,this.xfield=b,this.yfields=c,this.el=jQuery("<div/>"),this.el.uniqueId()},update_data:function(a){var b,c,d,e,f,g,h,i={};this.ids={},i.columns=[["labels"]];var j={},k=[this.xfield.name];for(e=0,f=this.yfields.length;f>e;e++)c=this.yfields[e],i.columns.push([c.string]),j[c.key||c.name]=e+1,k.push(c.name);var l=[],m=function(e){return function(){b=a[e];var f=b.field_get(this.xfield.name);i.columns[0][e+1]=f,this._add_id(f,b.id);var k;for(g=0,h=this.yfields.length;h>g;g++){if(c=this.yfields[g],d=c.key||c.name,k=i.columns[j[d]],c.domain){var l=jQuery.extend({},Sao.session.current_session.context);l.context=l,l._user=Sao.session.current_session.user_id;for(var m in a.model.fields)l[m]=b.field_get(m);var n=new Sao.PYSON.Decoder(l);if(!n.decode(c.domain)){k[e+1]=0;continue}}if("#"==c.name)k[e+1]=1;else{var o=b.field_get(c.name);o&&o.isTimeDelta&&(o=o.asSeconds()),k[e+1]=o||0}}}.bind(this)}.bind(this),n=function(a){return function(b){l.push(a.load(b))}},o=[];for(e=0,f=a.length;f>e;e++){for(b=a[e],k.forEach(n(a[e])),g=0,h=i.columns.length;h>g;g++)i.columns[g].push(void 0);o.push(jQuery.when.apply(jQuery,l).then(m(e)))}return jQuery.when.apply(jQuery,o).then(function(){return i})},_add_id:function(a,b){var c=a.isDate||a.isDateTime?a._d:a;c in this.ids||(this.ids[c]=[]),this.ids[c].push(b)},display:function(a){var b=this.update_data(a);return b.done(function(a){c3.generate(this._c3_config(a))}.bind(this)),b},_c3_config:function(a){var b={};b.bindto="#"+this.el.attr("id"),b.data=a,b.data.type=this._chart_type,b.data.x="labels",b.data.onclick=this.action.bind(this);var c,d,e,f;for(c=0,d=a.columns.length;d>c;c++)if(f=a.columns[c],"labels"==f[0]){e=!0;break}if(e&&f.length>1&&f[1]&&(f[1].isDateTime||f[1].isDate)){var g,h,i;h=this.view.screen.context.date_format||"%x",i="%X",g=f[1].isDateTime?function(a){return Sao.common.format_datetime(h,i,moment(a))}:function(a){return Sao.common.format_date(h,moment(a))},b.axis={x:{type:"timeseries",tick:{format:g}}}}return b},action:function(a,b){var c=this.ids[this._action_key(a)],d=jQuery.extend({},this.view.screen.context);delete d.active_ids,delete d.active_id,Sao.Action.exec_keyword("graph_open",{model:this.view.screen.model_name,id:c[0],ids:c},d,!1)},_action_key:function(a){return a.x}}),Sao.View.Graph.VerticalBar=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"bar"}),Sao.View.Graph.HorizontalBar=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"bar",_c3_config:function(a){var b=Sao.View.Graph.HorizontalBar._super._c3_config.call(this,a);b.axis.rotated=!0}}),Sao.View.Graph.Line=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"line"}),Sao.View.Graph.Pie=Sao.class_(Sao.View.Graph.Chart,{_chart_type:"pie",_c3_config:function(a){var b,c,d,e,f=Sao.View.Graph.Pie._super._c3_config.call(this,a),g=[];for(b=0,c=a.columns.length;c>b;b++)"labels"==a.columns[b][0]?d=a.columns[b].slice(1):e=a.columns[b].slice(1);delete f.axis,delete f.data.x;var h,i,j;d.length>0&&(d[0].isDateTime||d[0].isDate)&&(i=this.view.screen.context.date_format||"%x",j=i+" %X",h=d[1].isDateTime?function(a){return Sao.common.format_datetime(j,a)}:function(a){return Sao.common.format_date(i,a)});var k;for(b=0,c=d.length;c>b;b++)k=d[b],h&&(k=h(k)),g.push([k,e[b]]);return f.data.columns=g,f},_add_id:function(a,b){var c=a;if(a.isDateTime||a.isDate){var d=this.view.screen.context.date_format||"%x",e=d+" %X";c=a.isDateTime?Sao.common.format_datetime(e,a):Sao.common.format_date(d,a)}c in this.ids||(this.ids[c]=[]),this.ids[c].push(b)},_action_key:function(a){return a.id}})}(),function(){"use strict";Sao.View.Calendar=Sao.class_(Sao.View,{init:function(a,b){Sao.View.Graph._super.init.call(this,a,b),this.view_type="calendar",this.el=jQuery("<div/>",{"class":"calendar"}),Sao.common.warning.run(Sao.i18n.gettext("Calendar view not yet implemented"),Sao.i18n.gettext("Warning"))},display:function(){return jQuery.when()}})}(),function(){"use strict";Sao.Action={report_blob_url:void 0},Sao.Action.exec_action=function(a,b,c){c=void 0===c?{}:jQuery.extend({},c);var d=Sao.Session.current_session;"date_format"in c||d.context.locale&&d.context.locale.date&&(c.date_format=d.context.locale.date),b=void 0===b?{}:jQuery.extend({},b),b.action_id=a.id;var e={};switch(a.type){case"ir.action.act_window":e.view_ids=!1,e.mode=null,jQuery.isEmptyObject(a.views)?jQuery.isEmptyObject(a.view_id)||(e.view_ids=[a.view_id[0]]):(e.view_ids=[],e.mode=[],a.views.forEach(function(a){e.view_ids.push(a[0]),e.mode.push(a[1])})),void 0===a.pyson_domain&&(a.pyson_domain="[]");var f={active_model:b.model||null,active_id:b.id||null,active_ids:b.ids};f=jQuery.extend(f,d.context);var g=jQuery.extend({},f);g._user=d.user_id,e.context=new Sao.PYSON.Decoder(g).decode(a.pyson_context||"{}"),f=jQuery.extend(f,e.context),f=jQuery.extend(f,c),e.context=jQuery.extend(e.context,c);var h=jQuery.extend({},f);h.context=f,h._user=d.user_id,e.domain=new Sao.PYSON.Decoder(h).decode(a.pyson_domain);var i=jQuery.extend({},f);i.context=f,i._user=d.user_id,e.search_value=new Sao.PYSON.Decoder(i).decode(a.pyson_search_value||"[]");var j=jQuery.extend({},f);j.context=f,j._user=d.user_id;var k=new Sao.PYSON.Decoder(j);return e.tab_domain=[],a.domains.forEach(function(a,b){e.tab_domain.push([a[0],k.decode(a[1])])}),e.name=!1,a.window_name&&(e.name=a.name),e.model=a.res_model||b.res_model,e.res_id=a.res_id||b.res_id,e.limit=a.limit,e.icon=a["icon.rec_name"]||"",void Sao.Tab.create(e);case"ir.action.wizard":return e.action=a.wiz_name,e.data=b,e.name=a.name,e.context=c,e.window=a.window,void Sao.Wizard.create(e);case"ir.action.report":return e.name=a.report_name,e.data=b,e.direct_print=a.direct_print,e.email_print=a.email_print,e.email=a.email,e.context=c,void Sao.Action.exec_report(e);case"ir.action.url":return void window.open(a.url,"_blank")}},Sao.Action.exec_keyword=function(a,b,c,d,e){void 0===d&&(d=!0),void 0===e&&(e=!1);var f=b.id,g={method:"model.ir.action.keyword.get_keyword",params:[a,[b.model,f],{}]},h=Sao.rpc(g,Sao.Session.current_session),i=function(a){var f={};for(var g in a){var h=a[g];f[h.name.replace(/_/g,"")]=h}var i=Sao.common.selection(Sao.i18n.gettext("Select your action"),f,e);return i.then(function(a){Sao.Action.exec_action(a,b,c)},function(){jQuery.isEmptyObject(f)&&d&&alert(Sao.i18n.gettext("No action defined!"))})};return h.pipe(i)},Sao.Action.exec_report=function(a){a.context||(a.context={}),a.email||(a.email={});var b=jQuery.extend({},a.data),c=jQuery.extend({},Sao.Session.current_session.context);jQuery.extend(c,a.context),c.direct_print=a.direct_print,c.email_print=a.email_print,c.email=a.email;var d=Sao.rpc({method:"report."+a.name+".execute",params:[b.ids||[],b,c]},Sao.Session.current_session);d.done(function(a){var b=a[0],c=a[1],d=(a[2],a[3],new Blob([c],{type:Sao.common.guess_mimetype(b)})),e=window.URL.createObjectURL(d);Sao.Action.report_blob_url&&window.URL.revokeObjectURL(Sao.Action.report_blob_url),Sao.Action.report_blob_url=e,window.open(e)})},Sao.Action.execute=function(a,b,c,d){c?Sao.rpc({method:"model."+c+".search_read",params:[[["action","=",a]],0,1,null,null,d]},Sao.Session.current_session).done(function(a){Sao.Action.exec_action(a[0],b,d)}):Sao.rpc({method:"model.ir.action.read",params:[[a],["type"],d]},Sao.Session.current_session).done(function(c){Sao.Action.execute(a,b,c[0].type,d)})},Sao.Action.evaluate=function(a,b,c){a=jQuery.extend({},a);var d={};return"pyson_email"in a&&(d=c.expr_eval(a.pyson_email),jQuery.isEmptyObject(d)&&(d={})),"subject"in d||(d.subject=a.name.replace(/_/g,"")),a.email=d,a}}(),function(){"use strict";Sao.common={},Sao.common.BACKSPACE_KEYCODE=8,Sao.common.TAB_KEYCODE=9,Sao.common.RETURN_KEYCODE=13,Sao.common.ESC_KEYCODE=27,Sao.common.UP_KEYCODE=38,Sao.common.DOWN_KEYCODE=40,Sao.common.DELETE_KEYCODE=46,Sao.common.F2_KEYCODE=113,Sao.common.F3_KEYCODE=114,Sao.common.SELECTION_NONE=1,Sao.common.SELECTION_SINGLE=2,Sao.common.SELECTION_MULTIPLE=3,Sao.common.BIG_IMAGE_SIZE=Math.pow(10,6),Sao.common.compare=function(a,b){if(a.length!=b.length)return!1;for(var c=0;c<a.length;c++)if(a[c]instanceof Array&&b[c]instanceof Array){if(!Sao.common.compare(a[c],b[c]))return!1}else if(a[c]!=b[c])return!1;return!0},Sao.common.contains=function(a,b){for(var c=0;c<a.length;c++)if(Sao.common.compare(a[c],b))return!0;return!1},Sao.common.intersect=function(a,b){for(var c=0,d=0,e=[];c<a.length&&d<b.length;)a[c]<b[d]?c++:a[c]>b[d]?d++:(e.push(a[c]),c++,d++);return e},Sao.common.click_press=function(a,b){return function c(d){return"keypress"!=d.type||d.which==Sao.common.RETURN_KEYCODE?(b&&jQuery(this).off("click keypress",null,c),a(d)):void 0}},Sao.common.product=function(a,b){b=b||1;for(var c=[],d=0;b>d;)c=c.concat(a),d++;var e=[[]];return c.forEach(function(a){var b=[];e.forEach(function(c){a.forEach(function(a){b.push(c.concat([a]))})}),e=b}),e},Sao.common.selection=function(a,b,c){void 0===c&&(c=!1);var d=jQuery.Deferred();if(jQuery.isEmptyObject(b))return d.fail(),d;var e=Object.keys(b).sort();if(1==e.length&&!c){var f=e[0];return d.resolve(b[f]),d}var g=new Sao.Dialog(a||Sao.i18n.gettext("Your selection:"),"selection-dialog");return e.forEach(function(a,b){jQuery("<div/>",{"class":"checkbox"}).append(jQuery("<label/>").append(jQuery("<input/>",{type:"radio",name:"selection",value:b})).append(" "+a)).appendTo(g.body)}),g.body.find("input").first().prop("checked",!0),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){g.modal.modal("hide"),d.fail()}).appendTo(g.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){var a=g.body.find("input:checked").attr("value");g.modal.modal("hide"),d.resolve(b[e[a]])}).appendTo(g.footer),g.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),g.modal.modal("show"),d},Sao.common.moment_format=function(a){return a.replace("%a","ddd").replace("%A","dddd").replace("%w","d").replace("%d","DD").replace("%b","MMM").replace("%B","MMMM").replace("%m","MM").replace("%y","YY").replace("%Y","YYYY").replace("%H","HH").replace("%I","hh").replace("%p","A").replace("%M","mm").replace("%S","ss").replace("%f","SSS").replace("%z","ZZ").replace("%Z","zz").replace("%j","DDDD").replace("%U","ww").replace("%W","WW").replace("%c","llll").replace("%x","L").replace("%X","LTS").replace("%","%%")},Sao.common.date_format=function(a){if(jQuery.isEmptyObject(a)&&Sao.Session.current_session){var b=Sao.Session.current_session.context;b.locale&&b.locale.date&&(a=b.locale.date)}return a?Sao.common.moment_format(a):"%Y-%m-%d"},Sao.common.format_time=function(a,b){return b?b.format(Sao.common.moment_format(a)):""},Sao.common.parse_time=function(a,b){if(jQuery.isEmptyObject(b))return null;var c=function(c){var d=a.indexOf(c);if(~d){var e=parseInt(b.slice(d,d+c.length),10);if(!isNaN(e))return e}return 0};return Sao.Time(c("%H"),c("%M"),c("%S"),c("%f"))},Sao.common.format_date=function(a,b){return b?b.format(Sao.common.moment_format(a)):""},Sao.common.parse_date=function(a,b){var c=moment(b,Sao.common.moment_format(a));return c=c.isValid()?Sao.Date(c.year(),c.month(),c.date()):null},Sao.common.format_datetime=function(a,b,c){return c?c.format(Sao.common.moment_format(a+" "+b)):""},Sao.common.parse_datetime=function(a,b,c){var d=moment(c,Sao.common.moment_format(a+" "+b));return d=d.isValid()?Sao.DateTime(d.year(),d.month(),d.date(),d.hour(),d.minute(),d.second(),d.millisecond()):null},Sao.common.timedelta={},Sao.common.timedelta.DEFAULT_CONVERTER={s:1},Sao.common.timedelta.DEFAULT_CONVERTER.m=60*Sao.common.timedelta.DEFAULT_CONVERTER.s,Sao.common.timedelta.DEFAULT_CONVERTER.h=60*Sao.common.timedelta.DEFAULT_CONVERTER.m,Sao.common.timedelta.DEFAULT_CONVERTER.d=24*Sao.common.timedelta.DEFAULT_CONVERTER.h,Sao.common.timedelta.DEFAULT_CONVERTER.w=7*Sao.common.timedelta.DEFAULT_CONVERTER.d,Sao.common.timedelta.DEFAULT_CONVERTER.M=4*Sao.common.timedelta.DEFAULT_CONVERTER.w,Sao.common.timedelta.DEFAULT_CONVERTER.Y=365*Sao.common.timedelta.DEFAULT_CONVERTER.d,Sao.common.timedelta._get_separator=function(){return{Y:Sao.i18n.gettext("Y"),M:Sao.i18n.gettext("M"),w:Sao.i18n.gettext("w"),d:Sao.i18n.gettext("d"),h:Sao.i18n.gettext("h"),m:Sao.i18n.gettext("m"),s:Sao.i18n.gettext("s")}},Sao.common.timedelta.format=function(a,b){if(!a)return"";b||(b=Sao.common.timedelta.DEFAULT_CONVERTER);var c=[];a=a.asSeconds();var d="";0>a&&(d="-"),a=Math.abs(a),b=Object.keys(b).map(function(a){return[a,b[a]]}),b.sort(function(a,b){return b[1]-a[1]});for(var e,f,g=[],h=0;h<b.length;h++){e=b[h][0],f=b[h][1];var i=Math.floor(a/f);a-=i*f,g.push(i)}for(h=0;h<b.length-3;h++)e=b[h][0],f=g[h],f&&c.push(f+Sao.common.timedelta._get_separator()[e]);if(jQuery(g.slice(-3)).is(function(a,b){return b})||jQuery.isEmptyObject(c)){var j=g.slice(-3,-1);j=("00"+j[0]).slice(-2)+":"+("00"+j[1]).slice(-2),(g.slice(-1)[0]||a)&&(j+=":"+("00"+g.slice(-1)[0]).slice(-2)),c.push(j)}return c=d+c.reduce(function(a,b){return a?a+" "+b:b}),a&&(jQuery(g.slice(-3)).is(function(a,b){return b})||(c+=" "),c+=(""+a.toFixed(6)).slice(1)),c},Sao.common.timedelta.parse=function(a,b){if(!a)return null;b||(b=Sao.common.timedelta.DEFAULT_CONVERTER);var c,d=Sao.common.timedelta._get_separator();for(var e in d)c=d[e],a=a.replace(c,c+" ");for(var f,g=0,h=a.split(" "),i=0;i<h.length;i++){var j=h[i];if(j.contains(":"))for(var k=j.split(":"),l=[b.h,b.m,b.s],m=0;m<Math.min(k.length,l.length);m++){var n=k[m],o=l[m];f=Math.abs(Number(n))*o,isNaN(f)||(g+=f)}else{var p=!1;for(var q in d)if(c=d[q],j.endsWith(c)){j=j.slice(0,-c.length),f=Math.abs(parseInt(j,10))*b[q],isNaN(f)||(g+=f),p=!0;break}p||(f=Math.abs(Number(j)),isNaN(f)||(g+=f))}}return a.contains("-")&&(g*=-1),Sao.TimeDelta(null,g)},Sao.common.ModelAccess=Sao.class_(Object,{init:function(){this.batchnum=100,this._access={}},load_models:function(a){var b=jQuery.Deferred();return a||(this._access={}),Sao.rpc({method:"model.ir.model.list_models",params:[{}]},Sao.Session.current_session).then(function(a){for(var c=[],d=function(a){this._access=jQuery.extend(this._access,a)},e=0;e<a.length;e+=this.batchnum){var f=a.slice(e,e+this.batchnum);c.push(Sao.rpc({method:"model.ir.model.access.get_access",params:[f,{}]},Sao.Session.current_session).then(d.bind(this)))}jQuery.when.apply(jQuery,c).then(b.resolve,b.reject)}.bind(this)),b},get:function(a){return this._access[a]}}),Sao.common.MODELACCESS=new Sao.common.ModelAccess,Sao.common.ModelHistory=Sao.class_(Object,{init:function(){this._models=[]},load_history:function(){return this._models=[],Sao.rpc({method:"model.ir.model.list_history",params:[{}]},Sao.Session.current_session).then(function(a){this._models=a}.bind(this))},contains:function(a){return~this._models.indexOf(a)}}),Sao.common.MODELHISTORY=new Sao.common.ModelHistory,Sao.common.ViewSearch=Sao.class_(Object,{load_searches:function(){return this.searches={},Sao.rpc({method:"model.ir.ui.view_search.get_search",params:[{}]},Sao.Session.current_session).then(function(a){this.searches=a}.bind(this))},get:function(a){return this.searches[a]||[]},add:function(a,b,c){return Sao.rpc({method:"model.ir.ui.view_search.create",params:[[{model:a,name:b,domain:(new Sao.PYSON.Encoder).encode(c)}],{}]},Sao.Session.current_session).then(function(d){var e=d[0];void 0===this.searches[a]&&(this.searches[a]=[]),this.searches[a].push([e,b,c])}.bind(this))},remove:function(a,b){return Sao.rpc({method:"model.ir.ui.view_search.delete",params:[[b],{}]},Sao.Session.current_session).then(function(){for(var c=0;c<this.searches[a].length;c++){var d=this.searches[a][c];if(d[0]===b){this.searches[a].splice(c,1);break}}}.bind(this))}}),Sao.common.VIEW_SEARCH=new Sao.common.ViewSearch,Sao.common.humanize=function(a){for(var b=["bytes","KB","MB","GB","TB","PB"],c=0,d=b.length;d>c;c++){if(1e3>a)return a.toPrecision(4)+" "+b[c];a/=1e3}},Sao.common.EvalEnvironment=function(a,b){void 0===b&&(b="eval");var c;if("eval"==b)c=a.get_eval();else{c={};for(var d in a.model.fields){var e=a.model.fields[d];c[d]=e.get_on_change_value(a)}}return c.id=a.id,a.group.parent&&Object.defineProperty(c,"_parent_"+a.group.parent_name,{enumerable:!0,get:function(){return Sao.common.EvalEnvironment(a.group.parent,b)}}),c.get=function(a,b){return this.hasOwnProperty(a)?this[a]:b},c},Sao.common.selection_mixin={},Sao.common.selection_mixin.init=function(){this.selection=null,this.inactive_selection=[],this._last_domain=null,this._values2selection={},this._domain_cache={},void 0===this.nullable_widget&&(this.nullable_widget=!0)},Sao.common.selection_mixin.init_selection=function(a,b){a||(a={},(this.attributes.selection_change_with||[]).forEach(function(b){a[b]=null}));var c,d=JSON.stringify(a),e=this.attributes.selection||[],f=function(a){a=jQuery.extend([],a),(void 0===this.attributes.sort||this.attributes.sort)&&a.sort(function(a,b){return a[1].localeCompare(b[1])}),this.selection=jQuery.extend([],a),b&&b(this.selection)};e instanceof Array||d in this._values2selection?(d in this._values2selection&&(e=this._values2selection[d]),f.call(this,e),c=jQuery.when()):(c=jQuery.isEmptyObject(this.attributes.selection_change_with)?this.model.execute(e,[]):this.model.execute(e,[a]),c=c.then(function(a){return this._values2selection[d]=a,a}.bind(this)),c=c.then(f.bind(this))),this.inactive_selection=[],this._selection_prm=c},Sao.common.selection_mixin.update_selection=function(a,b,c){var d=function(){if(!b)return void(c&&c(this.selection));var d=b.get_domain(a);if("reference"==b.description.type&&(d=[]),"relation"in this.attributes){var e=b.get_context(a),f=JSON.stringify([d,e]);if(f in this._domain_cache&&(this.selection=this._domain_cache[f],this._last_domain=[d,e]),null!==this._last_domain&&Sao.common.compare(d,this._last_domain[0])&&JSON.stringify(e)==JSON.stringify(this._last_domain[1]))return void(c&&c(this.selection));var g=Sao.rpc({method:"model."+this.attributes.relation+".search_read",params:[d,0,null,null,["rec_name"],e]},a.model.session);g.done(function(a){var b=[];a.forEach(function(a){b.push([a.id,a.rec_name])}),this.nullable_widget&&b.push([null,""]),this._last_domain=d,this._domain_cache[f]=b,this.selection=jQuery.extend([],b),c&&c(this.selection)}.bind(this)),g.fail(function(){this._last_domain=null,this.selection=[],c&&c(this.selection)}.bind(this))}else{var h=this.attributes.selection_change_with||[],i=a._get_on_change_args(h);delete i.id,Sao.common.selection_mixin.init_selection.call(this,i,function(){Sao.common.selection_mixin.filter_selection.call(this,d,a,b),c&&c(this.selection)}.bind(this))}};this._selection_prm.done(d.bind(this))},Sao.common.selection_mixin.filter_selection=function(a,b,c){if(!jQuery.isEmptyObject(a)){var d=new Sao.common.DomainInversion;this.selection=this.selection.filter(function(b){var c={};return c[this.field_name]=b[0],d.eval_domain(a,c)}.bind(this))}},Sao.common.selection_mixin.get_inactive_selection=function(a){if(!this.attributes.relation)return jQuery.when([]);for(var b=0,c=this.inactive_selection.length;c>b;b++)if(a==this.inactive_selection[b][0])return jQuery.when(this.inactive_selection[b]);var d=Sao.rpc({method:"model."+this.attributes.relation+".read",params:[[a],["rec_name"],{}]},Sao.Session.current_session);return d.then(function(a){return this.inactive_selection.push([a[0].id,a[0].rec_name]),[a[0].id,a[0].rec_name]}.bind(this))},Sao.common.Button=Sao.class_(Object,{init:function(a){this.attributes=a,this.el=jQuery("<button/>",{"class":"btn btn-default",type:"button"}),this.icon=jQuery("<img/>",{"class":"icon","aria-hidden":!0}).appendTo(this.el),this.icon.hide(),this.el.append(a.string||""),this.set_icon(a.icon)},set_icon:function(a){if(!a)return this.icon.attr("src",""),void this.icon.hide();var b=Sao.common.ICONFACTORY.register_icon(a);b.done(function(a){this.icon.attr("src",a),this.icon.show()}.bind(this))},set_state:function(a){var b;if(a?(b=a.expr_eval(this.attributes.states||{}),(a.group.get_readonly()||a.readonly())&&(b.readonly=!0)):b={},b.invisible?this.el.hide():this.el.show(),this.el.prop("disabled",b.readonly),this.set_icon(b.icon||this.attributes.icon),(void 0===this.attributes.type||"class"===this.attributes.type)&&a)for(var c=a.group.parent;c;){
+if(c.has_changed()){this.el.prop("disabled",!1);break}c=c.group.parent}}}),Sao.common.udlex=Sao.class_(Object,{init:function(a){var b=Sao.class_(Object,{init:function(a){this.stream=a.split(""),this.i=0},read:function(a){if(void 0===a&&(a=1),this.i>=this.stream.length)return null;var b=this.stream.slice(this.i,this.i+a).join();return this.i+=a,b}});this.instream=new b(a),this.eof=null,this.commenters="",this.nowordchars=[":",">","<","=","!",'"',";","(",")"],this.whitespace=" 	\r\n",this.whitespace_split=!1,this.quotes='"',this.escape="\\",this.escapedquotes='"',this.state=" ",this.pushback=[],this.token=""},get_token:function(){if(this.pushback.length>0)return this.pushback.shift();var a=this.read_token();return a},read_token:function(){for(var a=!1,b=" ";;){var c=this.instream.read(1);if(null===this.state){this.token="";break}if(" "==this.state){if(!c){this.state=null;break}if(this.whitespace.contains(c)){if(this.token||a)break;continue}if(this.commenters.contains(c));else if(this.escape.contains(c))b="a",this.state=c;else if(~this.nowordchars.indexOf(c))if(this.quotes.contains(c))this.state=c;else{if(!this.whitespace_split){if(this.token=c,this.token||a)break;continue}this.token=c,this.state="a"}else this.token=c,this.state="a"}else if(this.quotes.contains(this.state)){if(a=!0,!c)throw"no closing quotation";c==this.state?this.state="a":this.escape.contains(c)&&this.escapedquotes.contains(this.state)?(b=this.state,this.state=c):this.token=this.token+c}else if(this.escape.contains(this.state)){if(!c)throw"no escaped character";this.quotes.contains(b)&&c!=this.state&&c!=b&&(this.token=this.token+this.state),this.token=this.token+c,this.state=b}else if("a"==this.state){if(!c){this.state=null;break}if(this.whitespace.contains(c)){if(this.state=" ",this.token||a)break;continue}if(this.commenters.contains(c));else if(this.quotes.contains(c))this.state=c;else if(this.escape.contains(c))b="a",this.state=c;else{if(~this.nowordchars.indexOf(c)&&!this.quotes.contains(c)&&!this.whitespace_split){if(this.pushback.unshift(c),this.state=" ",this.token)break;continue}this.token=this.token+c}}}var d=this.token;return this.token="",a||""!==d||(d=null),d},next:function(){var a=this.get_token();return a==this.eof?null:a}}),Sao.common.DomainParser=Sao.class_(Object,{OPERATORS:["!=","<=",">=","=","!","<",">"],init:function(a,b){this.fields={},this.strings={};for(var c in a){var d=a[c];(d.searchable||void 0===d.searchable)&&(this.fields[c]=d,this.strings[d.string.toLowerCase()]=d)}this.context=b},parse:function(a){try{for(var b=new Sao.common.udlex(a),c=[];;){var d=b.next();if(null===d)break;c.push(d)}return c=this.group_operator(c),c=this.parenthesize(c),c=this.group(c),c=this.operatorize(c,"or"),c=this.operatorize(c,"and"),c=this.parse_clause(c),this.simplify(c)}catch(e){if("no closing quotation"==e)return this.parse(a+'"');throw e}},stringable:function(a){var b=function(a){if(!a)return!0;var b=function(a){return a instanceof Array};return(~["AND","OR"].indexOf(a[0])||b(a[0]))&&a.slice(1).every(b)?this.stringable(a):a[0]in this.fields||"rec_name"==a[0]?!0:!1}.bind(this);return a?(~["AND","OR"].indexOf(a[0])&&(a=a.slice(1)),a.every(b)):!0},string:function(a){var b=function(a){if(jQuery.isEmptyObject(a))return"";if("string"!=typeof a[0]||~["AND","OR"].indexOf(a[0]))return"("+this.string(a)+")";var b,c=a[0],d=a[1],e=a[2];if(c.endsWith(".rec_name")&&(c=c.slice(0,-9)),!(c in this.fields))return b=e.replace("%%","__"),b.startsWith("%")&&b.endsWith("%")&&(e=e.slice(1,-1)),this.quote(e);var f=this.fields[c],g=null;a.length>3&&(g=a[3]),d.contains("ilike")&&(b=e.replace("%%","__"),b.startsWith("%")&&b.endsWith("%")?e=e.slice(1,-1):b.contains("%")||(d="ilike"==d?"=":"!",e=e.replace("%%","%")));var h=this.default_operator(f);(h==d.trim()||d.contains(h)&&(d.contains("not")||d.contains("!")))&&(d=d.replace(h,"").replace("not","!").trim()),d.endsWith("in")&&(d="not in"==d?"!":"");var i=this.format_value(f,e,g);return~this.OPERATORS.indexOf(d)&&~["char","text","selection"].indexOf(f.type)&&""===e&&(i='""'),this.quote(f.string)+": "+d+i};if(b=b.bind(this),jQuery.isEmptyObject(a))return"";var c=" ";return("AND"==a[0]||"OR"==a[0])&&("OR"==a[0]&&(c=" or "),a=a.slice(1)),a.map(b).join(c)},completion:function(a){var b,c,d=[],e=this.parse(a),f=0;for(b=a.length;b>0&&(")"!=a[b]&&" "!=a[b]);b--)")"==a[b]&&(f+=1);var g=this.ending_clause(e),h=g[0],i=g[1],j=i-f,k=this.string(e);j>0&&(k=k.substring(0,k.length-j)),k!=a&&d.push(k);var l,m,n=function(a,b){return b>0?a.substring(0,b):a};if(null!==h&&0===f){var o=this.complete(h);for(b=0,c=o.length;c>b;b++)l=o[b],m=this.string(this.replace_ending_clause(e,l)),d.push(n(m,j))}if(a.length>0){if(" "!=a.substr(a.length-1,1))return d;if(a.length>=2||":"==a.substr(a.length-2,1))return d}var p,q,r;for(var s in this.strings)p=this.strings[s],q=this.default_operator(p),r="",("ilike"==q||"not ilike"==q)&&(r=this.likify(r)),new_domain=this.append_ending_clause(e,[p.name,q,r],j),new_domain_string=this.string(new_domain),d.push(n(new_domain_string,j));return d},complete:function(a){var b,c,d,e,f=[];1==a.length?b=a[0]:3==a.length?(b=a[0],c=a[1],d=a[2]):(b=a[0],c=a[1],d=a[2],e=a[3],b.endsWith(".rec_name")&&(b=b.substring(0,b.length-9)));var g;"rec_name"==b&&("ilike"==c&&(g=d.replace(/%%/g,"__"),g.startsWith("%")||g.endsWith("%")?d=g.substring(1,g.length-1):~g.indexOf("%")&&(d=d.replace(/%%/g,"%")),c=null),b=d,d="");var h;if(!(b.toLowerCase()in this.strings||b in this.fields)){for(var i in this.strings)h=this.strings[i],h.string.toLowerCase().startsWith(b.toLowerCase())&&(c=this.default_operator(h),d="","ilike"==c&&(d=this.likify(d)),f.push([h.name,c,d]));return f}if(h=b in this.fields?this.fields[b]:this.strings[b.toLowerCase()],null===c)c=this.default_operator(h),d="",("ilike"==c||"not ilike"==c)&&(d=this.likify(d)),f.push([h.name,c,d]);else for(var j=this.complete_value(h,d),k=0,l=j.length;l>k;k++)f.push([h.name,c,j[k]]);return f},is_leaf:function(a){return a instanceof Array&&a.clause},ending_clause:function(a,b){if(void 0===b&&(b=0),0===a.length)return[null,b];var c=a[a.length-1];return this.is_leaf(c)?[c,b]:this.ending_clause(c,b+1)},replace_ending_clause:function(a,b){var c,d,e=[];for(c=0,d=a.length-1;d>c;c++)e.push(a[c]);return this.is_leaf(a[c])?e.push(b):e=e.concat(this.replace_ending_clause(a[c],b)),e},append_ending_clause:function(a,b,c){if(0===a.length)return[b];var d=a.slice(0,-1),e=a[a.length-1];return this.is_leaf(e)?(d.push(e),0===c&&d.push(b)):d.push(this.append_ending_clause(e,b,c-1)),d},complete_value:function(a,b){var c=function(){return b?[!0]:[!1]},d=function(){var c=[],d=null!==b?b:"";b instanceof Array&&(d=b[b.length-1]),d=d.replace(/^%*|%*$/g,"");var e,f,g,h;for(e=0,f=a.selection.length;f>e;e++)g=a.selection[e][0],h=a.selection[e][1].toLowerCase(),h.startsWith(d.toLowerCase())&&(b instanceof Array?c.push(b.slice(0,-1).concat([g])):c.push(g));return c},e=function(){var c=[],d=null!==b?b:"";b instanceof Array&&(d=b[b.length-1]),d=d.replace(/^%*|%*$/g,"");var e,f,g,h;for(e=0,f=a.selection.length;f>e;e++)g=a.selection[e][0],h=a.selection[e][1].toLowerCase(),h.startsWith(d.toLowerCase())&&(b instanceof Array?c.push(b.slice(0,-1).concat([g])):c.push(this.likify(g)));return c},f=function(){return[Sao.Date(),Sao.DateTime().utc()]},g=function(){return[Sao.Date()]},h=function(){return[Sao.Time()]},i={"boolean":c,selection:d,reference:e,datetime:f,date:g,time:h};return a.type in i?i[a.type]():[]},group_operator:function(a){var b=a[0],c=[];return a.slice(1).forEach(function(a){"="==a&&b&&~this.OPERATORS.indexOf(b+a)?(c.push(b+a),b=null):(null!==b&&c.push(b),b=a)}.bind(this)),null!==b&&c.push(b),c},parenthesize:function(a){var b=[],c=b,d=[];return a.forEach(function(a,b){void 0!==c&&("("==a?(d.push(c),c=c[c.push([])-1]):")"==a?c=d.pop():c.push(a))}),b},group:function(a){var b=[],c=function(a){var b=[],d=function(a){b.push([a])},e=a.indexOf(":");if(!~e)return a.forEach(d),b.forEach(function(a){a.clause=!0}),b;for(var f=function(a,c){return function(d){if(jQuery.isEmptyObject(a))b.push(d);else{var e;jQuery.isEmptyObject(c)?(e=a.concat(d),e.clause=!0,b.push(e)):(null!==d[0]&&c.push(d[0]),e=a.concat([c]),e.clause=!0,b.push(e)),a.splice(0,a.length)}}},g=0;e>g;g++){var h=a.slice(g,e).join(" ");if(h.toLowerCase()in this.strings){jQuery.isEmptyObject(a.slice(0,g))?d(null):a.slice(0,g).forEach(d),h=[h],e+1<a.length&&~this.OPERATORS.indexOf(a[e+1])?(h=h.concat([a[e+1]]),e+=1):h=h.concat([null]);for(var i=[];e+2<a.length&&";"==a[e+2];)i.push(a[e+1]),e+=2;if(c(a.slice(e+1)).forEach(f(h,i)),!jQuery.isEmptyObject(h)){var j;jQuery.isEmptyObject(i)?(j=h.concat([null]),j.clause=!0,b.push(j)):(j=h.concat([i]),j.clause=!0,b.push(j))}break}}return b};c=c.bind(this);var d=[];return a.forEach(function(a){this.is_generator(a)?(c(d).forEach(function(a){Sao.common.compare(a,[null])||b.push(a)}),d=[],b.push(this.group(a))):d.push(a)}.bind(this)),c(d).forEach(function(a){Sao.common.compare(a,[null])||b.push(a)}),b},is_generator:function(a){return a instanceof Array&&void 0===a.clause},operatorize:function(a,b){var c=[];b=b||"or",a=jQuery.extend([],a);for(var d=function(a){return a instanceof Array?Sao.common.compare(a,[b]):a==b},e=a.shift();d(e);)e=a.shift();if(void 0===e)return c;this.is_generator(e)&&(e=this.operatorize(e,b));for(var f=null;!jQuery.isEmptyObject(a);)if(f=a.shift(),this.is_generator(f)&&!d(f)&&(f=this.operatorize(f,b)),d(f)){for(f=a.shift();d(f);)f=a.shift();this.is_generator(f)&&(f=this.operatorize(f,b)),void 0!==f?e=[b.toUpperCase(),e,f]:d(e)||(c.push([b.toUpperCase(),e]),e=null),f=null}else d(e)||c.push(e),e=f;return jQuery.isEmptyObject(a)&&(null===f||d(f)?null===e||d(e)||c.push(e):c.push(f)),c},_clausify:function(a){return a.clause=!0,a},parse_clause:function(a){var b=[];return a.forEach(function(a){if(this.is_generator(a))b.concat(this.parse_clause(a));else if("OR"==a||"AND"==a)b.push(a);else if(1!=a.length||a[0]instanceof Array){if(3==a.length&&a[0].toLowerCase()in this.strings){var c=(a[0],a[1]),d=a[2],e=this.strings[a[0].toLowerCase()],f=null;if("reference"==e.type){var g=this.split_target_value(e,d);f=g[0],d=g[1]}if(null===c&&(c=this.default_operator(e)),d instanceof Array&&(c="!"==c?"not in":"in"),"!"==c&&(c=this.negate_operator(this.default_operator(e))),~["integer","float","numeric","datetime","date","time"].indexOf(e.type)&&d&&d.contains("..")){var h=d.split("..",2),i=this.convert_value(e,h[0]),j=this.convert_value(e,h[1]);return void b.push([this._clausify([e.name,">=",i]),this._clausify([e.name,"<=",j])])}d=d instanceof Array?d.map(function(a){return this.convert_value(e,a)}.bind(this)):this.convert_value(e,d),c.contains("like")&&(d=this.likify(d)),f?b.push(this._clausify([e.name+".rec_name",c,d,f])):b.push(this._clausify([e.name,c,d]))}}else b.push(this._clausify(["rec_name","ilike",this.likify(a[0])]))}.bind(this)),b},likify:function(a){if(!a)return"%";var b=a.replace("%%","__");return b.contains("%")?a:"%"+a+"%"},quote:function(a){if("string"!=typeof a)return a;a.contains("\\")&&(a=a.replace(new RegExp("\\\\","g"),"\\\\")),a.contains('"')&&(a=a.replace(new RegExp('"',"g"),'\\"'));for(var b=[":"," ","(",")"].concat(this.OPERATORS),c=0;c<b.length;c++){var d=b[c];if(a.contains(d))return'"'+a+'"'}return a},default_operator:function(a){return~["char","text","many2one","many2many","one2many","reference"].indexOf(a.type)?"ilike":"="},negate_operator:function(a){switch(a){case"ilike":return"not ilike";case"=":return"!=";case"in":return"not in"}},time_format:function(a){return new Sao.PYSON.Decoder({}).decode(a.format)},split_target_value:function(a,b){var c=null;if("string"==typeof b)for(var d=0;d<a.selection.length;d++){var e=a.selection[d],f=e[0],g=e[1];if(b.toLowerCase().startsWith(g.toLowerCase()+",")){c=f,b=b.slice(g.length+1);break}}return[c,b]},convert_value:function(a,b){var c=function(){if("string"==typeof b)for(var c=0;c<a.selection.length;c++){var d=a.selection[c],e=d[0],f=d[1];if(b.toLowerCase()==f.toLowerCase())return e}return b},d={"boolean":function(){return"string"==typeof b?[Sao.i18n.gettext("y"),Sao.i18n.gettext("yes"),Sao.i18n.gettext("true"),Sao.i18n.gettext("t"),"1"].some(function(a){return a.toLowerCase().startsWith(b.toLowerCase())}):Boolean(b)},"float":function(){var a=Number(b);return isNaN(a)||""===b||null===b?null:a},integer:function(){var a=parseInt(b,10);return isNaN(a)?null:a},numeric:function(){var a=new Sao.Decimal(b);return isNaN(a.valueOf())||""===b||null===b?null:a},selection:c,reference:c,datetime:function(){var c=Sao.common.parse_datetime(Sao.common.date_format(),this.time_format(a),b);return c||(c=Sao.common.parse_date(Sao.common.date_format(),b)),c}.bind(this),date:function(){return Sao.common.parse_date(Sao.common.date_format(),b)},time:function(){try{return Sao.common.parse_time(this.time_format(a),b)}catch(c){return null}}.bind(this),timedelta:function(){var c=null;return a.converter&&(c=this.context[a.converter]),Sao.common.timedelta.parse(b,c)}.bind(this),many2one:function(){return""===b?null:b}},e=d[a.type];return e?e():b},format_value:function(a,b,c){void 0===c&&(c=null);var d=function(){if(!b&&0!==b&&b!==new Sao.Decimal(0))return"";var a=String(b).split(".")[1];return a=a?a.length:0,b.toFixed(a)},e=function(){for(var c=0;c<a.selection.length;c++)if(a.selection[c][0]==b)return a.selection[c][1];return b||""},f=function(){if(!c)return e();for(var d=0;d<a.selection.length;d++)if(a.selection[d][0]==c){c=a.selection[d][1];break}return c+","+b},g={"boolean":function(){return b?Sao.i18n.gettext("True"):Sao.i18n.gettext("False")},integer:function(){return b||0===b?""+parseInt(b,10):""},"float":d,numeric:d,selection:e,reference:f,datetime:function(){return b?b.isDate||!(b.hour()||b.minute()||b.second())?Sao.common.format_date(Sao.common.date_format(),b):Sao.common.format_datetime(Sao.common.date_format(),this.time_format(a),b):""}.bind(this),date:function(){return Sao.common.format_date(Sao.common.date_format(),b)},time:function(){return b?Sao.common.format_time(this.time_format(a),b):""}.bind(this),timedelta:function(){if(!b||!b.valueOf())return"";var c=null;return a.converter&&(c=this.context[a.converter]),Sao.common.timedelta.format(b,c)}.bind(this),many2one:function(){return null===b?"":b}};if(b instanceof Array)return b.map(function(b){return this.format_value(a,b)}.bind(this)).join(";");var h=g[a.type];return h?this.quote(h(b)):null===b?"":this.quote(b)},simplify:function(a){return a instanceof Array&&!this.is_leaf(a)?1==a.length&&a[0]instanceof Array&&("AND"==a[0][0]||"OR"==a[0][0]||a[0][0]instanceof Array)?this.simplify(a[0]):2==a.length&&("AND"==a[0]||"OR"==a[0])&&a[1]instanceof Array?this.simplify(a[1]):(3==a.length&&("AND"==a[0]||"OR"==a[0])&&a[1]instanceof Array&&a[0]==a[1][0]&&(a=this.simplify(a[1]).concat([a[2]])),a.map(this.simplify.bind(this))):a}}),Sao.common.DomainInversion=Sao.class_(Object,{and:function(a,b){return a&&b},or:function(a,b){return a||b},OPERATORS:{"=":function(a,b){return a instanceof Array&&b instanceof Array?Sao.common.compare(a,b):a===b},">":function(a,b){return a>b},"<":function(a,b){return b>a},"<=":function(a,b){return b>=a},">=":function(a,b){return a>=b},"!=":function(a,b){return a instanceof Array&&b instanceof Array?!Sao.common.compare(a,b):a!==b},"in":function(a,b){return Sao.common.DomainInversion.in_(a,b)},"not in":function(a,b){return!Sao.common.DomainInversion.in_(a,b)},like:function(){return!0},ilike:function(){return!0},"not like":function(){return!0},"not ilike":function(){return!0},child_of:function(){return!0},"not child_of":function(){return!0}},locale_part:function(a,b,c){return void 0===c&&(c="id"),a===b?c:a.contains(".")?a.split(".").slice(1).join("."):a},is_leaf:function(a){return a instanceof Array&&a.length>2&&"string"==typeof a[1]},eval_leaf:function(a,b,c){void 0===c&&(c=this.and);var d=a[0],e=a[1],f=a[2];if(d.contains("."))return Boolean(b[d.split(".")[0]]);if("="==e&&(null===b[d]||void 0===b[d])&&c===this.and)return!0;var g=b[d];return g&&g._isAMomentObject&&!f&&(f=g.isDateTime?Sao.DateTime.min:Sao.Date.min),f&&f._isAMomentObject&&!g&&(g=f.isDateTime?Sao.DateTime.min:Sao.Date.min),"string"==typeof g&&f instanceof Array&&2==f.length?f=f.join(","):g instanceof Array&&"string"==typeof f&&2==g.length&&(g=g.join(",")),~["=","!="].indexOf(e)&&g instanceof Array&&"number"==typeof f&&(e={"=":"in","!=":"not in"}[e]),e in this.OPERATORS?this.OPERATORS[e](g,f):!0},inverse_leaf:function(a){return~["AND","OR"].indexOf(a)?a:this.is_leaf(a)?a[1].contains("child_of")?3==a.length?a:[a[3]].concat(a.slice(1)):a:a.map(this.inverse_leaf.bind(this))},filter_leaf:function(a,b,c){return~["AND","OR"].indexOf(a)?a:this.is_leaf(a)?a[0].startsWith(b)&&a.length>3&&a[3]!==c?["id","=",null]:a:a.map(function(a){return this.filter_leaf(a,b,c)}.bind(this))},eval_domain:function(a,b,c){return void 0===c&&(c=this.and),this.is_leaf(a)?this.eval_leaf(a,b,c):jQuery.isEmptyObject(a)&&c==this.and?!0:jQuery.isEmptyObject(a)&&c==this.or?!1:"AND"==a[0]?this.eval_domain(a.slice(1),b):"OR"==a[0]?this.eval_domain(a.slice(1),b,this.or):c(this.eval_domain(a[0],b),this.eval_domain(a.slice(1),b,c))},localize_domain:function(a,b,c){if(~["AND","OR",!0,!1].indexOf(a))return a;if(this.is_leaf(a)){if(a[1].contains("child_of"))return 3==a.length?a:[a[3]].concat(a.slice(1,-1));var d="id";"string"==typeof a[2]&&(d="rec_name");var e=c?3:4;return[this.locale_part(a[0],b,d)].concat(a.slice(1,e)).concat(a.slice(4))}return a.map(function(a){return this.localize_domain(a,b,c)}.bind(this))},simplify:function(a){return this.is_leaf(a)?a:~["OR","AND"].indexOf(a)?a:a instanceof Array&&1==a.length&&~["OR","AND"].indexOf(a[0])?[]:a instanceof Array&&1==a.length&&!this.is_leaf(a[0])?this.simplify(a[0]):a instanceof Array&&2==a.length&&~["AND","OR"].indexOf(a[0])?[this.simplify(a[1])]:a.map(this.simplify.bind(this))},merge:function(a,b){if(jQuery.isEmptyObject(a)||~["AND","OR"].indexOf(a))return[];var c="OR"==a[0]?"OR":"AND";return this.is_leaf(a)?[a]:void 0===b?[c].concat([].concat.apply([],a.map(function(a){return this.merge(a,c)}.bind(this)))):c==b?[].concat.apply([],a.map(function(a){return this.merge(a,c)}.bind(this))):[this.merge(a)]},concat:function(a,b){var c=[];return b&&c.push(b),a.forEach(function(a){jQuery.isEmptyObject(a)||c.push(a)}),this.simplify(this.merge(c))},unique_value:function(a){return a instanceof Array&&1==a.length&&!a[0][0].contains(".")&&"="==a[0][1]?[!0,a[0][1],a[0][2]]:[!1,null,null]},parse:function(a){var b=Sao.common.DomainInversion.And,c=Sao.common.DomainInversion.Or;if(this.is_leaf(a))return a;if(jQuery.isEmptyObject(a))return new b([]);if("OR"===a[0])return new c(a.slice(1));var d=0;return"AND"===a[0]&&(d=1),new b(a.slice(d))},domain_inversion:function(a,b,c){void 0===c&&(c={});var d=this.parse(a);return~d.variables.indexOf(b)?d.inverse(b,c):!0}}),Sao.common.DomainInversion.in_=function(a,b){if(a instanceof Array){if(b instanceof Array){for(var c=0,d=a.length;d>c;c++)if(~b.indexOf(a[c]))return!0;return!1}return Boolean(~a.indexOf(b))}return Boolean(~b.indexOf(a))},Sao.common.DomainInversion.And=Sao.class_(Object,{init:function(a){this.domain_inversion=new Sao.common.DomainInversion,this.branches=a.map(this.domain_inversion.parse.bind(this.domain_inversion)),this.variables=[];for(var b=0,c=this.branches.length;c>b;b++){var d=this.branches[b];this.domain_inversion.is_leaf(d)?this.variables.push(this.base(d[0])):d instanceof Sao.common.DomainInversion.And&&(this.variables=this.variables.concat(d.variables))}},base:function(a){return a.contains(".")?a.split(".")[0]:a},inverse:function(a,b){for(var c=Sao.common.DomainInversion,d=[],e=0,f=this.branches.length;f>e;e++){var g=this.branches[e];if(g instanceof c.And){var h=g.inverse(a,b),i="boolean"==typeof h;if(i){if(h)continue;return!1}d.push(h)}else if(this.domain_inversion.is_leaf(g)&&this.base(g[0])===a)d.push(g);else{var j=g[0];if(j in b&&!(j in b&&this.domain_inversion.eval_leaf(g,b,this.domain_inversion.and)))return!1;d.push(!0)}}return d=d.filter(function(a){return a!==!0}),jQuery.isEmptyObject(d)?!0:this.domain_inversion.simplify(d)}}),Sao.common.DomainInversion.Or=Sao.class_(Sao.common.DomainInversion.And,{inverse:function(a,b){var c=Sao.common.DomainInversion,d=[];if(!~this.variables.indexOf(a)&&!jQuery.isEmptyObject(this.variables.filter(function(a){return!(a in b)})))return!0;for(var e=0,f=this.branches.length;f>e;e++){var g=this.branches[e];if(g instanceof c.And){var h=g.inverse(a,b),i="boolean"==typeof h;if(!~this.variables.indexOf(a)){if(i&&h)return!0;continue}if(i){if(h)return!0;continue}d.push(h)}else if(this.domain_inversion.is_leaf(g)&&this.base(g[0])==a)d.push(g);else{var j=g[0];if(j=this.base(j),j in b&&this.domain_inversion.eval_leaf(g,b,this.domain_inversion.or))return!0;j in b&&!this.domain_inversion.eval_leaf(g,b,this.domain_inversion.or)&&d.push(!1)}}return d=d.filter(function(a){return a!==!1}),jQuery.isEmptyObject(d)?!1:this.domain_inversion.simplify(["OR"].concat(d))}}),Sao.common.guess_mimetype=function(a){return/.*odt$/.test(a)?"application/vnd.oasis.opendocument.text":/.*ods$/.test(a)?"application/vnd.oasis.opendocument.spreadsheet":/.*pdf$/.test(a)?"application/pdf":/.*docx$/.test(a)?"application/vnd.openxmlformats-officedocument.wordprocessingml.document":/.*doc/.test(a)?"application/msword":/.*xlsx$/.test(a)?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":/.*xls/.test(a)?"application/vnd.ms-excel":"application/octet-binary"},Sao.common.LOCAL_ICONS=["tryton-attachment-hi","tryton-attachment","tryton-bookmark","tryton-cancel","tryton-clear","tryton-close","tryton-connect","tryton-copy","tryton-delete","tryton-dialog-error","tryton-dialog-information","tryton-dialog-warning","tryton-disconnect","tryton-executable","tryton-find-replace","tryton-find","tryton-folder-new","tryton-fullscreen","tryton-go-home","tryton-go-jump","tryton-go-next","tryton-go-previous","tryton-help","tryton-icon","tryton-list-add","tryton-list-remove","tryton-locale","tryton-lock","tryton-log-out","tryton-mail-message-new","tryton-mail-message","tryton-new","tryton-ok","tryton-open","tryton-preferences-system-session","tryton-preferences-system","tryton-preferences","tryton-print-email","tryton-print-open","tryton-print","tryton-refresh","tryton-save-as","tryton-save","tryton-star","tryton-start-here","tryton-system-file-manager","tryton-system","tryton-text-background","tryton-text-foreground","tryton-text-markup","tryton-undo","tryton-unstar","tryton-web-browser"],Sao.common.IconFactory=Sao.class_(Object,{batchnum:10,name2id:{},loaded_icons:{},tryton_icons:[],register_prm:jQuery.when(),load_icons:function(a){if(a=a||!1,!a)for(var b in this.load_icons)this.load_icons.hasOwnProperty(b)&&window.URL.revokeObjectURL(this.load_icons[b]);var c=new Sao.Model("ir.ui.icon");return c.execute("list_icons",[],{}).then(function(b){a||(this.name2id={},this.loaded_icons={}),this.tryton_icons=[];for(var c,d,e=0,f=b.length;f>e;e++)c=b[e][0],d=b[e][1],a&&d in this.loaded_icons||(this.tryton_icons.push([c,d]),this.name2id[d]=c)}.bind(this))},register_icon:function(a){if(!a)return jQuery.when("");if(a in this.loaded_icons||~Sao.common.LOCAL_ICONS.indexOf(a))return jQuery.when(this.get_icon_url(a));if("pending"==this.register_prm.state()){var b=jQuery.Deferred();return this.register_prm.then(function(){this.register_icon(a).then(b.resolve,b.reject)}.bind(this)),b}var c;c=a in this.name2id?jQuery.when():this.load_icons(!0);var d=new Sao.Model("ir.ui.icon");return this.register_prm=c.then(function(){var b=function(a){var b,c;for(b=0,c=this.tryton_icons.length;c>b;b++){var d=this.tryton_icons[b];if(Sao.common.compare(d,a))break}return b}.bind(this),c=b([this.name2id[a],a]),e=Math.round(c-this.batchnum/2);e=0>e?0:e;var f=Math.round(c+this.batchnum/2),g=[];this.tryton_icons.slice(e,f).forEach(function(a){g.push(a[0])});var h=d.execute("read",[g,["name","icon"]],{});return h.then(function(c){return c.forEach(function(a){var c;if(navigator.userAgent.match(/firefox/i)){c="data:image/svg+xml;base64,"+window.btoa(unescape(encodeURIComponent(a.icon)));var d=new Image;d.src=c,d.onload=function(){var b=document.createElement("canvas");b.width=d.width,b.height=d.height;var e=b.getContext("2d");e.drawImage(d,0,0),b.toBlob(function(d){var e=c;c=window.URL.createObjectURL(d),this.loaded_icons[a.name]=c,jQuery(document).find("img").each(function(a,b){b.src==e&&(b.src=c)}),b.remove()}.bind(this),"image/png")}.bind(this)}else{var e=new Blob([a.icon],{type:"image/svg+xml"});c=window.URL.createObjectURL(e)}this.loaded_icons[a.name]=c,delete this.name2id[a.name],this.tryton_icons.splice(b([a.id,a.name]),1)}.bind(this)),this.get_icon_url(a)}.bind(this))}.bind(this)),this.register_prm},get_icon_url:function(a){return a in this.loaded_icons?this.loaded_icons[a]:"images/"+a+".svg"}}),Sao.common.ICONFACTORY=new Sao.common.IconFactory,Sao.common.UniqueDialog=Sao.class_(Object,{init:function(){this.running=!1},build_dialog:function(){var a=new Sao.Dialog("",this.class_);return a},run:function(){if(!this.running){var a=Array.prototype.slice.call(arguments),b=jQuery.Deferred();a.push(b);var c=this.build_dialog.apply(this,a);return this.running=!0,c.modal.modal("show"),b}},close:function(a){a.modal.on("hidden.bs.modal",function(a){jQuery(this).remove()}),a.modal.modal("hide"),this.running=!1}}),Sao.common.MessageDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"message-dialog",build_dialog:function(a,b,c){var d=Sao.common.MessageDialog._super.build_dialog.call(this);return d.header.remove(),d.body.append(jQuery("<div/>",{"class":"alert alert-info",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon "+b,"aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Message: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve("ok")}.bind(this)).appendTo(d.footer),d},run:function(a,b){return Sao.common.MessageDialog._super.run.call(this,a,b||"glyphicon-info-sign")}}),Sao.common.message=new Sao.common.MessageDialog,Sao.common.WarningDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"warning-dialog",build_dialog:function(a,b,c){var d=Sao.common.WarningDialog._super.build_dialog.call(this);return d.add_title(b),d.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-alert","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Warning: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve("ok")}.bind(this)).appendTo(d.footer),d}}),Sao.common.warning=new Sao.common.WarningDialog,Sao.common.UserWarningDialog=Sao.class_(Sao.common.WarningDialog,{class_:"user-warning-dialog",always:!1,_set_always:function(){this.always=jQuery(this).prop("checked")},build_dialog:function(a,b,c){var d=Sao.common.UserWarningDialog._super.build_dialog.call(this,a,b,c);return d.body.append(jQuery("<div/>").append(jQuery("<input/>",{type:"checkbox"}).change(this._set_always.bind(this))).append(jQuery("<span/>").text(Sao.i18n.gettext("Always ignore this warning.")))),d.body.append(jQuery("<p/>").text(Sao.i18n.gettext("Do you want to proceed?"))),d.footer.children().remove(),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("No")).click(function(){this.close(d),c.reject()}.bind(this)).appendTo(d.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Yes")).click(function(){this.close(d),this.always&&c.resolve("always"),c.resolve("ok")}.bind(this)).appendTo(d.footer),d}}),Sao.common.userwarning=new Sao.common.UserWarningDialog,Sao.common.ConfirmationDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"confirmation-dialog",build_dialog:function(a){var b=Sao.common.ConfirmationDialog._super.build_dialog.call(this);return b.header.remove(),b.body.append(jQuery("<div/>",{"class":"alert alert-info",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-info-sign","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Confirmation: "))).append(jQuery("<span/>").append(a).css("white-space","pre-wrap"))),b}}),Sao.common.SurDialog=Sao.class_(Sao.common.ConfirmationDialog,{build_dialog:function(a,b){var c=Sao.common.SurDialog._super.build_dialog.call(this,a);return jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(c),b.reject()}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(c),b.resolve()}.bind(this)).appendTo(c.footer),c}}),Sao.common.sur=new Sao.common.SurDialog,Sao.common.Sur3BDialog=Sao.class_(Sao.common.ConfirmationDialog,{build_dialog:function(a,b){var c=Sao.common.SurDialog._super.build_dialog.call(this,a);return jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(c),b.resolve("cancel")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("No")).click(function(){this.close(c),b.resolve("ko")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Yes")).click(function(){this.close(c),b.resolve("ok")}.bind(this)).appendTo(c.footer),c}}),Sao.common.sur_3b=new Sao.common.Sur3BDialog,Sao.common.AskDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"ask-dialog",run:function(){var a=Array.prototype.slice.call(arguments);return 1==a.length&&a.push(!0),Sao.common.AskDialog._super.run.apply(this,a)},build_dialog:function(a,b,c){var d=Sao.common.AskDialog._super.build_dialog.call(this);d.header.remove();var e=jQuery("<input/>",{"class":"form-control",type:b?"input":"password",id:"ask-dialog-entry"}),f=d.find(".modal-body");return f.append(jQuery("<div/>",{"class":"form-group"}).append(jQuery("<label/>",{"for":"ask-dialog-entry"}).append(a)).append(e)),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(d),c.reject()}.bind(this)).appendTo(d.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("OK")).click(function(){this.close(d),c.resolve(e.val())}.bind(this)).appendTo(d.footer),d}}),Sao.common.ask=new Sao.common.AskDialog,Sao.common.ConcurrencyDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"ask-dialog",build_dialog:function(a,b,c,d){var e=Sao.common.ConcurrencyDialog._super.build_dialog.call(this);return e.modal.find(".modal-dialog").removeClass("modal-sm").addClass("modal-lg"),e.add_title(Sao.i18n.gettext("Concurrency Exception")),e.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<p/>").append(jQuery("<span/>",{"class":"glyphicon glyphicon-info-sign","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Write Concurrency Warning: "))).append(Sao.i18n.gettext("This record has been modified while you were editing it."))).append(jQuery("<p/>").text(Sao.i18n.gettext("Choose:"))).append(jQuery("<ul/>").append(jQuery("<li/>").text(Sao.i18n.gettext('"Cancel" to cancel saving;'))).append(jQuery("<li/>").text(Sao.i18n.gettext('"Compare" to see the modified version;'))).append(jQuery("<li/>").text(Sao.i18n.gettext('"Write Anyway" to save your current version.'))))),jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.close(e),d.reject()}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Compare")).click(function(){this.close(e),Sao.Tab.create({model:a,res_id:b,domain:[["id","=",b]],context:c,mode:["form","tree"]}),d.reject();
+}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Write Anyway")).click(function(){this.close(e),d.resolve()}.bind(this)).appendTo(e.footer),e}}),Sao.common.concurrency=new Sao.common.ConcurrencyDialog,Sao.common.ErrorDialog=Sao.class_(Sao.common.UniqueDialog,{class_:"error-dialog",build_dialog:function(a,b,c){var d=Sao.common.ConcurrencyDialog._super.build_dialog.call(this);return d.modal.find(".modal-dialog").removeClass("modal-sm").addClass("modal-lg"),d.add_title(Sao.i18n.gettext("Application Error")),d.body.append(jQuery("<div/>",{"class":"alert alert-warning",role:"alert"}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-alert","aria-hidden":!0})).append(jQuery("<span/>",{"class":"sr-only"}).append(Sao.i18n.gettext("Warning: "))).append(jQuery("<p/>").append(jQuery("<pre/>").text(b))).append(jQuery("<p/>").append(jQuery("<a/>",{"class":"btn btn-link",href:Sao.config.roundup.url,target:"_blank"}).text(Sao.i18n.gettext("Report Bug"))))),jQuery("<button/>",{"class":"btn btn-primary",type:"button"}).append(Sao.i18n.gettext("Close")).click(function(){this.close(d),c.resolve()}.bind(this)).appendTo(d.footer),d}}),Sao.common.error=new Sao.common.ErrorDialog,Sao.common.Processing=Sao.class_(Object,{queries:0,timeout:500,init:function(){this.el=jQuery("<div/>",{id:"processing","class":"text-center"}),this.el.append(jQuery("<span/>",{"class":"label label-info",text:"Processing..."})),this.el.hide(),jQuery(function(){this.el.appendTo("body")}.bind(this))},show:function(){return window.setTimeout(function(){this.queries+=1,this.el.show()}.bind(this),this.timeout)},hide:function(a){window.clearTimeout(a),this.queries>0&&(this.queries-=1),this.queries<=0&&(this.queries=0,this.el.hide())}}),Sao.common.processing=new Sao.common.Processing,Sao.common.InputCompletion=Sao.class_(Object,{init:function(a,b,c,d){a.is("input")?(a.wrap('<div class="dropdown"/>'),this.dropdown=a.parent()):(a.addClass("dropdown"),this.dropdown=a),this.input=a.find("input").add(a.filter("input")).first(),this.input.attr("autocomplete","off"),jQuery("<span/>",{"data-toggle":"dropdown"}).appendTo(this.dropdown),this.menu=jQuery("<ul/>",{"class":"dropdown-menu",role:"listbox"}).appendTo(this.dropdown),this.separator=jQuery("<li/>",{role:"separator","class":"divider"}).appendTo(this.menu),this.separator.hide(),this.source=b,this.match_selected=c,this.format=d,this.action_activated=null,this._search_text=null,this.input.on("input",function(){window.setTimeout(this._input.bind(this),300,this.input.val())}.bind(this)),this.input.keydown(function(a){a.which==Sao.common.ESC_KEYCODE?this.dropdown.hasClass("open")&&this.menu.dropdown("toggle"):a.which==Sao.common.RETURN_KEYCODE&&(this.dropdown.hasClass("open")||this.menu.dropdown("toggle"))}.bind(this)),this.dropdown.on("hide.bs.dropdown",function(){this.input.focus(),this.input.closest(".treeview").css("overflow","")}.bind(this)),this.dropdown.on("show.bs.dropdown",function(){this.input.closest(".treeview").css("overflow","visible")}.bind(this))},set_actions:function(a,b){return void 0!==b&&(this.action_activated=b),this.menu.find("li.action").remove(),jQuery.isEmptyObject(a)?void this.separator.hide():(this.separator.show(),void a.forEach(function(a){var b=a[0],c=a[1];jQuery("<li/>",{"class":"action action-"+b}).append(jQuery("<a/>",{href:"#"}).append(this._format_action(c))).click(function(){this.action_activated&&this.action_activated(b),this.input.val("")}.bind(this)).appendTo(this.menu)},this))},_format:function(a){return this.format?this.format(a):a},_format_action:function(a){return this.format_action?this.format_action(a):a},_input:function(a){if(a==this.input.val()){var b;b=this.source instanceof Array?jQuery.when(source.filter(function(b){return b.toLowerCase().startsWith(a.toLowerCase())})):this.source(a),b.then(function(b){a==this.input.val()&&this._set_selection(b)}.bind(this))}},_set_selection:function(a){void 0===a&&(a=[]),this.menu.find("li.completion").remove(),a.reverse().forEach(function(a){jQuery("<li/>",{"class":"completion"}).append(jQuery("<a/>",{href:"#"}).append(this._format(a))).click(function(){this.match_selected&&this.match_selected(a),this.input.focus()}.bind(this)).prependTo(this.menu)},this),this.input.val()?this.dropdown.hasClass("open")||this.menu.dropdown("toggle"):this.dropdown.hasClass("open")&&this.menu.dropdown("toggle")}}),Sao.common.get_completion=function(a,b,c,d){var e=function(a){return a.rec_name},f=new Sao.common.InputCompletion(a,b,c,e);f.set_actions([["search",Sao.i18n.gettext("Search...")],["create",Sao.i18n.gettext("Create..")]],d)},Sao.common.update_completion=function(a,b,c,d,e){var f=a.val();if(!f||!d)return jQuery.when();void 0===e&&(e=c.get_domain(b));var g=c.get_context(b);e=[["rec_name","ilike","%"+f+"%"],e];var h=new Sao.Model(d);return h.execute("search_read",[e,0,Sao.config.limit,null,["rec_name"]],g)},Sao.common.Paned=Sao.class_(Object,{init:function(a){var b;this._orientation=a,this.el=jQuery("<div/>"),"horizontal"==a?(b=jQuery("<div/>",{"class":"row"}).appendTo(this.el),this.child1=jQuery("<div/>",{"class":"col-md-6"}).appendTo(b),this.child2=jQuery("<div/>",{"class":"col-md-6"}).appendTo(b)):"vertical"==a&&(this.child1=jQuery("<div/>",{"class":"row"}).appendTo(this.el),this.child2=jQuery("<div/>",{"class":"row"}).appendTo(this.el))},get_child1:function(){return this.child1},get_child2:function(){return this.child2}}),Sao.common.get_focus_chain=function(a){var b=a.find("input","textarea");return b.sort(function(a,b){if("tabindex"in a.attributes&&"tabindex"in b.attributes){var c=parseInt(a.attributes.tabindex.value),d=parseInt(b.attributes.tabindex.value);return c-d}return"tabindex"in a.attributes?-1:"tabindex"in b.attributes?1:0}),b},Sao.common.find_focusable_child=function(a){var b,c,d,e;if(!a.is(":visible"))return null;if(~["input","select","textarea"].indexOf(a[0].tagName.toLowerCase()))return a;for(d=Sao.common.get_focus_chain(a),b=0,c=d.length;c>b;b++)if(e=Sao.common.find_focusable_child(jQuery(d[b])))return e},Sao.common.find_first_focus_widget=function(a,b){var c,d,e,f,g;if(1==b.length)return jQuery(b[0]);for(e=Sao.common.get_focus_chain(a),c=0;c<e.length;c++){for(f=[],d=0;d<b.length;d++)g=jQuery(b[d]).closest(e[c]).length>0,g&&f.push(b[d]);if(f.length>0)return Sao.common.find_first_focus_widget(jQuery(e[c]),f)}}}(),function(){"use strict";Sao.Window={},Sao.Window.InfoBar=Sao.class_(Object,{init:function(){this.text=jQuery("<span/>"),this.text.css("white-space","pre-wrap"),this.el=jQuery("<div/>",{"class":"alert",role:"alert"}).append(jQuery("<button/>",{type:"button","class":"close","aria-label":Sao.i18n.gettext("Close")}).append(jQuery("<span/>",{"aria-hidden":!0}).append("×")).click(function(){this.el.hide()}.bind(this))).append(this.text),this.el.hide()},message:function(a,b){a?(this.el.removeClass("alert-success alert-info alert-warning alert-danger"),this.el.addClass("alert-"+(b||"info")),this.text.text(a),this.el.show()):this.el.hide()}}),Sao.Window.Form=Sao.class_(Object,{init:function(a,b,c){c=c||{},this.screen=a,this.callback=b,this.many=c.many||0,this.domain=c.domain||null,this.context=c.context||null,this.save_current=c.save_current,this.prev_view=a.current_view,this.screen.screen_container.alternate_view=!0,this.info_bar=new Sao.Window.InfoBar;for(var d=c.view_type||"form",e=jQuery.when(),f=[],g=0,h=this.screen.views.length;h>g;g++)f.push(this.screen.views[g].view_type);~f.indexOf(d)||~this.screen.view_to_load.indexOf(d)||(e=this.screen.add_view_id(null,d));var i=e.then(function(){return this.screen.switch_view(d).done(function(){c.new_&&this.screen.new_()}.bind(this))}.bind(this)),j=new Sao.Dialog("","","lg");this.el=j.modal,j.body.append(this.info_bar.el);var k=this.screen.attributes.readonly||this.screen.group.get_readonly();if("form"==d&&j.footer.append(jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(!c.new_&&this.screen.current_record.id<0?Sao.i18n.gettext("Delete"):Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this))),c.new_&&this.many&&j.footer.append(jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("New")).click(function(){this.response("RESPONSE_ACCEPT")}.bind(this))),this.save_current?j.footer.append(jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("Save"))):j.footer.append(jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK"))),j.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),"tree"==d){var l=jQuery("<div/>").appendTo(j.body),m=jQuery("<div/>",{"class":"input-group input-group-sm"}).appendTo(l);this.wid_text=jQuery("<input/>",{type:"input"}).appendTo(l),this.wid_text.hide();var n=jQuery("<div/>",{"class":"input-group-btn"}).appendTo(m),o=Sao.common.MODELACCESS.get(this.screen.model_name);this.domain&&(this.wid_text.show(),this.but_add=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Add")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-plus"})).appendTo(n),this.but_add.click(this.add.bind(this)),this.but_add.prop("disabled",!o.read||k),this.but_remove=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Remove")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-minus"})).appendTo(n),this.but_remove.click(this.remove.bind(this)),this.but_remove.prop("disabled",!o.read||k)),this.but_new=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("New")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-edit"})).appendTo(n),this.but_new.click(this.new_.bind(this)),this.but_new.prop("disabled",!o.create||k),this.but_del=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Delete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-trash"})).appendTo(n),this.but_del.click(this.delete_.bind(this)),this.but_del.prop("disabled",!o["delete"]||k),this.but_undel=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Undelete")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-repeat"})).appendTo(n),this.but_undel.click(this.undelete.bind(this)),this.but_undel.prop("disabled",!o["delete"]||k),this.but_previous=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Previous")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-left"})).appendTo(n),this.but_previous.click(this.previous.bind(this)),this.label=jQuery("<span/>",{"class":"btn"}).appendTo(n),this.label.text("(0, 0)"),this.but_next=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Next")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-chevron-right"})).appendTo(n),this.but_next.click(this.next.bind(this)),this.but_switch=jQuery("<button/>",{"class":"btn btn-default btn-sm",type:"button","aria-label":Sao.i18n.gettext("Switch")}).append(jQuery("<span/>",{"class":"glyphicon glyphicon-list-alt"})).appendTo(n),this.but_switch.click(this.switch_.bind(this))}i.done(function(){j.add_title(this.screen.current_view.attributes.string),j.body.append(this.screen.screen_container.alternate_viewport),this.el.modal("show")}.bind(this)),this.el.on("shown.bs.modal",function(a){this.screen.display().done(function(){this.screen.set_cursor()}.bind(this))}.bind(this)),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},add:function(){var a=jQuery.extend([],this.domain),b=this.screen.model_name,c=this.wid_text.val(),d=function(a){var b=jQuery.when();if(!jQuery.isEmptyObject(a)){for(var c=[],d=0,e=a.length;e>d;d++)c.push(a[d][0]);this.screen.group.load(c,!0),b=this.screen.display()}b.done(function(){this.screen.set_cursor()}.bind(this)),this.entry.val("")}.bind(this),e=new Sao.common.DomainParser;new Sao.Window.Search(b,d,{sel_multi:!0,context:this.context,domain:a,search_filter:e.quote(c)})},remove:function(){this.screen.remove(!1,!0,!1)},new_:function(){this.screen.new_()},delete_:function(){this.screen.remove(!1,!1,!1)},undelete:function(){this.screen.unremove()},previous:function(){this.screen.display_previous()},next:function(){this.screen.display_next()},switch_:function(){this.screen.switch_view()},response:function(a){var b;this.screen.current_view.set_value();var c=this.screen.group.get_readonly();if(~["RESPONSE_OK","RESPONSE_ACCEPT"].indexOf(a)&&!c&&this.screen.current_record)return void this.screen.current_record.validate().then(function(a){return a&&this.screen.attributes.pre_validate?this.screen.current_record.pre_validate():a}.bind(this)).then(function(c){var d=jQuery.Deferred();if(c&&this.save_current)this.screen.save_current().then(d.resolve,d.reject);else if(c&&"form"==this.screen.current_view.view_type){var e=this.screen.current_view,f=[];for(var g in e.widgets){var h=e.widgets[g];if(h.screen&&h.screen.attributes.pre_validate){var i=h.screen.current_record;i&&f.push(i.pre_validate())}}jQuery.when.apply(jQuery,f).then(d.resolve,d.reject)}else c?(this.info_bar.message(),d.resolve()):(this.info_bar.message(this.screen.invalid_message(),"danger"),d.reject());d.fail(function(){this.screen.display().done(function(){this.screen.set_cursor()}.bind(this))}.bind(this)),d.done(function(){"RESPONSE_ACCEPT"==a?(this.screen.new_(),this.screen.current_view.display().done(function(){this.screen.set_cursor()}.bind(this)),this.many-=1,0===this.many&&this.but_new.prop("disabled",!0)):(b=!0,this.callback(b),this.destroy())}.bind(this))}.bind(this));if("RESPONSE_CANCEL"==a&&!c&&this.screen.current_record){if(b=!1,this.screen.current_record.id<0||this.save_current)this.screen.group.remove(this.screen.current_record,!0);else if(this.screen.current_record.has_changed())return this.screen.current_record.cancel(),void this.screen.current_record.reload().always(function(){this.callback(b),this.destroy()}.bind(this))}else b="RESPONSE_CANCEL"!=a;this.callback(b),this.destroy()},destroy:function(){this.screen.screen_container.alternate_view=!1,this.screen.screen_container.alternate_viewport.children().detach(),this.prev_view&&this.screen.switch_view(this.prev_view.view_type),this.el.modal("hide")}}),Sao.Window.Attachment=Sao.class_(Sao.Window.Form,{init:function(a,b){this.resource=a.model.name+","+a.id,this.attachment_callback=b;var c=jQuery.extend({},a.get_context());c.resource=this.resource;var d=new Sao.Screen("ir.attachment",{domain:[["resource","=",this.resource]],mode:["tree","form"],context:c,exclude_field:"resource"});d.switch_view().done(function(){d.search_filter(),d.group.parent=a}),Sao.Window.Attachment._super.init.call(this,d,this.callback,{view_type:"tree"})},callback:function(a){a&&this.screen.group.save(),this.attachment_callback&&this.attachment_callback()}}),Sao.Window.Search=Sao.class_(Object,{init:function(a,b,c){c=c||{};var d=c.views_preload||{};this.model_name=a,this.domain=c.domain||[],this.context=c.context||{},this.sel_multi=c.sel_multi,this.callback=b;var e=new Sao.Dialog("Search","","lg");this.el=e.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("Find")).click(function(){this.response("RESPONSE_APPLY")}.bind(this)).appendTo(e.footer),c.new_&&Sao.common.MODELACCESS.get(a).create&&jQuery("<button/>",{"class":"btn btn-default",type:"button"}).append(Sao.i18n.gettext("New")).click(function(){this.response("RESPONSE_ACCEPT")}.bind(this)).appendTo(e.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(e.footer),e.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),this.screen=new Sao.Screen(a,{mode:["tree"],context:this.context,domain:this.domain,view_ids:c.view_ids,views_preload:d,row_activate:this.activate.bind(this)}),this.screen.load_next_view().done(function(){this.screen.switch_view().done(function(){e.body.append(this.screen.screen_container.el),this.el.modal("show"),this.screen.display(),void 0!==c.search_filter&&this.screen.search_filter(c.search_filter)}.bind(this))}.bind(this)),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},activate:function(){this.response("RESPONSE_OK")},response:function(a){var b,c=[];if("RESPONSE_OK"==a)b=this.screen.current_view.selected_records();else{if("RESPONSE_APPLY"==a)return void this.screen.search_filter();if("RESPONSE_ACCEPT"==a){var d=new Sao.Screen(this.model_name,{domain:this.domain,context:this.context,mode:["form"]}),e=function(a){a?d.save_current().then(function(){var a=d.current_record;this.callback([[a.id,a._values.rec_name||""]])}.bind(this),function(){this.callback(null)}.bind(this)):this.callback(null)};return this.el.modal("hide"),void new Sao.Window.Form(d,e.bind(this),{new_:!0})}}if(b){var f,g;for(f in b)g=b[f],c.push([g.id,g._values.rec_name||""])}this.callback(c),this.el.modal("hide")}}),Sao.Window.Preferences=Sao.class_(Object,{init:function(a){this.callback=a;var b=new Sao.Dialog("Preferences","","lg");this.el=b.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(b.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(b.footer),b.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this)),this.screen=new Sao.Screen("res.user",{mode:[]}),this.screen.attributes.readonly=!1,this.screen.group.set_readonly(!1),this.screen.group.skip_model_access=!0;var c=function(a){this.screen.add_view(a),this.screen.switch_view().done(function(){this.screen.new_(!1),this.screen.model.execute("get_preferences",[!1],{}).then(d.bind(this),this.destroy)}.bind(this))},d=function(a){this.screen.current_record.set(a),this.screen.current_record.id=this.screen.model.session.user_id,this.screen.current_record.validate(null,!0).then(function(){this.screen.display(!0)}.bind(this)),b.body.append(this.screen.screen_container.el),this.el.modal("show")};this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()}),this.screen.model.execute("get_preferences_fields_view",[],{}).then(c.bind(this),this.destroy)},response:function(a){var b=function(){this.destroy(),this.callback()}.bind(this),c=jQuery.when();"RESPONSE_OK"==a&&(c=this.screen.current_record.validate().then(function(a){if(a){var b=jQuery.extend({},this.screen.get()),c=function(a){return this.screen.model.execute("set_preferences",[b,a],{})}.bind(this);return"password"in b?Sao.common.ask.run("Current Password:",!1).then(function(a){return c(a)}):c(!1)}}.bind(this))),c.done(b)},destroy:function(){this.el.modal("hide")}}),Sao.Window.Revision=Sao.class_(Object,{init:function(a,b){this.callback=b;var c=new Sao.Dialog(Sao.i18n.gettext("Revision"),"","lg");this.el=c.modal,jQuery("<button/>",{"class":"btn btn-link",type:"button"}).append(Sao.i18n.gettext("Cancel")).click(function(){this.response("RESPONSE_CANCEL")}.bind(this)).appendTo(c.footer),jQuery("<button/>",{"class":"btn btn-primary",type:"submit"}).append(Sao.i18n.gettext("OK")).appendTo(c.footer),c.content.submit(function(a){this.response("RESPONSE_OK"),a.preventDefault()}.bind(this));var d=jQuery("<div/>",{"class":"form-group"}).appendTo(c.body);jQuery("<label/>",{"for":"revision",text:"Revision"}).appendTo(d),this.select=jQuery("<select/>",{"class":"form-control",id:"revision",placeholder:Sao.i18n.gettext("Revision")}).appendTo(d);var e=Sao.common.date_format(),f="%H:%M:%S.%f";this.select.append(jQuery("<option/>",{value:null,text:""})),a.forEach(function(a){var b=a[2];a=a[0],this.select.append(jQuery("<option/>",{value:a.valueOf(),text:Sao.common.format_datetime(e,f,a)+" "+b}))}.bind(this)),this.el.modal("show"),this.el.on("hidden.bs.modal",function(a){jQuery(this).remove()})},response:function(a){var b=null;"RESPONSE_OK"==a&&(b=this.select.val(),b&&(b=Sao.DateTime(parseInt(b,10)))),this.el.modal("hide"),this.callback(b)}})}(),function(){"use strict";Sao.Wizard=Sao.class_(Object,{init:function(a){this.widget=jQuery("<div/>",{"class":"wizard"}),this.name=a,this.action_id=null,this.id=null,this.ids=null,this.action=null,this.context=null,this.states={},this.session_id=null,this.start_state=null,this.end_state=null,this.screen=null,this.screen_state=null,this.state=null,this.session=Sao.Session.current_session,this.__processing=!1,this.__waiting_response=!1,this.info_bar=new Sao.Window.InfoBar},run:function(a){this.action=a.action,this.action_id=a.data.action_id,this.id=a.data.id,this.ids=a.data.ids,this.model=a.data.model,this.context=a.context,Sao.rpc({method:"wizard."+this.action+".create",params:[this.session.context]},this.session).then(function(a){this.session_id=a[0],this.start_state=this.state=a[1],this.end_state=a[2],this.process()}.bind(this))},process:function(){if(!this.__processing&&!this.__waiting_response){var a=function(){if(this.state==this.end_state)return void this.end();var a=jQuery.extend({},this.context);a.active_id=this.id,a.active_ids=this.ids,a.active_model=this.model,a.action_id=this.action_id;var b={};this.screen&&(b[this.screen_state]=this.screen.get_on_change_value()),Sao.rpc({method:"wizard."+this.action+".execute",params:[this.session_id,b,this.state,a]},this.session).then(function(a){if(a.view){this.clean();var b=a.view;this.update(b.fields_view,b.defaults,b.buttons),this.screen_state=b.state,this.__waiting_response=!0}else this.state=this.end_state;var c=function(){a.actions&&a.actions.forEach(function(a){Sao.Action.exec_action(a[0],a[1],jQuery.extend({},this.context))}.bind(this))}.bind(this);this.state==this.end_state?this.end().then(c):c(),this.__processing=!1}.bind(this),function(a){this.__processing=!1}.bind(this))};a.call(this)}},destroy:function(){},end:function(){return Sao.rpc({method:"wizard."+this.action+".delete",params:[this.session_id,this.session.context]},this.session)},clean:function(){this.widget.children().remove(),this.states={}},response:function(a){return this.__waiting_response=!1,this.screen.current_view.set_value(),this.screen.current_record.validate().then(function(b){return b||a==this.end_state?(this.info_bar.message(),this.state=a,void this.process()):(this.screen.display(!0),void this.info_bar.message(this.screen.invalid_message(),"danger"))}.bind(this))},_get_button:function(a){var b=new Sao.common.Button(a);return this.states[a.state]=b,a["default"]?b.el.addClass("btn-primary"):a.state==this.end_state&&b.el.addClass("btn-link"),b},update:function(a,b,c){c.forEach(function(a){this._get_button(a)}.bind(this)),this.screen=new Sao.Screen(a.model,{mode:[],context:this.context}),this.screen.add_view(a),this.screen.switch_view(),this.widget.append(this.screen.screen_container.el),this.screen.new_(!1),this.screen.current_record.set_default(b),this.screen.set_cursor()}}),Sao.Wizard.create=function(a){var b;if(a.window){b=new Sao.Wizard.Form(a.name);var c=new Sao.Tab.Wizard(b);Sao.Tab.add(c)}else b=new Sao.Wizard.Dialog(a.name);b.run(a)},Sao.Wizard.Form=Sao.class_(Sao.Wizard,{init:function(a){Sao.Wizard.Form._super.init.call(this),this.tab=null,this.name=a,this.form=jQuery("<div/>",{"class":"wizard-form"}).append(this.widget),this.footer=jQuery("<div/>",{"class":"modal-footer"}).appendTo(this.form)},clean:function(){Sao.Wizard.Form._super.clean.call(this),this.footer.children().remove()},_get_button:function(a){var b=Sao.Wizard.Form._super._get_button.call(this,a);return this.footer.append(b.el),b.el.click(function(){this.response(a.state)}.bind(this)),b},end:function(){return Sao.Wizard.Form._super.end.call(this).always(function(){return this.tab.close()}.bind(this))}}),Sao.Wizard.Dialog=Sao.class_(Sao.Wizard,{init:function(a){a||(a=Sao.i18n.gettext("Wizard")),Sao.Wizard.Dialog._super.init.call(this);var b=new Sao.Dialog(a,"wizard-dialog","lg");this.dialog=b.modal,this.content=b.content,this.footer=b.footer,b.body.append(this.info_bar.el).append(this.widget)},clean:function(){Sao.Wizard.Dialog._super.clean.call(this),this.footer.children().remove()},_get_button:function(a){var b=Sao.Wizard.Dialog._super._get_button.call(this,a);return this.footer.append(b.el),a["default"]?(this.content.unbind("submit"),this.content.submit(function(b){this.response(a.state),b.preventDefault()}.bind(this)),b.el.attr("type","submit")):b.el.click(function(){this.response(a.state)}.bind(this)),b},update:function(a,b,c){this.content.unbind("submit"),Sao.Wizard.Dialog._super.update.call(this,a,b,c),this.dialog.modal("show")},destroy:function(a){Sao.Wizard.Dialog._super.destroy.call(this),this.dialog.on("hidden.bsd.modal",function(a){jQuery(this).remove()}),this.dialog.modal("hide");var b,c=jQuery(".wizard-dialog").filter(":visible")[0],d=!1;if(c||(c=Sao.Tab.tabs.get_current(),c?c.screen&&c.screen.model_name!=this.model&&(d=!0,b=Sao.main_menu_screen):(d=!0,b=Sao.main_menu_screen)),c&&c.screen&&(b=c.screen),b){if(b.current_record&&!d){var e;e=b.model_name==this.model?this.ids:[b.current_record.id],b.reload(e,!0)}a&&b.client_action(a)}},end:function(){return Sao.Wizard.Dialog._super.end.call(this).then(this.destroy.bind(this))},show:function(){this.dialog.modal("show")},hide:function(){this.dialog.modal("hide")},state_changed:function(){this.process()}})}(),function(){"use strict";Sao.View.Board=Sao.class_(Object,{init:function(a,b){var c,d,e,f;this.context=b,this.widgets=[],this.actions=[],this.el=jQuery("<div/>",{"class":"board"}),c={},e=a.children()[0];for(var g=0,h=e.attributes.length;h>g;g++)d=e.attributes[g],c[d.name]=d.value;for(this.attributes=c,this.el.append(this.parse(e).el),f=[],g=0,h=this.actions.length;h>g;g++)f.push(this.actions[g].action_prm);this.actions_prms=jQuery.when.apply(jQuery,f)},_parse_node:function(a,b,c){switch(a.tagName){case"image":break;case"separator":this._parse_separator(a,b,c);break;case"label":this._parse_label(a,b,c);break;case"newline":b.add_row();break;case"notebook":this._parse_notebook(a,b,c);break;case"page":this._parse_page(a,b,c);break;case"group":this._parse_group(a,b,c);break;case"hpaned":this._parse_pane(a,b,c,"horizontal");break;case"vpaned":this._parse_pane(a,b,c,"vertical");break;case"child":this._parse_child(a,b,c);break;case"action":this._parse_action(a,b,c)}},parse:function(a,b){var c;return b||(b=new Sao.View.Form.Container(Number(a.getAttribute("col")||4))),c=function(a,c){var d,e,f,g;for(d={},f=0,g=c.attributes.length;g>f;f++)e=c.attributes[f],d[e.name]=e.value;["yexpand","yfill","xexpand","xfill","colspan","position"].forEach(function(a){d[a]&&(d[a]=Number(d[a]))}),this._parse_node(c,b,d)},jQuery(a).children().each(c.bind(this)),b},_parse_separator:function(a,b,c){var d,e;d=c.string,e=new Sao.view.Form.Separator(d,c),b.add(c,e)},_parse_label:function(a,b,c){var d,e;return(d=c.string)?(e=new Sao.View.Form.Label(d,c),void b.add(c,e)):void b.add(c)},_parse_notebook:function(a,b,c){var d;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),d=new Sao.View.Form.Notebook(c),b.add(c,d),this.parse(a,b)},_parse_page:function(a,b,c){var d;d=c.string,page=this.parse(a,b),page=new Sao.View.Form.Page(b.add(page.el,d),c)},_parse_group:function(a,b,c){var d;d=new Sao.View.Form.Group(c),b.add(c,d)},_parse_pane:function(a,b,c,d){var e;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),e=new Sao.common.Paned(d),b.add(c,e),this.parse(a,e)},_parse_child:function(a,b,c){var d,e,f;d=this.parse(a),e=b.get_child1(),e.children().length>0?(f=b.get_child2(),f.append(d.el)):e.append(d.el)},_parse_action:function(a,b,c){var d;void 0===c.yexpand&&(c.yexpand=!0),void 0===c.yfill&&(c.yfill=!0),d=new Sao.View.Board.Action(c,this.context),this.actions.push(d),b.add(c,d)},reload:function(){for(var a=0;a<this.actions.length;a++)this.actions[a].display()}}),Sao.View.Board.Action=Sao.class_(Object,{init:function(a,b){var c,d;this.name=a.name,this.context=b||{},c=new Sao.Model("ir.action.act_window"),this.action_prm=c.execute("get",[this.name],this.context),this.action_prm.done(function(b){var c,e,f,g,h,i;if(this.action=b,this.action.mode=[],f=[],(this.action.views||[]).length>0)for(c=0,e=this.action.views.length;e>c;c++)f.push(this.action.views[c][0]),this.action.mode.push(this.action.views[c][1]);else void 0!==this.action.view_id&&(f=[this.action.view_id[0]]);"mode"in a&&(this.action.mode=a.mode),"pyson_domain"in this.action||(this.action.pyson_domain="[]"),jQuery.extend(this.context,Sao.Session.current_session.context),this.context._user=Sao.Session.current_session.user_id,g=new Sao.PYSON.Decoder(this.context),jQuery.extend(this.context,g.decode(this.action.pyson_context||"{}")),g=new Sao.PYSON.Decoder(this.context),jQuery.extend(this.context,g.decode(this.action.pyson_context||"{}")),this.domain=[],this.update_domain([]),h=jQuery.extend({},this.context),h.context=this.context,h._user=Sao.Session.current_session.user_id,g=new Sao.PYSON.Decoder(h),d=g.decode(this.action.pyson_search_value||"{}"),i={mode:this.action.mode,context:this.context,view_ids:f,domain:this.domain,search_value:d,row_activate:this.row_activate.bind(this)},this.screen=new Sao.Screen(this.action.res_model,i),a.string?this.title.html(a.string):void 0!==this.action.window_name?this.title.html(this.action.name):this.title.html(this.screen.current_view.title),this.screen.switch_view().done(function(){this.body.append(this.screen.screen_container.el),this.screen.search_filter()}.bind(this))}.bind(this)),this.el=jQuery("<div/>",{"class":"board-action panel panel-default"}),this.title=jQuery("<div/>",{"class":"panel-heading"}),this.el.append(this.title),this.body=jQuery("<div/>",{"class":"panel-body"}),this.el.append(this.body)},row_activate:function(){var a,b;this.screen.current_record&&("tree"==this.screen.current_view.view_type&&"keyword_open"in this.screen.current_view.attributes?(a=this.screen.current_view.selected_records().map(function(a){return a.id}),Sao.Action.exec_keyword("tree_open",{model:this.screen.model_name,id:this.screen.current_record.id,ids:a},jQuery.extend({},this.context),!1)):b=new Sao.Window.Form(this.screen,function(a){a?this.screen.current_record.save():this.screen.current_record.cancel()}.bind(this)))},set_value:function(){},display:function(){this.screen.search_filter(this.screen.screen_container.get_text())},get_active:function(){return this.screen&&this.screen.current_record?Sao.common.EvalEnvironment(this.screen.current_record):void 0},update_domain:function(a){var b,c,d,e,f,g;for(e=jQuery.extend({},this.context),e.context=e,e._user=Sao.Session.current_session.user_id,b=0,c=a.length;c>b;b++)d=a[b].get_active(),d&&(e[a[b].name]=d);f=new Sao.PYSON.Decoder(e),g=f.decode(this.action.pyson_domain),Sao.common.compare(this.domain,g)||(this.domain.splice(0,this.domain.length),jQuery.extend(this.domain,g),this.screen&&this.display())}})}();
\ No newline at end of file
diff --git a/index.html b/index.html
index f0d6f8f..a2f1dad 100644
--- a/index.html
+++ b/index.html
@@ -22,13 +22,13 @@ this repository contains the full copyright notices and license terms. -->
         <link rel="stylesheet" href="bower_components/c3/c3.min.css">
         <link rel="stylesheet" href="offcanvas.css">
 
-        <script type="text/javascript" src="dist/sao.min.js"></script>
-        <link rel="stylesheet" type="text/css" href="dist/sao.min.css"
+        <script type="text/javascript" src="dist/tryton-sao.min.js"></script>
+        <link rel="stylesheet" type="text/css" href="dist/tryton-sao.min.css"
         media="screen"/>
         <script type="text/javascript">
             if (typeof Sao == 'undefined') {
-                document.write(unescape('%3cscript type="text/javascript" src="dist/sao.js"%3e%3c/script%3e'));
-                document.write(unescape('%3clink rel="stylesheet" type="text/css" href="dist/sao.css" media="screen"/%3e'));
+                document.write(unescape('%3cscript type="text/javascript" src="dist/tryton-sao.js"%3e%3c/script%3e'));
+                document.write(unescape('%3clink rel="stylesheet" type="text/css" href="dist/tryton-sao.css" media="screen"/%3e'));
             }
         </script>
         </head>
diff --git a/package.json b/package.json
index 456ed46..9bb806e 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "tryton-sao",
   "title": "sao",
   "description": "Tryton webclient",
-  "version": "3.8.0",
+  "version": "3.8.1",
   "homepage": "http://www.tryton.org/",
   "author": {
     "name": "Tryton"
diff --git a/src/sao.js b/src/sao.js
index 1bd6ffa..e166b26 100644
--- a/src/sao.js
+++ b/src/sao.js
@@ -192,9 +192,7 @@ var Sao = {};
                 if ('' === key) {
                     continue;
                 }
-                if ('' !== data[key][1]) {
-                    data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
-                }
+                data[key] = 2 == data[key].length ? data[key][1] : data[key].slice(1);
             }
             Sao.i18n.loadJSON(data);
         });
diff --git a/tests/index.html b/tests/index.html
index 0a62feb..ff15a59 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -9,7 +9,7 @@ this repository contains the full copyright notices and license terms. -->
         <script type="text/javascript" src="../bower_components/gettext.js/dist/gettext.js"></script>
         <script type="text/javascript" src="../bower_components/qunit/qunit/qunit.js"></script>
         <link rel="stylesheet" type="text/css" href="../bower_components/qunit/qunit/qunit.css" media="screen"/>
-        <script type="text/javascript" src="../dist/sao.js"></script>
+        <script type="text/javascript" src="../dist/tryton-sao.js"></script>
         <script type="text/javascript" src="./sao.js"></script>
     </head>
     <body>
-- 
tryton-sao



More information about the tryton-debian-vcs mailing list