PageSourceSearch

https://www.monopol-magazin.de/modules/custom/frisbii_shop/js/frisbii-cart.js?tltmen

js monopol-magazin.de collected 2026-09-24 08:49:31 UTC 5,768 bytes, 181 lines download raw bytes

1/**
2 * @file
3 * Frisbii Cart – client-side cart state via localStorage.
4 *
5 * Standalone ES6 object, no Drupal.behaviors.
6 * Consumed by frisbii-buy-button.js as a library dependency.
7 *
8 * localStorage key: frisbii_cart
9 * Item shape: {
10 *   offerId: string,
11 *   label: string,
12 *   quantity: number,
13 *   // optional product override fields (passed through to preparePurchase):
14 *   productId?: string,     // plenigoProductId
15 *   itemId?: string,        // productId + accessRightUniqueId (unique per issue)
16 *   price?: number,         // price override
17 *   shippingCosts?: number, // shipping cost per unit
18 *   maxQuantity?: number,   // quantity cap (e.g. 1 for subscriptions)
19 *   bonusId?: string,       // selected Plenigo bonus (Prämie) ID
20 *   bonusName?: string,     // selected bonus label for cart display
21 *   startDate?: string,     // subscription start date (YYYY-MM-DD, from start issue select)
22 *   startLabel?: string,    // selected start issue label for cart display
23 *   variant?: string,       // 'print'|'digital' for issues – picks the skeleton-offer slot pool
24 * }
25 *
26 * Deduplication key: itemId when present, otherwise offerId.
27 * This allows multiple issues that share the same offerId (one offer template)
28 * to coexist in the cart as separate line items.
29 */
30
31const FrisbiiCart = (() => {
32  const STORAGE_KEY = 'frisbii_cart';
33
34  /** @returns {Array} */
35  function getItems() {
36    try {
37      return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
38    }
39    catch {
40      return [];
41    }
42  }
43
44  /** @param {Array} items */
45  function setItems(items) {
46    localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
47  }
48
49  /**
50   * Returns the deduplication key for a cart item.
51   * Uses itemId when present so issues sharing an offerId stay separate.
52   *
53   * @param {string} offerId
54   * @param {string|undefined} itemId
55   * @returns {string}
56   */
57  function itemKey(offerId, itemId) {
58    return itemId || offerId;
59  }
60
61  /**
62   * Clamps a quantity to an item's maxQuantity (if set).
63   *
64   * @param {number} quantity
65   * @param {number|undefined} maxQuantity
66   * @returns {number}
67   */
68  function clampQuantity(quantity, maxQuantity) {
69    return maxQuantity ? Math.min(quantity, maxQuantity) : quantity;
70  }
71
72  /**
73   * Adds an item or increments its quantity if already in the cart.
74   * Quantities never exceed meta.maxQuantity when set.
75   *
76   * @param {string} offerId
77   * @param {string} label
78   * @param {number} [quantity=1]
79   * @param {{productId?: string, itemId?: string, price?: number, shippingCosts?: number, maxQuantity?: number, bonusId?: string, bonusName?: string, startDate?: string, startLabel?: string}} [meta={}]
80   */
81  function add(offerId, label, quantity = 1, meta = {}) {
82    const items = getItems();
83    const key = itemKey(offerId, meta.itemId);
84    const existing = items.find(item => itemKey(item.offerId, item.itemId) === key);
85    if (existing) {
86      // Backfill maxQuantity on items stored before it existed.
87      if (existing.maxQuantity === undefined && meta.maxQuantity !== undefined) {
88        existing.maxQuantity = meta.maxQuantity;
89      }
90      existing.quantity = clampQuantity(existing.quantity + quantity, existing.maxQuantity);
91      // Re-adding with a (new) start issue selection replaces the stored one;
92      // an empty startDate ("next possible issue") clears it.
93      if (meta.startDate !== undefined) {
94        if (meta.startDate) {
95          existing.startDate = meta.startDate;
96          existing.startLabel = meta.startLabel || '';
97        }
98        else {
99          delete existing.startDate;
100          delete existing.startLabel;
101        }
102      }
103    }
104    else {
105      const item = { offerId, label, quantity: clampQuantity(quantity, meta.maxQuantity), ...meta };
106      // Never store an empty start issue selection.
107      if (!item.startDate) {
108        delete item.startDate;
109        delete item.startLabel;
110      }
111      items.push(item);
112    }
113    setItems(items);
114  }
115
116  /**
117   * Marks an item as single-purchase: quantity 1, maxQuantity 1.
118   * Used to normalize legacy cart items (stored before maxQuantity existed).
119   *
120   * @param {string} key
121   */
122  function limitToSingle(key) {
123    const items = getItems();
124    const existing = items.find(item => itemKey(item.offerId, item.itemId) === key);
125    if (existing) {
126      existing.quantity = 1;
127      existing.maxQuantity = 1;
128      setItems(items);
129    }
130  }
131
132  /**
133   * Removes an item by its cart key (itemId if set, otherwise offerId).
134   *
135   * @param {string} key
136   */
137  function remove(key) {
138    setItems(getItems().filter(item => itemKey(item.offerId, item.itemId) !== key));
139  }
140
141  /**
142   * Sets the quantity for an item identified by its cart key.
143   * Removes the item when quantity drops to 0 or below; caps at maxQuantity.
144   *
145   * @param {string} key
146   * @param {number} quantity
147   */
148  function updateQuantity(key, quantity) {
149    if (quantity <= 0) {
150      remove(key);
151      return;
152    }
153    const items = getItems();
154    const existing = items.find(item => itemKey(item.offerId, item.itemId) === key);
155    if (existing) {
156      existing.quantity = clampQuantity(quantity, existing.maxQuantity);
157      setItems(items);
158    }
159  }
160
161  /**
162   * Clears the cart (call after successful checkout).
163   */
164  function clear() {
165    localStorage.removeItem(STORAGE_KEY);
166  }
167
168  /**
169   * Returns the total item count (sum of all quantities).
170   *
171   * @returns {number}
172   */
173  function count() {
174    return getItems().reduce((sum, item) => sum + item.quantity, 0);
175  }
176
177  return { add, remove, updateQuantity, limitToSingle, getItems, clear, count };
178})();
179
180// Expose globally so frisbii-buy-button.js and templates can access it.
181window.FrisbiiCart = FrisbiiCart;

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.