diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index aa14716cb6..7af6886f5e 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -3767,10 +3767,6 @@ Your {organizer} team""")) # noqa: W291
'default': "7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D", # NoQA
'type': str,
},
- 'widget_vue2_origins': {
- 'default': '',
- 'type': str,
- },
}
PERSON_NAME_TITLE_GROUPS = OrderedDict([
('english_common', (_('Most common English titles'), (
diff --git a/src/pretix/control/forms/global_settings.py b/src/pretix/control/forms/global_settings.py
index f35753a027..2a710574da 100644
--- a/src/pretix/control/forms/global_settings.py
+++ b/src/pretix/control/forms/global_settings.py
@@ -105,13 +105,6 @@ class GlobalSettingsForm(SettingsForm):
domain=settings.SITE_URL
)
)),
- ('widget_vue2_origins', forms.CharField(
- widget=forms.Textarea(attrs={'rows': '3'}),
- required=False,
- # Not translated on purpose, this is a temporary feature and contains too many special case words
- label="Vue2 widget origins",
- help_text="One origin per line (e.g. https://example.com). Requests from these origins will be served the old vue2-based widget.",
- ))
])
responses = register_global_settings.send(self)
for r, response in sorted(responses, key=lambda r: str(r[0])):
diff --git a/src/pretix/presale/templates/pretixpresale/fragment_js.html b/src/pretix/presale/templates/pretixpresale/fragment_js.html
index cde66ed807..0d5b0ea32c 100644
--- a/src/pretix/presale/templates/pretixpresale/fragment_js.html
+++ b/src/pretix/presale/templates/pretixpresale/fragment_js.html
@@ -12,7 +12,7 @@
-
+
diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py
index bc1b8c42d7..eccea871f2 100644
--- a/src/pretix/presale/views/widget.py
+++ b/src/pretix/presale/views/widget.py
@@ -122,26 +122,9 @@ def widget_css_etag(request, version, **kwargs):
return f'{_get_source_cache_key(version)}-{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}'
-# use vite by default, serve old vue2-based widget only for widget_vue2_origins
-def _use_vite(request):
- if getattr(settings, 'PRETIX_WIDGET_VUE', False) or "legacy" in request.GET:
- return False
- origin = request.META.get('HTTP_ORIGIN', '')
- gs = GlobalSettingsObject()
- vue_origins = gs.settings.get('widget_vue2_origins', as_type=str, default='')
- if vue_origins and not origin:
- referer = request.META.get('HTTP_REFERER', '')
- origin = '/'.join(referer.split('/', 3)[:3])
- if origin and vue_origins:
- origins_list = [o.strip() for o in vue_origins.strip().splitlines() if o.strip()]
- return origin not in origins_list
- return True
-
-
def widget_js_etag(request, version, lang, **kwargs):
gs = GlobalSettingsObject()
- variant = 'vite' if _use_vite(request) else 'legacy'
- return gs.settings.get('widget_checksum_{}_{}_{}'.format(version, lang, variant))
+ return gs.settings.get('widget_checksum_{}_{}'.format(version, lang))
@gzip_page
@@ -169,16 +152,13 @@ def widget_css(request, version, **kwargs):
return resp
-def generate_widget_js(version, lang, use_vite=False):
+def generate_widget_js(version, lang):
code = []
with language(lang):
# Provide isolation
code.append('(function (siteglobals) {\n')
code.append('var module = {}, exports = {};\n')
- if use_vite:
- code.append('const LANG = "%s";\n' % lang)
- else:
- code.append('var lang = "%s";\n' % lang)
+ code.append('const LANG = "%s";\n' % lang)
c = JavaScriptCatalog()
c.translation = DjangoTranslation(lang, domain='djangojs')
@@ -202,23 +182,11 @@ def generate_widget_js(version, lang, use_vite=False):
i18n_js = template.render(context)
code.append(i18n_js)
- if use_vite:
- vite_js = finders.find('vite/widget/widget.js')
- if not vite_js:
- raise FileNotFoundError('Vite widget build not found. Run: npm run build:widget')
- with open(vite_js, 'r', encoding='utf-8') as fp:
- code.append(fp.read())
- else:
- files = [
- 'vuejs/vue.js' if settings.DEBUG else 'vuejs/vue.min.js',
- 'pretixpresale/js/widget/docready.js',
- 'pretixpresale/js/widget/floatformat.js',
- 'pretixpresale/js/widget/widget.js' if version == version_max else 'pretixpresale/js/widget/widget.v{}.js'.format(version),
- ]
- for fname in files:
- f = finders.find(fname)
- with open(f, 'r', encoding='utf-8') as fp:
- code.append(fp.read())
+ vite_js = finders.find('vite/widget/widget.js')
+ if not vite_js:
+ raise FileNotFoundError('Vite widget build not found. Run: npm run build:widget')
+ with open(vite_js, 'r', encoding='utf-8') as fp:
+ code.append(fp.read())
if settings.DEBUG:
code.append('})(this);\n')
@@ -230,14 +198,13 @@ def generate_widget_js(version, lang, use_vite=False):
return f"/* v{version} */\n" + code
-def get_widget_js(version, lang, use_vite, force_regenerate=False):
+def get_widget_js(version, lang, force_regenerate=False):
if settings.DEBUG:
- return generate_widget_js(version, lang, use_vite=use_vite).encode()
+ return generate_widget_js(version, lang).encode()
- variant = 'vite' if use_vite else 'legacy'
- cache_prefix = 'widget_js_data_v{}_{}_{}'.format(version, lang, variant)
- settings_key = 'widget_file_v{}_{}_{}'.format(version, lang, variant)
- checksum_key = 'widget_checksum_v{}_{}_{}'.format(version, lang, variant)
+ cache_prefix = 'widget_js_data_v{}_{}'.format(version, lang)
+ settings_key = 'widget_file_v{}_{}'.format(version, lang)
+ checksum_key = 'widget_checksum_v{}_{}'.format(version, lang)
gs = GlobalSettingsObject()
if not force_regenerate:
@@ -259,7 +226,7 @@ def get_widget_js(version, lang, use_vite, force_regenerate=False):
else:
fname = gs.settings.get(settings_key)
- data = generate_widget_js(version, lang, use_vite=use_vite).encode()
+ data = generate_widget_js(version, lang).encode()
checksum = hashlib.sha1(data).hexdigest()
should_save = (
not fname
@@ -267,7 +234,7 @@ def get_widget_js(version, lang, use_vite, force_regenerate=False):
)
if should_save:
newname = default_storage.save(
- 'widget/widget.{}.{}.{}.{}.js'.format(version, lang, variant, checksum),
+ 'widget/widget.{}.{}.{}.js'.format(version, lang, checksum),
ContentFile(data)
)
gs.settings.set(settings_key, 'file://' + newname)
@@ -284,8 +251,7 @@ def get_widget_js(version, lang, use_vite, force_regenerate=False):
def regenerate_all_widget_js():
for lc, ll in settings.LANGUAGES:
for version in range(version_min, version_max + 1):
- for use_vite in [True, False]:
- get_widget_js(version, lc, use_vite, force_regenerate=True)
+ get_widget_js(version, lc, force_regenerate=True)
@gzip_page
@@ -297,8 +263,7 @@ def widget_js(request, version, lang, **kwargs):
if version < version_min:
version = version_min
- use_vite = _use_vite(request)
- data = get_widget_js(version, lang, use_vite)
+ data = get_widget_js(version, lang)
resp = HttpResponse(data, content_type='text/javascript')
resp['Access-Control-Allow-Origin'] = '*'
diff --git a/src/pretix/settings.py b/src/pretix/settings.py
index c358b91a10..e32ca6e926 100644
--- a/src/pretix/settings.py
+++ b/src/pretix/settings.py
@@ -913,7 +913,6 @@ VITE_DEV_SERVER_PORT = 5173
VITE_DEV_SERVER = f"http://localhost:{VITE_DEV_SERVER_PORT}"
VITE_DEV_MODE = DEBUG
VITE_IGNORE = False # Used to ignore `collectstatic`/`rebuild`
-PRETIX_WIDGET_VUE = os.environ.get('PRETIX_WIDGET_VUE', '') not in ('', '0')
if DEBUG:
# Reload if settings file changes
diff --git a/src/pretix/static/pretixpresale/js/widget/floatformat.js b/src/pretix/static/pretixpresale/js/floatformat.js
similarity index 100%
rename from src/pretix/static/pretixpresale/js/widget/floatformat.js
rename to src/pretix/static/pretixpresale/js/floatformat.js
diff --git a/src/pretix/static/pretixpresale/js/widget/docready.js b/src/pretix/static/pretixpresale/js/widget/docready.js
deleted file mode 100644
index f23aeef508..0000000000
--- a/src/pretix/static/pretixpresale/js/widget/docready.js
+++ /dev/null
@@ -1,81 +0,0 @@
-// Pure-js replacement for $.ready
-// by John Friend, https://github.com/jfriend00/docReady
-// MIT License
-
-(function (funcName, baseObj) {
- 'use strict'
- // The public function name defaults to window.docReady
- // but you can modify the last line of this function to pass in a different object or method name
- // if you want to put them in a different namespace and those will be used instead of
- // window.docReady(...)
- funcName = funcName || 'docReady'
- baseObj = baseObj || window
- let readyList = []
- let readyFired = false
- let readyEventHandlersInstalled = false
-
- // call this when the document is ready
- // this function protects itself against being called more than once
- function ready () {
- if (!readyFired) {
- // this must be set to true before we start calling callbacks
- readyFired = true
- for (let i = 0; i < readyList.length; i++) {
- // if a callback here happens to add new ready handlers,
- // the docReady() function will see that it already fired
- // and will schedule the callback to run right after
- // this event loop finishes so all handlers will still execute
- // in order and no new ones will be added to the readyList
- // while we are processing the list
- readyList[i].fn.call(window, readyList[i].ctx)
- }
- // allow any closures held by these functions to free
- readyList = []
- }
- }
-
- function readyStateChange () {
- if (document.readyState === 'complete') {
- ready()
- }
- }
-
- // This is the one public interface
- // docReady(fn, context);
- // the context argument is optional - if present, it will be passed
- // as an argument to the callback
- baseObj[funcName] = function (callback, context) {
- if (typeof callback !== 'function') {
- throw new TypeError('callback for docReady(fn) must be a function')
- }
- // if ready has already fired, then just schedule the callback
- // to fire asynchronously, but right away
- if (readyFired) {
- setTimeout(function () { callback(context) }, 1)
- return
- } else {
- // add the function and context to the list
- readyList.push({ fn: callback, ctx: context })
- }
- // if document already ready to go, schedule the ready function to run
- // IE only safe when readyState is "complete", others safe when readyState is "interactive"
- if (document.readyState === 'complete' || (!document.attachEvent && document.readyState === 'interactive')) {
- setTimeout(ready, 1)
- } else if (!readyEventHandlersInstalled) {
- // otherwise if we don't have event handlers installed, install them
- if (document.addEventListener) {
- // first choice is DOMContentLoaded event
- document.addEventListener('DOMContentLoaded', ready, false)
- // backup is window load event
- window.addEventListener('load', ready, false)
- } else {
- // must be IE
- document.attachEvent('onreadystatechange', readyStateChange)
- window.attachEvent('onload', ready)
- }
- readyEventHandlersInstalled = true
- }
- }
-})('docReady', window)
-// modify this previous line to pass in your own method name
-// and object for the method to be attached to
diff --git a/src/pretix/static/pretixpresale/js/widget/widget.js b/src/pretix/static/pretixpresale/js/widget/widget.js
deleted file mode 100644
index 0073f6ca14..0000000000
--- a/src/pretix/static/pretixpresale/js/widget/widget.js
+++ /dev/null
@@ -1,2592 +0,0 @@
-/* global siteglobals, module, lang, django */
-/* PRETIX WIDGET BEGINS HERE */
-/* This is embedded in an isolation wrapper that exposes siteglobals as the global
- scope. */
-
-window.PretixWidget = {
- build_widgets: true,
- widget_data: {
- referer: location.href
- }
-}
-
-let Vue = module.exports
-
-let strings = {
- quantity: django.pgettext('widget', 'Quantity'),
- quantity_dec: django.pgettext('widget', 'Decrease quantity'),
- quantity_inc: django.pgettext('widget', 'Increase quantity'),
- filter_events_by: django.pgettext('widget', 'Filter events by'),
- filter: django.pgettext('widget', 'Filter'),
- price: django.pgettext('widget', 'Price'),
- original_price: django.pgettext('widget', 'Original price: %s'),
- new_price: django.pgettext('widget', 'New price: %s'),
- select: django.pgettext('widget', 'Select'),
- select_item: django.pgettext('widget', 'Select %s'),
- select_variant: django.pgettext('widget', 'Select variant %s'),
- sold_out: django.pgettext('widget', 'Sold out'),
- buy: django.pgettext('widget', 'Buy'),
- register: django.pgettext('widget', 'Register'),
- reserved: django.pgettext('widget', 'Reserved'),
- free: django.pgettext('widget', 'FREE'),
- price_from: django.pgettext('widget', 'from %(currency)s %(price)s'),
- image_of: django.pgettext('widget', 'Image of %s'),
- tax_incl: django.pgettext('widget', 'incl. %(rate)s% %(taxname)s'),
- tax_plus: django.pgettext('widget', 'plus %(rate)s% %(taxname)s'),
- tax_incl_mixed: django.pgettext('widget', 'incl. taxes'),
- tax_plus_mixed: django.pgettext('widget', 'plus taxes'),
- quota_left: django.pgettext('widget', 'currently available: %s'),
- unavailable_require_voucher: django.pgettext('widget', 'Only available with a voucher'),
- unavailable_available_from: django.pgettext('widget', 'Not yet available'),
- unavailable_available_until: django.pgettext('widget', 'Not available anymore'),
- unavailable_active: django.pgettext('widget', 'Currently not available'),
- unavailable_hidden_if_item_available: django.pgettext('widget', 'Not yet available'),
- order_min: django.pgettext('widget', 'minimum amount to order: %s'),
- exit: django.pgettext('widget', 'Close ticket shop'),
- loading_error: django.pgettext('widget', 'The ticket shop could not be loaded.'),
- loading_error_429: django.pgettext('widget', 'There are currently a lot of users in this ticket shop. Please '
- + 'open the shop in a new tab to continue.'),
- open_new_tab: django.pgettext('widget', 'Open ticket shop'),
- checkout: django.pgettext('widget', 'Checkout'),
- cart_error: django.pgettext('widget', 'The cart could not be created. Please try again later'),
- cart_error_429: django.pgettext('widget', 'We could not create your cart, since there are currently too many '
- + 'users in this ticket shop. Please click "Continue" to retry in a new tab.'),
- waiting_list: django.pgettext('widget', 'Waiting list'),
- cart_exists: django.pgettext('widget', 'You currently have an active cart for this event. If you select more'
- + ' products, they will be added to your existing cart.'),
- resume_checkout: django.pgettext('widget', 'Resume checkout'),
- redeem_voucher: django.pgettext('widget', 'Redeem a voucher'),
- redeem: django.pgettext('widget', 'Redeem'),
- voucher_code: django.pgettext('widget', 'Voucher code'),
- close: django.pgettext('widget', 'Close'),
- close_checkout: django.pgettext('widget', 'Close checkout'),
- cancel_blocked: django.pgettext('widget', 'You cannot cancel this operation. Please wait for loading to finish.'),
- continue: django.pgettext('widget', 'Continue'),
- variations: django.pgettext('widget', 'Show variants'),
- hide_variations: django.pgettext('widget', 'Hide variants'),
- back_to_list: django.pgettext('widget', 'Choose a different event'),
- back_to_dates: django.pgettext('widget', 'Choose a different date'),
- back: django.pgettext('widget', 'Back'),
- next_month: django.pgettext('widget', 'Next month'),
- previous_month: django.pgettext('widget', 'Previous month'),
- next_week: django.pgettext('widget', 'Next week'),
- previous_week: django.pgettext('widget', 'Previous week'),
- show_seating: django.pgettext('widget', 'Open seat selection'),
- seating_plan_waiting_list: django.pgettext('widget', 'Some or all ticket categories are currently sold out. If you want, you can add yourself to the waiting list. We will then notify if seats are available again.'),
- load_more: django.pgettext('widget', 'Load more'),
- days: {
- MO: django.gettext('Mo'),
- TU: django.gettext('Tu'),
- WE: django.gettext('We'),
- TH: django.gettext('Th'),
- FR: django.gettext('Fr'),
- SA: django.gettext('Sa'),
- SU: django.gettext('Su'),
- MONDAY: django.gettext('Monday'),
- TUESDAY: django.gettext('Tuesday'),
- WEDNESDAY: django.gettext('Wednesday'),
- THURSDAY: django.gettext('Thursday'),
- FRIDAY: django.gettext('Friday'),
- SATURDAY: django.gettext('Saturday'),
- SUNDAY: django.gettext('Sunday'),
- },
- months: {
- '01': django.gettext('January'),
- '02': django.gettext('February'),
- '03': django.gettext('March'),
- '04': django.gettext('April'),
- '05': django.gettext('May'),
- '06': django.gettext('June'),
- '07': django.gettext('July'),
- '08': django.gettext('August'),
- '09': django.gettext('September'),
- 10: django.gettext('October'),
- 11: django.gettext('November'),
- 12: django.gettext('December'),
- }
-}
-
-let setCookie = function (cname, cvalue, exdays) {
- let d = new Date()
- d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000))
- var expires = 'expires=' + d.toUTCString()
- if (!cvalue) {
- var expires = 'expires=Thu, 01 Jan 1970 00:00:00 GMT'
- cvalue = ''
- }
- let same_site = ''
- if (site_is_secure()) {
- same_site = ';SameSite=None;Secure'
- }
- document.cookie = cname + '=' + cvalue + ';' + expires + same_site + ';path=/'
-}
-
-let getCookie = function (name) {
- let value = '; ' + document.cookie
- let parts = value.split('; ' + name + '=')
- if (parts.length == 2) return parts.pop().split(';').shift() || null
- else return null
-}
-
-let padNumber = function (number, size) {
- let s = String(number)
- while (s.length < (size || 2)) { s = '0' + s }
- return s
-}
-
-let getISOWeeks = function (y) {
- let d, isLeap
-
- d = new Date(y, 0, 1)
- isLeap = new Date(y, 1, 29).getMonth() === 1
-
- // check for a Jan 1 that's a Thursday or a leap year that has a
- // Wednesday jan 1. Otherwise it's 52
- return d.getDay() === 4 || isLeap && d.getDay() === 3 ? 53 : 52
-}
-
-/* HTTP API Call helpers */
-let api = {
- _getJSON: function (endpoint, callback, err_callback) {
- let xhr = new window.XMLHttpRequest()
- xhr.open('GET', endpoint, true)
- xhr.onload = function (e) {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- callback(JSON.parse(xhr.responseText), xhr)
- } else {
- err_callback(xhr, e)
- }
- }
- }
- xhr.onerror = function (e) {
- console.error(xhr.statusText)
- err_callback(xhr, e)
- }
- xhr.send(null)
- },
-
- _postFormJSON: function (endpoint, form, callback, err_callback) {
- let params = [].filter.call(form.elements, function (el) {
- return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked
- })
- .filter(function (el) {
- return !!el.name && !!el.value
- })
- .filter(function (el) {
- return !el.disabled
- })
- .map(function (el) {
- return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value)
- }).join('&')
-
- let xhr = new window.XMLHttpRequest()
- xhr.open('POST', endpoint, true)
- xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
- xhr.onload = function (e) {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- callback(JSON.parse(xhr.responseText))
- } else {
- err_callback(xhr, e)
- }
- }
- }
- xhr.onerror = function (e) {
- err_callback(xhr, e)
- }
- xhr.send(params)
- }
-}
-
-let makeid = function (length) {
- let text = ''
- let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
-
- for (let i = 0; i < length; i++) {
- text += possible.charAt(Math.floor(Math.random() * possible.length))
- }
-
- return text
-}
-
-var site_is_secure = function () {
- return /https.*/.test(document.location.protocol)
-}
-
-let widget_id = makeid(16)
-
-/* Vue Components */
-Vue.component('Availbox', {
- props: {
- item: Object,
- variation: Object
- },
- computed: {
- voucher_jump_link: function () {
- return '#' + this.$root.html_id + '-voucher-input'
- },
- aria_labelledby: function () {
- return this.$root.html_id + '-item-label-' + this.item.id
- },
- dec_label: function () {
- return '- ' + (this.item.has_variations ? this.variation.value : this.item.name) + ': ' + strings.quantity_dec
- },
- inc_label: function () {
- return '+ ' + (this.item.has_variations ? this.variation.value : this.item.name) + ': ' + strings.quantity_inc
- },
- unavailability_reason_message: function () {
- let reason = this.item.current_unavailability_reason || this.variation?.current_unavailability_reason
- if (reason) {
- return strings['unavailable_' + reason] || reason
- }
- return ''
- },
- label_select_item: function () {
- return this.item.has_variations
- ? strings.select_variant.replace('%s', this.variation.value)
- : strings.select_item.replace('%s', this.item.name)
- },
- input_name: function () {
- if (this.item.has_variations) {
- return 'variation_' + this.item.id + '_' + this.variation.id
- } else {
- return 'item_' + this.item.id
- }
- },
- order_max: function () {
- return this.item.has_variations ? this.variation.order_max : this.item.order_max
- },
- avail: function () {
- return this.item.has_variations ? this.variation.avail : this.item.avail
- },
- waiting_list_show: function () {
- return this.avail[0] < 100 && this.$root.waiting_list_enabled && this.item.allow_waitinglist
- },
- waiting_list_url: function () {
- let u = this.$root.target_url + 'w/' + widget_id + '/waitinglist/?locale=' + lang + '&item=' + this.item.id
- if (this.item.has_variations) {
- u += '&var=' + this.variation.id
- }
- if (this.$root.subevent) {
- u += '&subevent=' + this.$root.subevent
- }
- u += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json) + this.$root.consent_parameter
- return u
- }
- },
- mounted: function () {
- if (!this.$root.cart_exists && this.$root.itemnum === 1 && (!this.$root.categories[0].items[0].has_variations || this.$root.categories[0].items[0].variations.length < 2) && !this.$root.has_seating_plan ? 1 : 0) {
- this.$refs.quantity.value = 1
- if (this.order_max === 1) {
- this.$refs.quantity.checked = true
- }
- }
- },
- methods: {
- on_step: function (e) {
- let t = e.target.tagName == 'BUTTON' ? e.target : e.target.closest('button')
- let step = parseFloat(t.getAttribute('data-step'))
- let controls = document.getElementById(t.getAttribute('data-controls'))
- this.$refs.quantity.value = Math.max(controls.min, Math.min(controls.max || Number.MAX_SAFE_INTEGER, (parseInt(this.$refs.quantity.value || '0')) + step))
- this.$refs.quantity.dispatchEvent(new CustomEvent('change', {
- bubbles: true,
- }))
- }
- },
- template: ('
')
-})
-Vue.component('Pricebox', {
- props: {
- price: Object,
- free_price: Boolean,
- field_name: String,
- suggested_price: Object,
- original_price: String,
- mandatory_priced_addons: Boolean,
- item_id: Number,
- },
- computed: {
- aria_labelledby: function () {
- return [
- this.$root.html_id + '-item-label-' + this.item_id,
- this.price_box_id
- ].join(' ')
- },
- price_box_id: function () {
- return this.$root.html_id + '-item-pricebox-' + this.item_id
- },
- price_desc_id: function () {
- return this.$root.html_id + '-item-pricedesc-' + this.item_id
- },
- display_price: function () {
- if (this.$root.display_net_prices) {
- return floatformat(this.price.net, this.$root.currency_places)
- } else {
- return floatformat(this.price.gross, this.$root.currency_places)
- }
- },
- display_price_nonlocalized: function () {
- if (this.$root.display_net_prices) {
- return parseFloat(this.price.net).toFixed(this.$root.currency_places)
- } else {
- return parseFloat(this.price.gross).toFixed(this.$root.currency_places)
- }
- },
- suggested_price_nonlocalized: function () {
- let price = this.suggested_price
- if (price === null) {
- price = this.price
- }
- if (this.$root.display_net_prices) {
- return parseFloat(price.net).toFixed(this.$root.currency_places)
- } else {
- return parseFloat(price.gross).toFixed(this.$root.currency_places)
- }
- },
- original_price_aria_label: function () {
- return django.interpolate(strings.original_price, [this.stripHTML(this.original_line)])
- },
- new_price_aria_label: function () {
- return django.interpolate(strings.new_price, [this.stripHTML(this.priceline)])
- },
- original_line: function () {
- return '' + this.$root.currency + ' ' + floatformat(this.original_price, this.$root.currency_places)
- },
- priceline: function () {
- if (this.price.gross === '0.00') {
- if (this.mandatory_priced_addons && !this.original_price) {
- return '\xA0' // nbsp, because an empty string would cause the HTML element to collapse
- }
- return strings.free
- } else {
- return '' + this.$root.currency + ' ' + this.display_price
- }
- },
- show_taxline: function () {
- // rate can either be "0.00" or "0" => parseFloat to check
- return Number.parseFloat(this.price.rate) && Number.parseFloat(this.price.gross)
- },
- taxline: function () {
- if (this.$root.display_net_prices) {
- if (this.price.includes_mixed_tax_rate) {
- return strings.tax_plus_mixed
- } else {
- return django.interpolate(strings.tax_plus, {
- rate: autofloatformat(this.price.rate, 2),
- taxname: this.price.name
- }, true)
- }
- } else {
- if (this.price.includes_mixed_tax_rate) {
- return strings.tax_incl_mixed
- } else {
- return django.interpolate(strings.tax_incl, {
- rate: autofloatformat(this.price.rate, 2),
- taxname: this.price.name
- }, true)
- }
- }
- }
- },
- methods: {
- stripHTML: function (s) {
- let div = document.createElement('div')
- div.innerHTML = s
- return div.textContent || div.innerText || ''
- },
- },
- template: ('')
-})
-Vue.component('Variation', {
- props: {
- variation: Object,
- item: Object,
- category: Object,
- },
- computed: {
- orig_price: function () {
- if (this.variation.original_price) {
- return this.variation.original_price
- }
- return this.item.original_price
- },
- quota_left_str: function () {
- return django.interpolate(strings['quota_left'], [this.variation.avail[1]])
- },
- variation_label_id: function () {
- return this.$root.html_id + '-variation-label-' + this.item.id + '-' + this.variation.id
- },
- variation_desc_id: function () {
- return this.$root.html_id + '-variation-desc-' + this.item.id + '-' + this.variation.id
- },
- variation_price_id: function () {
- return this.$root.html_id + '-variation-price-' + this.item.id + '-' + this.variation.id
- },
- aria_labelledby: function () {
- return [this.variation_label_id, this.variation_price_id].join(' ')
- },
- headingLevel: function () {
- return this.category.name ? '5' : '4'
- },
- },
- template: ('')
-})
-Vue.component('Item', {
- props: {
- item: Object,
- category: Object,
- },
- data: function () {
- return {
- expanded: this.$root.show_variations_expanded
- }
- },
- computed: {
- classObject: function () {
- return {
- 'pretix-widget-item': true,
- 'pretix-widget-item-with-picture': !!this.item.picture,
- 'pretix-widget-item-with-variations': this.item.has_variations
- }
- },
- varClasses: function () {
- return {
- 'pretix-widget-item-variations': true,
- 'pretix-widget-item-variations-expanded': this.expanded,
- }
- },
- picture_alt_text: function () {
- return django.interpolate(strings['image_of'], [this.item.name])
- },
- headingLevel: function () {
- return this.category.name ? '4' : '3'
- },
- item_label_id: function () {
- return this.$root.html_id + '-item-label-' + this.item.id
- },
- item_desc_id: function () {
- return this.$root.html_id + '-item-desc-' + this.item.id
- },
- item_price_id: function () {
- return this.$root.html_id + '-item-price-' + this.item.id
- },
- aria_labelledby: function () {
- return [this.item_label_id, this.item_price_id].join(' ')
- },
- min_order_str: function () {
- return django.interpolate(strings['order_min'], [this.item.order_min])
- },
- quota_left_str: function () {
- return django.interpolate(strings['quota_left'], [this.item.avail[1]])
- },
- show_toggle: function () {
- return this.item.has_variations && !this.$root.show_variations_expanded
- },
- pricerange: function () {
- if (this.item.free_price) {
- return django.interpolate(strings.price_from, {
- currency: this.$root.currency,
- price: floatformat(this.item.min_price, this.$root.currency_places)
- }, true).replace(this.$root.currency, '' + this.$root.currency + '')
- } else if (this.item.min_price !== this.item.max_price) {
- return '' + this.$root.currency + ' '
- + floatformat(this.item.min_price, this.$root.currency_places) + ' – '
- + floatformat(this.item.max_price, this.$root.currency_places)
- } else if (this.item.min_price === '0.00' && this.item.max_price === '0.00') {
- if (this.item.mandatory_priced_addons) {
- return '\xA0' // nbsp, because an empty string would cause the HTML element to collapse
- }
- return strings.free
- } else {
- return '' + this.$root.currency + ' ' + floatformat(this.item.min_price, this.$root.currency_places)
- }
- },
- variationsToggleLabel: function () {
- return this.expanded ? strings.hide_variations : strings.variations
- },
- },
- mounted: function () {
- if (this.$refs.variations) {
- if (!this.expanded) {
- let $this = this
- this.$refs.variations.hidden = true
- this.$refs.variations.addEventListener('transitionend', function (event) {
- if (event.target == this) {
- this.hidden = !$this.expanded
- this.style.maxHeight = 'none'
- }
- })
- this.$watch('expanded', function (newValue) {
- let v = this.$refs.variations
- v.hidden = false
- v.style.maxHeight = (newValue ? 0 : v.scrollHeight) + 'px'
- // Vue.nextTick does not work here
- window.setTimeout(function () {
- v.style.maxHeight = (!newValue ? 0 : v.scrollHeight) + 'px'
- }, 50)
- })
- }
- }
- },
- methods: {
- expand: function () {
- this.expanded = !this.expanded
- },
- lightbox: function () {
- this.$root.overlay.lightbox = {
- image: this.item.picture_fullsize,
- description: this.item.name,
- }
- },
- },
- template: (''
- + '
'
-
- // Product description
- + '
'
-
- // Price
- + '
'
-
- // Availability
- + '
'
-
- + '
'
- + '
'
-
- // Variations
- + '
'
- + ''
- + ''
- + '
'
-
- + '
')
-})
-Vue.component('Category', {
- props: {
- category: Object
- },
- template: ('')
-})
-
-let shared_methods = {
- buy: function (event) {
- if (this.$root.useIframe) {
- if (event) {
- event.preventDefault()
- }
- } else {
- return
- }
- if (this.$root.is_button && this.$root.items.length === 0) {
- if (this.$root.voucher_code) {
- this.voucher_open(this.$root.voucher_code)
- } else {
- this.resume()
- }
- } else {
- let url = this.$root.formAction + '&locale=' + lang + '&ajax=1'
- this.$root.overlay.frame_loading = true
-
- this.async_task_interval = 100
- let form = this.$refs.form
- if (form === undefined) {
- form = this.$refs.formcomp.$refs.form
- }
- api._postFormJSON(url, form, this.buy_callback, this.buy_error_callback)
- }
- },
- buy_error_callback: function (xhr, data) {
- if (xhr.status === 429 && typeof xhr.responseURL !== 'undefined') {
- this.$root.overlay.error_message = strings['cart_error_429']
- this.$root.overlay.frame_loading = false
- this.$root.overlay.error_url_after = this.$root.newTabTarget
- this.$root.overlay.error_url_after_new_tab = true
- return
- }
- if (xhr.status === 405 && typeof xhr.responseURL !== 'undefined') {
- // Likely a redirect!
- this.$root.target_url = xhr.responseURL.substr(0, xhr.responseURL.indexOf('/cart/add') - 18)
- this.$root.overlay.frame_loading = false
- this.buy()
- return
- }
- this.$root.overlay.error_message = strings['cart_error']
- this.$root.overlay.frame_loading = false
- },
- buy_check_error_callback: function (xhr, data) {
- if (xhr.status == 200 || (xhr.status >= 400 && xhr.status < 500)) {
- this.$root.overlay.error_message = strings['cart_error']
- this.$root.overlay.frame_loading = false
- } else {
- this.async_task_timeout = window.setTimeout(this.buy_check, 1000)
- }
- },
- buy_callback: function (data) {
- if (data.redirect) {
- if (data.cart_id) {
- this.$root.set_cart_id(data.cart_id)
- }
- if (data.redirect.substr(0, 1) === '/') {
- data.redirect = this.$root.target_url.replace(/^([^\/]+:\/\/[^\/]+)\/.*$/, '$1') + data.redirect
- }
- let url = data.redirect
- if (url.indexOf('?')) {
- url = url + '&iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id())
- } else {
- url = url + '?iframe=1&locale=' + lang + '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id())
- }
- url += this.$root.consent_parameter
- if (this.$root.additionalURLParams) {
- url += '&' + this.$root.additionalURLParams
- }
- if (data.success === false) {
- url = url.replace(/checkout\/start/g, '')
- this.$root.overlay.error_message = data.message
- if (data.has_cart) {
- this.$root.overlay.error_url_after = url
- }
- this.$root.overlay.frame_loading = false
- } else {
- this.$root.overlay.frame_src = url
- }
- } else {
- this.async_task_id = data.async_id
- if (data.check_url) {
- this.async_task_check_url = this.$root.target_url.replace(/^([^\/]+:\/\/[^\/]+)\/.*$/, '$1') + data.check_url
- }
- this.async_task_timeout = window.setTimeout(this.buy_check, this.async_task_interval)
- this.async_task_interval = 250
- }
- },
- buy_check: function () {
- api._getJSON(this.async_task_check_url, this.buy_callback, this.buy_check_error_callback)
- },
- redeem: function (event) {
- if (this.$root.useIframe) {
- event.preventDefault()
- this.voucher_open(this.voucher)
- }
- },
- voucher_open: function (voucher) {
- let redirect_url = this.$root.voucherFormTarget + '&voucher=' + encodeURIComponent(voucher)
- if (this.$root.useIframe) {
- this.$root.overlay.frame_src = redirect_url
- } else {
- window.open(redirect_url)
- }
- },
- resume: function () {
- if (!this.$root.get_cart_id() && this.$root.keep_cart) {
- // create an empty cart whose id we can persist
- this.$root.create_cart(this.resume)
- return
- }
- let redirect_url
- redirect_url = this.$root.target_url + 'w/' + widget_id + '/'
- if (this.$root.subevent && this.$root.is_button && this.$root.items.length === 0) {
- // button with subevent but no items
- redirect_url += this.$root.subevent + '/'
- }
- redirect_url += '?iframe=1&locale=' + lang
- if (this.$root.get_cart_id()) {
- redirect_url += '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id())
- if (this.$root.keep_cart) {
- // make sure the cart-id is used, even if the cart is currently empty
- redirect_url += '&ajax=1'
- }
- }
- if (this.$root.widget_data) {
- redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json)
- }
- redirect_url += this.$root.consent_parameter
- if (this.$root.additionalURLParams) {
- redirect_url += '&' + this.$root.additionalURLParams
- }
- if (this.$root.useIframe) {
- this.$root.overlay.frame_src = redirect_url
- } else {
- window.open(redirect_url)
- }
- },
-}
-
-let shared_widget_data = function () {
- return {
- async_task_id: null,
- async_task_check_url: null,
- async_task_timeout: null,
- async_task_interval: 100,
- voucher: null,
- mobile: false,
- }
-}
-
-let shared_loading_fragment = (
- ''
-)
-
-let shared_iframe_fragment = (
- ''
-)
-
-let shared_alert_fragment = (
- ''
-)
-
-let shared_lightbox_fragment = (
- ''
-)
-
-Vue.component('PretixOverlay', {
- data: function () {
- return {
- cancelBlocked: false,
- }
- },
- computed: {
- frameClasses: function () {
- return {
- 'pretix-widget-frame-holder': true,
- 'pretix-widget-frame-shown': this.$root.frame_shown || this.$root.frame_loading,
- 'pretix-widget-frame-isloading': this.$root.frame_loading,
- }
- },
- alertClasses: function () {
- return {
- 'pretix-widget-alert-holder': true,
- 'pretix-widget-alert-shown': this.$root.error_message,
- }
- },
- lightboxClasses: function () {
- return {
- 'pretix-widget-lightbox-holder': true,
- 'pretix-widget-lightbox-shown': this.$root.lightbox,
- 'pretix-widget-lightbox-isloading': this.$root.lightbox?.loading,
- }
- },
- cancelBlockedClasses: function () {
- return {
- 'pretix-widget-visibility-hidden': !this.cancelBlocked,
- }
- },
- },
- watch: {
- '$root.lightbox': function (newValue, oldValue) {
- if (newValue) {
- if (newValue.image != oldValue?.image) {
- this.$set(newValue, 'loading', true)
- }
- if (!oldValue) {
- this.$el?.querySelector('.pretix-widget-lightbox-holder').showModal()
- }
- }
- },
- '$root.error_message': function (newValue, oldValue) {
- if (newValue) {
- if (!oldValue) {
- this.$el?.querySelector('.pretix-widget-alert-holder').showModal()
- }
- }
- },
- '$root.frame_shown': function (newValue) {
- if (newValue) {
- let btn = this.$el?.querySelector('.pretix-widget-frame-close button')
- this.$nextTick(function () {
- btn.focus()
- })
- }
- },
- },
- mounted () {
- window.addEventListener('message', this.onMessage, false)
- },
- unmounted () {
- window.removeEventListener('message', this.onMessage, false)
- },
- methods: {
- onMessage: function (e) {
- if (e.data.type && e.data.type == 'pretix:widget:title') {
- this.$el.querySelector('iframe').title = e.data.title
- }
- },
- lightboxClose: function () {
- this.$root.lightbox = null
- },
- lightboxLoaded: function () {
- this.$root.lightbox.loading = false
- },
- errorClose: function (e) {
- this.$root.error_message = null
- this.$root.error_url_after = null
- this.$root.error_url_after_new_tab = false
- },
- errorContinue: function () {
- if (this.$root.error_url_after_new_tab) {
- window.open(this.$root.error_url_after)
- } else if (this.$root.overlay) {
- this.$root.overlay.frame_src = this.$root.error_url_after
- this.$root.frame_loading = true
- }
- },
- close: function (e) {
- if (this.$root.frame_loading) {
- // Chrome does not allow blocking dialog.cancel event more than once
- // => wiggle the loading-element and re-open the modal
- this.cancel(e)
- e.target.showModal()
- return
- }
- this.$root.frame_shown = false
- this.$root.parent.frame_dismissed = true
- this.$root.frame_src = ''
- this.$root.parent.reload()
- this.$root.parent.trigger_close_callback()
- },
- cancel: function (e) {
- // do not allow to cancel while frame is loading as we cannot abort the operation
- if (this.$root.frame_loading) {
- e.preventDefault()
- e.target.addEventListener('animationend', function () {
- e.target.classList.remove('pretix-widget-shake-once')
- }, { once: true })
- e.target.classList.add('pretix-widget-shake-once')
- this.cancelBlocked = true
- }
- },
- iframeLoaded: function () {
- if (this.$root.frame_loading) {
- this.$root.frame_loading = false
- this.cancelBlocked = false
- if (this.$root.frame_src) {
- this.$root.frame_shown = true
- }
- }
- },
- },
- template: (''
- + shared_iframe_fragment
- + shared_alert_fragment
- + shared_lightbox_fragment
- + '
'
- )
-})
-
-Vue.component('PretixWidgetEventForm', {
- data: function () {
- return {
- is_items_selected: false,
- }
- },
- computed: {
- id_voucher_input: function () {
- return this.$root.html_id + '-voucher-input'
- },
- aria_labelledby: function () {
- return this.$root.html_id + '-voucher-headline'
- },
- display_event_info: function () {
- return this.$root.display_event_info || (this.$root.display_event_info === null && (this.$root.events || this.$root.weeks || this.$root.days))
- },
- id_cart_exists_msg: function () {
- return this.$root.html_id + '-cart-exists'
- },
- buy_label: function () {
- let i, j, k, all_free = true
- for (i = 0; i < this.$root.categories.length; i++) {
- let cat = this.$root.categories[i]
- for (j = 0; j < cat.items.length; j++) {
- let item = cat.items[j]
- for (k = 0; k < item.variations.length; k++) {
- let v = item.variations[k]
- if (v.price.gross !== '0.00') {
- all_free = false
- break
- }
- }
- if ((item.variations.length === 0 && item.price.gross !== '0.00') || item.mandatory_priced_addons) {
- all_free = false
- break
- }
- }
- if (!all_free) {
- break
- }
- }
- if (all_free) {
- return strings.register
- } else {
- return strings.buy
- }
- },
- hiddenParams: function () {
- let params = new URL(this.$root.voucherFormTarget).searchParams
- params.delete('iframe')
- params.delete('take_cart_id')
- return [...params.entries()]
- },
- },
- watch: {
- '$root.overlay.frame_shown': function (newValue) {
- if (!newValue) {
- this.$refs.form.reset()
- this.calc_items_selected()
- }
- }
- },
- mounted: function () {
- this.$root.$on('focus_voucher_field', this.focus_voucher_field)
- this.$refs.form.addEventListener('change', this.calc_items_selected)
- },
- beforeUnmount: function () {
- this.$root.$off('focus_voucher_field', this.focus_voucher_field)
- this.$refs.form.removeEventListener('change', this.calc_items_selected)
- },
- methods: {
- back_to_list: function () {
- this.$root.target_url = this.$root.parent_stack.pop()
- this.$root.error = null
- if (!this.$root.subevent) {
- // reset if we are not in a series
- this.$root.name = null
- this.$root.frontpage_text = null
- }
- this.$root.subevent = null
- this.$root.offset = 0
- this.$root.append_events = false
- this.$root.trigger_load_callback()
- if (this.$root.events !== undefined && this.$root.events !== null) {
- this.$root.view = 'events'
- } else if (this.$root.days !== undefined && this.$root.days !== null) {
- this.$root.view = 'days'
- } else {
- this.$root.view = 'weeks'
- }
-
- let $el = this.$root.$el
- this.$root.$nextTick(function () {
- // wait for redraw, then focus content element for better a11y
- $el.focus()
- })
- },
- calc_items_selected: function () {
- this.is_items_selected = [...this.$refs.form.querySelectorAll('input[type=checkbox], input[type=radio]')].some(function (element) {
- return element.checked
- }) || [...this.$refs.form.querySelectorAll('.pretix-widget-item-count-group input')].some(function (element) {
- return parseInt(element.value || '0') > 0
- })
- },
- },
- template: (''
- )
-})
-
-Vue.component('PretixWidgetEventListFilterField', {
- props: {
- field: Object
- },
- computed: {
- id: function () {
- return widget_id + '_' + this.field.key
- },
- currentValue: function () {
- let filterParams = new URLSearchParams(this.$root.filter)
- return filterParams.get(this.field.key) || ''
- },
- },
- template: (''
- + ''
- + ''
- + '
'),
-})
-
-Vue.component('PretixWidgetEventListFilterForm', {
- methods: {
- onSubmit: function (e) {
- e.preventDefault()
- let formData = new FormData(this.$refs.filterform)
- let filterParams = new URLSearchParams(formData)
- formData.forEach(function (value, key) {
- if (value == '') {
- filterParams.delete(key)
- }
- })
-
- this.$root.filter = filterParams.toString()
- this.$root.loading++
- this.$root.reload()
- },
- },
- template: (''),
-})
-
-Vue.component('PretixWidgetEventListEntry', {
- props: {
- event: Object
- },
- computed: {
- classObject: function () {
- let o = {
- 'pretix-widget-event-list-entry': true
- }
- o['pretix-widget-event-availability-' + this.event.availability.color] = true
- if (this.event.availability.reason) {
- o['pretix-widget-event-availability-' + this.event.availability.reason] = true
- }
- return o
- },
- location: function () {
- return this.event.location.replace(/\s*\n\s*/g, ', ')
- }
- },
- methods: {
- select: function () {
- this.$root.parent_stack.push(this.$root.target_url)
- this.$root.target_url = this.event.event_url
- this.$root.error = null
- this.$root.subevent = this.event.subevent
- this.$root.loading++
- this.$root.reload()
- }
- },
- template: (''
- + '{{ event.name }}
'
- + '{{ event.date_range }}
'
- + '{{ location }}
' // hidden by css for now, but
- // used by a few people
- + '{{ event.availability.text }}
'
- + '')
-})
-
-Vue.component('PretixWidgetEventList', {
- computed: {
- display_event_info: function () {
- return this.$root.display_event_info || (this.$root.display_event_info === null && this.$root.parent_stack.length > 0)
- },
- },
- methods: {
- back_to_calendar: function () {
- // make sure to always focus content element
- this.$nextTick(function () {
- this.$root.$el.focus()
- })
- this.$root.offset = 0
- this.$root.append_events = false
- if (this.$root.weeks) {
- this.$root.events = undefined
- this.$root.view = 'weeks'
- this.$root.name = null
- this.$root.frontpage_text = null
- } else {
- this.$root.loading++
- this.$root.target_url = this.$root.parent_stack.pop()
- this.$root.error = null
- this.$root.reload()
- }
- },
- load_more: function () {
- this.$root.append_events = true
- this.$root.offset += 50
- this.$root.loading++
- this.$root.reload()
- }
- },
- template: ('')
-})
-
-Vue.component('PretixWidgetEventCalendarEvent', {
- props: {
- event: Object,
- describedby: String,
- },
- computed: {
- classObject: function () {
- let o = {
- 'pretix-widget-event-calendar-event': true
- }
- o['pretix-widget-event-availability-' + this.event.availability.color] = true
- if (this.event.availability.reason) {
- o['pretix-widget-event-availability-' + this.event.availability.reason] = true
- }
- return o
- }
- },
- methods: {
- select: function () {
- this.$root.parent_stack.push(this.$root.target_url)
- this.$root.target_url = this.event.event_url
- this.$root.error = null
- this.$root.subevent = this.event.subevent
- this.$root.loading++
- this.$root.reload()
- }
- },
- template: (''
- + ''
- + '{{ event.name }}'
- + ''
- + '{{ event.time }}
'
- + '{{ event.availability.text }}
'
- + '')
-})
-
-Vue.component('PretixWidgetEventWeekCell', {
- props: {
- day: Object,
- },
- computed: {
- id: function () {
- return this.day ? this.$root.html_id + '-' + this.day.date : ''
- },
- dayhead: function () {
- if (!this.day) {
- return
- }
- return this.day.day_formatted
- },
- classObject: function () {
- let o = {}
- if (this.day && this.day.events.length > 0) {
- o['pretix-widget-has-events'] = true
- let best = 'red'
- let all_low = true
- for (let i = 0; i < this.day.events.length; i++) {
- let ev = this.day.events[i]
- if (ev.availability.color === 'green') {
- best = 'green'
- if (ev.availability.reason !== 'low') {
- all_low = false
- }
- } else if (ev.availability.color === 'orange' && best !== 'green') {
- best = 'orange'
- }
- }
- o['pretix-widget-day-availability-' + best] = true
- if (best === 'green' && all_low) {
- o['pretix-widget-day-availability-low'] = true
- }
- }
- return o
- }
- },
- methods: {
- selectDay: function () {
- if (!this.day || !this.day.events.length || !this.$parent.$parent.$parent.mobile) {
- return
- }
- if (this.day.events.length === 1) {
- let ev = this.day.events[0]
- this.$root.parent_stack.push(this.$root.target_url)
- this.$root.target_url = ev.event_url
- this.$root.error = null
- this.$root.subevent = ev.subevent
- this.$root.loading++
- this.$root.reload()
- } else {
- this.$root.events = this.day.events
- this.$root.view = 'events'
- }
- }
- },
- template: (''
- + '
'
- + '{{ dayhead }}'
- + '
'
- + '
'
- + '
')
-})
-
-Vue.component('PretixWidgetEventCalendarCell', {
- props: {
- day: Object,
- },
- computed: {
- role: function () {
- return (!this.day || !this.day.events.length || !this.$parent.$parent.$parent.mobile) ? 'cell' : 'button'
- },
- tabindex: function () {
- return this.role == 'button' ? '0' : '-1'
- },
- daynum: function () {
- if (!this.day) {
- return
- }
- return this.day.date.substr(8)
- },
- date: function () {
- return this.day ? (new Date(this.day.date)).toLocaleDateString() : ''
- },
- classObject: function () {
- let o = {}
- if (this.day && this.day.events.length > 0) {
- o['pretix-widget-has-events'] = true
- let best = 'red'
- let all_low = true
- for (let i = 0; i < this.day.events.length; i++) {
- let ev = this.day.events[i]
- if (ev.availability.color === 'green') {
- best = 'green'
- if (ev.availability.reason !== 'low') {
- all_low = false
- }
- } else if (ev.availability.color === 'orange' && best !== 'green') {
- best = 'orange'
- }
- }
- o['pretix-widget-day-availability-' + best] = true
- if (best === 'green' && all_low) {
- o['pretix-widget-day-availability-low'] = true
- }
- }
- return o
- }
- },
- watch: {
- role: function (newValue) {
- if (newValue == 'button') {
- this.$el.addEventListener('click', this.selectDay)
- this.$el.addEventListener('keydown', this.onKeyDown)
- } else {
- this.$el.removeEventListener('click', this.selectDay)
- this.$el.removeEventListener('keydown', this.onKeyDown)
- }
- }
- },
- mounted: function () {
- if (this.role == 'button') {
- this.$el.addEventListener('click', this.selectDay)
- this.$el.addEventListener('keydown', this.onKeyDown)
- }
- },
- methods: {
- selectDay: function (e) {
- if (!this.day || !this.day.events.length || !this.$parent.$parent.$parent.mobile) {
- return
- }
- e.preventDefault()
- e.stopPropagation()
- if (this.day.events.length === 1) {
- let ev = this.day.events[0]
- this.$root.parent_stack.push(this.$root.target_url)
- this.$root.target_url = ev.event_url
- this.$root.error = null
- this.$root.subevent = ev.subevent
- this.$root.loading++
- this.$root.reload()
- } else {
- this.$root.events = this.day.events
- this.$root.view = 'events'
- }
- },
- onKeyDown: function (e) {
- let keyDown = e.key !== undefined ? e.key : e.keyCode
- if ((keyDown === 'Enter' || keyDown === 13) || (['Spacebar', ' '].indexOf(keyDown) >= 0 || keyDown === 32)) {
- // (prevent default so the page doesn't scroll when pressing space)
- e.preventDefault()
- this.selectDay(e)
- }
- },
- },
- template: (''
- + ' '
- + '{{ daynum }}'
- + ' '
- + ''
- + ' | ')
-})
-
-Vue.component('PretixWidgetEventCalendarRow', {
- props: {
- week: Array
- },
- template: (''
- + ''
- + '
'),
-})
-
-Vue.component('PretixWidgetEventCalendar', {
- computed: {
- display_event_info: function () {
- return this.$root.display_event_info || (this.$root.display_event_info === null && this.$root.parent_stack.length > 0)
- },
- monthname: function () {
- return strings['months'][this.$root.date.substr(5, 2)] + ' ' + this.$root.date.substr(0, 4)
- },
- id: function () {
- return this.$root.html_id + '-event-calendar-table'
- },
- aria_labelledby: function () {
- return this.$root.html_id + '-event-calendar-table-label'
- },
- },
- methods: {
- back_to_list: function () {
- this.$root.weeks = undefined
- this.$root.view = 'events'
- this.$root.name = null
- this.$root.frontpage_text = null
- },
- prevmonth: function () {
- let curMonth = parseInt(this.$root.date.substr(5, 2))
- let curYear = parseInt(this.$root.date.substr(0, 4))
- curMonth--
- if (curMonth < 1) {
- curMonth = 12
- curYear--
- }
- this.$root.date = String(curYear) + '-' + padNumber(curMonth, 2) + '-01'
- this.$root.loading++
- this.$root.reload({ focus: '#' + this.id })
- },
- nextmonth: function () {
- let curMonth = parseInt(this.$root.date.substr(5, 2))
- let curYear = parseInt(this.$root.date.substr(0, 4))
- curMonth++
- if (curMonth > 12) {
- curMonth = 1
- curYear++
- }
- this.$root.date = String(curYear) + '-' + padNumber(curMonth, 2) + '-01'
- this.$root.loading++
- this.$root.reload({ focus: '#' + this.id })
- }
- },
- template: (''),
-})
-
-Vue.component('PretixWidgetEventWeekCalendar', {
- computed: {
- display_event_info: function () {
- return this.$root.display_event_info || (this.$root.display_event_info === null && this.$root.parent_stack.length > 0)
- },
- weekname: function () {
- let curWeek = this.$root.week[1]
- let curYear = this.$root.week[0]
- return curWeek + ' / ' + curYear
- },
- id: function () {
- return this.$root.html_id + '-event-week-table'
- },
- },
- methods: {
- back_to_list: function () {
- this.$root.weeks = undefined
- this.$root.name = null
- this.$root.frontpage_text = null
- this.$root.view = 'events'
- },
- prevweek: function () {
- let curWeek = this.$root.week[1]
- let curYear = this.$root.week[0]
- curWeek--
- if (curWeek < 1) {
- curYear--
- curWeek = getISOWeeks(curYear)
- }
- this.$root.week = [curYear, curWeek]
- this.$root.loading++
- this.$root.reload({ focus: '#' + this.id })
- },
- nextweek: function () {
- let curWeek = this.$root.week[1]
- let curYear = this.$root.week[0]
- curWeek++
- if (curWeek > getISOWeeks(curYear)) {
- curWeek = 1
- curYear++
- }
- this.$root.week = [curYear, curWeek]
- this.$root.loading++
- this.$root.reload({ focus: '#' + this.id })
- }
- },
- template: (''
- + ''),
-})
-
-Vue.component('PretixWidget', {
- data: shared_widget_data,
- computed: {
- classObject: function () {
- return {
- 'pretix-widget': true,
- 'pretix-widget-mobile': this.mobile,
- 'pretix-widget-use-custom-spinners': true,
- }
- }
- },
- mounted: function () {
- let thisObj = this
- let resizeObserver = new ResizeObserver(function (entries) {
- thisObj.mobile = entries[0].contentRect.width <= 800
- })
- resizeObserver.observe(this.$refs.wrapper)
- },
- methods: shared_methods,
- template: (''
- + ''
- )
-})
-
-Vue.component('PretixButton', {
- data: shared_widget_data,
- methods: shared_methods,
- template: (''
- + ''
- ),
-})
-
-/* Function to create the actual Vue instances */
-
-let shared_root_methods = {
- open_link_in_frame: function (event) {
- let url = event.target.attributes.href.value
- if (this.$root.additionalURLParams) {
- if (url.indexOf('?')) {
- url += '&' + this.$root.additionalURLParams
- } else {
- url += '?' + this.$root.additionalURLParams
- }
- }
- if (this.$root.useIframe) {
- event.preventDefault()
- if (url.indexOf('?')) {
- url += '&iframe=1'
- } else {
- url += '?iframe=1'
- }
- this.$root.overlay.frame_src = url
- } else {
- event.target.href = url
- return
- }
- },
- trigger_load_callback: function () {
- this.$nextTick(function () {
- for (let i = 0; i < window.PretixWidget._loaded.length; i++) {
- window.PretixWidget._loaded[i]()
- }
- })
- },
- trigger_close_callback: function () {
- this.$nextTick(function () {
- for (let i = 0; i < window.PretixWidget._closed.length; i++) {
- window.PretixWidget._closed[i]()
- }
- })
- },
- reload: function (opt = {}) {
- let url
- if (this.$root.is_button) {
- return
- }
- if (this.$root.subevent) {
- url = this.$root.target_url + this.$root.subevent + '/widget/product_list?lang=' + lang
- } else {
- url = this.$root.target_url + 'widget/product_list?lang=' + lang
- }
- if (this.$root.offset) {
- url += '&offset=' + this.$root.offset
- }
- if (this.$root.filter) {
- url += '&' + this.$root.filter
- }
- if (this.$root.item_filter) {
- url += '&items=' + encodeURIComponent(this.$root.item_filter)
- }
- if (this.$root.category_filter) {
- url += '&categories=' + encodeURIComponent(this.$root.category_filter)
- }
- if (this.$root.variation_filter) {
- url += '&variations=' + encodeURIComponent(this.$root.variation_filter)
- }
- if (this.$root.voucher_code) {
- url += '&voucher=' + encodeURIComponent(this.$root.voucher_code)
- }
- if (this.$root.get_cart_id()) {
- url += '&cart_id=' + encodeURIComponent(this.$root.get_cart_id())
- }
- if (this.$root.date !== null) {
- url += '&date=' + this.$root.date.substr(0, 7)
- } else if (this.$root.week !== null) {
- url += '&date=' + this.$root.week[0] + '-W' + this.$root.week[1]
- }
- if (this.$root.style !== null) {
- url = url + '&style=' + encodeURIComponent(this.$root.style)
- }
- let root = this.$root
- api._getJSON(url, function (data, xhr) {
- if (typeof xhr.responseURL !== 'undefined') {
- let new_url = xhr.responseURL.substr(0, xhr.responseURL.indexOf('/widget/product_list?') + 1)
- let old_url = url.substr(0, url.indexOf('/widget/product_list?') + 1)
- if (new_url !== old_url) {
- if (root.subevent) {
- new_url = new_url.substr(0, new_url.lastIndexOf('/', new_url.length - 1) + 1)
- }
- root.target_url = new_url
- root.reload()
- return
- }
- }
- root.connection_error = false
- if (data.weeks !== undefined) {
- root.weeks = data.weeks
- root.date = data.date
- root.week = null
- root.events = undefined
- root.view = 'weeks'
- root.name = data.name
- root.frontpage_text = data.frontpage_text
- root.meta_filter_fields = data.meta_filter_fields
- } else if (data.days !== undefined) {
- root.days = data.days
- root.date = null
- root.week = data.week
- root.events = undefined
- root.view = 'days'
- root.name = data.name
- root.frontpage_text = data.frontpage_text
- root.meta_filter_fields = data.meta_filter_fields
- } else if (data.events !== undefined) {
- root.events = root.append_events && root.events ? root.events.concat(data.events) : data.events
- root.append_events = false
- root.weeks = undefined
- root.view = 'events'
- root.name = data.name
- root.frontpage_text = data.frontpage_text
- root.has_more_events = data.has_more_events
- root.meta_filter_fields = data.meta_filter_fields
- } else {
- root.view = 'event'
- // Replace target_url and subevent with canonical values in case they were slightly wrong
- root.target_url = data.target_url
- root.subevent = data.subevent
- // Event data
- root.name = data.name
- root.frontpage_text = data.frontpage_text
- root.date_range = data.date_range
- root.location = data.location
- root.categories = data.items_by_category
- root.currency = data.currency
- root.currency_places = data.currency_places
- root.display_net_prices = data.display_net_prices
- root.voucher_explanation_text = data.voucher_explanation_text
- root.error = data.error
- root.display_add_to_cart = data.display_add_to_cart
- root.waiting_list_enabled = data.waiting_list_enabled
- root.show_variations_expanded = data.show_variations_expanded || !!root.variation_filter
- root.cart_exists = data.cart_exists
- root.vouchers_exist = data.vouchers_exist
- root.has_seating_plan = data.has_seating_plan
- root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist
- root.itemnum = data.itemnum
- }
- root.poweredby = data.poweredby
- if (root.loading > 0) {
- root.loading--
- root.trigger_load_callback()
- }
- if (root.parent_stack.length > 0 && root.has_seating_plan && root.categories.length === 0 && !root.frame_dismissed && root.useIframe && !root.error && !root.has_seating_plan_waitinglist) {
- // If we're on desktop and someone selects a seating-only event in a calendar, let's open it right away,
- // but only if the person didn't close it before.
- root.startseating()
- } else {
- // make sure to only move focus to content element when it had focus before the reload/click
- // this is needed because reload is also called on initial load and we do not want to move focus on initial load
- if (root.$el.contains(document.activeElement)) {
- root.$nextTick(function () {
- // wait for redraw, then focus content element for better a11y
- (opt.focus ? document.querySelector(opt.focus) : root.$el).focus()
- })
- }
- }
- }, function (error) {
- root.categories = []
- root.currency = ''
- root.currency_places = 2
- if (error.status === 429) {
- root.error = strings['loading_error_429']
- root.connection_error = true
- } else {
- root.error = strings['loading_error']
- root.connection_error = true
- }
- if (root.loading > 0) {
- root.loading--
- root.trigger_load_callback()
- }
- })
- },
- startwaiting: function () {
- let redirect_url = this.$root.target_url + 'w/' + widget_id + '/waitinglist/?iframe=1&locale=' + lang
- if (this.$root.subevent) {
- redirect_url += '&subevent=' + this.$root.subevent
- }
- if (this.$root.additionalURLParams) {
- redirect_url += '&' + this.$root.additionalURLParams
- }
- if (this.$root.useIframe) {
- this.$root.overlay.frame_src = redirect_url
- } else {
- window.open(redirect_url)
- }
- },
- startseating: function () {
- let redirect_url = this.$root.target_url + 'w/' + widget_id
- if (this.$root.subevent) {
- redirect_url += '/' + this.$root.subevent
- }
- redirect_url += '/seatingframe/?iframe=1&locale=' + lang
- if (this.$root.voucher_code) {
- redirect_url += '&voucher=' + encodeURIComponent(this.$root.voucher_code)
- }
- if (this.$root.get_cart_id()) {
- redirect_url += '&take_cart_id=' + encodeURIComponent(this.$root.get_cart_id())
- }
- if (this.$root.widget_data) {
- redirect_url += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json)
- }
- if (this.$root.additionalURLParams) {
- redirect_url += '&' + this.$root.additionalURLParams
- }
- redirect_url += this.$root.consent_parameter
- if (this.$root.useIframe) {
- this.$root.overlay.frame_src = redirect_url
- } else {
- window.open(redirect_url)
- }
- },
- choose_event: function (event) {
- this.$root.target_url = event.event_url
- this.$root.error = null
- this.$root.connection_error = false
- this.$root.subevent = event.subevent
- this.$root.loading++
- this.$root.reload()
- },
- create_cart: function (callback) {
- let url = this.$root.target_url + 'w/' + widget_id + '/cart/create?ajax=1'
-
- this.$root.overlay.frame_loading = true
- api._getJSON(url, (data) => {
- this.$root.set_cart_id(data.cart_id)
- this.$root.overlay.frame_loading = false
- callback()
- }, (xhr, data) => {
- if (xhr.status === 429 && typeof xhr.responseURL !== 'undefined') {
- this.$root.overlay.error_message = strings['cart_error_429']
- this.$root.overlay.frame_loading = false
- this.$root.overlay.error_url_after = this.$root.newTabTarget
- this.$root.overlay.error_url_after_new_tab = true
- } else {
- this.$root.overlay.error_message = strings['cart_error']
- this.$root.overlay.frame_loading = false
- }
- })
- },
- get_cart_id: function () {
- if (!this.$root.keep_cart) {
- return null
- }
- if (this.$root.cart_id) {
- return this.$root.cart_id
- }
- return getCookie(this.$root.cookieName)
- },
- set_cart_id: function (newValue) {
- this.$root.cart_id = newValue
- setCookie(this.$root.cookieName, newValue, 30)
- },
-}
-
-let shared_root_computed = {
- cookieName: function () {
- return 'pretix_widget_' + this.target_url.replace(/[^a-zA-Z0-9]+/g, '_')
- },
- formTarget: function () {
- let is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
- let is_android = navigator.userAgent.toLowerCase().indexOf('android') > -1
- if (is_android && is_firefox) {
- // Opening a POST form in a new browser fails in Firefox. This is supposed to be fixed since FF 76
- // but for some reason, it is still the case in FF for Android.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1629441
- // https://github.com/pretix/pretix/issues/1040
- return '_top'
- } else {
- return '_blank'
- }
- },
- voucherFormTarget: function () {
- let form_target = this.target_url + 'w/' + widget_id + '/redeem?iframe=1&locale=' + lang
- if (this.get_cart_id()) {
- form_target += '&take_cart_id=' + encodeURIComponent(this.get_cart_id())
- }
- if (this.subevent) {
- form_target += '&subevent=' + this.subevent
- }
- if (this.$root.widget_data) {
- form_target += '&widget_data=' + encodeURIComponent(this.$root.widget_data_json)
- }
- form_target += this.$root.consent_parameter
- if (this.$root.additionalURLParams) {
- form_target += '&' + this.$root.additionalURLParams
- }
- return form_target
- },
- formMethod: function () {
- if (!this.useIframe && this.is_button && this.items.length === 0) {
- return 'get'
- }
- return 'post'
- },
- formAction: function () {
- if (!this.useIframe && this.is_button && this.items.length === 0) {
- let target
- if (this.voucher_code) {
- target = this.target_url + 'redeem'
- } else if (this.subevent) {
- target = this.target_url + this.subevent + '/'
- } else {
- target = this.target_url
- }
- return target
- }
- let checkout_url = '/' + this.target_url.replace(/^[^\/]+:\/\/([^\/]+)\//, '') + 'w/' + widget_id + '/'
- if (!this.$root.cart_exists) {
- checkout_url += 'checkout/start'
- }
- if (this.$root.additionalURLParams) {
- checkout_url += '?' + this.$root.additionalURLParams
- }
- let form_target = this.target_url + 'w/' + widget_id + '/cart/add?iframe=1&next=' + encodeURIComponent(checkout_url)
- if (this.get_cart_id()) {
- form_target += '&take_cart_id=' + encodeURIComponent(this.get_cart_id())
- }
- form_target += this.$root.consent_parameter
- return form_target
- },
- newTabTarget: function () {
- let target = this.target_url
- if (this.subevent) {
- target = this.target_url + this.subevent + '/'
- }
- let parameters = this.$root.consent_parameter
- if (this.$root.additionalURLParams) {
- parameters += `&${this.$root.additionalURLParams}`
- }
- if (parameters) {
- target += '?' + parameters.replace(/^&/, '')
- }
- return target
- },
- useIframe: function () {
- if (window.crossOriginIsolated === true) {
- console.warn('pretix Widget cannot use iframe due to Cross-Origin-Embed-Policy')
- return false
- }
- return !this.disable_iframe && (this.skip_ssl || site_is_secure())
- },
- showPrices: function () {
- let has_priced = false
- let cnt_items = 0
- for (let i = 0; i < this.categories.length; i++) {
- for (let j = 0; j < this.categories[i].items.length; j++) {
- let item = this.categories[i].items[j]
- if (item.has_variations) {
- cnt_items += item.variations.length
- has_priced = true
- } else {
- cnt_items++
- has_priced = has_priced || item.price.gross != '0.00' || item.free_price
- }
- }
- }
- return has_priced || cnt_items > 1
- },
- consent_parameter_value: function () {
- if (typeof this.widget_data['consent'] !== 'undefined') {
- return encodeURIComponent(this.widget_data['consent'])
- }
- return ''
- },
- consent_parameter: function () {
- if (typeof this.widget_data['consent'] !== 'undefined') {
- return '&consent=' + encodeURIComponent(this.widget_data['consent'])
- }
- return ''
- },
- widget_data_json: function () {
- let cloned_data = Object.assign({}, this.widget_data)
- if (typeof cloned_data['consent'] !== 'undefined') {
- // Remove consent as we pass it differently. We still keep it as widget_data in the input to avoid breaking
- // the JS API of the widget.
- delete cloned_data['consent']
- }
- return JSON.stringify(cloned_data)
- },
- additionalURLParams: function () {
- if (!window.location.search.indexOf('utm_')) {
- return ''
- }
- let params = new URLSearchParams(window.location.search)
- for (let [key, value] of params.entries()) {
- if (!key.startsWith('utm_')) {
- params.delete(key)
- }
- }
- return params.toString()
- },
-}
-
-let create_overlay = function (app) {
- let elem = document.createElement('pretix-overlay')
- document.body.appendChild(elem)
-
- let framechild = new Vue({
- el: elem,
- props: {
- frame_src: String,
- },
- data: function () {
- return {
- parent: app,
- frame_loading: false,
- frame_shown: false,
- error_url_after: null,
- error_url_after_new_tab: true,
- error_message: null,
- lightbox: null,
- prevActiveElement: null,
- }
- },
- watch: {
- frame_src: function (newValue, oldValue) {
- // show loading spinner only when previously no frame_src was set
- if (newValue && !oldValue) {
- this.frame_loading = true
- }
- // to close and unload the iframe, frame_src can be empty -> make it valid HTML with about:blank
- this.$el.querySelector('iframe').src = newValue || 'about:blank'
- },
- frame_loading: function (newValue) {
- let dialog = this.$el?.querySelector('dialog.pretix-widget-frame-holder')
- if (newValue) {
- if (!dialog.open) {
- dialog.showModal()
- }
- } else {
- if (!this.frame_src && dialog.open) { // finished loading, but no iframe to display => close
- dialog.close()
- }
- }
- },
- },
- methods: {
- }
- })
- app.$root.overlay = framechild
-}
-
-function get_ga_client_id (tracking_id) {
- if (typeof ga === 'undefined') {
- return null
- }
- try {
- let trackers = ga.getAll()
- let i, len
- for (i = 0, len = trackers.length; i < len; i += 1) {
- if (trackers[i].get('trackingId') === tracking_id) {
- return trackers[i].get('clientId')
- }
- }
- } catch (e) {
- }
- return null
-}
-
-let create_widget = function (element, html_id = null) {
- let target_url = element.attributes.event.value
- if (!target_url.match(/\/$/)) {
- target_url += '/'
- }
- let voucher = element.attributes.voucher ? element.attributes.voucher.value : null
- let subevent = element.attributes.subevent ? element.attributes.subevent.value : null
- let style = element.attributes['list-type'] ? element.attributes['list-type'].value : (element.attributes.style ? element.attributes.style.value : null)
- let skip_ssl = element.attributes['skip-ssl-check'] ? true : false
- let disable_iframe = element.attributes['disable-iframe'] ? true : false
- let disable_vouchers = element.attributes['disable-vouchers'] ? true : false
- let disable_filters = element.attributes['disable-filters'] ? true : false
- let display_event_info = element.getAttribute('display-event-info') // null means "auto" (as before), everything other than "false" is true
- if (display_event_info !== null && display_event_info !== 'auto') {
- display_event_info = display_event_info !== 'false'
- } else {
- display_event_info = null
- }
- let widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data))
- let filter = element.attributes.filter ? element.attributes.filter.value : null
- let items = element.attributes.items ? element.attributes.items.value : null
- let variations = element.attributes.variations ? element.attributes.variations.value : null
- let categories = element.attributes.categories ? element.attributes.categories.value : null
- for (let i = 0; i < element.attributes.length; i++) {
- let attrib = element.attributes[i]
- if (attrib.name.match(/^data-.*$/)) {
- widget_data[attrib.name.replace(/^data-/, '')] = attrib.value
- }
- }
- html_id = html_id || element.id || makeid(16)
-
- let observer = new MutationObserver((mutationList) => {
- mutationList.forEach((mutation) => {
- if (mutation.type == 'attributes' && mutation.attributeName.startsWith('data-')) {
- Vue.set(app.widget_data, mutation.attributeName.substring(5), mutation.target.getAttribute(mutation.attributeName))
- }
- })
- })
- let observerOptions = { attributes: true }
-
- if (element.tagName !== 'pretix-widget') {
- element.innerHTML = ''
- // we need to watch the container as well as the replaced root-node (see mounted())
- observer.observe(element, observerOptions)
- }
-
- var app = new Vue({
- el: element,
- data: function () {
- return {
- target_url: target_url,
- parent_stack: [],
- subevent: subevent,
- is_button: false,
- categories: null,
- currency: null,
- currency_places: 2,
- name: null,
- date_range: null,
- location: null,
- offset: 0,
- has_more_events: false,
- append_events: false,
- frontpage_text: null,
- filter: filter,
- item_filter: items,
- category_filter: categories,
- variation_filter: variations,
- voucher_code: voucher,
- display_net_prices: false,
- voucher_explanation_text: null,
- show_variations_expanded: !!variations,
- skip_ssl: skip_ssl,
- disable_iframe: disable_iframe,
- style: style,
- connection_error: false,
- error: null,
- weeks: null,
- days: null,
- date: null,
- week: null,
- frame_dismissed: false,
- events: null,
- view: null,
- display_add_to_cart: false,
- widget_data: widget_data,
- loading: 1,
- widget_id: 'pretix-widget-' + widget_id,
- html_id: html_id,
- vouchers_exist: false,
- disable_vouchers: disable_vouchers,
- disable_filters: disable_filters,
- display_event_info: display_event_info,
- cart_exists: false,
- itemcount: 0,
- overlay: null,
- poweredby: '',
- has_seating_plan: false,
- has_seating_plan_waitinglist: false,
- meta_filter_fields: [],
- keep_cart: true,
- cart_id: null
- }
- },
- computed: shared_root_computed,
- watch: {
- view: function (newValue, oldValue) {
- if (oldValue) {
- // always make sure the widget is scrolled to the top
- // as we only check top, we do not need to wait for a redraw
- let rect = this.$el.getBoundingClientRect()
- if (rect.top < 0) {
- this.$el.scrollIntoView()
- }
- }
- }
- },
- created: function () {
- this.reload()
- },
- mounted: function () {
- observer.observe(this.$el, observerOptions)
- },
- methods: shared_root_methods
- })
- create_overlay(app)
- return app
-}
-
-let create_button = function (element, html_id = null) {
- let target_url = element.attributes.event.value
- if (!target_url.match(/\/$/)) {
- target_url += '/'
- }
- let voucher = element.attributes.voucher ? element.attributes.voucher.value : null
- let subevent = element.attributes.subevent ? element.attributes.subevent.value : null
- let raw_items = element.attributes.items ? element.attributes.items.value : ''
- let skip_ssl = element.attributes['skip-ssl-check'] ? true : false
- let disable_iframe = element.attributes['disable-iframe'] ? true : false
- let keep_cart = element.attributes['keep-cart'] ? true : false
- let button_text = element.innerHTML
- let widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data))
- for (var i = 0; i < element.attributes.length; i++) {
- let attrib = element.attributes[i]
- if (attrib.name.match(/^data-.*$/)) {
- widget_data[attrib.name.replace(/^data-/, '')] = attrib.value
- }
- }
- html_id = html_id || element.id || makeid(16)
-
- let observer = new MutationObserver((mutationList) => {
- mutationList.forEach((mutation) => {
- if (mutation.type == 'attributes' && mutation.attributeName.startsWith('data-')) {
- Vue.set(app.widget_data, mutation.attributeName.substring(5), mutation.target.getAttribute(mutation.attributeName))
- }
- })
- })
- let observerOptions = { attributes: true }
-
- if (element.tagName !== 'pretix-button') {
- element.innerHTML = '' + element.innerHTML + ''
- // Vue does not replace the container, so watch container as well
- observer.observe(element, observerOptions)
- }
-
- let itemsplit = raw_items.split(',')
- let items = []
- for (var i = 0; i < itemsplit.length; i++) {
- if (itemsplit[i].indexOf('=') > 0) {
- let splitthis = itemsplit[i].split('=')
- items.push({ item: splitthis[0], count: splitthis[1] })
- }
- }
-
- var app = new Vue({
- el: element,
- data: function () {
- return {
- target_url: target_url,
- subevent: subevent,
- is_button: true,
- skip_ssl: skip_ssl,
- disable_iframe: disable_iframe,
- voucher_code: voucher,
- items: items,
- error: null,
- filter: null,
- frame_dismissed: false,
- widget_data: widget_data,
- widget_id: 'pretix-widget-' + widget_id,
- html_id: html_id,
- button_text: button_text,
- keep_cart: keep_cart || items.length > 0,
- cart_id: null
- }
- },
- computed: shared_root_computed,
- created: function () {
- },
- mounted: function () {
- observer.observe(this.$el, observerOptions)
- },
- methods: shared_root_methods,
- })
- create_overlay(app)
- return app
-}
-
-/* Find all widgets on the page and render them */
-widgetlist = []
-buttonlist = []
-window.PretixWidget._loaded = []
-window.PretixWidget._closed = []
-window.PretixWidget.addLoadListener = function (f) {
- window.PretixWidget._loaded.push(f)
-}
-window.PretixWidget.addCloseListener = function (f) {
- window.PretixWidget._closed.push(f)
-}
-window.PretixWidget.buildWidgets = function () {
- document.createElement('pretix-widget')
- document.createElement('pretix-button')
- docReady(function () {
- let widgets = document.querySelectorAll('pretix-widget, div.pretix-widget-compat')
- let wlength = widgets.length
- for (var i = 0; i < wlength; i++) {
- let widget = widgets[i]
- widgetlist.push(create_widget(widget, widget.id || 'pretix-widget-' + i))
- }
-
- let buttons = document.querySelectorAll('pretix-button, div.pretix-button-compat')
- let blength = buttons.length
- for (var i = 0; i < blength; i++) {
- let button = buttons[i]
- buttonlist.push(create_button(button, button.id || 'pretix-button-' + i))
- }
- })
-}
-
-window.PretixWidget.open = function (target_url, voucher, subevent, items, widget_data, skip_ssl_check, disable_iframe) {
- if (!target_url.match(/\/$/)) {
- target_url += '/'
- }
-
- let all_widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data))
- if (widget_data) {
- Object.keys(widget_data).forEach(function (key) { all_widget_data[key] = widget_data[key] })
- }
- let root = document.createElement('div')
- document.body.appendChild(root)
- root.classList.add('pretix-widget-hidden')
- root.innerHTML = ''
- let app = new Vue({
- el: root,
- data: function () {
- return {
- target_url: target_url,
- subevent: subevent || null,
- is_button: true,
- skip_ssl: skip_ssl_check || false,
- disable_iframe: disable_iframe || false,
- voucher_code: voucher || null,
- items: items || [],
- error: null,
- filter: null,
- frame_dismissed: false,
- widget_data: all_widget_data,
- widget_id: 'pretix-widget-' + widget_id,
- button_text: '',
- keep_cart: true,
- cart_id: null
- }
- },
- computed: shared_root_computed,
- created: function () {
- },
- methods: shared_root_methods,
- })
- create_overlay(app)
- app.$nextTick(function () {
- if (this.$root.useIframe) {
- this.$refs.btn.buy()
- } else {
- this.$refs.btn.$refs.form.submit()
- }
- })
-}
-
-if (typeof window.pretixWidgetCallback !== 'undefined') {
- window.pretixWidgetCallback()
-}
-if (window.PretixWidget.build_widgets) {
- window.PretixWidget.buildWidgets()
-}
-
-/* Set a global variable for debugging. In DEBUG mode, siteglobals will be window, otherwise it will be something
- unnamed. */
-siteglobals.pretixwidget_debug = {
- Vue: Vue,
- widgets: widgetlist,
- buttons: buttonlist
-}
diff --git a/src/tests/e2e/conftest.py b/src/tests/e2e/conftest.py
index 7c223083a1..f0308e838a 100644
--- a/src/tests/e2e/conftest.py
+++ b/src/tests/e2e/conftest.py
@@ -89,8 +89,6 @@ def _widget_assets():
f'Start it with: npm run dev:widget'
)
yield
- elif os.environ.get("PRETIX_WIDGET_VUE"):
- yield # Old widget, no build needed
else:
subprocess.check_call(['npm', 'run', 'build:widget'], cwd=PROJECT_ROOT)
yield