ESlint: Ignore vendored and pre-vue code (#6541)

* ESlint: Ignore vendored and pre-vue code

* Format pre-vue code with eslint where possible

---------

Co-authored-by: Kara Engelhardt <engelhardt@pretix.eu>
This commit is contained in:
Raphael Michel
2026-09-10 12:35:10 +02:00
committed by GitHub
co-authored by Kara Engelhardt
parent d9b5fa5b16
commit d85e52c83e
55 changed files with 10025 additions and 10014 deletions
+58 -1
View File
@@ -8,7 +8,64 @@ import vuePug from 'eslint-plugin-vue-pug'
const ignores = globalIgnores([ const ignores = globalIgnores([
'**/node_modules', '**/node_modules',
'**/dist' '**/dist',
// Vendored code
'src/pretix/static/leaflet',
'src/pretix/static/clipboard',
'src/pretix/static/cropper',
'src/pretix/static/lightbox',
'src/pretix/static/are-you-sure',
'src/pretix/static/vuejs',
'src/pretix/static/fontawesome',
'src/pretix/static/typeahead',
'src/pretix/static/moment',
'src/pretix/static/pdfjs',
'src/pretix/static/sortable',
'src/pretix/static/iframeresizer',
'src/pretix/static/bootstrap',
'src/pretix/static/d3',
'src/pretix/static/jsi18n',
'src/pretix/static/fabric',
'src/pretix/static/datetimepicker',
'src/pretix/static/charts',
'src/pretix/static/fileupload',
'src/pretix/static/seating',
'src/pretix/static/rest_framework',
'src/pretix/static/select2',
'src/pretix/static/schema',
'src/pretix/static/slider',
'src/pretix/static/jquery',
'src/pretix/static/colorpicker',
'src/pretix/static/rrule',
'src/pretix/static/pretixcontrol/js/jquery.qrcode.min.js',
'src/pretix/static/pretixpresale/js/widget/docready.js',
// Pre-vue JS code
'src/pretix/static/pretixbase/js/addressform.js',
'src/pretix/static/pretixbase/js/asynctask.js',
'src/pretix/static/pretixbase/js/details.js',
'src/pretix/static/pretixbase/js/gettextstub.js',
'src/pretix/static/pretixbase/js/i18nstring.js',
'src/pretix/static/pretixcontrol/js/menu.js',
'src/pretix/static/pretixcontrol/js/ui/editor.js',
'src/pretix/static/pretixcontrol/js/ui/geo.js',
'src/pretix/static/pretixcontrol/js/ui/main.js',
'src/pretix/static/pretixcontrol/js/ui/plugins.js',
'src/pretix/static/pretixcontrol/js/ui/subevent.js',
'src/pretix/static/pretixcontrol/js/ui/variations.js',
'src/pretix/static/pretixcontrol/js/ui/webauthn.js',
'src/pretix/static/pretixpresale/js/ui/cart.js',
'src/pretix/static/pretixpresale/js/ui/main.js',
'src/pretix/static/pretixpresale/js/ui/questions.js',
'src/pretix/static/pretixpresale/js/widget/floatformat.js',
'src/pretix/static/pretixpresale/js/widget/widget.js',
'src/pretix/plugins/banktransfer/static',
'src/pretix/plugins/paypal2/static',
'src/pretix/plugins/statistics/static',
'src/pretix/plugins/stripe/static',
// Plugin checkouts
'local',
// docs
'doc',
]) ])
export default defineConfig([ export default defineConfig([
@@ -1,183 +1,183 @@
/*global $, gettext*/ /* global gettext */
var bankimport_transactionlist = { var bankimport_transactionlist = {
_btn_click: function (e) { _btn_click: function (e) {
console.log(e.delegateTarget); console.log(e.delegateTarget)
var trans_id = parseInt($(e.delegateTarget).attr("name").split("_")[1]); let trans_id = parseInt($(e.delegateTarget).attr('name').split('_')[1])
var value = $(e.delegateTarget).val(); let value = $(e.delegateTarget).val()
if (value === "discard") { if (value === 'discard') {
bankimport_transactionlist.discard(trans_id); bankimport_transactionlist.discard(trans_id)
} else if (value === "accept") { } else if (value === 'accept') {
bankimport_transactionlist.accept(trans_id); bankimport_transactionlist.accept(trans_id)
} else if (value === "retry") { } else if (value === 'retry') {
bankimport_transactionlist.retry(trans_id); bankimport_transactionlist.retry(trans_id)
} else if (value === "assign") { } else if (value === 'assign') {
bankimport_transactionlist.assign(trans_id); bankimport_transactionlist.assign(trans_id)
} }
return false; return false
}, },
_action: function (id, action, success) { _action: function (id, action, success) {
$("tr[data-id=" + id + "] button").prop("disabled", true); $('tr[data-id=' + id + '] button').prop('disabled', true)
var data = { let data = {
"csrfmiddlewaretoken": $("[name=csrfmiddlewaretoken]").val() csrfmiddlewaretoken: $('[name=csrfmiddlewaretoken]').val()
}; }
data["action_" + id] = action; data['action_' + id] = action
$.ajax({ $.ajax({
"method": "POST", method: 'POST',
"url": $(".transaction-list").attr("data-url"), url: $('.transaction-list').attr('data-url'),
"data": data, data: data,
"dataType": "json", dataType: 'json',
"success": function (data) { success: function (data) {
if (data.status == "ok") { if (data.status == 'ok') {
$("tr[data-id=" + id + "]").removeClass("has-error"); $('tr[data-id=' + id + ']').removeClass('has-error')
if (data.comment) { if (data.comment) {
bankimport_transactionlist.comment_reset_to_text(id, data.comment, data.plain); bankimport_transactionlist.comment_reset_to_text(id, data.comment, data.plain)
} }
success(); success()
} else { } else {
$("tr[data-id=" + id + "] button").prop("disabled", false); $('tr[data-id=' + id + '] button').prop('disabled', false)
$("tr[data-id=" + id + "] .help-block").remove(); $('tr[data-id=' + id + '] .help-block').remove()
$("tr[data-id=" + id + "]").addClass("has-error"); $('tr[data-id=' + id + ']').addClass('has-error')
$("<p>").addClass("help-block").text(data.message).appendTo($("tr[data-id=" + id + "] td.actions")); $('<p>').addClass('help-block').text(data.message).appendTo($('tr[data-id=' + id + '] td.actions'))
} }
} }
}); })
}, },
discard: function (id) { discard: function (id) {
bankimport_transactionlist._action(id, "discard", function () { bankimport_transactionlist._action(id, 'discard', function () {
$("tr[data-id=" + id + "] td").remove(); $('tr[data-id=' + id + '] td').remove()
}); })
}, },
retry: function (id) { retry: function (id) {
bankimport_transactionlist._action(id, "retry", function () { bankimport_transactionlist._action(id, 'retry', function () {
$("tr[data-id=" + id + "] td.actions").html('').text(gettext("Marked as paid")); $('tr[data-id=' + id + '] td.actions').html('').text(gettext('Marked as paid'))
}); })
}, },
accept: function (id) { accept: function (id) {
bankimport_transactionlist._action(id, "accept", function () { bankimport_transactionlist._action(id, 'accept', function () {
$("tr[data-id=" + id + "] td.actions").html('').text(gettext("Marked as paid")); $('tr[data-id=' + id + '] td.actions').html('').text(gettext('Marked as paid'))
}); })
}, },
assign: function (id) { assign: function (id) {
bankimport_transactionlist._action(id, "assign:" + $("tr[data-id=" + id + "] input.form-control:not(.tt-hint)").val(), function () { bankimport_transactionlist._action(id, 'assign:' + $('tr[data-id=' + id + '] input.form-control:not(.tt-hint)').val(), function () {
$("tr[data-id=" + id + "] td.actions").html('').text(gettext("Marked as paid")); $('tr[data-id=' + id + '] td.actions').html('').text(gettext('Marked as paid'))
}); })
}, },
comment_reset_to_text: function (id, text, plain) { comment_reset_to_text: function (id, text, plain) {
var $box = $("tr[data-id=" + id + "] .comment-box"); let $box = $('tr[data-id=' + id + '] .comment-box')
$box[0].dataset["plain"] = plain; $box[0].dataset['plain'] = plain
$box.html("") $box.html('')
.append($("<strong>").text(gettext("Comment:"))) .append($('<strong>').text(gettext('Comment:')))
.append(" ") .append(' ')
.append($("<span>").addClass("comment").append(" ").append(text)) .append($('<span>').addClass('comment').append(' ').append(text))
.append(" ") .append(' ')
.append($("<a>").addClass("comment-modify btn btn-default btn-xs") .append($('<a>').addClass('comment-modify btn btn-default btn-xs')
.append("<span class='fa fa-edit'></span>")); .append('<span class=\'fa fa-edit\'></span>'))
}, },
comment_start_edit: function (e) { comment_start_edit: function (e) {
var $box = $(e.target).closest("div"); let $box = $(e.target).closest('div')
var id = $box.closest("tr").attr("data-id"); let id = $box.closest('tr').attr('data-id')
var $inp = $("<textarea>").addClass("form-control"); let $inp = $('<textarea>').addClass('form-control')
var orig_rendered = $box.find(".comment"); let orig_rendered = $box.find('.comment')
var orig_text = $box[0].dataset.plain; let orig_text = $box[0].dataset.plain
$inp.val(orig_text); $inp.val(orig_text)
var $btngrp = $("<div>"); let $btngrp = $('<div>')
$btngrp.addClass("btn-group"); $btngrp.addClass('btn-group')
var $btn1 = $("<button>"); let $btn1 = $('<button>')
$btn1.attr("type", "button").addClass("btn btn-default"); $btn1.attr('type', 'button').addClass('btn btn-default')
$btn1.append("<span class='fa fa-check'></span>"); $btn1.append('<span class=\'fa fa-check\'></span>')
$btngrp.append($btn1); $btngrp.append($btn1)
var $btn2 = $("<button>"); let $btn2 = $('<button>')
$btn2.attr("type", "button").addClass("btn btn-default"); $btn2.attr('type', 'button').addClass('btn btn-default')
$btn2.append("<span class='fa fa-close'></span>"); $btn2.append('<span class=\'fa fa-close\'></span>')
$btngrp.append($btn2); $btngrp.append($btn2)
$box.html("").append($inp).append($btngrp); $box.html('').append($inp).append($btngrp)
$btn1.click(function () { $btn1.click(function () {
var text = $box.find("textarea").val(); let text = $box.find('textarea').val()
$box.find("input, textarea, button").prop("disabled", true); $box.find('input, textarea, button').prop('disabled', true)
bankimport_transactionlist._action(id, "comment:" + text, function () { bankimport_transactionlist._action(id, 'comment:' + text, function () {
$("tr[data-id=" + id + "] button").prop("disabled", false); $('tr[data-id=' + id + '] button').prop('disabled', false)
}); })
}); })
$btn2.click(function () { $btn2.click(function () {
bankimport_transactionlist.comment_reset_to_text(id, orig_rendered, orig_text); bankimport_transactionlist.comment_reset_to_text(id, orig_rendered, orig_text)
}); })
e.preventDefault(); e.preventDefault()
}, },
typeahead_source: function () { typeahead_source: function () {
return new Bloodhound({ return new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: { remote: {
url: $(".transaction-list").attr("data-url"), url: $('.transaction-list').attr('data-url'),
prepare: function (query, settings) { prepare: function (query, settings) {
settings.url = settings.url + '?query=' + encodeURIComponent(query); settings.url = settings.url + '?query=' + encodeURIComponent(query)
return settings; return settings
}, },
transform: function (object) { transform: function (object) {
var results = object.results; let results = object.results
var suggs = []; let suggs = []
var reslen = results.length; let reslen = results.length
for (var i = 0; i < reslen; i++) { for (let i = 0; i < reslen; i++) {
suggs.push(results[i]); suggs.push(results[i])
} }
return suggs; return suggs
} }
} }
}); })
}, },
init: function () { init: function () {
if ($(".transaction-list").length) { if ($('.transaction-list').length) {
$(".transaction-list button").click(bankimport_transactionlist._btn_click); $('.transaction-list button').click(bankimport_transactionlist._btn_click)
$(".transaction-list").on("click", ".comment-modify", bankimport_transactionlist.comment_start_edit); $('.transaction-list').on('click', '.comment-modify', bankimport_transactionlist.comment_start_edit)
$(".transaction-list .form-control").typeahead(null, { $('.transaction-list .form-control').typeahead(null, {
minLength: 2, minLength: 2,
name: 'order-dataset', name: 'order-dataset',
source: bankimport_transactionlist.typeahead_source(), source: bankimport_transactionlist.typeahead_source(),
display: function (obj) { display: function (obj) {
return obj.code; return obj.code
}, },
templates: { templates: {
suggestion: function (obj) { suggestion: function (obj) {
return '<div>' + obj.code + ' (' + obj.total + ', ' + obj.status + ')</div>'; return '<div>' + obj.code + ' (' + obj.total + ', ' + obj.status + ')</div>'
} }
} }
}).keypress(function (e) { }).keypress(function (e) {
if (e.keyCode === 13) { if (e.keyCode === 13) {
$(this).parent().parent().find("button[value=assign]").click(); $(this).parent().parent().find('button[value=assign]').click()
} }
}); })
} }
if ($("[data-job-waiting]").length) { if ($('[data-job-waiting]').length) {
window.setTimeout(bankimport_transactionlist.check_state, 750); window.setTimeout(bankimport_transactionlist.check_state, 750)
} }
}, },
check_state: function () { check_state: function () {
$.getJSON($("[data-job-waiting-url]").attr("data-job-waiting-url"), function (data) { $.getJSON($('[data-job-waiting-url]').attr('data-job-waiting-url'), function (data) {
if (data.state == 'running' || data.state == 'pending') { if (data.state == 'running' || data.state == 'pending') {
window.setTimeout(bankimport_transactionlist.check_state, 750); window.setTimeout(bankimport_transactionlist.check_state, 750)
} else { } else {
location.reload(); location.reload()
} }
}); })
} }
}; }
$(function () { $(function () {
bankimport_transactionlist.init(); bankimport_transactionlist.init()
}); })
@@ -1,350 +1,350 @@
/*global $, paypal_client_id, paypal_loadingmessage, gettext */ /* global paypal_client_id, paypal_loadingmessage, gettext */
'use strict'; 'use strict'
var pretixpaypal = { var pretixpaypal = {
paypal: null, paypal: null,
client_id: null, client_id: null,
order_id: null, order_id: null,
payer_id: null, payer_id: null,
merchant_id: null, merchant_id: null,
currency: null, currency: null,
method: null, method: null,
additional_disabled_funding: null, additional_disabled_funding: null,
additional_enabled_funding: null, additional_enabled_funding: null,
debug_buyer_country: null, debug_buyer_country: null,
continue_button: null, continue_button: null,
paypage: false, paypage: false,
method_map: { method_map: {
wallet: { wallet: {
method: 'wallet', method: 'wallet',
funding_source: 'paypal', funding_source: 'paypal',
//disable_funding: null, // disable_funding: null,
//enable_funding: 'paylater', // enable_funding: 'paylater',
early_auth: true, early_auth: true,
}, },
apm: { apm: {
method: 'apm', method: 'apm',
funding_source: null, funding_source: null,
//disable_funding: null, // disable_funding: null,
//enable_funding: null, // enable_funding: null,
early_auth: false, early_auth: false,
} }
}, },
apm_map: { apm_map: {
paypal: gettext('PayPal'), paypal: gettext('PayPal'),
venmo: gettext('Venmo'), venmo: gettext('Venmo'),
applepay: gettext('Apple Pay'), applepay: gettext('Apple Pay'),
itau: gettext('Itaú'), itau: gettext('Itaú'),
credit: gettext('PayPal Credit'), credit: gettext('PayPal Credit'),
card: gettext('Credit Card'), card: gettext('Credit Card'),
paylater: gettext('PayPal Pay Later'), paylater: gettext('PayPal Pay Later'),
ideal: gettext('iDEAL | Wero'), ideal: gettext('iDEAL | Wero'),
sepa: gettext('SEPA Direct Debit'), sepa: gettext('SEPA Direct Debit'),
bancontact: gettext('Bancontact'), bancontact: gettext('Bancontact'),
giropay: gettext('giropay'), giropay: gettext('giropay'),
sofort: gettext('SOFORT'), sofort: gettext('SOFORT'),
eps: gettext('eps'), eps: gettext('eps'),
mybank: gettext('MyBank'), mybank: gettext('MyBank'),
p24: gettext('Przelewy24'), p24: gettext('Przelewy24'),
verkkopankki: gettext('Verkkopankki'), verkkopankki: gettext('Verkkopankki'),
payu: gettext('PayU'), payu: gettext('PayU'),
blik: gettext('BLIK'), blik: gettext('BLIK'),
trustly: gettext('Trustly'), trustly: gettext('Trustly'),
zimpler: gettext('Zimpler'), zimpler: gettext('Zimpler'),
maxima: gettext('Maxima'), maxima: gettext('Maxima'),
oxxo: gettext('OXXO'), oxxo: gettext('OXXO'),
boleto: gettext('Boleto'), boleto: gettext('Boleto'),
wechatpay: gettext('WeChat Pay'), wechatpay: gettext('WeChat Pay'),
mercadopago: gettext('Mercado Pago') mercadopago: gettext('Mercado Pago')
}, },
readyToSubmitApproval: false, readyToSubmitApproval: false,
load: function () { load: function () {
if (pretixpaypal.paypal === null) { if (pretixpaypal.paypal === null) {
pretixpaypal.client_id = $.trim($("#paypal_client_id").html()); pretixpaypal.client_id = $.trim($('#paypal_client_id').html())
pretixpaypal.merchant_id = $.trim($("#paypal_merchant_id").html()); pretixpaypal.merchant_id = $.trim($('#paypal_merchant_id').html())
pretixpaypal.debug_buyer_country = $.trim($("#paypal_buyer_country").html()); pretixpaypal.debug_buyer_country = $.trim($('#paypal_buyer_country').html())
pretixpaypal.continue_button = $('.checkout-button-row').closest("form").find(".checkout-button-row .btn-primary"); pretixpaypal.continue_button = $('.checkout-button-row').closest('form').find('.checkout-button-row .btn-primary')
pretixpaypal.continue_button.closest('div').append('<div id="paypal-button-container"></div>'); pretixpaypal.continue_button.closest('div').append('<div id="paypal-button-container"></div>')
pretixpaypal.additional_disabled_funding = $.trim($("#paypal_disable_funding").html()); pretixpaypal.additional_disabled_funding = $.trim($('#paypal_disable_funding').html())
pretixpaypal.additional_enabled_funding = $.trim($("#paypal_enable_funding").html()); pretixpaypal.additional_enabled_funding = $.trim($('#paypal_enable_funding').html())
pretixpaypal.paypage = Boolean($('#paypal-button-container').data('paypage')); pretixpaypal.paypage = Boolean($('#paypal-button-container').data('paypage'))
pretixpaypal.order_id = $.trim($("#paypal_oid").html()); pretixpaypal.order_id = $.trim($('#paypal_oid').html())
pretixpaypal.currency = $("body").attr("data-currency"); pretixpaypal.currency = $('body').attr('data-currency')
pretixpaypal.locale = this.guessLocale(); pretixpaypal.locale = this.guessLocale()
} }
$("input[name=payment][value^='paypal']").change(function () { $('input[name=payment][value^=\'paypal\']').change(function () {
if (pretixpaypal.paypal !== null) { if (pretixpaypal.paypal !== null) {
pretixpaypal.renderButton($(this).val()); pretixpaypal.renderButton($(this).val())
} else { } else {
pretixpaypal.continue_button.prop("disabled", true); pretixpaypal.continue_button.prop('disabled', true)
} }
}); })
$("input[name=payment]").not("[value^='paypal']").change(function () { $('input[name=payment]').not('[value^=\'paypal\']').change(function () {
pretixpaypal.restore(); pretixpaypal.restore()
}); })
// If paypal is pre-selected, we must disable the continue button and handle it after SDK is loaded // If paypal is pre-selected, we must disable the continue button and handle it after SDK is loaded
if ($("input[name=payment][value^='paypal']").is(':checked')) { if ($('input[name=payment][value^=\'paypal\']').is(':checked')) {
pretixpaypal.continue_button.prop("disabled", true); pretixpaypal.continue_button.prop('disabled', true)
} }
// We are setting the cogwheel already here, as the renderAPM() method might take some time to get loaded. // We are setting the cogwheel already here, as the renderAPM() method might take some time to get loaded.
const apmtextselector = $("input[name=payment][value=paypal_apm]").closest("label").find(".accordion-label-text"); const apmtextselector = $('input[name=payment][value=paypal_apm]').closest('label').find('.accordion-label-text')
apmtextselector.append(' <span aria-hidden="true" class="fa fa-cog fa-spin"></span>'); apmtextselector.append(' <span aria-hidden="true" class="fa fa-cog fa-spin"></span>')
let sdk_url = 'https://www.paypal.com/sdk/js' + let sdk_url = 'https://www.paypal.com/sdk/js'
'?client-id=' + pretixpaypal.client_id + + '?client-id=' + pretixpaypal.client_id
'&components=buttons,funding-eligibility' + + '&components=buttons,funding-eligibility'
'&currency=' + pretixpaypal.currency; + '&currency=' + pretixpaypal.currency
if (pretixpaypal.locale) { if (pretixpaypal.locale) {
sdk_url += '&locale=' + pretixpaypal.locale; sdk_url += '&locale=' + pretixpaypal.locale
} }
if (pretixpaypal.merchant_id) { if (pretixpaypal.merchant_id) {
sdk_url += '&merchant-id=' + pretixpaypal.merchant_id; sdk_url += '&merchant-id=' + pretixpaypal.merchant_id
} }
if (pretixpaypal.additional_disabled_funding) { if (pretixpaypal.additional_disabled_funding) {
sdk_url += '&disable-funding=' + [pretixpaypal.additional_disabled_funding].filter(Boolean).join(','); sdk_url += '&disable-funding=' + [pretixpaypal.additional_disabled_funding].filter(Boolean).join(',')
} }
if (pretixpaypal.additional_enabled_funding) { if (pretixpaypal.additional_enabled_funding) {
sdk_url += '&enable-funding=' + [pretixpaypal.additional_enabled_funding].filter(Boolean).join(','); sdk_url += '&enable-funding=' + [pretixpaypal.additional_enabled_funding].filter(Boolean).join(',')
} }
if (pretixpaypal.debug_buyer_country) { if (pretixpaypal.debug_buyer_country) {
sdk_url += '&buyer-country=' + pretixpaypal.debug_buyer_country; sdk_url += '&buyer-country=' + pretixpaypal.debug_buyer_country
} }
let ppscript = document.createElement('script'); let ppscript = document.createElement('script')
let ready = false; let ready = false
let head = document.getElementsByTagName("head")[0]; let head = document.getElementsByTagName('head')[0]
ppscript.setAttribute('src', sdk_url); ppscript.setAttribute('src', sdk_url)
ppscript.setAttribute('data-csp-nonce', $.trim($("#csp_nonce").html())); ppscript.setAttribute('data-csp-nonce', $.trim($('#csp_nonce').html()))
ppscript.setAttribute('data-page-type', 'checkout'); ppscript.setAttribute('data-page-type', 'checkout')
ppscript.setAttribute('data-partner-attribution-id', 'ramiioGmbH_Cart_PPCP'); ppscript.setAttribute('data-partner-attribution-id', 'ramiioGmbH_Cart_PPCP')
document.head.appendChild(ppscript); document.head.appendChild(ppscript)
ppscript.onload = ppscript.onreadystatechange = function () { ppscript.onload = ppscript.onreadystatechange = function () {
if (!ready && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) { if (!ready && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) {
ready = true; ready = true
pretixpaypal.paypal = paypal; pretixpaypal.paypal = paypal
// Handle memory leak in IE // Handle memory leak in IE
ppscript.onload = ppscript.onreadystatechange = null; ppscript.onload = ppscript.onreadystatechange = null
if (head && ppscript.parentNode) { if (head && ppscript.parentNode) {
head.removeChild(ppscript); head.removeChild(ppscript)
} }
} }
}; }
document.addEventListener("visibilitychange", this.onApproveSubmit); document.addEventListener('visibilitychange', this.onApproveSubmit)
}, },
ready: function () { ready: function () {
if ($("input[name=payment][value=paypal_apm]").length > 0) { if ($('input[name=payment][value=paypal_apm]').length > 0) {
pretixpaypal.renderAPMs(); pretixpaypal.renderAPMs()
} }
if ($("input[name=payment][value^='paypal']").is(':checked')) { if ($('input[name=payment][value^=\'paypal\']').is(':checked')) {
pretixpaypal.renderButton($("input[name=payment][value^='paypal']:checked").val()); pretixpaypal.renderButton($('input[name=payment][value^=\'paypal\']:checked').val())
} else if ($(".payment-redo-form").length) { } else if ($('.payment-redo-form').length) {
pretixpaypal.renderButton($("input[name=payment][value^='paypal']").val()); pretixpaypal.renderButton($('input[name=payment][value^=\'paypal\']').val())
} else if ($('#paypal-button-container').data('paypage')) { } else if ($('#paypal-button-container').data('paypage')) {
pretixpaypal.renderButton('paypal_apm'); pretixpaypal.renderButton('paypal_apm')
} }
}, },
restore: function () { restore: function () {
// if PayPal has not been initialized, there shouldn't be anything to cleanup // if PayPal has not been initialized, there shouldn't be anything to cleanup
if (pretixpaypal.paypal !== null) { if (pretixpaypal.paypal !== null) {
$('#paypal-button-container').empty() $('#paypal-button-container').empty()
pretixpaypal.continue_button.text(gettext('Continue')); pretixpaypal.continue_button.text(gettext('Continue'))
pretixpaypal.continue_button.show(); pretixpaypal.continue_button.show()
} }
pretixpaypal.continue_button.prop("disabled", false); pretixpaypal.continue_button.prop('disabled', false)
}, },
renderButton: function (method) { renderButton: function (method) {
if (method === 'paypal') { if (method === 'paypal') {
method = "wallet" method = 'wallet'
} else { } else {
method = method.split('paypal_').at(-1) method = method.split('paypal_').at(-1)
} }
pretixpaypal.method = pretixpaypal.method_map[method]; pretixpaypal.method = pretixpaypal.method_map[method]
if (pretixpaypal.method.method === 'apm' && !pretixpaypal.paypage) { if (pretixpaypal.method.method === 'apm' && !pretixpaypal.paypage) {
pretixpaypal.restore(); pretixpaypal.restore()
return; return
} }
$('#paypal-button-container').empty() $('#paypal-button-container').empty()
$('#paypal-card-container').empty() $('#paypal-card-container').empty()
let button = pretixpaypal.paypal.Buttons({ let button = pretixpaypal.paypal.Buttons({
fundingSource: pretixpaypal.method.funding_source, fundingSource: pretixpaypal.method.funding_source,
style: { style: {
layout: pretixpaypal.method.early_auth ? 'horizontal' : 'vertical', layout: pretixpaypal.method.early_auth ? 'horizontal' : 'vertical',
//color: 'white', // color: 'white',
shape: 'rect', shape: 'rect',
label: 'pay', label: 'pay',
tagline: false tagline: false
}, },
createOrder: function (data, actions) { createOrder: function (data, actions) {
if (pretixpaypal.order_id) { if (pretixpaypal.order_id) {
return pretixpaypal.order_id; return pretixpaypal.order_id
} }
// On the paypal:pay view, we already pregenerated the OID. // On the paypal:pay view, we already pregenerated the OID.
// Since this view is also only used for APMs, we only need the XHR-calls for the Smart Payment Buttons. // Since this view is also only used for APMs, we only need the XHR-calls for the Smart Payment Buttons.
if (pretixpaypal.paypage) { if (pretixpaypal.paypage) {
return $("#payment_paypal_" + pretixpaypal.method.method + "_oid"); return $('#payment_paypal_' + pretixpaypal.method.method + '_oid')
} else { } else {
var xhrurl = $("#payment_paypal_" + pretixpaypal.method.method + "_xhr").val(); var xhrurl = $('#payment_paypal_' + pretixpaypal.method.method + '_xhr').val()
} }
return fetch(xhrurl, { return fetch(xhrurl, {
method: 'POST' method: 'POST'
}).then(function (res) { }).then(function (res) {
return res.json(); return res.json()
}).then(function (data) { }).then(function (data) {
if ('id' in data) { if ('id' in data) {
return data.id; return data.id
} else { } else {
// Refreshing the page to surface the request-error message // Refreshing the page to surface the request-error message
location.reload(); location.reload()
} }
}); })
}, },
onApprove: function (data, actions) { onApprove: function (data, actions) {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
pretixpaypal.order_id = data.orderID; pretixpaypal.order_id = data.orderID
pretixpaypal.payer_id = data.payerID; pretixpaypal.payer_id = data.payerID
let method = pretixpaypal.paypage ? "wallet" : pretixpaypal.method.method; let method = pretixpaypal.paypage ? 'wallet' : pretixpaypal.method.method
let selectorstub = "#payment_paypal_" + method; let selectorstub = '#payment_paypal_' + method
// Insert the tokens into the form, so it gets submitted to the server // Insert the tokens into the form, so it gets submitted to the server
$(selectorstub + "_oid").val(pretixpaypal.order_id); $(selectorstub + '_oid').val(pretixpaypal.order_id)
$(selectorstub + "_payer").val(pretixpaypal.payer_id); $(selectorstub + '_payer').val(pretixpaypal.payer_id)
// We are moving the submission to a separate function, which is also an EventListener, since // We are moving the submission to a separate function, which is also an EventListener, since
// SFSafariView refuses to submit a form that is not visible. Unfortunately, that is exactly the case // SFSafariView refuses to submit a form that is not visible. Unfortunately, that is exactly the case
// when the ticket shop is used on iOS within an SFSafariView and the PayPal payment popup has not // when the ticket shop is used on iOS within an SFSafariView and the PayPal payment popup has not
// closed itself quickly enough. // closed itself quickly enough.
pretixpaypal.readyToSubmitApproval = true; pretixpaypal.readyToSubmitApproval = true
pretixpaypal.onApproveSubmit(); pretixpaypal.onApproveSubmit()
// billingToken: null // billingToken: null
// facilitatorAccessToken: "A21AAL_fEu0gDD-sIXyOy65a6MjgSJJrhmxuPcxxUGnL5gW2DzTxiiAksfoC4x8hD-BjeY1LsFVKl7ceuO7UR1a9pQr8Q_AVw" // facilitatorAccessToken: "A21AAL_fEu0gDD-sIXyOy65a6MjgSJJrhmxuPcxxUGnL5gW2DzTxiiAksfoC4x8hD-BjeY1LsFVKl7ceuO7UR1a9pQr8Q_AVw"
// orderID: "7RF70259NY7589848" // orderID: "7RF70259NY7589848"
// payerID: "8M3BU92Z97VXA" // payerID: "8M3BU92Z97VXA"
// paymentID: null // paymentID: null
}, },
}); })
if (button.isEligible()) { if (button.isEligible()) {
button.render('#paypal-button-container'); button.render('#paypal-button-container')
pretixpaypal.continue_button.hide(); pretixpaypal.continue_button.hide()
} else { } else {
pretixpaypal.continue_button.text(gettext('Payment method unavailable')); pretixpaypal.continue_button.text(gettext('Payment method unavailable'))
pretixpaypal.continue_button.show(); pretixpaypal.continue_button.show()
} }
}, },
onApproveSubmit: function() { onApproveSubmit: function () {
if (document.visibilityState === "visible" && pretixpaypal.readyToSubmitApproval === true) { if (document.visibilityState === 'visible' && pretixpaypal.readyToSubmitApproval === true) {
let method = pretixpaypal.paypage ? "wallet" : pretixpaypal.method.method; let method = pretixpaypal.paypage ? 'wallet' : pretixpaypal.method.method
let selectorstub = "#payment_paypal_" + method; let selectorstub = '#payment_paypal_' + method
var $form = $(selectorstub + "_oid").closest("form"); let $form = $(selectorstub + '_oid').closest('form')
$form.get(0).submit(); $form.get(0).submit()
} }
}, },
renderAPMs: function () { renderAPMs: function () {
pretixpaypal.restore(); pretixpaypal.restore()
let inputselector = $("input[name=payment][value=paypal_apm]"); let inputselector = $('input[name=payment][value=paypal_apm]')
let textselector = inputselector.closest("label").find('.accordion-label-text'); let textselector = inputselector.closest('label').find('.accordion-label-text')
let eligibles = []; let eligibles = []
pretixpaypal.paypal.getFundingSources().forEach(function (fundingSource) { pretixpaypal.paypal.getFundingSources().forEach(function (fundingSource) {
// Let's always skip PayPal, since it's always a dedicated funding source // Let's always skip PayPal, since it's always a dedicated funding source
if (fundingSource === 'paypal') { if (fundingSource === 'paypal') {
return; return
} }
// This could also be paypal.Marks() - but they only expose images instead of cleartext... // This could also be paypal.Marks() - but they only expose images instead of cleartext...
let button = pretixpaypal.paypal.Buttons({ let button = pretixpaypal.paypal.Buttons({
fundingSource: fundingSource fundingSource: fundingSource
}); })
if (button.isEligible()) { if (button.isEligible()) {
eligibles.push(gettext(pretixpaypal.apm_map[fundingSource] || fundingSource)); eligibles.push(gettext(pretixpaypal.apm_map[fundingSource] || fundingSource))
} }
}); })
inputselector.attr('title', eligibles.join(', ')); inputselector.attr('title', eligibles.join(', '))
textselector.fadeOut(300, function () { textselector.fadeOut(300, function () {
textselector.text(eligibles.join(', ')); textselector.text(eligibles.join(', '))
textselector.fadeIn(300); textselector.fadeIn(300)
}); })
}, },
guessLocale: function() { guessLocale: function () {
// This is a horrible hackjob and does not at all take into consideration the actual locale. // This is a horrible hackjob and does not at all take into consideration the actual locale.
// Instead, we only look at the language that the shop is currently being displayed in and make // Instead, we only look at the language that the shop is currently being displayed in and make
// that into a locale. // that into a locale.
let allowed_locales = [ let allowed_locales = [
'en_US', 'en_US',
'ar_DZ', 'ar_DZ',
'fr_FR', 'fr_FR',
'es_ES', 'es_ES',
'zh_CN', 'zh_CN',
'de_DE', 'de_DE',
'nl_NL', 'nl_NL',
'pt_PT', 'pt_PT',
'cs_CZ', 'cs_CZ',
'da_DK', 'da_DK',
'fi_FI', 'fi_FI',
'el_GR', 'el_GR',
'hu_HU', 'hu_HU',
'id_ID', 'id_ID',
'he_IL', 'he_IL',
'it_IT', 'it_IT',
'ja_JP', 'ja_JP',
'ru_RU', 'ru_RU',
'no_NO', 'no_NO',
'pl_PL', 'pl_PL',
'sk_SK', 'sk_SK',
'sv_SE', 'sv_SE',
'th_TH', 'th_TH',
'tr_TR', 'tr_TR',
] ]
let lang = $("body").attr("data-locale").split('-')[0]; let lang = $('body').attr('data-locale').split('-')[0]
return allowed_locales.find(element => element.startsWith(lang)); return allowed_locales.find(element => element.startsWith(lang))
} }
}; }
$(function () { $(function () {
// This script is always loaded if paypal is enabled as a payment method, regardless of // This script is always loaded if paypal is enabled as a payment method, regardless of
// whether it is available (it could e.g. be hidden or limited to certain countries). // whether it is available (it could e.g. be hidden or limited to certain countries).
// We do not want to unnecessarily load the sdk. // We do not want to unnecessarily load the sdk.
// If no paypal/paypal_apm payment option is present and we are not on // If no paypal/paypal_apm payment option is present and we are not on
// the (APM) PayView, then we do not need the SDK. // the (APM) PayView, then we do not need the SDK.
if (!$("input[name=payment][value^='paypal']").length && !$('#paypal-button-container').data('paypage')) { if (!$('input[name=payment][value^=\'paypal\']').length && !$('#paypal-button-container').data('paypage')) {
return return
} }
pretixpaypal.load(); pretixpaypal.load();
(async() => { (async () => {
while(!pretixpaypal.paypal) while (!pretixpaypal.paypal)
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise(resolve => setTimeout(resolve, 1000))
pretixpaypal.ready(); pretixpaypal.ready()
})(); })()
}); })
@@ -1,81 +1,81 @@
/*globals $, Morris, gettext, django*/ /* globals Morris, django */
function gettext(msgid) { function gettext (msgid) {
if (typeof django !== 'undefined' && typeof django.gettext !== 'undefined') { if (typeof django !== 'undefined' && typeof django.gettext !== 'undefined') {
return django.gettext(msgid); return django.gettext(msgid)
} }
return msgid; return msgid
} }
$(function () { $(function () {
$(".chart").css("height", "250px"); $('.chart').css('height', '250px')
new Morris.Area({ new Morris.Area({
element: 'obd_chart', element: 'obd_chart',
data: JSON.parse($("#obd-data").html()), data: JSON.parse($('#obd-data').html()),
xkey: 'date', xkey: 'date',
ykeys: ['ordered', 'paid'], ykeys: ['ordered', 'paid'],
labels: [gettext('Placed orders'), gettext('Paid orders')], labels: [gettext('Placed orders'), gettext('Paid orders')],
lineColors: ['#3b1c4a', '#50a167'], lineColors: ['#3b1c4a', '#50a167'],
smooth: false, smooth: false,
resize: true, resize: true,
fillOpacity: 0.3, fillOpacity: 0.3,
behaveLikeLine: true behaveLikeLine: true
}); })
new Morris.Area({ new Morris.Area({
element: 'abd_chart', element: 'abd_chart',
data: JSON.parse($("#abd-data").html()), data: JSON.parse($('#abd-data').html()),
xkey: 'date', xkey: 'date',
ykeys: ['ordered', 'paid'], ykeys: ['ordered', 'paid'],
labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')], labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')],
lineColors: ['#3b1c4a', '#50a167'], lineColors: ['#3b1c4a', '#50a167'],
smooth: false, smooth: false,
resize: true, resize: true,
fillOpacity: 0.3, fillOpacity: 0.3,
behaveLikeLine: true behaveLikeLine: true
}); })
new Morris.Area({ new Morris.Area({
element: 'abt_chart', element: 'abt_chart',
data: JSON.parse($("#abt-data").html()), data: JSON.parse($('#abt-data').html()),
xkey: 'date', xkey: 'date',
ykeys: ['ordered', 'paid'], ykeys: ['ordered', 'paid'],
labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')], labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')],
lineColors: ['#3b1c4a', '#50a167'], lineColors: ['#3b1c4a', '#50a167'],
smooth: false, smooth: false,
resize: true, resize: true,
fillOpacity: 0.3, fillOpacity: 0.3,
behaveLikeLine: true behaveLikeLine: true
}); })
new Morris.Area({ new Morris.Area({
element: 'rev_chart', element: 'rev_chart',
data: JSON.parse($("#rev-data").html()), data: JSON.parse($('#rev-data').html()),
xkey: 'date', xkey: 'date',
ykeys: ['revenue'], ykeys: ['revenue'],
labels: [gettext('Total revenue')], labels: [gettext('Total revenue')],
smooth: false, smooth: false,
resize: true, resize: true,
lineColors: ['#3b1c4a'], lineColors: ['#3b1c4a'],
fillOpacity: 0.3, fillOpacity: 0.3,
preUnits: $.trim($("#currency").html()) + ' ' preUnits: $.trim($('#currency').html()) + ' '
}); })
new Morris.Bar({ new Morris.Bar({
element: 'obp_chart', element: 'obp_chart',
data: JSON.parse($("#obp-data").html()), data: JSON.parse($('#obp-data').html()),
xkey: 'item_short', xkey: 'item_short',
ykeys: ['ordered', 'paid'], ykeys: ['ordered', 'paid'],
labels: [gettext('Placed orders'), gettext('Paid orders')], labels: [gettext('Placed orders'), gettext('Paid orders')],
barColors: ['#3b1c4a', '#50a167'], barColors: ['#3b1c4a', '#50a167'],
hoverCallback: function (index, options, content, row) { hoverCallback: function (index, options, content, row) {
console.log(content); console.log(content)
var $c = $("<div>" + content + "</div>"); let $c = $('<div>' + content + '</div>')
var $label = $c.find(".morris-hover-row-label"); let $label = $c.find('.morris-hover-row-label')
$label.text(row.item); $label.text(row.item)
var newc = $label.get(0).outerHTML; let newc = $label.get(0).outerHTML
$c.find('.morris-hover-point').each(function (i, r) { $c.find('.morris-hover-point').each(function (i, r) {
if ($.trim($(r).text().split("\n")[2]) !== "0") { if ($.trim($(r).text().split('\n')[2]) !== '0') {
newc += r.outerHTML; newc += r.outerHTML
} }
}); })
return newc; return newc
}, },
resize: true, resize: true,
xLabelAngle: 30 xLabelAngle: 30
}); })
}); })
@@ -1,435 +1,435 @@
/*global $, stripe_pubkey, stripe_loadingmessage, gettext */ /* global stripe_pubkey, stripe_loadingmessage, gettext */
'use strict'; 'use strict'
var pretixstripe = { var pretixstripe = {
stripe: null, stripe: null,
elements: null, elements: null,
card: null, card: null,
sepa: null, sepa: null,
affirm: null, affirm: null,
klarna: null, klarna: null,
paymentRequest: null, paymentRequest: null,
paymentRequestButton: null, paymentRequestButton: null,
'pm_request': function (method, element, kwargs = {}) { pm_request: function (method, element, kwargs = {}) {
waitingDialog.show(gettext("Contacting Stripe …")); waitingDialog.show(gettext('Contacting Stripe …'))
$(".stripe-errors").hide(); $('.stripe-errors').hide()
pretixstripe.stripe.createPaymentMethod(method, element, kwargs).then(function (result) { pretixstripe.stripe.createPaymentMethod(method, element, kwargs).then(function (result) {
waitingDialog.hide(); waitingDialog.hide()
if (result.error) { if (result.error) {
$(".stripe-errors").stop().hide().removeClass("sr-only"); $('.stripe-errors').stop().hide().removeClass('sr-only')
$(".stripe-errors").html("<div class='alert alert-danger'>" + result.error.message + "</div>"); $('.stripe-errors').html('<div class=\'alert alert-danger\'>' + result.error.message + '</div>')
$(".stripe-errors").slideDown(); $('.stripe-errors').slideDown()
} else { } else {
var $form = $("#stripe_" + method + "_payment_method_id").closest("form"); let $form = $('#stripe_' + method + '_payment_method_id').closest('form')
// Insert the token into the form so it gets submitted to the server // Insert the token into the form so it gets submitted to the server
$("#stripe_" + method + "_payment_method_id").val(result.paymentMethod.id); $('#stripe_' + method + '_payment_method_id').val(result.paymentMethod.id)
if (method === 'card') { if (method === 'card') {
$("#stripe_card_brand").val(result.paymentMethod.card.brand); $('#stripe_card_brand').val(result.paymentMethod.card.brand)
$("#stripe_card_last4").val(result.paymentMethod.card.last4); $('#stripe_card_last4').val(result.paymentMethod.card.last4)
} }
if (method === 'sepa_debit') { if (method === 'sepa_debit') {
$("#stripe_sepa_debit_last4").val(result.paymentMethod.sepa_debit.last4); $('#stripe_sepa_debit_last4').val(result.paymentMethod.sepa_debit.last4)
} }
// and submit // and submit
$form.get(0).submit(); $form.get(0).submit()
} }
}).catch((e) => { }).catch((e) => {
waitingDialog.hide(); waitingDialog.hide()
$(".stripe-errors").stop().hide().removeClass("sr-only"); $('.stripe-errors').stop().hide().removeClass('sr-only')
$(".stripe-errors").html("<div class='alert alert-danger'>Technical error, please contact support: " + e + "</div>"); $('.stripe-errors').html('<div class=\'alert alert-danger\'>Technical error, please contact support: ' + e + '</div>')
$(".stripe-errors").slideDown(); $('.stripe-errors').slideDown()
}); })
}, },
'load': function () { load: function () {
if (pretixstripe.stripe !== null) { if (pretixstripe.stripe !== null) {
return; return
} }
$('.stripe-container').closest("form").find(".checkout-button-row .btn-primary").prop("disabled", true); $('.stripe-container').closest('form').find('.checkout-button-row .btn-primary').prop('disabled', true)
$.ajax( $.ajax(
{ {
url: 'https://js.stripe.com/v3/', url: 'https://js.stripe.com/v3/',
dataType: 'script', dataType: 'script',
success: function () { success: function () {
if ($.trim($("#stripe_connectedAccountId").html())) { if ($.trim($('#stripe_connectedAccountId').html())) {
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()), { pretixstripe.stripe = Stripe($.trim($('#stripe_pubkey').html()), {
stripeAccount: $.trim($("#stripe_connectedAccountId").html()), stripeAccount: $.trim($('#stripe_connectedAccountId').html()),
locale: $.trim($("body").attr("data-locale")) locale: $.trim($('body').attr('data-locale'))
}); })
} else { } else {
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()), { pretixstripe.stripe = Stripe($.trim($('#stripe_pubkey').html()), {
locale: $.trim($("body").attr("data-locale")) locale: $.trim($('body').attr('data-locale'))
}); })
} }
pretixstripe.elements = pretixstripe.stripe.elements(); pretixstripe.elements = pretixstripe.stripe.elements()
if ($.trim($("#stripe_merchantcountry").html()) !== "") { if ($.trim($('#stripe_merchantcountry').html()) !== '') {
try { try {
pretixstripe.paymentRequest = pretixstripe.stripe.paymentRequest({ pretixstripe.paymentRequest = pretixstripe.stripe.paymentRequest({
country: $("#stripe_merchantcountry").html(), country: $('#stripe_merchantcountry').html(),
currency: $("#stripe_card_currency").val().toLowerCase(), currency: $('#stripe_card_currency').val().toLowerCase(),
total: { total: {
label: gettext('Total'), label: gettext('Total'),
amount: parseInt($("#stripe_card_total").val()) amount: parseInt($('#stripe_card_total').val())
}, },
displayItems: [], displayItems: [],
requestPayerName: false, requestPayerName: false,
requestPayerEmail: false, requestPayerEmail: false,
requestPayerPhone: false, requestPayerPhone: false,
requestShipping: false, requestShipping: false,
}); })
pretixstripe.paymentRequest.on('paymentmethod', function (ev) { pretixstripe.paymentRequest.on('paymentmethod', function (ev) {
ev.complete('success'); ev.complete('success')
var $form = $("#stripe_card_payment_method_id").closest("form"); let $form = $('#stripe_card_payment_method_id').closest('form')
// Insert the token into the form so it gets submitted to the server // Insert the token into the form so it gets submitted to the server
$("#stripe_card_payment_method_id").val(ev.paymentMethod.id); $('#stripe_card_payment_method_id').val(ev.paymentMethod.id)
$("#stripe_card_brand").val(ev.paymentMethod.card.brand); $('#stripe_card_brand').val(ev.paymentMethod.card.brand)
$("#stripe_card_last4").val(ev.paymentMethod.card.last4); $('#stripe_card_last4').val(ev.paymentMethod.card.last4)
// and submit // and submit
$form.get(0).submit(); $form.get(0).submit()
}); })
} catch (e) { } catch (e) {
pretixstripe.paymentRequest = null; pretixstripe.paymentRequest = null
} }
} else { } else {
pretixstripe.paymentRequest = null; pretixstripe.paymentRequest = null
} }
if ($("#stripe-card").length) { if ($('#stripe-card').length) {
pretixstripe.card = pretixstripe.elements.create('card', { pretixstripe.card = pretixstripe.elements.create('card', {
'style': { style: {
'base': { base: {
'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif', fontFamily: '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
'fontSize': '14px', fontSize: '14px',
'color': '#555555', color: '#555555',
'lineHeight': '1.42857', lineHeight: '1.42857',
'border': '1px solid #ccc', border: '1px solid #ccc',
'::placeholder': { '::placeholder': {
color: 'rgba(0,0,0,0.4)', color: 'rgba(0,0,0,0.4)',
}, },
}, },
'invalid': { invalid: {
'color': 'red', color: 'red',
}, },
}, },
classes: { classes: {
focus: 'is-focused', focus: 'is-focused',
invalid: 'has-error', invalid: 'has-error',
} }
}); })
pretixstripe.card.mount("#stripe-card"); pretixstripe.card.mount('#stripe-card')
pretixstripe.card.on('ready', function () { pretixstripe.card.on('ready', function () {
$('.stripe-container').closest("form").find(".checkout-button-row .btn-primary").prop("disabled", false); $('.stripe-container').closest('form').find('.checkout-button-row .btn-primary').prop('disabled', false)
}); })
} }
if ($("#stripe-sepa").length) { if ($('#stripe-sepa').length) {
pretixstripe.sepa = pretixstripe.elements.create('iban', { pretixstripe.sepa = pretixstripe.elements.create('iban', {
'style': { style: {
'base': { base: {
'fontFamily': '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif', fontFamily: '"Open Sans","OpenSans","Helvetica Neue",Helvetica,Arial,sans-serif',
'fontSize': '14px', fontSize: '14px',
'color': '#555555', color: '#555555',
'lineHeight': '1.42857', lineHeight: '1.42857',
'border': '1px solid #ccc', border: '1px solid #ccc',
'::placeholder': { '::placeholder': {
color: 'rgba(0,0,0,0.4)', color: 'rgba(0,0,0,0.4)',
}, },
}, },
'invalid': { invalid: {
'color': 'red', color: 'red',
}, },
}, },
supportedCountries: ['SEPA'], supportedCountries: ['SEPA'],
classes: { classes: {
focus: 'is-focused', focus: 'is-focused',
invalid: 'has-error', invalid: 'has-error',
} }
}); })
pretixstripe.sepa.on('change', function (event) { pretixstripe.sepa.on('change', function (event) {
// List of IBAN-countries, that require the country as well as line1-property according to // List of IBAN-countries, that require the country as well as line1-property according to
// https://stripe.com/docs/payments/sepa-debit/accept-a-payment?platform=web&ui=element#web-submit-payment // https://stripe.com/docs/payments/sepa-debit/accept-a-payment?platform=web&ui=element#web-submit-payment
if (['AD', 'PF', 'TF', 'GI', 'GB', 'GG', 'VA', 'IM', 'JE', 'MC', 'NC', 'BL', 'PM', 'SM', 'CH', 'WF'].indexOf(event.country) > 0) { if (['AD', 'PF', 'TF', 'GI', 'GB', 'GG', 'VA', 'IM', 'JE', 'MC', 'NC', 'BL', 'PM', 'SM', 'CH', 'WF'].indexOf(event.country) > 0) {
$("#stripe_sepa_debit_country").prop('checked', true); $('#stripe_sepa_debit_country').prop('checked', true)
$("#stripe_sepa_debit_country").change(); $('#stripe_sepa_debit_country').change()
} else { } else {
$("#stripe_sepa_debit_country").prop('checked', false); $('#stripe_sepa_debit_country').prop('checked', false)
$("#stripe_sepa_debit_country").change(); $('#stripe_sepa_debit_country').change()
} }
if (event.bankName) { if (event.bankName) {
$("#stripe_sepa_debit_bank").val(event.bankName); $('#stripe_sepa_debit_bank').val(event.bankName)
} }
}); })
pretixstripe.sepa.mount("#stripe-sepa"); pretixstripe.sepa.mount('#stripe-sepa')
pretixstripe.sepa.on('ready', function () { pretixstripe.sepa.on('ready', function () {
$('.stripe-container').closest("form").find(".checkout-button-row .btn-primary").prop("disabled", false); $('.stripe-container').closest('form').find('.checkout-button-row .btn-primary').prop('disabled', false)
}); })
} }
if ($("#stripe-affirm").length) { if ($('#stripe-affirm').length) {
pretixstripe.affirm = pretixstripe.elements.create('affirmMessage', { pretixstripe.affirm = pretixstripe.elements.create('affirmMessage', {
'amount': parseInt($("#stripe_affirm_total").val()), amount: parseInt($('#stripe_affirm_total').val()),
'currency': $("#stripe_affirm_currency").val(), currency: $('#stripe_affirm_currency').val(),
}); })
pretixstripe.affirm.mount('#stripe-affirm'); pretixstripe.affirm.mount('#stripe-affirm')
} }
if ($("#stripe-klarna").length) { if ($('#stripe-klarna').length) {
try { try {
pretixstripe.klarna = pretixstripe.elements.create('paymentMethodMessaging', { pretixstripe.klarna = pretixstripe.elements.create('paymentMethodMessaging', {
'amount': parseInt($("#stripe_klarna_total").val()), amount: parseInt($('#stripe_klarna_total').val()),
'currency': $("#stripe_klarna_currency").val(), currency: $('#stripe_klarna_currency').val(),
'countryCode': $("#stripe_klarna_country").val(), countryCode: $('#stripe_klarna_country').val(),
'paymentMethodTypes': ['klarna'], paymentMethodTypes: ['klarna'],
}); })
pretixstripe.klarna.mount('#stripe-klarna'); pretixstripe.klarna.mount('#stripe-klarna')
} catch (e) { } catch (e) {
console.error(e); console.error(e)
$("#stripe-klarna").html("<div class='alert alert-danger'>Technical error, please contact support: " + e + "</div>"); $('#stripe-klarna').html('<div class=\'alert alert-danger\'>Technical error, please contact support: ' + e + '</div>')
} }
} }
if ($("#stripe-payment-request-button").length && pretixstripe.paymentRequest != null) { if ($('#stripe-payment-request-button').length && pretixstripe.paymentRequest != null) {
pretixstripe.paymentRequestButton = pretixstripe.elements.create('paymentRequestButton', { pretixstripe.paymentRequestButton = pretixstripe.elements.create('paymentRequestButton', {
paymentRequest: pretixstripe.paymentRequest, paymentRequest: pretixstripe.paymentRequest,
}); })
pretixstripe.paymentRequest.canMakePayment().then(function (result) { pretixstripe.paymentRequest.canMakePayment().then(function (result) {
if (result) { if (result) {
pretixstripe.paymentRequestButton.mount('#stripe-payment-request-button'); pretixstripe.paymentRequestButton.mount('#stripe-payment-request-button')
$('#stripe-card-elements .stripe-or').removeClass("hidden"); $('#stripe-card-elements .stripe-or').removeClass('hidden')
$('#stripe-payment-request-button').parent().removeClass("hidden"); $('#stripe-payment-request-button').parent().removeClass('hidden')
} else { } else {
$('#stripe-payment-request-button').hide(); $('#stripe-payment-request-button').hide()
document.getElementById('stripe-payment-request-button').style.display = 'none'; document.getElementById('stripe-payment-request-button').style.display = 'none'
} }
}); })
} }
} }
} }
); )
}, },
'withStripe': function (callback) { withStripe: function (callback) {
$.ajax({ $.ajax({
url: 'https://js.stripe.com/v3/', url: 'https://js.stripe.com/v3/',
dataType: 'script', dataType: 'script',
success: function () { success: function () {
if ($.trim($("#stripe_connectedAccountId").html())) { if ($.trim($('#stripe_connectedAccountId').html())) {
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()), { pretixstripe.stripe = Stripe($.trim($('#stripe_pubkey').html()), {
stripeAccount: $.trim($("#stripe_connectedAccountId").html()), stripeAccount: $.trim($('#stripe_connectedAccountId').html()),
locale: $.trim($("body").attr("data-locale")) locale: $.trim($('body').attr('data-locale'))
}); })
} else { } else {
pretixstripe.stripe = Stripe($.trim($("#stripe_pubkey").html()), { pretixstripe.stripe = Stripe($.trim($('#stripe_pubkey').html()), {
locale: $.trim($("body").attr("data-locale")) locale: $.trim($('body').attr('data-locale'))
}); })
} }
callback(); callback()
} }
}); })
}, },
'handleAlipayAction': function (payment_intent_client_secret) { handleAlipayAction: function (payment_intent_client_secret) {
pretixstripe.withStripe(function () { pretixstripe.withStripe(function () {
pretixstripe.stripe.confirmAlipayPayment( pretixstripe.stripe.confirmAlipayPayment(
payment_intent_client_secret, payment_intent_client_secret,
{ {
return_url: window.location.href return_url: window.location.href
} }
).then(function (result) { ).then(function (result) {
if (result.error) { if (result.error) {
waitingDialog.hide(); waitingDialog.hide()
$(".stripe-errors").stop().hide().removeClass("sr-only"); $('.stripe-errors').stop().hide().removeClass('sr-only')
$(".stripe-errors").html("<div class='alert alert-danger'>Technical error, please contact support: " + result.error.message + "</div>"); $('.stripe-errors').html('<div class=\'alert alert-danger\'>Technical error, please contact support: ' + result.error.message + '</div>')
$(".stripe-errors").slideDown(); $('.stripe-errors').slideDown()
} else { } else {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
} }
}); })
}); })
}, },
'handleWechatAction': function (payment_intent_client_secret) { handleWechatAction: function (payment_intent_client_secret) {
pretixstripe.withStripe(function () { pretixstripe.withStripe(function () {
pretixstripe.stripe.confirmWechatPayPayment( pretixstripe.stripe.confirmWechatPayPayment(
payment_intent_client_secret, payment_intent_client_secret,
{ {
payment_method_options: { payment_method_options: {
wechat_pay: { wechat_pay: {
client: 'web', client: 'web',
}, },
}, },
} }
).then(function (result) { ).then(function (result) {
if (result.error) { if (result.error) {
waitingDialog.hide(); waitingDialog.hide()
$(".stripe-errors").stop().hide().removeClass("sr-only"); $('.stripe-errors').stop().hide().removeClass('sr-only')
$(".stripe-errors").html("<div class='alert alert-danger'>Technical error, please contact support: " + result.error.message + "</div>"); $('.stripe-errors').html('<div class=\'alert alert-danger\'>Technical error, please contact support: ' + result.error.message + '</div>')
$(".stripe-errors").slideDown(); $('.stripe-errors').slideDown()
} else { } else {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
location.reload(); location.reload()
} }
}); })
}); })
}, },
'handleCardAction': function (payment_intent_client_secret) { handleCardAction: function (payment_intent_client_secret) {
pretixstripe.withStripe(function () { pretixstripe.withStripe(function () {
pretixstripe.stripe.handleCardAction( pretixstripe.stripe.handleCardAction(
payment_intent_client_secret payment_intent_client_secret
).then(function (result) { ).then(function (result) {
if (result.error) { if (result.error) {
waitingDialog.hide(); waitingDialog.hide()
$(".stripe-errors").stop().hide().removeClass("sr-only"); $('.stripe-errors').stop().hide().removeClass('sr-only')
$(".stripe-errors").html("<div class='alert alert-danger'>Technical error, please contact support: " + result.error.message + "</div>"); $('.stripe-errors').html('<div class=\'alert alert-danger\'>Technical error, please contact support: ' + result.error.message + '</div>')
$(".stripe-errors").slideDown(); $('.stripe-errors').slideDown()
} else { } else {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
location.reload(); location.reload()
} }
}); })
}); })
}, },
'handlePaymentRedirectAction': function (payment_intent_next_action_redirect_url) { handlePaymentRedirectAction: function (payment_intent_next_action_redirect_url) {
waitingDialog.show(gettext("Contacting your bank …")); waitingDialog.show(gettext('Contacting your bank …'))
let payment_intent_redirect_action_handling = $.trim($("#stripe_payment_intent_redirect_action_handling").html()); let payment_intent_redirect_action_handling = $.trim($('#stripe_payment_intent_redirect_action_handling').html())
if (payment_intent_redirect_action_handling === 'iframe') { if (payment_intent_redirect_action_handling === 'iframe') {
let iframe = document.createElement('iframe'); let iframe = document.createElement('iframe')
iframe.src = payment_intent_next_action_redirect_url; iframe.src = payment_intent_next_action_redirect_url
iframe.className = 'embed-responsive-item'; iframe.className = 'embed-responsive-item'
$('#scacontainer').append(iframe); $('#scacontainer').append(iframe)
$('#scacontainer iframe').on("load", function () { $('#scacontainer iframe').on('load', function () {
waitingDialog.hide(); waitingDialog.hide()
}); })
} else if (payment_intent_redirect_action_handling === 'redirect') { } else if (payment_intent_redirect_action_handling === 'redirect') {
window.location.href = payment_intent_next_action_redirect_url; window.location.href = payment_intent_next_action_redirect_url
} }
} }
}; }
$(function () { $(function () {
if ($("#stripe_payment_intent_SCA_status").length) { if ($('#stripe_payment_intent_SCA_status').length) {
let payment_intent_redirect_action_handling = $.trim($("#stripe_payment_intent_redirect_action_handling").html()); let payment_intent_redirect_action_handling = $.trim($('#stripe_payment_intent_redirect_action_handling').html())
let order_status = $.trim($("#order_status").html()); let order_status = $.trim($('#order_status').html())
let order_url = $.trim($("#order_url").html()) let order_url = $.trim($('#order_url').html())
if (payment_intent_redirect_action_handling === 'iframe') { if (payment_intent_redirect_action_handling === 'iframe') {
window.parent.postMessage('3DS-authentication-complete.' + order_status, '*'); window.parent.postMessage('3DS-authentication-complete.' + order_status, '*')
return; return
} else if (payment_intent_redirect_action_handling === 'redirect') { } else if (payment_intent_redirect_action_handling === 'redirect') {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
if (order_status === 'p') { if (order_status === 'p') {
window.location.href = order_url + '?paid=yes'; window.location.href = order_url + '?paid=yes'
} else { } else {
window.location.href = order_url; window.location.href = order_url
} }
} }
} else if ($("#stripe_payment_intent_next_action_redirect_url").length) { } else if ($('#stripe_payment_intent_next_action_redirect_url').length) {
let payment_intent_next_action_redirect_url = JSON.parse($("#stripe_payment_intent_next_action_redirect_url").html()); let payment_intent_next_action_redirect_url = JSON.parse($('#stripe_payment_intent_next_action_redirect_url').html())
pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url); pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url)
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "promptpay_display_qr_code") { } else if ($.trim($('#stripe_payment_intent_action_type').html()) === 'promptpay_display_qr_code') {
waitingDialog.hide(); waitingDialog.hide()
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") { } else if ($.trim($('#stripe_payment_intent_action_type').html()) === 'wechat_pay_display_qr_code') {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html()); let payment_intent_client_secret = $.trim($('#stripe_payment_intent_client_secret').html())
pretixstripe.handleWechatAction(payment_intent_client_secret); pretixstripe.handleWechatAction(payment_intent_client_secret)
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "alipay_handle_redirect") { } else if ($.trim($('#stripe_payment_intent_action_type').html()) === 'alipay_handle_redirect') {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html()); let payment_intent_client_secret = $.trim($('#stripe_payment_intent_client_secret').html())
pretixstripe.handleAlipayAction(payment_intent_client_secret); pretixstripe.handleAlipayAction(payment_intent_client_secret)
} else if ($("#stripe_payment_intent_client_secret").length) { } else if ($('#stripe_payment_intent_client_secret').length) {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html()); let payment_intent_client_secret = $.trim($('#stripe_payment_intent_client_secret').html())
pretixstripe.handleCardAction(payment_intent_client_secret); pretixstripe.handleCardAction(payment_intent_client_secret)
} }
$(window).on("message onmessage", function (e) { $(window).on('message onmessage', function (e) {
if (typeof e.originalEvent.data === "string" && e.originalEvent.data.startsWith('3DS-authentication-complete.')) { if (typeof e.originalEvent.data === 'string' && e.originalEvent.data.startsWith('3DS-authentication-complete.')) {
waitingDialog.show(gettext("Confirming your payment …")); waitingDialog.show(gettext('Confirming your payment …'))
$('#scacontainer').hide(); $('#scacontainer').hide()
$('#continuebutton').removeClass('hidden'); $('#continuebutton').removeClass('hidden')
if (e.originalEvent.data.split('.')[1] == 'p') { if (e.originalEvent.data.split('.')[1] == 'p') {
window.location.href = $('#continuebutton').attr('href') + '?paid=yes'; window.location.href = $('#continuebutton').attr('href') + '?paid=yes'
} else { } else {
window.location.href = $('#continuebutton').attr('href'); window.location.href = $('#continuebutton').attr('href')
} }
} }
}); })
if (!$(".stripe-container").length) if (!$('.stripe-container').length)
return; return
if ( if (
$("input[name=payment][value=stripe]").is(':checked') $('input[name=payment][value=stripe]').is(':checked')
|| $("input[name=payment][value=stripe_sepa_debit]").is(':checked') || $('input[name=payment][value=stripe_sepa_debit]').is(':checked')
|| $("input[name=payment][value=stripe_affirm]").is(':checked') || $('input[name=payment][value=stripe_affirm]').is(':checked')
|| $("input[name=payment][value=stripe_klarna]").is(':checked') || $('input[name=payment][value=stripe_klarna]').is(':checked')
|| $(".payment-redo-form").length) { || $('.payment-redo-form').length) {
pretixstripe.load(); pretixstripe.load()
} else { } else {
$("input[name=payment]").change(function () { $('input[name=payment]').change(function () {
if (['stripe', 'stripe_sepa_debit', 'stripe_affirm', 'stripe_klarna'].indexOf($(this).val()) > -1) { if (['stripe', 'stripe_sepa_debit', 'stripe_affirm', 'stripe_klarna'].indexOf($(this).val()) > -1) {
pretixstripe.load(); pretixstripe.load()
} }
}) })
} }
$("#stripe_other_card").click( $('#stripe_other_card').click(
function (e) { function (e) {
$("#stripe_card_payment_method_id").val(""); $('#stripe_card_payment_method_id').val('')
$("#stripe-current-card").slideUp(); $('#stripe-current-card').slideUp()
$("#stripe-card-elements").slideDown(); $('#stripe-card-elements').slideDown()
e.preventDefault(); e.preventDefault()
return false; return false
} }
); )
if ($("#stripe-current-card").length) { if ($('#stripe-current-card').length) {
$("#stripe-card-elements").hide(); $('#stripe-card-elements').hide()
} }
$("#stripe_other_account").click( $('#stripe_other_account').click(
function (e) { function (e) {
$("#stripe_sepa_debit_payment_method_id").val(""); $('#stripe_sepa_debit_payment_method_id').val('')
$("#stripe-current-account").slideUp(); $('#stripe-current-account').slideUp()
// We're using a css-selector here instead of the id-selector, // We're using a css-selector here instead of the id-selector,
// as we're hiding Stripe Elements *and* Django form fields // as we're hiding Stripe Elements *and* Django form fields
$('.stripe-sepa_debit-form').slideDown(); $('.stripe-sepa_debit-form').slideDown()
e.preventDefault(); e.preventDefault()
return false; return false
} }
); )
if ($("#stripe-current-account").length) { if ($('#stripe-current-account').length) {
// We're using a css-selector here instead of the id-selector, // We're using a css-selector here instead of the id-selector,
// as we're hiding Stripe Elements *and* Django form fields // as we're hiding Stripe Elements *and* Django form fields
$('.stripe-sepa_debit-form').hide(); $('.stripe-sepa_debit-form').hide()
} }
$('.stripe-container').closest("form").submit( $('.stripe-container').closest('form').submit(
function () { function () {
if ($("input[name=card_new]").length && !$("input[name=card_new]").prop('checked')) { if ($('input[name=card_new]').length && !$('input[name=card_new]').prop('checked')) {
return null; return null
} }
if (($("input[name=payment][value=stripe]").prop('checked') || $("input[name=payment][type=radio]").length === 0) if (($('input[name=payment][value=stripe]').prop('checked') || $('input[name=payment][type=radio]').length === 0)
&& $("#stripe_card_payment_method_id").val() == "") { && $('#stripe_card_payment_method_id').val() == '') {
pretixstripe.pm_request('card', pretixstripe.card); pretixstripe.pm_request('card', pretixstripe.card)
return false; return false
} }
if (($("input[name=payment][value=stripe_sepa_debit]").prop('checked')) && $("#stripe_sepa_debit_payment_method_id").val() == "") { if (($('input[name=payment][value=stripe_sepa_debit]').prop('checked')) && $('#stripe_sepa_debit_payment_method_id').val() == '') {
pretixstripe.pm_request('sepa_debit', pretixstripe.sepa, { pretixstripe.pm_request('sepa_debit', pretixstripe.sepa, {
billing_details: { billing_details: {
name: $("#id_payment_stripe_sepa_debit-accountname").val(), name: $('#id_payment_stripe_sepa_debit-accountname').val(),
email: $("#stripe_sepa_debit_email").val(), email: $('#stripe_sepa_debit_email').val(),
address: { address: {
line1: $("#id_payment_stripe_sepa_debit-line1").val(), line1: $('#id_payment_stripe_sepa_debit-line1').val(),
postal_code: $("#id_payment_stripe_sepa_debit-postal_code").val(), postal_code: $('#id_payment_stripe_sepa_debit-postal_code').val(),
city: $("#id_payment_stripe_sepa_debit-city").val(), city: $('#id_payment_stripe_sepa_debit-city').val(),
country: $("#id_payment_stripe_sepa_debit-country").val(), country: $('#id_payment_stripe_sepa_debit-country').val(),
} }
} }
}); })
return false; return false
} }
} }
); )
}); })
+169 -171
View File
@@ -1,195 +1,193 @@
$(function () { $(function () {
"use strict"; 'use strict'
// Responses are expected to only depend on the GET parameters passed, so we can have a little client-side cache // Responses are expected to only depend on the GET parameters passed, so we can have a little client-side cache
// to prevent fetching the same thing many times. // to prevent fetching the same thing many times.
var responseCache = {}; let responseCache = {}
const cleanName = (name) => { const cleanName = (name) => {
// Remove form prefix // Remove form prefix
name = name.split("-").pop(); name = name.split('-').pop()
// Remove settings prefix // Remove settings prefix
name = name.replace(/^invoice_address_from_/, ""); name = name.replace(/^invoice_address_from_/, '')
return name return name
} }
$("[data-address-information-url]").each(function () { $('[data-address-information-url]').each(function () {
let xhr; let xhr
const form = $(this); const form = $(this)
const dependencies = $(this).find("[data-trigger-address-info]"); const dependencies = $(this).find('[data-trigger-address-info]')
const loader = $("<span class='fa fa-cog fa-spin'></span>").hide().prependTo(dependencies.closest(".form-group").find("label").first()) const loader = $('<span class=\'fa fa-cog fa-spin\'></span>').hide().prependTo(dependencies.closest('.form-group').find('label').first())
const baseUrl = this.getAttribute('data-address-information-url') const baseUrl = this.getAttribute('data-address-information-url')
const isAnyRequired = dependencies.toArray().some(function (e) { return $(e).closest(".form-group").is(".required") }); const isAnyRequired = dependencies.toArray().some(function (e) { return $(e).closest('.form-group').is('.required') })
const dependents = { const dependents = {
'city': form.find("input[name$=city]"), city: form.find('input[name$=city]'),
'zipcode': form.find("input[name$=zipcode]"), zipcode: form.find('input[name$=zipcode]'),
'street': form.find("textarea[name$=street]"), street: form.find('textarea[name$=street]'),
'state': form.find("select[name$=state]"), state: form.find('select[name$=state]'),
'vat_id': form.find("input[name$=vat_id]"), vat_id: form.find('input[name$=vat_id]'),
}; }
form.find("select[name*=transmission_], textarea[name*=transmission_], input[name*=transmission_]").each(function () { form.find('select[name*=transmission_], textarea[name*=transmission_], input[name*=transmission_]').each(function () {
dependents[cleanName($(this).attr("name"))] = $(this) dependents[cleanName($(this).attr('name'))] = $(this)
}) })
const dependentsDisabled = []; const dependentsDisabled = []
for (var k in dependents) { for (let k in dependents) {
if (dependents[k].prop("disabled")) { if (dependents[k].prop('disabled')) {
dependentsDisabled.push(k); dependentsDisabled.push(k)
} }
} }
if (!Object.values(dependents).some((el) => el.length)) { if (!Object.values(dependents).some((el) => el.length)) {
// No address fields found, do not create request // No address fields found, do not create request
return; return
} }
const update_form = function (data) { const update_form = function (data) {
var selected_state = dependents.state.prop("data-selected-value"); let selected_state = dependents.state.prop('data-selected-value')
if (selected_state) dependents.state.prop("data-selected-value", ""); if (selected_state) dependents.state.prop('data-selected-value', '')
dependents.state.find("option:not([value=''])").remove(); dependents.state.find('option:not([value=\'\'])').remove()
$.each(data.data, function (k, s) { $.each(data.data, function (k, s) {
var o = $("<option>").attr("value", s.code).text(s.name); let o = $('<option>').attr('value', s.code).text(s.name)
if (selected_state === s.code) o.prop("selected", true); if (selected_state === s.code) o.prop('selected', true)
dependents.state.append(o); dependents.state.append(o)
}); })
if (dependents.transmission_type) { if (dependents.transmission_type) {
var selected_transmission_type = dependents.transmission_type.prop("data-selected-value"); let selected_transmission_type = dependents.transmission_type.prop('data-selected-value')
if (selected_transmission_type) dependents.transmission_type.prop("data-selected-value", ""); if (selected_transmission_type) dependents.transmission_type.prop('data-selected-value', '')
dependents.transmission_type.find("option:not([value='']):not([value='-'])").remove(); dependents.transmission_type.find('option:not([value=\'\']):not([value=\'-\'])').remove()
if (!data.transmission_type.visible) { if (!data.transmission_type.visible) {
selected_transmission_type = "email"; selected_transmission_type = 'email'
} }
$.each(data.transmission_types, function (k, s) { $.each(data.transmission_types, function (k, s) {
var o = $("<option>").attr("value", s.code).text(s.name); let o = $('<option>').attr('value', s.code).text(s.name)
if (selected_transmission_type === s.code) { if (selected_transmission_type === s.code) {
o.prop("selected", true); o.prop('selected', true)
} }
dependents.transmission_type.append(o); dependents.transmission_type.append(o)
}); })
}
} for (var k in dependents) {
const options = data[k],
dependent = dependents[k]
let visible = 'visible' in options ? options.visible : true
for (var k in dependents) { if (dependent.is('[data-display-dependency]')) {
const options = data[k], const dependency = $(dependent.attr('data-display-dependency'))
dependent = dependents[k]; visible = visible && (
let visible = 'visible' in options ? options.visible : true; (dependency.attr('type') === 'checkbox' || dependency.attr('type') === 'radio') ? dependency.prop('checked') : !!dependency.val()
)
}
if (dependent.is("[data-display-dependency]")) { if ('label' in options) {
const dependency = $(dependent.attr("data-display-dependency")); dependent.closest('.form-group').find('.control-label').text(options.label)
visible = visible && ( }
(dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val() if ('helptext_visible' in options) {
); dependent.closest('.form-group').find('.help-block').toggle(options.helptext_visible)
} }
if ('label' in options) { const required = 'required' in options && visible && (
dependent.closest(".form-group").find(".control-label").text(options.label); (options.required === 'if_any' && isAnyRequired)
} || (options.required === true)
if ('helptext_visible' in options) { )
dependent.closest(".form-group").find(".help-block").toggle(options.helptext_visible); dependent.closest('.form-group').toggle(visible).toggleClass('required', required)
} dependent.prop('required', required)
const required = 'required' in options && visible && ( const label = dependent.closest('.form-group').find('label')
(options.required === 'if_any' && isAnyRequired) || const labelRequired = label.find('.label-required')
(options.required === true) if (!required) {
); labelRequired.remove()
dependent.closest(".form-group").toggle(visible).toggleClass('required', required); } else if (!labelRequired.length) {
dependent.prop("required", required); label.append('<i class="label-required">' + gettext('required') + '</i>')
}
}
for (var k in dependents) dependents[k].prop('disabled', dependentsDisabled.includes(k))
loader.hide()
}
const label = dependent.closest(".form-group").find("label"); const update = function (ev) {
const labelRequired = label.find(".label-required"); dependents.state.prop('data-selected-value', dependents.state.val())
if (!required) { if (dependents.transmission_type) {
labelRequired.remove(); dependents.transmission_type.prop('data-selected-value', dependents.transmission_type.val())
} else if (!labelRequired.length) { }
label.append('<i class="label-required">' + gettext('required') + '</i>')
}
}
for (var k in dependents) dependents[k].prop("disabled", dependentsDisabled.includes(k));
loader.hide();
}
const update = function (ev) { for (let k in dependents) dependents[k].prop('disabled', true)
dependents.state.prop("data-selected-value", dependents.state.val()); loader.show()
if (dependents.transmission_type) { let url = new URL(baseUrl, location.href)
dependents.transmission_type.prop("data-selected-value", dependents.transmission_type.val()); // Address depends on all annotated fields
} form.find('[data-trigger-address-info]').each(function () {
// Remove prefix of the form to get actual field name
if (($(this).attr('type') === 'radio' || $(this).attr('type') === 'checkbox') && !$(this).prop('checked')) {
return
}
url.searchParams.append(cleanName($(this).attr('name')), $(this).val())
})
if (dependents.transmission_type) {
url.searchParams.append('transmission_type_required', !dependents.transmission_type.find('option[value=\'-\']').length)
}
for (var k in dependents) dependents[k].prop("disabled", true); if (xhr && url in responseCache) {
loader.show(); if (responseCache[url] == xhr) {
var url = new URL(baseUrl, location.href); // already requested this, but XHR is still running and will resolve promise
// Address depends on all annotated fields // only re-resolve promise for JSON-data in responseCache[url]
form.find("[data-trigger-address-info]").each(function () { return
// Remove prefix of the form to get actual field name } else {
if (($(this).attr("type") === "radio" || $(this).attr("type") === "checkbox") && !$(this).prop("checked")) { // abort current xhr as it is not the one we want
return // aborting deletes responseCache[url] but async
} xhr.abort()
url.searchParams.append(cleanName($(this).attr("name")), $(this).val()); }
}) }
if (dependents.transmission_type) {
url.searchParams.append("transmission_type_required", !dependents.transmission_type.find("option[value='-']").length);
}
if (xhr && url in responseCache) { if (!(url in responseCache)) {
if (responseCache[url] == xhr) { responseCache[url] = xhr = $.ajax({
// already requested this, but XHR is still running and will resolve promise dataType: 'json',
// only re-resolve promise for JSON-data in responseCache[url] url: url,
return; timeout: 3000,
} else { })
// abort current xhr as it is not the one we want }
// aborting deletes responseCache[url] but async
xhr.abort();
}
}
if (!(url in responseCache)) { Promise.resolve(responseCache[url]).then(function (data) {
responseCache[url] = xhr = $.ajax({ responseCache[url] = data
dataType: "json", update_form(data)
url: url, }).catch(function () {
timeout: 3000, delete responseCache[url]
}); // In case of errors, show everything and require nothing, we can still handle errors in backend
} for (let k in dependents) {
const dependent = dependents[k],
visible = true,
required = false
Promise.resolve(responseCache[url]).then(function (data) { dependent.closest('.form-group').toggle(visible).toggleClass('required', required)
responseCache[url] = data; dependent.prop('required', required).prop('disabled', dependentsDisabled.includes(k))
update_form(data); }
}).catch(function () { }).finally(function () {
delete responseCache[url]; loader.hide()
// In case of errors, show everything and require nothing, we can still handle errors in backend })
for (var k in dependents) { }
const dependent = dependents[k], update()
visible = true, dependencies.on('change', update)
required = false;
dependent.closest(".form-group").toggle(visible).toggleClass('required', required); if (dependents.vat_id && dependents.transmission_type && dependents.transmission_peppol_participant_id) {
dependent.prop("required", required).prop("disabled", dependentsDisabled.includes(k)); // In Belgium, the VAT ID is built from "BE" + the company ID. The Peppol ID also needs to be built
} // from the company ID with ID scheme 0208. We can save users some knowing and typing by filling this in!
}).finally(function () { if (!dependents.transmission_peppol_participant_id.val()) {
loader.hide(); const fill_peppol_id = function () {
}); const vatId = dependents.vat_id.val()
}; if (vatId && vatId.startsWith('BE') && dependents.transmission_type.val() === 'peppol') {
update(); dependents.transmission_peppol_participant_id.val('0208:' + vatId.substring(2).replaceAll('.', ''))
dependencies.on("change", update); }
}
if (dependents.vat_id && dependents.transmission_type && dependents.transmission_peppol_participant_id) { dependents.vat_id.add(dependents.transmission_type).on('change', fill_peppol_id)
// In Belgium, the VAT ID is built from "BE" + the company ID. The Peppol ID also needs to be built dependents.transmission_peppol_participant_id.one('change', () => {
// from the company ID with ID scheme 0208. We can save users some knowing and typing by filling this in! dependents.vat_id.add(dependents.transmission_type).unbind('change', fill_peppol_id)
if (!dependents.transmission_peppol_participant_id.val()) { })
const fill_peppol_id = function () { }
const vatId = dependents.vat_id.val(); }
if (vatId && vatId.startsWith("BE") && dependents.transmission_type.val() === "peppol") { })
dependents.transmission_peppol_participant_id.val("0208:" + vatId.substring(2).replaceAll(".", "")) })
}
}
dependents.vat_id.add(dependents.transmission_type).on("change", fill_peppol_id);
dependents.transmission_peppol_participant_id.one("change", () => {
dependents.vat_id.add(dependents.transmission_type).unbind("change", fill_peppol_id)
});
}
}
});
});
@@ -1,10 +1,10 @@
var check = function () { let check = function () {
$.getJSON(location.href + '&ajax=1', function (data, status) { $.getJSON(location.href + '&ajax=1', function (data, _status) {
if (data.redirect) { if (data.redirect) {
location.href = data.redirect; location.href = data.redirect
} else { } else {
window.setTimeout(check, 500); window.setTimeout(check, 500)
} }
}); })
} }
window.setTimeout(check, 500); window.setTimeout(check, 500)
+328 -330
View File
@@ -1,359 +1,357 @@
/*global $, gettext */ /* global gettext */
var async_task_id = null; let async_task_id = null
var async_task_timeout = null; let async_task_timeout = null
var async_task_check_url = null; let async_task_check_url = null
var async_task_old_url = null; let async_task_old_url = null
var async_task_is_download = false; let async_task_is_download = false
var async_task_is_long = false; let async_task_is_long = false
var async_task_dont_redirect = false; let async_task_dont_redirect = false
var async_task_status_messages = { let async_task_status_messages = {
// These are functions in order to be lazily evaluated after the gettext file is loaded // These are functions in order to be lazily evaluated after the gettext file is loaded
long_task_started: () => gettext( long_task_started: () => gettext(
'Your request is currently being processed. Depending on the size of your event, this might take up to ' + 'Your request is currently being processed. Depending on the size of your event, this might take up to '
'a few minutes.' + 'a few minutes.'
), ),
long_task_pending: () => gettext( long_task_pending: () => gettext(
'Your request has been queued on the server and will soon be ' + 'Your request has been queued on the server and will soon be '
'processed.' + 'processed.'
), ),
short_task: () => gettext( short_task: () => gettext(
'Your request arrived on the server but we still wait for it to be ' + 'Your request arrived on the server but we still wait for it to be '
'processed. If this takes longer than two minutes, please contact us or go ' + + 'processed. If this takes longer than two minutes, please contact us or go '
'back in your browser and try again.' + 'back in your browser and try again.'
) )
};
function async_task_schedule_check(context, timeout) {
"use strict";
async_task_timeout = window.setTimeout(function() {
$.ajax(
{
'type': 'GET',
'url': async_task_check_url,
'success': async_task_check_callback,
'error': async_task_check_error,
'context': context,
'dataType': 'json'
}
);
}, timeout);
} }
function async_task_on_success(data) { function async_task_schedule_check (context, timeout) {
"use strict"; 'use strict'
if ((async_task_is_download && data.success) || async_task_dont_redirect) { async_task_timeout = window.setTimeout(function () {
waitingDialog.hide(); $.ajax(
if (location.href.indexOf("async_id") !== -1) { {
history.replaceState({}, "pretix", async_task_old_url); type: 'GET',
} url: async_task_check_url,
} success: async_task_check_callback,
if (!async_task_dont_redirect) { error: async_task_check_error,
$(window).one("pageshow", function (e) { context: context,
// hide waitingDialog when using browser's history back dataType: 'json'
waitingDialog.hide(); }
}); )
if (async_task_is_download && window.self !== window.top) { }, timeout)
// if in an iframe, force to download an async_task_is_download
// e.g. pretix-reseller embeds order-page in iframe, which would cause ticket-PDFs to be displayed inline
var a = document.createElement("a");
a.href = data.redirect;
a.download = "";
a.target = "_blank";
a.click();
} else {
location.href = data.redirect;
}
}
$(this).trigger('pretix:async-task-success', data);
} }
function async_task_check_callback(data, textStatus, jqXHR) { function async_task_on_success (data) {
"use strict"; 'use strict'
if (data.ready && data.redirect) { if ((async_task_is_download && data.success) || async_task_dont_redirect) {
async_task_on_success.call(this, data); waitingDialog.hide()
return; if (location.href.indexOf('async_id') !== -1) {
} history.replaceState({}, 'pretix', async_task_old_url)
}
if (typeof data.percentage === "number") { }
waitingDialog.setProgress(data.percentage); if (!async_task_dont_redirect) {
} $(window).one('pageshow', function (e) {
if (typeof data.steps === "object" && Array.isArray(data.steps)) { // hide waitingDialog when using browser's history back
waitingDialog.setSteps(data.steps); waitingDialog.hide()
} })
async_task_schedule_check(this, 250); if (async_task_is_download && window.self !== window.top) {
// if in an iframe, force to download an async_task_is_download
async_task_update_status(data); // e.g. pretix-reseller embeds order-page in iframe, which would cause ticket-PDFs to be displayed inline
let a = document.createElement('a')
a.href = data.redirect
a.download = ''
a.target = '_blank'
a.click()
} else {
location.href = data.redirect
}
}
$(this).trigger('pretix:async-task-success', data)
} }
function async_task_update_status(data) { function async_task_check_callback (data, textStatus, jqXHR) {
if (async_task_is_long) { 'use strict'
if (data.started) { if (data.ready && data.redirect) {
waitingDialog.setStatus(async_task_status_messages.long_task_started()); async_task_on_success.call(this, data)
} else { return
waitingDialog.setStatus(async_task_status_messages.long_task_pending()); }
}
} else { if (typeof data.percentage === 'number') {
waitingDialog.setStatus(async_task_status_messages.short_task()); waitingDialog.setProgress(data.percentage)
} }
if (typeof data.steps === 'object' && Array.isArray(data.steps)) {
waitingDialog.setSteps(data.steps)
}
async_task_schedule_check(this, 250)
async_task_update_status(data)
} }
function async_task_replace_page(target, new_html) { function async_task_update_status (data) {
"use strict"; if (async_task_is_long) {
waitingDialog.hide(); if (data.started) {
$(target).html(new_html); waitingDialog.setStatus(async_task_status_messages.long_task_started())
setup_basics($(target)); } else {
form_handlers($(target)); waitingDialog.setStatus(async_task_status_messages.long_task_pending())
setup_collapsible_details($(target)); }
window.setTimeout(function () { $(window).scrollTop(0) }, 200) } else {
$(document).trigger("pretix:bind-forms"); waitingDialog.setStatus(async_task_status_messages.short_task())
}
} }
function async_task_check_error(jqXHR, textStatus, errorThrown) { function async_task_replace_page (target, new_html) {
"use strict"; 'use strict'
var respdom = $(jqXHR.responseText); waitingDialog.hide()
var c = respdom.filter('.container'); $(target).html(new_html)
if (jqXHR.status === 401 && jqXHR.getResponseHeader("X-Login-Url")) { setup_basics($(target))
window.location = jqXHR.getResponseHeader("X-Login-Url") + "?next=" + encodeURIComponent(location.pathname + location.search + location.hash); form_handlers($(target))
return; setup_collapsible_details($(target))
} window.setTimeout(function () { $(window).scrollTop(0) }, 200)
if (respdom.filter('form') && (respdom.filter('.has-error') || respdom.filter('.alert-danger'))) { $(document).trigger('pretix:bind-forms')
// This is a failed form validation, let's just use it
$("body").data('ajaxing', false);
async_task_replace_page("body", jqXHR.responseText.substring(
jqXHR.responseText.indexOf("<body"),
jqXHR.responseText.indexOf("</body")
));
} else if (c.length > 0) {
// This is some kind of 500/404/403 page, show it in an overlay
$("body").data('ajaxing', false);
waitingDialog.hide();
if (location.href.indexOf("async_id") !== -1) {
history.replaceState({}, "pretix", async_task_old_url);
}
ajaxErrDialog.show(c.first().html());
} else {
if (jqXHR.status >= 400 && jqXHR.status < 500) {
$("body").data('ajaxing', false);
waitingDialog.hide();
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status));
} else {
// 500 can be an application error or overload in some cases :(
waitingDialog.setStatus(gettext('We currently cannot reach the server, but we keep trying.' +
' Last error code: {code}').replace(/\{code\}/, jqXHR.status));
async_task_schedule_check(this, 5000);
}
}
} }
function async_task_callback(data, jqXHR, status) { function async_task_check_error (jqXHR, textStatus, errorThrown) {
"use strict"; 'use strict'
$("body").data('ajaxing', false); let respdom = $(jqXHR.responseText)
if (data.redirect) { let c = respdom.filter('.container')
async_task_on_success.call(this, data); if (jqXHR.status === 401 && jqXHR.getResponseHeader('X-Login-Url')) {
return; window.location = jqXHR.getResponseHeader('X-Login-Url') + '?next=' + encodeURIComponent(location.pathname + location.search + location.hash)
} return
var check_url = new URL(data.check_url, window.location); }
if (async_task_dont_redirect) { if (respdom.filter('form') && (respdom.filter('.has-error') || respdom.filter('.alert-danger'))) {
check_url.searchParams.set('ajax_dont_redirect', '1'); // This is a failed form validation, let's just use it
} $('body').data('ajaxing', false)
async_task_id = data.async_id; async_task_replace_page('body', jqXHR.responseText.substring(
async_task_check_url = check_url.toString(); jqXHR.responseText.indexOf('<body'),
async_task_schedule_check(this, 100); jqXHR.responseText.indexOf('</body')
))
async_task_update_status(data); } else if (c.length > 0) {
// This is some kind of 500/404/403 page, show it in an overlay
if (location.href.indexOf("async_id") === -1) { $('body').data('ajaxing', false)
history.pushState({}, "Waiting", async_task_check_url.replace(/ajax=1/, '')); waitingDialog.hide()
} if (location.href.indexOf('async_id') !== -1) {
history.replaceState({}, 'pretix', async_task_old_url)
}
ajaxErrDialog.show(c.first().html())
} else {
if (jqXHR.status >= 400 && jqXHR.status < 500) {
$('body').data('ajaxing', false)
waitingDialog.hide()
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status))
} else {
// 500 can be an application error or overload in some cases :(
waitingDialog.setStatus(gettext('We currently cannot reach the server, but we keep trying.'
+ ' Last error code: {code}').replace(/\{code\}/, jqXHR.status))
async_task_schedule_check(this, 5000)
}
}
} }
function async_task_error(jqXHR, textStatus, errorThrown) { function async_task_callback (data, jqXHR, status) {
"use strict"; 'use strict'
$("body").data('ajaxing', false); $('body').data('ajaxing', false)
if (jqXHR.status === 401 && jqXHR.getResponseHeader("X-Login-Url")) { if (data.redirect) {
window.location = jqXHR.getResponseHeader("X-Login-Url") + "?next=" + encodeURIComponent(location.pathname + location.search + location.hash); async_task_on_success.call(this, data)
return; return
} }
waitingDialog.hide(); let check_url = new URL(data.check_url, window.location)
if (textStatus === "timeout") { if (async_task_dont_redirect) {
alert(gettext("The request took too long. Please try again.")); check_url.searchParams.set('ajax_dont_redirect', '1')
} else if (jqXHR.responseText.indexOf('<html') > 0) { }
var respdom = $(jqXHR.responseText); async_task_id = data.async_id
var c = respdom.filter('.container'); async_task_check_url = check_url.toString()
if (respdom.filter('form') && (respdom.filter('.has-error') || respdom.filter('.alert-danger'))) { async_task_schedule_check(this, 100)
if (respdom.filter('#page-wrapper') && $('#page-wrapper').length) {
// This is a failed form validation, let's just use it
async_task_replace_page("#page-wrapper", respdom.find("#page-wrapper").html());
} else {
async_task_replace_page("body", jqXHR.responseText.substring(
jqXHR.responseText.indexOf("<body"),
jqXHR.responseText.indexOf("</body")
));
document.dispatchEvent(new Event("pretix:async-task-error"))
} async_task_update_status(data)
} else if (c.length > 0) { if (location.href.indexOf('async_id') === -1) {
// This is some kind of 500/404/403 page, show it in an overlay history.pushState({}, 'Waiting', async_task_check_url.replace(/ajax=1/, ''))
ajaxErrDialog.show(c.first().html()); }
} else { }
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status));
} function async_task_error (jqXHR, textStatus, errorThrown) {
} else { 'use strict'
if (jqXHR.status >= 400 && jqXHR.status < 500) { $('body').data('ajaxing', false)
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status)); if (jqXHR.status === 401 && jqXHR.getResponseHeader('X-Login-Url')) {
} else { window.location = jqXHR.getResponseHeader('X-Login-Url') + '?next=' + encodeURIComponent(location.pathname + location.search + location.hash)
alert(gettext('We currently cannot reach the server. Please try again. ' + return
'Error code: {code}').replace(/\{code\}/, jqXHR.status)); }
} waitingDialog.hide()
} if (textStatus === 'timeout') {
alert(gettext('The request took too long. Please try again.'))
} else if (jqXHR.responseText.indexOf('<html') > 0) {
let respdom = $(jqXHR.responseText)
let c = respdom.filter('.container')
if (respdom.filter('form') && (respdom.filter('.has-error') || respdom.filter('.alert-danger'))) {
if (respdom.filter('#page-wrapper') && $('#page-wrapper').length) {
// This is a failed form validation, let's just use it
async_task_replace_page('#page-wrapper', respdom.find('#page-wrapper').html())
} else {
async_task_replace_page('body', jqXHR.responseText.substring(
jqXHR.responseText.indexOf('<body'),
jqXHR.responseText.indexOf('</body')
))
document.dispatchEvent(new Event('pretix:async-task-error'))
}
} else if (c.length > 0) {
// This is some kind of 500/404/403 page, show it in an overlay
ajaxErrDialog.show(c.first().html())
} else {
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status))
}
} else {
if (jqXHR.status >= 400 && jqXHR.status < 500) {
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status))
} else {
alert(gettext('We currently cannot reach the server. Please try again. '
+ 'Error code: {code}').replace(/\{code\}/, jqXHR.status))
}
}
} }
$(function () { $(function () {
"use strict"; 'use strict'
$("body").on('submit', 'form[data-asynctask]', function (e) { $('body').on('submit', 'form[data-asynctask]', function (e) {
// Not supported on IE, may lead to wrong results, but we don't support IE in the backend anymore // Not supported on IE, may lead to wrong results, but we don't support IE in the backend anymore
var submitter = e.originalEvent ? e.originalEvent.submitter : null; let submitter = e.originalEvent ? e.originalEvent.submitter : null
if (submitter && submitter.hasAttribute("data-no-asynctask")) { if (submitter && submitter.hasAttribute('data-no-asynctask')) {
return; return
} }
e.preventDefault(); e.preventDefault()
$(this).removeClass("dirty"); // Avoid problems with are-you-sure.js $(this).removeClass('dirty') // Avoid problems with are-you-sure.js
if ($("body").data('ajaxing')) { if ($('body').data('ajaxing')) {
return; return
} }
async_task_id = null; async_task_id = null
async_task_is_download = $(this).is("[data-asynctask-download]"); async_task_is_download = $(this).is('[data-asynctask-download]')
async_task_dont_redirect = $(this).is("[data-asynctask-no-redirect]"); async_task_dont_redirect = $(this).is('[data-asynctask-no-redirect]')
async_task_is_long = $(this).is("[data-asynctask-long]"); async_task_is_long = $(this).is('[data-asynctask-long]')
async_task_old_url = location.href; async_task_old_url = location.href
$("body").data('ajaxing', true); $('body').data('ajaxing', true)
waitingDialog.show( waitingDialog.show(
$(this).attr("data-asynctask-headline") || gettext('We are processing your request …'), $(this).attr('data-asynctask-headline') || gettext('We are processing your request …'),
$(this).attr("data-asynctask-text") || '', $(this).attr('data-asynctask-text') || '',
gettext( gettext(
'We are currently sending your request to the server. If this takes longer ' + 'We are currently sending your request to the server. If this takes longer '
'than one minute, please check your internet connection and then reload ' + + 'than one minute, please check your internet connection and then reload '
'this page and try again.' + 'this page and try again.'
) )
); )
var action = this.action; let action = this.action
var formData = new FormData(this); let formData = new FormData(this)
formData.append('ajax', '1'); formData.append('ajax', '1')
if (async_task_dont_redirect) { if (async_task_dont_redirect) {
formData.append('ajax_dont_redirect', '1'); formData.append('ajax_dont_redirect', '1')
} }
if (submitter && submitter.name) { if (submitter && submitter.name) {
formData.append(submitter.name, submitter.value); formData.append(submitter.name, submitter.value)
} }
if (submitter && submitter.getAttribute("formaction")) { if (submitter && submitter.getAttribute('formaction')) {
action = submitter.getAttribute("formaction"); action = submitter.getAttribute('formaction')
} }
$.ajax( $.ajax(
{ {
'type': 'POST', type: 'POST',
'url': action, url: action,
'data': formData, data: formData,
processData: false, processData: false,
contentType: false, contentType: false,
'success': async_task_callback, success: async_task_callback,
'error': async_task_error, error: async_task_error,
'context': this, context: this,
'dataType': 'json', dataType: 'json',
'timeout': 60000, timeout: 60000,
} }
); )
}); })
window.addEventListener("pageshow", function (evt) { window.addEventListener('pageshow', function (evt) {
// In Safari, if you submit an async task, then get redirected, then go back, // In Safari, if you submit an async task, then get redirected, then go back,
// Safari won't reload the HTML from disk cache but instead reuse the DOM of the // Safari won't reload the HTML from disk cache but instead reuse the DOM of the
// previous request, thus not clearing the "loading" state. // previous request, thus not clearing the "loading" state.
if (evt.persisted && $("body").hasClass("loading")) { if (evt.persisted && $('body').hasClass('loading')) {
setTimeout(function () { setTimeout(function () {
window.location.reload(); window.location.reload()
}, 10); }, 10)
} }
}, false); }, false)
$("#ajaxerr").on("click", ".ajaxerr-close", ajaxErrDialog.hide); $('#ajaxerr').on('click', '.ajaxerr-close', ajaxErrDialog.hide)
$("#loadingmodal").on("cancel", function() { $('#loadingmodal').on('cancel', function () {
return false; return false
}); })
$("#loadingmodal").prop("closedBy", "none"); $('#loadingmodal').prop('closedBy', 'none')
}); })
var waitingDialog = { var waitingDialog = {
show: function (title, text, status) { show: function (title, text, status) {
"use strict"; 'use strict'
this.setTitle(title); this.setTitle(title)
this.setText(text); this.setText(text)
this.setStatus(status || gettext('If this takes longer than a few minutes, please contact us.')); this.setStatus(status || gettext('If this takes longer than a few minutes, please contact us.'))
this.setProgress(null); this.setProgress(null)
this.setSteps(null); this.setSteps(null)
document.getElementById("loadingmodal").showModal(); document.getElementById('loadingmodal').showModal()
}, },
hide: function () { hide: function () {
"use strict"; 'use strict'
document.getElementById("loadingmodal").close(); document.getElementById('loadingmodal').close()
}, },
setTitle: function(title) { setTitle: function (title) {
$("#loadingmodal .modal-card-title").text(title); $('#loadingmodal .modal-card-title').text(title)
}, },
setStatus: function(statusText) { setStatus: function (statusText) {
$("#loadingmodal p.status").text(statusText); $('#loadingmodal p.status').text(statusText)
}, },
setText: function(text) { setText: function (text) {
if (text) if (text)
$("#loadingmodal .modal-card-description").text(text).show(); $('#loadingmodal .modal-card-description').text(text).show()
else else
$("#loadingmodal .modal-card-description").hide(); $('#loadingmodal .modal-card-description').hide()
}, },
setProgress: function(percentage) { setProgress: function (percentage) {
if (typeof percentage === 'number') { if (typeof percentage === 'number') {
$("#loadingmodal .progress").show(); $('#loadingmodal .progress').show()
$("#loadingmodal .progress .progress-bar").css("width", percentage + "%"); $('#loadingmodal .progress .progress-bar').css('width', percentage + '%')
} else { } else {
$("#loadingmodal .progress").hide(); $('#loadingmodal .progress').hide()
} }
}, },
setSteps: function(steps) { setSteps: function (steps) {
var $steps = $("#loadingmodal .steps"); let $steps = $('#loadingmodal .steps')
if (steps) { if (steps) {
$steps.html("").show() $steps.html('').show()
for (var step of steps) { for (let step of steps) {
$steps.append( $steps.append(
$("<span>").addClass("fa fa-fw") $('<span>').addClass('fa fa-fw')
.toggleClass("fa-check text-success", step.done) .toggleClass('fa-check text-success', step.done)
.toggleClass("fa-cog fa-spin text-muted", !step.done) .toggleClass('fa-cog fa-spin text-muted', !step.done)
).append( ).append(
$("<span>").text(step.label) $('<span>').text(step.label)
).append( ).append(
$("<br>") $('<br>')
) )
} }
} else { } else {
$steps.hide(); $steps.hide()
} }
} }
}; }
var ajaxErrDialog = { var ajaxErrDialog = {
show: function (c) { show: function (c) {
"use strict"; 'use strict'
$("#ajaxerr").html(c); $('#ajaxerr').html(c)
$("#ajaxerr .links").html("<a class='btn btn-default ajaxerr-close'>" $('#ajaxerr .links').html('<a class=\'btn btn-default ajaxerr-close\'>'
+ gettext("Close message") + "</a>"); + gettext('Close message') + '</a>')
$("body").addClass("ajaxerr has-modal-dialog"); $('body').addClass('ajaxerr has-modal-dialog')
$("#ajaxerr").prop("hidden", false); $('#ajaxerr').prop('hidden', false)
}, },
hide: function () { hide: function () {
"use strict"; 'use strict'
$("body").removeClass("ajaxerr has-modal-dialog"); $('body').removeClass('ajaxerr has-modal-dialog')
$("#ajaxerr").prop("hidden", true); $('#ajaxerr').prop('hidden', true)
}, },
}; }
@@ -1,7 +1,7 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('a[href^="mailto:"]').forEach(function(link) { document.querySelectorAll('a[href^="mailto:"]').forEach(function (link) {
// Replace [at] with @ and the [dot] with . in both the href and the displayed text (if needed) // Replace [at] with @ and the [dot] with . in both the href and the displayed text (if needed)
link.href = link.href.replace('[at]', '@').replace('[dot]', '.'); link.href = link.href.replace('[at]', '@').replace('[dot]', '.')
link.textContent = link.textContent.replace('[at]', '@').replace('[dot]', '.'); link.textContent = link.textContent.replace('[at]', '@').replace('[dot]', '.')
}); })
}); })
+137 -140
View File
@@ -1,152 +1,149 @@
/*global $ */
setup_collapsible_details = function (el) { setup_collapsible_details = function (el) {
el.find('.sneak-peek-trigger').each(function () {
let trigger = this
let button = this.querySelector('button')
let content = document.getElementById(button.getAttribute('aria-controls'))
if (content.scrollHeight < 200) {
trigger.remove()
content.classList.remove('sneak-peek-content')
return
}
content.setAttribute('aria-hidden', 'true')
content.setAttribute('inert', true)
button.setAttribute('aria-expanded', 'false')
button.addEventListener('click', function (e) {
button.setAttribute('aria-expanded', 'true')
content.setAttribute('aria-hidden', 'false')
content.removeAttribute('inert')
el.find('.sneak-peek-trigger').each(function() { content.addEventListener('transitionend', function () {
var trigger = this; content.classList.remove('sneak-peek-content')
var button = this.querySelector('button'); content.style.removeProperty('height')
var content = document.getElementById(button.getAttribute('aria-controls')); // we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility
if (content.scrollHeight < 200) { trigger.classList.add('sr-only')
trigger.remove(); }, { once: true })
content.classList.remove('sneak-peek-content'); content.style.height = content.scrollHeight + 'px'
return;
}
content.setAttribute('aria-hidden', 'true');
content.setAttribute('inert', true);
button.setAttribute('aria-expanded', 'false');
button.addEventListener('click', function (e) {
button.setAttribute('aria-expanded', 'true');
content.setAttribute('aria-hidden', 'false');
content.removeAttribute('inert');
content.addEventListener('transitionend', function() { button.addEventListener('click', function (e) {
content.classList.remove('sneak-peek-content'); // this will be called by screenreader users if they kept focus on the button after expanding
content.style.removeProperty('height'); // we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility
// we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility let expanded = button.getAttribute('aria-expanded') == 'true'
trigger.classList.add('sr-only'); button.setAttribute('aria-expanded', !expanded)
}, {once: true}); content.setAttribute('aria-hidden', expanded)
content.style.height = content.scrollHeight + 'px'; })
button.addEventListener('blur', function (e) {
// if content is visible and the user leaves the button, we can safely remove the trigger/button
if (button.getAttribute('aria-expanded') == 'true') {
trigger.remove()
}
})
}, { once: true })
button.addEventListener('click', function (e) { let container = this.closest('details.sneak-peek-container')
// this will be called by screenreader users if they kept focus on the button after expanding if (container) {
// we need to keep the trigger/button in the DOM to not irritate screenreaders toggling visibility function removeSneekPeakWhenClosed (e) {
var expanded = button.getAttribute('aria-expanded') == 'true'; if (e.newState == 'closed') {
button.setAttribute('aria-expanded', !expanded); container.removeEventListener('toggle', removeSneekPeakWhenClosed)
content.setAttribute('aria-hidden', expanded); trigger.remove()
}); content.removeAttribute('aria-hidden')
button.addEventListener('blur', function (e) { content.removeAttribute('inert')
// if content is visible and the user leaves the button, we can safely remove the trigger/button content.classList.remove('sneak-peek-content')
if (button.getAttribute('aria-expanded') == 'true') { }
trigger.remove(); }
} container.addEventListener('toggle', removeSneekPeakWhenClosed)
}); }
}, { once: true }); })
var container = this.closest('details.sneak-peek-container'); let isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'
if (container) { el.find('details summary').click(function (e) {
function removeSneekPeakWhenClosed(e) { if (this.tagName !== 'A' && $(e.target).closest('a').length > 0) {
if (e.newState == "closed") { return true
container.removeEventListener("toggle", removeSneekPeakWhenClosed); }
trigger.remove(); let $details = $(this).closest('details')
content.removeAttribute('aria-hidden'); let isOpen = $details.prop('open')
content.removeAttribute('inert'); let $detailsNotSummary = $details.children(':not(summary)')
content.classList.remove('sneak-peek-content'); if ($detailsNotSummary.is(':animated')) {
} e.preventDefault()
} return false
container.addEventListener("toggle", removeSneekPeakWhenClosed); }
} if (isOpen) {
}); $details.removeClass('details-open')
$detailsNotSummary.stop().show().slideUp(500, function () {
$details.prop('open', false)
})
} else {
$detailsNotSummary.stop().hide()
$details.prop('open', true)
$details.addClass('details-open')
$detailsNotSummary.slideDown()
}
e.preventDefault()
return false
}).keyup(function (event) {
if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
// Space or Enter is pressed — trigger the `click` event on the `summary` element
// Opera already seems to trigger the `click` event when Enter is pressed
event.preventDefault()
$(this).click()
}
})
var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; $('details').each(function () {
el.find("details summary").click(function (e) { let $details = $(this),
if (this.tagName !== "A" && $(e.target).closest("a").length > 0) { $detailsSummary = $('summary', $details).first(),
return true; $detailsNotSummary = $details.children(':not(summary)')
} $details.prop('open', typeof $details.attr('open') == 'string')
var $details = $(this).closest("details"); if (!$details.prop('open')) {
var isOpen = $details.prop("open"); if ($details.find('.has-error, .alert-danger').length) {
var $detailsNotSummary = $details.children(':not(summary)'); $details.addClass('details-open')
if ($detailsNotSummary.is(':animated')) { $details.prop('open', true)
e.preventDefault(); } else {
return false; $detailsNotSummary.hide()
} }
if (isOpen) { } else {
$details.removeClass("details-open"); $details.addClass('details-open')
$detailsNotSummary.stop().show().slideUp(500, function () { }
$details.prop("open", false); $detailsSummary.attr({
}); role: 'button',
} else { 'aria-controls': $details.attr('id')
$detailsNotSummary.stop().hide(); }).prop('tabIndex', 0).bind('selectstart dragstart mousedown', function () {
$details.prop("open", true); return false
$details.addClass("details-open"); })
$detailsNotSummary.slideDown(); })
}
e.preventDefault();
return false;
}).keyup(function (event) {
if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
// Space or Enter is pressed — trigger the `click` event on the `summary` element
// Opera already seems to trigger the `click` event when Enter is pressed
event.preventDefault();
$(this).click();
}
});
$('details').each(function () { el.find('article button[data-toggle=variations]').click(function (e) {
var $details = $(this), let $button = $(this)
$detailsSummary = $('summary', $details).first(), let $details = $button.closest('article')
$detailsNotSummary = $details.children(':not(summary)'); let $detailsNotSummary = $button.attr('aria-controls') ? $('#' + $button.attr('aria-controls')) : $('.variations', $details)
$details.prop('open', typeof $details.attr('open') == 'string'); let isOpen = !$detailsNotSummary.prop('hidden')
if (!$details.prop('open')) { if ($detailsNotSummary.is(':animated')) {
if ($details.find(".has-error, .alert-danger").length) { e.preventDefault()
$details.addClass("details-open"); return false
$details.prop('open', true); }
} else {
$detailsNotSummary.hide();
}
} else {
$details.addClass("details-open");
}
$detailsSummary.attr({
'role': 'button',
'aria-controls': $details.attr('id')
}).prop('tabIndex', 0).bind('selectstart dragstart mousedown', function () {
return false;
});
});
el.find("article button[data-toggle=variations]").click(function (e) { let altLabel = $button.attr('data-label-alt')
var $button = $(this); $button.attr('data-label-alt', $button.text().trim())
var $details = $button.closest("article"); $button.find('span').text(altLabel)
var $detailsNotSummary = $button.attr("aria-controls") ? $('#' + $button.attr("aria-controls")) : $(".variations", $details); $button.attr('aria-expanded', !isOpen)
var isOpen = !$detailsNotSummary.prop("hidden");
if ($detailsNotSummary.is(':animated')) {
e.preventDefault();
return false;
}
var altLabel = $button.attr("data-label-alt"); if (isOpen) {
$button.attr("data-label-alt", $button.text().trim()); $details.removeClass('details-open')
$button.find("span").text(altLabel); $detailsNotSummary.stop().show().slideUp(500, function () {
$button.attr("aria-expanded", !isOpen); $detailsNotSummary.prop('hidden', true)
})
if (isOpen) { } else {
$details.removeClass("details-open"); $detailsNotSummary.prop('hidden', false).stop().hide()
$detailsNotSummary.stop().show().slideUp(500, function () { $details.addClass('details-open')
$detailsNotSummary.prop("hidden", true); $detailsNotSummary.slideDown()
}); }
} else { e.preventDefault()
$detailsNotSummary.prop("hidden", false).stop().hide(); return false
$details.addClass("details-open"); })
$detailsNotSummary.slideDown(); el.find('.variations-collapsed').prop('hidden', true)
} }
e.preventDefault();
return false;
});
el.find(".variations-collapsed").prop("hidden", true);
};
$(function () { $(function () {
"use strict"; 'use strict'
setup_collapsible_details($("body")); setup_collapsible_details($('body'))
}); })
+10 -10
View File
@@ -1,11 +1,11 @@
['DOMContentLoaded', 'pretix:async-task-error'].forEach(function (ev) { ['DOMContentLoaded', 'pretix:async-task-error'].forEach(function (ev) {
document.addEventListener(ev, function () { document.addEventListener(ev, function () {
document.querySelectorAll('#goback, #reload').forEach(function (element) { document.querySelectorAll('#goback, #reload').forEach(function (element) {
const regularLoad = ev === 'DOMContentLoaded' && element.id === 'goback'; const regularLoad = ev === 'DOMContentLoaded' && element.id === 'goback'
element.addEventListener('click', regularLoad element.addEventListener('click', regularLoad
? () => window.history.back() ? () => window.history.back()
: () => window.location.reload() : () => window.location.reload()
); )
}); })
}); })
}); })
@@ -1,3 +1,3 @@
// Attempt to auto-open page in new tab. Will be ignored by most browser's popup blockers anyways, though. // Attempt to auto-open page in new tab. Will be ignored by most browser's popup blockers anyways, though.
var url = JSON.parse(document.getElementById('framebreak-url').innerText) let url = JSON.parse(document.getElementById('framebreak-url').innerText)
window.open(url) window.open(url)
+21 -22
View File
@@ -1,30 +1,29 @@
// The actual gettext implementation is loaded asynchronously with the translation // The actual gettext implementation is loaded asynchronously with the translation
function gettext(msgid) { function gettext (msgid) {
if (typeof django !== 'undefined' && typeof django.gettext !== 'undefined') { if (typeof django !== 'undefined' && typeof django.gettext !== 'undefined') {
return django.gettext(msgid); return django.gettext(msgid)
} }
return msgid; return msgid
} }
function ngettext(singular, plural, count) { function ngettext (singular, plural, count) {
if (typeof django !== 'undefined' && typeof django.ngettext !== 'undefined') { if (typeof django !== 'undefined' && typeof django.ngettext !== 'undefined') {
return django.ngettext(singular, plural, count); return django.ngettext(singular, plural, count)
} }
return plural; return plural
} }
function pgettext(context, msgid) { function pgettext (context, msgid) {
if (typeof django !== 'undefined' && typeof django.pgettext !== 'undefined') { if (typeof django !== 'undefined' && typeof django.pgettext !== 'undefined') {
return django.pgettext(context, msgid); return django.pgettext(context, msgid)
} }
return msgid; return msgid
} }
function interpolate(fmt, object, named) { function interpolate (fmt, object, named) {
if (named) { if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); return fmt.replace(/%\(\w+\)s/g, function (match) { return String(obj[match.slice(2, -2)]) })
} else { } else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())}); return fmt.replace(/%s/g, function (match) { return String(obj.shift()) })
} }
} }
+19 -20
View File
@@ -1,25 +1,24 @@
function i18nstring_localize(o) { function i18nstring_localize (o) {
var locale = document.body.attributes['data-pretixlocale'].value let locale = document.body.attributes['data-pretixlocale'].value
var short_locale = locale.split('-')[0] let short_locale = locale.split('-')[0]
if (o[locale]) if (o[locale])
return o[locale] return o[locale]
if (o[short_locale]) if (o[short_locale])
return o[short_locale] return o[short_locale]
for (k of Object.keys(o)) { for (let k of Object.keys(o)) {
if (k.split('-')[0] === short_locale && o[k]) { if (k.split('-')[0] === short_locale && o[k]) {
return o[k] return o[k]
} }
} }
if (o['en']) if (o['en'])
return o['en'] return o['en']
for (k of Object.keys(o)) { for (let k of Object.keys(o)) {
if (o[k]) { if (o[k]) {
return o[k] return o[k]
} }
} }
} }
+1 -1
View File
@@ -32,7 +32,7 @@ $(document).on('pretix:bind-forms', () => {
} }
if (dirty) { if (dirty) {
beforeAfterSelect.dispatchEvent(new Event('change', {bubbles: true})) beforeAfterSelect.dispatchEvent(new Event('change', { bubbles: true }))
} }
} }
referenceSelect.addEventListener('change', updateBeforeOption) referenceSelect.addEventListener('change', updateBeforeOption)
@@ -1,8 +1,8 @@
var intId = window.setInterval(function () { const intId = window.setInterval(function () {
$.get(location.href + '?ajax=1', function (data, status) { $.get(location.href + '?ajax=1', function (data, _status) {
if (data === "1") { if (data === '1') {
window.clearInterval(intId); window.clearInterval(intId)
location.reload(); location.reload()
} }
}); })
}, 500); }, 500)
+17 -17
View File
@@ -1,21 +1,21 @@
var hiddenfield = document.querySelector("input[name=origin][type=hidden]"); let hiddenfield = document.querySelector('input[name=origin][type=hidden]')
if (hiddenfield) { if (hiddenfield) {
hiddenfield.value = window.location.origin hiddenfield.value = window.location.origin
} }
async function runCheck() { async function runCheck () {
if (document.getElementById("good_origin")) { if (document.getElementById('good_origin')) {
if (document.getElementById("good_origin").innerText.split('').reverse().join('') !== window.location.origin) { if (document.getElementById('good_origin').innerText.split('').reverse().join('') !== window.location.origin) {
const response = await fetch(document.getElementById("bad_origin_report_url").innerText.split('').reverse().join(''), { const _response = await fetch(document.getElementById('bad_origin_report_url').innerText.split('').reverse().join(''), {
method: "POST", method: 'POST',
mode: "cors", mode: 'cors',
referrerPolicy: "unsafe-url", referrerPolicy: 'unsafe-url',
headers: { headers: {
"Content-Type": "application/x-www-form-urlencoded", 'Content-Type': 'application/x-www-form-urlencoded',
}, },
body: "origin=" + window.location.origin, body: 'origin=' + window.location.origin,
}); })
} }
} }
} }
runCheck(); runCheck()
+95 -95
View File
@@ -1,118 +1,118 @@
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' let lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
;(function (exports) { ;(function (exports) {
'use strict' 'use strict'
var Arr = (typeof Uint8Array !== 'undefined') let Arr = (typeof Uint8Array !== 'undefined')
? Uint8Array ? Uint8Array
: Array : Array
var PLUS = '+'.charCodeAt(0) let PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0) let SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0) let NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0) let LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0) let UPPER = 'A'.charCodeAt(0)
var PLUS_URL_SAFE = '-'.charCodeAt(0) let PLUS_URL_SAFE = '-'.charCodeAt(0)
var SLASH_URL_SAFE = '_'.charCodeAt(0) let SLASH_URL_SAFE = '_'.charCodeAt(0)
function decode (elt) { function decode (elt) {
var code = elt.charCodeAt(0) let code = elt.charCodeAt(0)
if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+' if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+'
if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/' if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/'
if (code < NUMBER) return -1 // no match if (code < NUMBER) return -1 // no match
if (code < NUMBER + 10) return code - NUMBER + 26 + 26 if (code < NUMBER + 10) return code - NUMBER + 26 + 26
if (code < UPPER + 26) return code - UPPER if (code < UPPER + 26) return code - UPPER
if (code < LOWER + 26) return code - LOWER + 26 if (code < LOWER + 26) return code - LOWER + 26
} }
function b64ToByteArray (b64) { function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr let i, j, l, tmp, placeHolders, arr
if (b64.length % 4 > 0) { if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4') throw new Error('Invalid string. Length must be a multiple of 4')
} }
// the number of equal signs (place holders) // the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it // if there are two placeholders, than the two characters before it
// represent one byte // represent one byte
// if there is only one, then the three characters before it represent 2 bytes // if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice // this is just a cheap hack to not do indexOf twice
var len = b64.length let len = b64.length
placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0 placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0
// base64 is 4/3 + up to two characters of the original data // base64 is 4/3 + up to two characters of the original data
arr = new Arr(b64.length * 3 / 4 - placeHolders) arr = new Arr(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars // if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0 let L = 0
function push (v) { function push (v) {
arr[L++] = v arr[L++] = v
} }
for (i = 0, j = 0; i < l; i += 4, j += 3) { for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16) push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8) push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF) push(tmp & 0xFF)
} }
if (placeHolders === 2) { if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF) push(tmp & 0xFF)
} else if (placeHolders === 1) { } else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF) push((tmp >> 8) & 0xFF)
push(tmp & 0xFF) push(tmp & 0xFF)
} }
return arr return arr
} }
function uint8ToBase64 (uint8) { function uint8ToBase64 (uint8) {
var i let i
var extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes let extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes
var output = '' let output = ''
var temp, length let temp, length
function encode (num) { function encode (num) {
return lookup.charAt(num) return lookup.charAt(num)
} }
function tripletToBase64 (num) { function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
} }
// go through the array every three bytes, we'll deal with trailing stuff later // go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp) output += tripletToBase64(temp)
} }
// pad the end with zeros, but make sure to not forget the extra bytes // pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) { switch (extraBytes) {
case 1: case 1:
temp = uint8[uint8.length - 1] temp = uint8[uint8.length - 1]
output += encode(temp >> 2) output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F) output += encode((temp << 4) & 0x3F)
output += '==' output += '=='
break break
case 2: case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10) output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F) output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F) output += encode((temp << 2) & 0x3F)
output += '=' output += '='
break break
default: default:
break break
} }
return output return output
} }
exports.toByteArray = b64ToByteArray exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64 exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) }(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
+28 -28
View File
@@ -1,33 +1,33 @@
$(function() { /* global gettext */
$('.btn-clipboard').tooltip({ $(function () {
trigger: 'click', $('.btn-clipboard').tooltip({
placement: 'bottom' trigger: 'click',
}); placement: 'bottom'
})
function setTooltip(btn, message) { function setTooltip (btn, message) {
$(btn).tooltip('hide') $(btn).tooltip('hide')
.attr('data-original-title', message) .attr('data-original-title', message)
.tooltip('show'); .tooltip('show')
} }
function hideTooltip(btn) { function hideTooltip (btn) {
setTimeout(function() { setTimeout(function () {
$(btn).tooltip('hide'); $(btn).tooltip('hide')
}, 1000); }, 1000)
} }
var clipboard = new Clipboard('.btn-clipboard'); let clipboard = new Clipboard('.btn-clipboard')
clipboard.on('success', function(e) { clipboard.on('success', function (e) {
if (e.text.length > 0) { if (e.text.length > 0) {
setTooltip(e.trigger, gettext('Copied!')); setTooltip(e.trigger, gettext('Copied!'))
hideTooltip(e.trigger); hideTooltip(e.trigger)
} }
}); })
clipboard.on('error', function(e) {
setTooltip(e.trigger, gettext('Press Ctrl-C to copy!'));
hideTooltip(e.trigger);
});
});
clipboard.on('error', function (e) {
setTooltip(e.trigger, gettext('Press Ctrl-C to copy!'))
hideTooltip(e.trigger)
})
})
+61 -65
View File
@@ -8,79 +8,75 @@
* Under MIT License * Under MIT License
* Modified by Raphael Michel * Modified by Raphael Michel
*/ */
;(function($, window, document, undefined) { ;(function ($, window, document, undefined) {
let pluginName = 'metisMenu',
defaults = {
toggle: true,
}
var pluginName = "metisMenu", function Plugin (element, options) {
defaults = { this.element = $(element)
toggle: true, this.settings = $.extend({}, defaults, options)
}; this._defaults = defaults
this._name = pluginName
this.init()
}
function Plugin(element, options) { Plugin.prototype = {
this.element = $(element); init: function () {
this.settings = $.extend({}, defaults, options); let $this = this.element,
this._defaults = defaults; $toggle = this.settings.toggle,
this._name = pluginName; obj = this
this.init();
}
Plugin.prototype = { if (this.isIE() <= 9) {
init: function() { $this.find('li.active').has('ul').children('ul').collapse('show')
$this.find('li').not('.active').has('ul').children('ul').collapse('hide')
} else {
$this.find('li.active').has('ul').children('ul').addClass('collapse in')
$this.find('li').not('.active').has('ul').children('ul').addClass('collapse')
}
var $this = this.element, $this.find('li').has('ul').children('a.arrow').on('click' + '.' + pluginName, function (e) {
$toggle = this.settings.toggle, e.preventDefault()
obj = this; $(this).blur()
if (this.isIE() <= 9) { $(this).parent('li').toggleClass('active').children('ul').collapse('toggle')
$this.find("li.active").has("ul").children("ul").collapse("show");
$this.find("li").not(".active").has("ul").children("ul").collapse("hide");
} else {
$this.find("li.active").has("ul").children("ul").addClass("collapse in");
$this.find("li").not(".active").has("ul").children("ul").addClass("collapse");
}
$this.find("li").has("ul").children("a.arrow").on("click" + "." + pluginName, function(e) { if ($toggle) {
e.preventDefault(); $(this).parent('li').siblings().removeClass('active').children('ul.in').collapse('hide')
$(this).blur(); }
})
},
$(this).parent("li").toggleClass("active").children("ul").collapse("toggle"); isIE: function () { // https://gist.github.com/padolsey/527683
let undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i')
if ($toggle) { while (
$(this).parent("li").siblings().removeClass("active").children("ul.in").collapse("hide"); div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
} all[0]
) {
return v > 4 ? v : undef
}
},
}); remove: function () {
}, this.element.off('.' + pluginName)
this.element.removeData(pluginName)
}
isIE: function() { //https://gist.github.com/padolsey/527683 }
var undef,
v = 3,
div = document.createElement("div"),
all = div.getElementsByTagName("i");
while ( $.fn[pluginName] = function (options) {
div.innerHTML = "<!--[if gt IE " + (++v) + "]><i></i><![endif]-->", this.each(function () {
all[0] let el = $(this)
) { if (el.data(pluginName)) {
return v > 4 ? v : undef; el.data(pluginName).remove()
} }
}, el.data(pluginName, new Plugin(this, options))
})
remove: function() { return this
this.element.off("." + pluginName); }
this.element.removeData(pluginName); })(jQuery, window, document)
}
};
$.fn[pluginName] = function(options) {
this.each(function () {
var el = $(this);
if (el.data(pluginName)) {
el.data(pluginName).remove();
}
el.data(pluginName, new Plugin(this, options));
});
return this;
};
})(jQuery, window, document);
@@ -1,35 +1,34 @@
/*global $ */
/* /*
Based on https://github.com/BlackrockDigital/startbootstrap-sb-admin-2 Based on https://github.com/BlackrockDigital/startbootstrap-sb-admin-2
Copyright 2013-2016 Blackrock Digital LLC Copyright 2013-2016 Blackrock Digital LLC
MIT License MIT License
Modified by Raphael Michel Modified by Raphael Michel
*/ */
//Loads the correct sidebar on window load, // Loads the correct sidebar on window load,
//collapses the sidebar on window resize. // collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size // Sets the min-height of #page-wrapper to window size
$(window).bind("load resize", function () { $(window).bind('load resize', function () {
'use strict'; 'use strict'
var topOffset = 50, let topOffset = 50,
width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width
if (width < 768) { if (width < 768) {
$('div.navbar-collapse').addClass('collapse'); $('div.navbar-collapse').addClass('collapse')
topOffset = 100; // 2-row-menu topOffset = 100 // 2-row-menu
} else { } else {
$('div.navbar-collapse').removeClass('collapse'); $('div.navbar-collapse').removeClass('collapse')
} }
var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1; let height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1
height = height - topOffset; height = height - topOffset
if (height < 1) height = 1; if (height < 1) height = 1
if (height > topOffset) { if (height > topOffset) {
$("#page-wrapper").css("min-height", (height) + "px"); $('#page-wrapper').css('min-height', (height) + 'px')
} }
}); })
$(function () { $(function () {
'use strict'; 'use strict'
$('ul.nav ul.nav-second-level a.active').parent().parent().addClass('in').parent().addClass('active'); $('ul.nav ul.nav-second-level a.active').parent().parent().addClass('in').parent().addClass('active')
$('#side-menu').metisMenu({ $('#side-menu').metisMenu({
'toggle': false, toggle: false,
}); })
}); })
@@ -1 +1 @@
document.forms[0].submit(); document.forms[0].submit()
@@ -1,26 +1,25 @@
/*global $,gettext*/ /* global add_log_expand_handlers */
$(function () { $(function () {
if ($("div[data-lazy-id]").length == 0) { if ($('div[data-lazy-id]').length == 0) {
return; return
} }
$.getJSON("widgets.json" + ($("select[name='subevent']").val() ? "?subevent=" + $("select[name='subevent']").val() : ""), function (data) { $.getJSON('widgets.json' + ($('select[name=\'subevent\']').val() ? '?subevent=' + $('select[name=\'subevent\']').val() : ''), function (data) {
$.each(data.widgets, function (k, v) { $.each(data.widgets, function (_k, v) {
$("[data-lazy-id=" + v.lazy + "]").removeClass("widget-lazy-loading"); $('[data-lazy-id=' + v.lazy + ']').removeClass('widget-lazy-loading')
$("[data-lazy-id=" + v.lazy + "] .widget").html(v.content); $('[data-lazy-id=' + v.lazy + '] .widget').html(v.content)
}); })
}); })
}); })
$(function () { $(function () {
if ($("#logs_target").length == 0) { if ($('#logs_target').length == 0) {
return; return
} }
$.get("dashboard/partials/logs", function (data) { $.get('dashboard/partials/logs', function (data) {
$("#logs_target").html(data) $('#logs_target').html(data)
add_log_expand_handlers($("#logs_target")) add_log_expand_handlers($('#logs_target'))
}); })
$.get("dashboard/partials/warnings", function (data) { $.get('dashboard/partials/warnings', function (data) {
$("#warnings_loading").remove() $('#warnings_loading').remove()
$("#warnings_target").html(data) $('#warnings_target').html(data)
}); })
}); })
@@ -1,14 +1,12 @@
/*globals $, Morris, gettext, RRule, RRuleSet*/
$(function () { $(function () {
var update = function () { let update = function () {
$.getJSON(location.href + '?ajax=true', {}, function (data) { $.getJSON(location.href + '?ajax=true', {}, function (data) {
if (data.initialized) { if (data.initialized) {
location.reload(); location.reload()
} else { } else {
window.setTimeout(update, 500); window.setTimeout(update, 500)
} }
}); })
}; }
window.setTimeout(update, 500); window.setTimeout(update, 500)
}); })
@@ -1,91 +1,95 @@
/*global $, Sortable*/ /* global Sortable */
$(function () { $(function () {
const allContainers = $("[data-dnd-url]"); const allContainers = $('[data-dnd-url]')
function updateAllSortButtonStates() { function updateAllSortButtonStates () {
allContainers.each(function() { updateSortButtonState($(this)); }); allContainers.each(function () {
} updateSortButtonState($(this))
function updateSortButtonState(container) { })
var disabledUp = container.find(".sortable-up:disabled"), }
firstUp = container.find(">tr[data-dnd-id] .sortable-up").first(); function updateSortButtonState (container) {
if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) { let disabledUp = container.find('.sortable-up:disabled'),
disabledUp.prop("disabled", false); firstUp = container.find('>tr[data-dnd-id] .sortable-up').first()
firstUp.prop("disabled", true); if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) {
} disabledUp.prop('disabled', false)
firstUp.prop('disabled', true)
}
var disabledDown = container.find(".sortable-down:disabled"), let disabledDown = container.find('.sortable-down:disabled'),
lastDown = container.find(">tr[data-dnd-id] .sortable-down").last(); lastDown = container.find('>tr[data-dnd-id] .sortable-down').last()
if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) { if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) {
disabledDown.prop("disabled", false); disabledDown.prop('disabled', false)
lastDown.prop("disabled", true); lastDown.prop('disabled', true)
} }
} }
let didSort = false, lastClick = 0; let didSort = false, lastClick = 0
allContainers.each(function(){ allContainers.each(function () {
const container = $(this), const container = $(this),
url = container.data("dnd-url"), url = container.data('dnd-url'),
handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>'); handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>')
container.find(".dnd-container").append(handle); container.find('.dnd-container').append(handle)
if (!sessionStorage.dndShowMoveButtons) { if (!sessionStorage.dndShowMoveButtons) {
container.find(".sortable-up, .sortable-down").addClass("sr-only").on("click", function () { container.find('.sortable-up, .sortable-down').addClass('sr-only').on('click', function () {
sessionStorage.dndShowMoveButtons = 'true'; sessionStorage.dndShowMoveButtons = 'true'
}); })
} }
if (container.find("[data-dnd-id]").length < 2 && !container.data("dnd-group")) { if (container.find('[data-dnd-id]').length < 2 && !container.data('dnd-group')) {
handle.addClass("disabled"); handle.addClass('disabled')
return; return
} }
function maybeShowSortButtons() { function maybeShowSortButtons () {
if (Date.now() - lastClick < 3000) { if (Date.now() - lastClick < 3000) {
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").removeClass("sr-only"); $('[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down').removeClass('sr-only')
updateAllSortButtonStates(); updateAllSortButtonStates()
} }
lastClick = Date.now(); lastClick = Date.now()
} }
container.find(".dnd-sort-handle").on("mouseup", maybeShowSortButtons); container.find('.dnd-sort-handle').on('mouseup', maybeShowSortButtons)
const group = container.data("dnd-group"); const group = container.data('dnd-group')
const containers = group ? container.parent().find('[data-dnd-group="' + group + '"]') : container; const containers = group ? container.parent().find('[data-dnd-group="' + group + '"]') : container
Sortable.create(container.get(0), { Sortable.create(container.get(0), {
filter: ".sortable-disabled", filter: '.sortable-disabled',
handle: ".dnd-sort-handle", handle: '.dnd-sort-handle',
group: group, group: group,
onMove: function (evt) { onMove: function (evt) {
return evt.related.className.indexOf('sortable-disabled') === -1; return evt.related.className.indexOf('sortable-disabled') === -1
}, },
onStart: function (evt) { onStart: function () {
containers.addClass("sortable-dragarea"); containers.addClass('sortable-dragarea')
container.parent().addClass("sortable-sorting"); container.parent().addClass('sortable-sorting')
didSort = false; didSort = false
}, },
onEnd: function (evt) { onEnd: function () {
containers.removeClass("sortable-dragarea"); containers.removeClass('sortable-dragarea')
container.parent().removeClass("sortable-sorting"); container.parent().removeClass('sortable-sorting')
if (!didSort) { if (!didSort) {
maybeShowSortButtons(); maybeShowSortButtons()
} else { } else {
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").addClass("sr-only"); $('[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down').addClass('sr-only')
delete sessionStorage.dndShowMoveButtons; delete sessionStorage.dndShowMoveButtons
} }
}, },
onSort: function (evt){ onSort: function (evt) {
if (evt.target !== evt.to) return; if (evt.target !== evt.to) return
didSort = true; didSort = true
const ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; }); const ids = container.find('[data-dnd-id]').toArray().map(function (e) {
$.ajax( return e.dataset.dndId
{ })
'type': 'POST', $.ajax(
'url': url, {
'headers': {'X-CSRFToken': $("input[name=csrfmiddlewaretoken]").val()}, type: 'POST',
'data': JSON.stringify({ url: url,
ids: ids headers: { 'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val() },
}), data: JSON.stringify({
'contentType': "application/json", ids: ids
'timeout': 30000 }),
} contentType: 'application/json',
); timeout: 30000
} }
}); )
}); }
}); })
})
})
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,3 @@
$(function () { $(function () {
$("input, select, textarea").not(":disabled").focus(); $('input, select, textarea').not(':disabled').focus()
}); })
+179 -182
View File
@@ -1,201 +1,198 @@
/*globals $*/ /* globals $ */
$(document).on("pretix:bind-forms", function () { $(document).on('pretix:bind-forms', function () {
function cleanup(l) { function cleanup (l) {
return $.trim(l.replace(/\n/g, ", ")); return $.trim(l.replace(/\n/g, ', '))
} }
function combine($sel) { function combine ($sel) {
var parts = [ let parts = [
$sel.filter("[name*=street]").val(), $sel.filter('[name*=street]').val(),
$sel.filter("[name*=zipcode]").val(), $sel.filter('[name*=zipcode]').val(),
$sel.filter("[name*=city]").val(), $sel.filter('[name*=city]').val(),
$sel.filter("[name*=state]").val(), $sel.filter('[name*=state]').val(),
$sel.filter("[name*=country]").find("option:selected").text(), $sel.filter('[name*=country]').find('option:selected').text(),
$sel.filter("[name*=location]").val(), $sel.filter('[name*=location]').val(),
] ]
var res = ""; let res = ''
for (var val of parts) { for (let val of parts) {
if (val) { if (val) {
if (res) { if (res) {
res += ", " res += ', '
} }
res += val res += val
} }
} }
return cleanup(res) return cleanup(res)
} }
$(".geodata-section").each(function () { $('.geodata-section').each(function () {
// Geocoding // Geocoding
// detach notifications and append them to first label (should be from location) // detach notifications and append them to first label (should be from location)
var $notifications = $(".geodata-autoupdate", this).detach().appendTo($("label", this).first()); let $notifications = $('.geodata-autoupdate', this).detach().appendTo($('label', this).first())
var $lat = $("input[name$=geo_lat]", this).first(); let $lat = $('input[name$=geo_lat]', this).first()
var $lon = $("input[name$=geo_lon]", this).first(); let $lon = $('input[name$=geo_lon]', this).first()
var lat; let lat
var lon; let lon
var $updateButton = $("[data-action=update]", this); let $updateButton = $('[data-action=update]', this)
var $location; let $location
// The .geodata-section is expected to include either... // The .geodata-section is expected to include either...
// ... an English "location" field // ... an English "location" field
if ($("textarea[lang=en], input[lang=en]", this).length) { if ($('textarea[lang=en], input[lang=en]', this).length) {
$location = $("textarea[lang=en], input[lang=en], select", this).not("[name*=geo_]"); $location = $('textarea[lang=en], input[lang=en], select', this).not('[name*=geo_]')
} }
// ... a "location" field in any other language // ... a "location" field in any other language
if (!$location || !$location.length) { if (!$location || !$location.length) {
var lang = $("textarea, input[type=text]", this).not("[name*=geo_]").first().attr("lang"); let lang = $('textarea, input[type=text]', this).not('[name*=geo_]').first().attr('lang')
if (lang) { if (lang) {
$location = $("textarea[lang=" + lang + "], input[lang=" + lang + "], select", this); $location = $('textarea[lang=' + lang + '], input[lang=' + lang + '], select', this)
} }
} }
// ... or a set of fields like a full address form // ... or a set of fields like a full address form
if (!$location || !$location.length) { if (!$location || !$location.length) {
$location = $("textarea, input, select", this).not("[name*=geo_]"); $location = $('textarea, input, select', this).not('[name*=geo_]')
} }
if (!$lat.length || !$lon.length || !$location.length) { if (!$lat.length || !$lon.length || !$location.length) {
return; return
} }
var debounceLoad, debounceLatLonChange, delayUpdateDismissal; let debounceLoad, debounceLatLonChange, delayUpdateDismissal
var touched = $lat.val() !== ""; let touched = $lat.val() !== ''
var xhr; let xhr
var lastLocation = combine($location); let lastLocation = combine($location)
function load() { function load () {
window.clearTimeout(debounceLoad); window.clearTimeout(debounceLoad)
if (xhr) { if (xhr) {
xhr.abort(); xhr.abort()
xhr = null; xhr = null
} }
var q = combine($location); let q = combine($location)
if (q === "" || q === lastLocation) return; if (q === '' || q === lastLocation) return
lastLocation = q; lastLocation = q
$notifications.attr("data-notify", "loading"); $notifications.attr('data-notify', 'loading')
xhr = $.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) { xhr = $.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) {
if (!res.results || !res.results.length) { if (!res.results || !res.results.length) {
$notifications.attr("data-notify", "error"); $notifications.attr('data-notify', 'error')
return; return
} }
lat = res.results[0].lat; lat = res.results[0].lat
lon = res.results[0].lon; lon = res.results[0].lon
if ($lat.val() == lat && $lon.val() == lon) { if ($lat.val() == lat && $lon.val() == lon) {
$notifications.attr("data-notify", ""); $notifications.attr('data-notify', '')
} } else if (touched) {
else if (touched) { $notifications.attr('data-notify', 'confirm')
$notifications.attr("data-notify", "confirm"); } else {
} $notifications.attr('data-notify', '')
else { $lat.val(lat)
$notifications.attr("data-notify", ""); $lon.val(lon)
$lat.val(lat); center(13)
$lon.val(lon); }
center(13); })
} }
})
}
$lat.add($lon).change(function () { $lat.add($lon).change(function () {
if (this.value !== "") touched = true; if (this.value !== '') touched = true
center(13); center(13)
}).keyup(function () { }).keyup(function () {
window.clearTimeout(debounceLatLonChange); window.clearTimeout(debounceLatLonChange)
debounceLatLonChange = window.setTimeout(center, 300); debounceLatLonChange = window.setTimeout(center, 300)
}); })
$location.change(load); $location.change(load)
$location.keyup(function () { $location.keyup(function () {
window.clearTimeout(debounceLoad); window.clearTimeout(debounceLoad)
debounceLoad = window.setTimeout(load, 1000); debounceLoad = window.setTimeout(load, 1000)
if ($notifications.attr("data-notify") == "confirm" && lastLocation !== cleanup(this.value)) $notifications.attr("data-notify", ""); if ($notifications.attr('data-notify') == 'confirm' && lastLocation !== cleanup(this.value)) $notifications.attr('data-notify', '')
}); })
$updateButton.click(function() { $updateButton.click(function () {
$lat.val(lat); $lat.val(lat)
$lon.val(lon).trigger("change");// change-event is needed by bulk-edit $lon.val(lon).trigger('change')// change-event is needed by bulk-edit
touched = false; touched = false
center(13); center(13)
$notifications.attr("data-notify", "updated"); $notifications.attr('data-notify', 'updated')
delayUpdateDismissal = window.setTimeout(function() { delayUpdateDismissal = window.setTimeout(function () {
if ($notifications.attr("data-notify") == "updated") $notifications.attr("data-notify", ""); if ($notifications.attr('data-notify') == 'updated') $notifications.attr('data-notify', '')
}, 2500); }, 2500)
}); })
// Map // Map
var $grp = $(".geodata-group", this); let $grp = $('.geodata-group', this)
var tiles = $grp.attr("data-tiles"); let tiles = $grp.attr('data-tiles')
var attrib = $grp.attr("data-attrib"); let attrib = $grp.attr('data-attrib')
if (tiles) { if (tiles) {
var $map = $("<div>"); let $map = $('<div>')
$grp.append($("<div>").addClass("col-md-9 col-md-offset-3").append($map)); $grp.append($('<div>').addClass('col-md-9 col-md-offset-3').append($map))
var map = L.map($map.get(0)); let map = L.map($map.get(0))
L.tileLayer(tiles, { L.tileLayer(tiles, {
attribution: attrib, attribution: attrib,
maxZoom: 18, maxZoom: 18,
}).addTo(map); }).addTo(map)
function getpoint() { function getpoint () {
if ($lat.val() !== "" && $lon.val() !== "") { if ($lat.val() !== '' && $lon.val() !== '') {
var p = [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))]; let p = [parseFloat($lat.val().replace(',', '.')), parseFloat($lon.val().replace(',', '.'))]
// Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently // Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently
if (p[0] < -90) p[0] = -90 if (p[0] < -90) p[0] = -90
if (p[0] > 90) p[0] = 90 if (p[0] > 90) p[0] = 90
if (p[1] < -180) p[1] = -180 if (p[1] < -180) p[1] = -180
if (p[1] > 180) p[1] = 180 if (p[1] > 180) p[1] = 180
return p return p
} else { } else {
return [0.0, 0.0]; return [0.0, 0.0]
} }
} }
var marker = L.marker(getpoint(), { let marker = L.marker(getpoint(), {
draggable: 'true', draggable: 'true',
icon: L.icon({ icon: L.icon({
iconUrl: $grp.attr("data-icon"), iconUrl: $grp.attr('data-icon'),
shadowUrl: $grp.attr("data-shadow"), shadowUrl: $grp.attr('data-shadow'),
iconSize: [25, 41], iconSize: [25, 41],
iconAnchor: [12, 41], iconAnchor: [12, 41],
popupAnchor: [1, -34], popupAnchor: [1, -34],
tooltipAnchor: [16, -28], tooltipAnchor: [16, -28],
shadowSize: [41, 41] shadowSize: [41, 41]
}) })
}); })
marker.addTo(map); marker.addTo(map)
marker.on("dragend", function (event) { marker.on('dragend', function (event) {
var position = marker.getLatLng(); let position = marker.getLatLng()
marker.setLatLng(position, { marker.setLatLng(position, {
draggable: 'true' draggable: 'true'
}).bindPopup(position).update(); }).bindPopup(position).update()
$lat.val(position.lat.toFixed(7)); $lat.val(position.lat.toFixed(7))
$lon.val(position.lng.toFixed(7)); $lon.val(position.lng.toFixed(7))
touched = true; touched = true
center(null); center(null)
}); })
function center(zoom) { function center (zoom) {
if ($lat.val() !== "" && $lon.val() !== "") { if ($lat.val() !== '' && $lon.val() !== '') {
if (zoom) { if (zoom) {
map.setView(getpoint(), zoom); map.setView(getpoint(), zoom)
} else { } else {
map.panTo(getpoint()); map.panTo(getpoint())
} }
marker.setLatLng(getpoint(), { marker.setLatLng(getpoint(), {
draggable: 'true' draggable: 'true'
}).bindPopup(getpoint()).update(); }).bindPopup(getpoint()).update()
} else { } else {
map.fitWorld(); map.fitWorld()
} }
} }
center(13); center(13)
} else { } else {
function center(zoom) { function center (zoom) {
} }
} }
})
}); })
});
@@ -1,39 +1,39 @@
$(function () { $(function () {
hideDeselected(false); hideDeselected(false)
function hideDeselected(animate) { function hideDeselected (animate) {
var v = $("input[name='quota_option']:checked").val(), let v = $('input[name=\'quota_option\']:checked').val(),
fn = animate ? 'slideDown' : 'show'; fn = animate ? 'slideDown' : 'show'
if (v === "existing") { if (v === 'existing') {
hideAll(animate); hideAll(animate)
$("#existing-quota-group").children()[fn](); $('#existing-quota-group').children()[fn]()
} else if (v === "new") { } else if (v === 'new') {
hideAll(animate); hideAll(animate)
if ($("#id_quota_add_new_name").val() === "") { if ($('#id_quota_add_new_name').val() === '') {
$("#id_quota_add_new_name").val($("input[name^=name_]").first().val()); $('#id_quota_add_new_name').val($('input[name^=name_]').first().val())
} }
$("#new-quota-group").children()[fn](); $('#new-quota-group').children()[fn]()
} else { } else {
hideAll(animate); hideAll(animate)
} }
} }
function hideAll(animate) { function hideAll (animate) {
var fn = animate ? 'slideUp' : 'hide'; let fn = animate ? 'slideUp' : 'hide'
$("#new-quota-group").children()[fn](); $('#new-quota-group').children()[fn]()
$("#existing-quota-group").children()[fn](); $('#existing-quota-group').children()[fn]()
} }
$("input[name='quota_option']").on('change', $('input[name=\'quota_option\']').on('change',
function () { function () {
hideDeselected(true); hideDeselected(true)
} }
); )
function toggleblock() { function toggleblock () {
$("#new-quota-group").closest('fieldset').toggle(!$("[name=has_variations][value=on]").prop('checked')); $('#new-quota-group').closest('fieldset').toggle(!$('[name=has_variations][value=on]').prop('checked'))
} }
$("[name=has_variations]").change(toggleblock); $('[name=has_variations]').change(toggleblock)
toggleblock(); toggleblock()
}); })
+65 -65
View File
@@ -1,75 +1,75 @@
function preview_task_callback(data, jqXHR, status) { /* global gettext */
"use strict"; function preview_task_callback (data, _jqXHR, _status) {
if (data.item) { 'use strict'
$('#' + data.item + '_panel').data('ajaxing', false); if (data.item) {
for (var m in data.msgs){ $('#' + data.item + '_panel').data('ajaxing', false)
var target = $('div[for=' + data.item + '][lang=' + m +']'); for (let m in data.msgs) {
if (target.length === 1){ let target = $('div[for=' + data.item + '][lang=' + m + ']')
target.html(data.msgs[m]); if (target.length === 1) {
target.find('.placeholder').tooltip(); target.html(data.msgs[m])
} target.find('.placeholder').tooltip()
} }
} }
}
} }
function preview_task_error(item) { function preview_task_error (item) {
"use strict"; 'use strict'
return function(jqXHR, textStatus, errorThrown) { return function (jqXHR, textStatus, _errorThrown) {
$('#' + item + '_panel').data('ajaxing', false); $('#' + item + '_panel').data('ajaxing', false)
$('#' + item + '_preview div').text(gettext('An error has occurred.')); $('#' + item + '_preview div').text(gettext('An error has occurred.'))
if (textStatus === "timeout") { if (textStatus === 'timeout') {
alert(gettext("The request took too long. Please try again.")); alert(gettext('The request took too long. Please try again.'))
} else { } else {
if (jqXHR.status >= 400 && jqXHR.status < 500) { if (jqXHR.status >= 400 && jqXHR.status < 500) {
alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status)); alert(gettext('An error of type {code} occurred.').replace(/\{code\}/, jqXHR.status))
} else { } else {
alert(gettext('We currently cannot reach the server. Please try again. ' + alert(gettext('We currently cannot reach the server. Please try again. '
'Error code: {code}').replace(/\{code\}/, jqXHR.status)); + 'Error code: {code}').replace(/\{code\}/, jqXHR.status))
} }
} }
} }
} }
function mail_preview_setup($el) { function mail_preview_setup ($el) {
$el.find('.mail-preview .placeholder').tooltip(); $el.find('.mail-preview .placeholder').tooltip()
$el.find('a[type=preview]').on('click', function () { $el.find('a[type=preview]').on('click', function () {
var itemName = $(this).closest('.preview-panel').attr('for'); let itemName = $(this).closest('.preview-panel').attr('for')
if ($('#' + itemName + '_panel').data('ajaxing') || $(this).parent('.active').length !== 0) { if ($('#' + itemName + '_panel').data('ajaxing') || $(this).parent('.active').length !== 0) {
return; return
} }
// gathering data // gathering data
var parentForm = $(this).closest('form'); let parentForm = $(this).closest('form')
var previewUrl = $(parentForm).attr('mail-preview-url'); let previewUrl = $(parentForm).attr('mail-preview-url')
var token = $(parentForm).find('input[name=csrfmiddlewaretoken]').val(); let token = $(parentForm).find('input[name=csrfmiddlewaretoken]').val()
var dataString = 'item=' + itemName + '&csrfmiddlewaretoken=' + token; let dataString = 'item=' + itemName + '&csrfmiddlewaretoken=' + token
$('#' + itemName + '_edit textarea, #' + itemName + '_edit input').each(function () { $('#' + itemName + '_edit textarea, #' + itemName + '_edit input').each(function () {
dataString += '&' + $(this).serialize(); dataString += '&' + $(this).serialize()
}); })
// prepare for ajax // prepare for ajax
$('#' + itemName + '_panel').data('ajaxing', true); $('#' + itemName + '_panel').data('ajaxing', true)
$('#' + itemName + '_preview div').text(gettext('Generating messages …')); $('#' + itemName + '_preview div').text(gettext('Generating messages …'))
$.ajax( $.ajax(
{ {
'type': 'POST', type: 'POST',
'url': previewUrl, url: previewUrl,
'data': dataString, data: dataString,
'success': preview_task_callback, success: preview_task_callback,
'error': preview_task_error(itemName), error: preview_task_error(itemName),
'dataType': 'json', dataType: 'json',
'timeout': 60000, timeout: 60000,
} }
); )
})
});
} }
$(function () { $(function () {
"use strict"; 'use strict'
mail_preview_setup($("body")); mail_preview_setup($('body'))
$(document).on("pretix:bind-forms", function () { $(document).on('pretix:bind-forms', function () {
mail_preview_setup($("body")); mail_preview_setup($('body'))
}); })
}); })
File diff suppressed because it is too large Load Diff
@@ -1,59 +1,62 @@
/*global $, gettext*/ /* global gettext */
$(function () { $(function () {
if (!$(".form-order-change").length) { if (!$('.form-order-change').length) {
return; return
} }
$(".form-order-change").each(function () { $('.form-order-change').each(function () {
var url = $(this).attr("data-pricecalc-endpoint"); let url = $(this).attr('data-pricecalc-endpoint')
var $itemvar = $(this).find("[name*=itemvar]"); let $itemvar = $(this).find('[name*=itemvar]')
var $subevent = $(this).find("[name*=subevent]"); let $subevent = $(this).find('[name*=subevent]')
var $tax_rule = $(this).find("[name*=tax_rule]"); let $tax_rule = $(this).find('[name*=tax_rule]')
var $price = $(this).find("[name*=price]"); let $price = $(this).find('[name*=price]')
var update_price = function () { let update_price = function () {
console.log(url); console.log(url)
var itemvar = $itemvar.val(); let itemvar = $itemvar.val()
var item = null; let item
var variation = null; let variation = null
if (itemvar.indexOf("-")) { if (itemvar.indexOf('-')) {
item = parseInt(itemvar.split("-")[0]); item = parseInt(itemvar.split('-')[0])
variation = parseInt(itemvar.split("-")[1]); variation = parseInt(itemvar.split('-')[1])
} else { } else {
item = parseInt(itemvar); item = parseInt(itemvar)
} }
$price.closest(".field-container").append("<small class=\"loading-indicator\"><span class=\"fa fa-cog fa-spin\"></span> " + $price.closest('.field-container').append('<small class="loading-indicator"><span class="fa fa-cog fa-spin"></span> '
gettext("Calculating default price…") + "</small>"); + gettext('Calculating default price…') + '</small>')
$.ajax( $.ajax(
{ {
'type': 'POST', type: 'POST',
'url': url, url: url,
'headers': {'X-CSRFToken': $("input[name=csrfmiddlewaretoken]").val()}, headers: { 'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val() },
'data': JSON.stringify({ data: JSON.stringify({
'item': item, item: item,
'variation': variation, variation: variation,
'subevent': $subevent.val(), subevent: $subevent.val(),
'tax_rule': $tax_rule.val(), tax_rule: $tax_rule.val(),
'locale': $("body").attr("data-pretixlocale"), locale: $('body').attr('data-pretixlocale'),
}), }),
'contentType': "application/json", contentType: 'application/json',
'success': function (data) { success: function (data) {
$price.val(data.gross_formatted); $price.val(data.gross_formatted)
$tax_rule.val(data.tax_rule); $tax_rule.val(data.tax_rule)
$price.closest(".field-container").find(".loading-indicator").remove(); $price.closest('.field-container').find('.loading-indicator').remove()
}, },
// 'error': … // 'error': …
'context': this, context: this,
'dataType': 'json', dataType: 'json',
'timeout': 30000 timeout: 30000
} }
); )
}; }
$itemvar.on("change", function () { $tax_rule.val(null); update_price() }); $itemvar.on('change', function () {
$tax_rule.on("change", update_price); $tax_rule.val(null)
$subevent.on("change", update_price).on("change", function () { update_price()
var seat = $(this).closest(".form-order-change").find("[id$=seat]"); })
if (seat.length) { $tax_rule.on('change', update_price)
seat.prop("required", !!$subevent.val()); $subevent.on('change', update_price).on('change', function () {
} let seat = $(this).closest('.form-order-change').find('[id$=seat]')
}); if (seat.length) {
}); seat.prop('required', !!$subevent.val())
}); }
})
})
})
@@ -1,43 +1,43 @@
function is_sandbox_supported() { function is_sandbox_supported () {
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe')
return 'sandbox' in iframe; return 'sandbox' in iframe
} }
function safe_render(url, parent) { function safe_render (url, parent) {
// Estimate the height that prevents the user from having to scroll on two levels to see the full email // Estimate the height that prevents the user from having to scroll on two levels to see the full email
const height = ( const height = (
Math.max(400, window.innerHeight - parent.parent().get(0).getBoundingClientRect().top - document.querySelector("footer").getBoundingClientRect().height - 20) Math.max(400, window.innerHeight - parent.parent().get(0).getBoundingClientRect().top - document.querySelector('footer').getBoundingClientRect().height - 20)
) + "px"; ) + 'px'
const iframe = ( const iframe = (
// Per the HTML spec, a data: URL in an iframe is treated as its own origin: // Per the HTML spec, a data: URL in an iframe is treated as its own origin:
// https://github.com/whatwg/html/pull/1756 // https://github.com/whatwg/html/pull/1756
// It is unclear, if Firefox complies, and the behaviour around data URLs is quite wild: // It is unclear, if Firefox complies, and the behaviour around data URLs is quite wild:
// https://github.com/whatwg/html/issues/12091 // https://github.com/whatwg/html/issues/12091
// Together with the sandbox attribute disallowing all JavaScript, and the fact // Together with the sandbox attribute disallowing all JavaScript, and the fact
// that we sanitize the HTML before we even save it to the database, this should // that we sanitize the HTML before we even save it to the database, this should
// still be the safest way to render HTML in the context of our backend. // still be the safest way to render HTML in the context of our backend.
$("<iframe>") $('<iframe>')
.height(height) .height(height)
.attr("class", "html-email") .attr('class', 'html-email')
.attr("src", url) .attr('src', url)
.attr("sandbox", "allow-popups allow-popups-to-escape-sandbox") .attr('sandbox', 'allow-popups allow-popups-to-escape-sandbox')
.attr("csp", "script-src 'none'; font-src 'none'; connect-src 'none'; form-action 'none'; style-src 'unsafe-inline'") // respected only by chrome .attr('csp', 'script-src \'none\'; font-src \'none\'; connect-src \'none\'; form-action \'none\'; style-src \'unsafe-inline\'') // respected only by chrome
.prop("credentialless", true) // respected only by chrome .prop('credentialless', true) // respected only by chrome
); )
console.log(parent, iframe); console.log(parent, iframe)
parent.append(iframe); parent.append(iframe)
} }
$(function () { $(function () {
const script_element = $("#mail_body_html"); const script_element = $('#mail_body_html')
if (!script_element.length) return; if (!script_element.length) return
if (!is_sandbox_supported()) { if (!is_sandbox_supported()) {
// Browser is too old for <iframe sandbox> // Browser is too old for <iframe sandbox>
$(script_element.parent()).text("Please switch to a modern browser to view HTML content safely."); $(script_element.parent()).text('Please switch to a modern browser to view HTML content safely.')
return; return
} }
safe_render(JSON.parse(script_element.html()), script_element.parent()); safe_render(JSON.parse(script_element.html()), script_element.parent())
}); })
@@ -1,84 +1,89 @@
$(function() { /* global gettext */
var plugins = $(".plugin-container").toArray().map(function(el) { $(function () {
return { let plugins = $('.plugin-container').toArray().map(function (el) {
sortName: el.getAttribute('data-plugin-name').toLowerCase().replace(/pretix /g, ''), return {
name: el.getAttribute('data-plugin-name').toLowerCase(), sortName: el.getAttribute('data-plugin-name').toLowerCase().replace(/pretix /g, ''),
module: el.getAttribute('data-plugin-module').toLowerCase(), name: el.getAttribute('data-plugin-name').toLowerCase(),
description: $(el).find('.plugin-description').text().toLowerCase(), module: el.getAttribute('data-plugin-module').toLowerCase(),
html: el.outerHTML, description: $(el).find('.plugin-description').text().toLowerCase(),
category: $(el).closest('[data-plugin-category]').attr('data-plugin-category'), html: el.outerHTML,
categoryLabel: $(el).closest('[data-plugin-category]').attr('data-plugin-category-label'), category: $(el).closest('[data-plugin-category]').attr('data-plugin-category'),
active: !!$(el).has('[data-is-active]').length, categoryLabel: $(el).closest('[data-plugin-category]').attr('data-plugin-category-label'),
} active: !!$(el).has('[data-is-active]').length,
}); }
function SearchMatcher(term, fields) { })
this.searchFor = term.toLowerCase().split(/\s+/); function SearchMatcher (term, fields) {
this.fields = fields; this.searchFor = term.toLowerCase().split(/\s+/)
} this.fields = fields
function inStringRanked(haystack, needle) { }
let pos = -1, rank = 0; function inStringRanked (haystack, needle) {
do { let pos = -1, rank = 0
pos = haystack.indexOf(needle, pos + 1); do {
if (pos !== -1) rank = 10; pos = haystack.indexOf(needle, pos + 1)
if (pos === 0 || haystack.charCodeAt(pos - 1) <= 47) if (pos !== -1) rank = 10
return 15; // string start or word start (=char before match is special char) if (pos === 0 || haystack.charCodeAt(pos - 1) <= 47)
} while (pos !== -1); return 15 // string start or word start (=char before match is special char)
return rank; } while (pos !== -1)
} return rank
SearchMatcher.prototype.isMatch = function(obj) { }
let rank = 0; SearchMatcher.prototype.isMatch = function (obj) {
for(let j = 0; j < this.searchFor.length; j++) { let rank = 0
var searchFor = this.searchFor[j]; for (let j = 0; j < this.searchFor.length; j++) {
for(let i = this.fields.length - 1; i >= 0; i--) { let searchFor = this.searchFor[j]
var result = inStringRanked(obj[this.fields[i]], searchFor); for (let i = this.fields.length - 1; i >= 0; i--) {
if (result) { let result = inStringRanked(obj[this.fields[i]], searchFor)
rank += (i + 1) * result; if (result) {
break; rank += (i + 1) * result
} break
} }
} }
return rank; }
} return rank
function strcmp(a, b) { }
return a > b ? 1 : a < b ? -1 : 0; function strcmp (a, b) {
} return a > b ? 1 : a < b ? -1 : 0
var $results_box = $("#plugin_search_results"); }
var $plugin_tabs = $("#plugin_tabs"); let $results_box = $('#plugin_search_results')
var $results = $("#plugin_search_results .plugin-list"); let $plugin_tabs = $('#plugin_tabs')
function search() { let $results = $('#plugin_search_results .plugin-list')
$results.html(""); function search () {
var value = $("#plugin_search_input").val(); $results.html('')
var only_active = $("input[name=plugin_state_filter][value=active]").prop("checked"); let value = $('#plugin_search_input').val()
if (!value && !only_active) { let only_active = $('input[name=plugin_state_filter][value=active]').prop('checked')
$results_box.hide(); $plugin_tabs.show(); if (!value && !only_active) {
return; $results_box.hide()
} $plugin_tabs.show()
$results_box.show(); $plugin_tabs.hide(); return
var matcher = new SearchMatcher(value, ["description", "module", "name"]); }
var matches = []; $results_box.show()
for(const plugin of plugins) { $plugin_tabs.hide()
if (only_active && !plugin.active) continue; let matcher = new SearchMatcher(value, ['description', 'module', 'name'])
var rank = matcher.isMatch(plugin); let matches = []
if (!rank) continue; for (const plugin of plugins) {
matches.push([rank, plugin]); if (only_active && !plugin.active)
} continue
matches.sort(function (a,b) { return (b[0]-a[0]) || strcmp(a[1].sortName, b[1].sortName); }) let rank = matcher.isMatch(plugin)
$results.append(matches.map(function(res) { return $(res[1].html).prepend('<span class="pull-right">' + res[1].categoryLabel + '</span>'); })) if (!rank)
$results.find(".panel-body, .panel, .featured-plugin, .btn-lg").removeClass("panel-body panel featured-plugin btn-lg"); continue
if (matches.length === 0) { matches.push([rank, plugin])
$results.append(gettext("No results")); }
} matches.sort(function (a, b) { return (b[0] - a[0]) || strcmp(a[1].sortName, b[1].sortName) })
} $results.append(matches.map(function (res) { return $(res[1].html).prepend('<span class="pull-right">' + res[1].categoryLabel + '</span>') }))
$("#plugin_search_input").on("input", search); $results.find('.panel-body, .panel, .featured-plugin, .btn-lg').removeClass('panel-body panel featured-plugin btn-lg')
$("input[name=plugin_state_filter]").on("change", search); if (matches.length === 0) {
$results_box.find("button.close").on("click", function() { $results.append(gettext('No results'))
$("input[name=plugin_state_filter][value=all]").prop("checked", true).trigger("click"); }
$("#plugin_search_input").val("").trigger("input"); }
}); $('#plugin_search_input').on('input', search)
if (location.search) { $('input[name=plugin_state_filter]').on('change', search)
var search = new URLSearchParams(location.search); $results_box.find('button.close').on('click', function () {
if (search.has('q')) { $('input[name=plugin_state_filter][value=all]').prop('checked', true).trigger('click')
$("#plugin_search_input").val(search.get("q")).trigger("input"); $('#plugin_search_input').val('').trigger('input')
} })
} if (location.search) {
var search = new URLSearchParams(location.search)
if (search.has('q')) {
$('#plugin_search_input').val(search.get('q')).trigger('input')
}
}
}) })
+137 -137
View File
@@ -1,156 +1,156 @@
/*global $, Morris, gettext*/ /* global Morris, gettext, apiGET, i18nToString */
$(function () { $(function () {
// Question view // Question view
if (!$("#question_chart").length) { if (!$('#question_chart').length) {
return; return
} }
$(".chart").css("height", "250px"); $('.chart').css('height', '250px')
var data_type = $("#question_chart").attr("data-type"), let data_type = $('#question_chart').attr('data-type'),
data = JSON.parse($("#question-chart-data").text() || "[]"), data = JSON.parse($('#question-chart-data').text() || '[]'),
others_sum = 0, others_sum = 0,
max_num = 8; max_num = 8
data = data.map(function (d) { data = data.map(function (d) {
return { return {
'value': d.count, value: d.count,
'label': d.answer.length > 20 ? d.answer.substring(0, 20) + '…' : d.answer, label: d.answer.length > 20 ? d.answer.substring(0, 20) + '…' : d.answer,
} }
}); })
if (data_type == 'N') { if (data_type == 'N') {
// Sort // Sort
data.sort(function (a, b) { data.sort(function (a, b) {
if (parseFloat(a.label) > parseFloat(b.label)) { if (parseFloat(a.label) > parseFloat(b.label)) {
return 1; return 1
} else if (parseFloat(a.label) < parseFloat(b.label)) { } else if (parseFloat(a.label) < parseFloat(b.label)) {
return -1; return -1
} else { } else {
return 0; return 0
} }
}); })
max_num = 20; max_num = 20
} }
// Limit shown options // Limit shown options
if (data.length > max_num) { if (data.length > max_num) {
for (var i = max_num; i < data.length; i++) { for (let i = max_num; i < data.length; i++) {
others_sum += data[i].value; others_sum += data[i].value
} }
data = data.slice(0, max_num); data = data.slice(0, max_num)
data.push({'value': others_sum, 'label': gettext('Others')}); data.push({ value: others_sum, label: gettext('Others') })
} }
if (data_type === 'B') { if (data_type === 'B') {
var colors; let colors
if (data[0].answer_bool) { if (data[0].answer_bool) {
colors = ['#50A167', '#C44F4F']; colors = ['#50A167', '#C44F4F']
} else { } else {
colors = ['#C44F4F', '#50A167']; colors = ['#C44F4F', '#50A167']
} }
new Morris.Donut({ new Morris.Donut({
element: 'question_chart', element: 'question_chart',
data: data, data: data,
resize: true, resize: true,
colors: colors colors: colors
}); })
} else if (data_type === 'C') { } else if (data_type === 'C') {
new Morris.Donut({ new Morris.Donut({
element: 'question_chart', element: 'question_chart',
data: data, data: data,
resize: true, resize: true,
colors: [ colors: [
'#7F4A91', '#7F4A91',
'#50A167', '#50A167',
'#FFB419', '#FFB419',
'#5F9CD4', '#5F9CD4',
'#C44F4F', '#C44F4F',
'#83FFFA', '#83FFFA',
'#FF6C38', '#FF6C38',
'#1f5b8e', '#1f5b8e',
'#2d683c', '#2d683c',
] ]
}); })
} else { // M, N, S, T } else { // M, N, S, T
new Morris.Bar({ new Morris.Bar({
element: 'question_chart', element: 'question_chart',
data: data, data: data,
resize: true, resize: true,
xkey: 'label', xkey: 'label',
ykeys: ['value'], ykeys: ['value'],
labels: [gettext('Count')] labels: [gettext('Count')]
}); })
} }
// N, S, T // N, S, T
}); })
$(function () { $(function () {
// Question editor // Question editor
if (!$("#answer-options").length) { if (!$('#answer-options').length) {
return; return
} }
// Question editor // Question editor
$("#id_type").change(question_page_toggle_view); $('#id_type').change(question_page_toggle_view)
$("#id_required").change(question_page_toggle_view); $('#id_required').change(question_page_toggle_view)
question_page_toggle_view(); question_page_toggle_view()
function question_page_toggle_view() { function question_page_toggle_view () {
var show = $("#id_type").val() == "C" || $("#id_type").val() == "M"; let show = $('#id_type').val() == 'C' || $('#id_type').val() == 'M'
$("#answer-options").toggle(show); $('#answer-options').toggle(show)
$("#valid-date").toggle($("#id_type").val() == "D"); $('#valid-date').toggle($('#id_type').val() == 'D')
$("#valid-datetime").toggle($("#id_type").val() == "W"); $('#valid-datetime').toggle($('#id_type').val() == 'W')
$("#valid-string").toggle($("#id_type").val() == "T" || $("#id_type").val() == "S"); $('#valid-string').toggle($('#id_type').val() == 'T' || $('#id_type').val() == 'S')
$("#valid-number").toggle($("#id_type").val() == "N"); $('#valid-number').toggle($('#id_type').val() == 'N')
$("#valid-file").toggle($("#id_type").val() == "F"); $('#valid-file').toggle($('#id_type').val() == 'F')
show = $("#id_type").val() == "B" && $("#id_required").prop("checked"); show = $('#id_type').val() == 'B' && $('#id_required').prop('checked')
$(".alert-required-boolean").toggle(show); $('.alert-required-boolean').toggle(show)
} }
var $val = $("#id_dependency_values"); let $val = $('#id_dependency_values')
var $dq = $("#id_dependency_question"); let $dq = $('#id_dependency_question')
var oldval = JSON.parse($("#dependency_value_val").text()); let oldval = JSON.parse($('#dependency_value_val').text())
function update_dependency_options() { function update_dependency_options () {
$val.parent().find(".loading-indicator").remove(); $val.parent().find('.loading-indicator').remove()
$("#id_dependency_values option").remove(); $('#id_dependency_values option').remove()
$("#id_dependency_values").prop("required", false); $('#id_dependency_values').prop('required', false)
var val = $dq.children("option:selected").val(); let val = $dq.children('option:selected').val()
if (!val) { if (!val) {
$("#id_dependency_values").show(); $('#id_dependency_values').show()
$val.show(); $val.show()
return; return
} }
$("#id_dependency_values").prop("required", true); $('#id_dependency_values').prop('required', true)
$val.hide(); $val.hide()
$val.parent().append("<div class=\"help-block loading-indicator\"><span class=\"fa" + $val.parent().append('<div class="help-block loading-indicator"><span class="fa'
" fa-cog fa-spin\"></span></div>"); + ' fa-cog fa-spin"></span></div>')
// the container_type parameter is undocumented. this API is going to change in a later release. // the container_type parameter is undocumented. this API is going to change in a later release.
apiGET('/api/v1/organizers/' + $("body").attr("data-organizer") + '/events/' + $("body").attr("data-event") + '/questions/' + val + '/?container_type=' + encodeURIComponent($dq.data('container-type')), function (data) { apiGET('/api/v1/organizers/' + $('body').attr('data-organizer') + '/events/' + $('body').attr('data-event') + '/questions/' + val + '/?container_type=' + encodeURIComponent($dq.data('container-type')), function (data) {
if (data.type === "B") { if (data.type === 'B') {
$val.append($("<option>").attr("value", "True").text(gettext("Yes"))); $val.append($('<option>').attr('value', 'True').text(gettext('Yes')))
$val.append($("<option>").attr("value", "False").text(gettext("No"))); $val.append($('<option>').attr('value', 'False').text(gettext('No')))
} else { } else {
for (var i = 0; i < data.options.length; i++) { for (let i = 0; i < data.options.length; i++) {
var opt = data.options[i]; let opt = data.options[i]
var $opt = $("<option>").attr("value", opt.identifier).text(i18nToString(opt.answer)); let $opt = $('<option>').attr('value', opt.identifier).text(i18nToString(opt.answer))
$val.append($opt); $val.append($opt)
} }
} }
if (oldval) { if (oldval) {
$val.val(oldval); $val.val(oldval)
} }
$val.parent().find(".loading-indicator").remove(); $val.parent().find('.loading-indicator').remove()
$val.show(); $val.show()
}); })
} }
update_dependency_options(); update_dependency_options()
$dq.change(update_dependency_options); $dq.change(update_dependency_options)
}); })
@@ -1,50 +1,50 @@
$(function () { $(function () {
"use strict"; 'use strict'
var ticket_type_quota_calculation = function () { let ticket_type_quota_calculation = function () {
var sum = 0; let sum = 0
$("#ticket-type-formset div[data-formset-form]").each(function () { $('#ticket-type-formset div[data-formset-form]').each(function () {
if (!$(this).find("input[name$=DELETE]").prop("checked")) { if (!$(this).find('input[name$=DELETE]').prop('checked')) {
var val = $(this).find("input[name$=quota]").val(); let val = $(this).find('input[name$=quota]').val()
if (val === "") { if (val === '') {
sum = "∞"; sum = '∞'
} else if (sum !== "∞") { } else if (sum !== '∞') {
sum += parseInt(val); sum += parseInt(val)
} }
} }
}); })
$("#total-capacity").text(sum); $('#total-capacity').text(sum)
}; }
var toggle_payment = function () { let toggle_payment = function () {
var any = false; let any = false
$("#ticket-type-formset div[data-formset-form]").each(function () { $('#ticket-type-formset div[data-formset-form]').each(function () {
if (!$(this).find("input[name$=DELETE]").prop("checked")) { if (!$(this).find('input[name$=DELETE]').prop('checked')) {
var val = $(this).find("input[name$=default_price]").val(); let val = $(this).find('input[name$=default_price]').val()
if (/.*[1-9].*/.test(val)) { if (/.*[1-9].*/.test(val)) {
any = true; any = true
} }
} }
}); })
if ($("#quick-setup-step-payment:visible").length && !any) { if ($('#quick-setup-step-payment:visible').length && !any) {
$("#quick-setup-step-payment").stop().slideUp(); $('#quick-setup-step-payment').stop().slideUp()
} else if (!$("#quick-setup-step-payment:visible").length && any) { } else if (!$('#quick-setup-step-payment:visible').length && any) {
$("#quick-setup-step-payment").stop().slideDown(); $('#quick-setup-step-payment').stop().slideDown()
} }
}; }
$("#ticket-type-formset").bind("formAdded", ticket_type_quota_calculation); $('#ticket-type-formset').bind('formAdded', ticket_type_quota_calculation)
$("#ticket-type-formset").on("change keyup keydown keypress", "input", function () { $('#ticket-type-formset').on('change keyup keydown keypress', 'input', function () {
ticket_type_quota_calculation(); ticket_type_quota_calculation()
toggle_payment(); toggle_payment()
}); })
ticket_type_quota_calculation(); ticket_type_quota_calculation()
toggle_payment(); toggle_payment()
$("#total-capacity-edit").click(function () { $('#total-capacity-edit').click(function () {
$("#id_total_quota").val(parseInt($("#total-capacity").text())); $('#id_total_quota').val(parseInt($('#total-capacity').text()))
$("#total-capacity").hide(); $('#total-capacity').hide()
$("#id_total_quota").closest("div").removeClass("sr-only"); $('#id_total_quota').closest('div').removeClass('sr-only')
$("#total-capacity-edit").hide(); $('#total-capacity-edit').hide()
}); })
}); })
+37 -37
View File
@@ -1,43 +1,43 @@
/*globals $, Morris, gettext*/ /* globals Morris */
$(function () { $(function () {
if (!$("#quota-stats").length) { if (!$('#quota-stats').length) {
return; return
} }
$(".chart").css("height", "250px"); $('.chart').css('height', '250px')
new Morris.Donut({ new Morris.Donut({
element: 'quota_chart', element: 'quota_chart',
data: JSON.parse($("#quota-chart-data").html()), data: JSON.parse($('#quota-chart-data').html()),
resize: true, resize: true,
colors: [ colors: [
'#0044CC', // paid '#0044CC', // paid
'#0088CC', // pending '#0088CC', // pending
'#BD362F', // vouchers '#BD362F', // vouchers
'#F89406', // carts '#F89406', // carts
'#51A351' // available '#51A351' // available
] ]
}); })
}); })
$(function () { $(function () {
if (!$("input[name=itemvars]").length) { if (!$('input[name=itemvars]').length) {
return; return
} }
var autofill = ($("#id_name").val() === ""); let autofill = ($('#id_name').val() === '')
$("#id_name").on("change keyup keydown keypress", function () { $('#id_name').on('change keyup keydown keypress', function () {
autofill = false; autofill = false
}) })
function do_autofill() { function do_autofill () {
if (autofill) { if (autofill) {
var names = []; let names = []
$("input[name=itemvars]:checked").each(function () { $('input[name=itemvars]:checked').each(function () {
names.push($.trim($(this).closest("label").text())) names.push($.trim($(this).closest('label').text()))
}); })
$("#id_name").val(names.join(', ')); $('#id_name').val(names.join(', '))
} }
} }
$("input[name=itemvars]").change(do_autofill); $('input[name=itemvars]').change(do_autofill)
do_autofill(); do_autofill()
}); })
+15 -17
View File
@@ -1,21 +1,19 @@
/*globals $, Morris, gettext, RRule, RRuleSet*/ function rrule_form_toggles ($form) {
function rrule_form_toggles($form) { let freq = $form.find('select[name*=freq]').val()
var freq = $form.find("select[name*=freq]").val(); $form.find('.repeat-yearly').toggle(freq === 'yearly')
$form.find(".repeat-yearly").toggle(freq === "yearly"); $form.find('.repeat-monthly').toggle(freq === 'monthly')
$form.find(".repeat-monthly").toggle(freq === "monthly"); $form.find('.repeat-weekly').toggle(freq === 'weekly')
$form.find(".repeat-weekly").toggle(freq === "weekly");
} }
function rrule_bind_form($form) { function rrule_bind_form ($form) {
$form.find("select[name*=freq]").change(function () { $form.find('select[name*=freq]').change(function () {
rrule_form_toggles($form); rrule_form_toggles($form)
}); })
rrule_form_toggles($form); rrule_form_toggles($form)
} }
$(document).on('pretix:bind-forms', function () {
$(document).on("pretix:bind-forms", function () { $('.rrule-form').each(function () {
$(".rrule-form").each(function () { rrule_bind_form($(this))
rrule_bind_form($(this)); })
}); })
});
+203 -204
View File
@@ -1,226 +1,225 @@
/*globals $, Morris, gettext, RRule, RRuleSet*/ /* globals ngettext, rrule */
$(document).on("pretix:bind-forms", function () { $(document).on('pretix:bind-forms', function () {
if (!$("div[data-formset-prefix=checkinlist_set]").length) { if (!$('div[data-formset-prefix=checkinlist_set]').length) {
return; return
} }
function parse_weekday(wd) { function parse_weekday (wd) {
map = { map = {
'MO': 0, MO: 0,
'TU': 1, TU: 1,
'WE': 2, WE: 2,
'TH': 3, TH: 3,
'FR': 4, FR: 4,
'SA': 5, SA: 5,
'SU': 6 SU: 6
} }
if (wd.indexOf(",") > 0) { if (wd.indexOf(',') > 0) {
var wds = []; let wds = []
$.each(wd.split(","), function (k, v) { $.each(wd.split(','), function (k, v) {
wds.push(map[v]); wds.push(map[v])
}); })
return wds; return wds
} else { } else {
return map[wd]; return map[wd]
} }
} }
// RRule editor // RRule editor
function rrule_preview() { function rrule_preview () {
var ruleset = new rrule.RRuleSet(); let ruleset = new rrule.RRuleSet()
$(".rrule-form").each(function () { $('.rrule-form').each(function () {
if ($(this).find("input[name$=DELETE]").prop("checked")) { if ($(this).find('input[name$=DELETE]').prop('checked')) {
return; return
} }
var rule_args = {}; let rule_args = {}
var $form = $(this); let $form = $(this)
var freq = $form.find("select[name*=freq]").val(); let freq = $form.find('select[name*=freq]').val()
if (!$form.find("input[name*=dtstart]").data("DateTimePicker")) { if (!$form.find('input[name*=dtstart]').data('DateTimePicker')) {
// uninitialized // uninitialized
return; return
} }
var dtstart = $form.find("input[name*=dtstart]").data("DateTimePicker").date(); let dtstart = $form.find('input[name*=dtstart]').data('DateTimePicker').date()
dtstart = dtstart.add(dtstart.utcOffset(), 'm').add(12, 'h').utcOffset(0); dtstart = dtstart.add(dtstart.utcOffset(), 'm').add(12, 'h').utcOffset(0)
rule_args.dtstart = dtstart.toDate(); rule_args.dtstart = dtstart.toDate()
rule_args.interval = parseInt($form.find("input[name*=interval]").val()) || 1; rule_args.interval = parseInt($form.find('input[name*=interval]').val()) || 1
if (freq === 'yearly') { if (freq === 'yearly') {
rule_args.freq = rrule.RRule.YEARLY; rule_args.freq = rrule.RRule.YEARLY
var same = $form.find("input[name*=yearly_same]:checked").val(); var same = $form.find('input[name*=yearly_same]:checked').val()
if (same === "off") { if (same === 'off') {
rule_args.bysetpos = parseInt($form.find("select[name*=yearly_bysetpos]").val()); rule_args.bysetpos = parseInt($form.find('select[name*=yearly_bysetpos]').val())
rule_args.byweekday = parse_weekday($form.find("select[name*=yearly_byweekday]").val()); rule_args.byweekday = parse_weekday($form.find('select[name*=yearly_byweekday]').val())
rule_args.bymonth = parseInt($form.find("select[name*=yearly_bymonth]").val()); rule_args.bymonth = parseInt($form.find('select[name*=yearly_bymonth]').val())
} }
} else if (freq === 'monthly') { } else if (freq === 'monthly') {
rule_args.freq = rrule.RRule.MONTHLY; rule_args.freq = rrule.RRule.MONTHLY
var same = $form.find("input[name*=monthly_same]:checked").val(); var same = $form.find('input[name*=monthly_same]:checked').val()
if (same === "off") { if (same === 'off') {
rule_args.bysetpos = parseInt($form.find("select[name*=monthly_bysetpos]").val()); rule_args.bysetpos = parseInt($form.find('select[name*=monthly_bysetpos]').val())
rule_args.byweekday = parse_weekday($form.find("select[name*=monthly_byweekday]").val()); rule_args.byweekday = parse_weekday($form.find('select[name*=monthly_byweekday]').val())
} }
} else if (freq === 'weekly') { } else if (freq === 'weekly') {
rule_args.freq = rrule.RRule.WEEKLY; rule_args.freq = rrule.RRule.WEEKLY
var days = []; let days = []
$form.find("input[name*=weekly_byweekday]:checked").each(function () { $form.find('input[name*=weekly_byweekday]:checked').each(function () {
days.push(parse_weekday($(this).val())); days.push(parse_weekday($(this).val()))
}); })
if (days.length !== 0) { if (days.length !== 0) {
rule_args.byweekday = days; rule_args.byweekday = days
} }
} else if (freq === 'daily') { } else if (freq === 'daily') {
rule_args.freq = rrule.RRule.DAILY; rule_args.freq = rrule.RRule.DAILY
} }
var end = $form.find("input[name*=end]:checked").val(); let end = $form.find('input[name*=end]:checked').val()
if (end === "count") { if (end === 'count') {
rule_args.count = Math.max(parseInt($form.find("input[name*=count]").val()) || 1, 1); rule_args.count = Math.max(parseInt($form.find('input[name*=count]').val()) || 1, 1)
} else { } else {
var date = $form.find("input[name*=until]").data("DateTimePicker").date(); let date = $form.find('input[name*=until]').data('DateTimePicker').date()
if (date !== null) { if (date !== null) {
// rrule.until is non-inclusive, whereas in pretix-backend "until" is inclusive => add 1 day // rrule.until is non-inclusive, whereas in pretix-backend "until" is inclusive => add 1 day
// date is a Moment-object. Moment.add() mutates, but is save to do here // date is a Moment-object. Moment.add() mutates, but is save to do here
date.add(1, 'days'); date.add(1, 'days')
rule_args.until = date; rule_args.until = date
} }
} }
if ($form.find("input[name*=exclude]").prop("checked")) { if ($form.find('input[name*=exclude]').prop('checked')) {
ruleset.exrule(new rrule.RRule(rule_args)); ruleset.exrule(new rrule.RRule(rule_args))
$form.closest(".panel").addClass("panel-danger").removeClass("panel-default"); $form.closest('.panel').addClass('panel-danger').removeClass('panel-default')
} else { } else {
ruleset.rrule(new rrule.RRule(rule_args)); ruleset.rrule(new rrule.RRule(rule_args))
$form.closest(".panel").addClass("panel-default").removeClass("panel-danger"); $form.closest('.panel').addClass('panel-default').removeClass('panel-danger')
} }
}); })
var all_dates = ruleset.all(); let all_dates = ruleset.all()
var format = $("body").attr("data-longdateformat") + " (dddd)"; let format = $('body').attr('data-longdateformat') + ' (dddd)'
$("#rrule-preview").html(""); $('#rrule-preview').html('')
if (all_dates.length > 20) { if (all_dates.length > 20) {
$("#rrule-preview").html(""); $('#rrule-preview').html('')
all_dates.slice(0, 10).forEach(function(element) { all_dates.slice(0, 10).forEach(function (element) {
$("#rrule-preview").append($("<li>").text(moment(element).utc().format(format))); $('#rrule-preview').append($('<li>').text(moment(element).utc().format(format)))
}); })
$("#rrule-preview").append($("<li>").text(ngettext( $('#rrule-preview').append($('<li>').text(ngettext(
"(one more date)", '(one more date)',
"({num} more dates)", '({num} more dates)',
all_dates.length - 20 all_dates.length - 20
).replace(/\{num\}/g, all_dates.length - 20))); ).replace(/\{num\}/g, all_dates.length - 20)))
all_dates.slice(-10).forEach(function(element) { all_dates.slice(-10).forEach(function (element) {
$("#rrule-preview").append($("<li>").text(moment(element).utc().format(format))); $('#rrule-preview').append($('<li>').text(moment(element).utc().format(format)))
}); })
} else { } else {
all_dates.forEach(function(element) { all_dates.forEach(function (element) {
$("#rrule-preview").append($("<li>").text(moment(element).utc().format(format))); $('#rrule-preview').append($('<li>').text(moment(element).utc().format(format)))
}); })
} }
} }
$("#rrule-formset").on("change keydown keyup keypress dp.change", "input, select", function () { $('#rrule-formset').on('change keydown keyup keypress dp.change', 'input, select', function () {
rrule_preview(); rrule_preview()
}); })
rrule_preview(); rrule_preview()
$("#rrule-formset").on("formAdded", "div", function (event) {rrule_bind_form($(event.target)); }); $('#rrule-formset').on('formAdded', 'div', function (event) { rrule_bind_form($(event.target)) })
// Timeslot editor // Timeslot editor
$("#subevent_add_many_slots_go").on("click", function () { $('#subevent_add_many_slots_go').on('click', function () {
$("#time-formset [data-formset-form]").each(function () { $('#time-formset [data-formset-form]').each(function () {
var tf = $(this).find("[name$=time_from]").val() let tf = $(this).find('[name$=time_from]').val()
if (!tf) { if (!tf) {
$(this).remove(); $(this).remove()
} }
}) })
var first = $("#subevent_add_many_slots_first").data('DateTimePicker').date(); let first = $('#subevent_add_many_slots_first').data('DateTimePicker').date()
var end = $("#subevent_add_many_slots_end").data('DateTimePicker').date(); let end = $('#subevent_add_many_slots_end').data('DateTimePicker').date()
var length_m = parseFloat($("#subevent_add_many_slots_length").val()) || 0; let length_m = parseFloat($('#subevent_add_many_slots_length').val()) || 0
var break_m = parseFloat($("#subevent_add_many_slots_break").val()) || 0; let break_m = parseFloat($('#subevent_add_many_slots_break').val()) || 0
if (!first || !end || !length_m) { if (!first || !end || !length_m) {
console.log("invalid", first, end, length_m) console.log('invalid', first, end, length_m)
return return
} }
function closure($form, time) { function closure ($form, time) {
return function () { return function () {
console.log("setting value", time) console.log('setting value', time)
$form.find("[name$=time_from]").data('DateTimePicker').date(time); $form.find('[name$=time_from]').data('DateTimePicker').date(time)
time.add(length_m, 'minutes'); time.add(length_m, 'minutes')
$form.find("[name$=time_to]").data('DateTimePicker').date(time); $form.find('[name$=time_to]').data('DateTimePicker').date(time)
} }
} }
var pointer = first.clone(); let pointer = first.clone()
while (pointer.isBefore(end)) { while (pointer.isBefore(end)) {
var $form = $("#time-formset").formset("getOrCreate").addForm(); let $form = $('#time-formset').formset('getOrCreate').addForm()
$form.attr("data-formset-created-at-runtime", "false"); // prevents animation $form.attr('data-formset-created-at-runtime', 'false') // prevents animation
var time = pointer.clone(); let time = pointer.clone()
window.setTimeout(closure($form, time), 1); window.setTimeout(closure($form, time), 1)
// jquery.formset.js only calls trigger("formAdded") after a setTimeout of 0, // jquery.formset.js only calls trigger("formAdded") after a setTimeout of 0,
// but we need to run after that to make sure the date pickers are initialized // but we need to run after that to make sure the date pickers are initialized
pointer.add(break_m + length_m, 'minutes'); pointer.add(break_m + length_m, 'minutes')
} }
$("#subevent_add_many_slots").addClass("hidden"); $('#subevent_add_many_slots').addClass('hidden')
$("#subevent_add_many_slots_start").removeClass("hidden"); $('#subevent_add_many_slots_start').removeClass('hidden')
})
$('#subevent_add_many_slots_start').on('click', function () {
$('#subevent_add_many_slots').removeClass('hidden')
$(this).addClass('hidden')
})
}); // Hide config for products that are not for sale
$("#subevent_add_many_slots_start").on("click", function () { function quota_form_handlers (el) {
$("#subevent_add_many_slots").removeClass("hidden"); // searchable_selection = True
$(this).addClass("hidden"); el.find('[id^="id_quotas-"]').on('select2:select select2:unselect', () => {
}); update_item_visibility()
})
// searchable_selection = False
el.find('input[id^="id_quotas-"][id*=itemvars_]').on('change', () => {
update_item_visibility()
})
}
function update_item_visibility () {
const itemvars = []
// Hide config for products that are not for sale // searchable_selection = True
function quota_form_handlers(el) { $('select[id^=id_quotas-][id$=-itemvars]').filter((idx, el) => {
// searchable_selection = True return !$(el).closest('[data-formset-form]').is('[data-formset-form-deleted]')
el.find('[id^="id_quotas-"]').on("select2:select select2:unselect", () => { }).each((_, e) => itemvars.push(...$(e).val()))
update_item_visibility(); // searchable_selection = False
}); $('input[id^=id_quotas-][id*=itemvars_]:checked').filter((idx, el) => {
// searchable_selection = False return !$(el).closest('[data-formset-form]').is('[data-formset-form-deleted]')
el.find('input[id^="id_quotas-"][id*=itemvars_]').on("change", () => { }).each((_, e) => itemvars.push($(e).val()))
update_item_visibility();
});
}
function update_item_visibility() {
const itemvars = [];
// searchable_selection = True $('div[data-itemvar]').each(function (idx, e) {
$("select[id^=id_quotas-][id$=-itemvars]").filter((idx, el) => { const el = $(e)
return !$(el).closest('[data-formset-form]').is('[data-formset-form-deleted]'); el.prop('hidden', !itemvars.includes(el.attr('data-itemvar')) && !el.find('.has-error, .alert-danger').length)
}).each((_, e) => itemvars.push(...$(e).val())); })
// searchable_selection = False }
$("input[id^=id_quotas-][id*=itemvars_]:checked").filter((idx, el) => {
return !$(el).closest('[data-formset-form]').is('[data-formset-form-deleted]');
}).each((_, e) => itemvars.push($(e).val()));
$("div[data-itemvar]").each(function (idx, e) { $('[data-formset-prefix="quotas"]').on('formDeleted', 'div', () => {
const el = $(e); update_item_visibility()
el.prop("hidden", !itemvars.includes(el.attr("data-itemvar")) && !el.find(".has-error, .alert-danger").length); }).on('formAdded', 'div', (event) => {
}); quota_form_handlers($(event.target))
} update_item_visibility()
})
quota_form_handlers($('body'))
update_item_visibility()
$('[data-formset-prefix="quotas"]').on("formDeleted", "div", () => { // Auto-set name of check-in list
update_item_visibility(); let $namef = $('input[id^=id_name]').first()
}).on("formAdded", "div", (event) => { let lastValue = $namef.val()
quota_form_handlers($(event.target)); $namef.change(function () {
update_item_visibility(); let field = $('div[data-formset-prefix=checkinlist_set] input[id$=name]').first()
}) if (field.val() === lastValue) {
quota_form_handlers($("body")); lastValue = $(this).val()
update_item_visibility(); field.val(lastValue)
}
// Auto-set name of check-in list })
var $namef = $("input[id^=id_name]").first(); })
var lastValue = $namef.val();
$namef.change(function () {
var field = $("div[data-formset-prefix=checkinlist_set] input[id$=name]").first();
if (field.val() === lastValue) {
lastValue = $(this).val();
field.val(lastValue);
}
});
});
+52 -54
View File
@@ -1,56 +1,54 @@
/*globals $*/
$(function () { $(function () {
var j = 0; let j = 0
$(".tabbed-form").each(function () { $('.tabbed-form').each(function () {
var $form = $(this); let $form = $(this)
var $tabs = $("<ul>").addClass("nav nav-tabs").insertBefore($form); let $tabs = $('<ul>').addClass('nav nav-tabs').insertBefore($form)
$form.addClass("tab-content"); $form.addClass('tab-content')
var i = 0; let i = 0
var preselect = null; let preselect = null
var validity_error = false; let validity_error = false
$form.find("fieldset").each(function () { $form.find('fieldset').each(function () {
var $fieldset = $(this); let $fieldset = $(this)
var tid = $fieldset.attr("id"); let tid = $fieldset.attr('id')
if (!tid) tid = "tab-" + j + "-" + i; if (!tid) tid = 'tab-' + j + '-' + i
var $tabli = $("<li>").appendTo($tabs); let $tabli = $('<li>').appendTo($tabs)
var $tablink = $("<a>").attr("role", "tab") let $tablink = $('<a>').attr('role', 'tab')
.attr("data-toggle", "tab") .attr('data-toggle', 'tab')
.attr("href", "#" + tid) .attr('href', '#' + tid)
.text($fieldset.find("legend").text()) .text($fieldset.find('legend').text())
.appendTo($tabli); .appendTo($tabli)
if ($fieldset.find(".has-error, .alert-danger:not(.dynamic)").length > 0) { if ($fieldset.find('.has-error, .alert-danger:not(.dynamic)').length > 0) {
$tablink.append(" "); $tablink.append(' ')
$tablink.append($("<span>").addClass("fa fa-warning text-danger")); $tablink.append($('<span>').addClass('fa fa-warning text-danger'))
if (preselect === null) { if (preselect === null) {
preselect = i; preselect = i
} }
} }
$fieldset.find("input, select, textarea").on("invalid", function () { $fieldset.find('input, select, textarea').on('invalid', function () {
if ($tablink.find(".fa-warning").length === 0) { if ($tablink.find('.fa-warning').length === 0) {
$tablink.append(" "); $tablink.append(' ')
$tablink.append($("<span>").addClass("fa fa-warning text-danger")); $tablink.append($('<span>').addClass('fa fa-warning text-danger'))
if (!validity_error) { if (!validity_error) {
validity_error = true; validity_error = true
$tablink.click(); $tablink.click()
} }
} }
}); })
$fieldset.find("legend").remove(); $fieldset.find('legend').remove()
$fieldset.addClass("tab-pane").attr("id", tid); $fieldset.addClass('tab-pane').attr('id', tid)
if (location.hash && ($fieldset.find(location.hash).length || location.hash === "#" + tid + "-open") && preselect === null) { if (location.hash && ($fieldset.find(location.hash).length || location.hash === '#' + tid + '-open') && preselect === null) {
preselect = i; preselect = i
} }
i++; i++
}); })
$tabs.find("a").get(preselect != null ? preselect : 0).click(); $tabs.find('a').get(preselect != null ? preselect : 0).click()
$tabs.find("a").on('shown.bs.tab', function (e) { $tabs.find('a').on('shown.bs.tab', function (e) {
history.replaceState(null, null, e.target.getAttribute("href") + "-open"); history.replaceState(null, null, e.target.getAttribute('href') + '-open')
}); })
$form.closest("form").on("submit", function () { $form.closest('form').on('submit', function () {
validity_error = false; validity_error = false
}); })
j++; j++
}); })
}); })
+157 -158
View File
@@ -1,164 +1,163 @@
/*global $,u2f */
$(function () { $(function () {
$('.context-selector.dropdown').on('shown.bs.collapse shown.bs.dropdown', function () { $('.context-selector.dropdown').on('shown.bs.collapse shown.bs.dropdown', function () {
$(this).parent().find("input").val("").trigger('forceRunQuery').focus(); $(this).parent().find('input').val('').trigger('forceRunQuery').focus()
}); })
$('.dropdown-menu .form-box input').click(function (e) { $('.dropdown-menu .form-box input').click(function (e) {
e.stopPropagation(); e.stopPropagation()
}); })
$("[data-event-typeahead]").each(function () { $('[data-event-typeahead]').each(function () {
var $container = $(this); let $container = $(this)
var $query = $(this).find('[data-typeahead-query]').length ? $(this).find('[data-typeahead-query]') : $($(this).attr("data-typeahead-field")); let $query = $(this).find('[data-typeahead-query]').length ? $(this).find('[data-typeahead-query]') : $($(this).attr('data-typeahead-field'))
$container.find("li:not(.query-holder)").remove(); $container.find('li:not(.query-holder)').remove()
var lastQuery = null; let lastQuery = null
var runQueryTimeout = null; let runQueryTimeout = null
var loadIndicatorTimeout = null; let loadIndicatorTimeout = null
var focusOutTimeout = null; let focusOutTimeout = null
function showLoadIndicator() { function showLoadIndicator () {
$container.find("li:not(.query-holder)").remove(); $container.find('li:not(.query-holder)').remove()
$container.append("<li class='loading'><span class='fa fa-4x fa-cog fa-spin'></span></li>"); $container.append('<li class=\'loading\'><span class=\'fa fa-4x fa-cog fa-spin\'></span></li>')
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0); $container.toggleClass('focused', $query.is(':focus') && $container.children().length > 0)
} }
function runQuery() { function runQuery () {
var thisQuery = $query.val(); let thisQuery = $query.val()
if (thisQuery === lastQuery) return; if (thisQuery === lastQuery) return
lastQuery = $query.val(); lastQuery = $query.val()
window.clearTimeout(loadIndicatorTimeout) window.clearTimeout(loadIndicatorTimeout)
loadIndicatorTimeout = window.setTimeout(showLoadIndicator, 80) loadIndicatorTimeout = window.setTimeout(showLoadIndicator, 80)
$.getJSON( $.getJSON(
$container.attr("data-source") + "?query=" + encodeURIComponent($query.val()) + (typeof $container.attr("data-organizer") !== "undefined" ? "&organizer=" + $container.attr("data-organizer") : ""), $container.attr('data-source') + '?query=' + encodeURIComponent($query.val()) + (typeof $container.attr('data-organizer') !== 'undefined' ? '&organizer=' + $container.attr('data-organizer') : ''),
function (data) { function (data) {
if (thisQuery !== lastQuery) { if (thisQuery !== lastQuery) {
// Lost race condition // Lost race condition
return; return
} }
window.clearTimeout(loadIndicatorTimeout); window.clearTimeout(loadIndicatorTimeout)
$container.find("li:not(.query-holder)").remove(); $container.find('li:not(.query-holder)').remove()
$.each(data.results, function (i, res) { $.each(data.results, function (_i, res) {
let $linkContent = $("<div>"); let $linkContent = $('<div>')
if (res.type === "organizer") { if (res.type === 'organizer') {
$linkContent.append( $linkContent.append(
$("<span>").addClass("event-name-full").append( $('<span>').addClass('event-name-full').append(
$("<span>").addClass("fa fa-users fa-fw") $('<span>').addClass('fa fa-users fa-fw')
).append(" ").append($("<div>").text(res.name).html()) ).append(' ').append($('<div>').text(res.name).html())
) )
} else if (res.type === "order" || res.type === "voucher") { } else if (res.type === 'order' || res.type === 'voucher') {
$linkContent.append( $linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.title).html()) $('<span>').addClass('event-name-full').append($('<div>').text(res.title).html())
).append( ).append(
$("<span>").addClass("event-organizer").append( $('<span>').addClass('event-organizer').append(
$("<span>").addClass("fa fa-calendar fa-fw") $('<span>').addClass('fa fa-calendar fa-fw')
).append(" ").append($("<div>").text(res.event).html()) ).append(' ').append($('<div>').text(res.event).html())
) )
} else if (res.type === "user") { } else if (res.type === 'user') {
$linkContent.append( $linkContent.append(
$("<span>").addClass("event-name-full").append( $('<span>').addClass('event-name-full').append(
$("<span>").addClass("fa fa-user fa-fw") $('<span>').addClass('fa fa-user fa-fw')
).append(" ").append($("<div>").text(res.name).html()) ).append(' ').append($('<div>').text(res.name).html())
) )
} else { } else {
$linkContent.append( $linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.name).html()) $('<span>').addClass('event-name-full').append($('<div>').text(res.name).html())
).append( ).append(
$("<span>").addClass("event-organizer").append( $('<span>').addClass('event-organizer').append(
$("<span>").addClass("fa fa-users fa-fw") $('<span>').addClass('fa fa-users fa-fw')
).append(" ").append($("<div>").text(res.organizer).html()) ).append(' ').append($('<div>').text(res.organizer).html())
).append( ).append(
$("<span>").addClass("event-daterange").append( $('<span>').addClass('event-daterange').append(
$("<span>").addClass("fa fa-calendar fa-fw") $('<span>').addClass('fa fa-calendar fa-fw')
).append(" ").append(res.date_range) ).append(' ').append(res.date_range)
) )
} }
$container.append( $container.append(
$("<li>").append( $('<li>').append(
$("<a>").attr("href", res.url).append( $('<a>').attr('href', res.url).append(
$linkContent $linkContent
) )
) )
); )
}); })
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0); $container.toggleClass('focused', $query.is(':focus') && $container.children().length > 0)
} }
); )
} }
$query.on("forceRunQuery", function () { $query.on('forceRunQuery', function () {
runQuery(); runQuery()
}); })
$query.on("input", function () { $query.on('input', function () {
if ($container.attr("data-typeahead-field") && $query.val() === "") { if ($container.attr('data-typeahead-field') && $query.val() === '') {
$container.removeClass('focused'); $container.removeClass('focused')
$container.find("li:not(.query-holder)").remove(); $container.find('li:not(.query-holder)').remove()
lastQuery = null; lastQuery = null
return; return
} }
window.clearTimeout(runQueryTimeout) window.clearTimeout(runQueryTimeout)
runQueryTimeout = window.setTimeout(runQuery, 250) runQueryTimeout = window.setTimeout(runQuery, 250)
}); })
$query.on("keydown", function (event) { $query.on('keydown', function (event) {
var $selected = $container.find(".active"); let $selected = $container.find('.active')
if (event.which === 13) { // enter if (event.which === 13) { // enter
var $link = $selected.find("a"); let $link = $selected.find('a')
if ($link.length) { if ($link.length) {
location.href = $link.attr("href"); location.href = $link.attr('href')
} }
event.preventDefault(); event.preventDefault()
event.stopPropagation(); event.stopPropagation()
} }
}); })
$container.add($query).on("keydown", function (event) { $container.add($query).on('keydown', function (event) {
if (event.which === 27) { // escape if (event.which === 27) { // escape
$container.removeClass('focused'); $container.removeClass('focused')
} }
}).on("focusin", function (event) { }).on('focusin', function () {
window.clearTimeout(focusOutTimeout); window.clearTimeout(focusOutTimeout)
$(document.body).one("focusout", function (event) { $(document.body).one('focusout', function () {
focusOutTimeout = window.setTimeout(function () { focusOutTimeout = window.setTimeout(function () {
$container.removeClass('focused'); $container.removeClass('focused')
}, 100); }, 100)
}) })
}); })
$query.on("keyup", function (event) { $query.on('keyup', function (event) {
var $first = $container.find("li:not(.query-holder)").first(); let $first = $container.find('li:not(.query-holder)').first()
var $last = $container.find("li:not(.query-holder)").last(); let $last = $container.find('li:not(.query-holder)').last()
var $selected = $container.find(".active"); let $selected = $container.find('.active')
if (event.which === 13) { // enter if (event.which === 13) { // enter
event.preventDefault(); event.preventDefault()
event.stopPropagation(); event.stopPropagation()
return true; return true
} else if (event.which === 40) { // down } else if (event.which === 40) { // down
var $next; let $next
if ($selected.length === 0) { if ($selected.length === 0) {
$next = $first; $next = $first
} else { } else {
$next = $selected.next(); $next = $selected.next()
} }
if ($next.length === 0) { if ($next.length === 0) {
$next = $first; $next = $first
} }
$selected.removeClass("active"); $selected.removeClass('active')
$next.addClass("active"); $next.addClass('active')
event.preventDefault(); event.preventDefault()
event.stopPropagation(); event.stopPropagation()
return true; return true
} else if (event.which === 38) { // up } else if (event.which === 38) { // up
if ($selected.length === 0) { if ($selected.length === 0) {
$selected = $first; $selected = $first
} }
var $prev = $selected.prev(); let $prev = $selected.prev()
if ($prev.length === 0 || $prev.find("input").length > 0) { if ($prev.length === 0 || $prev.find('input').length > 0) {
$prev = $last; $prev = $last
} }
$selected.removeClass("active"); $selected.removeClass('active')
$prev.addClass("active"); $prev.addClass('active')
event.preventDefault(); event.preventDefault()
event.stopPropagation(); event.stopPropagation()
return true; return true
} }
}); })
}); })
}); })
@@ -1,78 +1,78 @@
/*global $, Morris, gettext, formatPrice*/ /* global i18nToString, formatPrice */
$(function () { $(function () {
// Question view // Question view
if (!$("#item_variations").length) { if (!$('#item_variations').length) {
return; return
} }
function update_variation_summary($el) { function update_variation_summary ($el) {
var var_names = Object.fromEntries( let var_names = Object.fromEntries(
$el $el
.find("input[name*=-value_]") .find('input[name*=-value_]')
.filter(function () { .filter(function () {
return !!this.value; return !!this.value
}) })
.map(function () { .map(function () {
return [[this.getAttribute("lang"), this.value]]; return [[this.getAttribute('lang'), this.value]]
}) })
.get() .get()
); )
var var_name = i18nToString(var_names); let var_name = i18nToString(var_names)
var price = $el.find("input[name*=-default_price]").val(); let price = $el.find('input[name*=-default_price]').val()
if (price) { if (price) {
var currency = $el.find("[name*=-default_price] + .input-group-addon").text(); let currency = $el.find('[name*=-default_price] + .input-group-addon').text()
price = formatPrice(price, currency); price = formatPrice(price, currency)
} }
$el.find(".variation-name").text(var_name); $el.find('.variation-name').text(var_name)
$el.find(".variation-price").text(price); $el.find('.variation-price').text(price)
$el.find(".variation-timeframe").toggleClass("variation-icon-hidden", !( $el.find('.variation-timeframe').toggleClass('variation-icon-hidden', !(
!!$el.find("input[name$=-available_from_0]").val() || !!$el.find('input[name$=-available_from_0]').val()
!!$el.find("input[name$=-available_until_0]").val() || !!$el.find('input[name$=-available_until_0]').val()
)); ))
$el.find(".variation-name").toggleClass("variation-disabled", !( $el.find('.variation-name').toggleClass('variation-disabled', !(
!!$el.find("input[name$=-active]").prop("checked") $el.find('input[name$=-active]').prop('checked')
)); ))
$el.find(".variation-voucher").toggleClass("variation-icon-hidden", !( $el.find('.variation-voucher').toggleClass('variation-icon-hidden', !(
!!$el.find("input[name$=-hide_without_voucher]").prop("checked") $el.find('input[name$=-hide_without_voucher]').prop('checked')
)); ))
$el.find(".variation-membership").toggleClass("variation-icon-hidden", !( $el.find('.variation-membership').toggleClass('variation-icon-hidden', !(
!!$el.find("input[name$=-require_membership]").prop("checked") $el.find('input[name$=-require_membership]').prop('checked')
)); ))
$el.find(".variation-warning").toggleClass("hidden", !( $el.find('.variation-warning').toggleClass('hidden', !(
$el.find(".alert-warning").length $el.find('.alert-warning').length
)); ))
$el.find(".variation-error").toggleClass("hidden", !( $el.find('.variation-error').toggleClass('hidden', !(
$el.find(".alert-danger, .has-error").length $el.find('.alert-danger, .has-error').length
)); ))
$el.find("input[name$=-limit_sales_channels]").each(function () { $el.find('input[name$=-limit_sales_channels]').each(function () {
$el.find(".variation-channel-" + $(this).val()).toggleClass("variation-icon-hidden", !( $el.find('.variation-channel-' + $(this).val()).toggleClass('variation-icon-hidden', !(
( (
$(this).closest("[data-formset-form]").find("input[name$=-all_sales_channels]").prop("checked") || $(this).closest('[data-formset-form]').find('input[name$=-all_sales_channels]').prop('checked')
$(this).prop("checked") || $(this).prop('checked')
) && ( ) && (
$("input[name=all_sales_channels]").prop("checked") || $('input[name=all_sales_channels]').prop('checked')
$("input[name=limit_sales_channels][value=" + $(this).val() + "]").prop("checked") || $('input[name=limit_sales_channels][value=' + $(this).val() + ']').prop('checked')
) )
)); ))
}) })
} }
$("#item_variations [data-formset-form]").each(function () { $('#item_variations [data-formset-form]').each(function () {
var $el = $(this); let $el = $(this)
update_variation_summary($el); update_variation_summary($el)
$(this).on("change dp.change", "input", function () {update_variation_summary($el)}); $(this).on('change dp.change', 'input', function () { update_variation_summary($el) })
}); })
$("input[name=limit_sales_channels] input[name=all_sales_channels]").on("change", function() { $('input[name=limit_sales_channels] input[name=all_sales_channels]').on('change', function () {
$("#item_variations [data-formset-form]").each(function () { $('#item_variations [data-formset-form]').each(function () {
update_variation_summary($(this)); update_variation_summary($(this))
}); })
}); })
$("#item_variations").on("formAdded", "details", function (event) { $('#item_variations').on('formAdded', 'details', function (event) {
var $el = $(event.target); let $el = $(event.target)
update_variation_summary($el); update_variation_summary($el)
$(this).on("change dp.change", "input", function () {update_variation_summary($el)}); $(this).on('change dp.change', 'input', function () { update_variation_summary($el) })
setup_collapsible_details($("#item_variations")); setup_collapsible_details($('#item_variations'))
form_handlers($(event.target)); form_handlers($(event.target))
}); })
}); })
+142 -145
View File
@@ -1,32 +1,32 @@
/*global $,u2f */ /* global base64js */
function b64enc(buf) { function b64enc (buf) {
return base64js.fromByteArray(buf) return base64js.fromByteArray(buf)
.replace(/\+/g, "-") .replace(/\+/g, '-')
.replace(/\//g, "_") .replace(/\//g, '_')
.replace(/=/g, ""); .replace(/=/g, '')
} }
function b64RawEnc(buf) { function b64RawEnc (buf) {
return base64js.fromByteArray(buf) return base64js.fromByteArray(buf)
.replace(/\+/g, "-") .replace(/\+/g, '-')
.replace(/\//g, "_"); .replace(/\//g, '_')
} }
function hexEncode(buf) { function hexEncode (buf) {
return Array.from(buf) return Array.from(buf)
.map(function(x) { .map(function (x) {
return ("0" + x.toString(16)).substr(-2); return ('0' + x.toString(16)).substr(-2)
}) })
.join(""); .join('')
} }
async function fetch_json(url, options) { async function fetch_json (url, options) {
const response = await fetch(url, options); const response = await fetch(url, options)
const body = await response.json(); const body = await response.json()
if (body.fail) if (body.fail)
throw body.fail; throw body.fail
return body; return body
} }
/** /**
@@ -35,27 +35,26 @@ async function fetch_json(url, options) {
* @param {Object} credentialCreateOptionsFromServer * @param {Object} credentialCreateOptionsFromServer
*/ */
const transformCredentialCreateOptions = function (credentialCreateOptionsFromServer) { const transformCredentialCreateOptions = function (credentialCreateOptionsFromServer) {
let {challenge, user, excludeCredentials} = credentialCreateOptionsFromServer; let { challenge, user, excludeCredentials } = credentialCreateOptionsFromServer
user.id = user.id.replace(/\_/g, "/").replace(/\-/g, "+"); user.id = user.id.replace(/_/g, '/').replace(/-/g, '+')
user.id = Uint8Array.from(atob(user.id), c => c.charCodeAt(0)); user.id = Uint8Array.from(atob(user.id), c => c.charCodeAt(0))
challenge = challenge.replace(/\_/g, "/").replace(/\-/g, "+"); challenge = challenge.replace(/_/g, '/').replace(/-/g, '+')
challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0)); challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0))
excludeCredentials = excludeCredentials.map(credentialDescriptor => { excludeCredentials = excludeCredentials.map(credentialDescriptor => {
let {id} = credentialDescriptor; let { id } = credentialDescriptor
id = id.replace(/\_/g, "/").replace(/\-/g, "+"); id = id.replace(/_/g, '/').replace(/-/g, '+')
id = Uint8Array.from(atob(id), c => c.charCodeAt(0)); id = Uint8Array.from(atob(id), c => c.charCodeAt(0))
return Object.assign({}, credentialDescriptor, {id}); return Object.assign({}, credentialDescriptor, { id })
}); })
const transformedCredentialCreateOptions = Object.assign( const transformedCredentialCreateOptions = Object.assign(
{}, credentialCreateOptionsFromServer, {}, credentialCreateOptionsFromServer,
{challenge, user, excludeCredentials}); { challenge, user, excludeCredentials })
return transformedCredentialCreateOptions;
};
return transformedCredentialCreateOptions
}
/** /**
* Transforms the binary data in the credential into base64 strings * Transforms the binary data in the credential into base64 strings
@@ -63,134 +62,132 @@ const transformCredentialCreateOptions = function (credentialCreateOptionsFromSe
* @param {PublicKeyCredential} newAssertion * @param {PublicKeyCredential} newAssertion
*/ */
const transformNewAssertionForServer = (newAssertion) => { const transformNewAssertionForServer = (newAssertion) => {
const attObj = new Uint8Array(newAssertion.response.attestationObject); const attObj = new Uint8Array(newAssertion.response.attestationObject)
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON); const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON)
const rawId = new Uint8Array(newAssertion.rawId); const rawId = new Uint8Array(newAssertion.rawId)
const transports = newAssertion.response.getTransports(); const transports = newAssertion.response.getTransports()
const authenticatorAttachment = newAssertion.authenticatorAttachment; const authenticatorAttachment = newAssertion.authenticatorAttachment
const registrationClientExtensions = newAssertion.getClientExtensionResults(); const registrationClientExtensions = newAssertion.getClientExtensionResults()
return {
id: newAssertion.id,
rawId: b64enc(rawId),
response: {
attestationObject: b64enc(attObj),
clientDataJSON: b64enc(clientDataJSON),
transports: transports,
},
type: newAssertion.type,
clientExtensionResults: JSON.stringify(registrationClientExtensions),
authenticatorAttachment: authenticatorAttachment,
};
};
return {
id: newAssertion.id,
rawId: b64enc(rawId),
response: {
attestationObject: b64enc(attObj),
clientDataJSON: b64enc(clientDataJSON),
transports: transports,
},
type: newAssertion.type,
clientExtensionResults: JSON.stringify(registrationClientExtensions),
authenticatorAttachment: authenticatorAttachment,
}
}
const transformCredentialRequestOptions = (credentialRequestOptionsFromServer) => { const transformCredentialRequestOptions = (credentialRequestOptionsFromServer) => {
let {challenge, allowCredentials} = credentialRequestOptionsFromServer; let { challenge, allowCredentials } = credentialRequestOptionsFromServer
challenge = challenge.replace(/\_/g, "/").replace(/\-/g, "+"); challenge = challenge.replace(/_/g, '/').replace(/-/g, '+')
challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0)); challenge = Uint8Array.from(atob(challenge), c => c.charCodeAt(0))
allowCredentials = allowCredentials.map(credentialDescriptor => { allowCredentials = allowCredentials.map(credentialDescriptor => {
let {id} = credentialDescriptor; let { id } = credentialDescriptor
id = id.replace(/\_/g, "/").replace(/\-/g, "+"); id = id.replace(/_/g, '/').replace(/-/g, '+')
id = Uint8Array.from(atob(id), c => c.charCodeAt(0)); id = Uint8Array.from(atob(id), c => c.charCodeAt(0))
return Object.assign({}, credentialDescriptor, {id}); return Object.assign({}, credentialDescriptor, { id })
}); })
const transformedCredentialRequestOptions = Object.assign( const transformedCredentialRequestOptions = Object.assign(
{}, {},
credentialRequestOptionsFromServer, credentialRequestOptionsFromServer,
{challenge, allowCredentials}); { challenge, allowCredentials })
return transformedCredentialRequestOptions; return transformedCredentialRequestOptions
}; }
/** /**
* Encodes the binary data in the assertion into strings for posting to the server. * Encodes the binary data in the assertion into strings for posting to the server.
* @param {PublicKeyCredential} newAssertion * @param {PublicKeyCredential} newAssertion
*/ */
const transformAssertionForServer = (newAssertion) => { const transformAssertionForServer = (newAssertion) => {
const authData = new Uint8Array(newAssertion.response.authenticatorData); const authData = new Uint8Array(newAssertion.response.authenticatorData)
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON); const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON)
const rawId = new Uint8Array(newAssertion.rawId); const rawId = new Uint8Array(newAssertion.rawId)
const sig = new Uint8Array(newAssertion.response.signature); const sig = new Uint8Array(newAssertion.response.signature)
const userHandle = new Uint8Array(newAssertion.response.userHandle); const userHandle = new Uint8Array(newAssertion.response.userHandle)
const assertionClientExtensions = newAssertion.getClientExtensionResults(); const assertionClientExtensions = newAssertion.getClientExtensionResults()
const authenticatorAttachment = newAssertion.authenticatorAttachment; const authenticatorAttachment = newAssertion.authenticatorAttachment
return { return {
id: newAssertion.id, id: newAssertion.id,
rawId: b64enc(rawId), rawId: b64enc(rawId),
type: newAssertion.type, type: newAssertion.type,
response: { response: {
authenticatorData: b64RawEnc(authData), authenticatorData: b64RawEnc(authData),
clientDataJSON: b64RawEnc(clientDataJSON), clientDataJSON: b64RawEnc(clientDataJSON),
signature: b64RawEnc(sig), signature: b64RawEnc(sig),
userHandle: b64RawEnc(userHandle), userHandle: b64RawEnc(userHandle),
}, },
authenticatorAttachment: authenticatorAttachment, authenticatorAttachment: authenticatorAttachment,
clientExtensionResults: JSON.stringify(assertionClientExtensions) clientExtensionResults: JSON.stringify(assertionClientExtensions)
}; }
}; }
const startRegister = async (e) => { const startRegister = async () => {
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(JSON.parse($("#webauthn-enroll").text())); const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(JSON.parse($('#webauthn-enroll').text()))
// request the authenticator(s) to create a new credential keypair. // request the authenticator(s) to create a new credential keypair.
let credential; let credential
try { try {
credential = await navigator.credentials.create({ credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreateOptions publicKey: publicKeyCredentialCreateOptions
}); })
} catch (err) { } catch (err) {
$("#webauthn-error").removeClass("hidden"); $('#webauthn-error').removeClass('hidden')
return console.error("Error creating credential:", err); return console.error('Error creating credential:', err)
} }
// we now have a new credential! We now need to encode the byte arrays // we now have a new credential! We now need to encode the byte arrays
// in the credential into strings, for posting to our server. // in the credential into strings, for posting to our server.
const newAssertionForServer = transformNewAssertionForServer(credential); const newAssertionForServer = transformNewAssertionForServer(credential)
$("#webauthn-response").val(JSON.stringify(newAssertionForServer)); $('#webauthn-response').val(JSON.stringify(newAssertionForServer))
$("#webauthn-form").submit(); $('#webauthn-form').submit()
}; }
const startLogin = async () => {
const transformedCredentialRequestOptions = transformCredentialRequestOptions(JSON.parse($('#webauthn-login').text()))
console.log(transformedCredentialRequestOptions)
const startLogin = async (e) => { // request the authenticator to create an assertion signature using the
const transformedCredentialRequestOptions = transformCredentialRequestOptions(JSON.parse($("#webauthn-login").text())); // credential private key
console.log(transformedCredentialRequestOptions); let assertion
try {
assertion = await navigator.credentials.get({
publicKey: transformedCredentialRequestOptions,
})
} catch (err) {
$('#webauthn-error').removeClass('hidden')
return console.error('Error when creating credential:', err)
}
// request the authenticator to create an assertion signature using the // we now have an authentication assertion! encode the byte arrays contained
// credential private key // in the assertion data as strings for posting to the server
let assertion; const transformedAssertionForServer = transformAssertionForServer(assertion)
try {
assertion = await navigator.credentials.get({
publicKey: transformedCredentialRequestOptions,
});
} catch (err) {
$("#webauthn-error").removeClass("hidden");
return console.error("Error when creating credential:", err);
}
// we now have an authentication assertion! encode the byte arrays contained // post the assertion to the server for verification.
// in the assertion data as strings for posting to the server $('input, select, textarea').prop('required', false)
const transformedAssertionForServer = transformAssertionForServer(assertion); $('#webauthn-response, #id_password').val(JSON.stringify(transformedAssertionForServer))
$('#webauthn-form').submit()
// post the assertion to the server for verification. }
$("input, select, textarea").prop("required", false);
$("#webauthn-response, #id_password").val(JSON.stringify(transformedAssertionForServer));
$("#webauthn-form").submit();
};
$(function () { $(function () {
$("#webauthn-progress").hide(); $('#webauthn-progress').hide()
if ($("#webauthn-enroll").length) { if ($('#webauthn-enroll').length) {
$("#webauthn-progress").show(); $('#webauthn-progress').show()
startRegister(); startRegister()
} else if ($("#webauthn-login").length) { } else if ($('#webauthn-login').length) {
$("#webauthn-progress").show(); $('#webauthn-progress').show()
startLogin(); startLogin()
} }
}); })
@@ -1,15 +1,15 @@
document.addEventListener("DOMContentLoaded", () => { document.addEventListener('DOMContentLoaded', () => {
const COOKIE_NAME = "__Host-pretix_csrftoken"; const COOKIE_NAME = '__Host-pretix_csrftoken'
const RELOAD_FLAG = "csrfReloadPerformed"; const RELOAD_FLAG = 'csrfReloadPerformed'
const hasCookie = document.cookie const hasCookie = document.cookie
.split("; ") .split('; ')
.some((c) => c.startsWith(COOKIE_NAME + "=")); .some((c) => c.startsWith(COOKIE_NAME + '='))
if (!hasCookie && !sessionStorage.getItem(RELOAD_FLAG)) { if (!hasCookie && !sessionStorage.getItem(RELOAD_FLAG)) {
sessionStorage.setItem(RELOAD_FLAG, "1"); sessionStorage.setItem(RELOAD_FLAG, '1')
location.reload(); location.reload()
} else if (hasCookie && sessionStorage.getItem(RELOAD_FLAG)) { } else if (hasCookie && sessionStorage.getItem(RELOAD_FLAG)) {
sessionStorage.removeItem(RELOAD_FLAG); sessionStorage.removeItem(RELOAD_FLAG)
} }
}); })
@@ -1,10 +1,8 @@
/*global $ */
$(function () { $(function () {
window.addEventListener("message", (event) => { window.addEventListener('message', (event) => {
if (event.data && event.data.__process === "popup_close") { if (event.data && event.data.__process === 'popup_close') {
window.close() window.close()
} }
}); })
window.opener.postMessage(JSON.parse($("#postmessage").text()), $("#origin").text()) window.opener.postMessage(JSON.parse($('#postmessage').text()), $('#origin').text())
}) })
+173 -173
View File
@@ -1,191 +1,191 @@
/*global $,gettext,ngettext */ /* global gettext,ngettext,moment,django */
var cart = { var cart = {
_deadline: null, _deadline: null,
_deadline_timeout: null, _deadline_timeout: null,
_deadline_call: 0, _deadline_call: 0,
_time_offset: 0, _time_offset: 0,
_prev_diff_minutes: 0, _prev_diff_minutes: 0,
_get_now: function () { _get_now: function () {
return moment().add(cart._time_offset, 'ms'); return moment().add(cart._time_offset, 'ms')
}, },
_calc_offset: function () { _calc_offset: function () {
if (typeof window.performance === "undefined") { if (typeof window.performance === 'undefined') {
return; return
} }
var perf = window.performance.timing; let perf = window.performance.timing
var server_time = Math.round(parseFloat($("body").attr("data-now")) * 1000); let server_time = Math.round(parseFloat($('body').attr('data-now')) * 1000)
// We use requestStart as we don't know how latency is distributed and we rather want to err on the safe side // We use requestStart as we don't know how latency is distributed and we rather want to err on the safe side
var client_time = perf.requestStart; let client_time = perf.requestStart
cart._time_offset = server_time - client_time; cart._time_offset = server_time - client_time
}, },
show_expiry_notification: function () { show_expiry_notification: function () {
document.getElementById("dialog-cart-extend").showModal(); document.getElementById('dialog-cart-extend').showModal()
cart._expiry_notified = true; cart._expiry_notified = true
}, },
draw_deadline: function () { draw_deadline: function () {
function pad(n, width, z) { function pad (n, width, z) {
z = z || '0'; z = z || '0'
n = n + ''; n = n + ''
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n
} }
cart._deadline_call++; cart._deadline_call++
if ((typeof django === 'undefined' || typeof django.gettext === 'undefined') && cart._deadline_call < 5) { if ((typeof django === 'undefined' || typeof django.gettext === 'undefined') && cart._deadline_call < 5) {
// Language files are not loaded yet, don't run during the first seconds // Language files are not loaded yet, don't run during the first seconds
return; return
} }
var now = cart._get_now(); let now = cart._get_now()
var diff_total_seconds = cart._deadline.diff(now) / 1000; let diff_total_seconds = cart._deadline.diff(now) / 1000
var diff_minutes = Math.floor(diff_total_seconds / 60); let diff_minutes = Math.floor(diff_total_seconds / 60)
var diff_seconds = Math.floor(diff_total_seconds % 60); let diff_seconds = Math.floor(diff_total_seconds % 60)
if (diff_minutes < 0) { if (diff_minutes < 0) {
$("#cart-deadline").text(gettext("The items in your cart are no longer reserved for you. You can still complete your order as long as theyre available.")); $('#cart-deadline').text(gettext('The items in your cart are no longer reserved for you. You can still complete your order as long as theyre available.'))
$("#cart-deadline-short").text( $('#cart-deadline-short').text(
gettext("Cart expired") gettext('Cart expired')
); )
if (!cart._deadline_timeout) { if (!cart._deadline_timeout) {
// no timeout => first time draw_deadline is invoked, but cart already expired => do not show dialog // no timeout => first time draw_deadline is invoked, but cart already expired => do not show dialog
cart._expiry_notified = true; cart._expiry_notified = true
} }
} else { } else {
if (diff_minutes !== cart._prev_diff_minutes) { if (diff_minutes !== cart._prev_diff_minutes) {
if (diff_minutes == 0) { if (diff_minutes == 0) {
$("#cart-deadline").text(gettext("Your cart is about to expire.")) $('#cart-deadline').text(gettext('Your cart is about to expire.'))
} else { } else {
$("#cart-deadline").text( $('#cart-deadline').text(
ngettext( ngettext(
"The items in your cart are reserved for you for one minute.", 'The items in your cart are reserved for you for one minute.',
"The items in your cart are reserved for you for {num} minutes.", 'The items in your cart are reserved for you for {num} minutes.',
diff_minutes diff_minutes
).replace(/\{num\}/g, diff_minutes) ).replace(/\{num\}/g, diff_minutes)
); )
} }
cart._prev_diff_minutes = diff_minutes; cart._prev_diff_minutes = diff_minutes
} }
$("#cart-deadline-short").text( $('#cart-deadline-short').text(
pad(diff_minutes.toString(), 2) + ':' + pad(diff_seconds.toString(), 2) pad(diff_minutes.toString(), 2) + ':' + pad(diff_seconds.toString(), 2)
); )
cart._deadline_timeout = window.setTimeout(cart.draw_deadline, 500); cart._deadline_timeout = window.setTimeout(cart.draw_deadline, 500)
} }
var already_expired = diff_total_seconds <= 0; let already_expired = diff_total_seconds <= 0
var can_extend_cart = diff_minutes < 3 && (already_expired || cart._deadline < cart._max_extend); let can_extend_cart = diff_minutes < 3 && (already_expired || cart._deadline < cart._max_extend)
$("#cart-extend-button").toggle(can_extend_cart); $('#cart-extend-button').toggle(can_extend_cart)
if (can_extend_cart && diff_total_seconds < 45) { if (can_extend_cart && diff_total_seconds < 45) {
if (!cart._expiry_notified) cart.show_expiry_notification(); if (!cart._expiry_notified) cart.show_expiry_notification()
$("#dialog-cart-extend-title").text(already_expired $('#dialog-cart-extend-title').text(already_expired
? gettext("Your cart has expired.") ? gettext('Your cart has expired.')
: gettext("Your cart is about to expire.")); : gettext('Your cart is about to expire.'))
$("#dialog-cart-extend-description").text(already_expired $('#dialog-cart-extend-description').text(already_expired
? gettext("The items in your cart are no longer reserved for you. You can still complete your order as long as they're available.") ? gettext('The items in your cart are no longer reserved for you. You can still complete your order as long as they\'re available.')
: gettext("Do you want to renew the reservation period?")); : gettext('Do you want to renew the reservation period?'))
$("#dialog-cart-extend .modal-card-confirm button").text(already_expired $('#dialog-cart-extend .modal-card-confirm button').text(already_expired
? gettext("Continue") ? gettext('Continue')
: gettext("Renew reservation")); : gettext('Renew reservation'))
} }
}, },
init: function () { init: function () {
"use strict"; 'use strict'
cart._calc_offset(); cart._calc_offset()
cart.set_deadline( cart.set_deadline(
$("#cart-deadline").attr("data-expires"), $('#cart-deadline').attr('data-expires'),
$("#cart-deadline").attr("data-max-expiry-extend") $('#cart-deadline').attr('data-max-expiry-extend')
); )
}, },
set_deadline: function (expiry, max_extend, renewed_message) { set_deadline: function (expiry, max_extend, renewed_message) {
"use strict"; 'use strict'
cart._expiry_notified = false; cart._expiry_notified = false
cart._deadline = moment(expiry); cart._deadline = moment(expiry)
if (cart._deadline_timeout) { if (cart._deadline_timeout) {
window.clearTimeout(cart._deadline_timeout); window.clearTimeout(cart._deadline_timeout)
} }
cart._deadline_timeout = null; cart._deadline_timeout = null
cart._max_extend = moment(max_extend); cart._max_extend = moment(max_extend)
cart.draw_deadline(); cart.draw_deadline()
} }
}; }
$(function () { $(function () {
"use strict"; 'use strict'
if ($("#cart-deadline").length) { if ($('#cart-deadline').length) {
cart.init(); cart.init()
$("#cart-extend-confirmation-button").hide().on("blur", function() { $('#cart-extend-confirmation-button').hide().on('blur', function () {
$(this).hide(); $(this).hide()
}); })
} }
$("#cart-extend-form").on("pretix:async-task-success", function(e, data) { $('#cart-extend-form').on('pretix:async-task-success', function (_e, data) {
if (data.success) { if (data.success) {
cart.set_deadline(data.expiry, data.max_expiry_extend); cart.set_deadline(data.expiry, data.max_expiry_extend)
} else { } else {
alert(data.message); alert(data.message)
} }
}); })
// renew-button in cart-panel is clicked, show inline dialog // renew-button in cart-panel is clicked, show inline dialog
$("#cart-extend-button").on("click", function() { $('#cart-extend-button').on('click', function () {
$("#cart-extend-form").one("pretix:async-task-success", function(e, data) { $('#cart-extend-form').one('pretix:async-task-success', function (_e, data) {
if (data.success) { if (data.success) {
document.getElementById("cart-extend-confirmation-dialog").show(); document.getElementById('cart-extend-confirmation-dialog').show()
} }
}); })
}); })
$("#cart-extend-confirmation-dialog").on("keydown", function (e) { $('#cart-extend-confirmation-dialog').on('keydown', function (e) {
if(e.key === "Escape") { if (e.key === 'Escape') {
this.close(); this.close()
} }
}); })
// renew-button in modal dialog is clicked, show modal dialog // renew-button in modal dialog is clicked, show modal dialog
$("#dialog-cart-extend form").submit(function() { $('#dialog-cart-extend form').submit(function () {
$("#cart-extend-form").one("pretix:async-task-success", function(e, data) { $('#cart-extend-form').one('pretix:async-task-success', function (_e, data) {
if (data.success) { if (data.success) {
$("#dialog-cart-extended-title").text(data.message); $('#dialog-cart-extended-title').text(data.message)
$("#dialog-cart-extended-description").text($("#cart-deadline").text()); $('#dialog-cart-extended-description').text($('#cart-deadline').text())
document.getElementById("dialog-cart-extended").showModal(); document.getElementById('dialog-cart-extended').showModal()
} }
}).submit(); }).submit()
}); })
$(".toggle-container").each(function() { $('.toggle-container').each(function () {
var summary = $(".toggle-summary", this); let summary = $('.toggle-summary', this)
var content = $("> :not(.toggle-summary)", this); let content = $('> :not(.toggle-summary)', this)
var toggle = summary.find(".toggle").on("click", function(e) { let toggle = summary.find('.toggle').on('click', function () {
this.ariaExpanded = !this.ariaExpanded; this.ariaExpanded = !this.ariaExpanded
if (this.classList.contains("toggle-remove")) summary.attr("hidden", true); if (this.classList.contains('toggle-remove')) summary.attr('hidden', true)
content.show().find(":input:visible").first().focus(); content.show().find(':input:visible').first().focus()
}); })
if (toggle.attr("aria-expanded")) { if (toggle.attr('aria-expanded')) {
content.hide(); content.hide()
} }
}); })
$(".cart-icon-details.collapse-lines").each(function () { $('.cart-icon-details.collapse-lines').each(function () {
var $content = $(this).find(".content"); let $content = $(this).find('.content')
var original_html = $content.html(); let original_html = $content.html()
var br_exp = /<br\s*\/?>/i; let br_exp = /<br\s*\/?>/i
$content.text(original_html.split(br_exp).join(', ')); $content.text(original_html.split(br_exp).join(', '))
if ($content.get(0).scrollWidth > $content.get(0).offsetWidth) { if ($content.get(0).scrollWidth > $content.get(0).offsetWidth) {
var $handler = $("<button>") let $handler = $('<button>')
.text($(this).attr("data-expand-text")) .text($(this).attr('data-expand-text'))
.addClass("btn btn-link collapse-handler") .addClass('btn btn-link collapse-handler')
.attr("type", "button") .attr('type', 'button')
.attr("aria-controls", $content.attr('id')) .attr('aria-controls', $content.attr('id'))
.attr("aria-expanded", "false"); .attr('aria-expanded', 'false')
$handler.on("click", function (ev) { $handler.on('click', function () {
$content.html(original_html).removeClass("content"); $content.html(original_html).removeClass('content')
$handler.attr("aria-expanded", "true").attr("aria-hidden", "true"); $handler.attr('aria-expanded', 'true').attr('aria-hidden', 'true')
$handler.hide(); $handler.hide()
}); })
$(this).append($handler); $(this).append($handler)
} }
}) })
}); })
@@ -1,145 +1,143 @@
/*global $ */
$(function () { $(function () {
window.pretix = window.pretix || {}; window.pretix = window.pretix || {}
var storage_key = $("#cookie-consent-storage-key").text(); let storage_key = $('#cookie-consent-storage-key').text()
var widget_consent = $("#cookie-consent-from-widget").text(); let widget_consent = $('#cookie-consent-from-widget').text()
var consent_checkboxes = $("#cookie-consent-details input[type=checkbox][name]"); let consent_checkboxes = $('#cookie-consent-details input[type=checkbox][name]')
var consent_modal = document.getElementById("cookie-consent-modal"); let consent_modal = document.getElementById('cookie-consent-modal')
function update_consent(consent, sessionOnly) { function update_consent (consent, sessionOnly) {
if (storage_key && window.sessionStorage && sessionOnly) { if (storage_key && window.sessionStorage && sessionOnly) {
if (!window.localStorage[storage_key] || window.localStorage[storage_key] !== JSON.stringify(consent)) { if (!window.localStorage[storage_key] || window.localStorage[storage_key] !== JSON.stringify(consent)) {
// No need to write to sessionStorage if the value is identical to the one in localStorage // No need to write to sessionStorage if the value is identical to the one in localStorage
window.sessionStorage[storage_key] = JSON.stringify(consent); window.sessionStorage[storage_key] = JSON.stringify(consent)
} }
} else if (storage_key && window.localStorage) { } else if (storage_key && window.localStorage) {
window.localStorage[storage_key] = JSON.stringify(consent); window.localStorage[storage_key] = JSON.stringify(consent)
// When saving permanent storage, clear session storage // When saving permanent storage, clear session storage
window.sessionStorage.removeItem(storage_key); window.sessionStorage.removeItem(storage_key)
} }
window.pretix.cookie_consent = consent; window.pretix.cookie_consent = consent
// Event() is not supported by IE11, see ployfill here: // Event() is not supported by IE11, see ployfill here:
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#polyfill // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#polyfill
var e = document.createEvent('CustomEvent'); let e = document.createEvent('CustomEvent')
e.initCustomEvent('pretix:cookie-consent:change', true, true, consent); e.initCustomEvent('pretix:cookie-consent:change', true, true, consent)
document.dispatchEvent(e) document.dispatchEvent(e)
} }
if (!storage_key) { if (!storage_key) {
// We are not on a page where the consent should run, fire the change event with empty consent but don't // We are not on a page where the consent should run, fire the change event with empty consent but don't
// actually store anything. // actually store anything.
update_consent(null, false); update_consent(null, false)
return; return
} }
if (!window.localStorage) { if (!window.localStorage) {
// Consent not supported. Even IE8 supports it, so we're on a weird embedded device. // Consent not supported. Even IE8 supports it, so we're on a weird embedded device.
// Let's just say we don't consent then. // Let's just say we don't consent then.
update_consent({}, false) update_consent({}, false)
return; return
} }
var storage_val, consent_source, save_for_session_only; let storage_val, consent_source, save_for_session_only
if (window.sessionStorage[storage_key]) { if (window.sessionStorage[storage_key]) {
// A manual input was given inside a widget. This is the user's last explicit choice and takes precedence // A manual input was given inside a widget. This is the user's last explicit choice and takes precedence
// as long as they are in the widget. // as long as they are in the widget.
storage_val = JSON.parse(window.sessionStorage[storage_key]); storage_val = JSON.parse(window.sessionStorage[storage_key])
consent_source = 'sessionStorage'; consent_source = 'sessionStorage'
save_for_session_only = true; save_for_session_only = true
} else if (widget_consent) { } else if (widget_consent) {
// An input was given through the widget. This takes precedence over localStorage as we need to assume the // An input was given through the widget. This takes precedence over localStorage as we need to assume the
// widget embedder is doing a correct job. If the user never visited the page without the widget, we also // widget embedder is doing a correct job. If the user never visited the page without the widget, we also
// use it to prefill local storage to save the user from seeing more cookie banners. (This will stop working // use it to prefill local storage to save the user from seeing more cookie banners. (This will stop working
// when browsers partition local storage of iframes, anyway.) If the user does have visited the page without // when browsers partition local storage of iframes, anyway.) If the user does have visited the page without
// the widget before and has a consent setting in localStorage, we respect the widget consent *only* within // the widget before and has a consent setting in localStorage, we respect the widget consent *only* within
// the widget -- hence, we save it into sessionStorage. We need to save it into sessionStorage because the // the widget -- hence, we save it into sessionStorage. We need to save it into sessionStorage because the
// widget_data value itself will not "survive" the entire lifetime of the tab, i.e. it is no longer present // widget_data value itself will not "survive" the entire lifetime of the tab, i.e. it is no longer present
// after the order was confirmed. // after the order was confirmed.
widget_consent = JSON.parse(widget_consent); widget_consent = JSON.parse(widget_consent)
storage_val = {}; storage_val = {}
consent_checkboxes.each(function () { consent_checkboxes.each(function () {
this.checked = storage_val[this.name] = widget_consent.indexOf(this.name) > -1; this.checked = storage_val[this.name] = widget_consent.indexOf(this.name) > -1
}); })
consent_source = 'widget'; consent_source = 'widget'
save_for_session_only = !!window.localStorage[storage_key]; save_for_session_only = !!window.localStorage[storage_key]
} else if (window.localStorage[storage_key]) { } else if (window.localStorage[storage_key]) {
// The user made a specific selection, let's use that. // The user made a specific selection, let's use that.
storage_val = JSON.parse(window.localStorage[storage_key]) || {}; storage_val = JSON.parse(window.localStorage[storage_key]) || {}
consent_source = 'localStorage'; consent_source = 'localStorage'
save_for_session_only = false; save_for_session_only = false
} else { } else {
// No consent given, dialog will be shown. // No consent given, dialog will be shown.
storage_val = {}; storage_val = {}
consent_source = 'new'; consent_source = 'new'
save_for_session_only = false; save_for_session_only = false
} }
var show_dialog = false; let show_dialog = false
consent_checkboxes.each(function () { consent_checkboxes.each(function () {
if (typeof storage_val[this.name] === "undefined") { if (typeof storage_val[this.name] === 'undefined') {
// A new cookie type has been added that we haven't asked for yet // A new cookie type has been added that we haven't asked for yet
if (consent_source === "widget") { if (consent_source === 'widget') {
// Trust the widget, keep it as "no consent" // Trust the widget, keep it as "no consent"
} else { } else {
show_dialog = true; show_dialog = true
} }
} else if (storage_val[this.name]) { } else if (storage_val[this.name]) {
this.checked = true; this.checked = true
} }
}) })
update_consent(storage_val, save_for_session_only); update_consent(storage_val, save_for_session_only)
if (!consent_modal) { if (!consent_modal) {
// Cookie consent is active, but no provider defined // Cookie consent is active, but no provider defined
return; return
} }
function _set_button_text () { function _set_button_text () {
var btn = $("#cookie-consent-button-no"); let btn = $('#cookie-consent-button-no')
btn.text( btn.text(
consent_checkboxes.filter(":checked").length ? consent_checkboxes.filter(':checked').length
btn.attr("data-detail-text") : ? btn.attr('data-detail-text')
btn.attr("data-summary-text") : btn.attr('data-summary-text')
); )
} }
if (consent_checkboxes.filter(":checked").length) { if (consent_checkboxes.filter(':checked').length) {
$("#cookie-consent-details").prop("open", true).find("> *:not(summary)").show(); $('#cookie-consent-details').prop('open', true).find('> *:not(summary)').show()
} }
_set_button_text(); _set_button_text()
if (show_dialog) { if (show_dialog) {
consent_modal.showModal(); consent_modal.showModal()
consent_modal.addEventListener("cancel", function() { consent_modal.addEventListener('cancel', function () {
// Dialog was initially shown, interpret Escape as „do not consent to new providers“ // Dialog was initially shown, interpret Escape as „do not consent to new providers“
var consent = {}; let consent = {}
consent_checkboxes.each(function () { consent_checkboxes.each(function () {
consent[this.name] = storage_val[this.name] || false; consent[this.name] = storage_val[this.name] || false
}); })
update_consent(consent, false); update_consent(consent, false)
}, {once : true}); }, { once: true })
} }
consent_modal.addEventListener("close", function () { consent_modal.addEventListener('close', function () {
if (!consent_modal.returnValue) {// ESC, do not save if (!consent_modal.returnValue) { // ESC, do not save
return; return
} }
var consent = {}; let consent = {}
var consent_all = consent_modal.returnValue == "yes"; let consent_all = consent_modal.returnValue == 'yes'
consent_checkboxes.each(function () { consent_checkboxes.each(function () {
consent[this.name] = this.checked = consent_all || this.checked; consent[this.name] = this.checked = consent_all || this.checked
}); })
if (consent_all) _set_button_text(); if (consent_all) _set_button_text()
update_consent(consent, false); update_consent(consent, false)
}); })
consent_checkboxes.on("change", _set_button_text); consent_checkboxes.on('change', _set_button_text)
$("#cookie-consent-reopen").on("click", function (e) { $('#cookie-consent-reopen').on('click', function (e) {
consent_modal.showModal() consent_modal.showModal()
e.preventDefault() e.preventDefault()
return true return true
}) })
}); })
+16 -16
View File
@@ -1,18 +1,18 @@
var inIframe = function () { let inIframe = function () {
try { try {
return window.self !== window.top; return window.self !== window.top
} catch (e) { } catch (_e) {
return true; return true
} }
}; }
if (inIframe()) { if (inIframe()) {
document.documentElement.classList.add('in-iframe'); document.documentElement.classList.add('in-iframe')
try { try {
window.parent.postMessage({ window.parent.postMessage({
type: "pretix:widget:title", type: 'pretix:widget:title',
title: document.title, title: document.title,
}, "*"); }, '*')
} catch (e) { } catch (e) {
console.error("Could not post message to parent.", e); console.error('Could not post message to parent.', e)
} }
} }
File diff suppressed because it is too large Load Diff
+426 -434
View File
@@ -1,134 +1,132 @@
/*global $ */ function questions_toggle_dependent (ev) {
function q_should_be_shown ($el) {
if (!$el.attr('data-question-dependency')) {
return true
}
function questions_toggle_dependent(ev) { let dependency_name = $el.attr('name').split('_')[0] + '_' + $el.attr('data-question-dependency')
function q_should_be_shown($el) { let dependency_values = JSON.parse($el.attr('data-question-dependency-values'))
if (!$el.attr('data-question-dependency')) { let $dependency_el
return true;
}
var dependency_name = $el.attr("name").split("_")[0] + "_" + $el.attr("data-question-dependency"); if ($('select[name=' + dependency_name + ']').length) {
var dependency_values = JSON.parse($el.attr("data-question-dependency-values")); // dependency is type C
var $dependency_el; $dependency_el = $('select[name=' + dependency_name + ']')
if (!$dependency_el.closest('.form-group').hasClass('dependency-hidden')) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && $.inArray($dependency_el.val(), dependency_values) > -1
}
} else if ($('input[type=checkbox][name=' + dependency_name + ']').length) {
// dependency type is B or M
if ($.inArray('True', dependency_values) > -1 || $.inArray('False', dependency_values) > -1) {
$dependency_el = $('input[name=' + dependency_name + ']')
if (!$dependency_el.closest('.form-group').hasClass('dependency-hidden')) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && (
($.inArray('True', dependency_values) > -1 && $dependency_el.prop('checked'))
|| ($.inArray('False', dependency_values) > -1 && !$dependency_el.prop('checked'))
)
}
} else {
let filter = ''
for (let i = 0; i < dependency_values.length; i++) {
if (filter) filter += ', '
filter += 'input[value=' + dependency_values[i] + '][name=' + dependency_name + ']:checked'
}
$dependency_el = $('input[value=' + dependency_values[0] + '][name=' + dependency_name + ']')
if (!$dependency_el.closest('.form-group').hasClass('dependency-hidden')) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && $(filter).length
}
}
}
}
if ($("select[name=" + dependency_name + "]").length) { $('[data-question-dependency]').each(function () {
// dependency is type C let $dependent = $(this).closest('.form-group')
$dependency_el = $("select[name=" + dependency_name + "]"); let is_shown = !$dependent.hasClass('dependency-hidden')
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things let should_be_shown = q_should_be_shown($(this))
return q_should_be_shown($dependency_el) && $.inArray($dependency_el.val(), dependency_values) > -1;
}
} else if ($("input[type=checkbox][name=" + dependency_name + "]").length) {
// dependency type is B or M
if ($.inArray("True", dependency_values) > -1 || $.inArray("False", dependency_values) > -1) {
$dependency_el = $("input[name=" + dependency_name + "]");
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && (
($.inArray("True", dependency_values) > -1 && $dependency_el.prop('checked'))
|| ($.inArray("False", dependency_values) > -1 && !$dependency_el.prop('checked'))
);
}
} else {
var filter = "";
for (var i = 0; i < dependency_values.length; i++) {
if (filter) filter += ", ";
filter += "input[value=" + dependency_values[i] + "][name=" + dependency_name + "]:checked";
}
$dependency_el = $("input[value=" + dependency_values[0] + "][name=" + dependency_name + "]");
if (!$dependency_el.closest(".form-group").hasClass("dependency-hidden")) { // do not show things that depend on hidden things
return q_should_be_shown($dependency_el) && $(filter).length;
}
}
}
}
$("[data-question-dependency]").each(function () { if (should_be_shown && !is_shown) {
var $dependent = $(this).closest(".form-group"); $dependent.stop().removeClass('dependency-hidden')
var is_shown = !$dependent.hasClass("dependency-hidden"); if (!ev) {
var should_be_shown = q_should_be_shown($(this)); $dependent.show()
} else {
if (should_be_shown && !is_shown) { $dependent.slideDown()
$dependent.stop().removeClass("dependency-hidden"); }
if (!ev) { $dependent.find('input.required-hidden, select.required-hidden, textarea.required-hidden').each(function () {
$dependent.show(); $(this).prop('required', true).removeClass('required-hidden')
} else { })
$dependent.slideDown(); } else if (!should_be_shown && is_shown) {
} if ($dependent.hasClass('has-error') || $dependent.find('.has-error').length) {
$dependent.find("input.required-hidden, select.required-hidden, textarea.required-hidden").each(function () { // Do not hide things with invalid validation
$(this).prop("required", true).removeClass("required-hidden"); return
}); }
} else if (!should_be_shown && is_shown) { $dependent.stop().addClass('dependency-hidden')
if ($dependent.hasClass("has-error") || $dependent.find(".has-error").length) { if (!ev) {
// Do not hide things with invalid validation $dependent.hide()
return; } else {
} $dependent.slideUp()
$dependent.stop().addClass("dependency-hidden"); }
if (!ev) { $dependent.find('input[required], select[required], textarea[required]').each(function () {
$dependent.hide(); $(this).prop('required', false).addClass('required-hidden')
} else { })
$dependent.slideUp(); }
} })
$dependent.find("input[required], select[required], textarea[required]").each(function () {
$(this).prop("required", false).addClass("required-hidden");
});
}
});
} }
function questions_init_photos(el) { function questions_init_photos (el) {
if (!FileReader) { if (!FileReader) {
// No browser support // No browser support
return return
} }
el.find("input[data-portrait-photo]").each(function () { el.find('input[data-portrait-photo]').each(function () {
var $inp = $(this) let $inp = $(this)
var $container = $inp.parent().parent() let $container = $inp.parent().parent()
$inp.prop("required", false) $inp.prop('required', false)
$container.find(".photo-input").addClass("hidden") $container.find('.photo-input').addClass('hidden')
$container.find(".photo-buttons").removeClass("hidden") $container.find('.photo-buttons').removeClass('hidden')
$container.find("button[data-action=upload]").click(function () { $container.find('button[data-action=upload]').click(function () {
$inp.click(); $inp.click()
}) })
var cropper = new Cropper($container.find(".photo-preview img").get(0), { var cropper = new Cropper($container.find('.photo-preview img').get(0), {
aspectRatio: 3 / 4, aspectRatio: 3 / 4,
viewMode: 1, viewMode: 1,
zoomable: false, zoomable: false,
crop: function (event) { crop: function (event) {
$container.find("input[type=hidden]").val(JSON.stringify(cropper.getData(true))); $container.find('input[type=hidden]').val(JSON.stringify(cropper.getData(true)))
}, },
}); })
/* This rule is very important, please don't ignore this */ /* This rule is very important, please don't ignore this */
$inp.on("change", function () { $inp.on('change', function () {
if (!$inp.get(0).files[0]) return if (!$inp.get(0).files[0]) return
$container.find("button[data-action=upload]").append("<span class='fa fa-spin fa-cog'></span>") $container.find('button[data-action=upload]').append('<span class=\'fa fa-spin fa-cog\'></span>')
var fr = new FileReader() let fr = new FileReader()
fr.onload = function () { fr.onload = function () {
cropper.replace(fr.result) cropper.replace(fr.result)
$container.find(".photo-preview").removeClass("hidden") $container.find('.photo-preview').removeClass('hidden')
$container.find("button[data-action=upload] .fa-spin").remove() $container.find('button[data-action=upload] .fa-spin').remove()
} }
fr.readAsDataURL($inp.get(0).files[0]) fr.readAsDataURL($inp.get(0).files[0])
}) })
}); })
} }
function questions_init_profiles(el) { function questions_init_profiles (el) {
/* /*
Auto-fill answers with profiles and addresses from customer account. Auto-fill answers with profiles and addresses from customer account.
There are two types of profiles: There are two types of profiles:
1. profiles for answers and 1. profiles for answers and
2. profiles for invoice addresses 2. profiles for invoice addresses
Both are handled the same way. Both are handled the same way.
Each form section/fieldset has its own auto-fill and save to profile Each form section/fieldset has its own auto-fill and save to profile
inputs. Each fieldset can define its own profiles by providing the inputs. Each fieldset can define its own profiles by providing the
HTML-attribute data-profiles-id, which defaults to "profiles_json". HTML-attribute data-profiles-id, which defaults to "profiles_json".
Currently only the invoice address fieldset uses this to load a Currently only the invoice address fieldset uses this to load a
different set of profiles. different set of profiles.
For each section each profiles answers are matched to inputs inside For each section each profiles answers are matched to inputs inside
@@ -141,349 +139,343 @@ function questions_init_profiles(el) {
in the original profile description, strikethrough which answer in the original profile description, strikethrough which answer
will be overwritten, followed by the new answer will be overwritten, followed by the new answer
add new answers with a + in front add new answers with a + in front
change <select> to a list of radio-buttons for multiline-display change <select> to a list of radio-buttons for multiline-display
of profiles? of profiles?
*/ */
var profilesById = {}; let profilesById = {}
function getProfilesById(id) { function getProfilesById (id) {
if (!(id in profilesById)) { if (!(id in profilesById)) {
var element = document.getElementById(id); let element = document.getElementById(id)
profilesById[id] = (!element || !element.textContent) ? [] : JSON.parse(element.textContent); profilesById[id] = (!element || !element.textContent) ? [] : JSON.parse(element.textContent)
} }
return profilesById[id]; return profilesById[id]
} }
function matchProfilesToInputs(profiles, scope) { function matchProfilesToInputs (profiles, scope) {
var filtered = []; let filtered = []
var data; let data
var matched_field; let matched_field
var addSpecialKey; let addSpecialKey
// special fields are used for substition with human readable or pre-formatted values // special fields are used for substition with human readable or pre-formatted values
var addSpecialFieldMap = { let addSpecialFieldMap = {
"country": "_country_for_address", country: '_country_for_address',
"state": "_state_for_address", state: '_state_for_address',
"name_parts_0": "_name", name_parts_0: '_name',
"attendee_name_parts_0": "_attendee_name", attendee_name_parts_0: '_attendee_name',
} }
for (var p of profiles) { for (let p of profiles) {
data = {}; data = {}
for (var key of Object.keys(p)) { for (let key of Object.keys(p)) {
if (key.startsWith("_") || p[key] === null) { if (key.startsWith('_') || p[key] === null) {
continue; continue
} }
matched_field = getMatchingInput(key, p[key], scope); matched_field = getMatchingInput(key, p[key], scope)
if (matched_field) { if (matched_field) {
// TODO: only add if no other answer matches same fields? // TODO: only add if no other answer matches same fields?
data[key] = { data[key] = {
"value": (typeof p[key] == "string") ? p[key] : p[key]["value"], value: (typeof p[key] == 'string') ? p[key] : p[key]['value'],
"field": matched_field field: matched_field
}; }
if (p[key]["label"]) data[key]["label"] = p[key]["label"]; if (p[key]['label']) data[key]['label'] = p[key]['label']
if (p[key]["type"]) data[key]["type"] = p[key]["type"]; if (p[key]['type']) data[key]['type'] = p[key]['type']
if (addSpecialKey = addSpecialFieldMap[key]) { if (addSpecialKey = addSpecialFieldMap[key]) {
data[addSpecialKey] = p[addSpecialKey]; data[addSpecialKey] = p[addSpecialKey]
} }
} }
} }
if (Object.keys(data).length) filtered.push(data); if (Object.keys(data).length) filtered.push(data)
}; };
return filtered; return filtered
} }
// For auto-fill with few inputs it could happen that multiple profiles // For auto-fill with few inputs it could happen that multiple profiles
// only match with the same fields that have the same values. It makes // only match with the same fields that have the same values. It makes
// no sense to show multiple profiles if all fill the same value(s). // no sense to show multiple profiles if all fill the same value(s).
// Therefore filter profiles to unique ones. // Therefore filter profiles to unique ones.
function uniqueProfiles(profiles) { function uniqueProfiles (profiles) {
var uniques = []; let uniques = []
var matchIndex; let matchIndex
for (var p of profiles) { for (var p of profiles) {
matchIndex = uniques.findIndex(function(element, index, array) { matchIndex = uniques.findIndex(function (element, index, array) {
return _profilesAreEqual(element, p); return _profilesAreEqual(element, p)
}); })
if (matchIndex === -1) uniques.push(p); if (matchIndex === -1) uniques.push(p)
} }
return uniques; return uniques
} }
function _profilesAreEqual(a, b) { function _profilesAreEqual (a, b) {
var keysA = Object.keys(a); let keysA = Object.keys(a)
var keysB = Object.keys(b); let keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false; if (keysA.length !== keysB.length) return false
keysA.sort(); keysA.sort()
keysB.sort(); keysB.sort()
if (!keysA.every((val, index) => val === keysB[index])) return false; if (!keysA.every((val, index) => val === keysB[index])) return false
if (!keysA.every((key, index) => a[key].value === b[key].value)) return false; if (!keysA.every((key, index) => a[key].value === b[key].value)) return false
return true; return true
} }
function _getInputForLabel(label) { function _getInputForLabel (label) {
if (!label) return null; if (!label) return null
var input; let input
if (label.getAttribute("for")) { if (label.getAttribute('for')) {
input = document.getElementById(label.getAttribute("for")); input = document.getElementById(label.getAttribute('for'))
if (input) return input; if (input) return input
} }
// for grouped inputs like phone number the "label" is more a fieldset/legend // for grouped inputs like phone number the "label" is more a fieldset/legend
return label.closest(".form-group").querySelectorAll("select, input, textarea"); return label.closest('.form-group').querySelectorAll('select, input, textarea')
} }
function getMatchingInput(key, answer, scope) { function getMatchingInput (key, answer, scope) {
var $label; let $label
// _0 and _1 are e.g. for phone-fields. name-fields have their parts/keys already split // _0 and _1 are e.g. for phone-fields. name-fields have their parts/keys already split
var $fields = $('[name$="' + key + '"], [name$="' + key + '_0"], [name$="' + key + '_1"]', scope).not(":disabled"); let $fields = $('[name$="' + key + '"], [name$="' + key + '_0"], [name$="' + key + '_1"]', scope).not(':disabled')
if ($fields.length) return $fields; if ($fields.length) return $fields
if (!answer) return null; if (!answer) return null
if (answer.identifier) { if (answer.identifier) {
$label = $('[data-identifier="' + answer.identifier + '"]', scope); $label = $('[data-identifier="' + answer.identifier + '"]', scope)
var input = _getInputForLabel($label.get(0)); var input = _getInputForLabel($label.get(0))
if (input) return $(input); if (input) return $(input)
} }
for (var label of scope.getElementsByTagName("label")) { for (let label of scope.getElementsByTagName('label')) {
if (label.textContent === answer.label) { if (label.textContent === answer.label) {
var input = _getInputForLabel(label); var input = _getInputForLabel(label)
if (input) return $(input); if (input) return $(input)
break; break
} }
} }
return null; return null
} }
function formatAnswerHumanReadable(answer) { function formatAnswerHumanReadable (answer) {
if (typeof answer == "string") return answer; if (typeof answer == 'string') return answer
if (typeof answer == "number") return answer.toString(); if (typeof answer == 'number') return answer.toString()
if (!answer && answer !== false) return ""; if (!answer && answer !== false) return ''
var value = answer.value; let value = answer.value
if ("type" in answer) { if ('type' in answer) {
if (answer.type === "TEL") { if (answer.type === 'TEL') {
// TODO: format phone number with locale or use pre-formatted like with names? // TODO: format phone number with locale or use pre-formatted like with names?
return value; return value
} }
if (answer.type === "W") { if (answer.type === 'W') {
return moment(value).format(document.body.getAttribute("data-datetimeformat")); return moment(value).format(document.body.getAttribute('data-datetimeformat'))
} }
if (answer.type === "D") { if (answer.type === 'D') {
return moment(value).format(document.body.getAttribute("data-dateformat")); return moment(value).format(document.body.getAttribute('data-dateformat'))
} }
if (answer.type === "H") { if (answer.type === 'H') {
var format = document.body.getAttribute("data-timeformat"); let format = document.body.getAttribute('data-timeformat')
return moment(value, "HH:mm:ss").format(format); return moment(value, 'HH:mm:ss').format(format)
} }
if (answer.type === "B") { if (answer.type === 'B') {
return value ? gettext("Yes") : gettext("No"); return value ? gettext('Yes') : gettext('No')
} }
} }
if (typeof value == "string") return value; if (typeof value == 'string') return value
if (!value) return ""; if (!value) return ''
return Object.values(value).join(", "); return Object.values(value).join(', ')
} }
// TODO: add as few info as possible to make a distinction between available profiles? // TODO: add as few info as possible to make a distinction between available profiles?
function labelForProfile(p, profiles, scope = null) { function labelForProfile (p, profiles, scope = null) {
var parts = describeProfile(p); let parts = describeProfile(p)
var label = parts.join(", "); let label = parts.join(', ')
if (label.length > 74) { if (label.length > 74) {
var len = label.lastIndexOf(' ', 74); let len = label.lastIndexOf(' ', 74)
label = label.substr(0, Math.max(len, 48)) + " …"; label = label.substr(0, Math.max(len, 48)) + ' …'
} }
return label; return label
} }
function getAnswer(a) { function getAnswer (a) {
if (typeof a == "string") return a; if (typeof a == 'string') return a
return a && "value" in a ? a["value"] : ""; return a && 'value' in a ? a['value'] : ''
} }
function describeProfile(p) { function describeProfile (p) {
if (!p) return []; if (!p) return []
var lines = [ let lines = [
getAnswer(p["company"]), getAnswer(p['company']),
p["_name"], p['_name'],
[p["_attendee_name"], getAnswer(p["attendee_email"])].filter(v => v).join(", "), [p['_attendee_name'], getAnswer(p['attendee_email'])].filter(v => v).join(', '),
[ [
getAnswer(p["street"]), getAnswer(p['street']),
[getAnswer(p["zipcode"]), getAnswer(p["city"]), p["_state_for_address"]].filter(v => v).join(" "), [getAnswer(p['zipcode']), getAnswer(p['city']), p['_state_for_address']].filter(v => v).join(' '),
p["_country_for_address"] p['_country_for_address']
].filter(v => v).join(", ") ].filter(v => v).join(', ')
]; ]
lines = lines.filter(line => line && line.trim()); lines = lines.filter(line => line && line.trim())
var answer; let answer
var label; let label
for (var key of Object.keys(p)) { for (let key of Object.keys(p)) {
if (!key.startsWith("question_")) continue; if (!key.startsWith('question_')) continue
answer = p[key]; answer = p[key]
label = answer["label"] || ""; label = answer['label'] || ''
lines.push(label + ("!?.:".split("").indexOf(label.slice(-1)) > -1 ? " " : ": ") + formatAnswerHumanReadable(answer)) lines.push(label + ('!?.:'.split('').indexOf(label.slice(-1)) > -1 ? ' ' : ': ') + formatAnswerHumanReadable(answer))
} }
return lines; return lines
} }
function escapeHTML(t) { function escapeHTML (t) {
return $("<div>").text(t).get(0).innerHTML; return $('<div>').text(t).get(0).innerHTML
} }
function describeProfileHTML(p) { function describeProfileHTML (p) {
return describeProfile(p).map(escapeHTML).join("<br>"); return describeProfile(p).map(escapeHTML).join('<br>')
} }
function _updateDescription (select, profile, $help) {
// show additional description if different from option-text
let label = select.options[select.selectedIndex].textContent
let lines = describeProfile(profile).map(escapeHTML)
if (!lines.length || label === lines.join(', ')) {
$help.slideUp(function () {
$help.html('')
})
} else {
$help.html(lines.join('<br>')).slideDown()
}
}
function _updateDescription(select, profile, $help) { function setupSaveToProfile (scope, profiles) {
// show additional description if different from option-text let $select = $('[name$="saved_id"]', scope)
var label = select.options[select.selectedIndex].textContent; let $selectContainer = $select.closest('.form-group').addClass('profile-save-id')
var lines = describeProfile(profile).map(escapeHTML); let $checkbox = $('[name$="save"]', scope)
if (!lines.length || label === lines.join(", ")) { let $checkboxContainer = $checkbox.closest('.form-group').addClass('profile-save')
$help.slideUp(function() { let $help = $selectContainer.find('.help-block')
$help.html("");
});
}
else {
$help.html(lines.join("<br>")).slideDown();
}
}
function setupSaveToProfile(scope, profiles) { let $container = $('<div class=\'profile-save-container js-do-not-copy-answers\'></div>')
var $select = $('[name$="saved_id"]', scope); $selectContainer.after($container)
var $selectContainer = $select.closest(".form-group").addClass("profile-save-id"); $container.append($checkboxContainer)
var $checkbox = $('[name$="save"]', scope); $container.append($selectContainer)
var $checkboxContainer = $checkbox.closest(".form-group").addClass("profile-save");
var $help = $selectContainer.find(".help-block");
var $container = $("<div class='profile-save-container js-do-not-copy-answers'></div>"); if (!profiles || !profiles.length) {
$selectContainer.after($container); $selectContainer.hide()
$container.append($checkboxContainer); return
$container.append($selectContainer); }
if (!profiles || !profiles.length) {
$selectContainer.hide();
return;
}
$checkbox.change(function() { $checkbox.change(function () {
if (this.checked) $selectContainer.slideDown(); if (this.checked) $selectContainer.slideDown()
else $selectContainer.slideUp(); else $selectContainer.slideUp()
}); })
for (var p of profiles) { for (let p of profiles) {
$select.append($('<option>').attr('value', p._pk).text(labelForProfile(p, profiles))); $select.append($('<option>').attr('value', p._pk).text(labelForProfile(p, profiles)))
} }
$select.append('<option value="" disabled></option>'); $select.append('<option value="" disabled></option>')
$select.append($select.find("option").first()); $select.append($select.find('option').first())
$select.get(0).selectedIndex = 0; $select.get(0).selectedIndex = 0
$select.change(function() { $select.change(function () {
_updateDescription(this, profiles[this.selectedIndex], $help); _updateDescription(this, profiles[this.selectedIndex], $help)
}).trigger("change"); }).trigger('change')
$checkbox.trigger("change"); $checkbox.trigger('change')
} }
// setup auto-fill for each scope/fieldset // setup auto-fill for each scope/fieldset
// match profiles answers to inputs in scope // match profiles answers to inputs in scope
// if none match, do not show auto-fill // if none match, do not show auto-fill
// if one matches, only show button to auto-fill // if one matches, only show button to auto-fill
// else show select with profiles and button to auto-fill // else show select with profiles and button to auto-fill
function setupAutoFill(scope, profiles) { function setupAutoFill (scope, profiles) {
var matchedProfiles = uniqueProfiles(matchProfilesToInputs(profiles, scope)); let matchedProfiles = uniqueProfiles(matchProfilesToInputs(profiles, scope))
if (!matchedProfiles.length) { if (!matchedProfiles.length) {
$(scope).addClass("profile-none-matched"); $(scope).addClass('profile-none-matched')
return; return
} }
var selectedProfile = matchedProfiles[0]; let selectedProfile = matchedProfiles[0]
var $select = $(".profile-select", scope); let $select = $('.profile-select', scope)
var $button = $(".profile-apply", scope); let $button = $('.profile-apply', scope)
var $help = $(".profile-desc", scope); let $help = $('.profile-desc', scope)
if (matchedProfiles.length === 1) { if (matchedProfiles.length === 1) {
$(".profile-select-control", scope).hide().parent().addClass("form-control-text"); $('.profile-select-control', scope).hide().parent().addClass('form-control-text')
$help.html(describeProfileHTML(selectedProfile)).addClass("single-profile-desc").after($button); $help.html(describeProfileHTML(selectedProfile)).addClass('single-profile-desc').after($button)
} } else {
else { let i = 0
var i = 0; for (p of matchedProfiles) {
for (p of matchedProfiles) { $select.append($('<option>').text(labelForProfile(p, matchedProfiles, scope)).attr('value', i))
$select.append($("<option>").text(labelForProfile(p, matchedProfiles, scope)).attr("value", i)); i++
i++; }
} $select.change(function () {
$select.change(function() { selectedProfile = matchedProfiles[this.value]
selectedProfile = matchedProfiles[this.value]; _updateDescription(this, selectedProfile, $help)
_updateDescription(this, selectedProfile, $help); }).trigger('change')
}).trigger("change"); }
} // Add-Ons sit on same level as their parent product scope
// Add-Ons sit on same level as their parent product scope // Therefore use .prevUntil("legend") as an Add-On is
// Therefore use .prevUntil("legend") as an Add-On is // offset by a <legend>
// offset by a <legend> // if no <legend> is present e.g. on invoice-address the
// if no <legend> is present e.g. on invoice-address the // containing <summary> would be selected, which is not what we want
// containing <summary> would be selected, which is not what we want $(scope).prevUntil('legend').not('summary').addClass('profile-pre-select')
$(scope).prevUntil("legend").not("summary").addClass("profile-pre-select");
$button.click(function() { $button.click(function () {
Object.keys(selectedProfile).forEach(function(key) { Object.keys(selectedProfile).forEach(function (key) {
var answer = selectedProfile[key].value; let answer = selectedProfile[key].value
var $field = selectedProfile[key].field; let $field = selectedProfile[key].field
if (!$field || !$field.length) return; if (!$field || !$field.length) return
if ($field.attr("type") === "checkbox") {
if (answer === true || answer === false) {
// boolean
$field.prop("checked", answer).trigger("change");
}
else if (typeof answer !== 'string') {
answer = Object.keys(answer);
$field.each(function() {
var checked = answer.indexOf(this.value) > -1;
if (checked !== this.checked) {
this.checked = checked;
$(this).trigger("change");
}
});
}
} else if ($field.attr("type") === "radio") {
$field.filter('[value="' + answer + '"]').prop("checked", true).trigger("change");
} else if ($field.length > 1) {
// multiple matching fields, could be phone number or datetime
var $field_0 = $field.filter('[name$="_0"]');
var $field_1 = $field.filter('[name$="_1"]');
if (answer.substr(0, 1) === "+") {
var prefix = "";
var options = $field_0.get(0).options;
for (var i = 0; i < options.length; i++) {
var v = options[i].value;
if (v && answer.substr(0, v.length) === v) {
prefix = v;
break;
}
}
var number = answer.substr(prefix.length);
$field_0.val(prefix).trigger("change");
$field_1.val(number).trigger("change");
}
else if ($field_0.hasClass("datepickerfield")) {
$field_0.data('DateTimePicker').date(moment(answer));
$field_1.data('DateTimePicker').date(moment(answer));
}
} else if ($field.is("select")) {
if (answer && typeof answer !== 'string') {
answer = Object.keys(answer);
}
// save answer as data-attribute so if external event changes select-element/options it can select correct entries
// currently used when country => state changes
$field.prop("data-selected-value", answer);
$field.find("option").each(function() {
this.selected = this.value === answer || (answer && answer.indexOf && answer.indexOf(this.value) > -1);
});
$field.trigger("change");
} else {
if ($field.hasClass("datepickerfield")) {
$field.data('DateTimePicker').date(moment(answer));
}
else {
$field.val(answer).trigger("change");
}
}
});
})
}
// each fieldset is its own scope for auto-fill and save if ($field.attr('type') === 'checkbox') {
el.find(".profile-scope").each(function () { if (answer === true || answer === false) {
var profiles = getProfilesById(this.getAttribute("data-profiles-id") || "profiles_json"); // boolean
$field.prop('checked', answer).trigger('change')
} else if (typeof answer !== 'string') {
answer = Object.keys(answer)
$field.each(function () {
let checked = answer.indexOf(this.value) > -1
if (checked !== this.checked) {
this.checked = checked
$(this).trigger('change')
}
})
}
} else if ($field.attr('type') === 'radio') {
$field.filter('[value="' + answer + '"]').prop('checked', true).trigger('change')
} else if ($field.length > 1) {
// multiple matching fields, could be phone number or datetime
let $field_0 = $field.filter('[name$="_0"]')
let $field_1 = $field.filter('[name$="_1"]')
if (answer.substr(0, 1) === '+') {
let prefix = ''
let options = $field_0.get(0).options
for (let i = 0; i < options.length; i++) {
let v = options[i].value
if (v && answer.substr(0, v.length) === v) {
prefix = v
break
}
}
let number = answer.substr(prefix.length)
$field_0.val(prefix).trigger('change')
$field_1.val(number).trigger('change')
} else if ($field_0.hasClass('datepickerfield')) {
$field_0.data('DateTimePicker').date(moment(answer))
$field_1.data('DateTimePicker').date(moment(answer))
}
} else if ($field.is('select')) {
if (answer && typeof answer !== 'string') {
answer = Object.keys(answer)
}
// save answer as data-attribute so if external event changes select-element/options it can select correct entries
// currently used when country => state changes
$field.prop('data-selected-value', answer)
$field.find('option').each(function () {
this.selected = this.value === answer || (answer && answer.indexOf && answer.indexOf(this.value) > -1)
})
$field.trigger('change')
} else {
if ($field.hasClass('datepickerfield')) {
$field.data('DateTimePicker').date(moment(answer))
} else {
$field.val(answer).trigger('change')
}
}
})
})
}
setupSaveToProfile(this, profiles); // each fieldset is its own scope for auto-fill and save
setupAutoFill(this, profiles); el.find('.profile-scope').each(function () {
let profiles = getProfilesById(this.getAttribute('data-profiles-id') || 'profiles_json')
this.classList.add("profile-select-initialized"); setupSaveToProfile(this, profiles)
}); setupAutoFill(this, profiles)
this.classList.add('profile-select-initialized')
})
} }
+43 -45
View File
@@ -1,53 +1,51 @@
/*global $ */
$(function () { $(function () {
var popup_window = null let popup_window = null
var popup_check_interval = null let popup_check_interval = null
$("a[data-open-in-popup-window]").on("click", function (e) { $('a[data-open-in-popup-window]').on('click', function (e) {
e.preventDefault() e.preventDefault()
$("#popupmodal a").attr("href", this.href) $('#popupmodal a').attr('href', this.href)
var url = this.href let url = this.href
if (url.includes("?")) { if (url.includes('?')) {
url += "&popup_origin=" + window.location.origin url += '&popup_origin=' + window.location.origin
} else { } else {
url += "?popup_origin=" + window.location.origin url += '?popup_origin=' + window.location.origin
} }
popup_window = window.open( popup_window = window.open(
url, url,
"presale-popup", 'presale-popup',
"scrollbars=yes,resizable=yes,status=yes,location=yes,toolbar=no,menubar=no,width=940,height=620,left=50,top=50" 'scrollbars=yes,resizable=yes,status=yes,location=yes,toolbar=no,menubar=no,width=940,height=620,left=50,top=50'
) )
$("body").addClass("has-popup has-modal-dialog") $('body').addClass('has-popup has-modal-dialog')
$("#popupmodal").removeAttr("hidden"); $('#popupmodal').removeAttr('hidden')
popup_check_interval = window.setInterval(function () { popup_check_interval = window.setInterval(function () {
if (popup_window.closed) { if (popup_window.closed) {
$("body").removeClass("has-popup has-modal-dialog") $('body').removeClass('has-popup has-modal-dialog')
$("#popupmodal").attr("hidden", true); $('#popupmodal').attr('hidden', true)
window.clearInterval(popup_check_interval) window.clearInterval(popup_check_interval)
} }
}, 250) }, 250)
return false return false
}); })
window.addEventListener("message", function (event) { window.addEventListener('message', function (event) {
if (event.source !== popup_window) if (event.source !== popup_window)
return return
if (event.data && event.data.__process === "customer_sso_popup") { if (event.data && event.data.__process === 'customer_sso_popup') {
if (event.data.status === "ok") { if (event.data.status === 'ok') {
$("#customer_login .alert.alert-danger").addClass("hidden"); $('#customer_login .alert.alert-danger').addClass('hidden')
$("#login_sso_data").val(event.data.value) $('#login_sso_data').val(event.data.value)
$("#login_sso_data").closest("form").get(0).submit() $('#login_sso_data').closest('form').get(0).submit()
} else { } else {
$("#customer_login .alert.alert-danger").html(event.data.value); $('#customer_login .alert.alert-danger').html(event.data.value)
$("#customer_login .alert.alert-danger").removeClass("hidden"); $('#customer_login .alert.alert-danger').removeClass('hidden')
} }
event.source.postMessage({'__process': 'popup_close'}, "*") event.source.postMessage({ __process: 'popup_close' }, '*')
} }
console.log(event) console.log(event)
}, false); }, false)
}) })
@@ -1,71 +1,72 @@
'use strict'; /* global google, gettext */
'use strict'
var walletdetection = { let walletdetection = {
applepay: async function () { applepay: async function () {
// This is a weak check for Apple Pay - in order to do a proper check, we would need to also call // This is a weak check for Apple Pay - in order to do a proper check, we would need to also call
// canMakePaymentsWithActiveCard(merchantIdentifier) // canMakePaymentsWithActiveCard(merchantIdentifier)
return !!(window.ApplePaySession && window.ApplePaySession.canMakePayments()); return !!(window.ApplePaySession && window.ApplePaySession.canMakePayments())
}, },
googlepay: async function () { googlepay: async function () {
// Checking for Google Pay is a little bit more involved, since it requires including the Google Pay JS SDK, and // Checking for Google Pay is a little bit more involved, since it requires including the Google Pay JS SDK, and
// providing a lot of information. // providing a lot of information.
// So for the time being, we only check if Google Pay is available in TEST-mode, which should hopefully give us a // So for the time being, we only check if Google Pay is available in TEST-mode, which should hopefully give us a
// good enough idea if Google Pay could be present on this device; even though there are still a lot of other // good enough idea if Google Pay could be present on this device; even though there are still a lot of other
// factors that could inhibit Google Pay from actually being offered to the customer. // factors that could inhibit Google Pay from actually being offered to the customer.
return $.ajax({ return $.ajax({
url: 'https://pay.google.com/gp/p/js/pay.js', url: 'https://pay.google.com/gp/p/js/pay.js',
dataType: 'script', dataType: 'script',
}).then(function() { }).then(function () {
const paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'}); const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' })
return paymentsClient.isReadyToPay({ return paymentsClient.isReadyToPay({
apiVersion: 2, apiVersion: 2,
apiVersionMinor: 0, apiVersionMinor: 0,
allowedPaymentMethods: [{ allowedPaymentMethods: [{
type: 'CARD', type: 'CARD',
parameters: { parameters: {
allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"], allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
allowedCardNetworks: ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"] allowedCardNetworks: ['AMEX', 'DISCOVER', 'INTERAC', 'JCB', 'MASTERCARD', 'VISA']
} }
}], }],
}) })
}).then(function (response) { }).then(function (response) {
return !!response.result; return !!response.result
}); })
}, },
name_map: { name_map: {
applepay: gettext('Apple Pay'), applepay: gettext('Apple Pay'),
googlepay: gettext('Google Pay'), googlepay: gettext('Google Pay'),
} }
} }
$(function () { $(function () {
const wallets = $('[data-wallets]') const wallets = $('[data-wallets]')
.map(function(index, pm) { .map(function (_index, pm) {
return pm.getAttribute("data-wallets").split("|"); return pm.getAttribute('data-wallets').split('|')
}) })
.get() .get()
.flat() .flat()
.filter(function(item, pos, self) { .filter(function (item, pos, self) {
// filter out empty or duplicate values // filter out empty or duplicate values
return item && self.indexOf(item) == pos; return item && self.indexOf(item) == pos
}); })
wallets.forEach(function(wallet) { wallets.forEach(function (wallet) {
const labels = $('[data-wallets*='+wallet+'] + .accordion-label-text') const labels = $('[data-wallets*=' + wallet + '] + .accordion-label-text')
.append('<span class="wallet wallet-loading" data-wallet="'+wallet+'"> <span aria-hidden="true" class="fa fa-cog fa-spin"></span></span>') .append('<span class="wallet wallet-loading" data-wallet="' + wallet + '"> <span aria-hidden="true" class="fa fa-cog fa-spin"></span></span>')
walletdetection[wallet]() walletdetection[wallet]()
.then(function(result) { .then(function (result) {
const spans = labels.find(".wallet-loading[data-wallet=" + wallet + "]"); const spans = labels.find('.wallet-loading[data-wallet=' + wallet + ']')
if (result) { if (result) {
spans.removeClass('wallet-loading').hide().text(', ' + walletdetection.name_map[wallet]).fadeIn(300); spans.removeClass('wallet-loading').hide().text(', ' + walletdetection.name_map[wallet]).fadeIn(300)
} else { } else {
spans.remove(); spans.remove()
} }
}) })
.catch(function(result) { .catch(function () {
labels.find(".wallet-loading[data-wallet=" + wallet + "]").remove(); labels.find('.wallet-loading[data-wallet=' + wallet + ']').remove()
}) })
}); })
}); })
@@ -2,80 +2,80 @@
// by John Friend, https://github.com/jfriend00/docReady // by John Friend, https://github.com/jfriend00/docReady
// MIT License // MIT License
(function(funcName, baseObj) { (function (funcName, baseObj) {
"use strict"; 'use strict'
// The public function name defaults to window.docReady // 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 // 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 // if you want to put them in a different namespace and those will be used instead of
// window.docReady(...) // window.docReady(...)
funcName = funcName || "docReady"; funcName = funcName || 'docReady'
baseObj = baseObj || window; baseObj = baseObj || window
var readyList = []; let readyList = []
var readyFired = false; let readyFired = false
var readyEventHandlersInstalled = false; let readyEventHandlersInstalled = false
// call this when the document is ready // call this when the document is ready
// this function protects itself against being called more than once // this function protects itself against being called more than once
function ready() { function ready () {
if (!readyFired) { if (!readyFired) {
// this must be set to true before we start calling callbacks // this must be set to true before we start calling callbacks
readyFired = true; readyFired = true
for (var i = 0; i < readyList.length; i++) { for (let i = 0; i < readyList.length; i++) {
// if a callback here happens to add new ready handlers, // if a callback here happens to add new ready handlers,
// the docReady() function will see that it already fired // the docReady() function will see that it already fired
// and will schedule the callback to run right after // and will schedule the callback to run right after
// this event loop finishes so all handlers will still execute // this event loop finishes so all handlers will still execute
// in order and no new ones will be added to the readyList // in order and no new ones will be added to the readyList
// while we are processing the list // while we are processing the list
readyList[i].fn.call(window, readyList[i].ctx); readyList[i].fn.call(window, readyList[i].ctx)
} }
// allow any closures held by these functions to free // allow any closures held by these functions to free
readyList = []; readyList = []
} }
} }
function readyStateChange() { function readyStateChange () {
if ( document.readyState === "complete" ) { if (document.readyState === 'complete') {
ready(); ready()
} }
} }
// This is the one public interface // This is the one public interface
// docReady(fn, context); // docReady(fn, context);
// the context argument is optional - if present, it will be passed // the context argument is optional - if present, it will be passed
// as an argument to the callback // as an argument to the callback
baseObj[funcName] = function(callback, context) { baseObj[funcName] = function (callback, context) {
if (typeof callback !== "function") { if (typeof callback !== 'function') {
throw new TypeError("callback for docReady(fn) must be a function"); throw new TypeError('callback for docReady(fn) must be a function')
} }
// if ready has already fired, then just schedule the callback // if ready has already fired, then just schedule the callback
// to fire asynchronously, but right away // to fire asynchronously, but right away
if (readyFired) { if (readyFired) {
setTimeout(function() {callback(context);}, 1); setTimeout(function () { callback(context) }, 1)
return; return
} else { } else {
// add the function and context to the list // add the function and context to the list
readyList.push({fn: callback, ctx: context}); readyList.push({ fn: callback, ctx: context })
} }
// if document already ready to go, schedule the ready function to run // 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" // IE only safe when readyState is "complete", others safe when readyState is "interactive"
if (document.readyState === "complete" || (!document.attachEvent && document.readyState === "interactive")) { if (document.readyState === 'complete' || (!document.attachEvent && document.readyState === 'interactive')) {
setTimeout(ready, 1); setTimeout(ready, 1)
} else if (!readyEventHandlersInstalled) { } else if (!readyEventHandlersInstalled) {
// otherwise if we don't have event handlers installed, install them // otherwise if we don't have event handlers installed, install them
if (document.addEventListener) { if (document.addEventListener) {
// first choice is DOMContentLoaded event // first choice is DOMContentLoaded event
document.addEventListener("DOMContentLoaded", ready, false); document.addEventListener('DOMContentLoaded', ready, false)
// backup is window load event // backup is window load event
window.addEventListener("load", ready, false); window.addEventListener('load', ready, false)
} else { } else {
// must be IE // must be IE
document.attachEvent("onreadystatechange", readyStateChange); document.attachEvent('onreadystatechange', readyStateChange)
window.attachEvent("onload", ready); window.attachEvent('onload', ready)
} }
readyEventHandlersInstalled = true; readyEventHandlersInstalled = true
} }
} }
})("docReady", window); })('docReady', window)
// modify this previous line to pass in your own method name // modify this previous line to pass in your own method name
// and object for the method to be attached to // and object for the method to be attached to
@@ -1,36 +1,34 @@
/*global django*/ /* global django */
var roundTo = function (n, digits) { let roundTo = function (n, digits) {
if (digits === undefined) { if (digits === undefined) {
digits = 0; digits = 0
} }
var multiplicator = Math.pow(10, digits); let multiplicator = Math.pow(10, digits)
n = parseFloat((n * multiplicator).toFixed(11)); n = parseFloat((n * multiplicator).toFixed(11))
return Math.round(n) / multiplicator; return Math.round(n) / multiplicator
}; }
let floatformat = function (val, places) {
'use strict'
if (places === undefined) {
places = 2
}
if (typeof val === 'string') {
val = parseFloat(val)
}
let parts = roundTo(val, places).toFixed(places).split('.')
if (places === 0) {
return parts[0]
}
parts[0] = parts[0].replace(new RegExp('\\B(?=(\\d{' + django.get_format('NUMBER_GROUPING') + '})+(?!\\d))', 'g'), django.get_format('THOUSAND_SEPARATOR'))
return parts[0] + django.get_format('DECIMAL_SEPARATOR') + parts[1]
}
var floatformat = function (val, places) { let autofloatformat = function (val, places) {
"use strict"; 'use strict'
if (places === undefined) { if (val == roundTo(val, 0)) {
places = 2; places = 0
} }
if (typeof val === "string") { return floatformat(val, places)
val = parseFloat(val); }
}
var parts = roundTo(val, places).toFixed(places).split(".");
if (places === 0) {
return parts[0];
}
parts[0] = parts[0].replace(new RegExp("\\B(?=(\\d{" + django.get_format("NUMBER_GROUPING") + "})+(?!\\d))", "g"), django.get_format("THOUSAND_SEPARATOR"));
return parts[0] + django.get_format("DECIMAL_SEPARATOR") + parts[1];
};
var autofloatformat = function (val, places) {
"use strict";
if (val == roundTo(val, 0)) {
places = 0;
}
return floatformat(val, places);
};
File diff suppressed because it is too large Load Diff