PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 4.16.19
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v4.16.19
4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / assets / js / frontend.js
wp-user-avatar / assets / js Last commit date
admin 1 month ago builder 1 month ago stripe 1 month ago admin.js 1 month ago checkout-fields-manager.js 1 month ago checkout.js 1 month ago clipboard.min.js 1 month ago content-control.js 1 month ago create-form.js 1 month ago frontend.js 1 month ago frontend.min.js 1 month ago index.php 1 month ago jcarousel.min.js 1 month ago jquery.blockUI.js 1 month ago mailoptin.js 1 month ago member-directory.js 1 month ago member-directory.min.js 1 month ago pp-wp-editor.js 1 month ago
frontend.js
616 lines
1 import Checkout from './checkout';
2 import $ from 'jquery';
3
4 function Frontend() {
5
6 var _this = this;
7
8 this.init = function () {
9
10 window.ppFormRecaptchaLoadCallback = this.recaptcha_processing;
11
12 $('.pp-del-profile-avatar').on('click', this.delete_avatar);
13 $('.pp-del-cover-image').on('click', this.delete_profile_image_cover);
14
15 $(document).on('click', '.has-password-visibility-icon .pp-form-material-icons', this.toggle_password_visibility);
16
17 // used by the WooCommerce module for toggling wc overridden checkout login form
18 $(document.body).on('click', 'a.showlogin', function () {
19 $(".pp_wc_login").slideToggle();
20 });
21
22 $(window).on('load resize ppress_updated_checkout', function () {
23 _this.defaultUserProfileResponsive();
24 });
25
26 $(window).on('ppress_updated_checkout', function () {
27 _this.recaptcha_processing();
28 });
29
30 $(document).on('click', '.ppress-confirm-delete', function (e) {
31 e.preventDefault();
32 if (confirm(pp_ajax_form.confirm_delete)) {
33 window.location.href = $(this).attr('href');
34 }
35 });
36
37 this.submit_reload_form_on_billing_country_field_change();
38
39 this.myaccount_password_strength_meter();
40
41 // only enable if pp_disable_ajax_form filter is false.
42 if (pp_ajax_form.disable_ajax_form === 'true') return;
43
44 $(document).on('submit', 'form[data-pp-form-submit="login"]', this.ajax_login);
45 $(document).on('submit', 'form[data-pp-form-submit="signup"]', this.ajax_registration);
46 $(document).on('submit', 'form[data-pp-form-submit="passwordreset"]', this.ajax_password_reset);
47 $(document).on('submit', 'form[data-pp-form-submit="editprofile"]', this.ajax_edit_profile);
48 };
49
50 this.recaptcha_processing = function () {
51
52 $('.pp-g-recaptcha').each(function (index, el) {
53
54 var $site_key = $(el).attr('data-sitekey'),
55 $form = $(this).parents('.pp-form-container').find('form'),
56 recaptchaApi = typeof grecaptcha.enterprise !== 'undefined' ? grecaptcha.enterprise : grecaptcha;
57
58 if ($(el).attr('data-type') === 'v3') {
59
60 $form.find('input.pp-submit-form').on('click', function (e) {
61 e.preventDefault();
62 _this._add_processing_label($form);
63 recaptchaApi.ready(function () {
64 recaptchaApi.execute($site_key, {action: 'form'}).then(function (token) {
65 $form.find('[name="g-recaptcha-response"]').remove();
66
67 $form.append($('<input>', {
68 type: 'hidden',
69 value: token,
70 name: 'g-recaptcha-response'
71 }));
72
73 $form.trigger('submit');
74 });
75 });
76 });
77 } else {
78
79 try {
80
81 var widgetId1 = recaptchaApi.render(el, {
82 'sitekey': $site_key,
83 'theme': $(el).attr('data-theme'),
84 'size': $(el).attr('data-size')
85 });
86
87 } catch (error) {
88 }
89
90 $form.on('pp_form_submitted', function () {
91 recaptchaApi.reset(widgetId1)
92 });
93
94 $(document).on('ppress_process_checkout_success_callback ppress_process_checkout_error_callback', function () {
95 recaptchaApi.reset(widgetId1)
96 });
97 }
98 });
99 };
100
101 this.toggle_password_visibility = function (e) {
102 e.preventDefault();
103 var input = $(this).parents('.pp-form-field-input-textarea-wrap').find('.pp-form-field');
104
105 if (input.attr('type') === 'password') {
106 input.attr('type', 'text');
107 $(this).text('visibility_off')
108 } else {
109 input.attr('type', 'password');
110 $(this).text('visibility')
111 }
112 };
113
114 this.submit_reload_form_on_billing_country_field_change = function () {
115 $(document).on('change', '.pp-edit-profile-form-wrap select[name=ppress_billing_country]', function (e) {
116 $(document).on('pp_form_edit_profile_success', function () {
117 window.location.reload();
118 });
119 $(this).closest('form').find('input.pp-submit-form').trigger('click');
120 });
121 };
122
123 this.ajax_edit_profile = function (e) {
124
125 if (typeof window.FormData === 'undefined' || !window.FormData) return;
126
127 e.preventDefault();
128
129 var $editprofile_form = $('form[data-pp-form-submit="editprofile"]');
130
131 var melange_id = _this.get_melange_id($editprofile_form);
132
133 var formData = new FormData(this);
134 formData.append("action", "pp_ajax_editprofile");
135 formData.append("nonce", pp_ajax_form.nonce);
136 formData.append("melange_id", melange_id);
137
138 // remove any prior edit profile error.
139 $('.profilepress-edit-profile-status').remove();
140 $('.profilepress-edit-profile-success').remove();
141
142 // remove any prior status message. Fixes removal of message with custom class.
143 if ("" !== window.edit_profile_msg_class) {
144 $('.' + window.edit_profile_msg_class).remove();
145 }
146
147 _this._add_processing_label($editprofile_form);
148
149 $.post({
150 url: pp_ajax_form.ajaxurl,
151 data: formData,
152 cache: false,
153 contentType: false,
154 enctype: 'multipart/form-data',
155 processData: false,
156 dataType: 'json',
157 success: function (response) {
158 $editprofile_form.trigger('pp_form_submitted');
159 $editprofile_form.trigger('pp_form_edit_profile_success', [$editprofile_form]);
160 if ("avatar_url" in response && response.avatar_url !== '') {
161 $("img[data-del='avatar'], img.pp-user-avatar").attr('src', response.avatar_url);
162 // remove the picture upload path text.
163 $('input[name=eup_avatar]', $editprofile_form).val('');
164 }
165
166 if ("cover_image_url" in response && response.cover_image_url !== '') {
167 $("img[data-del='cover-image'], img.pp-user-cover-image").attr('src', response.cover_image_url);
168 // remove the picture upload path text.
169 $('input[name=eup_cover_image]', $editprofile_form).val('');
170 $('.profilepress-myaccount-has-cover-image', $editprofile_form).show();
171 $('.profilepress-myaccount-cover-image-empty', $editprofile_form).hide();
172 }
173
174 if ('message' in response) {
175 // save the response error message class for early removal in next request.
176 window.edit_profile_msg_class = $(response.message).attr('class');
177
178 $editprofile_form.before(response.message);
179
180 // Auto-scroll to the error message
181 if (response.message && response.message.includes('profilepress-edit-profile-status')) {
182 _this.scroll_to_notices($('.profilepress-edit-profile-status'));
183 }
184 }
185
186 if ('redirect' in response) {
187 $editprofile_form.trigger('pp_edit_profile_success_before_redirect');
188 window.location.assign(response.redirect)
189 }
190
191 _this._remove_processing_label($editprofile_form);
192 }
193 }, 'json');
194 };
195
196 this.ajax_password_reset = function (e) {
197 e.preventDefault();
198
199 var $passwordreset_form = $(this),
200 melange_id = _this.get_melange_id($passwordreset_form),
201 is_tab_widget = $passwordreset_form.find('input[name="is-pp-tab-widget"]').val() === 'true',
202 ajaxData = {
203 action: 'pp_ajax_passwordreset',
204 // if this is melange, we need it ID thus "&melange_id".
205 data: $(this).serialize() + '&melange_id=' + melange_id
206 };
207
208 _this._remove_status_notice();
209
210 $passwordreset_form.parents('.pp-tab-widget-form').prev('.pp-tab-status').remove();
211
212 _this._add_processing_label($passwordreset_form);
213
214 $.post(pp_ajax_form.ajaxurl, ajaxData, function (response) {
215
216 $passwordreset_form.trigger('pp_form_submitted');
217 // remove the processing label and do nothing if 0 is returned which perhaps means the user is
218 // already logged in.
219 if (typeof response !== 'object') {
220 return _this._remove_processing_label($passwordreset_form);
221 }
222
223 if ('message' in response) {
224 $passwordreset_form.trigger('pp_password_reset_status');
225 if (is_tab_widget) {
226 // tab widget has its own class for status notice/message which is pp-tab-status thus the replacement.
227 var notice = response.message.replace('profilepress-reset-status', 'pp-tab-status');
228 $passwordreset_form.parents('.pp-tab-widget-form').before(notice);
229 } else if ($passwordreset_form.parents('.lucidContainer').length > 0) {
230 $passwordreset_form.parents('.lucidContainer').before(response.message)
231 } else {
232 $passwordreset_form.before(response.message);
233 }
234
235 if ('status' in response && response.status === true) {
236 $passwordreset_form.hide();
237 }
238
239 $('input[name="user_login"]', $passwordreset_form).val('');
240 }
241
242 _this._remove_processing_label($passwordreset_form);
243
244 }, 'json');
245 };
246
247 this.ajax_registration = function (e) {
248
249 if (typeof window.FormData === 'undefined' || !window.FormData) return;
250
251 e.preventDefault();
252
253 var $signup_form = $(this),
254 melange_id = _this.get_melange_id($signup_form),
255 formData = new FormData(this),
256 is_tab_widget = $signup_form.find('input[name="is-pp-tab-widget"]').val() === 'true';
257
258 formData.append("action", "pp_ajax_signup");
259 formData.append("melange_id", melange_id);
260
261 _this._remove_status_notice();
262
263 $signup_form.parents('.pp-tab-widget-form').prev('.pp-tab-status').remove();
264
265 _this._add_processing_label($signup_form);
266
267 $.post({
268 url: pp_ajax_form.ajaxurl,
269 data: formData,
270 cache: false,
271 contentType: false,
272 enctype: 'multipart/form-data',
273 processData: false,
274 dataType: 'json',
275 success: function (response) {
276 $signup_form.trigger('pp_form_submitted');
277 // remove the processing label and do nothing if 0 is returned which perhaps means the user is
278 // already logged in.
279 if (typeof response !== 'object') {
280 return _this._remove_processing_label($signup_form);
281 }
282
283 if ('message' in response) {
284 // backward compat. To be removed in future
285 $signup_form.trigger('pp_registration_error', [response]);
286
287 $signup_form.trigger('pp_registration_ajax_response', [response]);
288
289 if (is_tab_widget) {
290 // tab widget has its own class for status notice/message which is pp-tab-status thus the replacement.
291 var notice = response.message.replace('profilepress-reg-status', 'pp-tab-status');
292 $signup_form.parents('.pp-tab-widget-form').before(notice);
293 }
294 // if lucid tab widget
295 else if ($signup_form.parents('.lucidContainer').length > 0) {
296 $signup_form.parents('.lucidContainer').before(response.message)
297 } else {
298 $signup_form.before(response.message);
299 }
300
301 // Auto-scroll to the error message
302 if (response.message && response.message.includes('profilepress-reg-status')) {
303 _this.scroll_to_notices();
304 }
305 } else if ('redirect' in response) {
306 $signup_form.trigger('pp_registration_success', [response]);
307 window.location.assign(response.redirect)
308 }
309 _this._remove_processing_label($signup_form);
310 }
311 });
312 };
313
314 this.ajax_login = function (e) {
315 e.preventDefault();
316
317 var $login_form = $(this),
318 ajaxData = {action: 'pp_ajax_login', data: $(this).serialize()},
319 is_tab_widget = $login_form.find('input[name="is-pp-tab-widget"]').val() === 'true';
320
321 _this._remove_status_notice();
322
323 _this._add_processing_label($login_form);
324
325 $.post(pp_ajax_form.ajaxurl, ajaxData, function (response) {
326 $login_form.trigger('pp_form_submitted');
327 // remove the processing label and do nothing if 0 is returned which perhaps means the user is
328 // already logged in.
329 // we are checking for null because response can be null hence we want the processing label removed.
330 if (response === null || typeof response !== 'object') {
331 return _this._remove_processing_label($login_form);
332 }
333
334 if ('success' in response && response.success === true && 'redirect' in response) {
335 $login_form.trigger('pp_login_form_success');
336 window.location.assign(response.redirect)
337 } else {
338
339 $login_form.trigger('pp_login_form_error');
340
341 if ('code' in response && response.code == 'pp2fa_auth_code_invalid') {
342 $login_form.find('.pp-2fa').show();
343 }
344
345 if (is_tab_widget) {
346 // tab widget has its own class for status notice/message which is pp-tab-status thus the replacement.
347 var notice = response.message.replace('profilepress-login-status', 'pp-tab-status');
348 $login_form.parents('.pp-tab-widget-form').before(notice);
349 }
350 // if lucid tab widget
351 else if ($login_form.parents('.lucidContainer').length > 0) {
352 $login_form.parents('.lucidContainer').before(response.message)
353 } else {
354 $login_form.before(response.message);
355 }
356 }
357
358 _this._remove_processing_label($login_form);
359
360 }, 'json');
361 };
362
363 this.delete_avatar = function (e) {
364
365 e.preventDefault();
366
367 var button_text = $(this).text(),
368 this_obj = $(this);
369
370 e.preventDefault();
371 if (confirm(pp_ajax_form.confirm_delete)) {
372
373 if (this_obj.is('button')) {
374 this_obj.text(pp_ajax_form.deleting_text);
375 }
376
377 $.post(pp_ajax_form.ajaxurl, {
378 action: 'pp_del_avatar',
379 nonce: pp_ajax_form.nonce
380 }).done(function (data) {
381 if ('error' in data && data.error === 'nonce_failed') {
382 this_obj.text(button_text);
383 alert(pp_ajax_form.deleting_error);
384 } else if ('success' in data) {
385 $("img[data-del='avatar']").attr('src', data.default);
386 this_obj.remove();
387 }
388 });
389
390 }
391 };
392
393 this.delete_profile_image_cover = function (e) {
394
395 e.preventDefault();
396
397 var button_text = $(this).text(), this_obj = $(this);
398
399 e.preventDefault();
400
401 if (confirm(pp_ajax_form.confirm_delete)) {
402
403 if (this_obj.is('button')) {
404 this_obj.text(pp_ajax_form.deleting_text);
405 }
406
407 $.post(pp_ajax_form.ajaxurl, {
408 action: 'pp_del_cover_image',
409 nonce: pp_ajax_form.nonce
410 }).done(function (data) {
411 if ('error' in data && data.error === 'nonce_failed') {
412 this_obj.text(button_text);
413 alert(pp_ajax_form.deleting_error);
414 }
415
416 if ('success' in data) {
417 if (data.default !== '') {
418 $("img[data-del='cover-image']").attr('src', data.default);
419 this_obj.parent().find('.profilepress-myaccount-has-cover-image').show();
420 this_obj.parent().find('.profilepress-myaccount-cover-image-empty').hide();
421 } else {
422 this_obj.parent().find('.profilepress-myaccount-has-cover-image').hide();
423 this_obj.parent().find('.profilepress-myaccount-cover-image-empty').show();
424 }
425
426 this_obj.remove();
427 }
428 });
429
430 }
431 };
432
433 this.get_melange_id = function ($scope) {
434 var melange_id = $('input.pp_melange_id', $scope).val();
435 return melange_id === undefined ? '' : melange_id;
436 };
437
438 this._add_processing_label = function (obj) {
439 var submit_btn = obj.find('input[data-pp-submit-label]');
440 submit_btn.attr({
441 'value': submit_btn.data('pp-processing-label'),
442 'disabled': 'disabled',
443 }).css("opacity", ".4");
444 };
445
446 this._remove_processing_label = function (obj) {
447 var submit_btn = obj.find('input[data-pp-submit-label]');
448 submit_btn.attr('value', submit_btn.data('pp-submit-label'));
449 submit_btn.attr({
450 'value': submit_btn.data('pp-submit-label'),
451 // set to null to remove. See https://api.jquery.com/attr/#attr-attributeName-value
452 'disabled': null,
453 }).css("opacity", "");
454 };
455
456 this._remove_status_notice = function () {
457 $('.profilepress-login-status,.pp-tab-status,.profilepress-edit-profile-success,.profilepress-edit-profile-status,.pp-reset-success,.profilepress-reset-status,.profilepress-reg-status').remove();
458 };
459
460 this.scroll_to_notices = function (scrollElement) {
461 // Default to profilepress-reg-status if no specific element provided
462 scrollElement = scrollElement || $('.profilepress-reg-status');
463 if (scrollElement.length) {
464 $('html, body').animate({
465 scrollTop: (scrollElement.offset().top - 100)
466 }, 1000);
467 }
468 };
469
470 this.defaultUserProfileResponsive = function () {
471
472 $('.ppress-default-profile, .pp-member-directory, .ppress-checkout__form').each(function () {
473
474 var obj = $(this),
475 element_width = obj.width();
476
477 if (element_width <= 340) {
478
479 obj.removeClass('ppressui340');
480 obj.removeClass('ppressui500');
481 obj.removeClass('ppressui800');
482 obj.removeClass('ppressui768');
483 obj.removeClass('ppressui960');
484
485 obj.addClass('ppressui340');
486
487 } else if (element_width <= 500) {
488
489 obj.removeClass('ppressui340');
490 obj.removeClass('ppressui500');
491 obj.removeClass('ppressui768');
492 obj.removeClass('ppressui800');
493 obj.removeClass('ppressui960');
494
495 obj.addClass('ppressui500');
496
497 } else if (element_width <= 768) {
498
499 obj.removeClass('ppressui340');
500 obj.removeClass('ppressui500');
501 obj.removeClass('ppressui768');
502 obj.removeClass('ppressui800');
503 obj.removeClass('ppressui960');
504
505 obj.addClass('ppressui768');
506
507 } else if (element_width <= 800) {
508
509 obj.removeClass('ppressui340');
510 obj.removeClass('ppressui500');
511 obj.removeClass('ppressui768');
512 obj.removeClass('ppressui800');
513 obj.removeClass('ppressui960');
514
515 obj.addClass('ppressui800');
516
517 } else if (element_width <= 960) {
518
519 obj.removeClass('ppressui340');
520 obj.removeClass('ppressui500');
521 obj.removeClass('ppressui768');
522 obj.removeClass('ppressui800');
523 obj.removeClass('ppressui960');
524
525 obj.addClass('ppressui960');
526
527 } else if (element_width > 960) {
528
529 obj.removeClass('ppressui340');
530 obj.removeClass('ppressui500');
531 obj.removeClass('ppressui768');
532 obj.removeClass('ppressui800');
533 obj.removeClass('ppressui960');
534 }
535
536 obj.css('opacity', 1);
537 });
538
539 $('.ppress-default-profile-cover, .ppress-default-profile-cover-e').each(function () {
540
541 var elem = $(this),
542 calcHeight = Math.round(elem.width() / elem.data('ratio')) + 'px';
543
544 elem.height(calcHeight);
545 elem.find('.ppress-dpf-cover-add').height(calcHeight);
546 });
547 };
548
549 this.myaccount_password_strength_meter = function () {
550 function checkPasswordStrength($pass1,
551 $pass2,
552 $strengthResult,
553 $submitButton,
554 blacklistArray) {
555 var pass1 = $pass1.val();
556 var pass2 = $pass2.val();
557 // Reset the form & meter
558 $submitButton.attr('disabled', 'disabled');
559 $strengthResult.removeClass('short bad good strong');
560 // Extend our blacklist array with those from the inputs & site data
561 blacklistArray = blacklistArray.concat(wp.passwordStrength.userInputDisallowedList());
562 // Get the password strength
563 var strength = wp.passwordStrength.meter(pass1, blacklistArray, pass2);
564 // Add the strength meter results
565 switch (strength) {
566 case 2:
567 $strengthResult.addClass('bad').html(pwsL10n.bad);
568 break;
569 case 3:
570 $strengthResult.addClass('good').html(pwsL10n.good);
571 break;
572 case 4:
573 $strengthResult.addClass('strong').html(pwsL10n.strong);
574 break;
575 case 5:
576 $strengthResult.addClass('short').html(pwsL10n.mismatch);
577 break;
578 default:
579 $strengthResult.addClass('short').html(pwsL10n.short);
580 }
581 // The meter function returns a result even if pass2 is empty,
582 // enable only the submit button if the password is strong and
583 // both passwords are filled up
584 if (myacPwsL10n.disable_enforcement === 'false' && 4 === strength && '' !== pass2.trim()) {
585 $submitButton.removeAttr('disabled');
586 }
587
588 return strength;
589 }
590
591 $(function () {
592
593 var password1 = $('input[name=password_new]');
594 var password2 = $('input[name=password_confirm_new]');
595 var submitButton = $('input[name=submit-form]');
596 var strengthMeterId = $('#pp-pass-strength-result');
597
598 // Binding to trigger checkPasswordStrength
599 $('body').on('keyup', 'input[name=password_new], input[name=password_confirm_new]',
600 function (event) {
601 checkPasswordStrength(
602 password1, // First password field
603 password2, // Second password field
604 strengthMeterId, // Strength meter
605 submitButton, // Submit button
606 [] // Blacklisted words
607 );
608 }
609 );
610 });
611 };
612 }
613
614 (new Frontend()).init();
615
616 (new Checkout()).init();