PageSourceSearch

https://gutscheine.cafe-wien.at/templates/qwk/t1/javascript/ticketsystem/ticket-getdata.js

js cafe-wien.at collected 2026-10-02 07:19:19 UTC 17,363 bytes, 376 lines download raw bytes

1if (!String.format) {
2  // String format function is used to inject variables into lang-file strings
3  // {i} indicators will be replaced by the corresponding variables
4  String.format = function (format) {
5    var args = Array.prototype.slice.call(arguments, 1);
6    return format.replace(/{(\d+)}/g, function (match, number) {
7      return typeof args[number] != 'undefined' ? args[number] : match;
8    });
9  };
10}
11
12// various objects and arrays are used for different types of displays which require different structures,
13// however they all contain very similar data, eventData is to be considered the base for all other structures.
14let eventData, eventDataOrder, ticketData, dateData, categoryData = [], ticketIDs, calendarData, listViewDateInfo,
15  reservationEventData, reservationTicketData;
16
17
18// buildAjax creates the API request url to fetch ticket data
19// due to the variety of filters and other settings, this url has to be built dynamically
20function buildAjax(catID = -1, dateFrom = "", dateTo = "", event = "", location = "", price = "") {
21  let baseUrl = "api/frontend/eventSequence?includeLinks&includeEventDates";
22  if (activateEventView && !activateCalendarView && !activateMonthlyView && !activateListView) {
23    baseUrl += "&excludeEventProducts=1&excludeAvailable=1";
24  }
25  let url;
26  if (clEvent) url = baseUrl + `&parentCategoryId=${catID}&eventDateFrom=`;
27  else url = baseUrl + "&eventDateFrom=";
28  // if no dateFrom is set the API will default to using the current date
29
30  if (dateFrom == "") {
31    let date = new Date();
32    date.setDate(date.getDate() - 1);
33    const day = showElapsed ? "01" : date.getDate();
34    // if showElapsed is enabled the base date for the unfiltered rendering will be the first of the month instead of the current date
35    url += (date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + day) + "T23:59:59Z";
36  } else url += dateFrom;
37  if (catID != -1 && !clEvent) {
38    url += "&q=eq(categoryId," + catID + ")";
39  }
40  if (dateTo != "") url += '&eventDateTo=' + dateTo + 'T23:59:00Z';
41  // event and location both use rql queries to filter and therefore have to be added as one parameter
42  if ((event != "" || location != "") && (catID == -1 || clEvent)) url += '&q=';
43  if (event != "") {
44    event = encodeURIComponent(event);
45    url += 'eq(name,"' + event + '")';
46  }
47  if (location != "") {
48    location = encodeURIComponent(location);
49    url += 'eq(location,"' + location + '")';
50  }
51  if (price != "") url += '&includeEventDateMinPrice&eventDateMinPriceTo=' + price;
52  if (editReservationId === -1) {
53    url += !(event != "" || location != "" || price != "") ? ('&eventDateLimit=' + unfilteredLimit) : ('&eventDateLimit=' + filteredLimit);
54  }
55  return url;
56}
57
58
59async function getData(callback, url) {
60  const categoryDataDone = getCategoryData();
61  await $.ajax({
62    url: appendSubShopId(url),
63    async: true,
64    success: (data) => {
65      categoryDataDone.finally(() => structureData(data, callback));
66    },
67    error: () => {
68      categoryDataDone.finally(() => callback(false));
69    }
70  });
71  await categoryDataDone;
72}
73
74
75function structureData(data, callback) {
76  eventData = {}, eventDataOrder = [], ticketData = {}, dateData = [],
77    ticketIDs = [], calendarData = {}, reservationEventData = {}, reservationTicketData = {};
78  let j = 0;
79  for (let i = 0; i < data.length; i++) {
80    if (!data[i].hasOwnProperty("eventDates")) continue;
81    for (let j = 0; j < data[i].eventDates.length; j++) {
82      let object = data[i].eventDates[j];
83
84      //all dates have to be reformatted, to avoid errors on iOS and Safari
85      let startArray = ('' + object.start).split(/[- :]/);
86      let endArray = ('' + object.end).split(/[- :]/);
87      let salesEndArray = ('' + object.salesEnd).split(/[- :]/);
88      let salesStartArray = ('' + object.salesStart).split(/[- :]/);
89      let startD = object.start = new Date(startArray[0], startArray[1] - 1, startArray[2], startArray[3], startArray[4], startArray[5]);
90      object.s = new Date(startArray[0], startArray[1] - 1, startArray[2]);
91      let endD = object.end = new Date(endArray[0], endArray[1] - 1, endArray[2], endArray[3], endArray[4], endArray[5]);
92      object.e = new Date(endArray[0], endArray[1] - 1, endArray[2]);
93      let saleEndD = object.salesEnd = new Date(salesEndArray[0], salesEndArray[1] - 1, salesEndArray[2], salesEndArray[3], salesEndArray[4], salesEndArray[5]);
94      let saleStartD = object.salesStart = new Date(salesStartArray[0], salesStartArray[1] - 1, salesStartArray[2], salesStartArray[3], salesStartArray[4], salesStartArray[5]);
95
96      object.startDate = (startD.getDate() < 10 ? '0' : '') + startD.getDate() + '.' + ((startD.getMonth() + 1) < 10 ? '0' : '') + (startD.getMonth() + 1) + '.' + startD.getFullYear();
97      object.endDate = (endD.getDate() < 10 ? '0' : '') + endD.getDate() + '.' + ((endD.getMonth() + 1) < 10 ? '0' : '') + (endD.getMonth() + 1) + '.' + endD.getFullYear();
98
99      object.salesEndDate = (saleEndD.getDate() < 10 ? '0' : '') + saleEndD.getDate() + '.' + ((saleEndD.getMonth() + 1) < 10 ? '0' : '') + (saleEndD.getMonth() + 1) + '.' + saleEndD.getFullYear();
100      object.salesStartDate = (saleStartD.getDate() < 10 ? '0' : '') + saleStartD.getDate() + '.' + ((saleStartD.getMonth() + 1) < 10 ? '0' : '') + (saleStartD.getMonth() + 1) + '.' + saleStartD.getFullYear();
101
102      object.startTime = (startD.getHours() < 10 ? '0' : '') + startD.getHours() + ':' + (startD.getMinutes() < 10 ? '0' : '') + startD.getMinutes();
103      object.endTime = (endD.getHours() < 10 ? '0' : '') + endD.getHours() + ':' + (endD.getMinutes() < 10 ? '0' : '') + endD.getMinutes();
104
105      object.salesEndTime = (saleEndD.getHours() < 10 ? '0' : '') + saleEndD.getHours() + ':' + (saleEndD.getMinutes() < 10 ? '0' : '') + saleEndD.getMinutes();
106      object.salesStartTime = (saleStartD.getHours() < 10 ? '0' : '') + saleStartD.getHours() + ':' + (saleStartD.getMinutes() < 10 ? '0' : '') + saleStartD.getMinutes();
107
108
109      object.multiDay = object.start.getDate() !== object.end.getDate();
110
111      object.startString = '' + object.start.toDateString();
112
113      // attributes converted to boolean
114      object.notSaleableOnline = object.notSaleableOnline === "1";
115      object.waitingListEnabled = object.waitingListEnabled === "1";
116
117      //determine state, state is not 100% analogous to BE states
118      if (object.state !== "ELAPSED") {
119        if (object.state === "CANCELED") object.state = "CANCELLED";
120        else if (object.available === 0 || object.available === "0") object.state = "SOLD_OUT";
121        else if (object.notSaleableOnline) object.state = "NO_SALE";
122        else if (object.salesEnd < new Date()) object.state = "SALE_END";
123        else if (object.salesStart > new Date()) object.state = "PRE_SALE";
124        else if ((object.quota / 3) > object.available) object.state = "NARROW";
125      }
126
127      if (typeof calendarDate !== 'undefined') {
128        if (object.state !== "ELAPSED" && calendarDate.toDateString() === new Date().toDateString() || calendarDate > object.start) calendarDate = object.start;
129      }
130
131      // the description and title attributes are required by the calendar
132      object.description = String.format(calendar_item_desc, object.startDate, object.startTime, data[i].location);
133      object.title = data[i].name;
134      delete object.name;
135
136      dateData.push(object);
137
138      if (!calendarData[object.start.toDateString()]) calendarData[object.start.toDateString()] = [];
139      calendarData[object.start.toDateString()].push(object);
140
141      if (object.multiDay) {
142        let date = new Date(object.start);
143        // calculate the day-difference between start & end date
144        const dLimit = Math.floor(Math.abs(object.end - object.start) / (1000 * 60 * 60 * 24));
145        for (let i = 1; i < dLimit; i++) {
146          date.setDate(date.getDate() + 1);
147          if (!calendarData[date.toDateString()]) calendarData[date.toDateString()] = [];
148          calendarData[date.toDateString()].push(object);
149        }
150        if (!calendarData[object.end.toDateString()]) calendarData[object.end.toDateString()] = [];
151        calendarData[object.end.toDateString()].push(object);
152      }
153
154      object.isParticipantAssignable = false;
155      for (const p in object.eventProducts) {
156        const item = object.eventProducts[p];
157        //the ticket IDs are filtered and added to a list of IDs, these are required to fetch the required ticket information from the ticket data API
158        ticketIDs.indexOf(item.id) === -1 ? ticketIDs.push(item.id) : null;
159        if (item.isParticipantAssignable) {
160          object.isParticipantAssignable = true;
161        }
162      }
163
164    }
165    // eventProductLink is used for the eventView and sequence display in category-listing
166    const getItem = (ed, state) => ed.find(e => e.state === state);
167    const states = ['ON_SALE', 'NARROW', 'PRE_SALE', 'SALE_END', 'SOLD_OUT', 'CANCELLED', 'NO_SALE'];
168    let result;
169    for (const s of states) {
170      result = getItem(data[i].eventDates, s);
171      if (result) break;
172    }
173    if(result) {
174      for (const t of result.eventProducts) {
175        if (t.available > 0) {
176          result = t.productLink;
177          break;
178        }
179      }
180      data[i].eventProductLink = result;
181    }
182    eventData[data[i].id] = data[i];
183    eventDataOrder[j] = data[i].id;
184    j++;
185  }
186  reservationEventData = eventData;
187  getTicketData(callback);
188}
189
190
191// fetches generalised ticket data which serves to reduce redundancy within the eventSequence model
192async function getTicketData(callback) {
193  await $.ajax({
194    url: appendSubShopId("api/frontend/eventProductDescription?includeParticipantConfig" + (ticketIDs.length > 0 ? ("&q=in(id,(" + ticketIDs.join() + "))") : "")),
195    async: true,
196    success: (data) => {
197      for (let i = 0; i < data.length; i++) {
198        //ticket objects are stored as-is
199        ticketData[data[i].id] = data[i];
200      }
201      reservationTicketData = ticketData;
202      if (activateEventView) evaluateGroupAvailability(callback);
203      else if (activateListView) evaluateListViewDates(callback);
204      else callback(true);
205    },
206    error: () => {
207      callback(false);
208    }
209  });
210}
211
212// this function fetches category-related static data and is used exclusively by the clEvent logic
213async function getCategoryData() {
214  categoryData = {};
215  await $.ajax({
216    url: appendSubShopId("api/category?parentCategoryId=" + catID),
217    async: true,
218    success: (data) => {
219      for (let i = 0; i < data.length; i++) {
220        categoryData[data[i].id] = data[i];
221      }
222    }
223  });
224}
225
226// groupAvailable is an attribute added to eventData objects which indicates the group availability
227// of an eventSequence based on the eventDate objects contained therein
228// the hierarchy of priority between eventDate states is reflected in the loop below
229function evaluateGroupAvailability(callback) {
230  for (let i in eventData) {
231    let groupAvailable = '';
232    let event = eventData[i];
233    for (let j in event.eventDates) {
234      let object = event.eventDates[j];
235      groupAvailable = evaluateState(object, groupAvailable)[0];
236    }
237    event.groupAvailable = groupAvailable;
238  }
239  if (activateListView) evaluateListViewDates(callback);
240  else callback(true);
241}
242
243// this function is used to evaluate the
244function evaluateState(object, groupState, groupId = -1) {
245  let state = groupState, id = groupId;
246  if (object.state == "ON_SALE" && groupState != 'ON_SALE') {
247    state = object.state;
248    id = object.id;
249  } else if (object.state == "NARROW" && groupState != 'ON_SALE') {
250    state = object.state;
251    id = object.id;
252  } else if (object.state == "PRE_SALE" && groupState != 'ON_SALE' && groupState != 'NARROW') {
253    state = object.state;
254    id = object.id;
255  } else if (object.state == "SALE_END" && groupState != 'ON_SALE' && groupState != 'NARROW' && groupState != 'PRE_SALE') {
256    state = object.state;
257    id = object.id;
258  } else if (object.state == "SOLD_OUT" && groupState != 'ON_SALE' && groupState != 'NARROW' && groupState != 'PRE_SALE' && groupState != 'SALE_END' && groupState != "WAITINGLIST") {
259    state = object.state;
260    id = object.id;
261    if (object.waitingListEnabled) {
262      state = "WAITINGLIST";
263      id = object.id;
264    }
265  } else if (object.state == "CANCELLED" && groupState != 'ON_SALE' && groupState != 'NARROW' && groupState != 'PRE_SALE' && groupState != 'SALE_END' && groupState != 'SOLD_OUT' && groupState != "WAITINGLIST") {
266    state = object.state;
267    id = object.id;
268  } else if (object.state == "NO_SALE" && groupState != 'ON_SALE' && groupState != 'NARROW' && groupState != 'PRE_SALE' && groupState != 'SALE_END' && groupState != 'SOLD_OUT' && groupState != "WAITINGLIST" && groupState != 'CANCELLED') {
269    state = object.state;
270    id = object.id;
271  }
272  return [state, id];
273}
274
275
276// this function is used to prepare a date reference model for the listView,
277// this is required to accurately group eventDates by date
278function evaluateListViewDates(callback) {
279  listViewDateInfo = {};
280  for (let i = 0; i < dateData.length; i++) {
281    let object = dateData[i];
282    if (object.multiDay) continue;
283    if (listViewDateInfo.hasOwnProperty(object.eventSequenceId)) {
284      if (listViewDateInfo[object.eventSequenceId].hasOwnProperty(object.startDate)) {
285        listViewDateInfo[object.eventSequenceId][object.startDate].ids.push(object.id);
286        let available = evaluateState(object, listViewDateInfo[object.eventSequenceId][object.startDate].state, listViewDateInfo[object.eventSequenceId][object.startDate].first);
287        listViewDateInfo[object.eventSequenceId][object.startDate].state = available[0];
288        listViewDateInfo[object.eventSequenceId][object.startDate].first = available[1];
289      } else {
290        listViewDateInfo[object.eventSequenceId][object.startDate] = {};
291        listViewDateInfo[object.eventSequenceId][object.startDate].ids = [];
292        listViewDateInfo[object.eventSequenceId][object.startDate].ids.push(object.id);
293        listViewDateInfo[object.eventSequenceId][object.startDate].state = object.state;
294        listViewDateInfo[object.eventSequenceId][object.startDate].first = object.id;
295      }
296    } else {
297      listViewDateInfo[object.eventSequenceId] = {};
298      listViewDateInfo[object.eventSequenceId][object.startDate] = {};
299      listViewDateInfo[object.eventSequenceId][object.startDate].ids = [];
300      listViewDateInfo[object.eventSequenceId][object.startDate].ids.push(object.id);
301      listViewDateInfo[object.eventSequenceId][object.startDate].state = object.state;
302      listViewDateInfo[object.eventSequenceId][object.startDate].first = object.id;
303    }
304  }
305  // the result of this function will look like something akin to this:
306  // {
307  //   22: {
308  //     01.07.2021: {ids: Array(1), state: "ON_SALE", first: "3088"}
309  //     03.07.2021: {ids: Array(1), state: "ON_SALE", first: "3092"}
310  //     27.06.2021: {ids: Array(1), state: "ON_SALE", first: "3080"}
311  //     28.06.2021: {ids: Array(3), state: "ON_SALE", first: "2374"}
312  //   }
313  //   24: {
314  //     11.07.2021: {ids: Array(3), state: "ON_SALE", first: "2710"}
315  //     17.07.2021: {ids: Array(1), state: "ON_SALE", first: "2712"}
316  //     26.06.2021: {ids: Array(4), state: "ON_SALE", first: "2700"}
317  //     27.06.2021: {ids: Array(3), state: "ON_SALE", first: "2702"}
318  //   }
319  // }
320  // the object represents the different available times and dates for each eventSequence
321  // the "first" ID is the eventDate that is initially rendered, all other options are listed within the "ids" attribute
322  // the "state" attribute is not actually required by the listView to function however it is useful for debugging
323  callback(true);
324}
325
326
327// a helper function used by all views
328// it returns 2 values: the state of availability as well as a description
329function getAvailable(object, index) {
330  //special criteria such as when an event is sold out will affect how it is rendered
331  let state, desc, disabled = "";
332  let ticket = object.eventProducts[index];
333  if (object.state == "SOLD_OUT" || ticket.available == 0) {
334    state = "full";
335    desc = calendar_legend_full;
336    disabled = "disabled";
337    if (object.waitingListEnabled) desc = calendar_legend_waitinglist;
338  } else {
339    switch (object.state) {
340      case 'CANCELLED':
341        state = "full";
342        desc = calendar_legend_cancelled;
343        disabled = "disabled";
344        break;
345      case 'NO_SALE':
346        state = "nosale";
347        desc = calendar_legend_nosale;
348        disabled = "disabled";
349        break;
350      case 'SALE_END':
351        state = "full";
352        desc = calendar_legend_full;
353        disabled = "disabled";
354        break;
355      case 'PRE_SALE':
356        state = "presale";
357        desc = calendar_legend_presale;
358        disabled = "disabled";
359        break;
360      case 'NARROW':
361        state = "narrow";
362        desc = calendar_legend_narrow;
363        break;
364      default:
365        let string, available;
366        state = "available";
367        desc = calendar_legend_available;
368        object.available > ticket.available ? available = ticket.available : available = object.available;
369        available === 1 ? string = ticket_available : string = tickets_available;
370        if (eventListShowAvailable) desc = String.format(string, available);
371
372    }
373  }
374  desc = hideAvailabilityLabels ? '' : desc;
375  return [state, desc, disabled];
376}

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.