[REF] *: Replace underscore functions by native JS

Replace _.map(), _.flatten(), _.delay(), _.contains(), _.pluck(), _.isUndefined(), _.isEmpty(), _.isString(), _.isEqual(), _.isBoolean(), _.memoize(), _.invoke(), _.bind(), _.escape(), _.debounce(),
_.str.sprintf(), _.str.repeat(), _.str.startswith(), _.str.trim(),
_.str.escapeHTML(), _.str.escapeRegExp(), _.str.startsWith(), _.str.include()

closes odoo/odoo#118012

Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
This commit is contained in:
Pulinckx Pierre (PIPU)
2023-04-20 11:57:29 +02:00
parent 95c7031027
commit 608e90e998
103 changed files with 483 additions and 383 deletions
@@ -57,9 +57,7 @@ function fromField(f, record) {
clipboard.on('success', function () {
clipboard.destroy();
$(copyButton).tooltip('show');
_.delay(function () {
$(copyButton).tooltip("hide");
}, 800);
setTimeout(() => $(copyButton).tooltip("hide"), 800);
});
clipboard.on('error', function (e) {
clipboard.destroy();
+6 -3
View File
@@ -5,6 +5,7 @@ import { registry } from "@web/core/registry";
import { BoardController } from "./board_controller";
import { XMLParser } from "@web/core/utils/xml";
import { Domain } from "@web/core/domain";
import { escape } from "@web/core/utils/strings";
export class BoardArchParser extends XMLParser {
parse(arch, customViewId) {
@@ -36,7 +37,7 @@ export class BoardArchParser extends XMLParser {
const isFolded = Boolean(
node.hasAttribute("fold") ? parseInt(node.getAttribute("fold"), 10) : 0
);
let action = {
const action = {
id: nextId++,
title: node.getAttribute("string"),
actionId: parseInt(node.getAttribute("name"), 10),
@@ -45,10 +46,12 @@ export class BoardArchParser extends XMLParser {
isFolded,
};
if (node.hasAttribute("domain")) {
action.domain = new Domain(_.unescape(node.getAttribute("domain"))).toList();
action.domain = new Domain(
_.unescape(node.getAttribute("domain"))
).toList();
// so it can be serialized when reexporting board xml
// we unescape before re-escaping, to avoid double escaping due to subsequent layout change
action.domain.toString = () => _.escape(_.unescape(node.getAttribute("domain")));
action.domain.toString = () => escape(node.getAttribute("domain"));
}
archInfo.columns[currentIndex].actions.push(action);
break;
@@ -4,13 +4,14 @@ import { attr, Model } from "@im_livechat/legacy/model";
import { browser } from "@web/core/browser/browser";
import { isMobileOS } from "@web/core/browser/feature_detection";
import { SIZES } from "@web/core/ui/ui_service";
import { debounce } from "@web/core/utils/timing";
Model({
name: "Device",
lifecycleHooks: {
_created() {
this._refresh();
this._onResize = _.debounce(() => this._refresh(), 100);
this._onResize = debounce(() => this._refresh(), 100);
},
_willDelete() {
browser.removeEventListener("resize", this._onResize);
@@ -102,7 +102,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
});
}
if (_.isBoolean(params.data.folded)) {
if (typeof params.data.folded === "boolean") {
this.messaging.publicLivechatGlobal.publicLivechat.update({
isFolded: params.data.folded,
});
@@ -207,7 +207,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
* @returns {boolean}
*/
hasMessages() {
return !_.isEmpty(this.getMessages());
return Object.keys(this.getMessages() || {}).length > 0;
},
/**
* Tells if someone other than current user is typing something on this
@@ -216,7 +216,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
* @returns {boolean}
*/
isSomeoneTyping() {
return !_.isEmpty(this._typingPartnerIDs);
return Object.keys(this._typingPartnerIDs || {}).length > 0;
},
/**
* Mark the thread as read, which resets the unread counter to 0. This is
@@ -269,7 +269,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
timeoutCallbackArguments: [partnerID],
timerID: partnerID,
});
if (_.contains(this._typingPartnerIDs, partnerID)) {
if (this._typingPartnerIDs.includes(partnerID)) {
return;
}
this._typingPartnerIDs.push(partnerID);
@@ -327,7 +327,7 @@ const PublicLivechat = Class.extend(Mixins.EventDispatcherMixin, {
unregisterTyping(params) {
const partnerID = params.partnerID;
this._othersTypingTimers.unregisterTimer({ timerID: partnerID });
if (!_.contains(this._typingPartnerIDs, partnerID)) {
if (!this._typingPartnerIDs.includes(partnerID)) {
return;
}
this._typingPartnerIDs = this._typingPartnerIDs.filter((id) => id !== partnerID);
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { attr, one, clear, increment, Model } from "@im_livechat/legacy/model";
import { debounce } from "@web/core/utils/timing";
import { qweb } from "web.core";
import { Markup } from "web.utils";
@@ -410,7 +411,7 @@ Model({
debouncedAwaitUserInput: attr({
compute() {
// debounced to let the user type several sentences, see 'Chatbot/awaitUserInput' for details
return _.debounce(this.awaitUserInput, this.awaitUserInputDebounceTime);
return debounce(this.awaitUserInput, this.awaitUserInputDebounceTime);
},
}),
hasRestartButton: attr({
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { attr, clear, one, Model } from "@im_livechat/legacy/model";
import { debounce } from "@web/core/utils/timing";
import { getCookie, deleteCookie } from "web.utils.cookies";
@@ -480,7 +481,7 @@ Model({
}),
openChatDebounced: attr({
compute() {
return _.debounce(this._openChat, 200, true);
return debounce(this._openChat, 200, true);
},
}),
publicLivechatGlobalOwner: one("PublicLivechatGlobal", {
@@ -17,7 +17,7 @@ Model({
if (pageHistory) {
urlHistory = JSON.parse(pageHistory) || [];
}
if (!_.contains(urlHistory, page)) {
if (!urlHistory.includes(page)) {
urlHistory.push(page);
while (urlHistory.length > this.HISTORY_LIMIT) {
urlHistory.shift();
@@ -1,6 +1,7 @@
/** @odoo-module **/
import * as mailUtils from "@mail/js/utils";
import { debounce } from "@web/core/utils/timing";
import core from "web.core";
import time from "web.time";
@@ -324,7 +325,7 @@ const PublicLivechatView = Widget.extend({
* @param {string} options.model
* @param {integer} options.id
*/
_redirect: _.debounce(
_redirect: debounce(
function (options) {
if ("channelID" in options) {
this.trigger("redirect_to_channel", options.channelID);
@@ -6,6 +6,7 @@ import Widget from "web.Widget";
import { unaccent } from "web.utils";
import { setCookie } from "web.utils.cookies";
import { debounce } from "@web/core/utils/timing";
/**
* This is the widget that represent windows of livechat in the frontend.
@@ -35,7 +36,7 @@ const PublicLivechatWindow = Widget.extend({
this._super(parent);
this.messaging = messaging;
this._debouncedOnScroll = _.debounce(this._onScroll.bind(this), 100);
this._debouncedOnScroll = debounce(this._onScroll.bind(this), 100);
},
/**
* @override
@@ -175,7 +176,7 @@ const PublicLivechatWindow = Widget.extend({
* Otherwise, fold/unfold the window if set/unset.
*/
toggleFold(folded) {
if (!_.isBoolean(folded)) {
if (typeof folded !== "boolean") {
folded = !this.messaging.publicLivechatGlobal.publicLivechat.isFolded;
}
this.messaging.publicLivechatGlobal.publicLivechat.update({ isFolded: folded });
@@ -327,7 +328,7 @@ const PublicLivechatWindow = Widget.extend({
ev.stopPropagation(); // to prevent jquery's blockUI to cancel event
// ENTER key (avoid requiring jquery ui for external livechat)
if (ev.which === 13) {
const content = _.str.trim(this.$input.val());
const content = String(this.$input.val() || "").trim();
const messageData = {
content,
attachment_ids: [],
+1 -1
View File
@@ -17,7 +17,7 @@ patch(PosGlobalState.prototype, "l10n_fr_pos_cert.PosGlobalState", {
});
return false;
}
return _.contains(french_countries, this.company.country.code);
return french_countries.includes(this.company.country.code);
},
disallowLineQuantityChange() {
const result = this._super(...arguments);
+16 -11
View File
@@ -40,11 +40,13 @@ function parseAndTransform(htmlString, transformFunction) {
* @return {string}
*/
function _parseAndTransform(nodes, transformFunction) {
return _.map(nodes, function (node) {
return transformFunction(node, function () {
return _parseAndTransform(node.childNodes, transformFunction);
});
}).join("");
return Array.from($(nodes))
.map((node) => {
return transformFunction(node, function () {
return _parseAndTransform(node.childNodes, transformFunction);
});
})
.join("");
}
/**
@@ -85,16 +87,19 @@ function linkify(text, attrs) {
if (attrs.target === "_blank") {
attrs.rel = "noreferrer noopener";
}
attrs = _.map(attrs, function (value, key) {
return key + '="' + _.escape(value) + '"';
}).join(" ");
attrs = Object.keys(attrs || {})
.map((key) => {
const value = attrs[key];
return `${key}="${escape(value)}"`;
})
.join(" ");
let curIndex = 0;
let result = "";
let match;
while ((match = urlRegexp.exec(text)) !== null) {
result += _escapeEntities(text.slice(curIndex, match.index));
const url = match[0];
const href = !/^https?:\/\//i.test(url) ? "http://" + _.escape(url) : _.escape(url);
const href = !/^https?:\/\//i.test(url) ? "http://" + encodeURI(url) : encodeURI(url);
result += "<a " + attrs + ' href="' + href + '">' + _escapeEntities(url) + "</a>";
curIndex = match.index + match[0].length;
}
@@ -175,11 +180,11 @@ function parseEmail(text) {
if (text) {
var result = text.match(/"?(.*?)"? <(.*@.*)>/);
if (result) {
return [_.str.trim(result[1]), _.str.trim(result[2])];
return [(result[1] || "").trim(), (result[2] || "").trim()];
}
result = text.match(/(.*@.*)/);
if (result) {
return [_.str.trim(result[1]), _.str.trim(result[1])];
return [String(result[1] || "").trim(), String(result[1] || "").trim()];
}
return [text, false];
}
@@ -115,7 +115,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", {
const records = this.getRecords(res_model, domain);
const activityTypes = this.getRecords("mail.activity.type", []);
const activityIds = _.pluck(records, "activity_ids").flat();
const activityIds = records.map((x) => x.activity_ids).flat();
const groupedActivities = {};
const resIdToDeadline = {};
@@ -157,7 +157,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", {
count: group.__count,
state: state,
o_closest_deadline: group.date_deadline,
ids: _.pluck(activites, "id"),
ids: activites.map((x) => x.id),
};
});
@@ -177,9 +177,12 @@ patch(MockServer.prototype, "mail/models/mail_activity", {
}
return [type.id, type.display_name, mailTemplates];
}),
activity_res_ids: _.sortBy(_.pluck(records, "id"), function (id) {
return moment(resIdToDeadline[id]);
}),
activity_res_ids: _.sortBy(
records.map((x) => x.id),
function (id) {
return moment(resIdToDeadline[id]);
}
),
grouped_activities: groupedActivities,
};
},
@@ -18,6 +18,7 @@ QUnit.test("add_link utility function", function (assert) {
"https://tenor.com/view/chỗgiặt-dog-smile-gif-13860250": true,
"http://www.boîtenoire.be": true,
};
for (const [content, willLinkify] of Object.entries(testInputs)) {
const output = utils.parseAndTransform(content, utils.addLink);
if (willLinkify) {
@@ -4,7 +4,7 @@
import ajax from "web.ajax";
import core from "web.core";
import "web.dom_ready";
import { sprintf } from "@web/core/utils/strings";
import { sprintf, escape } from "@web/core/utils/strings";
var _t = core._t;
@@ -48,7 +48,7 @@ if ($('.o_unsubscribe_form').length) {
if (unsubscribed_list){
$('#subscription_info').html(sprintf(
_t("You have been <strong>successfully unsubscribed from %s</strong>."),
_.escape(unsubscribed_list)
escape(unsubscribed_list)
));
}
else{
@@ -262,7 +262,7 @@ export class MassMailingHtmlField extends HtmlField {
device.isMobile ?
$themes.filter(theme => !$(theme).data("hideFromMobile")) :
$themes;
this._themeParams = _.map(displayableThemes, (theme) => {
this._themeParams = Array.from(displayableThemes).map((theme) => {
const $theme = $(theme);
const name = $theme.data("name");
const classname = "o_" + name + "_theme";
@@ -3,6 +3,7 @@
import publicWidget from "web.public.widget";
import paymentFormMixin from "payment.payment_form_mixin";
import { debounce } from "@web/core/utils/timing";
publicWidget.registry.PaymentCheckoutForm = publicWidget.Widget.extend(paymentFormMixin, {
selector: 'form[name="o_payment_checkout"]',
@@ -19,7 +20,7 @@
*/
init: function () {
const preventDoubleClick = handlerMethod => {
return _.debounce(handlerMethod, 500, true);
return debounce(handlerMethod, 500, true);
};
this._super(...arguments);
// Prevent double-clicks and browser glitches on all inputs
+2 -1
View File
@@ -5,6 +5,7 @@
import Dialog from "web.Dialog";
import paymentFormMixin from "payment.payment_form_mixin";
import { debounce } from "@web/core/utils/timing";
const _t = core._t;
@@ -24,7 +25,7 @@
*/
init: function () {
const preventDoubleClick = handlerMethod => {
return _.debounce(handlerMethod, 500, true);
return debounce(handlerMethod, 500, true);
};
this._super(...arguments);
// Prevent double-clicks and browser glitches on all inputs
@@ -5,6 +5,26 @@
const _t = core._t;
/**
* Replace HTML character with ASCII character
*
* @param {string} str string
* @returns {string} ASCII string
*/
function escapeHTML(str) {
const htmlCaracters = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"'": "&#39;",
'"': "&quot;",
};
for (const [key, value] of Object.entries(htmlCaracters)) {
str = String(str).replace(new RegExp(key, "g"), value);
}
return str;
}
export default {
/**
@@ -76,7 +96,7 @@
return new Dialog(null, {
title: _.str.sprintf(_t("Error: %s"), title),
size: 'medium',
$content: `<p>${_.str.escapeHTML(description) || ''}</p>`,
$content: `<p>${escapeHTML(description) || ''}</p>`,
buttons: [{text: _t("Ok"), close: true}]
}).open();
} else { // Show error in inline form
@@ -84,12 +104,12 @@
// Build the html for the error
let errorHtml = `<div class="alert alert-danger mb4" name="o_payment_error">
<b>${_.str.escapeHTML(title)}</b>`;
<b>${escapeHTML(title)}</b>`;
if (description !== '') {
errorHtml += `</br>${_.str.escapeHTML(description)}`;
errorHtml += `</br>${escapeHTML(description)}`;
}
if (error !== '') {
errorHtml += `</br>${_.str.escapeHTML(error)}`;
errorHtml += `</br>${escapeHTML(error)}`;
}
errorHtml += '</div>';
+1 -1
View File
@@ -230,7 +230,7 @@ export const PosDB = core.Class.extend({
},
add_packagings: function (product_packagings) {
var self = this;
_.map(product_packagings, function (product_packaging) {
Object.values(product_packagings || {}).map((product_packaging) => {
const products = Object.values(self.product_by_id);
if (products.find(({ id }) => id === product_packaging.product_id[0])) {
self.product_packaging_by_barcode[product_packaging.barcode] = product_packaging;
+10 -21
View File
@@ -1011,7 +1011,7 @@ export class PosGlobalState extends PosModel {
// Keep the order ids that are about to be sent to the
// backend. In between create_from_ui and the success callback
// new orders may have been added to it.
var order_ids_to_sync = _.pluck(orders, "id");
var order_ids_to_sync = orders.map((o) => o.id);
for (const order of orders) {
order.to_invoice = options.to_invoice || false;
@@ -1590,10 +1590,9 @@ export class Product extends PosModel {
let draftPackLotLines, weight, description, packLotLinesToEdit;
if (this.attribute_line_ids.some((id) => id in this.pos.attributes_by_ptal_id)) {
const attributes = _.map(
this.attribute_line_ids,
(id) => this.pos.attributes_by_ptal_id[id]
).filter((attr) => attr !== undefined);
const attributes = this.attribute_line_ids
.map((id) => this.pos.attributes_by_ptal_id[id])
.filter((attr) => attr !== undefined);
const { confirmed, payload } = await this.pos.env.services.popup.add(
ProductConfiguratorPopup,
{
@@ -2172,15 +2171,7 @@ export class Orderline extends PosModel {
price_subtotal_incl: this.get_price_with_tax(),
discount: this.get_discount(),
product_id: this.get_product().id,
tax_ids: [
[
6,
false,
_.map(this.get_applicable_taxes(), function (tax) {
return tax.id;
}),
],
],
tax_ids: [[6, false, this.get_applicable_taxes().map((tax) => tax.id)]],
id: this.id,
pack_lot_ids: pack_lot_ids,
description: this.description,
@@ -2441,7 +2432,7 @@ export class Orderline extends PosModel {
if (line_taxes.length && line_taxes[0].price_include) {
new_included_taxes = new_included_taxes.concat(line_taxes);
}
if (tax.price_include && !_.contains(line_taxes, tax)) {
if (tax.price_include && !line_taxes.includes(tax)) {
mapped_included_taxes.push(tax);
}
});
@@ -2521,7 +2512,7 @@ export class Packlotline extends PosModel {
}
set_lot_name(name) {
this.lot_name = _.str.trim(name) || null;
this.lot_name = String(name || "").trim() || null;
}
get_lot_name() {
@@ -2836,11 +2827,9 @@ export class Order extends PosModel {
return orderLines.push([0, 0, item.export_as_JSON()]);
});
paymentLines = [];
this.paymentlines.forEach(
_.bind(function (item) {
return paymentLines.push([0, 0, item.export_as_JSON()]);
}, this)
);
this.paymentlines.forEach((item) => {
return paymentLines.push([0, 0, item.export_as_JSON()]);
});
var json = {
name: this.get_name(),
amount_paid: this.get_total_paid() - this.get_change(),
@@ -147,8 +147,8 @@ var PortalComposer = publicWidget.Widget.extend({
_prepareMessageData: function () {
return Object.assign(this.options || {}, {
'message': this.$('textarea[name="message"]').val(),
'attachment_ids': _.pluck(this.attachments, 'id'),
'attachment_tokens': _.pluck(this.attachments, 'access_token'),
attachment_ids: this.attachments.map((a) => a.id),
attachment_tokens: this.attachments.map((a) => a.access_token),
});
},
/**
@@ -32,7 +32,7 @@ PortalChatter.include({
init: function (parent, options) {
this._super.apply(this, arguments);
// options
if (!_.contains(this.options, 'display_rating')) {
if (!Object.keys(this.options).includes("display_rating")) {
this.options = _.defaults(this.options, {
'display_rating': false,
'rating_default_value': 0.0,
+3 -4
View File
@@ -377,7 +377,7 @@ patch(Order.prototype, "pos_loyalty.Order", {
},
wait_for_push_order() {
return (
!_.isEmpty(this.couponPointChanges) ||
Object.keys(this.couponPointChanges || {}).length > 0 ||
this._has_gift_card_product() ||
this._get_reward_lines().length ||
this._super(...arguments)
@@ -931,12 +931,11 @@ patch(Order.prototype, "pos_loyalty.Order", {
continue;
}
let price_to_use = line.get_price_with_tax();
if (program.program_type === 'gift_card') {
if (program.program_type === "gift_card") {
price_to_use = line.price;
}
const pointsPerUnit = round_precision(
(rule.reward_point_amount * price_to_use) /
line.get_quantity(),
(rule.reward_point_amount * price_to_use) / line.get_quantity(),
0.01
);
if (pointsPerUnit > 0) {
@@ -33,7 +33,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
return;
}
// No need to do an rpc if no existing coupon is being used.
if (!_.isEmpty(pointChanges) || newCodes.length) {
if (Object.keys(pointChanges || {}).length > 0 || newCodes.length) {
try {
const { successful, payload } = await this.orm.call(
"pos.order",
@@ -121,7 +121,7 @@ patch(PaymentScreen.prototype, "pos_loyalty.PaymentScreen", {
return true;
})
);
if (!_.isEmpty(couponData)) {
if (Object.keys(couponData || []).length > 0) {
const payload = await this.orm.call("pos.order", "confirm_coupon_programs", [
server_ids,
couponData,
@@ -15,6 +15,7 @@ import { Table } from "./table";
import { usePos } from "@point_of_sale/app/pos_hook";
import { useService } from "@web/core/utils/hooks";
import { Component, onPatched, onMounted, onWillUnmount, useRef, useState } from "@odoo/owl";
import { sprintf } from "@web/core/utils/strings";
export class FloorScreen extends Component {
static components = { EditableTable, EditBar, Table };
@@ -98,7 +99,7 @@ export class FloorScreen extends Component {
_onDeselectTable() {
this.state.selectedTableIds = [];
}
async _createTableHelper(copyTable, duplicateFloor=false) {
async _createTableHelper(copyTable, duplicateFloor = false) {
const existingTable = this.activeFloor.tables;
let newTable;
if (copyTable) {
@@ -226,9 +227,13 @@ export class FloorScreen extends Component {
this.activeFloor.table_ids.push(newTable.id);
return newTable;
}
_getNewTableName() {
_getNewTableName() {
let firstNum = 1;
const tablesNameNumber = this.activeTables.map(table => +table.name).sort(function(a, b){return a-b});
const tablesNameNumber = this.activeTables
.map((table) => +table.name)
.sort(function (a, b) {
return a - b;
});
for (let i = 0; i < tablesNameNumber.length; i++) {
if (tablesNameNumber[i] == firstNum) {
@@ -262,7 +267,9 @@ export class FloorScreen extends Component {
//This will lead in receiving information on orders from a table that we do not have.
//If we do not have a table in the PoS but still receive information about it, we reload the tables
//from the server.
const result = await this.orm.call("pos.session", "get_pos_ui_restaurant_floor", [[odoo.pos_session_id]]);
const result = await this.orm.call("pos.session", "get_pos_ui_restaurant_floor", [
[odoo.pos_session_id],
]);
if (this.env.pos.config.is_table_management) {
this.env.pos.floors = result;
this.env.pos.loadRestaurantFloor();
@@ -279,8 +286,10 @@ export class FloorScreen extends Component {
table_obj.order_count = table.orders + unsynced_orders;
}
}
get activeFloor() {
return this.state.selectedFloorId ? this.env.pos.floors_by_id[this.state.selectedFloorId] : null;
get activeFloor() {
return this.state.selectedFloorId
? this.env.pos.floors_by_id[this.state.selectedFloorId]
: null;
}
get activeTables() {
return this.activeFloor ? this.activeFloor.tables : null;
@@ -368,7 +377,11 @@ export class FloorScreen extends Component {
if (!confirmed) {
return;
}
const floor = await this.orm.call("restaurant.floor", "create_from_ui", [newName, "#ACADAD",this.env.pos.config.id]);
const floor = await this.orm.call("restaurant.floor", "create_from_ui", [
newName,
"#ACADAD",
this.env.pos.config.id,
]);
this.env.pos.floors_by_id[floor.id] = floor;
this.env.pos.floors.push(floor);
this.selectFloor(floor);
@@ -386,10 +399,14 @@ export class FloorScreen extends Component {
const floor = this.activeFloor;
const tables = this.activeFloor.tables;
const newFloorName = floor.name + " (copy)";
const newFloor = await this.orm.call("restaurant.floor", "create_from_ui", [newFloorName, floor.background_color, this.env.pos.config.id]);
const newFloor = await this.orm.call("restaurant.floor", "create_from_ui", [
newFloorName,
floor.background_color,
this.env.pos.config.id,
]);
this.env.pos.floors_by_id[newFloor.id] = newFloor;
this.env.pos.floors.push(newFloor);
this.selectFloor(newFloor)
this.selectFloor(newFloor);
for (const table of tables) {
await this._createTableHelper(table, true);
}
@@ -458,8 +475,7 @@ export class FloorScreen extends Component {
selectedTable.seats = newSeatsNum;
await this._save(selectedTable);
}
})
});
}
async changeToCircle() {
await this.changeShape("round");
@@ -474,7 +490,7 @@ export class FloorScreen extends Component {
this.selectedTables.forEach(async (selectedTable) => {
selectedTable.shape = form;
await this._save(selectedTable);
})
});
this.render();
}
async setTableColor(color) {
@@ -482,7 +498,7 @@ export class FloorScreen extends Component {
selectedTables.forEach(async (selectedTable) => {
selectedTable.color = color;
await this._save(selectedTable);
})
});
this.render();
this.state.isColorPicker = false;
}
@@ -500,8 +516,8 @@ export class FloorScreen extends Component {
async deleteFloorOrTable() {
if (this.selectedTables.length == 0) {
const { confirmed } = await this.popup.add(ConfirmPopup, {
title: _.str.sprintf("Removing floor %s", this.activeFloor.name),
body: _.str.sprintf(
title: `Removing floor ${this.activeFloor.name}`,
body: sprintf(
this.env._t("Removing a floor cannot be undone. Do you still wanna remove %s?"),
this.activeFloor.name
),
@@ -511,7 +527,8 @@ export class FloorScreen extends Component {
}
const originalSelectedFloorId = this.activeFloor.id;
await this.orm.call("restaurant.floor", "deactivate_floor", [
originalSelectedFloorId, this.env.pos.pos_session.id
originalSelectedFloorId,
this.env.pos.pos_session.id,
]);
const floor = this.env.pos.floors_by_id[originalSelectedFloorId];
const orderList = [...this.env.pos.get_order_list()];
@@ -524,7 +541,9 @@ export class FloorScreen extends Component {
delete this.env.pos.tables_by_id[tableId];
});
delete this.env.pos.floors_by_id[originalSelectedFloorId];
this.env.pos.floors = this.env.pos.floors.filter((floor) => floor.id != originalSelectedFloorId);
this.env.pos.floors = this.env.pos.floors.filter(
(floor) => floor.id != originalSelectedFloorId
);
this.env.pos.TICKET_SCREEN_STATE.syncedOrders.cache = {};
if (this.env.pos.floors.length > 0) {
this.selectFloor(this.env.pos.floors[0]);
@@ -558,17 +577,17 @@ export class FloorScreen extends Component {
await this.orm.call("restaurant.table", "create_from_ui", [
{ active: false, id: id },
]);
this.activeFloor.tables = this.activeTables.filter(
(table) => table.id !== id
);
this.activeFloor.tables = this.activeTables.filter((table) => table.id !== id);
delete this.env.pos.tables_by_id[id];
} else {
await this.popup.add(ErrorPopup, {
title: this.env._t("Delete Error"),
body: this.env._t("You cannot delete a table with orders still in draft for this table."),
body: this.env._t(
"You cannot delete a table with orders still in draft for this table."
),
});
}
})
});
// Value of an object can change inside async function call.
// Which means that in this code block, the value of `state.selectedTableId`
// before the await call can be different after the finishing the await call.
@@ -577,7 +596,7 @@ export class FloorScreen extends Component {
// else is selected during the rpc call.
const equalsCheck = (a, b) => {
return JSON.stringify(a) === JSON.stringify(b);
}
};
if (equalsCheck(this.state.selectedTableIds, originalSelectedTableIds)) {
this.state.selectedTableIds = [];
}
@@ -5,7 +5,7 @@ import { Order } from "@point_of_sale/js/models";
import { SplitOrderline } from "./SplitOrderline";
import { registry } from "@web/core/registry";
import { usePos } from "@point_of_sale/app/pos_hook";
import { Component, useState, onMounted } from "@odoo/owl";
import { Component, useState, onMounted } from "@odoo/owl";
export class SplitBillScreen extends Component {
static template = "SplitBillScreen";
@@ -46,7 +46,7 @@ export class SplitBillScreen extends Component {
this.pos.showScreen("ProductScreen");
}
proceed() {
if (_.isEmpty(this.splitlines)) {
if (Object.keys(this.splitlines || {})?.length === 0) {
// Splitlines is empty
return;
}
@@ -118,22 +118,19 @@ publicWidget.registry.PurchasePortalSidebar = PortalSidebar.extend({
_extractText: function ($node) {
var self = this;
var rawText = [];
$node
.contents()
.toArray()
.forEach((el) => {
var current = $(el);
if ($.trim(current.text())) {
var tagName = current.prop("tagName");
if (
_.isUndefined(tagName) ||
(!_.isUndefined(tagName) &&
_.contains(self.authorizedTextTag, tagName.toLowerCase()))
) {
rawText.push($.trim(current.text()));
}
Array.from($node.contents()).forEach((el) => {
var current = $(el);
if ($.trim(current.text())) {
var tagName = current.prop("tagName");
if (
typeof tagName === "undefined" ||
(typeof tagName !== "undefined" &&
self.authorizedTextTag.includes(tagName.toLowerCase()))
) {
rawText.push($.trim(current.text()));
}
});
}
});
return rawText.join(" ");
},
});
@@ -105,7 +105,11 @@ publicWidget.registry.SalePortalSidebar = PortalSidebar.extend({
var current = $(el);
if ($.trim(current.text())) {
var tagName = current.prop("tagName");
if (_.isUndefined(tagName) || (!_.isUndefined(tagName) && _.contains(self.authorizedTextTag, tagName.toLowerCase()))) {
if (
typeof tagName === "undefined" ||
(typeof tagName !== "undefined" &&
self.authorizedTextTag.includes(tagName.toLowerCase()))
) {
rawText.push($.trim(current.text()));
}
}
+7 -7
View File
@@ -5,6 +5,8 @@ import core from "web.core";
import utils from "web.utils";
import ajax from "web.ajax";
import { sprintf } from "@web/core/utils/strings";
import { memoize } from "@web/core/utils/functions";
var _t = core._t;
var VariantMixin = {
@@ -39,7 +41,7 @@ var VariantMixin = {
if (!$parent.data('uniqueId')) {
$parent.data('uniqueId', _.uniqueId());
}
this._throttledGetCombinationInfo($parent.data('uniqueId'))(ev);
this._throttledGetCombinationInfo(this, $parent.data('uniqueId'))(ev);
},
/**
* @see onChangeVariant
@@ -523,7 +525,7 @@ var VariantMixin = {
$default_price.text(self._priceToStr(combination.list_price));
var isCombinationPossible = true;
if (!_.isUndefined(combination.is_combination_possible)) {
if (typeof combination.is_combination_possible !== "undefined") {
isCombinationPossible = combination.is_combination_possible;
}
this._toggleDisable($parent, isCombinationPossible);
@@ -628,12 +630,10 @@ var VariantMixin = {
* @param {string} uniqueId
* @returns {function}
*/
_throttledGetCombinationInfo: _.memoize(function (uniqueId) {
_throttledGetCombinationInfo: memoize(function (self, uniqueId) {
var dropMisordered = new concurrency.DropMisordered();
var _getCombinationInfo = _.throttle(this._getCombinationInfo.bind(this), 500);
return function (ev, params) {
return dropMisordered.add(_getCombinationInfo(ev, params));
};
var _getCombinationInfo = _.throttle(self._getCombinationInfo.bind(self), 500);
return (ev, params) => dropMisordered.add(_getCombinationInfo(ev, params));
}),
/**
* Toggles the disabled class depending on the $parent element
@@ -210,7 +210,7 @@ patch(SaleOrderLineProductField.prototype, 'sale_product_configurator', {
var noVariantAttributeValues = mainProduct.no_variant_attribute_values;
var noVariantCommands = [{ operation: "DELETE_ALL" }];
if (noVariantAttributeValues && noVariantAttributeValues.length !== 0) {
var resIds = _.map(noVariantAttributeValues, function (noVariantValue) {
var resIds = noVariantAttributeValues.map((noVariantValue) => {
return { id: parseInt(noVariantValue.value) };
});
@@ -116,9 +116,7 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre
clipboard.on('success', function () {
clipboard.destroy();
$clipboardBtn.popover('show');
_.delay(function () {
$clipboardBtn.popover('hide');
}, 800);
setTimeout(() => $clipboardBtn.popover('hide'), 800);
});
clipboard.on('error', function (e) {
+5 -4
View File
@@ -16,15 +16,16 @@ export function clamp(num, min, max) {
* 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} start default 0
* @param {number} stop
* @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);
const nsteps = Math.floor((stop - start) / step);
for (let i = 0; i < nsteps; i++) {
array.push(start + step * i);
}
return array;
}
@@ -9,7 +9,7 @@ var Context = Class.extend({
this.__contexts = [];
this.__eval_context = null;
var self = this;
Object.values(arguments).forEach((x) => self.add(x));
Object.values(arguments || {}).forEach((x) => self.add(x));
},
//--------------------------------------------------------------------------
@@ -137,7 +137,7 @@ var Dialog = Widget.extend({
self.$footer = self.$modal.find(".modal-footer");
self.set_buttons(self.buttons);
}
self.$modal.on('hidden.bs.modal', _.bind(self.destroy, self));
self.$modal.on('hidden.bs.modal', self.destroy.bind(self));
});
},
/**
@@ -312,7 +312,7 @@ var Dialog = Widget.extend({
_setButtonsTo($target, buttons) {
var self = this;
$target.empty();
buttons.forEach(buttonData => {
buttons.forEach((buttonData) => {
var $button = dom.renderButton({
attrs: {
class: buttonData.classes || (buttons.length > 1 ? 'btn-secondary' : 'btn-primary'),
@@ -35,7 +35,7 @@ var Domain = collections.Tree.extend({
*/
init: function (domain, evalContext) {
this._super.apply(this, arguments);
if (Array.isArray(domain) || _.isString(domain)) {
if (Array.isArray(domain) || typeof domain === "string") {
this._parse(this.normalizeArray(_.clone(this.stringToArray(domain, evalContext))));
} else {
this._data = !!domain;
@@ -87,10 +87,10 @@ var Domain = collections.Tree.extend({
switch (this._data[1]) {
case "=":
case "==":
return _.isEqual(fieldValue, this._data[2]);
return JSON.stringify(fieldValue) === JSON.stringify(this._data[2]);
case "!=":
case "<>":
return !_.isEqual(fieldValue, this._data[2]);
return JSON.stringify(fieldValue) !== JSON.stringify(this._data[2]);
case "<":
return (fieldValue < this._data[2]);
case ">":
@@ -156,9 +156,10 @@ var Domain = collections.Tree.extend({
return [];
} else {
var arr = [this._data];
return arr.concat.apply(arr, _.map(this._children, function (child) {
return child.toArray();
}));
return arr.concat.apply(
arr,
this._children.map((child) => child.toArray())
);
}
},
/**
@@ -239,7 +240,7 @@ var Domain = collections.Tree.extend({
* @returns {string}
*/
arrayToString: function (domain) {
if (_.isString(domain)) return domain;
if (typeof domain === "string") return domain;
function jsToPy(p) {
switch (p) {
@@ -267,7 +268,7 @@ var Domain = collections.Tree.extend({
* @returns {Array}
*/
stringToArray: function (domain, evalContext) {
if (!_.isString(domain)) return _.clone(domain);
if (typeof domain !== "string") return _.clone(domain);
return pyUtils.eval("domain", domain ? domain.replace(/%%/g, '%') : "[]", evalContext);
},
/**
+3 -1
View File
@@ -86,7 +86,9 @@ function blockUI() {
}
function unblockUI() {
_.invoke(throbbers, 'destroy');
throbbers.forEach((throbber) => {
throbber.destroy();
});
throbbers = [];
$(document.body).removeClass('o_ui_blocked');
unblockAccessKeys();
@@ -277,7 +277,7 @@ var EventDispatcherMixin = Object.assign({}, ParentedMixin, {
};
},
_delegateCustomEvents: function () {
if (_.isEmpty(this.custom_events)) { return; }
if (Object.keys(this.custom_events || {}).length === 0) { return; }
for (var key in this.custom_events) {
if (!this.custom_events.hasOwnProperty(key)) { continue; }
@@ -1,5 +1,6 @@
/** @odoo-module alias=web.Popover **/
import { debounce } from "@web/core/utils/timing";
import { LegacyComponent } from "@web/legacy/legacy_component";
const { status, onWillUnmount, useEffect, useRef, useState } = owl;
@@ -29,7 +30,7 @@
this._onResizeWindow = this._onResizeWindow.bind(this);
this._onScrollDocument = _.throttle(this._onScrollDocument, 50);
this._onResizeWindow = _.debounce(this._onResizeWindow, 250);
this._onResizeWindow = debounce(this._onResizeWindow, 250);
/**
* Those events are only necessary if the popover is currently open,
@@ -109,8 +109,8 @@ function eval_contexts(contexts, evaluation_context) {
return _(contexts).reduce(function (result_context, ctx) {
// __eval_context evaluations can lead to some of `contexts`'s
// values being null, skip them as well as empty contexts
if (_.isEmpty(ctx)) { return result_context; }
if (_.isString(ctx)) {
if (Object.keys(ctx || {}).length === 0) { return result_context; }
if (typeof ctx === "string") {
// wrap raw strings in context
ctx = { __ref: 'context', __debug: ctx };
}
@@ -144,7 +144,7 @@ function eval_domains(domains, evaluation_context) {
(domains[0][0] === "|" || domains[0][0] === "!")
);
_(domains).each(function (domain) {
if (_.isString(domain)) {
if (typeof domain === "string") {
// wrap raw strings in domain
domain = { __ref: 'domain', __debug: domain };
}
@@ -190,7 +190,7 @@ function eval_groupbys(contexts, evaluation_context) {
evaluation_context = Object.assign(pycontext(), evaluation_context || {});
var result_group = [];
_(contexts).each(function (ctx) {
if (_.isString(ctx)) {
if (typeof ctx === "string") {
// wrap raw strings in context
ctx = { __ref: 'context', __debug: ctx };
}
+3 -3
View File
@@ -116,9 +116,9 @@ const rpc = {
* @returns {string}
*/
_serializeSort: function (orderBy) {
return _.map(orderBy, function (order) {
return order.name + (order.asc !== false ? ' ASC' : ' DESC');
}).join(', ');
return orderBy
.map((order) => order.name + (order.asc !== false ? " ASC" : " DESC"))
.join(", ");
},
};
@@ -47,7 +47,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
// must be able to customize server
var window_origin = location.protocol + "//" + location.host;
origin = origin ? origin.replace( /\/+$/, '') : window_origin;
if (!_.isUndefined(this.origin) && this.origin !== origin)
if (typeof this.origin !== "undefined" && this.origin !== origin)
throw new Error('Session already bound to ' + this.origin);
else
this.origin = origin;
@@ -256,7 +256,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
}
// TODO: remove
if (! _.isString(url)) {
if (typeof url !== "string") {
Object.assign(options, url);
url = url.url;
}
@@ -949,7 +949,7 @@ const utils = {
*/
toBoolElse: function (str, elseValues, trueValues, falseValues) {
var ret = _.str.toBool(str, trueValues, falseValues);
if (_.isUndefined(ret)) {
if (typeof ret === "undefined") {
return elseValues;
}
return ret;
@@ -218,7 +218,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
* @param {boolean} [display] use true to show the widget or false to hide it
*/
do_toggle: function (display) {
if (_.isBoolean(display)) {
if (typeof display === "boolean") {
display ? this.do_show() : this.do_hide();
} else if (this.$el) {
this.$el.hasClass('o_hidden') ? this.do_show() : this.do_hide();
@@ -283,9 +283,9 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
* @returns {Promise}
*/
replace: function (target) {
return this._widgetRenderAndInsert(_.bind(function (t) {
return this._widgetRenderAndInsert((t) => {
this.$el.replaceAll(t);
}, this), target);
}, target);
},
/**
* Re-sets the widget's root element (el/$el/$el).
@@ -337,7 +337,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
*/
_delegateEvents: function () {
var events = this.events;
if (_.isEmpty(events)) { return; }
if (Object.keys(events || {}).length === 0) { return; }
for(var key in events) {
if (!events.hasOwnProperty(key)) { continue; }
@@ -372,7 +372,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
attrs['class'] = this.className;
}
var $el = $(document.createElement(this.tagName));
if (!_.isEmpty(attrs)) {
if (Object.keys(attrs || {}).length > 0) {
$el.attr(attrs);
}
return $el;
@@ -16,6 +16,7 @@ import session from "web.session";
import field_utils from "web.field_utils";
import utils from "web.utils";
import { sprintf } from "@web/core/utils/strings";
import { debounce } from "@web/core/utils/timing";
var _t = core._t;
var _lt = core._lt;
@@ -238,7 +239,7 @@ var DebouncedField = AbstractField.extend({
this._isDirty = false;
if (this.mode === 'edit') {
if (this.DEBOUNCE) {
this._doDebouncedAction = _.debounce(this._doAction, this.DEBOUNCE);
this._doDebouncedAction = debounce(this._doAction, this.DEBOUNCE);
} else {
this._doDebouncedAction = this._doAction;
}
@@ -14,7 +14,7 @@
* conversions.
*/
import { sprintf } from "@web/core/utils/strings";
import { escape, escapeRegExp, sprintf } from "@web/core/utils/strings";
import core from "web.core";
import dom from "web.dom";
import session from "web.session";
@@ -87,10 +87,10 @@ function formatBoolean(value, field, options) {
function formatChar(value, field, options) {
value = typeof value === 'string' ? value : '';
if (options && options.isPassword) {
return _.str.repeat('*', value ? value.length : 0);
return "*".repeat(value ? value.length : 0);
}
if (options && options.escape) {
value = _.escape(value);
value = escape(value);
}
return value;
}
@@ -241,7 +241,7 @@ function formatFloatTime(value, field, options) {
function formatInteger(value, field, options) {
options = options || {};
if (options.isPassword) {
return _.str.repeat('*', String(value).length);
return "*".repeat(String(value).length);
}
if (!value && value !== 0) {
// previously, it returned 'false'. I don't know why. But for the Pivot
@@ -282,8 +282,8 @@ function formatMany2one(value, field, options) {
value = value.data.display_name || '';
}
}
if (options && options.escape) {
value = _.escape(value);
if (options?.escape) {
value = escape(value);
}
return value;
}
@@ -373,7 +373,7 @@ function formatMonetary(value, field, options) {
if (options.forceString) {
return val.join(' ');
}
return utils.Markup(val.map((v) => _.escape(v)).join(NBSP));
return utils.Markup(val.map((v) => escape(v)).join(NBSP));
}
/**
* Returns a string representing the given value (multiplied by 100)
@@ -408,8 +408,8 @@ function formatSelection(value, field, options) {
return '';
}
value = val[1];
if (options && options.escape) {
value = _.escape(value);
if (options?.escape) {
value = escape(value);
}
return value;
}
@@ -575,8 +575,9 @@ function parseDateTime(value, field, options) {
* @returns {float|NaN} the number value contained in the string representation
*/
function parseNumber(value) {
if (core._t.database.parameters.thousands_sep) {
var escapedSep = _.str.escapeRegExp(core._t.database.parameters.thousands_sep);
var escapedSep = escapeRegExp(core._t.database.parameters.thousands_sep);
value = value.replace(new RegExp(escapedSep, 'g'), '');
}
if (core._t.database.parameters.decimal_point) {
@@ -735,7 +736,7 @@ function parseMany2one(value) {
display_name: value[1],
};
}
if (Number.isFinite(value) || _.isString(value)) {
if (Number.isFinite(value) || typeof value === "string") {
return {
id: parseInt(value, 10),
};
@@ -753,7 +754,7 @@ export default {
float: formatFloat,
float_factor: formatFloatFactor,
float_time: formatFloatTime,
html: _.identity, // todo
html: (value) => value, // todo
integer: formatInteger,
many2many: formatX2Many,
many2one: formatMany2one,
@@ -767,25 +768,25 @@ export default {
json: formatJson,
},
parse: {
binary: _.identity,
boolean: _.identity, // todo
char: _.identity, // todo
binary: (value) => value,
boolean: (value) => value, // todo
char: (value) => value, // todo
date: parseDate, // todo
datetime: parseDateTime, // todo
float: parseFloat,
float_factor: parseFloatFactor,
float_time: parseFloatTime,
html: _.identity, // todo
html: (value) => value, // todo
integer: parseInteger,
many2many: _.identity, // todo
many2many: (value) => value, // todo
many2one: parseMany2one,
many2one_reference: parseInteger,
monetary: parseMonetary,
one2many: _.identity,
one2many: (value) => value,
percentage: parsePercentage,
reference: parseMany2one,
selection: _.identity, // todo
text: _.identity, // todo
json: _.identity, // todo
selection: (value) => value, // todo
text: (value) => value, // todo
json: (value) => value, // todo
},
};
@@ -582,7 +582,7 @@ var FieldMany2One = AbstractField.extend({
* @returns {string} escaped html of value lines
*/
_renderValueLines: function (needFirstLine) {
const escapedValue = _.escape((this.m2o_value || "").trim());
const escapedValue = escape((this.m2o_value || "").trim());
const lines = escapedValue.split('\n');
if (!needFirstLine) {
lines.shift();
@@ -1050,7 +1050,7 @@ var FieldMany2ManyTags = AbstractField.extend({
* @returns {Promise}
*/
_addTag: function (data) {
if (!_.contains(this.value.res_ids, data.id)) {
if (!this.value.res_ids.includes(data.id)) {
return this._setValue({
operation: 'ADD_M2M',
ids: data
@@ -1076,7 +1076,7 @@ var FieldMany2ManyTags = AbstractField.extend({
* @returns {Object}
*/
_getRenderTagsContext: function () {
var elements = this.value ? _.pluck(this.value.data, 'data') : [];
var elements = this.value ? this.value.data.map((d) => d.data) : [];
return {
colorField: this.colorField,
elements: elements,
@@ -1249,7 +1249,7 @@ const M2MAvatarMixin = {
html: true,
placement: 'auto',
content: () => {
const elements = this.value ? _.pluck(this.value.data, 'data') : [];
const elements = this.value ? this.value.data.map((d) => d.data) : [];
return qweb.render('Many2ManyTagAvatarPopover', {
elements: elements.slice(this.visibleAvatarCount - 1),
});
@@ -251,7 +251,7 @@ export const PublicRoot = publicWidget.RootWidget.extend({
args = args.concat(ev.target);
var route = args[0];
if (_.str.startsWith(route, '/web/dataset/call_kw/')) {
if (String(route).startsWith("/web/dataset/call_kw/")) {
var params = args[1];
var options = args[2];
var noContextKeys;
@@ -212,14 +212,14 @@ var PublicWidget = Widget.extend({
}
// If the method has no meaningful options, use the default Widget
// system
var isAsync = _.contains(methodOptions, 'async');
var isAsync = methodOptions.includes('async');
if (!isAsync) {
events[event] = method;
return;
}
method = self.proxy(methodOptions[methodOptions.length - 1]);
if (_.str.startsWith(event, 'click')) {
if (String(event).startsWith("click")) {
// Protect click handler to be called multiple times by
// mistake by the user and add a visual disabling effect
// for buttons.
@@ -114,7 +114,10 @@ function _updateSizeProps() {
}
}
_.invoke(medias, 'addListener', _updateSizeProps);
medias.forEach((media) => {
media.addListener(_updateSizeProps);
});
_updateSizeProps();
export default config;
@@ -366,7 +366,7 @@ var BasicModel = AbstractModel.extend({
return this._rpc({
model: modelName,
method: 'unlink',
args: [_.pluck(records, 'res_id')],
args: [records.map(r => r.res_id)],
context: context,
})
.then(function () {
@@ -908,7 +908,7 @@ var BasicModel = AbstractModel.extend({
// We only need to load the datapoint in the first case.
if (field.value && field.value.length) {
if (_.isObject(field.value[0])) {
dpParams.res_ids = _.pluck(field.value, 'id');
dpParams.res_ids = field.value.map((v) => v.id);
dataPoint = self._makeDataPoint(dpParams);
field.value.forEach((data) => {
var recordDP = self._makeDataPoint({
@@ -1298,7 +1298,7 @@ var BasicModel = AbstractModel.extend({
args: [resIDs],
})
.then(function (action) {
if (!_.isEmpty(action)) {
if (Object.keys(action || {}).length > 0) {
return new Promise(function (resolve, reject) {
self.do_action(action, {
on_close: function (result) {
@@ -1339,7 +1339,7 @@ var BasicModel = AbstractModel.extend({
})
.then(function (action) {
// optionally clear the DataManager's cache
if (!_.isEmpty(action)) {
if (Object.keys(action || {}).length > 0) {
return new Promise(function (resolve, reject) {
self.do_action(action, {
on_close: function () {
@@ -2013,7 +2013,7 @@ var BasicModel = AbstractModel.extend({
def = this._rpc({
model: list.model,
method: 'read',
args: [_.pluck(data, 'id'), fieldNames],
args: [data.map((d) => d.id), fieldNames],
context: Object.assign({}, record.context, field.context, list.getContext()),
}).then(function (records) {
records.forEach((record) => {
@@ -2895,7 +2895,7 @@ var BasicModel = AbstractModel.extend({
*/
_fetchSpecialRelation: function (record, fieldName) {
var field = record.fields[fieldName];
if (!_.contains(["many2one", "many2many", "one2many"], field.type)) {
if (!["many2one", "many2many", "one2many"].includes(field.type)) {
return Promise.resolve();
}
@@ -3318,7 +3318,7 @@ var BasicModel = AbstractModel.extend({
this._sortList(list);
if (type === 'many2many' || list._forceM2MLink) {
var relRecordCreated = relRecordAdded.filter(rec => typeof rec.res_id === 'string');
var realIDs = _.difference(list.res_ids, _.pluck(relRecordCreated, 'res_id'));
var realIDs = _.difference(list.res_ids, relRecordCreated.map(r => r.res_id));
// deliberately generate a single 'replace' command instead
// of a 'delete' and a 'link' commands with the exact diff
// because 1) performance-wise it doesn't change anything
@@ -3333,7 +3333,7 @@ var BasicModel = AbstractModel.extend({
// updated (it may happen with editable lists)
relRecordUpdated.forEach((relRecord) => {
var changes = self._generateChanges(relRecord, options);
if (!_.isEmpty(changes)) {
if (Object.keys(changes || {}).length > 0) {
var command = x2ManyCommands.update(relRecord.res_id, changes);
commands[fieldName].push(command);
}
@@ -3348,18 +3348,18 @@ var BasicModel = AbstractModel.extend({
var didChange = false;
var changes, command, relRecord;
for (var i = 0; i < list.res_ids.length; i++) {
if (_.contains(keptIds, list.res_ids[i])) {
if (keptIds.includes(list.res_ids[i])) {
// this is an id that already existed
relRecord = _.findWhere(relRecordUpdated, {res_id: list.res_ids[i]});
changes = relRecord ? this._generateChanges(relRecord, options) : {};
if (!_.isEmpty(changes)) {
if (Object.keys(changes || {}).length > 0) {
command = x2ManyCommands.update(relRecord.res_id, changes);
didChange = true;
} else {
command = x2ManyCommands.link_to(list.res_ids[i]);
}
commands[fieldName].push(command);
} else if (_.contains(addedIds, list.res_ids[i])) {
} else if (addedIds.includes(list.res_ids[i])) {
// this is a new id (maybe existing in DB, but new in JS)
relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]});
if (!relRecord) {
@@ -3443,7 +3443,7 @@ var BasicModel = AbstractModel.extend({
if (options.full || !(options.fieldName || options.additionalContext)) {
var context_to_add = options.sanitize_default_values ?
_.omit(element.context, function (val, key) {
return _.str.startsWith(key, 'default_');
return String(key).startsWith('default_');
})
: element.context;
context.add(context_to_add);
@@ -4643,7 +4643,7 @@ var BasicModel = AbstractModel.extend({
});
r._changes[fieldName] = rec.id;
many2ones[fieldName] = true;
} else if (_.contains(['one2many', 'many2many'], fieldType)) {
} else if (['one2many', 'many2many'].includes(fieldType)) {
var x2mCommands = value[2][fieldName];
defs.push(self._processX2ManyCommands(r, fieldName, x2mCommands));
} else {
@@ -4799,7 +4799,7 @@ var BasicModel = AbstractModel.extend({
groups.forEach((group) => {
var aggregateValues = {};
for (const [key, value] of Object.entries(group)) {
if (_.contains(fields, key) && key !== groupByField &&
if (fields.includes(key) && key !== groupByField &&
AGGREGATABLE_TYPES.includes(list.fields[key].type)) {
aggregateValues[key] = value;
}
@@ -4870,7 +4870,7 @@ var BasicModel = AbstractModel.extend({
// readGroup but are not there anymore.
// Note that these groups are put after existing groups so
// the order is not conserved. A sort *might* be useful.
var emptyGroupsIDs = _.difference(_.pluck(previousGroups, 'id'), list.data);
var emptyGroupsIDs = _.difference(previousGroups.map((group) => group.id), list.data);
emptyGroupsIDs.forEach((groupID) => {
list.data.push(groupID);
var emptyGroup = self.localData[groupID];
@@ -4884,9 +4884,9 @@ var BasicModel = AbstractModel.extend({
if (!options.onlyGroups) {
// generate the res_ids of the main list, being the concatenation
// of the fetched res_ids in each group
list.res_ids = _.flatten(_.map(groups, function (group) {
list.res_ids = _.map(groups, function (group) {
return group ? group.res_ids : [];
}));
}).flat();
}
return list;
}).then(function () {
@@ -4918,7 +4918,7 @@ var BasicModel = AbstractModel.extend({
// order field first to apply the order on all pages
if (list.res_ids.length > list.limit && list.orderedBy.length) {
if (!list.orderedResIDs) {
var fieldNames = _.pluck(list.orderedBy, 'name');
var fieldNames = list.orderedBy.map((list) => list.name);
def = this._readMissingFields(list, list.res_ids.filter(id => Number.isFinite(id)), fieldNames, options.withoutRecordData);
}
def.then(function () {
@@ -5077,7 +5077,7 @@ var BasicModel = AbstractModel.extend({
* @returns {boolean} false if the value was already the given one
*/
_saveSpecialDataCache: function (record, fieldName, value) {
if (_.isEqual(record._specialDataCache[fieldName], value)) {
if (JSON.stringify(record._specialDataCache[fieldName]) === JSON.stringify(value)) {
return false;
}
record._specialDataCache[fieldName] = value;
@@ -5121,7 +5121,7 @@ var BasicModel = AbstractModel.extend({
return prom.then(function (result) {
delete list.__data;
list.count = result.length;
var ids = _.pluck(result.records, 'id');
var ids = result.records.map((r) => r.id);
var data = _.map(result.records, function (record) {
var dataPoint = self._makeDataPoint({
context: list.context,
@@ -5291,9 +5291,9 @@ var BasicModel = AbstractModel.extend({
var self = this;
if (element.parentID) {
var parent = this.localData[element.parentID];
parent.res_ids = _.flatten(_.map(parent.data, function (dataPointID) {
parent.res_ids = _.map(parent.data, function (dataPointID) {
return self.localData[dataPointID].res_ids;
}));
}).flat();
this._updateParentResIDs(parent);
}
},
@@ -5395,7 +5395,7 @@ var BasicModel = AbstractModel.extend({
if (!field) {
continue;
}
if (_.contains(['one2many', 'many2one', 'many2many'], field.type)) {
if (['one2many', 'many2one', 'many2many'].includes(field.type)) {
var hasChange = element._changes && fieldName in element._changes;
var value = hasChange ? element._changes[fieldName] : element.data[fieldName];
var relationalElement = this.localData[value];
@@ -42,7 +42,7 @@ var StandaloneFieldManagerMixin = Object.assign({}, FieldManagerMixin, {
var result = FieldManagerMixin._confirmChange.apply(this, arguments);
var record = this.model.get(id);
for (const [fieldName, widget] of Object.entries(this.registeredWidgets[id])) {
if (_.contains(fields, fieldName)) {
if (fields.includes(fieldName)) {
widget.reset(record, event);
}
}
@@ -62,12 +62,12 @@ var viewUtils = {
}
var dateTypes = ['date', 'datetime'];
if (!list.fields[groupByField].readonly &&
_.contains(dateTypes, list.fields[groupByField].type)) {
dateTypes.includes(list.fields[groupByField].type)) {
return list.fieldsInfo && list.fieldsInfo[list.viewType][groupByField] &&
list.fieldsInfo[list.viewType][groupByField].allowGroupRangeValue;
}
var availableTypes = ['char', 'boolean', 'many2one', 'selection'];
return _.contains(availableTypes, list.fields[groupByField].type);
return availableTypes.includes(list.fields[groupByField].type);
},
/**
* @param {string} arch view arch
@@ -694,7 +694,7 @@ var DomainLeaf = DomainNode.extend({
// Set list of operators according to field type
var selectedField = this.fieldSelector.getSelectedField() || {};
this.operators = this._getOperatorsFromType(selectedField.type);
if (_.contains(["child_of", "parent_of", "like", "not like", "=like", "=ilike"], this.operator)) {
if (["child_of", "parent_of", "like", "not like", "=like", "=ilike"].includes(this.operator)) {
// In case user entered manually or from demo data
this.operators[this.operator] = operator_mapping[this.operator];
} else if (!this.operators[this.operator]) {
@@ -726,7 +726,7 @@ var DomainLeaf = DomainNode.extend({
}
// TODO the value could be a m2o input, etc...
if (_.contains(["date", "datetime"], selectedField.type)) {
if (["date", "datetime"].includes(selectedField.type)) {
this.valueWidget = new (selectedField.type === "datetime" ? datepicker.DateTimeWidget : datepicker.DateWidget)(this);
wDefs.push(this.valueWidget.appendTo("<div/>").then((function () {
this.valueWidget.$el.addClass("o_domain_leaf_value_input");
@@ -821,10 +821,10 @@ var DomainLeaf = DomainNode.extend({
_changeOperator: function (operator, silent) {
this.operator = operator;
if (_.contains(["set", "not set"], this.operator)) {
if (["set", "not set"].includes(this.operator)) {
this.operator = this.operator === "not set" ? "=" : "!=";
this.value = false;
} else if (_.contains(["in", "not in"], this.operator)) {
} else if (["in", "not in"].includes(this.operator)) {
this.value = Array.isArray(this.value) ? this.value : this.value ? ("" + this.value).split(",") : [];
} else {
if (Array.isArray(this.value)) {
@@ -857,22 +857,22 @@ var DomainLeaf = DomainNode.extend({
}
if (selectedField.type === "boolean") {
if (!_.isBoolean(this.value)) { // Convert boolean-like value to boolean
if (typeof this.value !== "boolean") { // Convert boolean-like value to boolean
this.value = !!parseFloat(this.value);
}
} else if (selectedField.type === "selection") {
if (!_.some(selectedField.selection, (function (option) { return option[0] === this.value; }).bind(this))) {
this.value = selectedField.selection[0][0];
}
} else if (_.contains(["date", "datetime"], selectedField.type)) {
if (couldNotParse || _.isBoolean(this.value)) {
} else if (["date", "datetime"].includes(selectedField.type)) {
if (couldNotParse || typeof this.value === "boolean") {
this.value = field_utils.parse[selectedField.type](field_utils.format[selectedField.type](moment())).toJSON(); // toJSON to get date with server format
} else {
this.value = this.value.toJSON(); // toJSON to get date with server format
}
} else {
// Never display "true" or "false" strings from boolean value
if (_.isBoolean(this.value)) {
if (typeof this.value === "boolean") {
this.value = "";
} else if (_.isObject(this.value) && !Array.isArray(this.value)) { // Can be object if parsed to x2x representation
this.value = this.value.id || value || "";
@@ -975,7 +975,7 @@ var DomainLeaf = DomainNode.extend({
// TODO The two following functions should be in an independant widget
on_add_tag: function (e) {
if (e.type === "keyup" && e.which !== $.ui.keyCode.ENTER) return;
if (!_.contains(["not in", "in"], this.operator)) return;
if (!["not in", "in"].includes(this.operator)) return;
var values = Array.isArray(this.value) ? this.value.slice() : [];
@@ -158,7 +158,7 @@ var ModelFieldSelectorPopover = Widget.extend({
* @returns {Promise} resolved once the re-rendering is finished
*/
setChain: function (chain) {
if (_.isEqual(chain, this.chain)) {
if (JSON.stringify(chain) === JSON.stringify(this.chain)) {
return Promise.resolve();
}
@@ -284,7 +284,7 @@ var ModelFieldSelectorPopover = Widget.extend({
* @param {Object} field - the field to add to the chain node
*/
_goToNextPage: function (field) {
if (!_.isEqual(this._getLastPageField(field.name), field)) return;
if (JSON.stringify(this._getLastPageField(field.name)) !== JSON.stringify(field)) return;
this._validate(true);
this._addChainNode(field.name);
@@ -489,7 +489,7 @@ var ModelFieldSelectorPopover = Widget.extend({
* @param {Object} field - the field to select
*/
_selectField: function (field) {
if (!_.isEqual(this._getLastPageField(field.name), field)) return;
if (JSON.stringify(this._getLastPageField(field.name)) !== JSON.stringify(field)) return;
this._validate(true);
this._addChainNode(field.name);
@@ -4,6 +4,7 @@ import core from "web.core";
import config from "web.config";
import utils from "web.utils";
import Widget from "web.Widget";
import { debounce } from "@web/core/utils/timing";
/**
@@ -147,7 +148,7 @@ var NameAndSignature = Widget.extend({
}
// Resize the signature area if it is resized
$(window).on('resize.o_web_sign_name_and_signature', _.debounce(function () {
$(window).on('resize.o_web_sign_name_and_signature', debounce(function () {
if (self.isDestroyed()) {
// May happen since this is debounced
return;
@@ -0,0 +1,15 @@
/** @odoo-module **/
import { range } from "@web/core/utils/numbers";
QUnit.module("utils", () => {
QUnit.module("Numbers", () => {
QUnit.test("test range function from core/utils/numbers.js", (assert) => {
assert.deepEqual(range(0, 10), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert.deepEqual(range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]);
assert.deepEqual(range(0, 35, 5), [0, 5, 10, 15, 20, 25, 30]);
assert.deepEqual(range(-10, 6, 2), [-10, -8, -6, -4, -2, 0, 2, 4]);
assert.deepEqual(range(4, -4, -1), [4, 3, 2, 1, 0, -1, -2, -3]);
});
});
});
@@ -544,7 +544,8 @@ export class MockServer {
item.includes("readonly")
))) &&
(!["1", "True"].includes(node.getAttribute("readonly")) ||
!_.isEmpty(evaluateExpr(node.getAttribute("attrs") || "{}")))
Object.keys(evaluateExpr(node.getAttribute("attrs") || "{}") || {}).length >
0)
);
}
default:
@@ -1275,7 +1276,7 @@ export class MockServer {
group.__domain = [[fieldName, "=", value]].concat(group.__domain);
}
}
if (_.isEmpty(group.__range)) {
if (Object.keys(group.__range || {}).length === 0) {
delete group.__range;
}
// compute count key to match dumb server logic...
@@ -5,6 +5,7 @@
import testUtils from "web.test_utils";
import * as cpHelpers from "@web/../tests/search/helpers";
const { createControlPanel, mock } = testUtils;
const { patchDate } = mock;
@@ -40,7 +40,7 @@ QUnit.module('core', {}, function () {
try {
window.Date = function (v) {
if (_.isUndefined(v)) {
if (typeof v === "undefined") {
v = '2013-10-31 12:34:56';
}
return new wDate(v);
@@ -73,7 +73,7 @@ QUnit.module('core', {}, function () {
try {
window.Date = function (v) {
if (_.isUndefined(v)) {
if (typeof v === "undefined") {
v = '2013-10-31 12:34:56';
}
return new wDate(v);
@@ -100,7 +100,7 @@ QUnit.module('core', {}, function () {
assert.strictEqual(widget.el.nodeName, 'DIV', "should have generated the default element");
assert.strictEqual(widget.el.attributes.length, 0, "should not have generated any attribute");
assert.ok(_.isEmpty(widget.$el.html(), "should not have generated any content"));
assert.ok(Object.keys(widget.$el.html() || {}).length === 0, "should not have generated any content");
widget.destroy();
});
@@ -216,7 +216,7 @@ var MockServer = Class.extend({
if ('default' in model.fields[fieldName]) {
const def = model.fields[fieldName].default;
record[fieldName] = typeof def === 'function' ? def.call(this) : def;
} else if (_.contains(['one2many', 'many2many'], model.fields[fieldName].type)) {
} else if (["one2many", "many2many"].includes(model.fields[fieldName].type)) {
record[fieldName] = [];
} else {
record[fieldName] = false;
@@ -1254,7 +1254,7 @@ var MockServer = Class.extend({
ids = [ids];
}
var records = this.data[model].records;
var names = _.map(ids, function (id) {
var names = ids.map(id => {
return id ? [id, _.findWhere(records, {id: id}).display_name] : [null, ""];
});
return names;
@@ -1299,7 +1299,7 @@ var MockServer = Class.extend({
if (str.length) {
records = records.filter(record => record.display_name.indexOf(str) !== -1);
}
var result = _.map(records, function (record) {
var result = records.map(record => {
return [record.id, record.display_name];
});
return result.slice(0, limit);
@@ -1471,8 +1471,8 @@ var MockServer = Class.extend({
});
// filter out non existing fields
aggregatedFields = aggregatedFields.filter(name => {
return name in self.data[model].fields && !(_.contains(groupByFieldNames,name));
aggregatedFields = aggregatedFields.filter((name) => {
return name in self.data[model].fields && !groupByFieldNames.includes(name);
});
function aggregateFields(group, records) {
@@ -1487,7 +1487,7 @@ var MockServer = Class.extend({
}
}
if (type === 'many2one') {
var ids = _.pluck(records, aggregatedFields[i]);
var ids = records.map((record) => aggregatedFields[i]);
group[aggregatedFields[i]] = _.uniq(ids).length || null;
}
}
@@ -1657,7 +1657,7 @@ var MockServer = Class.extend({
group.__domain = [[fieldName, "=", value]].concat(group.__domain);
}
}
if (_.isEmpty(group.__range)) {
if (Object.keys(group.__range || {}).length === 0) {
delete group.__range;
}
// compute count key to match dumb server logic...
@@ -1869,7 +1869,9 @@ var MockServer = Class.extend({
if (!Array.isArray(ids)) {
ids = [ids];
}
this.data[model].records = this.data[model].records.filter(record => !_.contains(ids, record.id));
this.data[model].records = this.data[model].records.filter(
(record) => !ids.includes(record.id)
);
// update value of relationnal fields pointing to the deleted records
Object.values(this.data).forEach((d) => {
@@ -1995,7 +1997,10 @@ var MockServer = Class.extend({
case '/web/dataset/resequence':
return this._mockResequence(args);
}
if (route.indexOf('/web/image') >= 0 || _.contains(['.png', '.jpg'], route.substr(route.length - 4))) {
if (
route.indexOf("/web/image") >= 0 ||
[".png", ".jpg"].includes(route.substr(route.length - 4))
) {
return;
}
switch (args.method) {
@@ -2204,7 +2209,7 @@ var MockServer = Class.extend({
if (!field) {
throw Error(`Mock: Can't write value "${JSON.stringify(value)}" on field "${field_changed}" on record "${model},${id}" (field is undefined)`);
}
if (_.contains(['one2many', 'many2many'], field.type)) {
if (["one2many", "many2many"].includes(field.type)) {
var ids = _.clone(record[field_changed]) || [];
if (
@@ -2234,7 +2239,7 @@ var MockServer = Class.extend({
} else if (command[0] === 3) { // FORGET
ids = _.without(ids, command[1]);
} else if (command[0] === 4) { // LINK_TO
if (!_.contains(ids, command[1])) {
if (!ids.includes(command[1])) {
ids.push(command[1]);
}
} else if (command[0] === 5) { // DELETE ALL
+5 -4
View File
@@ -4,6 +4,7 @@ import {Markup} from "web.utils";
var qwebPath = '/web/static/lib/qweb/';
import {hushConsole} from "@web/../tests/helpers/utils";
import { escape } from "@web/core/utils/strings";
function trim(s) {
return s.replace(/(^\s+|\s+$)/g, '');
@@ -114,13 +115,13 @@ QUnit.module('QWeb', {
QUnit.test('escape', assert => {
// not strictly about qweb...
assert.expect(8);
assert.equal(_.escape('a'), 'a');
assert.equal(_.escape('<a>'), '&lt;a&gt;');
assert.equal(escape('a'), 'a');
assert.equal(escape('<a>'), '&lt;a&gt;');
assert.equal(_.escape({[_.escapeMethod]() { return 'a'; }}), 'a');
assert.equal(_.escape({[_.escapeMethod]() { return '<a>'; }}), '<a>');
assert.equal(_.escape(Markup('a')), 'a');
assert.equal(escape(Markup('a')), 'a');
assert.equal(_.escape(Markup('<a>')), '<a>');
assert.equal(_.escape(Markup`a`), 'a');
assert.equal(escape(Markup`a`), 'a');
assert.equal(_.escape(Markup`<a>`), '<a>');
});
QUnit.module('t-out', {}, () => {
@@ -1374,7 +1374,7 @@
await model.load(params).then(function (resultID) {
var record = model.get(resultID);
assert.ok(_.isEqual(record.data.category.res_ids, [12, 14]),
assert.ok(JSON.stringify(record.data.category.res_ids) === JSON.stringify([12, 14]),
"category field should have correct default value");
});
+4 -3
View File
@@ -8,6 +8,7 @@ import {
fullAnnotatedTraceback,
} from "@web/core/errors/error_utils";
import { registry } from "@web/core/registry";
import { escape } from "@web/core/utils/strings";
function setQUnitDebugMode() {
owl.whenReady(() => document.body.classList.add("debug")); // make the test visible to the naked eye
@@ -263,15 +264,15 @@ export function setupQUnit() {
modulesAlert.classList.toggle("alert-info");
let error = "Some modules couldn't be started:<ul>";
if (info.failed.length) {
const failedList = info.failed.map((mod) => "<li>" + _.escape(mod) + "</li>");
const failedList = info.failed.map((mod) => "<li>" + escape(mod) + "</li>");
error += `<li> Failed modules: <ul>${failedList.join("")}</ul> </li>`;
}
if (info.missing.length) {
const missingList = info.missing.map((mod) => "<li>" + _.escape(mod) + "</li>");
const missingList = info.missing.map((mod) => "<li>" + escape(mod) + "</li>");
error += `<li> Missing dependencies: <ul>${missingList.join("")}</ul> </li>`;
}
if (info.unloaded.length) {
const unloadedList = info.unloaded.map((mod) => "<li>" + _.escape(mod) + "</li>");
const unloadedList = info.unloaded.map((mod) => "<li>" + escape(mod) + "</li>");
error += `
<li> Non loaded modules due to missing dependencies:
<ul>${unloadedList.join("")}</ul>
@@ -286,7 +286,8 @@ QUnit.module("Fields", (hooks) => {
});
var ids = _.sortBy([12, 15, 18].concat(createdType.id), _.identity.bind(_));
assert.ok(
_.isEqual(_.sortBy(commands[0][2], _.identity.bind(_)), ids),
JSON.stringify(_.sortBy(commands[0][2], _.identity.bind(_))) ===
JSON.stringify(ids),
"new value should be " + ids
);
}
@@ -481,7 +481,10 @@ QUnit.module("Fields", (hooks) => {
6,
"generated command should be REPLACE WITH"
);
assert.ok(_.isEqual(commands[0][2], [12]), "new value should be [12]");
assert.ok(
JSON.stringify(commands[0][2]) === JSON.stringify([12]),
"new value should be [12]"
);
}
},
});
@@ -93,19 +93,17 @@ QUnit.module("Fields", (hooks) => {
"should have 10% as pie value since int_field=10"
);
assert.ok(
_.str.include(
String(
target.querySelector(".o_field_percent_pie.o_field_widget .o_pie .o_mask").style
.transform,
"rotate(180deg)"
),
.transform
).includes("rotate(180deg)"),
"left mask should be covering the whole left side of the pie"
);
assert.ok(
_.str.include(
String(
target.querySelectorAll(".o_field_percent_pie.o_field_widget .o_pie .o_mask")[1]
.style.transform,
"rotate(36deg)"
),
.style.transform
).includes("rotate(36deg)"),
"right mask should be rotated from 360*(10/100) = 36 degrees"
);
@@ -162,11 +160,10 @@ QUnit.module("Fields", (hooks) => {
"should have 80% as pie value since int_field=80"
);
assert.ok(
_.str.include(
String(
target.querySelector(".o_field_percent_pie.o_field_widget .o_pie .o_mask").style
.transform,
"rotate(288deg)"
),
.transform
).includes("rotate(288deg)"),
"left mask should be rotated from 360*(80/100) = 288 degrees"
);
assert.hasClass(
@@ -391,7 +391,7 @@ function classToStyle($editable, cssRules) {
style = `${key}:${value};${style}`;
}
};
if (_.isEmpty(style)) {
if (Object.keys(style || {}).length === 0) {
writes.push(() => { node.removeAttribute('style'); });
} else {
writes.push(() => {
@@ -1371,7 +1371,12 @@ function _normalizeStyle(style) {
const normalizedStyle = {};
for (const styleName of style) {
const value = style[styleName];
if (value && !styleName.includes('animation') && !styleName.includes('-webkit') && _.isString(value)) {
if (
value &&
!styleName.includes("animation") &&
!styleName.includes("-webkit") &&
typeof value === "string"
) {
const normalizedStyleName = styleName.replace(/-(.)/g, (a, b) => b.toUpperCase());
normalizedStyle[styleName] = style[normalizedStyleName];
if (style.getPropertyPriority(styleName) === 'important') {
@@ -62,8 +62,9 @@ export class HtmlFieldWysiwygAdapterComponent extends ComponentAdapter {
stripHistoryIds(newValue) !== stripHistoryIds(newProps.editingValue) &&
stripHistoryIds(lastValue) !== stripHistoryIds(newValue)
) ||
!_.isEqual(lastRecordInfo, newRecordInfo) ||
!_.isEqual(lastCollaborationChannel, newCollaborationChannel))
JSON.stringify(lastRecordInfo) !== JSON.stringify(newRecordInfo) ||
JSON.stringify(lastCollaborationChannel) !== JSON.stringify(newCollaborationChannel)
)
{
this.widget.resetEditor(newValue, newProps.widgetArgs[0]);
this.env.onWysiwygReset && this.env.onWysiwygReset();
@@ -132,7 +133,7 @@ export class HtmlField extends Component {
res_model: newProps.record.resModel,
res_id: newProps.record.resId,
};
if (!_.isEqual(this._lastRecordInfo, newRecordInfo)) {
if (JSON.stringify(this._lastRecordInfo) !== JSON.stringify(newRecordInfo)) {
this.currentEditingValue = undefined;
}
this._lastRecordInfo = newRecordInfo;
@@ -8,6 +8,7 @@ import Dialog from "web.Dialog";
import Widget from "web.Widget";
import localStorage from "web.local_storage";
import { sprintf } from "@web/core/utils/strings";
import { debounce } from "@web/core/utils/timing";
var _t = core._t;
@@ -110,10 +111,10 @@ var checkSCSS = (function () {
return function (scss) {
var stack = [];
var line = 1;
for (var i = 0 ; i < scss.length ; i++) {
if (_.contains(openings, scss[i])) {
for (var i = 0; i < scss.length; i++) {
if (openings.includes(scss[i])) {
stack.push(scss[i]);
} else if (_.contains(closings, scss[i])) {
} else if (closings.includes(scss[i])) {
if (stack.pop() !== mapping[scss[i]]) {
return _getCheckReturn(false, line, _t("Unexpected ") + scss[i]);
}
@@ -254,7 +255,7 @@ var ViewEditor = Widget.extend({
var refX = 0;
var resizing = false;
var minWidth = 400;
var debounceStoreEditorWidth = _.debounce(storeEditorWidth, 500);
var debounceStoreEditorWidth = debounce(storeEditorWidth, 500);
this._updateViewSelectDOM();
@@ -262,8 +263,8 @@ var ViewEditor = Widget.extend({
var initType;
if (this.options.initialResID) {
initResID = this.options.initialResID;
if (_.isString(initResID) && initResID[0] === '/') {
if (_.str.endsWith(initResID, '.scss')) {
if (typeof initResID === "string" && initResID[0] === '/') {
if (initResID.endsWith(".scss")) {
initType = 'scss';
} else {
initType = 'js';
@@ -739,7 +740,7 @@ var ViewEditor = Widget.extend({
} else {
var onChangeSession = (function () {
this.aceEditor.off('changeSession', onChangeSession);
_.delay(__showErrorLine.bind(this, line), 400);
setTimeout(__showErrorLine.bind(this, line), 400);
}).bind(this);
this.aceEditor.on('changeSession', onChangeSession);
this._displayResource(resID, this.currentType);
@@ -799,7 +800,7 @@ var ViewEditor = Widget.extend({
this.errorSession = this.aceEditor.getSession();
this.errorSessionChangeCallback = __restore.bind(this);
this.errorSession.on('change', this.errorSessionChangeCallback);
this.errorSessionScrollCallback = _.debounce(__updateErrorLineDisplay.bind(this, line), 10);
this.errorSessionScrollCallback = debounce(__updateErrorLineDisplay.bind(this, line), 10);
this.errorSession.on('changeScrollTop', this.errorSessionScrollCallback);
__updateErrorLineDisplay.call(this, line);
@@ -11,9 +11,10 @@ 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";
import { sprintf, escape } from "@web/core/utils/strings";
const QWeb = core.qweb;
import {closestElement} from "@web_editor/js/editor/odoo-editor/src/utils/utils";
import { debounce } from "@web/core/utils/timing";
var _t = core._t;
@@ -23,7 +24,7 @@ let cacheSnippetTemplate = {};
$.extend($.expr[':'], {
o_editable: function (node, i, m) {
while (node) {
if (node.className && _.isString(node.className)) {
if (node.className && typeof node.className === "string") {
if (node.className.indexOf('o_not_editable') !== -1) {
return false;
}
@@ -2011,7 +2012,7 @@ var SnippetsMenu = Widget.extend({
const range = selection.getRangeAt(0);
$(range.startContainer).closest('.o_default_snippet_text').removeClass('o_default_snippet_text');
});
const refreshSnippetEditors = _.debounce(() => {
const refreshSnippetEditors = debounce(() => {
for (const snippetEditor of this.snippetEditors) {
this._mutex.exec(() => snippetEditor.destroy());
}
@@ -2042,7 +2043,7 @@ var SnippetsMenu = Widget.extend({
this.options.wysiwyg.odooEditor.addEventListener('observerApply', () => {
$(this.options.wysiwyg.odooEditor.editable).trigger('content_changed');
});
this.options.wysiwyg.odooEditor.addEventListener('historyRevert', _.debounce(() => {
this.options.wysiwyg.odooEditor.addEventListener('historyRevert', debounce(() => {
this.trigger_up('widgets_start_request', {
$target: this.options.wysiwyg.$editable,
editableMode: true,
@@ -2878,8 +2879,8 @@ var SnippetsMenu = Widget.extend({
.addClass('oe_snippet')
.each((i, el) => {
const $snippet = $(el);
const name = _.escape(el.getAttribute('name'));
const thumbnailSrc = _.escape(el.dataset.oeThumbnail);
const name = escape(el.getAttribute('name'));
const thumbnailSrc = escape(el.dataset.oeThumbnail);
const $sbody = $snippet.children().addClass('oe_snippet_body');
const isCustomSnippet = !!el.closest('#snippet_custom');
@@ -34,6 +34,7 @@ import {
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";
import { debounce } from "@web/core/utils/timing";
var qweb = core.qweb;
var _t = core._t;
@@ -2370,7 +2371,7 @@ const RangeUserValueWidget = UnitUserValueWidget.extend({
this.containerEl.appendChild(this.outputEl);
}
this._onInputChange = _.debounce(this._onInputChange, 100);
this._onInputChange = debounce(this._onInputChange, 100);
},
//--------------------------------------------------------------------------
@@ -4767,8 +4768,8 @@ registry['sizing_x'] = registry.sizing.extend({
var gridE = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var gridW = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
this.grid = {
e: [_.map(gridE, v => ('col-lg-' + v)), _.map(gridE, v => width / 12 * v), 'width'],
w: [_.map(gridW, v => ('offset-lg-' + v)), _.map(gridW, v => width / 12 * v), 'margin-left'],
e: [gridE.map(v => ('col-lg-' + v)), gridE.map(v => width / 12 * v), 'width'],
w: [gridW.map(v => ('offset-lg-' + v)), gridW.map(v => width / 12 * v), 'margin-left'],
};
return this.grid;
},
@@ -4847,10 +4848,10 @@ registry['sizing_grid'] = registry.sizing.extend({
const gridE = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
this.grid = {
n: [_.map(gridN, v => ('g-height-' + (rowEnd - v))), _.map(gridN, v => ((gridProp.rowSize + gridProp.rowGap) * (v - 1))), 'grid-row-start'],
s: [_.map(gridS, v => ('g-height-' + (v - rowStart))), _.map(gridS, v => ((gridProp.rowSize + gridProp.rowGap) * (v - 1))), 'grid-row-end'],
w: [_.map(gridW, v => ('g-col-lg-' + (columnEnd - v))), _.map(gridW, v => ((gridProp.columnSize + gridProp.columnGap) * (v - 1))), 'grid-column-start'],
e: [_.map(gridE, v => ('g-col-lg-' + (v - columnStart))), _.map(gridE, v => ((gridProp.columnSize + gridProp.columnGap) * (v - 1))), 'grid-column-end'],
n: [gridN.map(v => ('g-height-' + (rowEnd - v))), gridN.map(v => ((gridProp.rowSize + gridProp.rowGap) * (v - 1))), 'grid-row-start'],
s: [gridS.map(v => ('g-height-' + (v - rowStart))), gridS.map(v => ((gridProp.rowSize + gridProp.rowGap) * (v - 1))), 'grid-row-end'],
w: [gridW.map(v => ('g-col-lg-' + (columnEnd - v))), gridW.map(v => ((gridProp.columnSize + gridProp.columnGap) * (v - 1))), 'grid-column-start'],
e: [gridE.map(v => ('g-col-lg-' + (v - columnStart))), gridE.map(v => ((gridProp.columnSize + gridProp.columnGap) * (v - 1))), 'grid-column-end'],
};
return this.grid;
@@ -90,7 +90,7 @@ export default {
var self = this;
this.fontIcons.forEach((data) => {
data.cssData = self.getCssSelectors(data.parser);
data.alias = _.flatten(_.map(data.cssData, _.property('names')));
data.alias = _.map(data.cssData, _.property('names')).flat();
});
}),
};
+4 -4
View File
@@ -386,10 +386,10 @@ range(40, 127).forEach((keyCode) => {
* @param {Number} addTests
*/
var testKeyboard = function ($editable, assert, keyboardTests, addTests) {
var tests = _.compact(_.pluck(keyboardTests, 'test'));
var testNumber = _.compact(_.pluck(tests, 'start')).length +
_.compact(_.pluck(tests, 'content')).length +
_.compact(_.pluck(tests, 'check')).length +
var tests = _.compact(keyboardTests.map((k) => k.test));
var testNumber = _.compact(tests.map((test) => test.start)).length +
_.compact(tests.map((test) => test.content)).length +
_.compact(tests.map((test) => test.check)).length +
(addTests | 0);
assert.expect(testNumber);
@@ -50,7 +50,7 @@ export function getFirstVisibleElement($elements) {
}
export function getJQueryElementFromSelector(selector) {
const iframeSplit = _.isString(selector) && selector.match(/(.*\biframe[^ ]*)(.*)/);
const iframeSplit = typeof selector === "string" && selector.match(/(.*\biframe[^ ]*)(.*)/);
if (iframeSplit && iframeSplit[2]) {
var $iframe = $(`${iframeSplit[1]}:not(.o_ignore_in_tour)`);
if ($iframe.is('[is-ready="false"]')) {
@@ -10,7 +10,7 @@ publicWidget.registry.UnsplashBeacon = publicWidget.Widget.extend({
* @override
*/
start: function () {
var unsplashImages = _.map(this.$('img[src*="/unsplash/"]'), function (img) {
var unsplashImages = this.$('img[src*="/unsplash/"]').map((img) => {
// get image id from URL (`http://www.domain.com:1234/unsplash/xYdf5feoI/lion.jpg` -> `xYdf5feoI`)
return img.src.split('/unsplash/')[1].split('/')[0];
});
@@ -5,6 +5,8 @@
*/
import { loadJS } from "@web/core/assets";
import { escape } from "@web/core/utils/strings";
import { debounce } from "@web/core/utils/timing";
import Class from "web.Class";
import config from "web.config";
import core from "web.core";
@@ -229,7 +231,7 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
this.play(e);
clearTimeout(pauseTimer);
pauseTimer = _.delay((function () {
pauseTimer = setTimeout((() => {
this.pause();
pauseTimer = null;
}).bind(this), 2000);
@@ -310,7 +312,7 @@ var AnimationEffect = Class.extend(mixins.ParentedMixin, {
var animationState = this._getStateCallback(elapsedTime, this._newEvent);
if (!this._newEvent
&& animationState !== undefined
&& _.isEqual(animationState, this._animationLastState)) {
&& JSON.stringify(animationState) === JSON.stringify(this._animationLastState)) {
return;
}
this._animationLastState = animationState;
@@ -451,7 +453,7 @@ registry.slider = publicWidget.Widget.extend({
this._computeHeights();
// Initialize carousel and pause if in edit mode.
this.$el.carousel(this.editableMode ? 'pause' : undefined);
$(window).on('resize.slider', _.debounce(() => this._computeHeights(), 250));
$(window).on('resize.slider', debounce(() => this._computeHeights(), 250));
return this._super.apply(this, arguments);
},
/**
@@ -524,7 +526,7 @@ registry.Parallax = Animation.extend({
*/
start: function () {
this._rebuild();
$(window).on('resize.animation_parallax', _.debounce(this._rebuild.bind(this), 500));
$(window).on('resize.animation_parallax', debounce(this._rebuild.bind(this), 500));
this.modalEl = this.$target[0].closest('.modal');
if (this.modalEl) {
$(this.modalEl).on('shown.bs.modal.animation_parallax', () => {
@@ -724,7 +726,7 @@ registry.mediaVideo = publicWidget.Widget.extend(MobileYoutubeAutoplayMixin, {
// the src is saved in the 'data-src' attribute or the
// 'data-oe-expression' one (the latter is used as a workaround in 10.0
// system but should obviously be reviewed in master).
var src = _.escape(this.$el.data('oe-expression') || this.$el.data('src'));
var src = escape(this.$el.data('oe-expression') || this.$el.data('src'));
// Validate the src to only accept supported domains we can trust
var m = src.match(/^(?:https?:)?\/\/([^/?#]+)/);
if (!m) {
@@ -733,7 +735,7 @@ registry.mediaVideo = publicWidget.Widget.extend(MobileYoutubeAutoplayMixin, {
}
var domain = m[1].replace(/^www\./, '');
var supportedDomains = ['youtu.be', 'youtube.com', 'youtube-nocookie.com', 'instagram.com', 'vine.co', 'player.vimeo.com', 'vimeo.com', 'dailymotion.com', 'player.youku.com', 'youku.com'];
if (!_.contains(supportedDomains, domain)) {
if (!supportedDomains.includes(domain)) {
// Unsupported domain, don't inject iframe
return;
}
@@ -923,7 +925,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(Object.keys(socialNetworks), social)) {
if (!Object.keys(socialNetworks).includes(social)) {
return;
}
var wHeight = 500;
@@ -1036,7 +1038,7 @@ registry.FullScreenHeight = publicWidget.Widget.extend({
// rules may alter the full-screen-height class behavior in some
// cases (blog...).
this._adaptSize();
$(window).on('resize.FullScreenHeight', _.debounce(() => this._adaptSize(), 250));
$(window).on('resize.FullScreenHeight', debounce(() => this._adaptSize(), 250));
}
return this._super(...arguments);
},
@@ -1162,7 +1164,7 @@ registry.BottomFixedElement = publicWidget.Widget.extend({
*/
async start() {
this.$scrollingElement = $().getScrollingElement();
this.__hideBottomFixedElements = _.debounce(() => this._hideBottomFixedElements(), 100);
this.__hideBottomFixedElements = debounce(() => this._hideBottomFixedElements(), 100);
this.$scrollingElement.on('scroll.bottom_fixed_element', this.__hideBottomFixedElements);
$(window).on('resize.bottom_fixed_element', this.__hideBottomFixedElements);
return this._super(...arguments);
@@ -72,11 +72,9 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
*/
_computeSnippetTemplates: function (html) {
const $html = $(html);
const fontVariables = _.map($html.find('we-fontfamilypicker[data-variable]'), el => {
return el.dataset.variable;
});
const toFind = $html.find("we-fontfamilypicker[data-variable]").toArray();
const fontVariables = toFind.map((el) => el.dataset.variable);
FontFamilyPickerUserValueWidget.prototype.fontVariables = fontVariables;
return this._super(...arguments);
},
/**
@@ -2,6 +2,7 @@
import weWidgets from "wysiwyg.widgets";
import wUtils from "website.utils";
import { debounce } from "@web/core/utils/timing";
weWidgets.LinkTools.include({
custom_events: Object.assign({}, weWidgets.LinkTools.prototype.custom_events || {}, {
@@ -14,7 +15,7 @@ weWidgets.LinkTools.include({
*/
init: function () {
this._super.apply(this, arguments);
this._adaptPageAnchor = _.debounce(this._adaptPageAnchor, this.LINK_DEBOUNCE);
this._adaptPageAnchor = debounce(this._adaptPageAnchor, this.LINK_DEBOUNCE);
},
/**
* Allows the URL input to propose existing website pages.
@@ -16,7 +16,7 @@ import socialMediaOptions from "@website/snippets/s_social_media/options";
* @returns {Promise<jQuery>}
*/
function toggleDropdown($toggles, show) {
return Promise.all(_.map($toggles, toggle => {
return Promise.all($($toggles).toArray().map(toggle => {
// We must select the element via the iframe so that the event handlers
// declared on the iframe are triggered.
const $toggle = toggle.ownerDocument.defaultView.$(toggle);
+3 -3
View File
@@ -22,7 +22,7 @@ function loadAnchors(url, body) {
resolve();
}
}).then(function (response) {
const anchors = _.map($(response).find('[id][data-anchor=true]'), function (el) {
const anchors = $(response).find('[id][data-anchor=true]').toArray().map((el) => {
return '#' + el.id;
});
// Always suggest the top and the bottom of the page as internal link
@@ -123,7 +123,7 @@ function autocompleteWithPages(self, $input, options) {
* @param {jQuery} [$excluded]
*/
function onceAllImagesLoaded($element, $excluded) {
var defs = _.map($element.find('img').addBack('img'), function (img) {
var defs = $element.find('img').addBack('img').map(img => {
if (img.complete || $excluded && ($excluded.is(img) || $excluded.has(img).length)) {
return; // Already loaded
}
@@ -175,7 +175,7 @@ function prompt(options, _qweb) {
text: options
};
}
if (_.isUndefined(_qweb)) {
if (typeof _qweb === "undefined") {
_qweb = 'website.prompt';
}
options = Object.assign({
@@ -170,9 +170,13 @@ options.registry.InnerChart = options.Class.extend({
} else {
// Find max value from each row/column data
const datasets = JSON.parse(dataset.data).datasets || [];
const dataValue = _.flatten(datasets.map(el => el.data.map(data => {
return !isNaN(parseInt(data)) ? parseInt(data) : 0;
})));
const dataValue = datasets
.map((el) => {
return el.data.map((data) => {
return !isNaN(parseInt(data)) ? parseInt(data) : 0;
});
})
.flat();
// When max value is not given and min value is greater than chart
// data values
if (minValue >= Math.max(...dataValue)) {
@@ -278,7 +278,7 @@ options.registry.gallery = options.Class.extend({
*/
slideshow: function () {
const imageEls = this._getImages();
const images = _.map(imageEls, img => ({
const images = imageEls.map((img) => ({
// Use getAttribute to get the attribute value otherwise .src
// returns the absolute url.
src: img.getAttribute('src'),
@@ -5,6 +5,7 @@ import publicWidget from 'web.public.widget';
import {qweb} from 'web.core';
import {Markup} from 'web.utils';
import { debounce } from '@web/core/utils/timing';
publicWidget.registry.searchBar = publicWidget.Widget.extend({
selector: '.o_searchbar_form',
@@ -24,8 +25,8 @@ publicWidget.registry.searchBar = publicWidget.Widget.extend({
this._dp = new concurrency.DropPrevious();
this._onInput = _.debounce(this._onInput, 400);
this._onFocusOut = _.debounce(this._onFocusOut, 100);
this._onInput = debounce(this._onInput, 400);
this._onFocusOut = debounce(this._onFocusOut, 100);
},
/**
* @override
@@ -8,7 +8,8 @@
import publicWidget from "web.public.widget";
import dom from "web.dom";
import concurrency from "web.concurrency";
import { sprintf } from "@web/core/utils/strings";
import { sprintf } from "@web/core/utils/strings";
import { debounce } from "@web/core/utils/timing";
var _t = core._t;
var qweb = core.qweb;
@@ -91,7 +92,7 @@ import { sprintf } from "@web/core/utils/strings";
}
this._updateFieldsVisibility();
this._onFieldInputDebounced = _.debounce(this._onFieldInput.bind(this), 400);
this._onFieldInputDebounced = debounce(this._onFieldInput.bind(this), 400);
this.$el.on('input.s_website_form', '.s_website_form_field', this._onFieldInputDebounced);
// Initialize datetimepickers
@@ -459,7 +460,7 @@ import { sprintf } from "@web/core/utils/strings";
if (invalid_inputs.length || error_fields[field_name]) {
$field.addClass('o_has_error');
$controls.addClass('is-invalid');
if (_.isString(error_fields[field_name])) {
if (typeof error_fields[field_name] === "string") {
$field.popover({content: error_fields[field_name], trigger: 'hover', container: 'body', placement: 'top'});
// update error message and show it.
$field.data("bs.popover").config.content = error_fields[field_name];
@@ -175,7 +175,7 @@ registry.category("web_tour.tours").add("website_form_editor_tour_results", {
$('body').append('<div id="website_form_editor_success_test_tour_'+model+'"></div>');
}
};
mailDef.then(_.bind(success, this, 'mail_mail'));
mailDef.then(success.bind(this, 'mail_mail'));
}
},
{
@@ -44,7 +44,7 @@ var EventRegistrationForm = Widget.extend({
$('#registration_form select').each(function () {
post[$(this).attr('name')] = $(this).val();
});
var tickets_ordered = _.some(_.map(post, function (value, key) { return parseInt(value); }));
var tickets_ordered = _.some(Object.values(post).map((value) => parseInt(value)));
if (!tickets_ordered) {
$('<div class="alert alert-info"/>')
.text(_t('Please select at least one ticket.'))
@@ -1,5 +1,6 @@
/** @odoo-module alias=website_event_exhibitor.event_exhibitor_connect **/
import { debounce } from "@web/core/utils/timing";
import Dialog from "web.Dialog";
import publicWidget from "web.public.widget";
import {Markup} from "web.utils";
@@ -63,7 +64,7 @@ publicWidget.registry.eventExhibitorConnect = publicWidget.Widget.extend({
*/
init: function () {
this._super(...arguments);
this._onConnectClick = _.debounce(this._onConnectClick, 500, true);
this._onConnectClick = debounce(this._onConnectClick, 500, true);
},
/**
@@ -1,6 +1,7 @@
/** @odoo-module alias=website_event_track.website_event_track_reminder **/
import { sprintf } from "@web/core/utils/strings";
import { debounce } from "@web/core/utils/timing";
import core from "web.core";
var _t = core._t;
import publicWidget from "web.public.widget";
@@ -16,7 +17,7 @@ publicWidget.registry.websiteEventTrackReminder = publicWidget.Widget.extend({
*/
init: function () {
this._super.apply(this, arguments);
this._onReminderToggleClick = _.debounce(this._onReminderToggleClick, 500, true);
this._onReminderToggleClick = debounce(this._onReminderToggleClick, 500, true);
},
//--------------------------------------------------------------------------
@@ -8,6 +8,7 @@ import wysiwygLoader from "web_editor.loader";
import publicWidget from "web.public.widget";
import { Markup } from "web.utils";
import session from "web.session";
import { escape } from "@web/core/utils/strings";
var qweb = core.qweb;
var _t = core._t;
@@ -76,9 +77,9 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
},
formatResult: function (term) {
if (term.isNew) {
return '<span class="badge bg-primary">New</span> ' + _.escape(term.text);
return '<span class="badge bg-primary">New</span> ' + escape(term.text);
} else {
return _.escape(term.text);
return escape(term.text);
}
},
ajax: {
@@ -616,9 +617,7 @@ publicWidget.registry.websiteForumSpam = publicWidget.Widget.extend({
_onMarkSpamClick: function (ev) {
var key = this.$('.modal .tab-pane.active').data('key');
var $inputs = this.$('.modal .tab-pane.active input.form-check-input:checked');
var values = _.map($inputs, function (o) {
return parseInt(o.value);
});
var values = $inputs.map((o) => parseInt(o.value));
return this._rpc({model: 'forum.post',
method: 'mark_as_offensive_batch',
args: [this.spamIDs, key, values],
@@ -34,7 +34,7 @@ var SelectBox = publicWidget.Widget.extend({
fields: ['id', 'name'],
},
}).then(function (result) {
self.objects = _.map(result, function (val) {
self.objects = result.map((val) => {
return {id: val.id, text: val.name};
});
}));
@@ -103,7 +103,7 @@ var SelectBox = publicWidget.Widget.extend({
* @param {Object} ev
*/
_onChange: function (ev) {
if (!ev.added || !_.isString(ev.added.id)) {
if (!ev.added || typeof ev.added.id !== "string") {
return;
}
this._createObject(ev.added.id);
@@ -327,7 +327,9 @@ var RecentLinks = publicWidget.Widget.extend({
* @private
*/
removeLinks: function () {
_.invoke(this.getChildren(), 'destroy');
this.getChildren().forEach((child) => {
child.destroy();
});
},
/**
* @private
@@ -12,6 +12,7 @@ import dom from "web.dom";
import { cartesian } from "@web/core/utils/arrays";
import { ComponentWrapper } from "web.OwlCompatibility";
import { ProductImageViewerWrapper } from "@website_sale/js/components/website_sale_image_viewer";
import { debounce } from "@web/core/utils/timing";
publicWidget.registry.WebsiteSale = publicWidget.Widget.extend(VariantMixin, cartHandlerMixin, {
selector: '.oe_website_sale',
@@ -49,8 +50,8 @@ publicWidget.registry.WebsiteSale = publicWidget.Widget.extend(VariantMixin, car
init: function () {
this._super.apply(this, arguments);
this._changeCartQuantity = _.debounce(this._changeCartQuantity.bind(this), 500);
this._changeCountry = _.debounce(this._changeCountry.bind(this), 500);
this._changeCartQuantity = debounce(this._changeCartQuantity.bind(this), 500);
this._changeCountry = debounce(this._changeCountry.bind(this), 500);
this.isWebsite = true;
this.filmStripStartX = 0;
@@ -181,9 +182,7 @@ publicWidget.registry.WebsiteSale = publicWidget.Widget.extend(VariantMixin, car
*/
_setUrlHash: function ($parent) {
var $attributes = $parent.find('input.js_variant_change:checked, select.js_variant_change option:selected');
var attributeIds = _.map($attributes, function (elem) {
return $(elem).data('value_id');
});
var attributeIds = $attributes.toArray().map((elem) => $(elem).data("value_id"));
window.location.replace('#attr=' + attributeIds.join(','));
},
/**
@@ -1,6 +1,7 @@
/** @odoo-module **/
import checkoutForm from "payment.checkout_form";
import { debounce } from "@web/core/utils/timing";
import checkoutForm from "payment.checkout_form";
import publicWidget from "web.public.widget";
const websiteSalePaymentMixin = {
@@ -9,7 +10,7 @@
* @override
*/
init: function () {
this._onClickTCCheckbox = _.debounce(this._onClickTCCheckbox, 100, true);
this._onClickTCCheckbox = debounce(this._onClickTCCheckbox, 100, true);
this._super(...arguments);
},
@@ -60,9 +61,7 @@
* @return {boolean} Whether the submit button can be enabled
*/
_isButtonReady: function () {
const disabledReasonFound = _.contains(
this.$submitButton.data('disabled_reasons'), true
);
const disabledReasonFound = this.$submitButton.data("disabled_reasons");
return !disabledReasonFound && this._super();
},
@@ -115,10 +114,7 @@
*/
_onClickTCCheckbox: function () {
this._adaptConfirmButton();
const disabledReasonFound = _.contains(
this.$submitButton.data('disabled_reasons'), true
);
const disabledReasonFound = this.$submitButton.data("disabled_reasons");
this.$submitButton.prop('disabled', disabledReasonFound);
},
}));
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { debounce } from "@web/core/utils/timing";
import publicWidget from "web.public.widget";
import {getCookie, setCookie} from "web.utils.cookies";
@@ -15,7 +16,7 @@ publicWidget.registry.productsRecentlyViewedUpdate = publicWidget.Widget.extend(
*/
init: function () {
this._super.apply(this, arguments);
this._onProductChange = _.debounce(this._onProductChange, this.debounceValue);
this._onProductChange = debounce(this._onProductChange, this.debounceValue);
},
//--------------------------------------------------------------------------
@@ -68,7 +68,7 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
self._removeFromComparelist(ev);
self.guard.exec(function() {
const newLink = '/shop/compare?products=' + encodeURIComponent(self.comparelist_product_ids);
window.location.href = _.isEmpty(self.comparelist_product_ids) ? '/shop' : newLink;
window.location.href = Object.keys(self.comparelist_product_ids || {}).length === 0 ? '/shop' : newLink;
});
});
@@ -172,7 +172,7 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
_addNewProductsImpl: function (product_id) {
var self = this;
$('.o_product_feature_panel').addClass('d-md-block');
if (!_.contains(self.comparelist_product_ids, product_id)) {
if (!self.comparelist_product_ids.includes(product_id)) {
self.comparelist_product_ids.push(product_id);
if (_.has(self.product_data, product_id)){
self._updateContent();
@@ -233,7 +233,7 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
_updateComparelistView: function () {
this.$('.o_product_circle').text(this.comparelist_product_ids.length);
this.$('.o_comparelist_button').removeClass('d-md-block');
if (_.isEmpty(this.comparelist_product_ids)) {
if (Object.keys(this.comparelist_product_ids || {}).length === 0) {
$('.o_product_feature_panel').removeClass('d-md-block');
} else {
$('.o_product_feature_panel').addClass('d-md-block');
@@ -96,7 +96,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin,
productReady.then(function (productId) {
productId = parseInt(productId, 10);
if (productId && !_.contains(self.wishlistProductIDs, productId)) {
if (productId && !self.wishlistProductIDs.includes(productId)) {
return self._rpc({
route: '/shop/wishlist/add',
params: {
@@ -250,7 +250,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin,
var $input = $(ev.target);
var $parent = $input.closest('.js_product');
var $el = $parent.find("[data-action='o_wishlist']");
if (!_.contains(this.wishlistProductIDs, parseInt($input.val(), 10))) {
if (!this.wishlistProductIDs.includes(parseInt($input.val(), 10))) {
$el.prop("disabled", false).removeClass('disabled').removeAttr('disabled');
} else {
$el.prop("disabled", true).addClass('disabled').attr('disabled', 'disabled');
@@ -265,7 +265,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin,
var productID = ev.currentTarget.value;
var $el = $(ev.target).closest('.js_add_cart_variants').find("[data-action='o_wishlist']");
if (!_.contains(this.wishlistProductIDs, parseInt(productID, 10))) {
if (!this.wishlistProductIDs.includes(parseInt(productID, 10))) {
$el.prop("disabled", false).removeClass('disabled').removeAttr('disabled');
} else {
$el.prop("disabled", true).addClass('disabled').attr('disabled', 'disabled');
@@ -450,9 +450,7 @@
clipboard.on('success', function () {
clipboard.destroy();
$clipboardBtn.tooltip('show');
_.delay(function () {
$clipboardBtn.tooltip("hide");
}, 800);
setTimeout(() => $clipboardBtn.tooltip("hide"), 800);
});
clipboard.on('error', function (e) {
clipboard.destroy();
@@ -619,7 +617,7 @@
// technical settings for the Fullscreen to work
var autoSetDone = false;
if (!slideData.hasQuestion) {
if (_.contains(['infographic', 'document', 'article'], slideData.category)) {
if (['infographic', 'document', 'article'].includes(slideData.category)) {
autoSetDone = true; // images, documents (local + external) and articles are marked as completed when opened
} else if (slideData.category === 'video' && slideData.videoSourceType === 'google_drive') {
autoSetDone = true; // google drive videos do not benefit from the YouTube integration and are marked as completed when opened
@@ -669,7 +667,7 @@
}
// render slide content
if (_.contains(['document', 'infographic'], slide.category)) {
if (['document', 'infographic'].includes(slide.category)) {
$content.html(QWeb.render('website.slides.fullscreen.content', {widget: this}));
} else if (slide.category === 'video' && slide.videoSourceType === 'youtube') {
this.videoPlayer = new VideoPlayerYouTube(this, slide);
@@ -280,7 +280,7 @@
$question.find('a.o_wslides_quiz_answer').each(function () {
var $answer = $(this);
if (!$answer.find('input[type=radio]')[0].checked &&
_.contains(self.slide.sessionAnswers, $answer.data('answerId'))) {
self.slide.sessionAnswers.includes($answer.data('answerId'))) {
$answer.find('input[type=radio]').prop('checked', true);
}
});
@@ -100,17 +100,17 @@ var SlideQuizFinishModal = Dialog.extend({
if (this.quiz.rankProgress.level_up) {
this.$('.o_wslides_quiz_modal_title').text(_t('Level up!'));
$progressBar.css('width', '100%');
_.delay(function () {
setTimeout(() => {
self.$('.o_wslides_quiz_modal_rank_lower_bound')
.text(self.quiz.rankProgress.new_rank.lower_bound);
self.$('.o_wslides_quiz_modal_rank_upper_bound')
.text(self.quiz.rankProgress.new_rank.upper_bound || "");
// we need to use _.delay to force DOM re-rendering between 0 and new percentage
_.delay(function () {
setTimeout(() => {
$progressBar.addClass('no-transition').width('0%');
}, 1);
_.delay(function () {
setTimeout(() => {
$progressBar
.removeClass('no-transition')
.width(self.quiz.rankProgress.new_rank.progress + '%');
@@ -128,15 +128,15 @@ var SlideQuizFinishModal = Dialog.extend({
*/
_animateText: function () {
var self = this;
_.delay(function () {
setTimeout(function () {
self.$('h4.o_wslides_quiz_modal_xp_gained').addClass('show in');
self.$('.o_wslides_quiz_modal_dismiss').removeClass('d-none');
}, 800);
if (this.quiz.rankProgress.level_up) {
_.delay(function () {
setTimeout(function () {
self.$('.o_wslides_quiz_modal_rank_motivational').addClass('fade');
_.delay(function () {
setTimeout(function () {
self.$('.o_wslides_quiz_modal_rank_motivational').html(
self.quiz.rankProgress.last_rank ?
self.quiz.rankProgress.description :
@@ -1,4 +1,5 @@
/* global PDFSlidesViewer */
/**
* This is a minimal version of the PDFViewer widget.
* It is NOT use in the website_slides module, but it is called when embedding
@@ -7,6 +8,14 @@
*/
$(function () {
function debounce(func, timeout = 300){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
if ($('#PDFViewer') && $('#PDFViewerCanvas')) { // check if presentation only
var MIN_ZOOM=1, MAX_ZOOM=10, ZOOM_INCREMENT=.5;
@@ -177,7 +186,7 @@ $(function () {
return false;
}
});
$(window).on('resize', _.debounce(function() {
$(window).on("resize", debounce(() => {
embeddedViewer.on_resize();
}, 500));

Some files were not shown because too many files have changed in this diff Show More