[REF] *: Replace underscore function by native JS

Replace _.isNumber(), _.filter(), _.reject(), _.unique(), _.indexOf(), _.lastIndexOf(), _.findIndex(), _.range()
_.keys(), _.values(), _.str.sprintf() and some _.each()

closes odoo/odoo#118003

Signed-off-by: Géry Debongnie <ged@odoo.com>
This commit is contained in:
Pulinckx Pierre (PIPU)
2023-04-13 16:40:11 +02:00
parent 4153c33fc9
commit 614de86989
93 changed files with 490 additions and 404 deletions
@@ -3,6 +3,7 @@
import { _t } from "web.core";
import Widget from "web.Widget";
import { computeScore } from "@auth_password_policy/password_policy";
import { sprintf } from "@web/core/utils/strings";
export default Widget.extend({
tagName: "meter",
@@ -27,7 +28,7 @@ export default Widget.extend({
);
this.el.setAttribute(
"title",
_.str.sprintf(helpMessage, String(this._required) || _t("no requirements"))
sprintf(helpMessage, String(this._required) || _t("no requirements"))
);
return this._super().then(function () {});
},
@@ -12,7 +12,7 @@ BasicModel.include({
_fetchSpecialAttendeeStatus: function (record, fieldName) {
var context = record.getContext({fieldName: fieldName});
var attendeeIDs = record.data[fieldName] ? this.localData[record.data[fieldName]].res_ids : [];
var meetingID = _.isNumber(record.res_id) ? record.res_id : false;
var meetingID = Number.isFinite(record.res_id) ? record.res_id : false;
return this._rpc({
model: 'res.partner',
method: 'get_attendee_detail',
@@ -7,7 +7,7 @@ import core from "web.core";
import "@crm/js/tours/crm";
var _t = core._t;
var DragOppToWonStepIndex = _.findIndex(registry.category("web_tour.tours").get("crm_tour").steps, function (step) {
var DragOppToWonStepIndex = registry.category("web_tour.tours").get("crm_tour").steps.findIndex(step => {
return (step.id === 'drag_opportunity_to_won_step');
});
@@ -2,12 +2,13 @@
import { registry } from "@web/core/registry";
import { Component, xml } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
const actionRegistry = registry.category("actions");
class QRModalComponent extends Component {
setup() {
this.url = _.str.sprintf(
this.url = sprintf(
"/report/barcode/?barcode_type=QR&value=%s&width=256&height=256&humanreadable=1",
this.props.action.params.url);
}
@@ -181,7 +181,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
? [this.messaging.publicLivechatGlobal.publicLivechat.operator]
: [];
const sortedTypingMembers = _.sortBy(typingMembers, function (member) {
return _.indexOf(typingPartnerIDs, member.id);
return typingPartnerIDs.indexOf(member.id);
});
const displayableTypingMembers = sortedTypingMembers.slice(0, 3);
@@ -330,9 +330,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
if (!_.contains(this._typingPartnerIDs, partnerID)) {
return;
}
this._typingPartnerIDs = _.reject(this._typingPartnerIDs, function (id) {
return id === partnerID;
});
this._typingPartnerIDs = this._typingPartnerIDs.filter((id) => id !== partnerID);
this._warnUpdatedTypingPartners();
},
@@ -92,7 +92,7 @@ const Feedback = Widget.extend({
_onClickSend() {
this.$(".o_livechat_rating_reason").hide();
this._showThanksMessage();
if (_.isNumber(this.rating)) {
if (Number.isFinite(this.rating)) {
this._sendFeedback(this.$("textarea").val());
}
},
@@ -4,10 +4,10 @@
import { registry } from "@web/core/registry";
let account_tour = registry.category("web_tour.tours").get("account_tour");
// Remove the step suggesting to change the name as it is done another way (document number)
account_tour.steps = _.filter(account_tour.steps, step => step.trigger != "input[name=name]");
account_tour.steps = account_tour.steps.filter(step => step.trigger != "input[name=name]");
// Configure the AFIP Responsibility
let partner_step_idx = _.findIndex(account_tour.steps, step => step.trigger == 'div[name=partner_id] input');
let partner_step_idx = account_tour.steps.findIndex(step => step.trigger == 'div[name=partner_id] input');
account_tour.steps.splice(partner_step_idx + 2, 0, {
// FIXME WOWL: this selector needs to work in both legacy and non-legacy views
trigger: "div[name=l10n_ar_afip_responsibility_type_id] input",
+2 -5
View File
@@ -3,8 +3,8 @@
import { PosGlobalState, Order, Orderline } from "@point_of_sale/js/models";
import core from "web.core";
import { patch } from "@web/core/utils/patch";
import { sprintf } from "@web/core/utils/strings";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
var _t = core._t;
patch(PosGlobalState.prototype, "l10n_fr_pos_cert.PosGlobalState", {
@@ -13,10 +13,7 @@ patch(PosGlobalState.prototype, "l10n_fr_pos_cert.PosGlobalState", {
if (!this.company.country) {
this.env.services.popup.add(ErrorPopup, {
title: _t("Missing Country"),
body: _.str.sprintf(
_t("The company %s doesn't have a country set."),
this.company.name
),
body: sprintf(_t("The company %s doesn't have a country set."), this.company.name),
});
return false;
}
@@ -4,6 +4,7 @@
import ajax from "web.ajax";
import core from "web.core";
import "web.dom_ready";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
@@ -45,7 +46,7 @@ if ($('.o_unsubscribe_form').length) {
var unsubscribed_list = $("input[name='unsubscribed_list']").val();
if (unsubscribed_list){
$('#subscription_info').html(_.str.sprintf(
$('#subscription_info').html(sprintf(
_t("You have been <strong>successfully unsubscribed from %s</strong>."),
_.escape(unsubscribed_list)
));
@@ -268,7 +268,7 @@ export class MassMailingHtmlField extends HtmlField {
this._themeClassNames = "";
const displayableThemes =
device.isMobile ?
_.filter($themes, theme => !$(theme).data("hideFromMobile")) :
$themes.filter(theme => !$(theme).data("hideFromMobile")) :
$themes;
this._themeParams = _.map(displayableThemes, (theme) => {
const $theme = $(theme);
@@ -27,7 +27,7 @@ LinkDialog.include({
this.__realMMColors = {};
const $previewArea = $('<div/>').addClass('o_mail_snippet_general');
$(this.editable).find('.o_layout').append($previewArea);
_.each(['link', 'primary', 'secondary'], type => {
['link', 'primary', 'secondary'].forEach(type => {
const $el = $('<a href="#" class="btn btn-' + type + '"/>');
$el.appendTo($previewArea);
this.__realMMColors[type] = {
@@ -62,7 +62,7 @@ LinkDialog.include({
$preview.css('border-color', '');
$preview.css('background-color', '');
$preview.css('color', '');
_.each(['link', 'primary', 'secondary'], type => {
['link', 'primary', 'secondary'].forEach(type => {
if ($preview.hasClass('btn-' + type) || type === 'link' && !$preview.hasClass('btn')) {
$preview.css(this.__realMMColors[type]);
}
@@ -62,10 +62,7 @@ export class Navbar extends Component {
this.env.pos.setLoadingOrderState(true);
const message = await this.env.pos._syncAllOrdersFromServer();
if (message) {
this.notification.add(
_.str.sprintf(message),
5000
);
this.notification.add(message, 5000);
}
} finally {
this.env.pos.setLoadingOrderState(false);
@@ -12,6 +12,7 @@ import { ConnectionLostError } from "@web/core/network/rpc_service";
import { identifyError } from "@point_of_sale/app/error_handlers/error_handlers";
import { _t } from "@web/core/l10n/translation";
import { usePos } from "@point_of_sale/app/pos_hook";
import { sprintf } from "@web/core/utils/strings";
export class ClosePosPopup extends AbstractAwaitablePopup {
static components = { SaleDetailsButton };
@@ -51,7 +52,7 @@ export class ClosePosPopup extends AbstractAwaitablePopup {
} else {
await this.popup.add(ConfirmPopup, {
title: this.env._t("Payments Difference"),
body: _.str.sprintf(
body: sprintf(
this.env._t(
"The maximum difference allowed is %s.\n\
Please contact your manager to accept the closing difference."
@@ -8,6 +8,7 @@ import { PartnerLine } from "./PartnerLine";
import { PartnerDetailsEdit } from "./PartnerDetailsEdit";
import { usePos } from "@point_of_sale/app/pos_hook";
import { Component, onWillUnmount, useRef } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
/**
* Render this screen using `showTempScreen` to select partner.
@@ -115,7 +116,7 @@ export class PartnerListScreen extends Component {
const result = await this.searchPartner();
if (result.length > 0) {
this.notification.add(
_.str.sprintf(
sprintf(
this.env._t('%s customer(s) found for "%s".'),
result.length,
this.state.query
@@ -124,14 +125,10 @@ export class PartnerListScreen extends Component {
);
} else {
this.notification.add(
_.str.sprintf(
this.env._t('No more customer found for "%s".'),
this.state.query
),
sprintf(this.env._t('No more customer found for "%s".'), this.state.query),
3000
);
}
}
_clearSearch() {
this.searchWordInputRef.el.value = "";
@@ -188,7 +185,7 @@ export class PartnerListScreen extends Component {
async getNewPartners() {
let domain = [];
const limit = 30;
if(this.state.query) {
if (this.state.query) {
domain = [
"|",
["name", "ilike", this.state.query + "%"],
@@ -199,7 +196,8 @@ export class PartnerListScreen extends Component {
const result = await this.orm.silent.call(
"pos.session",
"get_pos_ui_res_partner_by_params",
[[odoo.pos_session_id], { domain, limit: limit, offset: this.state.currentOffset }]);
[[odoo.pos_session_id], { domain, limit: limit, offset: this.state.currentOffset }]
);
return result;
}
}
@@ -16,6 +16,7 @@ import { PaymentScreenPaymentLines } from "./PaymentScreenPaymentLines";
import { PaymentScreenStatus } from "./PaymentScreenStatus";
import { usePos } from "@point_of_sale/app/pos_hook";
import { Component } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class PaymentScreen extends Component {
static template = "PaymentScreen";
@@ -334,7 +335,7 @@ export class PaymentScreen extends Component {
const paymentMethod = splitPayments[0].payment_method;
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: this.env._t("Customer Required"),
body: _.str.sprintf(
body: sprintf(
this.env._t("Customer is required for %s payment method."),
paymentMethod.name
),
@@ -6,7 +6,7 @@ import { useService } from "@web/core/utils/hooks";
export class SaveButton extends Component {
static template = "point_of_sale.SaveButton";
setup() {
super.setup();
this.notification = useService("pos_notification");
@@ -14,25 +14,15 @@ export class SaveButton extends Component {
onClick() {
const orderline = this.env.pos.get_order().get_selected_orderline();
if (!orderline) {
this.notification.add(
_.str.sprintf(
this.env._t('You cannot save an empty order')
),
3000
);
this.notification.add(this.env._t("You cannot save an empty order"), 3000);
return;
}
this._selectEmptyOrder();
this.notification.add(
_.str.sprintf(
this.env._t('Order saved for later')
),
3000
);
this.notification.add(this.env._t("Order saved for later"), 3000);
}
_selectEmptyOrder() {
const orders = this.env.pos.get_order_list();
let emptyOrders = orders.filter((order) => order.is_empty());
const emptyOrders = orders.filter((order) => order.is_empty());
if (emptyOrders.length > 0) {
this.env.pos.sendDraftToServer();
this.env.pos.set_order(emptyOrders[0]);
@@ -40,7 +30,6 @@ export class SaveButton extends Component {
this.env.pos.add_new_order();
}
}
}
ProductScreen.addControlButton({
@@ -214,9 +214,9 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
}
if (newQuantity >= selectedLine.saved_quantity) {
if (newQuantity == 0) {
order.remove_orderline(selectedLine)
order.remove_orderline(selectedLine);
}
selectedLine.set_quantity(newQuantity)
selectedLine.set_quantity(newQuantity);
return;
}
const newLine = selectedLine.clone();
@@ -241,10 +241,7 @@ export class ProductScreen extends ControlButtonsMixin(Component) {
this.pos.showScreen("TicketScreen", {
ui: { filter: "SYNCED", searchDetails },
});
this.notification.add(
_.str.sprintf(this.env._t("The order has been already paid.")),
3000
);
this.notification.add(this.env._t("The order has been already paid."), 3000);
this.env.pos.removeOrder(this.env.pos.get_order(), false);
this.env.pos.add_new_order();
return;
@@ -8,6 +8,7 @@ import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/r
import { ProductItem } from "./ProductItem";
import { ProductsWidgetControlPanel } from "./ProductsWidgetControlPanel";
import { Component, useState } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class ProductsWidget extends Component {
static components = { ProductItem, ProductsWidgetControlPanel };
@@ -88,7 +89,7 @@ export class ProductsWidget extends Component {
const result = await this.loadProductFromDB();
if (result.length > 0) {
this.notification.add(
_.str.sprintf(
sprintf(
this.env._t('%s product(s) found for "%s".'),
result.length,
this.env.pos.searchProductWord
@@ -97,7 +98,7 @@ export class ProductsWidget extends Component {
);
} else {
this.notification.add(
_.str.sprintf(
sprintf(
this.env._t('No more product found for "%s".'),
this.env.pos.searchProductWord
),
@@ -112,34 +113,47 @@ export class ProductsWidget extends Component {
}
}
async loadProductFromDB() {
if(!this.env.pos.searchProductWord)
if (!this.env.pos.searchProductWord) {
return;
}
try {
const limit = 30;
let ProductIds = await this.orm.call(
const ProductIds = await this.orm.call(
"product.product",
"search",
[['&',['available_in_pos', '=', true], '|','|',
['name', 'ilike', this.env.pos.searchProductWord],
['default_code', 'ilike', this.env.pos.searchProductWord],
['barcode', 'ilike', this.env.pos.searchProductWord]]],
[
[
"&",
["available_in_pos", "=", true],
"|",
"|",
["name", "ilike", this.env.pos.searchProductWord],
["default_code", "ilike", this.env.pos.searchProductWord],
["barcode", "ilike", this.env.pos.searchProductWord],
],
],
{
offset: this.state.currentOffset,
limit: limit,
}
);
if(ProductIds.length) {
if (ProductIds.length) {
await this.env.pos._addProducts(ProductIds, false);
}
this.updateProductList();
return ProductIds;
} catch (error) {
const identifiedError = identifyError(error)
if (identifiedError instanceof ConnectionLostError || identifiedError instanceof ConnectionAbortedError) {
const identifiedError = identifyError(error);
if (
identifiedError instanceof ConnectionLostError ||
identifiedError instanceof ConnectionAbortedError
) {
return this.popup.add("OfflineErrorPopup", {
title: this.env._t("Network Error"),
body: this.env._t("Product is not loaded. Tried loading the product from the server but there is a network error."),
body: this.env._t(
"Product is not loaded. Tried loading the product from the server but there is a network error."
),
});
} else {
throw error;
@@ -1,6 +1,7 @@
/** @odoo-module */
import { Component } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
import { format } from "web.field_utils";
import { round_precision as round_pr } from "web.utils";
@@ -67,9 +68,9 @@ export class OrderlineDetails extends Component {
return this.env.pos.formatProductQty(toRefundDetail && toRefundDetail.qty);
}
getRefundingMessage() {
return _.str.sprintf(this.env._t("Refunding %s in "), this.getFormattedToRefundQty());
return sprintf(this.env._t("Refunding %s in "), this.getFormattedToRefundQty());
}
getToRefundMessage() {
return _.str.sprintf(this.env._t("To Refund: %s"), this.getFormattedToRefundQty());
return sprintf(this.env._t("To Refund: %s"), this.getFormattedToRefundQty());
}
}
@@ -17,6 +17,7 @@ import { ReprintReceiptButton } from "./ControlButtons/ReprintReceiptButton";
import { SearchBar } from "../../Misc/SearchBar";
import { usePos } from "@point_of_sale/app/pos_hook";
import { onMounted, useState } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class TicketScreen extends IndependentToOrderScreen {
static template = "TicketScreen";
@@ -126,7 +127,7 @@ export class TicketScreen extends IndependentToOrderScreen {
) {
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: this.env._t("Existing orderlines"),
body: _.str.sprintf(
body: sprintf(
this.env._t(
"%s has a total amount of %s, are you sure you want to delete this order?"
),
@@ -207,7 +208,7 @@ export class TicketScreen extends IndependentToOrderScreen {
this.numberBuffer.reset();
this.popup.add(ErrorPopup, {
title: this.env._t("Maximum Exceeded"),
body: _.str.sprintf(
body: sprintf(
this.env._t(
"The requested quantity to be refunded is higher than the ordered quantity. %s is requested while only %s can be refunded."
),
@@ -656,19 +657,28 @@ export class TicketScreen extends IndependentToOrderScreen {
const limit = this._state.syncedOrders.nPerPage;
const offset =
(this._state.syncedOrders.currentPage - 1) * this._state.syncedOrders.nPerPage;
const { ordersInfo, totalCount } = await this.orm.call("pos.order", "search_paid_order_ids", [], {
config_id: this.env.pos.config.id,
domain,
limit,
offset,
});
const idsNotInCache = ordersInfo.filter((orderInfo) => !(orderInfo[0] in this._state.syncedOrders.cache));
const idsNotUpToDate = ordersInfo.filter((orderInfo) => format.datetime(moment(orderInfo[1]), {}, {}) > format.datetime(moment(this._state.syncedOrders.cacheDate), {}, { timezone: false }));
const { ordersInfo, totalCount } = await this.orm.call(
"pos.order",
"search_paid_order_ids",
[],
{
config_id: this.env.pos.config.id,
domain,
limit,
offset,
}
);
const idsNotInCache = ordersInfo.filter(
(orderInfo) => !(orderInfo[0] in this._state.syncedOrders.cache)
);
const idsNotUpToDate = ordersInfo.filter(
(orderInfo) =>
format.datetime(moment(orderInfo[1]), {}, {}) >
format.datetime(moment(this._state.syncedOrders.cacheDate), {}, { timezone: false })
);
const idsToLoad = idsNotInCache.concat(idsNotUpToDate).map((info) => info[0]);
if (idsToLoad.length > 0) {
const fetchedOrders = await this.orm.call("pos.order", "export_for_ui", [
idsToLoad,
]);
const fetchedOrders = await this.orm.call("pos.order", "export_for_ui", [idsToLoad]);
// Check for missing products and partners and load them in the PoS
await this.env.pos._loadMissingProducts(fetchedOrders);
await this.env.pos._loadMissingPartners(fetchedOrders);
+3 -9
View File
@@ -493,9 +493,7 @@ export const PosDB = core.Class.extend({
},
remove_order: function (order_id) {
var orders = this.load("orders", []);
orders = _.filter(orders, function (order) {
return order.id !== order_id;
});
orders = orders.filter((order) => order.id !== order_id);
this.save("orders", orders);
},
remove_all_orders: function () {
@@ -534,9 +532,7 @@ export const PosDB = core.Class.extend({
},
remove_unpaid_order: function (order) {
var orders = this.load("unpaid_orders", []);
orders = _.filter(orders, function (o) {
return o.id !== order.uid;
});
orders = orders.filter((o) => o.id !== order.uid);
this.save("unpaid_orders", orders);
},
remove_all_unpaid_orders: function () {
@@ -591,9 +587,7 @@ export const PosDB = core.Class.extend({
set_ids_removed_from_server: function (ids) {
var to_remove = this.load("unpaid_orders_to_remove", []);
to_remove = _.filter(to_remove, function (id) {
return !ids.includes(id);
});
to_remove = to_remove.filter((id) => !ids.includes(id));
this.save("unpaid_orders_to_remove", to_remove);
},
});
+30 -12
View File
@@ -12,12 +12,32 @@ import { ProductConfiguratorPopup } from "@point_of_sale/js/Popups/ProductConfig
import { EditListPopup } from "@point_of_sale/js/Popups/EditListPopup";
import { markRaw, reactive } from "@odoo/owl";
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
import { escape } from "@web/core/utils/strings";
import { escape, sprintf } from "@web/core/utils/strings";
import { Mutex } from "@web/core/utils/concurrency";
import { memoize } from "@web/core/utils/functions";
import { _t } from "@web/core/l10n/translation";
import { renderToString } from "@web/core/utils/render";
/* Returns an array containing all elements of the given
* array corresponding to the rule function {agg} and without duplicates
*
* @template T
* @template F
* @param {T[]} array
* @param {F} function
* @returns {T[]}
*/
export function uniqueBy(array, agg) {
const map = new Map();
for (const item of array) {
const key = agg(item);
if (!map.has(key)) {
map.set(key, item);
}
}
return [...map.values()];
}
var round_di = utils.round_decimals;
var round_pr = utils.round_precision;
const Markup = utils.Markup;
@@ -720,7 +740,7 @@ export class PosGlobalState extends PosModel {
const pricelistsNames = pricelistsJson.map((pricelist) => {
return pricelist.display_name;
});
message = _.str.sprintf(
message = sprintf(
_t("%s fiscal position(s) added to the configuration."),
pricelistsNames.join(", ")
);
@@ -759,7 +779,7 @@ export class PosGlobalState extends PosModel {
const fiscalPositionNames = fiscalPositionJson.map((fp) => {
return fp.display_name;
});
message = _.str.sprintf(
message = sprintf(
_t("%s fiscal position(s) added to the configuration."),
fiscalPositionNames.join(", ")
);
@@ -1115,7 +1135,7 @@ export class PosGlobalState extends PosModel {
this.orders.add(orders);
}
report.unpaid_skipped_sessions = _.keys(skipped_sessions);
report.unpaid_skipped_sessions = Object.keys(skipped_sessions);
}
return report;
@@ -1394,7 +1414,7 @@ export class PosGlobalState extends PosModel {
}
}
}
return _.uniq(mappedTaxes, (tax) => tax.id);
return uniqueBy(mappedTaxes, (tax) => tax.id);
}
/**
@@ -1978,7 +1998,7 @@ export class Orderline extends PosModel {
} else {
this.pos.env.services.popup.add(ErrorPopup, {
title: _t("Greater than allowed"),
body: _.str.sprintf(
body: sprintf(
_t(
"The requested quantity to be refunded is higher than the refundable quantity of %s."
),
@@ -2377,7 +2397,7 @@ export class Orderline extends PosModel {
var product = this.get_product();
var taxes_ids = this.tax_ids || product.taxes_id;
taxes_ids = _.filter(taxes_ids, (t) => t in this.pos.taxes_by_id);
taxes_ids = taxes_ids.filter((t) => t in this.pos.taxes_by_id);
var taxdetail = {};
var product_taxes = this.pos.get_taxes_after_fp(taxes_ids, this.order.fiscal_position);
@@ -2700,7 +2720,7 @@ export class Order extends PosModel {
this.access_token = uuidv4(); // unique uuid used to identify the authenticity of the request from the QR code.
this.ticketCode = this._generateTicketCode(); // 5-digits alphanum code shown on the receipt
this.uid = this.generate_unique_id();
this.name = _.str.sprintf(_t("Order %s"), this.uid);
this.name = sprintf(_t("Order %s"), this.uid);
this.validation_date = undefined;
this.fiscal_position = this.pos.fiscal_positions.find(function (fp) {
return fp.id === self.pos.config.default_fiscal_position_id[0];
@@ -2737,7 +2757,7 @@ export class Order extends PosModel {
}
this.session_id = this.pos.pos_session.id;
this.uid = json.uid;
this.name = _.str.sprintf(_t("Order %s"), this.uid);
this.name = sprintf(_t("Order %s"), this.uid);
this.validation_date = json.creation_date;
this.server_id = json.server_id ? json.server_id : false;
this.user_id = json.user_id;
@@ -3139,9 +3159,7 @@ export class Order extends PosModel {
var self = this;
this.pricelist = pricelist;
var lines_to_recompute = _.filter(this.get_orderlines(), function (line) {
return !line.price_manually_set;
});
var lines_to_recompute = this.get_orderlines().filter((line) => !line.price_manually_set);
_.each(lines_to_recompute, function (line) {
line.set_unit_price(
line.product.get_price(self.pricelist, line.get_quantity(), line.get_price_extra())
@@ -13,7 +13,7 @@ function assert(condition, message) {
function assertProductPrice(product, pricelist_name, quantity, expected_price) {
return function () {
var pricelist = _.findWhere(posmodel.pricelists, { name: pricelist_name });
var pricelist = posmodel.pricelists.find((pricelist) => pricelist.name === pricelist_name);
var frontend_price = product.get_price(pricelist, quantity);
frontend_price = round_di(frontend_price, posmodel.dp["Product Price"]);
@@ -6,6 +6,8 @@ import publicWidget from "web.public.widget";
import time from "web.time";
import portalComposer from "portal.composer";
import {Markup} from "web.utils";
import { range } from "@web/core/utils/numbers";
import { sprintf } from "@web/core/utils/strings";
var qweb = core.qweb;
var _t = core._t;
@@ -101,8 +103,8 @@ var PortalChatter = publicWidget.Widget.extend({
*/
preprocessMessages(messages) {
_.each(messages, function (m) {
m['author_avatar_url'] = _.str.sprintf('/web/image/%s/%s/author_avatar/50x50', 'mail.message', m.id);
m['published_date_str'] = _.str.sprintf(_t('Published on %s'), moment(time.str_to_datetime(m.date)).format('MMMM Do YYYY, h:mm:ss a'));
m['author_avatar_url'] = sprintf('/web/image/%s/%s/author_avatar/50x50', 'mail.message', m.id);
m['published_date_str'] = sprintf(_t('Published on %s'), moment(time.str_to_datetime(m.date)).format('MMMM Do YYYY, h:mm:ss a'));
m['body'] = Markup(m.body);
});
return messages;
@@ -245,9 +247,7 @@ var PortalChatter = publicWidget.Widget.extend({
}
var pages = [];
_.each(_.range(pmin, pmax + 1), function (index) {
pages.push(index);
});
range(pmin, pmax + 1).forEach(index => pages.push(index));
return {
"page_count": pageCount,
@@ -1,5 +1,6 @@
/** @odoo-module alias=portal.composer **/
import { sprintf } from "@web/core/utils/strings";
import ajax from "web.ajax";
import core from "web.core";
import publicWidget from "web.public.widget";
@@ -91,7 +92,7 @@ var PortalComposer = publicWidget.Widget.extend({
'access_token': accessToken,
},
}).then(function () {
self.attachments = _.reject(self.attachments, {'id': attachmentId});
self.attachments = self.attachments.filter(attachment => attachment.id !== attachmentId);
self._updateAttachments();
self.$sendButton.prop('disabled', false);
});
@@ -124,7 +125,7 @@ var PortalComposer = publicWidget.Widget.extend({
resolve();
}).guardedCatch(function (error) {
self.displayNotification({
message: _.str.sprintf(_t("Could not save file <strong>%s</strong>"),
message: sprintf(_t("Could not save file <strong>%s</strong>"),
_.escape(file.name)),
type: 'warning',
sticky: true,
@@ -4,6 +4,7 @@ import core from "web.core";
import publicWidget from "web.public.widget";
import time from "web.time";
import session from "web.session";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
@@ -40,9 +41,9 @@ var PortalSidebar = publicWidget.Widget.extend({
} else if (diff > 0) {
// Workaround: force uniqueness of these two translations. We use %1d because the string
// with %d is already used in mail and mail's translations are not sent to the frontend.
displayStr = _.str.sprintf(_t('Due in %1d days'), Math.abs(diff));
displayStr = sprintf(_t('Due in %s days'), Math.abs(diff).toFixed(1));
} else {
displayStr = _.str.sprintf(_t('%1d days overdue'), Math.abs(diff));
displayStr = sprintf(_t('%s days overdue'), Math.abs(diff).toFixed(1));
}
$(el).text(displayStr);
});
@@ -136,7 +136,7 @@ PortalChatter.include({
'avg': Math.round(result['rating_stats']['avg'] * 100) / 100,
'percent': [],
};
_.each(_.sortBy(_.keys(result['rating_stats']['percent'])).reverse(), function (rating) {
_.each(_.sortBy(Object.keys(result['rating_stats']['percent'])).reverse(), function (rating) {
ratingData['percent'].push({
'num': self.roundToHalf(rating),
'percent': utils.round_precision(result['rating_stats']['percent'][rating], 0.01),
@@ -172,7 +172,7 @@ PortalChatter.include({
return {
mes_index: messageIndex,
publisher_id: this.options.partner_id,
publisher_avatar: _.str.sprintf('/web/image/res.partner/%s/avatar_128/50x50', this.options.partner_id),
publisher_avatar: `/web/image/res.partner/${this.options.partner_id}/avatar_128/50x50`,
publisher_name: _t("Write your comment"),
publisher_datetime: '',
publisher_comment: '',
@@ -197,7 +197,7 @@ PortalChatter.include({
if (rawRating.publisher_id && rawRating.publisher_id.length >= 2) {
ratingData.publisher_id = rawRating.publisher_id[0];
ratingData.publisher_name = rawRating.publisher_id[1];
ratingData.publisher_avatar = _.str.sprintf('/web/image/res.partner/%s/avatar_128/50x50', ratingData.publisher_id);
ratingData.publisher_avatar = `/web/image/res.partner/${ratingData.publisher_id}/avatar_128/50x50`;
}
var commentData = Object.assign(this._newPublisherCommentData(messageIndex), ratingData);
return commentData;
@@ -4,6 +4,7 @@ import core from "web.core";
import rpc from "web.rpc";
import { PaymentInterface } from "@point_of_sale/js/payment";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
@@ -78,7 +79,7 @@ export const PaymentAdyen = PaymentInterface.extend({
_adyen_get_sale_id: function () {
var config = this.pos.config;
return _.str.sprintf("%s (ID: %s)", config.display_name, config.id);
return sprintf("%s (ID: %s)", config.display_name, config.id);
},
_adyen_common_message_header: function () {
@@ -192,9 +193,9 @@ export const PaymentAdyen = PaymentInterface.extend({
return output_text.reduce(function (acc, entry) {
var params = new URLSearchParams(entry.Text);
if (params.get("name") && !params.get("value")) {
return acc + _.str.sprintf("\n%s", params.get("name"));
return acc + sprintf("\n%s", params.get("name"));
} else if (params.get("name") && params.get("value")) {
return acc + _.str.sprintf("\n%s: %s", params.get("name"), params.get("value"));
return acc + sprintf("\n%s: %s", params.get("name"), params.get("value"));
}
return acc;
@@ -287,7 +288,7 @@ export const PaymentAdyen = PaymentInterface.extend({
resolve(true);
} else {
var message = additional_response.get("message");
self._show_error(_.str.sprintf(_t("Message from Adyen: %s"), message));
self._show_error(sprintf(_t("Message from Adyen: %s"), message));
// this means the transaction was cancelled by pressing the cancel button on the device
if (message.startsWith("108 ")) {
@@ -327,7 +328,7 @@ export const PaymentAdyen = PaymentInterface.extend({
}
this._show_error(
_.str.sprintf(_t("An unexpected error occurred. Message from Adyen: %s"), msg)
sprintf(_t("An unexpected error occurred. Message from Adyen: %s"), msg)
);
if (line) {
line.set_payment_status("force_done");
@@ -5,6 +5,7 @@ import { useService } from "@web/core/utils/hooks";
import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { Component } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class DiscountButton extends Component {
static template = "DiscountButton";
@@ -68,7 +69,7 @@ export class DiscountButton extends Component {
description:
`${pc}%, ` +
(tax_ids_array.length
? _.str.sprintf(
? sprintf(
this.env._t("Tax: %s"),
tax_ids_array
.map((taxId) => this.env.pos.taxes_by_id[taxId].amount + "%")
@@ -2,6 +2,7 @@
import core from "web.core";
import { PrinterMixin, PrintResult, PrintResultGenerator } from "@point_of_sale/js/printers";
import { sprintf } from "@web/core/utils/strings";
var QWeb = core.qweb;
var _t = core._t;
@@ -19,15 +20,15 @@ class EpsonPrintResultGenerator extends PrintResultGenerator {
title: _t("Connection to the printer failed"),
body: _t(
"Please check if the printer is still connected. \n" +
"Some browsers don't allow HTTP calls from websites to devices in the network (for security reasons). " +
"If it is the case, you will need to follow Odoo's documentation for " +
"'Self-signed certificate for ePOS printers' and 'Secure connection (HTTPS)' to solve the issue"
"Some browsers don't allow HTTP calls from websites to devices in the network (for security reasons). " +
"If it is the case, you will need to follow Odoo's documentation for " +
"'Self-signed certificate for ePOS printers' and 'Secure connection (HTTPS)' to solve the issue"
),
},
});
if (window.location.protocol === "https:") {
printRes.message.body += _.str.sprintf(
printRes.message.body += sprintf(
_t(
"If you are on a secure server (HTTPS) please make sure you manually accepted the certificate by accessing %s"
),
@@ -39,22 +40,32 @@ class EpsonPrintResultGenerator extends PrintResultGenerator {
}
IoTResultError(printerErrorCode) {
let message = _t("The printer was successfully reached, but it wasn't able to print.") + '\n';
let message =
_t("The printer was successfully reached, but it wasn't able to print.") + "\n";
if (printerErrorCode) {
message += '\n' + _t("The following error code was given by the printer:") + '\n' + printerErrorCode;
message +=
"\n" +
_t("The following error code was given by the printer:") +
"\n" +
printerErrorCode;
const extra_messages = {
"DeviceNotFound":
_t("Check on the printer configuration for the 'Device ID' setting. " +
"It should be set to: ") + "\nlocal_printer",
"EPTR_REC_EMPTY":
_t("No paper was detected by the printer"),
DeviceNotFound:
_t(
"Check on the printer configuration for the 'Device ID' setting. " +
"It should be set to: "
) + "\nlocal_printer",
EPTR_REC_EMPTY: _t("No paper was detected by the printer"),
};
if (printerErrorCode in extra_messages) {
message += '\n' + extra_messages[printerErrorCode];
message += "\n" + extra_messages[printerErrorCode];
}
message += '\n' + _t("To find more details on the error reason, please search online for:") + '\n' +
" Epson Server Direct Print " + printerErrorCode;
message +=
"\n" +
_t("To find more details on the error reason, please search online for:") +
"\n" +
" Epson Server Direct Print " +
printerErrorCode;
} else {
message += _t("Please check if the printer has enough paper and is ready to print.");
}
@@ -184,6 +195,9 @@ export const EpsonPrinter = core.Class.extend(PrinterMixin, {
data: img,
});
const response = $(res).find("response");
return {"result": response.attr("success") === "true", "printerErrorCode": response.attr("code")};
return {
result: response.attr("success") === "true",
printerErrorCode: response.attr("code"),
};
},
});
@@ -2,6 +2,7 @@
import { PartnerLine } from "@point_of_sale/js/Screens/PartnerListScreen/PartnerLine";
import { patch } from "@web/core/utils/patch";
import { sprintf } from "@web/core/utils/strings";
patch(PartnerLine.prototype, "pos_loyalty.PartnerLine", {
_getLoyaltyPointsRepr(loyaltyCard) {
@@ -13,6 +14,6 @@ patch(PartnerLine.prototype, "pos_loyalty.PartnerLine", {
if (program.portal_visible) {
return `${balanceRepr} ${program.portal_point_name}`;
}
return _.str.sprintf(this.env._t("%s Points"), balanceRepr);
return sprintf(this.env._t("%s Points"), balanceRepr);
},
});
@@ -5,6 +5,7 @@ import { useBarcodeReader } from "@point_of_sale/app/barcode_reader_hook";
import { patch } from "@web/core/utils/patch";
import { ConfirmPopup } from "@point_of_sale/js/Popups/ConfirmPopup";
import { useService } from "@web/core/utils/hooks";
import { sprintf } from "@web/core/utils/strings";
patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
setup() {
@@ -43,7 +44,7 @@ patch(ProductScreen.prototype, "pos_loyalty.ProductScreen", {
const reward = this.env.pos.reward_by_id[selectedLine.reward_id];
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: this.env._t("Deactivating reward"),
body: _.str.sprintf(
body: sprintf(
this.env._t(
"Are you sure you want to remove %s from this order?\n You will still be able to claim it through the reward button."
),
@@ -5,6 +5,7 @@ import { useService } from "@web/core/utils/hooks";
import { NumberPopup } from "@point_of_sale/js/Popups/NumberPopup";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { Component } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class TableGuestsButton extends Component {
static template = "TableGuestsButton";
@@ -34,7 +35,7 @@ export class TableGuestsButton extends Component {
if (guestCount > max_capacity) {
await this.popup.add(ErrorPopup, {
title: this.env._t("Blocked action"),
body: _.str.sprintf(
body: sprintf(
this.env._t("You cannot put a number that exceeds %s "),
max_capacity
),
@@ -98,7 +98,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
await this.env.pos._loadPartners([sale_order.partner_id[0]]);
} catch {
const title = this.env._t("Customer loading error");
const body = _.str.sprintf(
const body = sprintf(
this.env._t("There was a problem in loading the %s customer."),
sale_order.partner_id[1]
);
@@ -5,6 +5,7 @@ import core from "web.core";
import rpc from "web.rpc";
import { PaymentInterface } from "@point_of_sale/js/payment";
import { ErrorPopup } from "@point_of_sale/js/Popups/ErrorPopup";
import { sprintf } from "@web/core/utils/strings";
const _t = core._t;
@@ -48,7 +49,7 @@ export const PaymentStripe = PaymentInterface.extend({
discoverReaders: async function () {
const discoverResult = await this.terminal.discoverReaders({});
if (discoverResult.error) {
this._showError(_.str.sprintf(_t("Failed to discover: %s"), discoverResult.error));
this._showError(sprintf(_t("Failed to discover: %s"), discoverResult.error));
} else if (discoverResult.discoveredReaders.length === 0) {
this._showError(_t("No available Stripe readers."));
} else {
@@ -100,7 +101,7 @@ export const PaymentStripe = PaymentInterface.extend({
}
}
this._showError(
_.str.sprintf(
sprintf(
this.env._t("Stripe readers %s not listed in your account"),
this.payment_method.stripe_serial_number
)
+3 -2
View File
@@ -4,6 +4,7 @@ import concurrency from "web.concurrency";
import core from "web.core";
import utils from "web.utils";
import ajax from "web.ajax";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
var VariantMixin = {
@@ -500,7 +501,7 @@ var VariantMixin = {
}
excludedByData.push(excludedByName);
$target.attr('title', _.str.sprintf(_t('Not available with %s'), excludedByData.join(', ')));
$target.attr('title', sprintf(_t('Not available with %s'), excludedByData.join(', ')));
$target.data('excluded-by', JSON.stringify(excludedByData));
}
},
@@ -600,7 +601,7 @@ var VariantMixin = {
if ($('.decimal_precision').length) {
precision = parseInt($('.decimal_precision').last().data('precision'));
}
var formatted = _.str.sprintf('%.' + precision + 'f', price).split('.');
var formatted = price.toFixed(precision).split(".");
formatted[0] = utils.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
},
@@ -2,6 +2,7 @@
import { _t } from "web.core";
import * as spreadsheet from "@odoo/o-spreadsheet";
import { sprintf } from "@web/core/utils/strings";
const { arg, toString, toNumber } = spreadsheet.helpers;
const { functionRegistry } = spreadsheet.registries;
@@ -12,7 +13,7 @@ const { functionRegistry } = spreadsheet.registries;
function assertListsExists(listId, getters) {
if (!getters.isExistingList(listId)) {
throw new Error(_.str.sprintf(_t('There is no list with id "%s"'), listId));
throw new Error(sprintf(_t('There is no list with id "%s"'), listId));
}
}
+4 -4
View File
@@ -483,7 +483,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
}
var submitPromise = self._rpc({
route: _.str.sprintf('%s/%s/%s', route, self.options.surveyToken, self.options.answerToken),
route: `${route}/${self.options.surveyToken}/${self.options.answerToken}`,
params: params,
});
this._nextScreen(submitPromise, options);
@@ -736,7 +736,7 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
break;
}
});
if (_.keys(errors).length > 0) {
if (Object.keys(errors).length > 0) {
this._showErrors(errors);
return false;
}
@@ -1170,8 +1170,8 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa
_showErrors: function (errors) {
var self = this;
var errorKeys = _.keys(errors);
_.each(errorKeys, function (key) {
var errorKeys = Object.keys(errors || {});
errorKeys.forEach(key => {
self.$("#" + key + '>.o_survey_question_error').append($('<p>', {text: errors[key]})).addClass("slide_in");
if (errorKeys[0] === key) {
self._scrollToError(self.$('.js_question-wrapper#' + key));
@@ -44,7 +44,7 @@ publicWidget.registry.SurveySessionLeaderboard = publicWidget.Widget.extend({
}
var leaderboardPromise = this._rpc({
route: _.str.sprintf('/survey/session/leaderboard/%s', this.surveyAccessToken)
route: `/survey/session/leaderboard/${this.surveyAccessToken}`,
});
Promise.all([fadeOutPromise, leaderboardPromise]).then(function (results) {
@@ -250,10 +250,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre
args: [[this.surveyId]],
}).then(function () {
if ($(ev.currentTarget).data('showResults')) {
document.location = _.str.sprintf(
'/survey/results/%s',
self.surveyId
);
document.location = `/survey/results/${encodeURIComponent(self.surveyId)}`;
} else {
window.history.back();
}
@@ -359,7 +356,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre
}
var nextQuestionPromise = this._rpc({
route: _.str.sprintf('/survey/session/next_question/%s', self.surveyAccessToken),
route: `/survey/session/next_question/${self.surveyAccessToken}`,
params: {
'go_back': goBack,
}
@@ -472,7 +469,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre
var self = this;
return this._rpc({
route: _.str.sprintf('/survey/session/results/%s', self.surveyAccessToken)
route: `/survey/session/results/${self.surveyAccessToken}`,
}).then(function (questionResults) {
if (questionResults) {
self.attendeesCount = questionResults.attendees_count;
@@ -116,7 +116,7 @@ export class ProfilingQwebView extends Component {
* @returns {string}
*/
_formatDelay(delay) {
return delay ? _.str.sprintf("%.1f", Math.ceil(delay * 10) / 10) : ".";
return delay ? (Math.ceil(delay * 10) / 10).toFixed(1) : ".";
}
/**
@@ -32,3 +32,20 @@ export function computeVariation(value, comparisonValue) {
export function clamp(num, min, max) {
return Math.max(Math.min(num, max), min);
}
/**
* A function to create flexibly-numbered lists of integers, handy for each and map loops.
* step defaults to 1.
* Returns a list of integers from start (inclusive) to stop (exclusive), incremented (or decremented) by step.
* @param {number} min default 0
* @param {number} max
* @param {number} step default 1
* @returns {number[]}
*/
export function range(start, stop, step = 1) {
const array = [];
for (let i = start; i < stop; i += step) {
array.push(i);
}
return array;
}
@@ -1,5 +1,5 @@
/** @odoo-module alias=web.KeyboardNavigationMixin **/
import BrowserDetection from "web.BrowserDetection";
import BrowserDetection from "web.BrowserDetection";
import core from "web.core";
/**
@@ -77,7 +77,7 @@
}
var accesskeyElements = $(document).find('[accesskey]').filter(':visible');
_.each(accesskeyElements, function (elem) {
var overlay = $(_.str.sprintf("<div class='o_web_accesskey_overlay font-sans-serif'>%s</div>", $(elem).attr('accesskey').toUpperCase()));
var overlay = $(`<div class='o_web_accesskey_overlay font-sans-serif'>${$(elem).attr('accesskey').toUpperCase()}</div>`);
var $overlayParent;
if (elem.tagName.toUpperCase() === "INPUT") {
@@ -10,6 +10,7 @@
import SearchBar from "web.SearchBar";
import { useModel } from "web.Model";
import { LegacyComponent } from "@web/legacy/legacy_component";
import { sprintf } from "@web/core/utils/strings";
const {
onMounted,
@@ -132,7 +133,7 @@
this.fields = this._formatFields(toRaw(this.props.fields || {}));
this.sprintf = _.str.sprintf;
this.sprintf = sprintf;
onWillDestroy(() => {
const content = this.props.cp_content;
@@ -312,7 +312,7 @@ var Dialog = Widget.extend({
_setButtonsTo($target, buttons) {
var self = this;
$target.empty();
_.each(buttons, function (buttonData) {
buttons.forEach(buttonData => {
var $button = dom.renderButton({
attrs: {
class: buttonData.classes || (buttons.length > 1 ? 'btn-secondary' : 'btn-primary'),
+13 -21
View File
@@ -1,5 +1,6 @@
/** @odoo-module alias=web.Domain **/
import { intersection } from "@web/core/utils/arrays";
import collections from "web.collections";
import pyUtils from "web.py_utils";
var py = window.py; // look py.js
@@ -34,7 +35,7 @@ var Domain = collections.Tree.extend({
*/
init: function (domain, evalContext) {
this._super.apply(this, arguments);
if (_.isArray(domain) || _.isString(domain)) {
if (Array.isArray(domain) || _.isString(domain)) {
this._parse(this.normalizeArray(_.clone(this.stringToArray(domain, evalContext))));
} else {
this._data = !!domain;
@@ -57,7 +58,7 @@ var Domain = collections.Tree.extend({
if (this._data === true || this._data === false) {
// The domain is a always-true or a always-false domain
return this._data;
} else if (_.isArray(this._data)) {
} else if (Array.isArray(this._data)) {
// The domain is a [name, operator, value] entity
// First check if we have the field value in the field values set
// and if the first part of the domain contains 'parent.field'
@@ -78,10 +79,7 @@ var Domain = collections.Tree.extend({
fieldName in values.parent;
}
if (!(this._data[0] in values) && !(isParentField)) {
throw new Error(_.str.sprintf(
"Unknown field %s in domain",
this._data[0]
));
throw new Error(`Unknown field ${this._data[0]} in domain`);
}
fieldValue = isParentField ? values.parent[fieldName] : values[fieldName];
}
@@ -102,14 +100,14 @@ var Domain = collections.Tree.extend({
case ">=":
return (fieldValue >= this._data[2]);
case "in":
return _.intersection(
_.isArray(this._data[2]) ? this._data[2] : [this._data[2]],
_.isArray(fieldValue) ? fieldValue : [fieldValue],
return intersection(
Array.isArray(this._data[2]) ? this._data[2] : [this._data[2]],
Array.isArray(fieldValue) ? fieldValue : [fieldValue],
).length !== 0;
case "not in":
return _.intersection(
_.isArray(this._data[2]) ? this._data[2] : [this._data[2]],
_.isArray(fieldValue) ? fieldValue : [fieldValue],
return intersection(
Array.isArray(this._data[2]) ? this._data[2] : [this._data[2]],
Array.isArray(fieldValue) ? fieldValue : [fieldValue],
).length === 0;
case "like":
if (fieldValue === false) {
@@ -132,10 +130,7 @@ var Domain = collections.Tree.extend({
}
return new RegExp(this._data[2].replace(/%/g, '.*'), 'i').test(fieldValue);
default:
throw new Error(_.str.sprintf(
"Domain %s uses an unsupported operator",
this._data
));
throw new Error(`Domain ${this._data} uses an unsupported operator`);
}
} else { // The domain is a set of [name, operator, value] entitie(s)
switch (this._data) {
@@ -289,7 +284,7 @@ var Domain = collections.Tree.extend({
normalizeArray: function (domain) {
if (domain.length === 0) { return domain; }
var expected = 1;
_.each(domain, function (item) {
domain.forEach(item => {
if (item === "&" || item === "|") {
expected++;
} else if (item !== "!") {
@@ -299,10 +294,7 @@ var Domain = collections.Tree.extend({
if (expected < 0) {
domain.unshift.apply(domain, _.times(Math.abs(expected), _.constant("&")));
} else if (expected > 0) {
throw new Error(_.str.sprintf(
"invalid domain %s (missing %d segment(s))",
JSON.stringify(domain), expected
));
throw new Error(`invalid domain ${JSON.stringify(domain)} (missing ${expected.toFixed(0)} segment(s))`);
}
return domain;
},
@@ -305,7 +305,7 @@ var EventDispatcherMixin = Object.assign({}, ParentedMixin, {
_.each(events, function (eventName) {
self.__edispatcherEvents.off(eventName, func, dest);
if (dest && dest.__eventDispatcherMixin) {
dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function (el) {
dest.__edispatcherRegisteredEvents = dest.__edispatcherRegisteredEvents.filter(el => {
return !(el.name === eventName && el.func === func && el.source === self);
});
}
@@ -386,7 +386,7 @@ var PropertiesMixin = Object.assign({}, EventDispatcherMixin, {
// remove this, or move it elsewhere. Also, learn OO programming.
if (key === 'value' && self.field && self.field.type === 'float' && tmp && val){
var digits = self.field.digits;
if (_.isArray(digits)) {
if (Array.isArray(digits)) {
if (utils.float_is_zero(tmp - val, digits[1])) {
return;
}
@@ -46,7 +46,7 @@ var RamStorage = Class.extend(mixins.EventDispatcherMixin, {
* @return {string}
*/
key: function (index) {
return _.keys(this.storage)[index];
return Object.keys(this.storage)[index];
},
/**
* Removes the given key from the storage
+3 -2
View File
@@ -1,5 +1,6 @@
/** @odoo-module alias=web.time **/
import { sprintf } from "@web/core/utils/strings";
import translation from "web.translation";
import utils from "web.utils";
@@ -189,7 +190,7 @@ function auto_str_to_date (value) {
try {
return str_to_time(value);
} catch {}
throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
throw new Error(sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
}
function auto_date_to_str (value, type) {
@@ -201,7 +202,7 @@ function auto_date_to_str (value, type) {
case 'time':
return time_to_str(value);
default:
throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
throw new Error(sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
}
}
@@ -19,7 +19,7 @@ var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase#
set_bundle: function(translation_bundle) {
var self = this;
this.multi_lang = translation_bundle.multi_lang
var modules = _.keys(translation_bundle.modules);
var modules = Object.keys(translation_bundle.modules);
modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
@@ -9,6 +9,7 @@
import translation from "web.translation";
import { Component } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
var _t = translation._t;
var id = -1;
@@ -633,7 +634,7 @@ const utils = {
return sindent + node.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
} else if (typeof(node.tag) !== 'string' || node.children && !(node.children instanceof Array) || node.attrs && !(node.attrs instanceof Object)) {
throw new Error(
_.str.sprintf(_t("Node [%s] is not a JSONified XML node"),
sprintf(_t("Node [%s] is not a JSONified XML node"),
JSON.stringify(node)));
}
for (var attr in node.attrs) {
@@ -15,6 +15,7 @@ import py_utils from "web.py_utils";
import session from "web.session";
import field_utils from "web.field_utils";
import utils from "web.utils";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
var _lt = core._lt;
@@ -1281,7 +1282,7 @@ var FieldFloatToggle = AbstractField.extend({
_nextValue: function () {
var range = this.nodeOptions.range;
var val = utils.closestNumber(this._getDisplayedValue(), range);
var index = _.indexOf(range, val);
var index = range.indexOf(val);
if (index !== -1) {
if (index + 1 < range.length) {
return range[index + 1];
@@ -1539,7 +1540,7 @@ var AbstractFieldBinary = AbstractField.extend({
var file = file_node.files[0];
if (file.size > this.max_upload_size) {
var msg = _t("The selected file exceed the maximum file size of %s.");
this.displayNotification({ title: _t("File upload"), message: _.str.sprintf(msg, utils.human_size(this.max_upload_size)), type: 'danger' });
this.displayNotification({ title: _t("File upload"), message: sprintf(msg, utils.human_size(this.max_upload_size)), type: 'danger' });
return false;
}
utils.getDataURLFromFile(file).then(function (data) {
@@ -14,6 +14,7 @@
* conversions.
*/
import { sprintf } from "@web/core/utils/strings";
import core from "web.core";
import dom from "web.dom";
import session from "web.session";
@@ -175,7 +176,7 @@ function formatFloat(value, field, options) {
} else {
precision = 2;
}
var formatted = _.str.sprintf('%.' + precision + 'f', value || 0).split('.');
var formatted = (value || 0).toFixed(precision).split(".");
formatted[0] = utils.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
}
@@ -210,18 +211,18 @@ function formatFloatFactor(value, field, options) {
*/
function formatFloatTime(value, field, options) {
options = options || {};
var pattern = options.noLeadingZeroHour ? '%1d:%02d' : '%02d:%02d';
if (value < 0) {
value = Math.abs(value);
pattern = '-' + pattern;
}
var hour = Math.floor(value);
var min = Math.round((value % 1) * 60);
if (min === 60){
var hour = Math.floor(Math.abs(value));
var min = Math.round((Math.abs(value) % 1) * 60);
if (min === 60) {
min = 0;
hour = hour + 1;
}
return _.str.sprintf(pattern, hour, min);
const val = options.noLeadingZeroHour
? String(hour) + ":" + String(min).padStart(2, "0")
: String(hour).padStart(2, "0") + ":" + String(min).padStart(2, "0");
return value < 0 ? "-" + val : val;
}
/**
@@ -251,7 +252,7 @@ function formatInteger(value, field, options) {
if (options.humanReadable && options.humanReadable(value)) {
return utils.human_number(value, options.decimals, options.minDigits, options.formatterCallback);
}
return utils.insert_thousand_seps(_.str.sprintf('%d', value));
return utils.insert_thousand_seps(String(value));
}
/**
@@ -271,7 +272,7 @@ function formatInteger(value, field, options) {
function formatMany2one(value, field, options) {
if (!value) {
value = '';
} else if (_.isArray(value)) {
} else if (Array.isArray(value)) {
// value is a pair [id, nameget]
value = value[1];
} else {
@@ -509,7 +510,7 @@ function parseDate(value, field, options) {
return date;
}
}
throw new Error(_.str.sprintf(core._t("'%s' is not a correct date"), value));
throw new Error(sprintf(core._t("'%s' is not a correct date"), value));
}
/**
@@ -562,7 +563,7 @@ function parseDateTime(value, field, options) {
return datetime;
}
}
throw new Error(_.str.sprintf(core._t("'%s' is not a correct datetime"), value));
throw new Error(sprintf(core._t("'%s' is not a correct datetime"), value));
}
/**
@@ -596,7 +597,7 @@ function parseNumber(value) {
function parseFloat(value) {
var parsed = parseNumber(value);
if (isNaN(parsed)) {
throw new Error(_.str.sprintf(core._t("'%s' is not a correct float"), value));
throw new Error(sprintf(core._t("'%s' is not a correct float"), value));
}
return parsed;
}
@@ -644,7 +645,7 @@ function parseMonetary(value, field, options) {
return parseFloat(value);
}
if (!value.includes(currency.symbol)) {
throw new Error(_.str.sprintf(core._t("'%s' is not a correct monetary field"), value));
throw new Error(sprintf(core._t("'%s' is not a correct monetary field"), value));
}
if (currency.position === 'before') {
return parseFloat(value
@@ -709,7 +710,7 @@ function parseInteger(value) {
var parsed = parseNumber(value);
// do not accept not numbers or float values
if (isNaN(parsed) || parsed % 1 || parsed < -2147483648 || parsed > 2147483647) {
throw new Error(_.str.sprintf(core._t("'%s' is not a correct integer"), value));
throw new Error(sprintf(core._t("'%s' is not a correct integer"), value));
}
return parsed;
}
@@ -728,13 +729,13 @@ function parseInteger(value) {
* number, the value is returned untouched.
*/
function parseMany2one(value) {
if (_.isArray(value)) {
if (Array.isArray(value)) {
return {
id: value[0],
display_name: value[1],
};
}
if (_.isNumber(value) || _.isString(value)) {
if (Number.isFinite(value) || _.isString(value)) {
return {
id: parseInt(value, 10),
};
@@ -20,8 +20,7 @@ import dom from "web.dom";
import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";
import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog";
import Domain from "web.Domain";
import { sprintf } from "web.utils";
import { escape } from "@web/core/utils/strings";
import { escape, sprintf } from "@web/core/utils/strings";
var _t = core._t;
var _lt = core._lt;
@@ -37,7 +36,7 @@ var M2ODialog = Dialog.extend({
this.name = name;
this.value = value;
this._super(parent, {
title: _.str.sprintf(_t("New %s"), this.name),
title: sprintf(_t("New %s"), this.name),
size: 'medium',
buttons: [{
text: _t('Create'),
@@ -423,7 +422,7 @@ var FieldMany2One = AbstractField.extend({
domain: this.record.getDomain({fieldName: this.name}),
context: Object.assign({}, this.record.getContext(this.recordParams), context || {}),
dynamicFilters: dynamicFilters || [],
title: _.str.sprintf((view === 'search' ? _t("Search: %s") : _t("Create: %s")), this.string),
title: sprintf((view === 'search' ? _t("Search: %s") : _t("Create: %s")), this.string),
multiSelect: false,
noCreate: !self.can_create,
onSelected: function (records) {
@@ -469,7 +468,7 @@ var FieldMany2One = AbstractField.extend({
return x[0];
});
dynamicFilters = [{
description: _.str.sprintf(_t('Quick search: %s'), search_val),
description: sprintf(_t('Quick search: %s'), search_val),
domain: [['id', 'in', ids]],
}];
}
@@ -596,7 +595,7 @@ var FieldMany2One = AbstractField.extend({
_renderReadonly: function () {
this.$el.html(this._renderValueLines(true));
if (!this.noOpen && this.value) {
this.$el.attr('href', _.str.sprintf('#id=%s&model=%s', this.value.res_id, this.field.relation));
this.$el.attr('href', `#id=${this.value.res_id}&model=${this.field.relation}`);
this.$el.addClass('o_form_uri');
}
},
@@ -1512,9 +1511,7 @@ var FieldSelection = AbstractField.extend({
this.values = this.record.specialData[this.name];
this.formatType = 'many2one';
} else {
this.values = _.reject(this.field.selection, function (v) {
return v[0] === false && v[1] === '';
});
this.values = this.field.selection.filter(v => !(v[0] === false && v[1] === ''));
}
this.values = [[false, this.attrs.placeholder || '']].concat(this.values);
},
@@ -82,6 +82,8 @@
* element, so it can be edited later.
*/
import { intersection, unique } from "@web/core/utils/arrays";
import { sprintf } from "@web/core/utils/strings";
import AbstractModel from "web.AbstractModel";
import concurrency from "web.concurrency";
import Context from "web.Context";
@@ -860,8 +862,8 @@ var BasicModel = AbstractModel.extend({
record.data[field.name] = null;
if (field.type === 'many2one') {
if (field.value) {
var id = _.isArray(field.value) ? field.value[0] : field.value;
var display_name = _.isArray(field.value) ? field.value[1] : undefined;
var id = Array.isArray(field.value) ? field.value[0] : field.value;
var display_name = Array.isArray(field.value) ? field.value[1] : undefined;
dataPoint = self._makeDataPoint({
modelName: field.relation,
data: {
@@ -991,7 +993,7 @@ var BasicModel = AbstractModel.extend({
if (parent.static) {
// x2Many case: the new record has been stored in _changes, as a
// command so we remove the command(s) related to that record
parent._changes = _.filter(parent._changes, function (change) {
parent._changes = parent._changes.filter( change => {
if (change.id === elementID &&
change.operation === 'ADD' && // For now, only an ADD command increases limits
parent.tempLimitIncrement) {
@@ -1072,7 +1074,7 @@ var BasicModel = AbstractModel.extend({
}
if (dataType === 'list') {
data.data = _.sortBy(data.data, function (d) {
return _.indexOf(resIDs, self.localData[d].res_id)
return resIDs.indexOf(self.localData[d].res_id);
});
}
}
@@ -1247,9 +1249,7 @@ var BasicModel = AbstractModel.extend({
list.orderedBy[0].asc = !list.orderedBy[0].asc;
}
} else {
var orderedBy = _.reject(list.orderedBy, function (o) {
return o.name === fieldName;
});
var orderedBy = list.orderedBy.filter(o => o.name !== fieldName);
list.orderedBy = [{name: fieldName, asc: true}].concat(orderedBy);
}
@@ -1538,7 +1538,7 @@ var BasicModel = AbstractModel.extend({
if (options.notifyChange === false) {
return Promise.all(defs).then(function () {
return Promise.resolve(_.keys(changes));
return Promise.resolve(Object.keys(changes));
});
}
@@ -1558,7 +1558,7 @@ var BasicModel = AbstractModel.extend({
self._performOnChange(record, onChangeFields, { viewType: options.viewType })
.then(function (result) {
delete record._warning;
resolve(_.keys(changes).concat(Object.keys(result && result.value || {})));
resolve(Object.keys(changes).concat(Object.keys(result && result.value || {})));
}).guardedCatch(function () {
self._visitChildren(record, function (elem) {
Object.assign(elem, initialData[elem.id]);
@@ -1566,13 +1566,13 @@ var BasicModel = AbstractModel.extend({
reject();
});
} else {
resolve(_.keys(changes));
resolve(Object.keys(changes));
}
}).then(function (fieldNames) {
return self._fetchSpecialData(record).then(function (fieldNames2) {
// Return the names of the fields that changed (onchange or
// associated special data change)
return _.union(fieldNames, fieldNames2);
return fieldNames.concat(fieldNames2);
});
});
});
@@ -1729,7 +1729,7 @@ var BasicModel = AbstractModel.extend({
if (val) {
// when the value isn't false, it can be either
// an array [id, display_name] or just an id.
var data = _.isArray(val) ?
var data = Array.isArray(val) ?
{id: val[0], display_name: val[1]} :
{id: val};
if (!oldValue || (self.localData[oldValue].res_id !== data.id)) {
@@ -1961,7 +1961,7 @@ var BasicModel = AbstractModel.extend({
list._forceM2MLink = true;
// handle multiple add: command[2] may be a dict of values (1
// record added) or an array of dict of values
var data = _.isArray(command.ids) ? command.ids : [command.ids];
var data = Array.isArray(command.ids) ? command.ids : [command.ids];
// name_create records for which there is no id (typically, could
// be the case of a quick_create in a many2many_tags, so data.length
@@ -2066,23 +2066,22 @@ var BasicModel = AbstractModel.extend({
// no 'ADD' operation for that dataPoint, as it would mean
// that the record wasn't in the relation yet
var idsToRemove = command.ids;
list._changes = _.reject(list._changes, function (change, index) {
var idInCommands = _.contains(command.ids, change.id);
list._changes = list._changes.filter(change => {
var idInCommands = command.ids.includes(change.id);
if (idInCommands && change.operation === 'ADD') {
idsToRemove = _.without(idsToRemove, change.id);
idsToRemove = idsToRemove.filter(id => id !== change.id);
}
return idInCommands;
return !idInCommands;
});
_.each(idsToRemove, function (id) {
idsToRemove.forEach(id => {
var operation = list._forceM2MUnlink ? 'FORGET': 'DELETE';
list._changes.push({operation: operation, id: id});
});
break;
case 'DELETE_ALL':
// first remove all pending 'ADD' operations
list._changes = _.reject(list._changes, function (change) {
return change.operation === 'ADD';
});
list._changes = list._changes.filter(change => change.operation !== 'ADD');
// then apply 'DELETE' on existing records
return this._applyX2ManyChange(record, fieldName, {
@@ -2393,7 +2392,7 @@ var BasicModel = AbstractModel.extend({
evalContext = evalContext || this._getEvalContext(element);
evaluated[k] = new Domain(mod, evalContext).compute(evalContext);
} catch (e) {
throw new Error(_.str.sprintf('for modifier "%s": %s', k, e.message));
throw new Error(sprintf('for modifier "%s": %s', k, e.message));
}
}
return evaluated;
@@ -2436,12 +2435,12 @@ var BasicModel = AbstractModel.extend({
const referenceFieldName = record.fields[fieldName].string;
this.displayNotification({
title: _.str.sprintf(
title: sprintf(
_t(`'%s' is unsynchronized with '%s'.`),
referenceFieldName,
modelFieldName,
),
message: _.str.sprintf(
message: sprintf(
_t(`If you change %s or %s, the synchronization will be reapplied and the data will be modified.`),
modelFieldName,
referenceFieldName,
@@ -2512,7 +2511,7 @@ var BasicModel = AbstractModel.extend({
return this._rpc({
model: model,
method: 'name_get',
args: [_.uniq(ids)],
args: [unique(ids)],
context: list.context,
})
.then(function (name_gets) {
@@ -2539,7 +2538,7 @@ var BasicModel = AbstractModel.extend({
var self = this;
options = options || {};
var fieldNames = options.fieldNames || record.getFieldNames(options);
fieldNames = _.uniq(fieldNames.concat(['display_name']));
fieldNames = unique(fieldNames.concat(['display_name']));
return this._rpc({
model: record.model,
method: 'read',
@@ -2761,7 +2760,7 @@ var BasicModel = AbstractModel.extend({
*/
_fetchRelatedData: function (list, toFetch, fieldName) {
var self = this;
var ids = _.keys(toFetch);
var ids = Object.keys(toFetch || {});
for (var i = 0; i < ids.length; i++) {
ids[i] = Number(ids[i]);
}
@@ -2772,7 +2771,7 @@ var BasicModel = AbstractModel.extend({
}
var def;
var fieldNames = _.keys(fieldInfo.relatedFields);
var fieldNames = Object.keys(fieldInfo.relatedFields || {});
if (fieldNames.length) {
var field = list.fields[fieldName];
def = this._rpc({
@@ -2782,12 +2781,14 @@ var BasicModel = AbstractModel.extend({
context: list.getContext({withoutRecordData: true}) || {},
});
} else {
def = Promise.resolve(_.map(ids, function (id) {
return {id:id};
}));
def = Promise.resolve(
ids.map((id) => {
return { id: id };
})
);
}
return def.then(function (result) {
var records = _.uniq(_.flatten(_.values(toFetch)));
var records = unique(Object.values(toFetch || {}).flat());
self._updateRecordsData(records, fieldName, result);
});
},
@@ -3121,7 +3122,7 @@ var BasicModel = AbstractModel.extend({
_.each(list.data, function (groupIndex) {
var group = self.localData[groupIndex];
var nextDataToFetch = self._getDataToFetch(group, fieldName);
_.each(_.keys(nextDataToFetch), function (id) {
_.each(Object.keys(nextDataToFetch), function (id) {
if (toFetch[id]) {
toFetch[id] = toFetch[id].concat(nextDataToFetch[id]);
} else {
@@ -3316,9 +3317,7 @@ var BasicModel = AbstractModel.extend({
list = this._applyX2ManyOperations(list);
this._sortList(list);
if (type === 'many2many' || list._forceM2MLink) {
var relRecordCreated = _.filter(relRecordAdded, function (rec) {
return typeof rec.res_id === 'string';
});
var relRecordCreated = relRecordAdded.filter(rec => typeof rec.res_id === 'string');
var realIDs = _.difference(list.res_ids, _.pluck(relRecordCreated, 'res_id'));
// deliberately generate a single 'replace' command instead
// of a 'delete' and a 'link' commands with the exact diff
@@ -3342,7 +3341,7 @@ var BasicModel = AbstractModel.extend({
} else if (type === 'one2many') {
var removedIds = _.difference(oldResIDs, list.res_ids);
var addedIds = _.difference(list.res_ids, oldResIDs);
var keptIds = _.intersection(oldResIDs, list.res_ids);
var keptIds = intersection(oldResIDs, list.res_ids);
// the didChange variable keeps track of the fact that at
// least one id was updated
@@ -3878,7 +3877,7 @@ var BasicModel = AbstractModel.extend({
}
if (field.type === 'one2many' || field.type === 'many2many') {
var ids;
if (!context[fieldName] || _.isArray(context[fieldName])) { // no dataPoint created yet
if (!context[fieldName] || Array.isArray(context[fieldName])) { // no dataPoint created yet
ids = context[fieldName] ? context[fieldName].slice(0) : [];
} else {
relDataPoint = this._applyX2ManyOperations(this.localData[context[fieldName]]);
@@ -3894,9 +3893,7 @@ var BasicModel = AbstractModel.extend({
* The webClient doesn't do literal id list comparison like ids == list
* Only relevant in o2m: m2m does create actual records in db
*/
ids = _.filter(ids, function (id) {
return typeof id !== 'string';
});
ids = ids.filter(id => typeof id !== 'string');
}
context[fieldName] = ids;
}
@@ -3949,7 +3946,7 @@ var BasicModel = AbstractModel.extend({
}
if (field.type === 'one2many' || field.type === 'many2many') {
let ids;
if (!fieldValue || _.isArray(fieldValue)) { // no dataPoint created yet
if (!fieldValue || Array.isArray(fieldValue)) { // no dataPoint created yet
ids = fieldValue ? fieldValue.slice(0) : [];
} else {
relDataPoint = self._applyX2ManyOperations(self.localData[fieldValue]);
@@ -3965,13 +3962,11 @@ var BasicModel = AbstractModel.extend({
* The webClient doesn't do literal id list comparison like ids == list
* Only relevant in o2m: m2m does create actual records in db
*/
ids = _.filter(ids, function (id) {
return typeof id !== 'string';
});
ids = ids.filter(id => typeof id !== 'string');
}
return ids;
}
if (field.type === "properties" && _.isArray(fieldValue)) {
if (field.type === "properties" && Array.isArray(fieldValue)) {
// remove deleted properties to be able
// to filter based on empty properties field
return fieldValue.filter(definition => !definition.definition_deleted);
@@ -4193,7 +4188,7 @@ var BasicModel = AbstractModel.extend({
limit: type === 'record' ? 1 : (params.limit || Number.MAX_SAFE_INTEGER),
loadMoreOffset: 0,
model: params.modelName,
offset: params.offset || (type === 'record' ? _.indexOf(res_ids, res_id) : 0),
offset: params.offset || (type === 'record' ? res_ids.indexOf(res_id) : 0),
openGroupByDefault: params.openGroupByDefault,
orderedBy: params.orderedBy || [],
orderedResIDs: params.orderedResIDs,
@@ -4265,7 +4260,7 @@ var BasicModel = AbstractModel.extend({
if (parentRecord && parentRecord.viewType in parentRecord.fieldsInfo) {
var originView = parentRecord.viewType;
fieldNames = _.union(fieldNames, Object.keys(parentRecord.fieldsInfo[originView]));
fieldNames = fieldNames.concat(Object.keys(parentRecord.fieldsInfo[originView]));
fieldsInfo[targetView] = _.defaults({}, fieldsInfo[targetView], parentRecord.fieldsInfo[originView]);
fields = _.defaults({}, fields, parentRecord.fields);
}
@@ -4594,7 +4589,7 @@ var BasicModel = AbstractModel.extend({
var many2ones = {};
var r;
commands = commands || []; // handle false value
var isCommandList = commands.length && _.isArray(commands[0]);
var isCommandList = commands.length && Array.isArray(commands[0]);
if (!isCommandList) {
commands = [[6, false, commands]];
}
@@ -4673,7 +4668,7 @@ var BasicModel = AbstractModel.extend({
});
// fetch many2ones display_name
_.each(_.keys(many2ones), function (name) {
_.each(Object.keys(many2ones), function (name) {
defs.push(self._fetchNameGets(x2manyList, name));
});
@@ -4701,7 +4696,7 @@ var BasicModel = AbstractModel.extend({
}
var record = self.localData[dataPointID];
var data = Object.assign({}, record.data, record._changes);
if (_.difference(fieldNames, _.keys(data)).length) {
if (_.difference(fieldNames, Object.keys(data)).length) {
missingIDs.push(resId);
}
}
@@ -4770,9 +4765,9 @@ var BasicModel = AbstractModel.extend({
var groupByField = list.groupedBy[0];
var rawGroupBy = groupByField.split(':')[0];
var fields = _.uniq(list.getFieldNames().concat(rawGroupBy));
var orderedBy = _.filter(list.orderedBy, function (order) {
var orderedBy = list.orderedBy?.filter(order => {
return order.name === rawGroupBy || list.fields[order.name].group_operator !== undefined;
});
}) || [];
var openGroupsLimit = list.groupsLimit || self.OPEN_GROUP_LIMIT;
var expand = list.openGroupByDefault && options.fetchRecordsWithGroups;
return this._rpc({
@@ -4924,7 +4919,7 @@ var BasicModel = AbstractModel.extend({
if (list.res_ids.length > list.limit && list.orderedBy.length) {
if (!list.orderedResIDs) {
var fieldNames = _.pluck(list.orderedBy, 'name');
def = this._readMissingFields(list, _.filter(list.res_ids, _.isNumber), fieldNames, options.withoutRecordData);
def = this._readMissingFields(list, list.res_ids.filter(id => Number.isFinite(id)), fieldNames, options.withoutRecordData);
}
def.then(function () {
self._sortList(list);
@@ -4953,7 +4948,7 @@ var BasicModel = AbstractModel.extend({
var fieldNames = list.getFieldNames();
for (var i = list.offset; i < upperBound; i++) {
var resId = currentResIDs[i];
if (_.isNumber(resId)) {
if (Number.isFinite(resId)) {
resIDs.push(resId);
}
}
@@ -5045,7 +5040,7 @@ var BasicModel = AbstractModel.extend({
element.count = element.res_ids.length;
}
if (element.type === 'record') {
element.offset = _.indexOf(element.res_ids, element.res_id);
element.offset = element.res_ids.indexOf(element.res_id);
}
var loadOptions = _.pick(options, 'fieldNames', 'viewType');
return this._load(element, loadOptions).then(function (result) {
@@ -1,6 +1,7 @@
/** @odoo-module alias=web.viewUtils **/
import { unique } from '@web/core/utils/arrays';
import * as dom from 'web.dom';
import * as utils from 'web.utils';
@@ -93,7 +94,7 @@ var viewUtils = {
};
if (options && options.extraClass) {
var classes = btnOptions.attrs.class ? btnOptions.attrs.class.split(' ') : [];
btnOptions.attrs.class = _.uniq(classes.concat(options.extraClass.split(' '))).join(' ');
btnOptions.attrs.class = unique(classes.concat(options.extraClass.split(' '))).join(' ');
}
var str = (node.attrs.string || '').replace(/_/g, '');
if (str) {
@@ -136,14 +136,17 @@ var ColorpickerWidget = Widget.extend({
// Update inputs
_.each(this.colorComponents, function (value, color) {
self.$(_.str.sprintf('.o_%s_input', color)).val(value);
self.$(`.o_${color}_input`).val(value);
});
// Update preview
this.$('.o_color_preview').css('background-color', this.colorComponents.cssColor);
// Update picker area and picker pointer position
this.$colorpickerArea.css('background-color', _.str.sprintf('hsl(%s, 100%%, 50%%)', this.colorComponents.hue));
this.$colorpickerArea.css(
"background-color",
`hsl(${this.colorComponents.hue}, 100%, 50%)`
);
var top = (100 - this.colorComponents.lightness) * this.$colorpickerArea.height() / 100;
var left = this.colorComponents.saturation * this.$colorpickerArea.width() / 100;
this.$colorpickerPointer.css({
@@ -251,7 +251,7 @@ var DomainTree = DomainNode.extend({
* false otherwise (the afterNode was not found)
*/
_addChild: function (domain, afterNode) {
var i = afterNode ? _.indexOf(this.children, afterNode) : this.children.length;
var i = afterNode ? this.children.indexOf(afterNode) : this.children.length;
if (i < 0) return false;
this.children.splice(i+1, 0, instantiateNode(this, this.model, domain, this.options));
@@ -355,7 +355,7 @@ var DomainTree = DomainNode.extend({
* widget does not own the child)
*/
_removeChild: function (oldChild) {
var i = _.indexOf(this.children, oldChild);
var i = this.children.indexOf(oldChild);
if (i < 0) return false;
this.children[i].destroy();
@@ -708,7 +708,7 @@ var DomainLeaf = DomainNode.extend({
// Adapt display value and operator for rendering
this.displayValue = this.value;
try {
if (selectedField && !selectedField.relation && !_.isArray(this.value)) {
if (selectedField && !selectedField.relation && !Array.isArray(this.value)) {
this.displayValue = field_utils.format[selectedField.type](this.value, selectedField);
}
} catch {/**/}
@@ -819,9 +819,9 @@ var DomainLeaf = DomainNode.extend({
this.operator = this.operator === "not set" ? "=" : "!=";
this.value = false;
} else if (_.contains(["in", "not in"], this.operator)) {
this.value = _.isArray(this.value) ? this.value : this.value ? ("" + this.value).split(",") : [];
this.value = Array.isArray(this.value) ? this.value : this.value ? ("" + this.value).split(",") : [];
} else {
if (_.isArray(this.value)) {
if (Array.isArray(this.value)) {
this.value = this.value.join(",");
}
this._changeValue(this.value, true);
@@ -868,7 +868,7 @@ var DomainLeaf = DomainNode.extend({
// Never display "true" or "false" strings from boolean value
if (_.isBoolean(this.value)) {
this.value = "";
} else if (_.isObject(this.value) && !_.isArray(this.value)) { // Can be object if parsed to x2x representation
} else if (_.isObject(this.value) && !Array.isArray(this.value)) { // Can be object if parsed to x2x representation
this.value = this.value.id || value || "";
}
}
@@ -971,7 +971,7 @@ var DomainLeaf = DomainNode.extend({
if (e.type === "keyup" && e.which !== $.ui.keyCode.ENTER) return;
if (!_.contains(["not in", "in"], this.operator)) return;
var values = _.isArray(this.value) ? this.value.slice() : [];
var values = Array.isArray(this.value) ? this.value.slice() : [];
var $input = this.$(".o_domain_leaf_value_tags input");
var val = $input.val().trim();
@@ -982,7 +982,7 @@ var DomainLeaf = DomainNode.extend({
}
},
on_remove_tag: function (e) {
var values = _.isArray(this.value) ? this.value.slice() : [];
var values = Array.isArray(this.value) ? this.value.slice() : [];
var val = this.$(e.currentTarget).data("value");
var index = values.indexOf(val);
@@ -245,7 +245,7 @@ var ModelFieldSelectorPopover = Widget.extend({
}).bind(this));
}
return def.then((function () {
return _.filter(modelFieldsCache.cache[model], function (f) {
return modelFieldsCache.cache[model].filter(f => {
return (!filters.searchable || f.searchable) && self.options.filter(f);
});
}).bind(this));
@@ -429,7 +429,12 @@ var ModelFieldSelectorPopover = Widget.extend({
var page = this.pages.at(-1);
this.$(".o_field_selector_popover_header .o_field_selector_title").text(this._getTitle());
var lines = _.filter(page, this.options.filter);
var lines = page.filter((item) => {
return Object.keys(this.options.filter).every((key) => {
return item[key] === this.options.filter[key];
});
});
if (this.searchValue) {
lines = fuzzyLookup(this.searchValue, lines, (l) => l.string);
}
@@ -27,8 +27,12 @@ export class TimezoneMismatchField extends SelectionField {
if (userOffset && this.props.record.data[this.props.name]) {
const offset = -new Date().getTimezoneOffset();
let browserOffset = offset < 0 ? "-" : "+";
browserOffset += _.str.sprintf("%02d", Math.abs(offset / 60));
browserOffset += _.str.sprintf("%02d", Math.abs(offset % 60));
browserOffset += Math.abs(offset / 60)
.toFixed(0)
.padStart(2, "0");
browserOffset += Math.abs(offset % 60)
.toFixed(0)
.padStart(2, "0");
return browserOffset !== userOffset;
} else if (!this.props.record.data[this.props.name]) {
return true;
@@ -85,10 +85,20 @@ QUnit.module('core', function () {
return String(d.getFullYear()) + '-01-30';
});
check("time.strftime('%Y-%m-%d %H:%M:%S')", function(d) {
return _.str.sprintf('%04d-%02d-%02d %02d:%02d:%02d',
d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds());
check("time.strftime('%Y-%m-%d %H:%M:%S')", function (d) {
return (
[
String(d.getUTCFullYear()).padStart(4, "0"),
String(d.getUTCMonth() + 1).padStart(2, "0"),
String(d.getUTCDate()).padStart(2, "0"),
].join("-") +
" " +
[
String(d.getUTCHours()).padStart(2, "0"),
String(d.getUTCMinutes()).padStart(2, "0"),
String(d.getUTCSeconds()).padStart(2, "0"),
].join(":")
);
});
});
@@ -98,8 +108,11 @@ QUnit.module('core', function () {
var check = makeTimeCheck(assert, pyUtils);
check("context_today().strftime('%Y-%m-%d')", function(d) {
return String(_.str.sprintf('%04d-%02d-%02d',
d.getFullYear(), d.getMonth() + 1, d.getDate()));
return [
String(d.getFullYear()).padStart(4, "0"),
String(d.getMonth() + 1).padStart(2, "0"),
String(d.getDate()).padStart(2, "0"),
].join("-");
});
});
@@ -709,8 +722,11 @@ QUnit.module('core', function () {
});
var d = new Date();
var today = _.str.sprintf("%04d-%02d-%02d",
d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
var today = [
String(d.getUTCFullYear()).padStart(4, "0"),
String(d.getUTCMonth() + 1).padStart(2, "0"),
String(d.getUTCDate()).padStart(2, "0"),
].join("-");
assert.deepEqual(result.domain, [
["type", "=", "contract"],
"|", ["state", "in", ["open", "draft"]],
@@ -777,8 +793,11 @@ QUnit.module('core', function () {
});
var date = new Date();
var today = _.str.sprintf("%04d-%02d-%02d",
date.getFullYear(), date.getMonth() + 1, date.getDate());
var today = [
String(date.getFullYear()).padStart(4, "0"),
String(date.getMonth() + 1).padStart(2, "0"),
String(date.getDate()).padStart(2, "0"),
].join("-");
assert.deepEqual(result.domain, [
['state', '!=', 'cancel'],
['opening_date', '>', today]
@@ -794,11 +813,17 @@ QUnit.module('core', function () {
contexts: [],
});
var date = new Date();
var today = _.str.sprintf("%04d-%02d-%02d",
date.getFullYear(), date.getMonth() + 1, date.getDate());
var today = [
String(date.getFullYear()).padStart(4, "0"),
String(date.getMonth() + 1).padStart(2, "0"),
String(date.getDate()).padStart(2, "0"),
].join("-");
date.setDate(date.getDate() - 15);
var ago_15_d = _.str.sprintf("%04d-%02d-%02d",
date.getFullYear(), date.getMonth() + 1, date.getDate());
var ago_15_d = [
String(date.getFullYear()).padStart(4, "0"),
String(date.getMonth() + 1).padStart(2, "0"),
String(date.getDate()).padStart(2, "0"),
].join("-");
assert.deepEqual(result.domain, [
['type', '=', 'in'],
['day', '<=', today],
@@ -381,14 +381,14 @@ var MockServer = Class.extend({
var v = pyUtils.py_eval(mod, {context: pyevalContext}) ? true: false;
if (inTreeView && !inListHeader && a === 'invisible') {
modifiers.column_invisible = v;
} else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) {
} else if (v || !(a in modifiers) || !Array.isArray(modifiers[a])) {
modifiers[a] = v;
}
}
});
_.each(modifiersNames, function (a) {
if (a in modifiers && (!!modifiers[a] === false || (_.isArray(modifiers[a]) && !modifiers[a].length))) {
if (a in modifiers && (!!modifiers[a] === false || (Array.isArray(modifiers[a]) && !modifiers[a].length))) {
delete modifiers[a];
}
});
@@ -470,7 +470,7 @@ var MockServer = Class.extend({
* @returns {Object[]} a list of records
*/
_getRecords: function (model, domain, { active_test = true } = {}) {
if (!_.isArray(domain)) {
if (!Array.isArray(domain)) {
throw new Error("MockServer._getRecords: given domain has to be an array.");
}
@@ -517,9 +517,7 @@ var MockServer = Class.extend({
}
return criterion;
});
records = _.filter(records, function (record) {
return self._evaluateDomain(domain, record);
});
records = records.filter(record => self._evaluateDomain(domain, record));
}
return records;
@@ -1217,7 +1215,7 @@ var MockServer = Class.extend({
}
}
var key = [model, viewID, viewType].join(',');
var arch = self.archs[key] || _.find(self.archs, function (_v, k) {
var arch = self.archs[key] || Object.keys(self.archs).find(k => {
var ka = k.split(',');
viewID = parseInt(ka[1], 10);
return ka[0] === model && ka[2] === viewType;
@@ -1252,7 +1250,7 @@ var MockServer = Class.extend({
else if (!ids) {
return []
}
if (!_.isArray(ids)) {
if (!Array.isArray(ids)) {
ids = [ids];
}
var records = this.data[model].records;
@@ -1299,9 +1297,7 @@ var MockServer = Class.extend({
var domain = (args && args[1]) || _kwargs.args || [];
var records = this._getRecords(model, domain);
if (str.length) {
records = _.filter(records, function (record) {
return record.display_name.indexOf(str) !== -1;
});
records = records.filter(record => record.display_name.indexOf(str) !== -1);
}
var result = _.map(records, function (record) {
return [record.id, record.display_name];
@@ -1380,7 +1376,7 @@ var MockServer = Class.extend({
_mockRead: function (model, args, _kwargs) {
var self = this;
var ids = args[0];
if (!_.isArray(ids)) {
if (!Array.isArray(ids)) {
ids = [ids];
}
var fields = args[1] && args[1].length ? _.uniq(args[1].concat(['id'])) : Object.keys(this.data[model].fields);
@@ -1467,7 +1463,7 @@ var MockServer = Class.extend({
// if no fields have been given, the server picks all stored fields
if (kwargs.fields.length === 0) {
aggregatedFields = _.keys(this.data[model].fields);
aggregatedFields = Object.keys(this.data[model].fields);
}
var groupByFieldNames = _.map(groupBy, function (groupByField) {
@@ -1475,7 +1471,7 @@ var MockServer = Class.extend({
});
// filter out non existing fields
aggregatedFields = _.filter(aggregatedFields, function (name) {
aggregatedFields = aggregatedFields.filter(name => {
return name in self.data[model].fields && !(_.contains(groupByFieldNames,name));
});
@@ -1827,7 +1823,7 @@ var MockServer = Class.extend({
var records = this._getRecords(args.model, args.domain || [], {
active_test,
});
var fields = args.fields && args.fields.length ? args.fields : _.keys(this.data[args.model].fields);
var fields = args.fields && args.fields.length ? args.fields : Object.keys(this.data[args.model].fields);
var nbRecords = records.length;
var offset = args.offset || 0;
if (args.sort) {
@@ -1870,12 +1866,10 @@ var MockServer = Class.extend({
*/
_mockUnlink: function (model, args) {
var ids = args[0];
if (!_.isArray(ids)) {
if (!Array.isArray(ids)) {
ids = [ids];
}
this.data[model].records = _.reject(this.data[model].records, function (record) {
return _.contains(ids, record.id);
});
this.data[model].records = this.data[model].records.filter(record => !_.contains(ids, record.id));
// update value of relationnal fields pointing to the deleted records
_.each(this.data, function (d) {
@@ -146,7 +146,7 @@ async function _getMockedOwlEnv(params, mockServer) {
delete servicesToDeploy[serviceName];
services[serviceName].start();
} else {
const serviceNames = _.keys(servicesToDeploy);
const serviceNames = Object.keys(servicesToDeploy);
if (serviceNames.length) {
console.warn("Non loaded services:", serviceNames);
}
@@ -2303,7 +2303,7 @@ QUnit.module("Fields", (hooks) => {
if (args.method === "write") {
saveCount++;
const nbCommands = args.args[1].p.length;
const nbLinkCommands = _.filter(args.args[1].p, function (command) {
const nbLinkCommands = args.args[1].p.filter((command) => {
return command[0] === 4;
}).length;
switch (saveCount) {
@@ -7,6 +7,7 @@ import dom from "web.dom";
import Dialog from "web.Dialog";
import Widget from "web.Widget";
import localStorage from "web.local_storage";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
@@ -283,9 +284,9 @@ var ViewEditor = Widget.extend({
if (typeof this.viewKey === "number") {
initResID = this.viewKey;
} else {
var view = _.findWhere(this.views, {xml_id: this.viewKey});
var view = Object.values(this.views).find(view => view.xml_id === this.viewKey);
if (!view) {
view = _.findWhere(this.views, {key: this.viewKey});
view = Object.values(this.views).find(view => view.key === this.viewKey);
}
initResID = view.id;
}
@@ -425,11 +426,11 @@ var ViewEditor = Widget.extend({
this.aceEditor.setSession(editingSession);
if (this.currentType === 'xml') {
this.$viewID.text(_.str.sprintf(_t("Template ID: %s"), this.views[resID].key));
this.$viewID.text(sprintf(_t("Template ID: %s"), this.views[resID].key));
} else if (this.currentType === 'scss') {
this.$viewID.text(_.str.sprintf(_t("SCSS file: %s"), resID));
this.$viewID.text(sprintf(_t("SCSS file: %s"), resID));
} else {
this.$viewID.text(_.str.sprintf(_t("JS file: %s"), resID));
this.$viewID.text(sprintf(_t("JS file: %s"), resID));
}
const isCustomized = this._isCustomResource(resID);
this.$lists[this.currentType].select2('val', resID);
@@ -518,9 +519,7 @@ var ViewEditor = Widget.extend({
function _processViews(views) {
// Only keep the active views and index them by ID.
Object.assign(this.views, _.indexBy(_.filter(views, function (view) {
return view.active;
}), 'id'));
Object.assign(this.views, _.indexBy(views.filter(view => view.active), 'id'));
// Initialize a 0 level for each view and assign them an array containing their children.
var self = this;
@@ -893,7 +892,7 @@ var ViewEditor = Widget.extend({
label: bundleInfos[0],
}).appendTo($list);
_.each(bundleInfos[1], function (dataInfo) {
var name = dataInfo.url.substring(_.lastIndexOf(dataInfo.url, '/') + 1, dataInfo.url.length - lettersToRemove);
var name = dataInfo.url.substring(dataInfo.url.lastIndexOf('/') + 1, dataInfo.url.length - lettersToRemove);
$optgroup.append($('<option/>', {
value: dataInfo.url,
text: name,
@@ -4,13 +4,14 @@ import concurrency from "web.concurrency";
import core from "web.core";
import Dialog from "web.Dialog";
import dom from "web.dom";
import {Markup, sprintf, confine} from "web.utils";
import {Markup, confine} from "web.utils";
import Widget from "web.Widget";
import options from "web_editor.snippets.options";
import {ColorPaletteWidget} from "web_editor.ColorPalette";
import SmoothScrollOnDrag from "web.smooth_scroll_on_drag";
import {getCSSVariableValue} from "web_editor.utils";
import * as gridUtils from "@web_editor/js/common/grid_layout_utils";
import { sprintf } from "@web/core/utils/strings";
const QWeb = core.qweb;
import {closestElement} from "@web_editor/js/editor/odoo-editor/src/utils/utils";
@@ -666,7 +667,7 @@ var SnippetEditor = Widget.extend({
*/
toggleTargetVisibility: async function (show) {
show = this._toggleVisibilityStatus(show);
var styles = _.values(this.styles);
var styles = Object.values(this.styles);
const proms = _.sortBy(styles, '__order').map(style => {
return show ? style.onTargetShow() : style.onTargetHide();
});
@@ -2900,12 +2901,12 @@ var SnippetsMenu = Widget.extend({
const btnRenameEl = document.createElement('we-button');
btnRenameEl.dataset.snippetId = $snippet.data('oeSnippetId');
btnRenameEl.classList.add('o_rename_btn', 'fa', 'fa-pencil', 'btn', 'o_we_hover_success');
btnRenameEl.title = _.str.sprintf(_t("Rename %s"), name);
btnRenameEl.title = sprintf(_t("Rename %s"), name);
$snippet.append(btnRenameEl);
const btnEl = document.createElement('we-button');
btnEl.dataset.snippetId = $snippet.data('oeSnippetId');
btnEl.classList.add('o_delete_btn', 'fa', 'fa-trash', 'btn', 'o_we_hover_danger');
btnEl.title = _.str.sprintf(_t("Delete %s"), name);
btnEl.title = sprintf(_t("Delete %s"), name);
$snippet.append(btnEl);
}
})
@@ -3728,9 +3729,9 @@ var SnippetsMenu = Widget.extend({
var moduleID = $snippet.data('moduleId');
var name = $snippet.attr('name');
new Dialog(this, {
title: _.str.sprintf(_t("Install %s"), name),
title: sprintf(_t("Install %s"), name),
size: 'medium',
$content: $('<div/>', {text: _.str.sprintf(_t("Do you want to install the %s App?"), name)}).append(
$content: $('<div/>', {text: sprintf(_t("Do you want to install the %s App?"), name)}).append(
$('<a/>', {
target: '_blank',
href: '/web#id=' + moduleID + '&view_type=form&model=ir.module.module&action=base.open_module_tree',
@@ -3817,7 +3818,7 @@ var SnippetsMenu = Widget.extend({
new Dialog(this, {
size: 'medium',
title: _t('Confirmation'),
$content: $('<div><p>' + _.str.sprintf(_t("Are you sure you want to delete the snippet: %s?"), $snippet.attr('name')) + '</p></div>'),
$content: $('<div><p>' + sprintf(_t("Are you sure you want to delete the snippet: %s?"), $snippet.attr('name')) + '</p></div>'),
buttons: [{
text: _t("Yes"),
close: true,
@@ -33,6 +33,7 @@ import {
} from "web_editor.image_processing";
import * as OdooEditorLib from "@web_editor/js/editor/odoo-editor/src/OdooEditor";
import {SIZES, MEDIAS_BREAKPOINTS} from "@web/core/ui/ui_service";
import { sprintf } from "@web/core/utils/strings";
var qweb = core.qweb;
var _t = core._t;
@@ -5771,9 +5772,9 @@ const ImageHandlerOption = SnippetOptionWidget.extend({
1024: '1024px',
1920: '1920px',
};
widths[img.naturalWidth] = _.str.sprintf(_t("%spx"), img.naturalWidth);
widths[optimizedWidth] = _.str.sprintf(_t("%dpx (Suggested)"), optimizedWidth);
widths[maxWidth] = _.str.sprintf(_t("%dpx (Original)"), maxWidth);
widths[img.naturalWidth] = sprintf(_t("%spx"), img.naturalWidth);
widths[optimizedWidth] = sprintf(_t("%dpx (Suggested)"), optimizedWidth);
widths[maxWidth] = sprintf(_t("%dpx (Original)"), maxWidth);
return Object.entries(widths)
.filter(([width]) => width <= maxWidth)
.sort(([v1], [v2]) => v1 - v2);
@@ -7815,7 +7816,7 @@ registry.SnippetSave = SnippetOptionWidget.extend({
reloadEditor: true,
invalidateSnippetCache: true,
onSuccess: async () => {
const defaultSnippetName = _.str.sprintf(_t("Custom %s"), this.data.snippetName);
const defaultSnippetName = sprintf(_t("Custom %s"), this.data.snippetName);
const targetCopyEl = this.$target[0].cloneNode(true);
delete targetCopyEl.dataset.name;
// By the time onSuccess is called after request_save, the
+2 -1
View File
@@ -7,6 +7,7 @@ import * as OdooEditorLib from "@web_editor/js/editor/odoo-editor/src/OdooEditor
import Widget from "web.Widget";
import Wysiwyg from "web_editor.wysiwyg";
import options from "web_editor.snippets.options";
import { range } from "@web/core/utils/numbers";
const COLOR_PICKER_TEMPLATE = `
<colorpicker>
@@ -356,7 +357,7 @@ var keyboardMap = {
"91": "OS_KEY", // 'left command': Windows Key (Windows) or Command Key (Mac)
"93": "CONTEXT_MENU", // 'right command'
};
_.each(_.range(40, 127), function (keyCode) {
_.each(range(40, 127), function (keyCode) {
if (!keyboardMap[keyCode]) {
keyboardMap[keyCode] = String.fromCharCode(keyCode);
}
@@ -8,6 +8,7 @@ import { useService } from '@web/core/utils/hooks';
import { uploadService, AUTOCLOSE_DELAY } from '@web_editor/components/upload_progress_toast/upload_service';
import { useState, Component } from "@odoo/owl";
import { sprintf } from '@web/core/utils/strings';
class UnsplashCredentials extends Component {
setup() {
@@ -224,8 +225,8 @@ patch(uploadService, 'upload_service_unsplash', {
const file = service.addFile({
id: service.fileId,
name: records.length > 1 ?
_.str.sprintf(env._t("Uploading %s '%s' images."), records.length, records[0].query) :
_.str.sprintf(env._t("Uploading '%s' image."), records[0].query),
sprintf(env._t("Uploading %s '%s' images."), records.length, records[0].query) :
sprintf(env._t("Uploading '%s' image."), records[0].query),
size: null,
progress: 0,
});
@@ -36,7 +36,7 @@ export const WebsiteAceEditor = AceEditor.extend({
// We need to find the new ID (either because the view became
// specific or because its parent was edited too and the view
// got copy/unlink).
const selectedView = _.findWhere(this.views, {id: this._getSelectedResource()});
const selectedView = Object.values(this.views).find(view => view.id === this._getSelectedResource());
let context;
this.trigger_up('context_get', {
callback: (ctx) => {
@@ -55,7 +55,7 @@ var Dashboard = AbstractAction.extend({
return Promise.all([loadBundle(this), this._super()]).then(function() {
return self.fetch_data();
}).then(function(){
var website = _.findWhere(self.websites, {selected: true});
var website = self.websites.find(w => w.selected);
self.website_id = website ? website.id : false;
});
},
@@ -102,7 +102,7 @@ var Dashboard = AbstractAction.extend({
on_go_to_website: function (ev) {
ev.preventDefault();
var website = _.findWhere(this.websites, {selected: true});
var website = this.websites.find(website => website.selected);
window.location.replace(`/web#action=website.website_preview&website_id=${website.id}`);
},
@@ -921,7 +921,7 @@ registry.socialShare = publicWidget.Widget.extend({
'twitter': 'https://twitter.com/intent/tweet?original_referer=' + url + '&text=' + encodeURIComponent(title + hashtags + ' - ') + url,
'linkedin': 'https://www.linkedin.com/sharing/share-offsite/?url=' + url,
};
if (!_.contains(_.keys(socialNetworks), social)) {
if (!_.contains(Object.keys(socialNetworks), social)) {
return;
}
var wHeight = 500;
@@ -177,7 +177,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMi
url: encodeURIComponent($target.attr('href').replace(/[&?]edit_translations[^&?]+/, '')),
hash: encodeURIComponent(window.location.hash)
};
window.location.href = _.str.sprintf("/website/lang/%(lang)s?r=%(url)s%(hash)s", redirect);
window.location.href = `/website/lang/${redirect.lang}?r=${redirect.url}${redirect.hash}`;
},
/**
* @private
@@ -11,6 +11,7 @@ import wLinkPopoverWidget from "@website/js/widgets/link_popover_widget";
import wUtils from "website.utils";
import {isImageSupportedForStyle} from "web_editor.image_processing";
import "website.s_popup_options";
import { range } from "@web/core/utils/numbers";
var _t = core._t;
var qweb = core.qweb;
@@ -1387,7 +1388,7 @@ options.registry.OptionsTab = options.Class.extend({
await this._super(...arguments);
const extraSaturationRangeEl = uiFragment.querySelector(`we-range[data-param=${this.GRAY_PARAMS.EXTRA_SATURATION}]`);
if (extraSaturationRangeEl) {
const baseGrays = _.range(100, 1000, 100).map(id => {
const baseGrays = range(100, 1000, 100).map(id => {
const gray = weUtils.getCSSVariableValue(`base-${id}`);
const grayRGB = ColorpickerWidget.convertCSSColorToRgba(gray);
const hsl = ColorpickerWidget.convertRgbToHsl(grayRGB.red, grayRGB.green, grayRGB.blue);
@@ -3,6 +3,7 @@
import {_t} from "web.core";
import {Markup} from "web.utils";
import { registry } from "@web/core/registry";
import { sprintf } from "@web/core/utils/strings";
function addMedia(position = "right") {
return {
@@ -104,7 +105,7 @@ function changeOption(optionName, weName = '', optionTooltipLabel = '', position
const option_block = `${noPalette} we-customizeblock-option[class='snippet-option-${optionName}']`;
return {
trigger: `${option_block} ${weName}, ${option_block} [title='${weName}']`,
content: Markup(_.str.sprintf(_t("<b>Click</b> on this option to change the %s of the block."), optionTooltipLabel)),
content: Markup(sprintf(_t("<b>Click</b> on this option to change the %s of the block."), optionTooltipLabel)),
position: position,
run: "click",
};
@@ -115,7 +116,7 @@ function selectNested(trigger, optionName, alt_trigger = null, optionTooltipLabe
const option_block = `${noPalette} we-customizeblock-option[class='snippet-option-${optionName}']`;
return {
trigger: trigger,
content: Markup(_.str.sprintf(_t("<b>Select</b> a %s."), optionTooltipLabel)),
content: Markup(sprintf(_t("<b>Select</b> a %s."), optionTooltipLabel)),
alt_trigger: alt_trigger == null ? undefined : `${option_block} ${alt_trigger}`,
position: position,
run: 'click',
@@ -132,7 +133,7 @@ function changePaddingSize(direction) {
}
return {
trigger: `iframe .oe_overlay.ui-draggable.o_we_overlay_sticky.oe_active .o_handle.${paddingDirection}`,
content: Markup(_.str.sprintf(_t("<b>Slide</b> this button to change the %s padding"), direction)),
content: Markup(sprintf(_t("<b>Slide</b> this button to change the %s padding"), direction)),
consumeEvent: 'mousedown',
position: position,
};
@@ -229,7 +230,7 @@ function dragNDrop(snippet, position = "bottom") {
return {
trigger: `#oe_snippets .oe_snippet[name="${snippet.name}"] .oe_snippet_thumbnail:not(.o_we_already_dragging)`,
extra_trigger: ".o_website_preview.editor_enable.editor_has_snippets",
content: Markup(_.str.sprintf(_t("Drag the <b>%s</b> building block and drop it at the bottom of the page."), snippet.name)),
content: Markup(sprintf(_t("Drag the <b>%s</b> building block and drop it at the bottom of the page."), snippet.name)),
position: position,
// Normally no main snippet can be dropped in the default footer but
// targeting it allows to force "dropping at the end of the page".
+2 -1
View File
@@ -1,5 +1,6 @@
/** @odoo-module alias=website.utils **/
import { intersection } from "@web/core/utils/arrays";
import core from "web.core";
const { qweb, _t } = core;
@@ -186,7 +187,7 @@ function prompt(options, _qweb) {
btn_secondary_title: _t('Cancel'),
}, options || {});
var type = _.intersection(Object.keys(options), ['input', 'textarea', 'select']);
var type = intersection(Object.keys(options), ['input', 'textarea', 'select']);
type = type.length ? type[0] : 'input';
options.field_type = type;
options.field_name = options.field_name || options[type];
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { pick } from "@web/core/utils/objects";
import options from "web_editor.snippets.options";
options.registry.facebookPage = options.Class.extend({
@@ -19,8 +20,7 @@ options.registry.facebookPage = options.Class.extend({
small_header: true,
hide_cover: true,
};
this.fbData = _.defaults(_.pick(this.$target[0].dataset, _.keys(defaults)), defaults);
this.fbData = Object.assign({}, defaults, pick(this.$target[0].dataset, Object.keys(defaults)), defaults);
if (!this.fbData.href) {
// Fetches the default url for facebook page from website config
var self = this;
@@ -312,7 +312,7 @@ options.registry.gallery = options.Class.extend({
this.mode('reset', this.getMode());
} else if (name === 'image_index_request') {
var imgs = this._getImages();
var position = _.indexOf(imgs, data.$image[0]);
var position = imgs.indexOf(data.$image[0]);
imgs.splice(position, 1);
switch (data.position) {
case 'first':
@@ -8,6 +8,7 @@
import publicWidget from "web.public.widget";
import dom from "web.dom";
import concurrency from "web.concurrency";
import { sprintf } from "@web/core/utils/strings";
var _t = core._t;
var qweb = core.qweb;
@@ -415,9 +416,7 @@
// filter neither on required, nor on checkbox and
// checking the validity of the group of checkbox is
// currently done for each checkbox of that group...
var checkboxes = _.filter(inputs, function (input) {
return input.required && input.type === 'checkbox';
});
var checkboxes = inputs.filter(input => input.required && input.type === 'checkbox');
return !_.any(checkboxes, checkbox => checkbox.checkValidity());
// Special cases for dates and datetimes
@@ -493,13 +492,13 @@
if (datetime.isValid()) {
return time.datetime_to_str(datetime.toDate());
}
throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
throw new Error(sprintf(_t("'%s' is not a correct datetime"), value));
case 'date':
var date = moment(value, [date_pattern, date_pattern_wo_zero], true);
if (date.isValid()) {
return time.date_to_str(date.toDate());
}
throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value));
throw new Error(sprintf(_t("'%s' is not a correct date"), value));
}
return value;
},
@@ -7,6 +7,7 @@ import Dialog from "web.Dialog";
import dom from "web.dom";
import {generateHTMLId} from "web_editor.utils";
import "website.editor.snippets.options";
import { sprintf } from "@web/core/utils/strings";
const qweb = core.qweb;
const _t = core._t;
@@ -307,9 +308,9 @@ options.registry.WebsiteFormEditor = FormEditor.extend({
model: 'ir.model',
method: 'get_compatible_form_models',
});
const targetModelName = this.$target[0].dataset.model_name || 'mail.mail';
this.activeForm = _.findWhere(this.models, {model: targetModelName});
this.activeForm = this.models.find(m => m.model === targetModelName);
// Create the Form Action select
this.selectActionEl = document.createElement('we-select');
this.selectActionEl.setAttribute('string', 'Action');
@@ -708,7 +709,7 @@ options.registry.WebsiteFormEditor = FormEditor.extend({
oldFormInfo = FormEditorRegistry.get(oldFormKey);
}
this.$target.find('.s_website_form_field').remove();
this.activeForm = _.findWhere(this.models, {id: modelId});
this.activeForm = this.models.find(model => model.id === modelId);
}
const formKey = this.activeForm.website_form_key;
const formInfo = FormEditorRegistry.get(formKey);
@@ -1293,7 +1294,7 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
const list = document.createElement('we-list');
const optionText = select ? 'Option' : type === 'selection' ? 'Radio' : 'Checkbox';
list.setAttribute('string', `${optionText} List`);
list.dataset.addItemTitle = _.str.sprintf(_t("Add new %s"), optionText);
list.dataset.addItemTitle = sprintf(_t("Add new %s"), optionText);
list.dataset.renderListItems = '';
list.dataset.hasDefault = ['one2many', 'many2many'].includes(type) ? 'multiple' : 'unique';
@@ -203,14 +203,14 @@ export class NewContentModal extends Component {
const {id, name} = this.modulesInfo[element.moduleName];
const dialogProps = {
title: element.title,
installationText: _.str.sprintf(this.newContentText.installNeeded, name),
installationText: sprintf(this.newContentText.installNeeded, name),
installModule: async () => {
// Update the NewContentElement with installing icon and text.
this.state.newContentElements = this.state.newContentElements.map(el => {
if (el.moduleXmlId === element.moduleXmlId) {
el.status = MODULE_STATUS.INSTALLING;
el.icon = xml`<i class="fa fa-spin fa-circle-o-notch"/>`;
el.title = _.str.sprintf(this.newContentText.installPleaseWait, name);
el.title = sprintf(this.newContentText.installPleaseWait, name);
}
return el;
});
@@ -226,7 +226,7 @@ export class NewContentModal extends Component {
if (el.moduleXmlId === element.moduleXmlId) {
el.status = MODULE_STATUS.FAILED_TO_INSTALL;
el.icon = xml`<i class="fa fa-exclamation-triangle"/>`;
el.title = _.str.sprintf(this.newContentText.failed, name);
el.title = sprintf(this.newContentText.failed, name);
}
return el;
});
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { sprintf } from "@web/core/utils/strings";
import dom from "web.dom";
$.fn.share = function (options) {
@@ -32,8 +33,8 @@ $.fn.share = function (options) {
// Note: all (non-localhost) urls in a tweet have 23 characters https://support.twitter.com/articles/78124
var selectedText = this.getSelection('string').substring(0, option.maxLength - baseLength - 23);
var text = window.btoa(encodeURIComponent(_.str.sprintf(tweet, selectedText, window.location.href)));
$popover_content.append(_.str.sprintf(
var text = window.btoa(encodeURIComponent(sprintf(tweet, selectedText, window.location.href)));
$popover_content.append(sprintf(
"<a onclick=\"window.open('%s' + atob('%s'), '_%s','location=yes,height=570,width=520,scrollbars=yes,status=yes')\"><i class=\"ml4 mr4 fa fa-twitter fa-lg\"/></a>",
option.shareLink, text, option.target));
}
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { sprintf } from "@web/core/utils/strings";
import {_t} from "web.core";
import publicWidget from "web.public.widget";
import time from "web.time";
@@ -238,7 +239,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({
const contactName = ev.currentTarget.value.trim();
let titleEl = this.el.querySelector('.title');
if (!titleEl.value.trim()) {
titleEl.value = contactName ? _.str.sprintf(_t("%s's Opportunity"), contactName) : '';
titleEl.value = contactName ? sprintf(_t("%s's Opportunity"), contactName) : '';
}
},
/**
@@ -1,5 +1,6 @@
/** @odoo-module alias=website_event_track.website_event_track_reminder **/
import { sprintf } from "@web/core/utils/strings";
import core from "web.core";
var _t = core._t;
import publicWidget from "web.public.widget";
@@ -49,7 +50,7 @@ publicWidget.registry.websiteEventTrackReminder = publicWidget.Widget.extend({
self.displayNotification({
type: 'info',
title: _t('Error'),
message: _.str.sprintf(_t('Talk already in your Favorites')),
message: sprintf(_t('Talk already in your Favorites')),
});
} else {
self.reminderOn = reminderOnValue;
@@ -63,9 +63,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
maximumSelectionSize: 5,
lastsearch: [],
createSearchChoice: function (term) {
if (_.filter(self.lastsearch, function (s) {
return s.text.localeCompare(term) === 0;
}).length === 0) {
if (self.lastsearch.filter(s => s.text.localeCompare(term) === 0).length === 0) {
//check Karma
if (parseInt($('#karma').val()) >= parseInt($('#karma_edit_retag').val())) {
return {
@@ -52,7 +52,7 @@ var SelectBox = publicWidget.Widget.extend({
if (self._objectExists(term)) {
return null;
}
return {id: term, text: _.str.sprintf("Create '%s'", term)};
return { id: term, text: `Create '${term}'` };
},
createSearchChoicePosition: 'bottom',
multiple: false,
@@ -2,6 +2,7 @@
import core, { _t } from 'web.core';
import checkoutForm from 'payment.checkout_form';
import { sprintf } from '@web/core/utils/strings';
checkoutForm.include({
events: Object.assign({}, checkoutForm.prototype.events || {}, {
@@ -45,7 +46,7 @@ checkoutForm.include({
const $field = this.$('input[name="' + id + '"],select[name="' + id + '"]');
$field.removeClass('is-invalid').popover('dispose');
if (!$field.val().trim()) {
errorFields[id] = _.str.sprintf(_t("Field '%s' is mandatory"), mandatoryFields[id]);
errorFields[id] = sprintf(_t("Field '%s' is mandatory"), mandatoryFields[id]);
}
}
if (Object.keys(errorFields).length) {
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { sprintf } from '@web/core/utils/strings';
import {_t} from 'web.core';
import publicWidget from 'web.public.widget';
@@ -126,7 +127,7 @@ publicWidget.registry.DonationSnippet = publicWidget.Widget.extend({
} else if (amount < parseFloat(minAmount)) {
const before = this.currency.position === "before" ? this.currency.symbol : "";
const after = this.currency.position === "after" ? this.currency.symbol : "";
errorMessage = _.str.sprintf(_t("The minimum donation amount is %s%s%s"), before, minAmount, after);
errorMessage = sprintf(_t("The minimum donation amount is %s%s%s"), before, minAmount, after);
}
if (errorMessage) {
$(ev.currentTarget).before($('<p>', {
@@ -6,9 +6,7 @@
import { registry } from "@web/core/registry";
import "website_sale.tour";
var addCartStepIndex = _.findIndex(registry.category("web_tour.tours").get("shop_buy_product").steps, function (step) {
return (step.id === 'add_cart_step');
});
var addCartStepIndex = registry.category("web_tour.tours").get("shop_buy_product").steps.findIndex(step => step.id === 'add_cart_step');
registry.category("web_tour.tours").get("shop_buy_product").steps.splice(addCartStepIndex + 1, 1, {
content: "click in modal on 'Proceed to checkout' button",
@@ -2,6 +2,7 @@
import { _t } from 'web.core';
import { PortalChatter } from 'portal.chatter';
import { sprintf } from '@web/core/utils/strings';
/**
* PortalChatter
@@ -18,7 +19,7 @@ PortalChatter.include({
_reloadChatterContent: async function (data) {
await this._super(...arguments);
if (this.options.res_model === "slide.channel") {
$('#review-tab').text(_.str.sprintf(_t('Reviews (%d)'), data.rating_count));
$('#review-tab').text(sprintf(_t('Reviews (%d)'), data.rating_count));
}
},
});
@@ -288,7 +288,7 @@
*/
_getCurrentIndex: function () {
var slide = this.get('slideEntry');
var currentIndex = _.findIndex(this.slideEntries, function (entry) {
var currentIndex = this.slideEntries.findIndex(entry =>{
return entry.id === slide.id && entry.isQuiz === slide.isQuiz;
});
return currentIndex;
@@ -609,11 +609,11 @@
} else if (slideData.category === 'video' && slideData.videoSourceType === 'vimeo') {
slideData.embedCode = Markup(slideData.embedCode);
} else if (slideData.category === 'infographic') {
slideData.embedUrl = _.str.sprintf('/web/image/slide.slide/%s/image_1024', slideData.id);
slideData.embedUrl = `/web/image/slide.slide/${slideData.id}/image_1024`;
} else if (slideData.category === 'document') {
slideData.embedUrl = $(slideData.embedCode).attr('src');
}
// fill empty property to allow searching on it with _.filter(list, matcher)
// fill empty property to allow searching on it with list.filter(matcher)
slideData.isQuiz = !!slideData.isQuiz;
slideData.hasQuestion = !!slideData.hasQuestion;
// technical settings for the Fullscreen to work
@@ -644,7 +644,7 @@
if (this.get('slide').isQuiz){
params.quiz = 1;
}
var fullscreenUrl = _.str.sprintf('%s?%s', url, $.param(params));
var fullscreenUrl = `${url}?${$.param(params)}`;
history.pushState(null, '', fullscreenUrl);
},
/**
@@ -755,8 +755,7 @@
_toggleSlideCompleted: async function (slide, completed = true) {
await this._super(...arguments);
const slideMatch = _.matcher({id: slide.id});
const fsSlides = _.filter(this.slides, slideMatch);
const fsSlides = this.slides.filter(_slide => _slide.id === slide.id);
fsSlides.forEach(slide => slide.completed = completed);
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { sprintf } from '@web/core/utils/strings';
import { _t } from 'web.core';
import Dialog from 'web.Dialog';
import publicWidget from 'web.public.widget';
@@ -111,7 +112,7 @@ var TagCourseDialog = Dialog.extend({
},
createSearchChoice: function (term, data) {
var addedTags = $(this.opts.element).select2('data');
if (_.filter(_.union(addedTags, data), function (tag) {
if (addedTags.concat(data).filter(tag => {
return tag.text.toLowerCase().localeCompare(term.toLowerCase()) === 0;
}).length === 0) {
if (this.opts.can_create) {
@@ -119,7 +120,7 @@ var TagCourseDialog = Dialog.extend({
id: _.uniqueId('tag_'),
create: true,
tag: term,
text: _.str.sprintf(_t("Create new %s '%s'"), tag, term),
text: sprintf(_t("Create new %s '%s'"), tag, term),
};
} else {
return undefined;
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { sprintf } from '@web/core/utils/strings';
import {_t, qweb as QWeb} from 'web.core';
import Dialog from 'web.Dialog';
import publicWidget from 'web.public.widget';
@@ -293,7 +294,7 @@ var SlideUploadDialog = Dialog.extend({
},
createSearchChoice: function (term, data) {
var addedTags = $(this.opts.element).select2('data');
if (_.filter(_.union(addedTags, data), function (tag) {
if (addedTags.concat(data).filter(tag => {
return tag.text.toLowerCase().localeCompare(term.toLowerCase()) === 0;
}).length === 0) {
if (this.opts.can_create) {
@@ -301,7 +302,7 @@ var SlideUploadDialog = Dialog.extend({
id: _.uniqueId('tag_'),
create: true,
tag: term,
text: _.str.sprintf(_t("Create new %s '%s'"), tag, term),
text: sprintf(_t("Create new %s '%s'"), tag, term),
};
} else {
return undefined;
@@ -650,23 +651,23 @@ var SlideUploadDialog = Dialog.extend({
this.set('state', '_import');
if (this.modulesToInstallStatus.installing) {
this.$('#o_wslides_install_module_text')
.text(_.str.sprintf(_t('Already installing "%s".'), this.modulesToInstallStatus.name));
.text(sprintf(_t('Already installing "%s".'), this.modulesToInstallStatus.name));
} else if (this.modulesToInstallStatus.failed) {
this.$('#o_wslides_install_module_text')
.text(_.str.sprintf(_t('Failed to install "%s".'), this.modulesToInstallStatus.name));
.text(sprintf(_t('Failed to install "%s".'), this.modulesToInstallStatus.name));
}
} else {
this.modulesToInstallStatus = Object.assign({}, this.modulesToInstall.find( function (item) { return item.id === moduleId; }));
this.set('state', '_import');
this.$('#o_wslides_install_module_text')
.text(_.str.sprintf(_t('Do you want to install the "%s" app?'), this.modulesToInstallStatus.name));
.text(sprintf(_t('Do you want to install the "%s" app?'), this.modulesToInstallStatus.name));
}
},
_onClickInstallModuleConfirm: function () {
var self = this;
var $el = this.$('#o_wslides_install_module_text');
$el.text(_.str.sprintf(_t('Installing "%s".'), this.modulesToInstallStatus.name));
$el.text(sprintf(_t('Installing "%s".'), this.modulesToInstallStatus.name));
this.modulesToInstallStatus.installing = true;
this._resetModalButton();
this._rpc({
@@ -681,7 +682,7 @@ var SlideUploadDialog = Dialog.extend({
}
window.location.href = redirectUrl;
}, function () {
$el.text(_.str.sprintf(_t('Failed to install "%s".'), self.modulesToInstallStatus.name));
$el.text(sprintf(_t('Failed to install "%s".'), self.modulesToInstallStatus.name));
self.modulesToInstallStatus.installing = false;
self.modulesToInstallStatus.failed = true;
self._resetModalButton();