changeset 9efb7fa52876 in sao:default
details: https://hg.tryton.org/sao?cmd=changeset&node=9efb7fa52876
description:
        Use for...of loop

        issue8183
        review403221002
diffstat:

 src/action.js         |   12 +-
 src/board.js          |   25 ++--
 src/common.js         |   66 +++++++-------
 src/model.js          |  219 ++++++++++++++++++++++---------------------------
 src/pyson.js          |    6 +-
 src/sao.js            |   43 ++++-----
 src/screen.js         |  220 ++++++++++++++++++++++---------------------------
 src/session.js        |    4 +-
 src/tab.js            |  109 +++++++++++------------
 src/view.js           |   42 ++++----
 src/view/calendar.js  |   13 +-
 src/view/form.js      |  115 ++++++++++++-------------
 src/view/graph.js     |   24 ++---
 src/view/list_form.js |   19 +--
 src/view/tree.js      |  152 ++++++++++++++++------------------
 src/window.js         |   61 ++++++-------
 src/wizard.js         |   14 +-
 17 files changed, 531 insertions(+), 613 deletions(-)

diffs (2872 lines):

diff -r 3e66fd9e064f -r 9efb7fa52876 src/action.js
--- a/src/action.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/action.js     Sun Apr 10 19:28:21 2022 +0200
@@ -63,10 +63,10 @@
                 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]);
-                    });
+                    for (const view of action.views) {
+                        params.view_ids.push(view[0]);
+                        params.mode.push(view[1]);
+                    }
                 } else if (!jQuery.isEmptyObject(action.view_id)) {
                     params.view_ids = [action.view_id[0]];
                 }
@@ -94,10 +94,10 @@
                 params.search_value = decoder.decode(
                     action.pyson_search_value || '[]');
                 params.tab_domain = [];
-                action.domains.forEach(function(element, index) {
+                for (const element of action.domains) {
                     params.tab_domain.push(
                         [element[0], decoder.decode(element[1]), element[2]]);
-                });
+                }
                 name_prm = jQuery.when(action.name);
                 params.model = action.res_model || data.res_model;
                 params.res_id = action.res_id || data.res_id;
diff -r 3e66fd9e064f -r 9efb7fa52876 src/board.js
--- a/src/board.js      Sun Apr 10 19:25:17 2022 +0200
+++ b/src/board.js      Sun Apr 10 19:28:21 2022 +0200
@@ -42,8 +42,8 @@
             new this.xml_parser(this, null, {}).parse(xml.children()[0]);
 
             actions_prms = [];
-            for (var i = 0, len = this.actions.length; i < len; i++) {
-                actions_prms.push(this.actions[i].action_prm);
+            for (const action of this.actions) {
+                actions_prms.push(action.action_prm);
             }
             this.actions_prms = jQuery.when.apply(jQuery, actions_prms);
         },
@@ -57,12 +57,11 @@
             }
         },
         active_changed: function(event_action) {
-            this.actions.forEach(function(action) {
-                if (action === event_action) {
-                    return;
+            for (const action of this.actions) {
+                if (action !== event_action) {
+                    action.update_domain(this.actions);
                 }
-                action.update_domain(this.actions);
-            });
+            }
         },
     });
 
@@ -94,10 +93,10 @@
                 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]);
-                    });
+                    for (const view of action.views) {
+                        params.view_ids.push(view[0]);
+                        params.mode.push(view[1]);
+                    }
                 } else if (!jQuery.isEmptyObject(action.view_id)) {
                     params.view_ids = [action.view_id[0]];
                 }
@@ -120,10 +119,10 @@
                 params.search_value = decoder.decode(
                     action.pyson_search_value || '[]');
                 params.tab_domain = [];
-                action.domains.forEach(function(element, index) {
+                for (const element of action.domains) {
                     params.tab_domain.push(
                         [element[0], decoder.decode(element[1]), element[2]]);
-                });
+                }
                 params.context_model = action.context_model;
                 params.context_domain = action.context_domain;
                 if (action.limit !== null) {
diff -r 3e66fd9e064f -r 9efb7fa52876 src/common.js
--- a/src/common.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/common.js     Sun Apr 10 19:28:21 2022 +0200
@@ -109,15 +109,15 @@
             i++;
         }
         var result = [[]];
-        pools.forEach(function(pool) {
+        for (const pool of pools) {
             var tmp = [];
-            result.forEach(function(x) {
-                pool.forEach(function(y) {
+            for (const x of result) {
+                for (const y of pool) {
                     tmp.push(x.concat([y]));
-                });
-            });
+                }
+            }
             result = tmp;
-        });
+        }
         return result;
     };
 
@@ -667,9 +667,9 @@
     Sao.common.selection_mixin.init_selection = function(value, callback) {
         if (!value) {
             value = {};
-            (this.attributes.selection_change_with || []).forEach(function(e) {
+            for (const e of (this.attributes.selection_change_with || [])) {
                 value[e] = null;
-            });
+            }
         }
         var key = JSON.stringify(value);
         var selection = this.attributes.selection || [];
@@ -758,17 +758,17 @@
                 }, record.model.session);
                 prm.done(function(result) {
                     var selection = [];
-                    result.forEach(function(x) {
+                    for (const x of result) {
                         selection.push([x.id, x.rec_name]);
-                    });
+                    }
                     if (this.nullable_widget) {
                         selection.push([null, '']);
                     }
                     var help = {};
                     if (help_field){
-                        result.forEach(function(x) {
+                        for (const x of result) {
                             help[x.id] = x[help_field];
-                        });
+                        }
                     }
                     this._last_domain = [domain, context];
                     this._domain_cache[jdomain] = selection;
@@ -1554,7 +1554,7 @@
             var cur = tokens[0];
             var nex = null;
             var result = [];
-            tokens.slice(1).forEach(function(nex) {
+            for (const nex of tokens.slice(1)) {
                 if ((nex == '=') && cur &&
                     ~this.OPERATORS.indexOf(cur + nex)) {
                     result.push(cur + nex);
@@ -1565,7 +1565,7 @@
                     }
                     cur = nex;
                 }
-            }.bind(this));
+            }
             if (cur !== null) {
                 result.push(cur);
             }
@@ -1575,9 +1575,9 @@
             var result = [];
             var current = result;
             var parent = [];
-            tokens.forEach(function(token, i) {
+            for (const token of tokens) {
                 if (current === undefined) {
-                    return;
+                    continue;
                 }
                 if (token == '(') {
                     parent.push(current);
@@ -1587,7 +1587,7 @@
                 } else {
                     current.push(token);
                 }
-            });
+            }
             return result;
         },
         group: function(tokens) {
@@ -1676,24 +1676,24 @@
             _group = _group.bind(this);
 
             var parts = [];
-            tokens.forEach(function(token) {
+            for (const token of tokens) {
                 if (this.is_generator(token)) {
-                    _group(parts).forEach(function(group) {
+                    for (const group of _group(parts)) {
                         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) {
+            }
+            for (const group of _group(parts)) {
                 if (!Sao.common.compare(group, [null])) {
                     result.push(group);
                 }
-            });
+            }
             return result;
         },
         is_generator: function(value) {
@@ -2653,11 +2653,11 @@
             if (domoperator) {
                 result.push(domoperator);
             }
-            domains.forEach(function append(domain) {
+            for (const domain of domains) {
                 if (!jQuery.isEmptyObject(domain)) {
                     result.push(domain);
                 }
-            });
+            }
             return this.simplify(this.merge(result));
         },
         unique_value: function(domain) {
@@ -3066,20 +3066,20 @@
                 from = (from < 0) ? 0 : from;
                 var to = Math.round(idx + this.batchnum / 2);
                 var ids = [];
-                this.tryton_icons.slice(from, to).forEach(function(e) {
+                for (const e of this.tryton_icons.slice(from, to)) {
                     ids.push(e[0]);
-                });
+                }
 
                 var read_prm = icon_model.execute('read',
                     [ids, ['name', 'icon']], {});
                 return read_prm.then(function(icons) {
-                    icons.forEach(function(icon) {
+                    for (const icon of icons) {
                         var img_url = this._convert(icon.icon);
                         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));
+                    }
                 }.bind(this));
             }.bind(this));
             return this.register_prm;
@@ -4096,9 +4096,8 @@
                 'class': 'btn-group',
                 'role': 'group'
             }).appendTo(toolbar);
-            for (var i in buttons) {
-                var properties = buttons[i];
-                var button = jQuery('<button/>', {
+            for (const properties of buttons) {
+                const button = jQuery('<button/>', {
                     'class': 'btn btn-default',
                     'type': 'button',
                     'title': properties.label,
@@ -4144,8 +4143,7 @@
                 })));
             };
         };
-        for (var i in selections) {
-            var properties = selections[i];
+        for (var properties of selections) {
             var group = jQuery('<div/>', {
                 'class': 'btn-group',
                 'role': 'group'
diff -r 3e66fd9e064f -r 9efb7fa52876 src/model.js
--- a/src/model.js      Sun Apr 10 19:25:17 2022 +0200
+++ b/src/model.js      Sun Apr 10 19:28:21 2022 +0200
@@ -81,10 +81,8 @@
                 position = this.length;
             }
             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);
+            for (const id of ids) {
+                let new_record = this.get(id);
                 if (!new_record) {
                     new_record = new Sao.Record(this.model, id);
                     new_record.group = this;
@@ -95,33 +93,29 @@
             }
             // 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];
+            for (const record of this.record_removed) {
                 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];
+            for (const record of this.record_deleted) {
                 if (!~ids.indexOf(record.id)) {
                     record_deleted.push(record);
                 }
             }
             this.record_deleted = record_deleted;
             if (new_records.length && modified) {
-                new_records.forEach(function(record) {
+                for (const record of new_records) {
                     record.modified_fields.id = true;
-                });
+                }
                 this.record_modified();
             }
         };
         array.get = function(id) {
             // TODO optimize
-            for (var i = 0, len = this.length; i < len; i++) {
-                var record = this[i];
+            for (const record of this) {
                 if (record.id == id) {
                     return record;
                 }
@@ -226,9 +220,9 @@
         };
         array.record_modified = function() {
             if (!this.parent) {
-                this.screens.forEach(function(screen) {
+                for (const screen of this.screens) {
                     screen.record_modified();
-                });
+                }
             } else {
                 this.parent.modified_fields[this.child_name] = true;
                 this.parent.model.fields[this.child_name].changed(this.parent);
@@ -252,16 +246,16 @@
             });
             var context = this.context;
             context._timestamp = {};
-            records.forEach(function(record) {
+            for (const record of records) {
                 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) {
-                records.forEach(function(record) {
+                for (const record of records) {
                     record.destroy();
-                });
+                }
                 reload_ids = reload_ids.filter(function(e) {
                     return !~record_ids.indexOf(e);
                 });
@@ -288,9 +282,9 @@
                 deferreds.push(record.save());
             });
             if (!jQuery.isEmptyObject(this.record_deleted)) {
-                this.record_deleted.forEach(function(record) {
+                for (const record of this.record_deleted) {
                     this._remove(record);
-                }.bind(this));
+                }
                 deferreds.push(this.delete_(this.record_deleted));
                 this.record_deleted.splice(0, this.record_deleted.length);
             }
@@ -308,15 +302,15 @@
             }.bind(this));
         };
         array.reload = function(ids) {
-            this.children.forEach(function(child) {
+            for (const child of this.children) {
                 child.reload(ids);
-            });
-            ids.forEach(function(id) {
-                var record = this.get(id);
+            }
+            for (const id of ids) {
+                const record = this.get(id);
                 if (record && jQuery.isEmptyObject(record.modified_fields)) {
                     record.cancel();
                 }
-            }.bind(this));
+            }
         };
         array.on_write_ids = function(ids) {
             var deferreds = [];
@@ -346,17 +340,17 @@
                 return;
             }
             var new_ = [];
-            this.forEach(function(record) {
+            for (const record of this) {
                 if (record.id < 0) {
                     new_.push(record);
                 }
-            });
+            }
             if (new_.length && added.length) {
                 this.model.execute('default_get', [added, this.context])
                     .then(function(values) {
-                        new_.forEach(function(record) {
+                        for (const record of new_) {
                             record.set_default(values, true, false);
-                        });
+                        }
                         this.record_modified();
                     }.bind(this));
             }
@@ -373,11 +367,11 @@
         Object.defineProperty(array, 'domain', {
             get: function() {
                 var domain = [];
-                this.screens.forEach(function(screen) {
+                for (const screen of this.screens) {
                     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)];
@@ -487,8 +481,7 @@
             } else {
                 cmp = function(a, b) { return a < b; };
             }
-            for (var i=0; i < this.length; i++) {
-                record = this[i];
+            for (const record of this) {
                 if (record.get_loaded([field]) || changed || record.id < 0) {
                     if (prev) {
                         index = prev.field_get(field);
@@ -656,15 +649,14 @@
             if (name == '*') {
                 loading = 'eager';
                 views = new Set();
-                var views_add = function(view) {
-                    views.add(view);
-                };
                 for (fname in this.model.fields) {
                     field = this.model.fields[fname];
                     if ((field.description.loading || 'eager') == 'lazy') {
                         loading = 'lazy';
                     }
-                    field.views.forEach(views_add);
+                    for (const view of field.views) {
+                        views.add(view);
+                    }
                 }
             } else {
                 loading = this.model.fields[name].description.loading || 
'eager';
@@ -694,8 +686,7 @@
             }
             var fnames_to_fetch = fnames.slice();
             var rec_named_fields = ['many2one', 'one2one', 'reference'];
-            for (var i in fnames) {
-                fname = fnames[i];
+            for (const fname of fnames) {
                 var fdescription = this.model.fields[fname].description;
                 if (~rec_named_fields.indexOf(fdescription.type))
                     fnames_to_fetch.push(fname + '.rec_name');
@@ -775,9 +766,9 @@
                     fnames_to_fetch], context, async);
             var succeed = function(values, exception=false) {
                 var id2value = {};
-                values.forEach(function(e, i, a) {
+                for (const e of values) {
                     id2value[e.id] = e;
-                });
+                }
                 for (var id in id2record) {
                     if (!id2record.hasOwnProperty(id)) {
                         continue;
@@ -805,8 +796,8 @@
                     default_values = {
                         id: id
                     };
-                    for (var i in fnames_to_fetch) {
-                        default_values[fnames_to_fetch[i]] = null;
+                    for (const fname of fnames_to_fetch) {
+                        default_values[fname] = null;
                     }
                     failed_values.push(default_values);
                 }
@@ -1043,27 +1034,27 @@
         _get_on_change_args: function(args) {
             var result = {};
             var values = Sao.common.EvalEnvironment(this, 'on_change');
-            args.forEach(function(arg) {
+            for (const arg of args) {
                 var scope = values;
-                arg.split('.').forEach(function(e) {
+                for (const e of arg.split('.')) {
                     if (scope !== undefined) {
                         scope = scope[e];
                     }
-                });
+                }
                 result[arg] = scope;
-            });
+            }
             return result;
         },
         on_change: function(fieldnames) {
             var values = {};
-            fieldnames.forEach(function(fieldname) {
+            for (const fieldname of fieldnames) {
                 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 changes;
                 try {
@@ -1294,11 +1285,11 @@
         get_loaded: function(fields) {
             if (!jQuery.isEmptyObject(fields)) {
                 var result = true;
-                fields.forEach(function(field) {
+                for (const field of fields) {
                     if (!(field in this._loaded) && !(field in 
this.modified_fields)) {
                         result = false;
                     }
-                }.bind(this));
+                }
                 return result;
             }
             return Sao.common.compare(Object.keys(this.model.fields).sort(),
@@ -1454,8 +1445,7 @@
         },
         destroy: function() {
             var vals = Object.values(this._values);
-            for (var i=0; i < vals.length; i++) {
-                var val = vals[i];
+            for (const val of vals) {
                 if (val && val.hasOwnProperty('destroy')) {
                     val.destroy();
                 }
@@ -1621,9 +1611,9 @@
             record, states=['readonly', 'required', 'invisible']) {
             var state_changes = record.expr_eval(
                     this.description.states || {});
-            states.forEach(function(state) {
+            for (const state of states) {
                 if ((state == 'readonly') && this.description.readonly) {
-                    return;
+                    continue;
                 }
                 if (state_changes[state] !== undefined) {
                     this.get_state_attrs(record)[state] = state_changes[state];
@@ -1631,7 +1621,7 @@
                     this.get_state_attrs(record)[state] =
                         this.description[state];
                 }
-            }.bind(this));
+            }
             if (record.group.readonly ||
                     this.get_state_attrs(record).domain_readonly) {
                 this.get_state_attrs(record).readonly = true;
@@ -1707,11 +1697,10 @@
                         var localpart = leftpart.split('.', 1)[1];
                         var constraintfields = [];
                         if (domain_readonly) {
-                            inversion.localize_domain(
-                                    original_domain.slice(1))
-                                .forEach(function(leaf) {
-                                    constraintfields.push(leaf);
-                                });
+                            for (const leaf of inversion.localize_domain(
+                                original_domain.slice(1))) {
+                                constraintfields.push(leaf);
+                            }
                         }
                         if ((localpart != 'id') ||
                                 !~constraintfields.indexOf(recordpart)) {
@@ -2124,17 +2113,14 @@
             if (mode == 'list values') {
                 var context = this.get_context(record);
                 var field_names = {};
-                value.forEach(function(val) {
-                    for (var fieldname in val) {
-                        if (!val.hasOwnProperty(fieldname)) {
-                            continue;
-                        }
+                for (const val of value) {
+                    for (var fieldname of val) {
                         if (!(fieldname in group.model.fields) &&
                                 (!~fieldname.indexOf('.'))) {
                             field_names[fieldname] = true;
                         }
                     }
-                });
+                }
                 if (!jQuery.isEmptyObject(field_names)) {
                     var args = {
                         'method': 'model.' + this.description.relation +
@@ -2152,19 +2138,17 @@
             }
             if (mode == 'list ids') {
                 var records_to_remove = [];
-                for (var i = 0, len = group.length; i < len; i++) {
-                    var old_record = group[i];
+                for (const old_record of group) {
                     if (!~value.indexOf(old_record.id)) {
                         records_to_remove.push(old_record);
                     }
                 }
-                for (i = 0, len = records_to_remove.length; i < len; i++) {
-                    var record_to_remove = records_to_remove[i];
+                for (const record_to_remove of records_to_remove) {
                     group.remove(record_to_remove, true, true, false, false);
                 }
                 group.load(value, modified || default_);
             } else {
-                value.forEach(function(vals) {
+                for (const vals of value) {
                     var new_record = group.new_(false);
                     if (default_) {
                         // Don't validate as parent will validate
@@ -2174,7 +2158,7 @@
                         new_record.set(vals);
                         group.push(new_record);
                     }
-                });
+                }
             }
         },
         set: function(record, value, _default=false) {
@@ -2204,8 +2188,7 @@
             var to_add = [];
             var to_create = [];
             var to_write = [];
-            for (var i = 0, len = group.length; i < len; i++) {
-                var record2 = group[i];
+            for (const record2 of group) {
                 if (~record_removed.indexOf(record2) ||
                         ~record_deleted.indexOf(record2)) {
                     continue;
@@ -2287,29 +2270,29 @@
             if (value instanceof Array) {
                 return this._set_value(record, value, false, true);
             }
+            var new_field_names = {};
             if (value.add || value.update) {
                 var context = this.get_context(record);
                 fields = record._values[this.name].model.fields;
-                var new_field_names = {};
                 var adding_values = [];
                 if (value.add) {
-                    for (var i=0; i < value.add.length; i++) {
-                        adding_values.push(value.add[i][1]);
+                    for (const add of value.add) {
+                        adding_values.push(add[1]);
                     }
                 }
-                [adding_values, value.update].forEach(function(l) {
+                for (const l of [adding_values, value.update]) {
                     if (!jQuery.isEmptyObject(l)) {
-                        l.forEach(function(v) {
-                            Object.keys(v).forEach(function(f) {
+                        for (const v of l) {
+                            for (const f of Object.keys(v)) {
                                 if (!(f in fields) &&
                                     (f != 'id') &&
                                     (!~f.indexOf('.'))) {
                                         new_field_names[f] = true;
                                     }
-                            });
-                        });
+                            }
+                        }
                     }
-                });
+                }
                 if (!jQuery.isEmptyObject(new_field_names)) {
                     var args = {
                         'method': 'model.' + this.description.relation +
@@ -2328,31 +2311,31 @@
 
             var group = record._values[this.name];
             if (value.delete) {
-                value.delete.forEach(function(record_id) {
-                    var record2 = group.get(record_id);
+                for (const record_id of value.delete) {
+                    const record2 = group.get(record_id);
                     if (record2) {
                         group.remove(record2, false, true, false, false);
                     }
-                }.bind(this));
+                }
             }
             if (value.remove) {
-                value.remove.forEach(function(record_id) {
-                    var record2 = group.get(record_id);
+                for (const record_id of value.remove) {
+                    const record2 = group.get(record_id);
                     if (record2) {
                         group.remove(record2, true, true, false, false);
                     }
-                }.bind(this));
+                }
             }
 
             if (value.add || value.update) {
                 // First set already added fields to prevent triggering a
                 // second on_change call
                 if (value.update) {
-                    value.update.forEach(function(vals) {
+                    for (const vals of value.update) {
                         if (!vals.id) {
-                            return;
+                            continue;
                         }
-                        var record2 = group.get(vals.id);
+                        const record2 = group.get(vals.id);
                         if (record2) {
                             var vals_to_set = {};
                             for (var key in vals) {
@@ -2362,16 +2345,16 @@
                             }
                             record2.set_on_change(vals_to_set);
                         }
-                    });
+                    }
                 }
 
                 group.add_fields(new_fields);
                 if (value.add) {
-                    value.add.forEach(function(vals) {
-                        var new_record;
-                        var index = vals[0];
-                        var data = vals[1];
-                        var id_ = data.id;
+                    for (const vals of value.add) {
+                        let new_record;
+                        const index = vals[0];
+                        const data = vals[1];
+                        const id_ = data.id;
                         delete data.id;
                         if (id_) {
                             new_record = group.get(id_);
@@ -2381,18 +2364,18 @@
                         }
                         group.add(new_record, index, false);
                         new_record.set_on_change(data);
-                    });
+                    }
                 }
                 if (value.update) {
-                    value.update.forEach(function(vals) {
+                    for (const vals of value.update) {
                         if (!vals.id) {
-                            return;
+                            continue;
                         }
-                        var record2 = group.get(vals.id);
+                        const record2 = group.get(vals.id);
                         if (record2) {
                             record2.set_on_change(vals);
                         }
-                    });
+                    }
                 }
             }
         },
@@ -2420,11 +2403,10 @@
             var records = group.filter(function(record) {
                 return record.modified;
             });
-            var record2;
-            jQuery.extend(records, group.record_removed, group.record_deleted)
-            .forEach(function(record) {
+            for (const record of jQuery.extend(
+                records, group.record_removed, group.record_deleted)) {
                 jQuery.extend(timestamps, record.get_timestamp());
-            });
+            }
             return timestamps;
         },
         get_eval: function(record) {
@@ -2434,9 +2416,7 @@
 
             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];
+            for (const record2 of group) {
                 if (~record_removed.indexOf(record2) ||
                         ~record_deleted.indexOf(record2))
                     continue;
@@ -2448,9 +2428,7 @@
             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];
+            for (const record2 of group) {
                 if (!record2.deleted && !record2.removed)
                     result.push(record2.get_on_change_value(
                                 [this.description.relation_field || '']));
@@ -2485,9 +2463,7 @@
                     ldomain = [['id', '=', null]];
                 }
             }
-            for (var i = 0, len = (record._values[this.name] || []).length;
-                    i < len; i++) {
-                var record2 = record._values[this.name][i];
+            for (const record2 of (record._values[this.name] || [])) {
                 if (!record2.get_loaded() && (record2.id >= 0) &&
                         !pre_validate) {
                     continue;
@@ -2775,8 +2751,7 @@
 
             keys = jQuery.extend([], keys);
             var update_keys = function(values) {
-                for (var i = 0, len = values.length; i < len; i++) {
-                    var k = values[i];
+                for (const k of values) {
                     this.keys[k.name] = k;
                 }
             }.bind(this);
@@ -2797,10 +2772,10 @@
             return this.schema_model.execute('get_keys', [ids], context)
                 .then(function(new_fields) {
                     var names = [];
-                    new_fields.forEach(function(new_field) {
+                    for (const new_field of new_fields) {
                         this.keys[new_field.name] = new_field;
                         names.push(new_field.name);
-                    }.bind(this));
+                    }
                     return names;
                 }.bind(this));
         },
diff -r 3e66fd9e064f -r 9efb7fa52876 src/pyson.js
--- a/src/pyson.js      Sun Apr 10 19:25:17 2022 +0200
+++ b/src/pyson.js      Sun Apr 10 19:28:21 2022 +0200
@@ -311,8 +311,7 @@
 
     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];
+        for (const statement of value.s) {
             result = result && statement;
         }
         return result;
@@ -335,8 +334,7 @@
 
     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];
+        for (const statement of value.s) {
             result = result || statement;
         }
         return result;
diff -r 3e66fd9e064f -r 9efb7fa52876 src/sao.js
--- a/src/sao.js        Sun Apr 10 19:25:17 2022 +0200
+++ b/src/sao.js        Sun Apr 10 19:28:21 2022 +0200
@@ -95,9 +95,9 @@
             // Implementation is not strictly equivalent but works for most
             // cases
             var result = [];
-            value.forEach(function(e) {
+            for (const e of value) {
                 result.push(e);
-            });
+            }
             return result;
         };
     }
@@ -404,9 +404,9 @@
                 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) {
+                    for (const action_id of (preferences.actions || [])) {
                         Sao.Action.execute(action_id, {}, null, {});
-                    });
+                    }
                     Sao.set_title();
                     var new_lang = preferences.language != 
Sao.i18n.getLocale();
                     var prm = jQuery.Deferred();
@@ -465,8 +465,7 @@
             }
         } else {
             url = decodeURIComponent(url);
-            for (var i = 0; i < Sao.Tab.tabs.length; i++) {
-                tab = Sao.Tab.tabs[i];
+            for (const tab of Sao.Tab.tabs) {
                 if (decodeURIComponent(tab.get_url()) == url) {
                     tab.show();
                     return;
@@ -487,12 +486,12 @@
         var path, params = {};
         if (i >= 0) {
             path = url.substring(0, i);
-            url.substring(i + 1).split('&').forEach(function(part) {
+            for (const part of url.substring(i + 1).split('&')) {
                 if (part) {
                     var item = part.split('=').map(decodeURIComponent);
                     params[item[0]] = item[1];
                 }
-            });
+            }
         } else {
             path = url;
         }
@@ -984,18 +983,18 @@
                     [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));
+                    var results = [];
+                    for (const result of s_results) {
+                        results.push({
+                            'model': result[1],
+                            'model_name': result[2],
+                            'record_id': result[3],
+                            'record_name': result[4],
+                            'icon': result[5],
+                        });
+                    }
+                    return results;
+                }.bind(this));
         },
         match_selected: function(item) {
             if (item.model == Sao.main_menu_screen.model_name) {
@@ -1175,7 +1174,7 @@
             .text(Sao.i18n.gettext('Tab shortcuts')))
             .appendTo(row);
 
-        shortcuts_defs().forEach(function(definition) {
+        for (const definition of shortcuts_defs()) {
             var dt = jQuery('<dt/>').text(definition.label);
             var dd = jQuery('<dd/>').append(jQuery('<kbd>')
                 .text(definition.shortcut));
@@ -1187,7 +1186,7 @@
             }
             dt.appendTo(dest_dl);
             dd.appendTo(dest_dl);
-        });
+        }
         dialog.modal.on('hidden.bs.modal', function() {
             jQuery(this).remove();
         });
diff -r 3e66fd9e064f -r 9efb7fa52876 src/screen.js
--- a/src/screen.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/screen.js     Sun Apr 10 19:28:21 2022 +0200
@@ -101,9 +101,9 @@
             this.but_bookmark.click(function() {
                 dropdown_bookmark.empty();
                 var bookmarks = this.bookmarks();
-                for (var i=0; i < bookmarks.length; i++) {
-                    var name = bookmarks[i][1];
-                    var domain = bookmarks[i][2];
+                for (const bookmark of bookmarks) {
+                    const name = bookmark[1];
+                    const domain = bookmark[2];
                     jQuery('<li/>', {
                         'role': 'presentation'
                     })
@@ -228,11 +228,11 @@
             var completions = this.screen.domain_parser.completion(
                     this.get_text());
             this.search_list.empty();
-            completions.forEach(function(e) {
+            for (const e of completions) {
                 jQuery('<option/>', {
                     'value': e.trim()
                 }).appendTo(this.search_list);
-            }, this);
+            }
         },
         set_star: function(star) {
             var img = this.but_star.children('img');
@@ -306,12 +306,12 @@
                         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 access = bookmarks[i][3];
-                    var text = this.screen.domain_parser.string(domain);
+                for (const bookmark of bookmarks) {
+                    const id = bookmark[0];
+                    const name = bookmark[1];
+                    const domain = bookmark[2];
+                    const access = bookmark[3];
+                    const text = this.screen.domain_parser.string(domain);
                     if ((text === current_text) ||
                             (Sao.common.compare(domain, current_domain))) {
                         this.set_star(true);
@@ -428,10 +428,10 @@
                 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;
+                for (const field of this.search_form.fields) {
+                    const label = field[0];
+                    const entry = field[1];
+                    let value;
                     if ((entry instanceof Sao.ScreenContainer.Between) ||
                         (entry instanceof Sao.ScreenContainer.Selection)) {
                         value = entry.get_value(quote);
@@ -459,36 +459,17 @@
                 });
 
                 var fields = [];
-                var field;
                 for (var f in domain_parser.fields) {
-                    field = domain_parser.fields[f];
+                    const field = domain_parser.fields[f];
                     if ((field.searchable || field.searchable === undefined) &&
                         !field.name.contains('.')) {
                         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];
+                for (const field of fields) {
                     var form_group = jQuery('<div/>', {
                         'class': 'form-group form-group-sm'
                     }).append(jQuery('<label/>', {
@@ -506,10 +487,15 @@
                                 'class': 'form-control input-sm',
                                 id: prefix + field.name
                             });
-                            ['',
-                            Sao.i18n.gettext('True'),
-                            Sao.i18n.gettext('False')].forEach(
-                                    boolean_option(input));
+                            for (const e of [
+                                '',
+                                Sao.i18n.gettext('True'),
+                                Sao.i18n.gettext('False')]) {
+                                jQuery('<option/>', {
+                                    value: e,
+                                    text: e
+                                }).appendTo(input);
+                            }
                             break;
                         case 'selection':
                         case 'multiselection':
@@ -775,12 +761,12 @@
                 multiple: true,
                 id: id
             });
-            selections.forEach(function(s) {
+            for (const s of selections) {
                 jQuery('<option/>', {
                     value: s[1],
                     text: s[1]
                 }).appendTo(this.el);
-            }.bind(this));
+            }
         },
         get_value: function(quote) {
             var value = this.el.val();
@@ -1232,12 +1218,11 @@
                 this.current_record = null;
             }
             this.group.add_fields(fields);
-            var views_add = function(view) {
-                this.group.model.fields[name].views.add(view);
-            }.bind(this);
             for (name in fields_views) {
                 var views = fields_views[name];
-                views.forEach(views_add);
+                for (const view of views) {
+                    this.group.model.fields[name].views.add(view);
+                }
             }
             this.group.exclude_field = this.exclude_field;
         },
@@ -1250,35 +1235,35 @@
             this.set_group(group);
         },
         record_modified: function(display=true) {
-            this.windows.forEach(function(window_) {
+            for (const window_ of this.windows) {
                 if (window_.record_modified) {
                     window_.record_modified();
                 }
-            });
+            }
             if (display) {
                 return this.display();
             }
         },
         record_message: function(position, size, max_size, record_id) {
-            this.windows.forEach(function(window_) {
+            for (const window_ of this.windows) {
                 if (window_.record_message) {
                     window_.record_message(position, size, max_size, 
record_id);
                 }
-            });
+            }
         },
         record_saved: function() {
-            this.windows.forEach(function(window_) {
+            for (const window_ of this.windows) {
                 if (window_.record_saved) {
                     window_.record_saved();
                 }
-            });
+            }
         },
         update_resources: function(resources) {
-            this.windows.forEach(function(window_) {
+            for (const window_ of this.windows) {
                 if (window_.update_resources) {
                     window_.update_resources(resources);
                 }
-            });
+            }
         },
         has_update_resources: function() {
             return this.windows.some(function(window_) {
@@ -1345,11 +1330,11 @@
                         ~['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] &&
-                        ((this.views[i] == this.current_view) ||
-                            this.views[i].el.parent().length)) {
-                        deferreds.push(this.views[i].display());
+                for (const view of this.views) {
+                    if (view &&
+                        ((view == this.current_view) ||
+                            view.el.parent().length)) {
+                        deferreds.push(view.display());
                     }
                 }
             }
@@ -1639,9 +1624,9 @@
         },
         unremove: function() {
             var records = this.current_view.selected_records;
-            records.forEach(function(record) {
+            for (const record of records) {
                 record.group.unremove(record);
-            });
+            }
         },
         remove: function(delete_, remove, force_remove, records) {
             var prm = jQuery.when();
@@ -1658,14 +1643,14 @@
             var idx = top_group.indexOf(top_record);
             var path = top_record.get_path(this.group);
             return prm.then(function() {
-                records.forEach(function(record) {
+                for (const record of records) {
                     record.group.remove(record, remove, true, force_remove, 
false);
-                });
+                }
                 // trigger changed only once
                 records[0].group.record_modified();
                 var prms = [];
                 if (delete_) {
-                    records.forEach(function(record) {
+                    for (const record of records) {
                         if (record.group.parent) {
                             prms.push(record.group.parent.save(false));
                         }
@@ -1678,7 +1663,7 @@
                                 record.group.record_removed.indexOf(record), 
1);
                         }
                         // TODO destroy
-                    });
+                    }
                 }
                 if (idx > 0) {
                     var record = top_group[idx - 1];
@@ -1767,11 +1752,11 @@
                         // take only the first definition
                         if (!(name in dom_fields)) {
                             dom_fields[name] = fields[name];
-                            ['string', 'factor'].forEach(function(attr) {
+                            for (const attr of ['string', 'factor']) {
                                 if (node.getAttribute(attr)) {
                                     dom_fields[name][attr] = 
node.getAttribute(attr);
                                 }
-                            });
+                            }
                         }
                         var symbol = node.getAttribute('symbol');
                         if (symbol && !(symbol in dom_fields)) {
@@ -1789,27 +1774,25 @@
             }
 
             // Add common fields
-            [
+            const common_fields = new Map([
                 ['id', Sao.i18n.gettext('ID'), 'integer'],
                 ['create_uid', Sao.i18n.gettext('Created by'), 'many2one'],
                 ['create_date', Sao.i18n.gettext('Created at'), 'datetime'],
                 ['write_uid', Sao.i18n.gettext('Modified by'), 'many2one'],
                 ['write_date', Sao.i18n.gettext('Modified at'), '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"';
-                            }
-                        }
-                    });
+            ]);
+            for (const [name, string, type] of common_fields) {
+                if (!(name in fields)) {
+                    fields[name] = {
+                        'string': string,
+                        'name': name,
+                        'type': type
+                    };
+                    if (type == 'datetime') {
+                        fields[name].format = '"%H:%M:%S"';
+                    }
+                }
+            }
 
             domain_parser = new Sao.common.DomainParser(fields, this.context);
             this._domain_parser[view_id] = domain_parser;
@@ -1820,9 +1803,9 @@
             var change_with = props.selection_change_with;
             if (!jQuery.isEmptyObject(change_with)) {
                 var values = {};
-                change_with.forEach(function(p) {
+                for (const p of change_with) {
                     values[p] = null;
-                });
+                }
                 selection = this.model.execute(props.selection,
                         [values], undefined, false);
             } else {
@@ -1857,32 +1840,30 @@
             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') {
+            for (const field of Object.keys(invalid_fields).sort()) {
+                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));
-                    } 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);
-                        }
+                            '"%1" is not valid according to its domain.'),
+                            string);
                     }
-                });
+                }
+            }
             if (fields.length > 5) {
                 fields.splice(5, fields.length);
                 fields.push('...');
@@ -1922,7 +1903,7 @@
                 return [];
             }
             var buttons = this.current_view.get_buttons();
-            selected_records.forEach(function(record) {
+            for (const record of selected_records) {
                 buttons = buttons.filter(function(button) {
                     if (button.attributes.type === 'instance') {
                         return false;
@@ -1931,7 +1912,7 @@
                         button.attributes.states || {});
                     return !(states.invisible || states.readonly);
                 });
-            });
+            }
             return buttons;
         },
         button: function(attributes) {
@@ -1963,9 +1944,8 @@
                     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(
+            for (const record of selected_records) {
+                const domain = record.expr_eval(
                     (attributes.states || {})).pre_validate || [];
                 prms.push(record.validate(fields, false, domain));
             }
@@ -1996,10 +1976,10 @@
                             this.context).then(function(changes) {
                             record.set_on_change(changes);
                             record.set_modified();
-                            record.group.root_group.screens.forEach(
-                                function(screen) {
-                                    screen.display();
-                            });
+                            for (const screen of
+                                record.group.root_group.screens) {
+                                screen.display();
+                            }
                         });
                     } else {
                         return record.save(false).then(function() {
@@ -2248,8 +2228,8 @@
                     return 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]);
+                        for (const id of selected_nodes[0]) {
+                            const new_record = this.group.get(id);
                             if (!new_record) {
                                 break;
                             } else {
diff -r 3e66fd9e064f -r 9efb7fa52876 src/session.js
--- a/src/session.js    Sun Apr 10 19:25:17 2022 +0200
+++ b/src/session.js    Sun Apr 10 19:28:21 2022 +0200
@@ -286,12 +286,12 @@
                 el = dialog.database_input;
             } else {
                 el = dialog.database_select;
-                databases.forEach(function(database) {
+                for (const database of databases) {
                     el.append(jQuery('<option/>', {
                         'value': database,
                         'text': database
                     }));
-                });
+                }
             }
             el.prop('readonly', databases.length == 1);
             el.show();
diff -r 3e66fd9e064f -r 9efb7fa52876 src/tab.js
--- a/src/tab.js        Sun Apr 10 19:25:17 2022 +0200
+++ b/src/tab.js        Sun Apr 10 19:28:21 2022 +0200
@@ -376,8 +376,7 @@
         if (attributes.context === undefined) {
             attributes.context = {};
         }
-        for (var i = 0; i < Sao.Tab.tabs.length; i++) {
-            var other = Sao.Tab.tabs[i];
+        for (const other of Sao.Tab.tabs) {
             if (other.compare(attributes)) {
                 tablist.find('a[href="#' + other.id + '"]')
                     .tab('show')[0].scrollIntoView();
@@ -609,18 +608,17 @@
                     });
 
                     var kw_plugins = [];
-                    Sao.Plugins.forEach(function(plugin) {
-                        plugin.get_plugins(screen.model.name).forEach(
-                            function(spec) {
-                                var name = spec[0],
-                                    func = spec[1],
-                                    keyword = spec[2] || 'action';
-                                if (keyword != menu_action[0]) {
-                                    return;
-                                }
+                    for (const plugin of Sao.Plugins) {
+                        for (const spec of plugin.get_plugins(
+                            screen.model.name)) {
+                            var name = spec[0],
+                                func = spec[1],
+                                keyword = spec[2] || 'action';
+                            if (keyword == menu_action[0]) {
                                 kw_plugins.push([name, func]);
-                            });
-                    });
+                            }
+                        }
+                    }
                     if (kw_plugins.length) {
                         menu.append(jQuery('<li/>', {
                             'role': 'separator',
@@ -861,12 +859,12 @@
                         return this.screen.search_filter(
                             this.screen.screen_container.search_entry.val())
                             .then(function() {
-                                this.screen.group.forEach(function(record) {
+                                for (const record of this.screen.group) {
                                     if (record.id == record_id) {
                                         this.screen.current_record = record;
                                         set_cursor = true;
                                     }
-                                }.bind(this));
+                                }
                                 return set_cursor;
                             }.bind(this));
                     }
@@ -975,15 +973,15 @@
             .then(function(data) {
                 data = data[0];
                 var message = '';
-                fields.forEach(function(field) {
-                    var key = field[0];
-                    var label = field[1];
-                    var value = data;
-                    var keys = key.split('.');
-                    var name = keys.splice(-1);
-                    keys.forEach(function(key) {
+                for (const field of fields) {
+                    const key = field[0];
+                    const label = field[1];
+                    let value = data;
+                    const keys = key.split('.');
+                    const name = keys.splice(-1);
+                    for (const key of keys) {
                         value = value[key + '.'] || {};
-                    });
+                    }
                     value = (value || {})[name] || '/';
                     if (value && value.isDateTime) {
                         value = Sao.common.format_datetime(
@@ -991,7 +989,7 @@
                             value);
                     }
                     message += label + ' ' + value + '\n';
-                });
+                }
                 message += Sao.i18n.gettext('Model: ') + 
this.screen.model.name;
                 Sao.common.message.run(message);
             }.bind(this));
@@ -1063,14 +1061,14 @@
         set_buttons_sensitive: function(revision) {
             if (!revision) {
                 var access = 
Sao.common.MODELACCESS.get(this.screen.model_name);
-                [['new_', access.create],
-                ['save', access.create || access.write],
-                ['delete_', access.delete],
-                ['copy', access.create],
-                ['import', access.create],
-                ].forEach(function(e) {
-                    var name = e[0];
-                    var access = e[1];
+                const accesses = new Map([
+                    ['new_', access.create],
+                    ['save', access.create || access.write],
+                    ['delete_', access.delete],
+                    ['copy', access.create],
+                    ['import', access.create],
+                ]);
+                for (const [name, access] of accesses) {
                     if (this.buttons[name]) {
                         this.buttons[name].prop('disabled', !access);
                     }
@@ -1078,17 +1076,17 @@
                         this.menu_buttons[name]
                             .toggleClass('disabled', !access);
                     }
-                }.bind(this));
+                }
             } else {
-                ['new_', 'save', 'delete_', 'copy', 'import'].forEach(
-                    function(name) {
-                        if (this.buttons[name]) {
-                            this.buttons[name].prop('disabled', true);
-                        }
-                        if (this.menu_buttons[name]) {
-                            this.menu_buttons[name].addClass('disabled');
-                        }
-                    }.bind(this));
+                for (const name of [
+                    'new_', 'save', 'delete_', 'copy', 'import']) {
+                    if (this.buttons[name]) {
+                        this.buttons[name].prop('disabled', true);
+                    }
+                    if (this.menu_buttons[name]) {
+                        this.menu_buttons[name].addClass('disabled');
+                    }
+                }
             }
         },
         attach: function(evt) {
@@ -1239,24 +1237,22 @@
             var window_ = new Sao.Window.Attachment(record, function() {
                 this.refresh_resources(true);
             }.bind(this));
-            files.forEach(function(file) {
-                Sao.common.get_file_data(file, function(data, filename) {
-                    window_.add_data(data, filename);
-                });
-            });
+            for (const file of files) {
+                Sao.common.get_file_data(file, window_.add_data);
+            }
             jQuery.when.apply(jQuery, uris).then(function() {
                 function empty(value) {
                     return Boolean(value);
                 }
-                for (var i = 0; i < arguments.length; i++) {
-                    arguments[i].split('\r\n')
+                for (const argument of arguments) {
+                    argument.split('\r\n')
                         .filter(empty)
                         .forEach(window_.add_uri, window_);
                 }
             });
             jQuery.when.apply(jQuery, texts).then(function() {
-                for (var i = 0; i < arguments.length; i++) {
-                    window_.add_text(arguments[i]);
+                for (const argument of arguments) {
+                    window_.add_text(argument);
                 }
             });
             if (evt.dataTransfer.items) {
@@ -1371,8 +1367,7 @@
                         .then(function(toolbars) {
                             var prints = toolbars.print.filter(is_report);
                             var emails = {};
-                            for (var i = 0; i < toolbars.emails.length; i++) {
-                                var email = toolbars.emails[i];
+                            for (const email of toolbars.emails) {
                                 emails[email.name] = email.id;
                             }
                             record.rec_name().then(function(rec_name) {
@@ -1476,9 +1471,9 @@
                 }
             }
             var buttons = ['print', 'relate', 'email', 'save', 'attach'];
-            buttons.forEach(function(button_id){
-                var button = this.buttons[button_id];
-                var can_be_sensitive = button._can_be_sensitive;
+            for (const button_id of buttons) {
+                const button = this.buttons[button_id];
+                let can_be_sensitive = button._can_be_sensitive;
                 if (can_be_sensitive === undefined) {
                     can_be_sensitive = true;
                 }
@@ -1494,7 +1489,7 @@
                     can_be_sensitive &= !this.screen.readonly;
                 }
                 set_sensitive(button_id, position && can_be_sensitive);
-            }.bind(this));
+            }
             set_sensitive('switch_', this.screen.number_of_views > 1);
             set_sensitive('delete_', this.screen.deletable);
             set_sensitive('previous', position > (this.screen.offset + 1));
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view.js
--- a/src/view.js       Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view.js       Sun Apr 10 19:28:21 2022 +0200
@@ -20,8 +20,7 @@
             this.state_widgets = [];
             var attributes = xml.children()[0].attributes;
             this.attributes = {};
-            for (var i = 0, len = attributes.length; i < len; i++) {
-                var attribute = attributes[i];
+            for (const attribute of attributes) {
                 this.attributes[attribute.name] = attribute.value;
             }
             screen.set_on_write(this.attributes.on_write);
@@ -102,8 +101,7 @@
         },
         _node_attributes: function(node) {
             var node_attrs = {};
-            for (var i = 0; i < node.attributes.length; i++) {
-                var attribute = node.attributes[i];
+            for (var attribute of node.attributes) {
                 node_attrs[attribute.name] = attribute.value;
             }
 
@@ -112,22 +110,24 @@
                 field = this.field_attrs[node_attrs.name] || {};
             }
 
-            ['readonly', 'homogeneous'].forEach(function(name) {
+            for (const name of ['readonly', 'homogeneous']) {
                 if (node_attrs[name]) {
                     node_attrs[name] = node_attrs[name] == 1;
                 }
-            });
-            ['yexpand', 'yfill', 'xexpand', 'xfill', 'colspan',
-                'position', 'height', 'width'].forEach(function(name) {
-                    if (node_attrs[name]) {
-                        node_attrs[name] = Number(node_attrs[name]);
-                    }
-            });
-            ['xalign', 'yalign'].forEach(function(name) {
+            }
+            for (const name of [
+                'yexpand', 'yfill',
+                'xexpand', 'xfill',
+                'colspan', 'position', 'height', 'width']) {
                 if (node_attrs[name]) {
                     node_attrs[name] = Number(node_attrs[name]);
                 }
-            });
+            }
+            for (const name of ['xalign', 'yalign']) {
+                if (node_attrs[name]) {
+                    node_attrs[name] = Number(node_attrs[name]);
+                }
+            }
 
             if (!jQuery.isEmptyObject(field)) {
                 if (!node_attrs.widget) {
@@ -141,17 +141,17 @@
                     node_attrs.help = field.help;
                 }
 
-                ['relation', 'domain', 'selection', 'string', 'states',
+                for (const name of [
+                    'relation', 'domain', 'selection', 'string', 'states',
                     'relation_field', 'views', 'invisible', 'add_remove',
                     'sort', 'context', 'size', 'filename', 'autocomplete',
                     'translate', 'create', 'delete', 'selection_change_with',
                     'schema_model', 'required', 'help_selection', 'help_field',
-                    'order', 'symbol', 'monetary',
-                ].forEach(function(name) {
-                        if ((name in field) && (!(name in node_attrs))) {
-                            node_attrs[name] = field[name];
-                        }
-                    });
+                    'order', 'symbol', 'monetary']) {
+                    if ((name in field) && (!(name in node_attrs))) {
+                        node_attrs[name] = field[name];
+                    }
+                }
             }
             return node_attrs;
         },
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view/calendar.js
--- a/src/view/calendar.js      Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view/calendar.js      Sun Apr 10 19:28:21 2022 +0200
@@ -5,9 +5,9 @@
 
     Sao.View.CalendarXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
         _parse_calendar: function(node, attributes) {
-            [].forEach.call(node.childNodes, function(child) {
+            for (const child of node.childNodes) {
                 this.parse(child);
-            }.bind(this));
+            }
 
             var view_week;
             if (this.view.screen.model.fields[attributes.dtstart]
@@ -138,10 +138,9 @@
                 model_access.write);
 
             var description = [];
-            for (var i = 1; i < this.fields.length; i++) {
+            for (const field of this.fields) {
                 description.push(
-                    this.screen.model.fields[this.fields[i]].get_client(
-                        record));
+                    this.screen.model.fields[field].get_client( record));
             }
             description = description.join('\n');
             if (date_start) {
@@ -191,9 +190,9 @@
             prm.then(function()  {
                 this.group.forEach(function(record) {
                     var record_promisses = [];
-                    this.fields.forEach(function(name) {
+                    for (const name of this.fields) {
                         record_promisses.push(record.load(name));
-                    });
+                    }
                     var prm = jQuery.when.apply(jQuery, record_promisses).then(
                         function(){
                             this.insert_event(record);
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view/form.js
--- a/src/view/form.js  Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view/form.js  Sun Apr 10 19:28:21 2022 +0200
@@ -42,9 +42,9 @@
             if (container) {
                 this._containers.push(container);
             }
-            [].forEach.call(node.childNodes, function(child) {
+            for (const child of node.childNodes) {
                 this.parse(child);
-            }.bind(this));
+            }
             if (container) {
                 this._containers.pop();
             }
@@ -308,13 +308,12 @@
             var record = this.record;
             var field;
             var depends;
-            var name;
             var promesses = [];
             if (record) {
                 // Force to set fields in record
                 // Get first the lazy one from the view to reduce number of 
requests
                 var field_names = new Set(this.get_fields());
-                for (name in record.model.fields) {
+                for (const name in record.model.fields) {
                     field = record.model.fields[name];
                     if (~field.views.has(this.view_id)) {
                         field_names.add(name);
@@ -322,14 +321,14 @@
                 }
 
                 var fields = [];
-                field_names.forEach(function(fname) {
+                for (const fname of field_names) {
                     field = record.model.fields[fname];
                     fields.push([
                         fname,
                         field.description.loading || 'eager' == 'eager',
                         field.views.size,
                     ]);
-                });
+                }
                 fields.sort(function(a, b) {
                     if (!a[1] && b[1]) {
                         return -1;
@@ -339,18 +338,15 @@
                         return a[2] - b[2];
                     }
                 });
-                fields.forEach(function(e) {
-                    var name = e[0];
+                for (const e of fields) {
+                    const name = e[0];
                     promesses.push(record.load(name));
-                });
-            }
-            var display = function(widget) {
-                widget.display();
-            };
+                }
+            }
             return jQuery.when.apply(jQuery,promesses)
                 .done(function() {
                     var record = this.record;
-                    for (name in this.widgets) {
+                    for (const name in this.widgets) {
                         var widgets = this.widgets[name];
                         field = null;
                         if (record) {
@@ -359,7 +355,9 @@
                         if (field) {
                             field.set_state(record);
                         }
-                        widgets.forEach(display);
+                        for (const widget of widgets) {
+                            widget.display();
+                        }
                     }
                 }.bind(this))
                 .done(function() {
@@ -378,14 +376,13 @@
         set_value: function() {
             var record = this.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);
+                        for (const widget of widgets) {
+                            widget.set_value(record, field);
+                        }
                     }
                 }
             }
@@ -404,8 +401,8 @@
         get modified() {
             for (var name in this.widgets) {
                 var widgets = this.widgets[name];
-                for (var i=0; i < widgets.length; i++) {
-                    if (widgets[i].modified) {
+                for (const widget of widgets) {
+                    if (widget.modified) {
                         return true;
                     }
                 }
@@ -594,7 +591,6 @@
             var widths = [];
             var col = this.col;
             var has_expand = false;
-            var i, j;
 
             var parent_max_width = 0.9;
             this.el.parents('td').each(function() {
@@ -607,7 +603,7 @@
             var get_xexpands = function(row) {
                 row = jQuery(row);
                 var xexpands = [];
-                i = 0;
+                let i = 0;
                 row.children().map(function() {
                     var cell = jQuery(this);
                     var colspan = Math.min(Number(cell.attr('colspan')), col);
@@ -638,19 +634,19 @@
                     return b.length - a.length;
                 }
             });
-            rows.forEach(function(row) {
+            for (let row of rows) {
                 row = jQuery(row);
                 var xexpands = get_xexpands(row);
-                var width = 100 / xexpands.length;
-                xexpands.forEach(function(e) {
+                const width = 100 / xexpands.length;
+                for (const e of xexpands) {
                     var cell = e[0];
-                    i = e[1];
-                    var colspan = Math.min(Number(cell.attr('colspan')), col);
+                    let i = e[1];
+                    const colspan = Math.min(Number(cell.attr('colspan')), 
col);
                     var current_width = 0;
-                    for (j = 0; j < colspan; j++) {
+                    for (let j = 0; j < colspan; j++) {
                         current_width += widths[i + j] || 0;
                     }
-                    for (j = 0; j < colspan; j++) {
+                    for (let j = 0; j < colspan; j++) {
                         if (!current_width) {
                             widths[i + j] = width / colspan;
                         } else if (current_width > width) {
@@ -663,22 +659,22 @@
                             }
                         }
                     }
-                });
+                }
                 if (!jQuery.isEmptyObject(xexpands)) {
                     has_expand = true;
                 }
-            });
-            rows.forEach(function(row) {
+            }
+            for (let row of rows) {
                 row = jQuery(row);
-                i = 0;
-                row.children().map(function() {
-                    var cell = jQuery(this);
-                    var colspan = Math.min(Number(cell.attr('colspan')), col);
+                let i = 0;
+                for (let cell of row.children()) {
+                    cell = jQuery(cell);
+                    const 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++) {
+                        let width = 0;
+                        for (let j = 0; j < colspan; j++) {
                             width += widths[i + j] || 0;
                         }
                         cell.css('width', width + '%');
@@ -704,8 +700,8 @@
                         }
                     }
                     i += colspan;
-                });
-            });
+                }
+            }
             if (has_expand &&
                 (!this.el.closest('td').length ||
                     this.el.closest('td').hasClass('xexpand'))) {
@@ -1400,7 +1396,7 @@
             }.bind(this));
         },
         write: function(widget, dialog) {
-            this.languages.forEach(function(lang) {
+            for (const lang of this.languages) {
                 var input = jQuery('[data-lang-id=' + lang.id + ']');
                 if (!input.attr('readonly')) {
                     var current_language = widget.model.session.context.
@@ -1421,7 +1417,7 @@
                     };
                     Sao.rpc(args, widget.model.session, false);
                 }
-            }.bind(this));
+            }
             widget.record.cancel();
             widget.view.display();
             this.close(dialog);
@@ -1564,11 +1560,11 @@
                 } else {
                     selection = [];
                 }
-                selection.forEach(function(e) {
+                for (const e of selection) {
                     jQuery('<option/>', {
                         'value': e
                     }).appendTo(this.datalist);
-                }.bind(this));
+                }
             }
 
             // Set size
@@ -2091,13 +2087,13 @@
         set_selection: function(selection, help) {
             var select = this.select;
             select.empty();
-            selection.forEach(function(e) {
+            for (const e of selection) {
                 select.append(jQuery('<option/>', {
                     'value': JSON.stringify(e[0]),
                     'text': e[1],
                     'title': help[e[0]],
                 }));
-            });
+            }
         },
         display_update_selection: function() {
             var record = this.record;
@@ -2109,8 +2105,8 @@
                 }
                 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) {
+                for (const option of this.selection) {
+                    if (option[0] === value) {
                         found = true;
                         break;
                     }
@@ -2883,13 +2879,13 @@
         set_selection: function(selection, help) {
             var select = this.select;
             select.empty();
-            selection.forEach(function(e) {
+            for (const e of selection) {
                 select.append(jQuery('<option/>', {
                     'value': e[0],
                     'text': e[1],
                     'title': help[e[0]],
                 }));
-            });
+            }
         },
         get modified() {
             if (this.record && this.field) {
@@ -3322,10 +3318,9 @@
             }
         },
         _sequence: function() {
-            for (var i=0, len = this.screen.views.length; i < len; i++) {
-                var view = this.screen.views[i];
+            for (const view of this.screen.views) {
                 if (view.view_type == 'tree') {
-                    var sequence = view.attributes.sequence;
+                    const sequence = view.attributes.sequence;
                     if (sequence) {
                         return sequence;
                     }
@@ -4556,9 +4551,9 @@
         },
         translate_dialog: function(languages) {
             var options = {};
-            languages.forEach(function(language) {
+            for (const language of languages) {
                 options[language.name] = language.code;
-            });
+            }
             Sao.common.selection(Sao.i18n.gettext("Choose a language"), 
options)
             .done(function(language) {
                 window.open(this.uri(language), '_blank', 
'noreferrer,noopener');
@@ -4709,7 +4704,7 @@
                 .then(function(new_names) {
                     this.send_modified();
                     var focus = false;
-                    new_names.forEach(function(name) {
+                    for (const name of new_names) {
                         if (!(name in this.fields)) {
                             this.add_line(name);
                             if (!focus) {
@@ -4717,7 +4712,7 @@
                                 focus = true;
                             }
                         }
-                    }.bind(this));
+                    }
                 }.bind(this));
         },
         remove: function(key, modified=true) {
@@ -5023,13 +5018,13 @@
                     return a[1].localeCompare(b[1]);
                 });
             }
-            selection.forEach(function(e) {
+            for (const e of selection) {
                 select.append(jQuery('<option/>', {
                     'value': JSON.stringify(e[0]),
                     'text': e[1],
                     'title': this.definition.help_selection[e[0]],
                 }));
-            }.bind(this));
+            }
         },
         set_readonly: function(readonly) {
             this._readonly = readonly;
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view/graph.js
--- a/src/view/graph.js Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view/graph.js Sun Apr 10 19:28:21 2022 +0200
@@ -12,8 +12,7 @@
         },
         _node_attributes: function(node) {
             var node_attrs = {};
-            for (var i = 0, len = node.attributes.length; i < len; i++) {
-                var attribute = node.attributes[i];
+            for (const attribute of node.attributes) {
                 node_attrs[attribute.name] = attribute.value;
             }
             if (node_attrs.name) {
@@ -25,9 +24,9 @@
             return node_attrs;
         },
         _parse_graph: function(node, attributes) {
-            [].forEach.call(node.childNodes, function(child) {
+            for (const child of node.childNodes) {
                 this.parse(child);
-            }.bind(this));
+            }
             var Widget = Sao.View.GraphXMLViewParser.WIDGETS[
                 attributes.type || 'vbar'];
             var widget = new Widget(this.view, this._xfield, this._yfields);
@@ -35,13 +34,13 @@
             this.view.widgets.root = widget;
         },
         _parse_x: function(node, attributes) {
-            for (var i = 0; i < node.children.length; i++) {
-                this._xfield = this._node_attributes(node.children[i]);
+            for (const child of node.children) {
+                this._xfield = this._node_attributes(child);
             }
         },
         _parse_y: function(node, attributes) {
-            for (var i = 0; i < node.children.length; i++) {
-                this._yfields.push(this._node_attributes(node.children[i]));
+            for (const child of node.children) {
+                this._yfields.push(this._node_attributes(child));
             }
         }
     });
@@ -142,16 +141,13 @@
                     }
                 }.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 (const fname of fields2load) {
+                    prms.push(record.load(fname));
+                }
                 r_prms.push(
                         jQuery.when.apply(jQuery, prms).then(set_data(i)));
             }
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view/list_form.js
--- a/src/view/list_form.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view/list_form.js     Sun Apr 10 19:28:21 2022 +0200
@@ -79,11 +79,9 @@
             return frame;
         },
         get selected_records() {
-            var view_form, records = [];
-            var frame;
-            for (var i = 0; i < this._view_forms.length; i++) {
-                view_form = this._view_forms[i];
-                frame = view_form.el.parent();
+            var records = [];
+            for (const view_form of this._view_forms) {
+                const frame = view_form.el.parent();
                 if (frame.hasClass('list-group-item-selected')) {
                     records.push(view_form.record);
                 }
@@ -116,10 +114,9 @@
                 to = tmp;
             }
 
-            var select_form = function(form) {
+            for (const form of this._view_forms.slice(from, to + 1)) {
                 form.el.parent().addClass('list-group-item-selected');
-            };
-            this._view_forms.slice(from, to + 1).forEach(select_form);
+            }
         },
         _select_row: function(event_) {
             var current_view_form;
@@ -127,9 +124,9 @@
             var view_form = this._view_forms[view_form_idx];
 
             if (event_.shiftKey) {
-                for (var i=0; i < this._view_forms.length; i++) {
-                    if (this._view_forms[i].record === this.record) {
-                        current_view_form = this._view_forms[i];
+                for (const other_view_form of this._view_forms) {
+                    if (other_view_forms.record === this.record) {
+                        current_view_form = other_view_form;
                         break;
                     }
                 }
diff -r 3e66fd9e064f -r 9efb7fa52876 src/view/tree.js
--- a/src/view/tree.js  Sun Apr 10 19:25:17 2022 +0200
+++ b/src/view/tree.js  Sun Apr 10 19:28:21 2022 +0200
@@ -5,11 +5,11 @@
 
     if ('IntersectionObserver' in window) {
         var moreObserver = new IntersectionObserver(function(entries, 
observer) {
-            entries.forEach(function(entry) {
+            for (const entry of entries) {
                 if (entry.isIntersecting) {
                     jQuery(entry.target).trigger('click');
                 }
-            });
+            }
         }, {
             rootMargin: '0px 0px 50px 0px',
         });
@@ -53,9 +53,9 @@
 
     Sao.View.TreeXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
         _parse_tree: function(node, attributes) {
-            [].forEach.call(node.childNodes, function(child) {
+            for (const child of node.childNodes) {
                 this.parse(child);
-            }.bind(this));
+            }
         },
         _parse_field: function(node, attributes) {
             var name = attributes.name;
@@ -81,19 +81,15 @@
             if ('icon' in attributes) {
                 column.prefixes.push(new Sao.View.Tree.Affix(attributes));
             }
-            var affix, affix_attributes;
-            var affixes = node.childNodes;
-            for (var i = 0; i < affixes.length; i++) {
-                affix = affixes[i];
-                affix_attributes = {};
-                for (var j = 0, len = affix.attributes.length; j < len; j++) {
-                    var attribute = affix.attributes[j];
+            for (const affix of node.childNodes) {
+                let affix_attributes = {};
+                for (const attribute of affix.attributes) {
                     affix_attributes[attribute.name] = attribute.value;
                 }
                 if (!affix_attributes.name) {
                     affix_attributes.name = name;
                 }
-                var list;
+                let list;
                 if (affix.tagName == 'prefix') {
                     list = column.prefixes;
                 } else {
@@ -224,7 +220,7 @@
                     }.bind(this));
             }
 
-            this.columns.forEach(function(column, i) {
+            for (const column of this.columns) {
                 col = jQuery('<col/>', {
                     'class': column.attributes.widget,
                 }).appendTo(this.colgroup);
@@ -273,7 +269,7 @@
                     sum_row.append(total_cell);
                     column.footers.push(total_cell);
                 }
-            }, this);
+            }
             this.tbody = jQuery('<tbody/>');
             this.table.append(this.tbody);
 
@@ -319,13 +315,13 @@
                 column.set_visible(jQuery(evt.delegateTarget).prop('checked'));
                 this.save_optional();
                 this.display();
-                this.rows.forEach(function(row) {
+                for (const row of this.rows) {
                     row.update_visible();
-                });
+                }
             }.bind(this);
             var menu = evt.data;
             menu.empty();
-            this.optionals.forEach(function(optional) {
+            for (const optional of this.optionals) {
                 menu.append(jQuery('<li/>', {
                     'role': 'presentation',
                 }).append(jQuery('<a/>', {
@@ -337,13 +333,13 @@
                     'checked': optional.get_visible(),
                 }).change(optional, toggle))
                     .append(' ' + optional.attributes.string))));
-            });
+            }
         },
         save_optional: function(store=true) {
             var fields = {};
-            this.optionals.forEach(function(column) {
+            for (const optional of this.optionals) {
                 fields[column.attributes.name] = !column.get_visible();
-            });
+            }
             if (store) {
                 var tree_optional_model = new Sao.Model(
                     'ir.ui.view_tree_optional');
@@ -416,13 +412,13 @@
         sort_model: function(e){
             var column = e.data;
             var arrow = column.arrow;
-            this.columns.forEach(function(col) {
+            for (const col of this.columns) {
                 if (col.arrow){
                     if (col != column && col.arrow.attr('src')) {
                         col.arrow.attr('src', '');
                     }
                 }
-            });
+            }
             this.screen.order = this.screen.default_order;
             if (arrow.data('order') == 'ASC') {
                 arrow.data('order', 'DESC');
@@ -443,11 +439,11 @@
                 this.screen.order = [[column.attributes.name, 'ASC']];
             }
             var unsaved_records = [];
-            this.group.forEach(function(unsaved_record) {
-                    if (unsaved_record.id < 0) {
-                        unsaved_records = unsaved_record.group;
+            for (const unsaved_record of this.group) {
+                if (unsaved_record.id < 0) {
+                    unsaved_records = unsaved_record.group;
                 }
-            });
+            }
             var search_string = this.screen.screen_container.get_text();
             if ((!jQuery.isEmptyObject(unsaved_records)) ||
                     (this.screen.search_count == this.group.length) ||
@@ -674,12 +670,12 @@
 
                 row.parent_ = parent_row;
                 row.record.group = dest_group;
-                dest_rows.slice(dest_position).forEach(function(r) {
+                for (const r of dest_rows.slice(dest_position)) {
                     r.reset_path();
-                });
-                origin_rows.slice(origin_position).forEach(function(r) {
+                }
+                for (const r of origin_rows.slice(origin_position)) {
                     r.reset_path();
-                });
+                }
 
                 var selected = this.get_selected_paths();
                 row.redraw(selected);
@@ -700,11 +696,11 @@
         },
         get_buttons: function() {
             var buttons = [];
-            this.columns.forEach(function(column) {
+            for (const column of this.columns) {
                 if (column instanceof Sao.View.Tree.ButtonColumn) {
                     buttons.push(column);
                 }
-            });
+            }
             return buttons;
         },
         display: function(selected, expanded) {
@@ -716,11 +712,10 @@
                 selected = this.get_selected_paths();
                 if (this.selection.prop('checked') &&
                     !this.selection.prop('indeterminate')) {
-                    this.screen.group.slice(
-                        this.rows.length, this.display_size)
-                        .forEach(function(record) {
-                            selected.push([record.id]);
-                        });
+                    for (const record of this.screen.group.slice(
+                        this.rows.length, this.display_size)) {
+                        selected.push([record.id]);
+                    }
                 } else {
                     if (current_record) {
                         var current_path = current_record.get_path(this.group);
@@ -745,12 +740,11 @@
 
             var group_records = function(group, root) {
                 var records = [];
-                for (var i = 0; i < group.length; i++) {
-                    var record = group[i];
+                for (const record of group) {
                     records.push(record);
-                    var path = root.concat([record.id]);
+                    let path = root.concat([record.id]);
                     if (Sao.common.contains(expanded, path)) {
-                        var children = record.field_get_client(
+                        const children = record.field_get_client(
                             this.children_field);
                         Array.prototype.push.apply(
                             records, group_records(children, path));
@@ -761,8 +755,7 @@
 
             var row_records = function(rows) {
                 var records = [];
-                for (var i = 0; i < rows.length; i++) {
-                    var row = rows[i];
+                for (const row of rows) {
                     records.push(row.record);
                     if (row.is_expanded()) {
                         Array.prototype.push.apply(
@@ -807,7 +800,7 @@
             var min_width = [];
             var tree_column_optional = (
                 Sao.Screen.tree_column_optional[this.view_id] || {});
-            this.columns.forEach(function(column) {
+            for (const column of this.columns) {
                 visible_columns += 1;
                 var name = column.attributes.name;
                 if (!name) {
@@ -877,7 +870,7 @@
                     column.col.css('width', c_width);
                     column.col.show();
                 }
-            }.bind(this));
+            }
             if (this.children_field) {
                 this.columns.every(function(column) {
                     if (column.col.hasClass('draggable-handle') ||
@@ -960,7 +953,8 @@
             }
             // The rows are added to tbody after being rendered
             // to minimize browser reflow
-            var add_row = function(record, pos, group) {
+            for (const record of this.group.slice(
+                this.rows.length, this.display_size)) {
                 var RowBuilder;
                 if (this.editable) {
                     RowBuilder = Sao.View.Tree.RowEditable;
@@ -968,9 +962,7 @@
                     RowBuilder = Sao.View.Tree.Row;
                 }
                 this.rows.push(new RowBuilder(this, record, this.rows.length));
-            };
-            this.group.slice(this.rows.length, this.display_size).forEach(
-                    add_row.bind(this));
+            }
         },
         redraw: function(selected, expanded) {
             return redraw_async(this.rows, selected, expanded).then(function() 
{
@@ -989,7 +981,7 @@
             // TODO update_children
         },
         update_sum: function() {
-            this.sum_widgets.forEach(function(sum_widget, column) {
+            for (const [column, sum_widget] of this.sum_widgets) {
                 var name = column.attributes.name;
                 var selected_records = this.selected_records;
                 var aggregate = '-';
@@ -1059,7 +1051,7 @@
                 sum_value.text(aggregate);
                 sum_value.parent().attr(
                     'title', sum_label.text() + ' ' + sum_value.text());
-            }.bind(this));
+            }
         },
         get selected_records() {
             if (this.selection_mode == Sao.common.SELECTION_NONE) {
@@ -1075,10 +1067,9 @@
             this.rows.forEach(add_record);
             if (this.selection.prop('checked') &&
                     !this.selection.prop('indeterminate')) {
-                this.group.slice(this.rows.length)
-                    .forEach(function(record) {
-                        records.push(record);
-                    });
+                for (const record of this.group.slice(this.rows.length)) {
+                    records.push(record);
+                }
             }
             return records;
         },
@@ -1433,8 +1424,8 @@
         reset_path: function() {
             this._group_position = null;
             this._path = null;
-            for (var i=0; i < this.rows.length; i++) {
-                this.rows[i].reset_path();
+            for (const row of this.rows) {
+                row.reset_path();
             }
         },
         is_expanded: function() {
@@ -1581,15 +1572,14 @@
             }
 
             function apply_visual(el, visual) {
-                ['muted', 'success', 'warning', 'danger'].forEach(
-                    function(name) {
-                        var klass = name == 'muted' ? 'text-muted' : name;
-                        if (name == visual) {
-                            el.addClass(klass);
-                        } else {
-                            el.removeClass(klass);
-                        }
-                    });
+                for (const name of ['muted', 'success', 'warning', 'danger']) {
+                    var klass = name == 'muted' ? 'text-muted' : name;
+                    if (name == visual) {
+                        el.addClass(klass);
+                    } else {
+                        el.removeClass(klass);
+                    }
+                }
             }
 
             if (this._drawed_record !== this.record.identity) {
@@ -1742,11 +1732,11 @@
                 }.bind(this));
         },
         collapse_children: function() {
-            this.rows.forEach(function(row, pos, rows) {
+            for (const row of this.rows) {
                 row.collapse_children();
                 var node = row.el[0];
                 node.parentNode.removeChild(node);
-            });
+            }
             this.rows = [];
         },
         expand_children: function(selected, expanded) {
@@ -2396,7 +2386,7 @@
         set_visible: function(visible) {
             var cells = this.footers.slice();
             cells.push(this.header);
-            cells.forEach(function(cell) {
+            for (const cell of cells) {
                 if (visible) {
                     cell.show();
                     cell.removeClass('invisible');
@@ -2404,7 +2394,7 @@
                     cell.hide();
                     cell.addClass('invisible');
                 }
-            });
+            }
         },
         get_visible: function() {
             return !this.header.hasClass('invisible');
@@ -2529,10 +2519,10 @@
             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) {
+                for (const option of this.selection) {
+                    if (option[0] === value) {
                         found = true;
-                        text = this.selection[i][1];
+                        text = option[1];
                         break;
                     }
                 }
@@ -2569,9 +2559,9 @@
         update_text: function(cell, record) {
             this.update_selection(record, function() {
                 var values = this.field.get_eval(record).map(function(value) {
-                    for (var i = 0; i < this.selection.length; i++) {
-                        if (this.selection[i][0] === value) {
-                            return this.selection[i][1];
+                    for (const option of this.selection) {
+                        if (option[0] === value) {
+                            return option[1];
                         }
                     }
                     return '';
@@ -2609,9 +2599,9 @@
                     name = value[1];
                 }
                 if (model) {
-                    for (var i = 0, len = this.selection.length; i < len; i++) 
{
-                        if (this.selection[i][0] === model) {
-                            model = this.selection[i][1];
+                    for (const option of this.selection) {
+                        if (option[0] === model) {
+                            model = option[1];
                             break;
                         }
                     }
@@ -2852,7 +2842,7 @@
         set_visible: function(visible) {
             var cells = this.footers.slice();
             cells.push(this.header);
-            cells.forEach(function(cell) {
+            for (const cell of cells) {
                 if (visible) {
                     cell.show();
                     cell.removeClass('invisible');
@@ -2860,7 +2850,7 @@
                     cell.hide();
                     cell.addClass('invisible');
                 }
-            });
+            }
         },
         get_visible: function() {
             return !this.header.hasClass('invisible');
diff -r 3e66fd9e064f -r 9efb7fa52876 src/window.js
--- a/src/window.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/window.js     Sun Apr 10 19:28:21 2022 +0200
@@ -412,8 +412,8 @@
                 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]);
+                    for (const record of result) {
+                        ids.push(record[0]);
                     }
                     this.screen.group.load(ids, true);
                     prm = this.screen.display();
@@ -698,13 +698,13 @@
             var prm = jQuery.when();
             if (result) {
                 var unread = this.screen.group.model.fields.unread;
-                this.screen.group.forEach(function(record) {
+                for (const record of this.screen.group) {
                     if (record.get_loaded() || record.id < 0) {
                         if (!record.modified_fields.unread) {
                             unread.set_client(record, false);
                         }
                     }
-                }.bind(this));
+                }
                 prm = this.screen.save_current();
             }
             if (this.note_callback) {
@@ -978,7 +978,7 @@
                 value: null,
                 text: ''
             }));
-            revisions.forEach(function(revision) {
+            for (let revision of revisions) {
                 var name = revision[2];
                 revision = revision[0];
                 this.select.append(jQuery('<option/>', {
@@ -986,7 +986,7 @@
                     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();
@@ -1252,10 +1252,10 @@
                 'id': 'input-encoding'
             });
 
-            for(var i=0; i < ENCODINGS.length; i++) {
+            for (const encoding of ENCODINGS) {
                 jQuery('<option/>', {
-                    'val': ENCODINGS[i]
-                }).append(ENCODINGS[i]).appendTo(this.el_csv_encoding);
+                    'val': encoding,
+                }).append(encoding).appendTo(this.el_csv_encoding);
             }
 
             var enc = 'utf-8';
@@ -1369,7 +1369,7 @@
             prefix_field = prefix_field || '';
             prefix_name = prefix_name || '';
 
-            Object.keys(fields).forEach(function(field) {
+            for (const field of Object.keys(fields)) {
                 if(!fields[field].readonly || field == 'id') {
                     var name = fields[field].string || field;
                     name = prefix_name + name;
@@ -1393,7 +1393,7 @@
                         node.children = {};
                     }
                 }
-            }.bind(this));
+            }
         },
         children_expand: function(node) {
             if (jQuery.isEmptyObject(node.children) && node.relation) {
@@ -1534,7 +1534,7 @@
             Sao.Window.Export._super.init.call(this,
                 Sao.i18n.gettext('CSV Export: %1',name));
             var fields = this.screen.model.fields;
-            names.forEach(function(name) {
+            for (const name of names) {
                 var type = fields[name].description.type;
                 if (type == 'selection') {
                     this.sel_field(name + '.translated');
@@ -1544,7 +1544,7 @@
                 } else {
                     this.sel_field(name);
                 }
-            }.bind(this));
+            }
 
             this.predef_exports = {};
             this.fill_predefwin();
@@ -1732,7 +1732,7 @@
             prefix_field = prefix_field || '';
             prefix_name = prefix_name || '';
 
-            Object.keys(fields).forEach(function(name) {
+            for (const name of Object.keys(fields)) {
                 var field = fields[name];
                 var string = field.string || name;
                 var items = [{ name: name, field: field, string: string }];
@@ -1756,7 +1756,7 @@
                     });
                 }
 
-                items.forEach(function(item) {
+                for (const item of items) {
                     var path = prefix_field + item.name;
                     var long_string = prefix_name + item.string;
 
@@ -1773,8 +1773,8 @@
                     if (item.name.indexOf('.') == -1 && item.field.relation) {
                         node.children = {};
                     }
-                }.bind(this));
-            }.bind(this));
+                }
+            }
         },
         children_expand: function(node) {
             if (jQuery.isEmptyObject(node.children) && node.relation) {
@@ -1795,12 +1795,12 @@
                     [['resource', '=', this.screen.model_name]], 0, null, null,
                     ['name', 'export_fields.name'], {}],
             }, this.session).done(function(exports) {
-                exports.forEach(function(export_) {
+                for (const export_ of exports) {
                     this.predef_exports[export_.id] = export_['export_fields.']
                         .map(function(field) {return field.name;});
                     this.add_to_predef(export_.id, export_.name);
                     this.predef_exports_list.children('li').first().focus();
-                }.bind(this));
+                }
             }.bind(this));
         },
         add_to_predef: function(id, name) {
@@ -1825,8 +1825,8 @@
         addreplace_predef: function() {
             var fields = [];
             var selected_fields = this.fields_selected.children('li');
-            for(var i=0; i<selected_fields.length; i++) {
-                fields.push(selected_fields[i].getAttribute('path'));
+            for (const field of selected_fields) {
+                fields.push(field.getAttribute('path'));
             }
             if(fields.length === 0) {
                 return;
@@ -1902,7 +1902,7 @@
         },
         sel_predef: function(export_id) {
             this.fields_selected.empty();
-            this.predef_exports[export_id].forEach(function(name) {
+            for (const name of this.predef_exports[export_id]) {
                 if (!(name in this.fields)) {
                     var fields = this.fields_model;
                     var prefix = '';
@@ -1913,7 +1913,7 @@
                     return;
                 }
                 this.sel_field(name);
-            }.bind(this));
+            }
         },
         _traverse: function(fields, prefix, parents, i) {
             var field, item;
@@ -2054,11 +2054,11 @@
                             this.screen.offset / 
this.screen.limit).toString()]);
                 }
                 if (this.screen.order) {
-                    this.screen.order.forEach(function(expr) {
+                    for (const expr of this.screen.order) {
                         query_string.push(['o', expr.map(function(e) {
                             return e;
                         }).join(',')]);
-                    });
+                    }
                 }
             }
             query_string.splice(
@@ -2187,9 +2187,9 @@
             this.to = add_group('to', Sao.i18n.gettext('To:'), true);
             this.cc = add_group('cc', Sao.i18n.gettext('Cc:'));
             this.bcc = add_group('bcc', Sao.i18n.gettext('Bcc:'));
-            [this.to, this.cc, this.bcc].forEach(function(input) {
+            for (const input of [this.to, this.cc, this.bcc]) {
                 new Sao.Window.EmailEntry(input, this.record.model.session);
-            }.bind(this));
+            }
             this.subject = add_group(
                 'subject', Sao.i18n.gettext('Subject:'), true);
 
@@ -2215,8 +2215,7 @@
                 'text': Sao.i18n.gettext("Reports"),
             }).appendTo(print_frame);
             this.print_actions = {};
-            for (var i = 0; i < prints.length; i++) {
-                var print = prints[i];
+            for (const print of prints) {
                 var print_check = jQuery('<input/>', {
                     'type': 'checkbox',
                 });
@@ -2251,12 +2250,12 @@
                     ],
                     0, null, null, ['rec_name'], record.get_context()],
             }, record.model.session).then(function(attachments) {
-                attachments.forEach(function(attachment) {
+                for (const attachment of attachments) {
                     this.attachments.append(jQuery('<option/>', {
                         'value': JSON.stringify(attachment.id),
                         'text': attachment.rec_name,
                     }));
-                }.bind(this));
+                }
             }.bind(this))
             .fail(function() {
                 Sao.Logger.error(
diff -r 3e66fd9e064f -r 9efb7fa52876 src/wizard.js
--- a/src/wizard.js     Sun Apr 10 19:25:17 2022 +0200
+++ b/src/wizard.js     Sun Apr 10 19:28:21 2022 +0200
@@ -86,7 +86,7 @@
 
                     var execute_actions = function execute_actions() {
                         if (result.actions) {
-                            result.actions.forEach(function(action) {
+                            for (const action of result.actions) {
                                 var context = jQuery.extend({}, this.context);
                                 // Remove wizard keys added by run
                                 delete context.active_id;
@@ -95,7 +95,7 @@
                                 delete context.action_id;
                                 Sao.Action.execute(
                                     action[0], action[1], context);
-                            }.bind(this));
+                            }
                         }
                     }.bind(this);
 
@@ -169,9 +169,9 @@
             }
         },
         update: function(view, buttons) {
-            buttons.forEach(function(button) {
+            for (const button of buttons) {
                 this._get_button(button);
-            }.bind(this));
+            }
             if (this.screen) {
                 this.screen.windows.splice(
                     this.screen.windows.indexOf(this), 1);
@@ -348,11 +348,9 @@
             if (view.view_type == 'form') {
                 expand = false;
                 var fields = view.get_fields();
-                for (var i = 0; i < fields.length; i++) {
-                    var name = fields[i];
+                for (const name of fields) {
                     var widgets = view.widgets[name];
-                    for (var j = 0; j < widgets.length; j++) {
-                        var widget = widgets[j];
+                    for (const widget of widgets) {
                         if (widget.expand) {
                             expand = true;
                             break;

Reply via email to