PageSourceSearch

https://site.fr/_static/8c8af5c529149f32/scripts/features/common/cart-list.js

js site.fr collected 2026-09-24 18:21:13 UTC 12,458 bytes, 326 lines download raw bytes

1import {
2  formatCurrencyValue,
3  getCurrencyFromRoot,
4  getDomainInfoState,
5  getRootData,
6  tText,
7} from '../../client-utils.js';
8import { normalizeText } from '../../value-utils.js';
9import { PRODUCT_IDS } from '../../product-ids.js';
10import { normalizePlanKey, getPlanLabel, toNumber } from '../domain/plans-shared.js';
11import { toDisplayDomainOrderPeriod } from '../domain/domain-pricing-options.js';
12import { getCartProductDefinition, isPlanCartProduct } from './plan-cart-model.js';
13import { getDomainTld } from './normalization.js';
14import { getCartDomainName } from './cart-domain-names.js';
15
16const formatCartCurrency = (value) =>
17  formatCurrencyValue(toNumber(value), {
18    rootData: getRootData(),
19  });
20
21const formatCartPeriod = (period) => {
22  const parsed = Number(period);
23  const safe = Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
24  const key = safe === 1 ? 'backend.common.cartForm.orderPeriod.year' : 'backend.common.cartForm.orderPeriod.years';
25  return tText(key, { vars: { number: { $val: safe } } });
26};
27
28const formatCartDomainSubtitle = (periodLabel) =>
29  `${periodLabel}, ${tText('backend.common.domainNamePlans.domainOnly')}`;
30
31const getDomainProductType = (productName) => {
32  const types = {
33    'Holder change': 'backend.common.cartForm.domainType.holderchange',
34    Registration: 'backend.common.cartForm.domainType.registration',
35    Transfer: 'backend.common.cartForm.domainType.transfer',
36    Renewal: 'backend.common.cartForm.domainType.renewal',
37    Restore: 'backend.common.cartForm.domainType.restore',
38  };
39  return tText(types[productName] || types.Registration);
40};
41
42const getPlanProductType = (productName) => {
43  const types = {
44    'Renew all in one plan': 'backend.common.cartForm.domainType.renewal',
45    'Upgrade all in one plan': 'backend.common.cart.upgradePlan',
46  };
47  return tText(types[productName] || 'backend.common.cartForm.domainType.newPlan');
48};
49
50const formatCartPlanId = (planId) =>
51  tText('backend.common.cartForm.plan.id', {
52    vars: { id: { $val: planId } },
53  });
54
55const getDomainAction = (productName) => {
56  const actions = {
57    'Holder change': 'holder-change',
58    Transfer: 'transfer',
59    Renewal: 'renewal',
60    Restore: 'restore',
61  };
62  return actions[productName] || 'registration';
63};
64
65const resolveCartDomainName = (product) => {
66  const configuration = product?.shopping_cart_product_configuration?.product_configuration || null;
67  const directName = getCartDomainName(product);
68  if (directName) {
69    return directName;
70  }
71
72  const referencedDomainId = Number(
73    configuration?.domain_name_to_renew_id ?? configuration?.domain_name_to_restore_id ?? configuration?.domain_name_id,
74  );
75  if (!Number.isFinite(referencedDomainId) || referencedDomainId <= 0) {
76    return '';
77  }
78
79  const name = normalizeText(configuration?.name);
80  const topLevelDomain = normalizeText(configuration?.top_level_domain);
81  if (name && topLevelDomain) {
82    return `${name}.${topLevelDomain}`;
83  }
84
85  const domainInfo = getDomainInfoState()?.data || {};
86  const referencedDomain = [...(domainInfo.activeDomains || []), ...(domainInfo.expiredDomains || [])].find(
87    (domain) => Number(domain?.id) === referencedDomainId,
88  );
89  return normalizeText(referencedDomain?.name);
90};
91
92const resolveCartDisplayDomainName = (product) =>
93  normalizeText(product?.display_domain_name) || resolveCartDomainName(product);
94
95const createCartEventItem = (product, index) => {
96  const currency = getCurrencyFromRoot(getRootData()) || 'EUR';
97  const configuration = product?.shopping_cart_product_configuration?.product_configuration || null;
98  const productType = normalizeText(product?.product?.type?.type).toLowerCase();
99  let name = '';
100
101  if (productType === 'domain') {
102    name = normalizeText(resolveCartDisplayDomainName(product)).toLowerCase();
103  } else if (productType === 'plan') {
104    name = normalizeText(configuration?.plan_name).toLowerCase();
105  } else {
106    name = normalizeText(configuration?.product?.name?.name || product?.product?.name?.name).toLowerCase();
107  }
108
109  return {
110    price: product?.price,
111    name,
112    index,
113    currency,
114  };
115};
116
117const buildCartProductViewModel = (product) => {
118  const isDomain = product?.product?.type?.type === 'domain';
119  const configuration = product?.shopping_cart_product_configuration?.product_configuration || null;
120  const orderPeriod = configuration?.order_period;
121  const displayOrderPeriod =
122    isDomain && orderPeriod != null
123      ? toDisplayDomainOrderPeriod(orderPeriod, configuration?.minimum_year)
124      : orderPeriod;
125  const periodLabel = formatCartPeriod(displayOrderPeriod);
126  const productId = product?.id != null ? String(product.id) : '';
127  const productName = product?.product?.name?.name;
128
129  if (isDomain) {
130    const isHolderChange = productName === 'Holder change';
131    const domainName = resolveCartDomainName(product);
132    const configId = Number(product?.shopping_cart_product_configuration?.id);
133    const hasConfig = Number.isFinite(configId) && configId > 0;
134    const domainType = getDomainProductType(productName);
135
136    return {
137      productId,
138      title: resolveCartDisplayDomainName(product) || domainType,
139      subtitle: isHolderChange ? '' : formatCartDomainSubtitle(periodLabel),
140      type: domainType,
141      price: formatCartCurrency(product?.price),
142      config: {
143        type: 'domain',
144        id: hasConfig ? String(configId) : '',
145        domainName,
146        domainTld: getDomainTld(domainName),
147        orderPeriod: orderPeriod != null ? String(displayOrderPeriod) : '',
148        privacyProtection: Number(configuration?.privacy_protection) === 1 ? '1' : '0',
149        domainAction: getDomainAction(productName),
150        transferKey: normalizeText(configuration?.auth_key),
151        domainHolderContactId: normalizeText(
152          isHolderChange ? configuration?.new_contact_id : configuration?.contact?.id,
153        ),
154        ...(isHolderChange
155          ? {
156              domainId: normalizeText(configuration?.domain_name_id),
157              allowUserChange: Number(configuration?.allow_user_change) ? '1' : '0',
158            }
159          : {}),
160        hasConfig,
161      },
162    };
163  }
164
165  const planName = configuration?.plan_name || '';
166  const planKey = normalizePlanKey(planName || product?.product?.name?.name || '');
167  const configId = Number(product?.shopping_cart_product_configuration?.id);
168  const hasConfig = Number.isFinite(configId) && configId > 0;
169  const isUpgrade = getCartProductDefinition(product)?.id === PRODUCT_IDS.UPGRADE_ALL_IN_ONE_PLAN;
170  const upgradedPlanId = configuration?.all_in_one_plan?.id ?? configuration?.customer_plan?.id;
171
172  return {
173    productId,
174    title: getPlanLabel(planKey || planName || product?.product?.name?.name || '') || planName,
175    subtitle: isUpgrade && upgradedPlanId != null ? formatCartPlanId(upgradedPlanId) : periodLabel,
176    type: getPlanProductType(productName),
177    price: formatCartCurrency(product?.price),
178    config:
179      isUpgrade || !isPlanCartProduct(product)
180        ? null
181        : {
182            type: 'plan',
183            id: hasConfig ? String(configId) : '',
184            planKey,
185            planActionId: getCartProductDefinition(product)?.id || '',
186            orderPeriod: orderPeriod != null ? String(orderPeriod) : '',
187            hasConfig,
188          },
189  };
190};
191
192const renderCartProductList = ({
193  container,
194  template,
195  products,
196  allowConfig = true,
197  allowRemove = true,
198  isConfigAvailable = false,
199} = {}) => {
200  if (!(container instanceof HTMLElement) || !(template instanceof HTMLTemplateElement)) {
201    return;
202  }
203
204  container.replaceChildren();
205
206  if (!Array.isArray(products) || !products.length) {
207    return;
208  }
209
210  products.forEach((product) => {
211    const fragment = template.content.cloneNode(true);
212    const item = fragment.querySelector('[data-cart-item]');
213    const title = fragment.querySelector('[data-cart-item-title]');
214    const subtitle = fragment.querySelector('[data-cart-item-subtitle]');
215    const type = fragment.querySelector('[data-cart-item-type]');
216    const price = fragment.querySelector('[data-cart-item-price]');
217    const removeButton = fragment.querySelector('[data-cart-item-remove]');
218    const configButton = fragment.querySelector('[data-cart-item-config]');
219    const configLabel = fragment.querySelector('[data-cart-item-config-label]');
220    const viewModel = buildCartProductViewModel(product);
221
222    if (title instanceof HTMLElement) {
223      title.textContent = viewModel.title;
224    }
225    if (subtitle instanceof HTMLElement) {
226      subtitle.textContent = viewModel.subtitle;
227      if (viewModel.subtitle) {
228        subtitle.removeAttribute('hidden');
229      } else {
230        subtitle.setAttribute('hidden', '');
231      }
232    }
233    if (type instanceof HTMLElement) {
234      type.textContent = viewModel.type;
235    }
236    if (price instanceof HTMLElement) {
237      price.textContent = viewModel.price;
238    }
239    if (configLabel instanceof HTMLElement) {
240      configLabel.textContent = tText(
241        viewModel.config?.type === 'domain'
242          ? 'backend.common.cartForm.headings.domainConfiguration'
243          : 'backend.common.cartForm.headings.planConfiguration',
244      );
245    }
246
247    if (item instanceof HTMLElement) {
248      item.setAttribute('data-cart-item-id', viewModel.productId);
249    }
250
251    if (removeButton instanceof HTMLElement) {
252      removeButton.setAttribute('data-cart-item-id', viewModel.productId);
253      removeButton.setAttribute('aria-label', tText('common.cart.removeProduct'));
254      if (!allowRemove) {
255        removeButton.setAttribute('hidden', '');
256        if (removeButton instanceof HTMLButtonElement) {
257          removeButton.disabled = true;
258          removeButton.setAttribute('aria-disabled', 'true');
259        }
260      } else {
261        removeButton.removeAttribute('hidden');
262        if (removeButton instanceof HTMLButtonElement) {
263          removeButton.disabled = false;
264          removeButton.removeAttribute('aria-disabled');
265        }
266      }
267    }
268
269    if (configButton instanceof HTMLElement) {
270      const showConfig = allowConfig && isConfigAvailable && Boolean(viewModel.config);
271      const syncConfigAttribute = (name, value) => {
272        if (value !== undefined && value !== null && value !== '') {
273          configButton.setAttribute(name, value);
274          return;
275        }
276        configButton.removeAttribute(name);
277      };
278
279      if (!viewModel.config) {
280        configButton.remove();
281      } else if (!showConfig) {
282        configButton.setAttribute('hidden', '');
283        if (configButton instanceof HTMLButtonElement) {
284          configButton.disabled = true;
285          configButton.setAttribute('aria-disabled', 'true');
286        }
287      } else {
288        configButton.removeAttribute('hidden');
289
290        syncConfigAttribute('data-cart-config-id', viewModel.config.id);
291        syncConfigAttribute('data-cart-config-type', viewModel.config.type);
292        syncConfigAttribute('data-cart-config-plan', viewModel.config.planKey);
293        syncConfigAttribute('data-cart-config-plan-action-id', viewModel.config.planActionId);
294        syncConfigAttribute('data-cart-config-period', viewModel.config.orderPeriod);
295        syncConfigAttribute('data-cart-config-domain', viewModel.config.domainName);
296        syncConfigAttribute('data-cart-config-tld', viewModel.config.domainTld);
297        syncConfigAttribute('data-cart-config-privacy-protection', viewModel.config.privacyProtection);
298        syncConfigAttribute('data-cart-config-domain-action', viewModel.config.domainAction);
299        syncConfigAttribute('data-cart-config-transfer-key', viewModel.config.transferKey);
300        syncConfigAttribute('data-cart-config-domain-holder-contact', viewModel.config.domainHolderContactId);
301        syncConfigAttribute('data-cart-config-domain-id', viewModel.config.domainId);
302        syncConfigAttribute('data-cart-config-allow-user-change', viewModel.config.allowUserChange);
303
304        if (configButton instanceof HTMLButtonElement) {
305          configButton.disabled = !viewModel.config.hasConfig;
306          if (viewModel.config.hasConfig) {
307            configButton.removeAttribute('aria-disabled');
308          } else {
309            configButton.setAttribute('aria-disabled', 'true');
310          }
311        }
312      }
313    }
314
315    container.appendChild(fragment);
316  });
317};
318
319export {
320  buildCartProductViewModel,
321  createCartEventItem,
322  formatCartCurrency,
323  formatCartPeriod,
324  renderCartProductList,
325  resolveCartDomainName,
326};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.