MediaWiki:DonationForm.js: Difference between revisions

Content deleted Content added
add otherAmt and validateError tracking
No edit summary
 
(133 intermediate revisions by 2 users not shown)
Line 1:
/* jshint strict:false */
/** MediaWiki:DonationForm.js - loaded on all donation forms
* TODO: lots of cleanup
Line 6 ⟶ 7:
 
donationForm.loadedTime = Date.now();
donationForm.extraData = {};
 
'vw' : window.innerWidth,
// Don't offer recurring at all in these countries
'vh' : window.innerHeight
donationForm.noRecurringCountries = [ 'AR' ]; // is this still needed?
};
 
donationForm.noRecurringPaypalCountries = [ 'CL', 'CO', 'PE', 'UY', 'BR' ];
 
donationForm.maxUSD = 25000;
 
donationForm.minimums = {
/*/ From https://github.com/wikimedia/wikimedia-fundraising-SmashPig/blob/master/PaymentData/ReferenceData/CurrencyRates.php */
'ADF'// :Updated 5.65,2024-01-19
'ADPADF' : 1436.08,
'ADP' : 154,
'AED' : 3.67,
'AFA' : 7368,
'AFN' : 7368,
'ALL' : 10994,
'AMD' : 477388,
'ANG' : 1.79,
'AOA' : 278828,
'AON' : 278828,
'ATSARS' : 12366,
'AUDATS' : 1.3913,
'AWGAUD' : 1.7852,
'AZMAWG' : 84861.79,
'AZM' : 8500,
'AZN' : 1.7,
'BAM' : 1.6981,
'BBD' : 2,
'BDT' : 83109,
'BEF' : 3537,
'BGL' : 1.6981,
'BGN' : 1.6981,
'BHD' : 0.3837452802080547,
'BIF' : 17482826,
'BMD' : 1,
'BND' : 1.3834,
'BOB' : 6.873,
'BRL' : 4.95,
'BSD' : 1,
'BTN' : 7283,
'BWP' : 1114,
'BYR' : 2103532900,
'BZD' : 1.9897,
'CAD' : 1.3236,
'CDF' : 15732611,
'CHF' : 0.9887620362389075,
'CNYCLP' : 6.83880,
'CRCCNY' : 5727.17,
'COP' : 3970,
'CRC' : 520,
'CUC' : 1,
'CUP' : 25,
'CVE' : 95102,
'CYP' : 0.5154264835979874,
'CZK' : 2223,
'DEM' : 1.6981,
'DJF' : 178,
'DKK' : 6.4291,
'DOP' : 5056,
'DZD' : 118134,
'ECS' : 24094,
'EEK' : 1315,
'EGP' : 1831,
'ESP' : 143154,
'ETB' : 2756,
'EUR' : 0.8792716976971251,
'FIM' : 5.1251,
'FJD' : 2.122,
'FKP' : 0.7879581971718638,
'FRF' : 56.6508,
'GBP' : 0.7879581971718638,
'GEL' : 2.5264,
'GHC' : 47467120114,
'GHS' : 4.7512,
'GIP' : 0.7879581971718638,
'GMD' : 4867,
'GNF' : 89838522,
'GRD' : 294316,
'GTQ' : 7.564,
'GYD' : 204200,
'HKD' : 7.8581,
'HNL' : 24,
'HRK' : 6.499,
'HTG' : 68131,
'HUF' : 282355,
'IDR' : 1496615593,
'IEP' : 0.6873020553251391,
'ILS' : 3.6171,
'INR' : 7210,
'IQD' : 11781292,
'IRR' : 42000,
'ISK' : 109140,
'ITL' : 16691795,
'JMD' : 136154,
'JOD' : 0.7170900000000001,
'JPY' : 112146,
'KES' : 100153,
'KGS' : 6989,
'KHR' : 40254051,
'KMF' : 424456,
'KPW' : 135,
'KRW' : 11201313,
'KWD' : 0.3130762164728865,
'KYD' : 0.8483333299999999,
'KZT' : 369456,
'LAK' : 839320577,
'LBP' : 150815000,
'LKR' : 162325,
'LRD' : 154187,
'LSL' : 1519,
'LTL' : 23.982,
'LUF' : 3537,
'LVL' : 0.6165161862283304,
'LYD' : 14.378,
'MAD' : 9.3910,
'MDL' : 1718,
'MGA' : 33114549,
'MGF' : 9150,
'MKD' : 5357,
'MMK' : 15292080,
'MNT' : 24602620,
'MOP' : 8.0804,
'MRO' : 357392,
'MTL' : 0.3739803398213759,
'MUR' : 3343,
'MVR' : 15,
'MWK' : 7171674,
'MYRMXN' : 4.1517,
'MZMMYR' : 597544.68,
'MZNMZM' : 6063200,
'NADMZN' : 1563,
'NGNNAD' : 35919,
'NIONGN' : 32791,
'NLGNIO' : 1.936,
'NOKNLG' : 82.404,
'NPRNOK' : 11411,
'NZDNPR' : 1.52132,
'OMRNZD' : 01.3963,
'OMR' : 0.38381212511836,
'PAB' : 1,
'PEN' : 3.374,
'PGK' : 3.2664,
'PHP' : 5456,
'PKR' : 123283,
'PLN' : 34.7202,
'PTE' : 173186,
'PYG' : 57637244,
'QAR' : 3.5764,
'ROL' : 3988946063,
'RON' : 34.9961,
'RSD' : 101108,
'RUB' : 6890,
'RWF' : 8621237,
'SAR' : 3.75,
'SBD' : 78.635,
'SCR' : 13,
'SDD' : 179559800,
'SDG' : 18598,
'SDP' : 2261,
'SEK' : 9.0810,
'SGD' : 1.3834,
'SHP' : 0.7879581971718638,
'SIT' : 207222,
'SKK' : 2628,
'SLL' : 843019750,
'SOS' : 542562,
'SRD' : 7.4137,
'SRG' : 740837356,
'STD' : 2107022552,
'SVC' : 8.75,
'SYP' : 215513,
'SZL' : 1519,
'THB' : 3336,
'TJS' : 9.3611,
'TMM' : 1705016750,
'TMT' : 3.4135,
'TND' : 23.7611,
'TOP' : 2.2932,
'TRL' : 662922329009007,
'TRY' : 6.6329,
'TTD' : 6.6465,
'TWD' : 31,
'TZS' : 22812498,
'UAH' : 2837,
'UGX' : 37493784,
'USD' : 1,
'UZSUYU' : 778039,
'VEBUZS' : 605084113612302,
'VEFVEB' : 60508413553825326,
'VNDVEF' : 231503553825,
'VUVVND' : 11124259,
'WSTVUV' : 2.64112,
'XAFWST' : 5652.68,
'XAF' : 608,
'XAG' : 0.043725386677957,
'XAU' : 0.000504038919286,
'XCD' : 2.7,
'XOFXEU' : 5650.92716976971251,
'XPFXOF' : 103608,
'XPD' : 0.001025937507186,
'XPF' : 111,
'XPT' : 0.001084078572954,
'YER' : 250,
'YUN' : 101108,
'ZAR' : 1519,
'ZMK' : 5176,
'ZWD' : 373,
/* Astropay currencies have higher minimums - 1.50 USD */
'ARS' : 58.5,
'BRL' : 6.23,
'CLP' : 1035,
'COP' : 4640,
'MXN' : 28.5
};
 
/* Localize the amount errors. Call when initialising form. */
donationForm.localizeErrors = function() {
var currency = documentdonationForm.paypalcontribution.currency_code.value;currency,
var minAmount = donationForm.minimums[ currency ];,
locale = donationForm.getLocale( mw.config.get('wgPageContentLanguage'), donationForm.country );
 
// Round up
minAmount = Math.ceil( minAmount * 100 ) / 100;
 
$('.lp-error-smallamount').text( function( index, oldText ) {
return oldText.replace( '$1', donationForm.formatAmount( minAmount, locale ) + ' \xa0' + currency );
});
 
if ( currency === 'USD' ) {
// we don't need to include the conversion
$('.lp-error-bigamount').text( function( index, oldText ) {
return oldText.replace( '($1 $2) ', '' )
.replace( '($1 $2) ', '' );
});
}
 
$('.lp-error-bigamount').text( function( index, oldText ) {
return oldText.replace( '$1', 10000donationForm.formatAmount( donationForm.maxUSD * minAmount, locale ) )
.replace( '$2', currency )
.replace( '$3', 'benefactors@wikimedia.org' );
.replace( '$4', donationForm.formatAmount( donationForm.maxUSD, locale ) );
});
};
 
 
function adjustHPC() {
Line 239 ⟶ 264:
var hpcSet = mw.util.getParamValue('hpcSet');
 
var currency = $("input[name='currency_code']")donationForm.val()currency;
var language = mw.config.get('wgUserLanguagewgPageContentLanguage');
 
// If changing, please update https://docs.google.com/spreadsheets/d/1e02TsZ_bKDAS1BMVBCdyo9D7RGln_wCGnkg7IF5kU5s/edit
var radioAmountsData = {
'"USD'" : { // also used for CAD, AUD, NZD
'"default'" : [
[ 0, [ 2.75, 5, 10, 20, 25, 35, 50, 10050 ] ],
[ 5, [ 5, 10, 15, 20, 35, 50, 100, 150100 ] ],
[ 10, [ 10, 15, 20, 25, 35, 50, 100, 150100 ] ],
[ 15, [ 15, 20, 25, 35, 50, 75, 100, 250100 ] ],
[ 20, [ 20, 25, 35, 50, 75, 100, 150, 250150 ] ],
[ 25, [ 25, 30, 40, 50, 75, 100, 150, 250150 ] ],
[ 35, [ 25 35, 50, 75, 100, 200, 250 300, 300 500 ] ],
[ 7550, [ 25, 50, 75, 100, 200, 300, 500, 750 ] ],
[ 100 75, [ 25, 5075, 100, 150, 250, 500, 750, 1000 ] ],
[ 150100, [ 50, 100, 150, 200 250, 350 500, 500 750, 1000, 2500 ] ],
[ 200150, [ 100, 150, 200, 300, 400 500, 500 750, 1000, 2000 ] ],
[ 500200, [ 100 200, 250 300, 500, 750, 1000, 1500 2500, 2000 5000 ] ],
[ 1000 500, [ 500, 1000 750, 2000 1000, 2500, 3000 5000, 4000 7500, 500010000 ] ],
[ 1000, [ 1000, 2000, 3000, 4000, 5000, 7500, 10000 ] ],
[ 3000, [ 3000, 4000, 5000, 6000, 7500, 10000, 12000 ] ]
],
'LTLA'"midtier2018" : [
[ 0, [ 3, 10 5, 15 10, 20, 35 30, 50, 100 ] ],
[ 5, [ 5, 10, 20, 35, 50, 100, 150 ] ],
[ 10, [ 10, 20 15, 35 20, 50 35, 75 50, 100, 150 ] ],
[ 15, [ 15, 25 20, 35 25, 50 35, 75 50, 100, 250 150 ] ],
[ 20, [ 20, 30 25, 35, 50, 75, 100, 150, 250150 ] ],
[ 25, [ 25, 35, 50, 75, 100, 150, 250, 500 ] ],
[ 35, [ 25, 50, 75, 100, 200 150, 250, 300 350, 500 ] ],
[ 7550, [ 25 75, 50100, 75150, 100 200, 200 250, 300 350, 500 ] ],
[ 100 75, [ 25100, 50150, 100 200, 150 300, 250 400, 500, 1000 ] ],
[ 150100, [ 50150, 100 200, 150 250, 200 300, 350 400, 500, 1000 ] ],
[ 200150, [ 100, 150200, 200 250, 300, 400, 500, 1000, 2500 ] ],
[ 500200, [ 100 500, 250 750, 5001000, 7502000, 1000 3500, 1500 5000, 2000 7500 ] ],
[ 1000 500, [ 500 750, 1000, 2000 1500, 2500, 3000 5000, 4000 7500, 500010000 ] ],
[ 1000, [ 1000, 2000, 3000, 4000, 5000, 7500, 10000 ] ]
],
'midtier2018' : [
[ 0, [ 3, 5, 10, 20, 30, 50, 100 ] ],
[ 5, [ 5, 10, 20, 35, 50, 100, 150 ] ],
[ 10, [ 10, 15, 20, 35, 50, 100, 150 ] ],
[ 15, [ 15, 20, 25, 35, 50, 100, 150 ] ],
[ 20, [ 20, 25, 35, 50, 75, 100, 150 ] ],
[ 25, [ 35, 50, 75, 100, 150, 250, 500 ] ],
[ 35, [ 50, 75, 100, 150, 250, 350, 500 ] ],
[ 50, [ 75, 100, 150, 200, 250, 350, 500 ] ],
[ 75, [ 100, 150, 200, 300, 400, 500, 1000 ] ],
[ 100, [ 150, 200, 250, 300, 400, 500, 1000 ] ],
[ 150, [ 200, 250, 300, 400, 500, 1000, 2500 ] ],
[ 200, [ 500, 750, 1000, 2000, 3500, 5000, 7500 ] ],
[ 500, [ 750, 1000, 1500, 2500, 5000, 7500, 10000 ] ],
[ 1000, [ 1000, 2000, 3000, 4000, 5000, 7500, 10000 ] ]
],
'LT2018' : [
[ 0, [ 5, 10, 20.18, 25, 35, 50, 100 ] ],
[ 5, [ 10, 15, 20.18, 35, 50, 100, 150 ] ],
[ 10, [ 15, 20.18, 25, 35, 50, 100, 150 ] ],
[ 15, [ 20.18, 25, 35, 50, 75, 100, 250 ] ],
[ 20, [ 25, 35, 50, 75, 100, 150, 250 ] ],
[ 25, [ 30, 40, 50, 75, 100, 150, 250 ] ],
[ 35, [ 25, 50, 75, 100, 200, 250, 300 ] ],
[ 75, [ 25, 50, 75, 100, 200, 300, 500 ] ],
[ 100, [ 25, 50, 100, 150, 250, 500, 1000 ] ],
[ 150, [ 50, 100, 150, 200, 350, 500, 1000 ] ],
[ 200, [ 100, 150, 200, 300, 400, 500, 1000 ] ],
[ 500, [ 100, 250, 500, 750, 1000, 1500, 2018 ] ],
[ 1000, [ 500, 1000, 2018, 2500, 3000, 4000, 5000 ] ]
]
},
'BRL'"EUR" : [ // also used for GBP
[ 0, [ 15 2, 25 5, 50 10, 75 20, 100 25, 150 35, 300 50 ] ],
[ 5, [ 25 5, 35 10, 50 15, 75 20, 100 35, 200 50, 300100 ] ],
[ 10, [ 3010, 50 15, 75 20, 100 25, 150 35, 300 50, 500100 ] ],
[ 15, [ 5015, 75 20, 100 25, 150 35, 225 50, 300 75, 500100 ] ],
[ 20, [ 5020, 100 25, 150 35, 225 50, 300 75, 400100, 500150 ] ],
[ 25, [ 7525, 125 30, 200 40, 250 50, 300 75, 400100, 500150 ] ],
[ 35, [ 100 35, 150 50, 225 75, 300 100, 500200, 750300, 1000 500 ] ],
[ 7550, [ 100 50, 150 75, 225 100, 300 200, 500300, 1000 500, 1500 750 ] ],
[ 100 75, [ 75, 100, 150, 300 250, 500, 1000, 1500750, 30001000 ] ],
[ 150100, [ 250100, 500 150, 750 250, 1000 500, 1500 750, 30001000, 50002500 ] ],
[ 200150, [ 500150, 1000 200, 2000 300, 3000 500, 4000 750, 50001000, 10000 2000 ] ],
[ 500200, [ 1000 200, 2500 300, 5000 500, 7500 750, 10000 1000, 12500 2500, 15000 5000 ] ],
[ 1000 500, [ 500, 750, 1000, 2500, 5000, 7500, 10000, 12500, 15000 ] ],
[ 1000, [ 1000, 2000, 3000, 4000, 5000, 7500, 10000 ] ],
[ 3000, [ 3000, 4000, 5000, 6000, 7500, 10000, 12000 ] ]
],
'"JPY'" : [
[ 0, [ 500, 1000, 2000, 2500, 4000, 5000, 10000 ] ],
[ 51000, [ 1000, 1500, 2500, 4000, 5000, 10000, 15000 ] ],
[ 101500, [ 1500, 2000, 3000, 4000, 5000, 10000, 15000 ] ],
[ 152000, [ 2000, 2500, 3500, 5000, 7500, 10000, 25000 ] ],
[ 202500, [ 2500, 3500, 5000, 7500, 10000, 15000, 25000 ] ],
[ 253000, [ 3000, 4000, 5000, 7500, 10000, 15000, 25000 ] ],
[ 352500, [ 2500, 5000, 7500, 10000, 20000, 30000, 50000 ] ],
[ 752500, [ 2500, 5000, 7500, 10000, 20000, 50000, 100000 ] ],
[ 100 5000, [ 5000, 10000, 15000, 20000, 35000, 50000, 100000 ] ],
[ 50010000, [ 10000, 25000, 50000, 75000, 100000, 150000, 200000 ] ]
],
'"SEK'" : [
[ 0, [ 2030, 50100, 100150, 200, 300500, 500750, 1000 ] ],
[ 350, [ 3050, 50100, 100150, 200, 300, 500750, 1000 ] ],
[ 5200, [ 50, 100, 150, 200, 300, 500, 750, 1000 ] ],
[ 23, [ 50, 100, 200, 300, 500, 750, 1000 ] ]
]
};
radioAmountsData.AUD = radioAmountsData.USD;
radioAmountsData.CAD = radioAmountsData.USD;
radioAmountsData.GBP = radioAmountsData.USD;
radioAmountsData.NZD = radioAmountsData.USD;
 
radioAmountsData.EUR = radioAmountsData.USD;
radioAmountsData.GBP = radioAmountsData.EUR;
 
var appealAmountsData = {
'"USD'" : [ // also used for CAD, AUD, NZD, GBP, EUR
[ 0, [ 5, 10, 20 ] ],
[ 10, [ 10, 20, 50 ] ],
Line 358 ⟶ 357:
[ 200, [ 100, 200, 300 ] ]
],
'"JPY'" : [
[ 0, [ 300, 500, 1000 ] ],
[ 3, [ 500, 1000, 1500 ] ],
Line 367 ⟶ 366:
[ 100, [ 5000, 10000, 15000 ] ]
],
'"SEK'" : [
[ 0, [ 20, 50, 100 ] ],
[ 3, [ 30, 50, 100 ] ],
Line 408 ⟶ 407:
};
 
var format;
if ( formats[currency] ) {
var format = formats[currency][language] || formats[currency]["'default"'] || formats[currency] || '\t';
} else {
format = '\t';
}
 
// Radio button amounts
Line 480 ⟶ 484:
/* Check for a 'preSelect' url parameter, and select that option.
If there isn't an option, add it to the "Other" box and select that */
var preSelectAmount = parseFloat( mw.util.getParamValue('preSelect') );
if ( preSelectAmount > 0 ) {
var $preSelectOption = $('input[name="amount"][value="' + preSelectAmount + '"]');
if ( $preSelectOption.length ) {
// Select existing input
Line 490 ⟶ 494:
$('#input_amount_other').prop('checked', true);
}
donationForm.updateFeeDisplay();
}
}
Line 511 ⟶ 516:
'BE' : 'vmaj',
'ES' : 'vmaj',
'FR' : 'CBvmavma', // Adyen - Carte Bancaire was removed
'IT' : 'vmaj',
'LU' : 'vmaj',
Line 518 ⟶ 523:
'PT' : 'vmaj',
'SK' : 'vmaj',
'GR' : 'vma',
// Others
'CZ' : 'vmad',
'DK' : 'vma',
'HU' : 'vma',
Line 529 ⟶ 536:
'SE' : 'vma',
'UA' : 'vma', // Adyen
'ZA' : 'vm',
'ZZ' : 'vmad' // For testing
};
if ( cardTypes[country] ) {
$('.paymentmethod-cc').addClass('cctypes-' + cardTypes[country] );
$('.cc-text-label').addClass('sr-only');
}
}
Line 538 ⟶ 547:
/* Form functions */
function clearOther(box) {
document.getElementById("'input_amount_other"').checked = true;
box.value = "";
}
 
function selectOther() {
document.getElementById("'input_amount_other"').checked = true;
}
 
function selectAmount() {
$('#input_amount_other_box').val('');
$('input[name="amountGiven"]').val('');
}
 
/* -- Moved from Template:2012FR/Form-section/Processing/Default -- */
/**
donationForm.redirectPayment = function(paymentMethod, paymentSubMethod, skipValidation) {
* Validate form, and prep most of the parameters
*
* @param {string} paymentMethod - method e.g. 'cc', 'paypal'
* @param {string} paymentSubMethod - submethod e.g. 'rtbt_ideal' (a submethod of 'rtbt')
* @param {string} skipAmountValidation - skip validating amount for PayPal forced to USD
*/
donationForm.redirectPayment = function( paymentMethod, paymentSubMethod, skipAmountValidation ) {
 
if ( donationForm.validate( skipAmountValidation ) ) {
var form = document.paypalcontribution; // we should really change this some day
 
var params = {};
if ( skipValidation || donationForm.validate() ) {
 
params.currency = donationForm.currency;
if (typeof paymentSubMethod == 'undefined') {
paymentSubMethodparams.country = ''donationForm.country;
 
// Overrides for specific cc gateways
if ( paymentMethod === 'cc-adyen' ) {
params.payment_method = 'cc';
params.gateway = 'adyen';
} else if ( paymentMethod === 'cc-dlocal' ) {
params.payment_method = 'cc';
params.gateway = 'astropay';
} else {
params.payment_method = paymentMethod;
}
var paymentsURL = 'https://payments.wikimedia.org/index.php/Special:GatewayFormChooser';
 
if ( params.payment_method === 'cc' && params.country === 'ZA' ) {
form.action = paymentsURL;
params.gateway = 'astropay';
}
 
if (typeof paymentSubMethod == 'undefined') {
paymentSubMethodparams.payment_submethod = ''paymentSubMethod;
}
 
var frequency = $('input[name="frequency"]:checked').val();
// WorldPay override for cc
if ( paymentMethodfrequency =!== 'cc-wpmonthly' ) {
paymentMethodparams.recurring = 'cc'false;
} else {
form.payment_method.value = 'cc';
formparams.gateway.valuerecurring = 'worldpay'true;
form.ffname.value = 'worldpay';
}
 
params.uselang = mw.config.get('wgPageContentLanguage'); // see T281285 for why not wgUserLanguage
// Adyen override for cc
 
if ( paymentMethod === 'cc-adyen' ) {
if ( params.uselang === paymentMethod'pt' && params.country === 'ccBR'; ) {
formparams.payment_method.valueuselang = 'ccpt-br';
}
form.gateway.value = 'adyen';
if ( formparams.ffname.valueuselang === 'adyenes'; &&
( params.country === 'AR' || params.country === 'CL' ||
params.country === 'CO' || params.country === 'MX' ||
params.country === 'PE' || params.country === 'UY' ||
params.country === 'US' )
) {
params.uselang = 'es-419';
}
 
var frequencyamount = $("input[name='frequency']:checked")donationForm.valgetAmount();
if ( frequency !== $('monthly#ptf-checkbox').prop('checked') ) {
frequencyamount = 'onetime'amount + donationForm.calculateFee( amount );
formdonationForm.recurringextraData.valueptf = 'false'1;
} else {
form.recurring.value = 'true';
}
params.amount = amount;
 
// Email optin
form.payment_method.value = paymentMethod;
if ( $('input[name="opt_in"]').length > 0 ) {
form.payment_submethod.value = paymentSubMethod;
var opt_inValue = $('input[name="opt_in"]:checked').val();
params.opt_in = opt_inValue; // donationForm.validate() already checked it's 1 or 0
}
 
if ( mw.util.getParamValue( 'pym_variant' ) ) {
var full_dotted_payment_method = paymentMethod;
if ( form.recurring params.valuevariant == mw.util.getParamValue( 'truepym_variant' ) {;
full_dotted_payment_method = 'r' + full_dotted_payment_method;
}
if ( params.recurring && params.variant && params.variant.match( /monthlyConvert/ ) ) {
if ( paymentSubMethod ) {
// Post-payments monthly convert makes no sense if it's already recurring
full_dotted_payment_method = form.payment_method.value + '.' + paymentSubMethod;
// Avoid things like T312905
delete params.variant;
}
 
if ( mw.util.getParamValue( 'pym_appeal' ) ) {
donationForm.extraData.time = Math.round( (Date.now() - donationForm.loadedTime)/1000 );
params.appeal = mw.util.getParamValue( 'pym_appeal' );
}
 
// Monthly convert
form.utm_medium.value = mw.util.getParamValue( 'utm_medium' );
if ( mc ) { // check just in-case this wasn't loaded for some reason
form.utm_campaign.value = mw.util.getParamValue( 'utm_campaign' );
mc.main( params, donationForm.finalStep );
form.utm_source.value = donationForm.buildUtmSource() + '.' + full_dotted_payment_method;
} else {
form.utm_key.value = donationForm.buildUtmKey( donationForm.extraData );
donationForm.finalStep( params );
}
 
form.method = 'GET';
form.submit();
} else {
donationForm.extraData.validateError = 1; // Flag they had an error, even if fixed later
Line 622 ⟶ 657:
 
/**
* Build afinal utm_sourcetracking valueparameters, includingand thesubmit landingto page info.payments
* @param {Object} params
*/
donationForm.finalStep = function( params ) {
 
var uri = new mw.Uri('https://payments.wikimedia.org/index.php/Special:GatewayChooser');
 
// Skip form chooser for Apple Pay / Google Pay
if ( params.payment_method === 'apple' || params.payment_method === 'google' ) {
uri = new mw.Uri('https://payments.wikimedia.org/index.php/Special:AdyenCheckoutGateway');
}
 
// Skip form chooser for Venmo
if ( params.payment_method === 'venmo' ) {
uri = new mw.Uri('https://payments.wikimedia.org/index.php/Special:BraintreeGateway');
}
 
donationForm.extraData.time = Math.round( (Date.now() - donationForm.loadedTime)/1000 );
 
// Tracking data
params.wmf_medium = mw.util.getParamValue( 'wmf_medium' ) || mw.util.getParamValue( 'utm_medium' );
params.wmf_campaign = mw.util.getParamValue( 'wmf_campaign' ) || mw.util.getParamValue( 'utm_campaign' );
params.wmf_source = donationForm.buildTrackingSource( params );
params.wmf_key = donationForm.buildTrackingKey( donationForm.extraData );
if ( document.referrer ) { // TODO: do we need this?
// Strip protocol to stop firewall complaining
params.referrer = document.referrer.replace(/https?:\/\//i, '');
}
 
uri.extend( params );
 
if ( window.top !== window.self ) {
// In a frame, open payments in a new tab
window.open( uri.toString() );
} else {
window.location.href = uri.toString();
}
};
 
/**
* Build a wmf_source value, including the landing page info.
*
* Own function so it can be overriden for weird tests
* Note this doesn't include payment method, that's added in redirectPayment method
*
* @returnparam {stringObject} utm_sourceparams
* @return {string} wmf_source
*/
donationForm.buildUtmSourcebuildTrackingSource = function( params ) {
 
var utm_sourcewmf_source = mw.util.getParamValue( 'utm_sourcewmf_source' ) +|| 'mw.util.getParamValue( 'utm_source' );
wmf_source += '.';
 
var fullDottedPaymentMethod = params.payment_method;
if ( params.recurring ) {
fullDottedPaymentMethod = 'r' + fullDottedPaymentMethod;
}
if ( params.payment_submethod ) {
fullDottedPaymentMethod = fullDottedPaymentMethod + '.' + params.payment_submethod;
}
 
/* Get URL parameter, but remove parts using old format. Allow fallback to a default value */
var getParam = function( param, removeText, dflt ) {
if ( mw.util.getParamValue( param ) ) {
Line 641 ⟶ 727:
 
/* The landing page info, separated by ~. This mostly exists for legacy reasons */
utm_sourcewmf_source += getParam( 'template' , 'Lp-layout' , 'default' ) + '~';
utm_sourcewmf_source += getParam( 'appeal-template' , 'Appeal-template-' , 'default' ) + '~';
utm_sourcewmf_source += getParam( 'appeal' , 'Appeal-' , 'default' ) + '~';
utm_sourcewmf_source += getParam( 'form-template' , 'Form-template-' , 'default' ) + '~';
utm_sourcewmf_source += getParam( 'form-countryspecific', 'Form-countryspecific-', 'control' );
 
wmf_source += '.' + fullDottedPaymentMethod;
return utm_source;
 
return wmf_source;
 
};
 
/**
* Build a string for utm_keywmf_key from extra tracking data
*
* @param {Object} data
* @return {string} utm_keywmf_key
*/
donationForm.buildUtmKeybuildTrackingKey = function(data) {
var existingKey = mw.util.getParamValue( 'wmf_key' ) || mw.util.getParamValue( 'utm_key' ),
var dataArray = [];
dataArray = [];
 
if ( existingKey ) {
dataArray.push( existingKey );
}
for (var key in data) {
if (data.hasOwnProperty(key)) {
Line 664 ⟶ 757:
}
}
return dataArray.join('.~');
};
 
/* Return amount selected or input */
donationForm.getAmount = function() {
var form = document.paypalcontribution;forms.donateForm,
var amount = null;
donationForm.extraData.otherAmt = 0;
 
Line 677 ⟶ 770:
for ( var i = 0; i < form.amount.length; i++ ) {
if ( form.amount[i].checked ) {
amount = parseFloat( form.amount[i].value );
}
}
}
 
// Check the "other" amount box
if ( document.getElementById('input_amount_other').checked ) {
if ( form.input_amount_other_box.value !== '' ) {
var otherAmountamount = donationForm.parseOtherAmount( form.input_amount_other_box.value );
otherAmount = otherAmount.replace(/[,.](\d)$/, '\:$10');
otherAmount = otherAmount.replace(/[,.](\d)(\d)$/, '\:$1$2');
otherAmount = otherAmount.replace(/[\$£€¥,.]/g, '');
otherAmount = otherAmount.replace(/:/, '.');
form.amountGiven.value = otherAmount; // TODO: change this
amount = otherAmount;
donationForm.extraData.otherAmt = 1;
}
 
amount =return parseFloat(amount);
 
};
if ( isNaN(amount) ) {
 
/**
* Parse Other field value into amount
*
* Does some awful regex stuff to rm symbols and turn the string into a number
* Remember some locales flip . & , for decimal point/thousands separator
*
* @param {string} value Value of "Other" field
* @return {float} Float with amount, or 0 if NaN
*/
donationForm.parseOtherAmount = function( value ) {
var amount;
 
value = value.replace(/[,.](\d)$/, '\:$10');
value = value.replace(/[,.](\d)(\d)$/, '\:$1$2');
value = value.replace(/[\$£€¥,.]/g, '');
value = value.replace(/:/, '.');
 
amount = parseFloat( value );
if ( isNaN( amount ) ) {
return 0;
} else {
return amount;
}
 
};
 
 
/**
* Validate the form.
*/
donationForm.validate = function( skipAmountValidation ) {
 
var error = false;
var form = document.paypalcontributionforms.donateForm;
 
// Reset all errors
Line 717 ⟶ 821:
$('.lp-error').hide();
 
if ( !skipAmountValidation && !donationForm.validateAmount() ) {
error = true;
}
 
if ( form.opt_in ) {
if ( $("'input[name='"opt_in'"]:checked"').val() === undefined ) {
$('#error-optin').show();
error = true;
} else {
$('#error-optin').hide();
form.variant.value = 'emailExplain';
}
}
Line 741 ⟶ 844:
 
var amount = donationForm.getAmount();
var minAmount = donationForm.minimums[ donationForm.currency ] || 1;
var currency = document.paypalcontribution.currency_code.value;
var minAmount = donationForm.minimums[ currency ] || 1;
 
if ( amount === null || isNaN(amount) || amount <= 0 || amount < minAmount ) {
$('.amount-options').addClass('lp-haserror');
$('.lp-error-bigamount').hide();
$('.lp-error-smallamount').show();
return false;
} else if ( amount > 10000donationForm.maxUSD * minAmount ) {
$('.amount-options').addClass('lp-haserror');
$('.lp-error-bigamount').show();
Line 767 ⟶ 870:
}
};
/* End form functions */
 
donationForm.updateFeeDisplay = function() {
var selectedAmount = donationForm.getAmount(),
feeAmount = donationForm.calculateFee( selectedAmount ),
minAmount = donationForm.minimums[ donationForm.currency ] || 1,
maxAmount = donationForm.maxUSD * minAmount,
feeText, locale;
 
locale = donationForm.getLocale( mw.config.get('wgPageContentLanguage'), donationForm.country );
feeText = donationForm.formatAmount( feeAmount, locale );
 
$('.ptf label span').text( feeText );
if ( selectedAmount + feeAmount <= maxAmount ) {
$('.ptf').slideDown();
}
};
 
/**
* Calculate approximate transaction fee on given amount
* @param {number} amount
* @return {number} Rounded to 2 decimal places
*/
donationForm.calculateFee = function( amount ) {
 
// Minimum fee/PTF amounts. Default is 0.35.
// Updated 2019-05-21 to approx 0.35 USD equivalent
var feeMinimums = {
'DKK' : 2,
'HUF' : 100,
'ILS' : 1.2,
'INR' : 4,
'JPY' : 35,
'MYR' : 1,
'NOK' : 3,
'PLN' : 1.35,
'CZK' : 7.5,
'RON' : 1.5,
'SEK' : 3,
'UAH' : 10,
'ZAR' : 5,
// Latin America // Updated 2023-01-17 to approx 0.35 USD equivalent
'BRL' : 1.75,
'ARS' : 32,
'CLP' : 322,
'COP' : 1385,
'MXN' : 6,
'PEN' : 1.3,
'UYU' : 13.7
};
 
var feeMultiplier = 0.04,
feeMinimum = feeMinimums[ donationForm.currency ] || 0.35,
feeAmount = amount * feeMultiplier;
 
if ( feeAmount < feeMinimum ) {
feeAmount = feeMinimum;
}
return parseFloat( feeAmount.toFixed(2) );
};
 
 
donationForm.initOptin = function() {
$('.optin-options').on('change', function(e) {
 
$('#error-optin').hide();
 
// Only do all this if we have translated prompts
if ( $('.optin-no-prompt').data('is-translated') === 'yes' ) {
if ( e.target.id === 'optin-no' ) {
$('.optin-no-prompt').removeClass('is-positive');
if ( !$('.optin-no-prompt').is(':visible') ) {
$('.optin-no-prompt').slideDown();
}
} else {
$('.optin-no-prompt').addClass('is-positive');
}
}
});
};
 
/**
* Block typing letters and symbols in given input. Used for Other amount inputs
*
* If we don't do this, Safari allows typing them and then chokes on submit
* https://phabricator.wikimedia.org/T118741, https://phabricator.wikimedia.org/T173431
*
* @param {Element} inputElement The element to block typing on
*/
donationForm.otherInputControl = function( inputElement ) {
if ( inputElement ) {
inputElement.onkeypress = function(e) {
// Allow special keys in Firefox
if ((e.code == 'ArrowLeft') || (e.code == 'ArrowRight') ||
(e.code == 'ArrowUp') || (e.code == 'ArrowDown') ||
(e.code == 'Delete') || (e.code == 'Backspace')) {
return;
}
var chr = String.fromCharCode(e.which);
if ('0123456789., '.indexOf(chr) === -1) {
return false;
}
};
}
};
 
/**
* Make language and country into a standard javascript Intl locale identifier
*
* @param {string} language
* @param {string} country
* @return {string} locale identifier e.g. en-GB
*/
donationForm.getLocale = function( language, country ) {
if ( language === 'en-gb' ) {
language = 'en';
}
if ( language === 'es-419' ) {
language = 'es';
}
if ( language === 'pt-br' ) {
language = 'pt';
}
return language + '-' + country;
};
 
/**
* Should we show Apple Pay?
*
* Note there is a ~500ms delay in Safari when checking, so only call this if needed
*
* @param {string} country
* @return {boolean}
*/
donationForm.shouldShowApplePay = function ( country ) {
if ( location.search.match('forceApplePay') ) {
return true;
}
if ( window.ApplePaySession ) {
if ( ApplePaySession.canMakePayments() ) {
return true;
}
}
return false;
};
 
/**
* Format an amount for a given locale
*
* 2 decimal places if it has a fractional part, 0 if not
* Note this doesn't include any currency symbol
*
* @param {number} amount
* @param {string} locale To determine correct separators
* @return {string}
*/
donationForm.formatAmount = function( amount, locale ) {
var formatterOptions, output;
if ( amount % 1 !== 0 ) { // Not a whole number
formatterOptions = { minimumFractionDigits: 2, maximumFractionDigits: 2 };
} else {
formatterOptions = {};
}
try {
output = amount.toLocaleString( locale, formatterOptions );
} catch(e) {
output = amount.toFixed(2);
}
return output;
};
 
/*
Based on github:braintree/braintree-web/src/venmo/shared/supports-venmo.js
See also on meta: MediaWiki:FundraisingBanners/VenmoBrowserCheck.js
*/
donationForm.isVenmoSupported = function(options) {
var options = options || {
allowNewBrowserTab: false,
allowWebviews: true,
allowDesktop: true,
allowDesktopWebLogin: true
};
var ua = window.navigator.userAgent;
 
var merchantAllowsReturningToNewBrowserTab,
merchantAllowsWebviews,
merchantAllowsDesktopBrowsers;
var isMobileDevice = isAndroid() || isIos();
var isAndroidChrome = isAndroid() && isChrome();
var isMobileDeviceThatSupportsReturnToSameTab = isIosSafari() || isAndroidChrome;
var isKnownUnsupportedMobileBrowser = isIosChrome() || isFacebookOwnedBrowserOnAndroid() || isSamsung();
 
options = options || {};
// NEXT_MAJOR_VERSION allowDesktop will default to true, but can be opted out
merchantAllowsDesktopBrowsers =
(options.allowDesktopWebLogin || options.allowDesktop) === true;
merchantAllowsReturningToNewBrowserTab = options.hasOwnProperty(
"allowNewBrowserTab"
)
? options.allowNewBrowserTab
: true;
// NEXT_MAJOR_VERSION webviews are not supported, except for the case where
// the merchant themselves is presenting venmo in a webview using the deep
// link url to get back to their app. For the next major version, we should
// just not have this option and instead require the merchant to determine
// if the venmo button should be displayed when presenting it in the
// merchant's app via a webview.
merchantAllowsWebviews = options.hasOwnProperty("allowWebviews")
? options.allowWebviews
: true;
 
if (isKnownUnsupportedMobileBrowser) {
return false;
}
 
if (
!merchantAllowsWebviews &&
(isAndroidWebview() || isIosWebview())
) {
return false;
}
 
if (!isMobileDevice) {
return merchantAllowsDesktopBrowsers;
}
 
if (!merchantAllowsReturningToNewBrowserTab) {
return isMobileDeviceThatSupportsReturnToSameTab;
}
 
return isMobileDevice;
 
/* -- functions mostly from github:braintree/browser-detection library -- */
 
function isAndroid() {
return /Android/i.test(ua);
}
 
function isIos(checkIpadOS = true) {
const iOsTest = /iPhone|iPod|iPad/i.test(ua);
return checkIpadOS ? iOsTest || isIpadOS() : iOsTest;
}
 
function isIpadOS() {
// "ontouchend" is used to determine if a browser is on an iPad, otherwise
// user-agents for iPadOS behave/identify as a desktop browser
return /Mac|iPad/i.test(ua) && "ontouchend" in window.document;
}
 
function isEdge() {
return ua.indexOf("Edge/") !== -1 || ua.indexOf("Edg/") !== -1;
}
 
function isSamsung() {
return /SamsungBrowser/i.test(ua);
}
 
function isDuckDuckGo() {
return ua.indexOf("DuckDuckGo/") !== -1;
}
 
function isOpera() {
return (
ua.indexOf("OPR/") !== -1 ||
ua.indexOf("Opera/") !== -1 ||
ua.indexOf("OPT/") !== -1
);
}
 
function isSilk() {
return ua.indexOf("Silk/") !== -1;
}
 
function isChrome() {
return (
(ua.indexOf("Chrome") !== -1 || ua.indexOf("CriOS") !== -1) &&
!isEdge() &&
!isSamsung() &&
!isDuckDuckGo() &&
!isOpera() &&
!isSilk()
);
}
 
function isIosFirefox() {
return /FxiOS/i.test(ua);
}
 
function isWebkit() {
const webkitRegexp = /webkit/i;
return webkitRegexp.test(ua);
}
 
function isIosChrome() {
return ua.indexOf("CriOS") > -1;
}
 
function isFacebook() {
return ua.indexOf("FBAN") > -1;
}
 
function isIosSafari() {
return (
isIos() &&
isWebkit() &&
!isIosChrome() &&
!isIosFirefox() &&
!isFacebook()
);
}
 
function isFacebookOwnedBrowserOnAndroid() {
var e = ua.toLowerCase();
return -1 < e.indexOf("huawei") && -1 < e.indexOf("fban") || isAndroid() && (-1 < e.indexOf("fb_iab") || -1 < e.indexOf("instagram"));
}
 
function isSamsungBrowser() {
return /SamsungBrowser/i.test(ua);
}
 
function isAndroidWebview() {
return isAndroid() && -1 < ua.toLowerCase().indexOf("wv");
}
 
function isGoogleSearchApp() {
return /\bGSA\b/.test(ua);
}
 
function isIosGoogleSearchApp() {
return isIos() && isGoogleSearchApp();
}
 
function isIosWebview() {
if (isIos()) {
// The Google Search iOS app is technically a webview and doesn't support popups.
if (isIosGoogleSearchApp()) {
return true;
}
// Historically, a webview could be identified by the presence of AppleWebKit and _no_ presence of Safari after.
return /.+AppleWebKit(?!.*Safari)/i.test(ua);
}
return false;
}
};
 
/* End form functions */
 
$(document).ready(function() {
Line 774 ⟶ 1,220:
mw.loader.using( ['mediawiki.util'] ).done( function() {
 
var form = document.forms.donateForm;
// Block typing symbols in input field, otherwise Safari allows them and then chokes
// https://phabricator.wikimedia.org/T118741, https://phabricator.wikimedia.org/T173431
 
// These get used in quite a few places
var amountOtherInput = document.getElementById('input_amount_other_box');
if ( amountOtherInput )try {
amountOtherInputdonationForm.onkeypresscurrency = function(e) {form.currency_code.value;
} catch (error) {
// Allow special keys in Firefox
if ((edonationForm.codecurrency == 'ArrowLeftUSD') || (e.code == 'ArrowRight') ||;
(e.code == 'ArrowUp') || (e.code == 'ArrowDown') ||
(e.code == 'Delete') || (e.code == 'Backspace')) {
return;
}
var chr = String.fromCharCode(e.which);
if ("0123456789., ".indexOf(chr) === -1) {
return false;
}
};
}
donationForm.country = mw.util.getParamValue('country').toUpperCase();
 
// ValidateBlock amounttyping whensymbols selected/enteredin Other field
donationForm.otherInputControl( document.getElementById('input_amount_other_box') );
$('.amount-options').on( 'input change', donationForm.validateAmount );
 
// Validate amount and update fee when selected/entered
$('.amount-options').on( 'input change', function() {
donationForm.validateAmount();
donationForm.updateFeeDisplay();
});
 
// Disable submitting form with Enter key
$('form[name="paypalcontributiondonateForm"]').on('keypress', function(e) {
var code = ( e.keyCode ? e.keyCode : e.which );
if ( code == 13 ) {
Line 811 ⟶ 1,254:
});
 
if ( document.paypalcontributionform ) {
document.paypalcontribution.utm_medium.value = mw.util.getParamValue( 'utm_medium' );
document.paypalcontribution.utm_campaign.value = mw.util.getParamValue( 'utm_campaign' );
document.paypalcontribution.utm_key.value = mw.util.getParamValue( 'utm_key' );
 
// Striphide protocolfrequency tooptions stopfor firewallsome throwing fitscountries
if ( donationForm.noRecurringCountries.indexOf( donationForm.country ) !== -1 ) {
document.paypalcontribution.referrer.value = document.referrer.replace(/https?:\/\//i, "");
$('#frequency_onetime').prop('checked', true);
$('.frequency-options, #cancel-monthly, #donate-recurring-smallprint').hide();
}
 
if ( donationForm.noRecurringPaypalCountries.indexOf( donationForm.country ) !== -1 ) {
// hide frequency options in India, where we can only handle one-time donations
$( '.paymentmethod-pp, .paymentmethod-pp-usd' ).addClass( 'not-monthly-capable' );
if (document.paypalcontribution.country.value === 'IN') {
$("#frequency_onetime").prop('checked', true);
$(".frequency-options").hide();
$("#cancel-monthly").hide();
}
 
addCardTypesClass(document.paypalcontribution donationForm.country.value );
 
// Only show Amazon for links from Ways to give
// TODO: remove utm_source when Ways to give has been updated
if (
mw.util.getParamValue( 'wmf_source' ) === 'Waystogive' ||
mw.util.getParamValue( 'wmf_source' ) === 'Ways_to_Give'
) {
$('.paymentmethod-amazon').show();
}
 
// Apple Pay
if ( $('.paymentmethod-applepay').length > 0 ) {
if ( !donationForm.shouldShowApplePay( donationForm.country ) ) {
$('.paymentmethod-applepay').remove();
}
}
 
// Venmo browser check
if ( $('.paymentmethod-venmo').length > 0 ) {
if ( !donationForm.isVenmoSupported() || donationForm.country !== 'US' ) {
$('.paymentmethod-venmo').remove();
}
}
}
 
Line 833 ⟶ 1,296:
 
// Disable logo link
$("'#p-logo a"').attr(" { href",: "'#"', title: '' } );
$("#p-logo a").attr("title", "");
 
// These don't need to be tabbable on the landing page
$('#searchInput, .mw-jump-link').attr('tabindex', '-1');
 
$("'.input_amount_other"').click(function() {
$("'#input_amount_other_box"').focus();
});
 
// Allow preselecting monthly
if (
mw.util.getParamValue('monthly') ) {
&& mw.util.getParamValue('monthly') !== '0'
&& donationForm.noRecurringCountries.indexOf( donationForm.country ) === -1
) {
$('#frequency_monthly').click();
}
 
donationForm.initOptin();
// If the optin section has some javascript to run, do that
if ( typeof initOptin === 'function' ) {
initOptin();
}
 
try {
Line 859 ⟶ 1,322:
}
finally {
$('.frb-monthly-pitch, .frb-monthly-pitch-thanks').appendTo('.frequency-options');
$('.ptf').appendTo('.amount-options');
$('.optin-options').insertAfter('.amount-options');
$('.consider-amounts').show();