PageSourceSearch

https://shadowyoga.com/wp-content/plugins/woocommerce-custom-fields/assets/js/general.js?ver=2.3.4

js shadowyoga.com collected 2026-09-24 11:12:10 UTC 70,291 bytes, 1,995 lines download raw bytes

1/**
2 * WooCommerce Custom Fields Plugin General Scripts
3 */
4jQuery(document).ready(function() {
5
6    // Track if customer id cookie was set
7    var customer_id_cookie_set = false;
8
9    // Checkout color picker handlers
10    var checkout_color_picker_handlers = {};
11
12    // Character limit flash control
13    var character_limit_flashing = false;
14
15    /**
16     * Ajax request delay function
17     */
18    var delay_while_typing = (function(){
19        var timer = 0;
20        return function(callback, ms) {
21            clearTimeout(timer);
22            timer = setTimeout(callback, ms);
23        };
24    })();
25
26    /**
27     * Limit multiselect or checkbox selections
28     */
29    jQuery('select[data-wccf-max-selected] option').click(function() {
30
31        // Get selected options
32        var select_field = jQuery(this).parent();
33        var selected_options = select_field.find('option:selected');
34
35        // Get counts
36        var selected_option_count = selected_options.length;
37        var max_selected = select_field.data('wccf-max-selected');
38        var difference = selected_option_count - max_selected;
39
40        // Limit reached
41        if (selected_option_count > max_selected) {
42
43            // Remove last
44            jQuery(this).removeAttr('selected');
45
46            // Limit is still reached (selection made while holding SHIFT)
47            if (difference > 1) {
48                for (var i = 1; i < difference; i++) {
49                    select_field.find('option:selected').first().removeAttr('selected');
50                }
51            }
52        }
53    });
54    jQuery('input[data-wccf-max-selected]').change(function() {
55
56        var name = jQuery(this).prop('name');
57
58        // Get checked checkboxes
59        var checked_checkboxes = jQuery(this).closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').find('input[name="' + name + '"]:checked');
60
61        // Limit reached
62        if (checked_checkboxes.length > jQuery(this).data('wccf-max-selected')) {
63            this.checked = false;
64        }
65    });
66
67    /**
68     * Frontend setup
69     */
70    if (!wccf_general_config.is_backend) {
71
72        /**
73         * Dynamic extra fees on checkout
74         */
75        jQuery('form .wccf_checkout_field').each(function() {
76
77            // Check if field has pricing
78            if (!jQuery(this).data('wccf-checkout-pricing')) {
79                return;
80            }
81
82            // Handle field value change
83            jQuery(this).change(function(e) {
84
85                // Trigger checkout refresh but only if this is not a file field (handled separately)
86                if (jQuery(this).prop('type') !== 'file') {
87                    jQuery('body').trigger('update_checkout');
88                }
89            });
90        });
91
92        /**
93         * Toggle character limit
94         */
95        jQuery('.wccf[data-wccf-character-limit]').each(function() {
96            set_up_character_limit_for_field(jQuery(this));
97        });
98
99        /**
100         * Set customer id cookie if none exists yet
101         */
102        jQuery('.wccf_field_container input.wccf_file').first().each(function() {
103            maybe_set_customer_id_cookie();
104        });
105
106        /**
107         * Maybe reload fields via Ajax on variation change
108         */
109        if (jQuery('form.variations_form div[data-wccf-uses-attribute-conditions]').length) {
110            jQuery('.variations_form').on('woocommerce_variation_has_changed', function() {
111                refresh_product_fields_view();
112            }).closest('form.cart').on('found_variation', function() {
113                refresh_product_fields_view();
114            });
115        }
116
117        /**
118         * Maybe reload fields via Ajax on quantity change
119         */
120        jQuery('form.cart input[name="quantity"]').each(function() {
121
122            // Quantity change
123            jQuery(this).change(function() {
124                handle_product_quantity_change(jQuery(this));
125            });
126
127            // Initial setup
128            if (jQuery(this).val() > 1) {
129                handle_product_quantity_change(jQuery(this));
130            }
131        });
132    }
133
134    /**
135     * Set up character limit for single field
136     */
137    function set_up_character_limit_for_field(field)
138    {
139        var character_limit_element = field.closest('.wccf_field_container').find('.wccf_character_limit');
140
141        // Show or hide character limit
142        if (character_limit_element.length) {
143            field.focus(function() {
144                character_limit_element.fadeIn();
145            });
146            field.focusout(function() {
147                character_limit_element.fadeOut();
148            });
149        }
150
151        // Update character limit
152        field.keyup(function() {
153            update_characters_remaining(jQuery(this));
154        });
155        field.change(function() {
156            update_characters_remaining(jQuery(this));
157        });
158        update_characters_remaining(field);
159
160        // Prevent form submit
161        field.closest('form').submit(function(e) {
162            if (field.val().length > field.data('wccf-character-limit')) {
163                field.focus();
164                e.preventDefault();
165            }
166        });
167    }
168
169    /**
170     * Update characters remaining
171     */
172    function update_characters_remaining(field)
173    {
174
175        // Select character limit element
176        var character_limit_element = field.closest('.wccf_field_container').find('.wccf_character_limit');
177
178        // Check if character limit element exists
179        if (character_limit_element.length) {
180
181            // Get character limit value
182            var limit = field.data('wccf-character-limit');
183
184            // Get number of remaining character
185            var remaining = limit - field.val().length;
186
187            // Update value
188            character_limit_element.find('.wccf_characters_remaining').html(remaining);
189
190            // Over limit
191            if (remaining < 0) {
192
193                // Set over quota class
194                character_limit_element.addClass('wccf_character_limit_over_quota');
195
196                // Flash element twice
197                if (!character_limit_flashing) {
198                    character_limit_flashing = true;
199                    character_limit_element.fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100, function() {
200                        character_limit_flashing = false;
201                    });
202                }
203            }
204            // Not over limit
205            else {
206
207                // Unset over quota class
208                character_limit_element.removeClass('wccf_character_limit_over_quota');
209            }
210        }
211    }
212
213    /**
214     * Set customer id cookie if none exists yet
215     */
216    function maybe_set_customer_id_cookie()
217    {
218        // Check if cookie is already set
219        if (customer_id_cookie_set) {
220            return;
221        }
222
223        // Send request to retrieve customer id cookie data
224        jQuery.get(
225            wccf_general_config.ajaxurl,
226            {
227                action:     'wccf_get_customer_id_cookie_data'
228            },
229            function(response) {
230
231                // Parse response
232                response = parse_ajax_json_response(response);
233
234                // Error occurred
235                if (typeof response !== 'object' || typeof response.result === 'undefined' || response.result !== 'success' || typeof response.data !== 'object') {
236                    return;
237                }
238
239                // Get cookie data
240                var cookie_data = response.data;
241
242                // Cookie already exists
243                if (cookie_exists(cookie_data.name)) {
244                    return;
245                }
246
247                // Set customer id cookie
248                set_cookie(cookie_data);
249            }
250        );
251
252        customer_id_cookie_set = true;
253    }
254
255    /**
256     * Check if cookie exists
257     */
258    function cookie_exists(cookie_name)
259    {
260        return document.cookie.indexOf(cookie_name + '=') !== -1;
261    }
262
263    /**
264     * Set cookie
265     */
266    function set_cookie(cookie_data)
267    {
268        // Generate cookie string
269        var cookie = cookie_data.name + '=' + cookie_data.value + '; expires=' + cookie_data.expiration + '; path=' + cookie_data.path;
270
271        // Optionally make cookie secure
272        if (cookie_data.secure) {
273            cookie += '; secure';
274        }
275
276        // Optionally set cookie domain
277        if (cookie_data.domain) {
278            cookie += '; domain=' + cookie_data.domain;
279        }
280
281        // Set cookie
282        document.cookie = cookie;
283    }
284
285    /**
286     * Maybe reload fields via Ajax on quantity change
287     */
288    function handle_product_quantity_change(quantity_field) {
289        if (quantity_field.closest('form.cart').find('.wccf[data-wccf-quantity-based]').length) {
290            refresh_product_fields_view();
291        }
292    }
293
294    /**
295     * Refresh product fields view
296     */
297    function refresh_product_fields_view()
298    {
299
300        // Take reference of the form
301        var form = jQuery('form.cart');
302
303        // Block UI until we update fields
304        form.block({
305            message: null,
306            overlayCSS: {
307                background: '#fff',
308                opacity: 0.6
309            }
310        });
311
312        // Prepare data
313        var form_data = form.serialize();
314
315        // Add product id
316        form.find('button[type="submit"][name="add-to-cart"]').each(function() {
317
318            var product_id = jQuery(this).val();
319
320            if (product_id) {
321                form_data += (form_data !== '' ? '&' : '') + 'rightpress_reference_product_id=' + product_id;
322            }
323        });
324
325        // Update product fields
326        jQuery.post(
327            wccf_general_config.ajaxurl,
328            {
329                action:     'wccf_refresh_product_field_view',
330                data:       form_data
331            },
332            function(response) {
333
334                // Parse response
335                response = parse_ajax_json_response(response);
336
337                // Error occurred or response is malformed
338                if (typeof response !== 'object' || typeof response.result === 'undefined' || response.result !== 'success' || typeof response.fields !== 'object') {
339                    alert(wccf_general_config.messages.error_reload_notice + ' ' + wccf_general_config.messages.page_reload);
340                    location.reload();
341                    return;
342                }
343
344                // Take reference of old container
345                var old_container = jQuery('div#wccf_product_field_master_container');
346
347                // Check if we have any fields
348                if (response.fields.length) {
349
350                    // Insert hidden new container before old container
351                    old_container.before('<div id="wccf_product_field_master_container_
351copy" style="display: none;"></div>');
352
353                    // Take reference of new container
354                    var new_container = jQuery('div#wccf_product_field_master_container_copy');
355
356                    // Iterate over fields
357                    jQuery.each(response.fields, function(index, field) {
358
359                        // Move existing field from old container to new container (detach method copies all events too)
360                        if (old_container.find('#' + field.element_id).length) {
361                            old_container.find('#' + field.element_id).closest('div.wccf_field_container').detach().appendTo(new_container);
362                        }
363                        // Special handling for existing checkbox and radio button sets
364                        else if (['checkbox', 'radio'].indexOf(field.field_type) !== -1 && old_container.find('input[name="' + field.element_name + '"]').length) {
365                            old_container.find('input[name="' + field.element_name + '"]').closest('div.wccf_field_container').detach().appendTo(new_container);
366                        }
367                        // Field does not exist
368                        else {
369
370                            // Print new field
371                            new_container.append(field.html);
372
373                            // Take reference of new field element
374                            var new_field = new_container.find('.wccf_field_container').last();
375
376                            // Set up field functionality after dynamic field view update
377                            set_up_field_functionality_after_view_update(new_field, form, false);
378
379                            // Set up live product update
380                            form.trigger('rightpress_live_product_update_attach_input', new_field);
381                        }
382                    });
383
384                    // Remove old container along with no longer needed fields
385                    old_container.remove();
386
387                    // Change id of new container
388                    jQuery('div#wccf_product_field_master_container_copy').prop('id', 'wccf_product_field_master_container').fadeIn();
389                }
390                // No fields returned
391                else {
392
393                    // Remove all fields from old container
394                    old_container.empty();
395                }
396
397                // Recheck frontend conditions
398                wccf_check_frontend_conditions(form);
399
400                // Trigger live product update
401                form.trigger('rightpress_live_product_update_trigger');
402
403                // Unblock UI
404                form.unblock();
405            }
406        );
407    }
408
409    /**
410     * Set up field functionality after dynamic field view update
411     */
412    function set_up_field_functionality_after_view_update(field, form, preserve_existing_files_on_server)
413    {
414
415        // Set up character limit
416        field.find('.wccf[data-wccf-character-limit]').each(function() {
417            set_up_character_limit_for_field(jQuery(this));
418        });
419
420        // Set customer id cookie if not exists
421        field.find('input.wccf_file').first().each(function() {
422            maybe_set_customer_id_cookie();
423        });
424
425        // Set up date/time pickers
426        jQuery.each(['date', 'time', 'datetime'], function(index, type) {
427            field.find('.wccf_' + type).each(function() {
428                set_up_datetimepicker(jQuery(this), type);
429            });
430        });
431
432        // Set up color picker
433        field.find('.wccf_color').each(function() {
434            set_up_color_picker(jQuery(this));
435        });
436
437        // Set up frontend condition checks for this element
438        field.find('.wccf').each(function() {
439            wccf_initialize_frontend_condition_checks(jQuery(this), form);
440        });
441
442        // Set up file uploads
443        field.find('.wccf_file').each(function() {
444            wccf_set_up_file_upload_handler_for_field(jQuery(this), preserve_existing_files_on_server);
445        });
446    }
447
448    /**
449     * Date/time pickers
450     */
451    jQuery.each(['date', 'time', 'datetime'], function(index, type) {
452        jQuery('.wccf_' + type).each(function() {
453            set_up_datetimepicker(jQuery(this), type);
454        });
455    });
456
457    /**
458     * Color picker
459     */
460    jQuery('.wccf_color').each(function() {
461        set_up_color_picker(jQuery(this));
462    });
463
464    /**
465     * Check frontend conditions
466     */
467    function wccf_check_frontend_conditions(form)
468    {
469
470        var fields_with_pricing_displayed = false;
471
472        // Do not check frontend conditions in cart item product field editing form
473        if (form.prop('id') === 'wccf_cart_item_product_field_editing_form') {
474            return;
475        }
476
477        // Iterate over all custom fields in this form
478        form.find('.wccf').each(function() {
479
480            // Get element id and conditions variable name
481            var id = jQuery(this).prop('id');
482            var conditions = 'wccf_conditions_' + id;
483
484            // Check if we have any conditions for this field
485            if (typeof window[conditions] !== 'undefined') {
486
487                // Track if all conditions match
488                var conditions_match = true;
489
490                // Reference context
491                var context = window[conditions]['context'];
492
493                // Iterate over conditions
494                for (var i = 0; i < window[conditions]['conditions'].length; i++) {
495
496                    // Reference current condition and context
497                    var condition = window[conditions]['conditions'][i];
498
499                    // Check if condition is matched
500                    if (!check_condition(form, condition, context)) {
501                        conditions_match = false;
502                    }
503                }
504
505                // Show field
506                if (conditions_match) {
507                    jQuery('#' + id).closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').each(function() {
508                        jQuery(this).fadeIn().find('input, select, textarea').removeAttr('disabled').each(function() {
509                            if (typeof jQuery(this).data('wccf-pricing') !== 'undefined') {
510                                fields_with_pricing_displayed = true;
511                            }
512                        });
513                    });
514                }
515                // Hide field
516                else {
517                    jQuery('#' + id).closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').each(function() {
518                        jQuery(this).fadeOut().find('input, select, textarea').attr('disabled', 'disabled');
519                    });
520                }
521            }
522        });
523
524        // Trigger live product update if needed
525        if (fields_with_pricing_displayed) {
526            form.trigger('rightpress_live_product_update_trigger');
527        }
528    }
529
530    /**
531     * Check single frontend condition
532     */
533    function check_condition(form, condition, context)
534    {
535        if (condition.type === 'other__other_custom_field') {
536            return check_condition_other_custom_field(form, condition, context);
537        }
538
539        return false;
540    }
541
542    /**
543     * Check other custom field condition
544     */
545    function check_condition_other_custom_field(form, condition, context)
546    {
547
548        // Reference field that we are checking against
549        var other_field = form.find('.wccf_' + context + '[name="wccf[' + context + '][' + condition.other_field_id + ']"]:not(:disabled), .wccf_' + context + '[name="wccf_ignore[' + context + '][' + condition.other_field_id + ']"]:not(:disabled)');
550
551        // Field not found? Maybe it accepts multiple values
552        if (!other_field.length) {
553            other_field = form.find('.wccf_' + context + '[name="wccf[' + context + '][' + condition.other_field_id + '][]"]:not(:disabled), .wccf_' + context + '[name="wccf_ignore[' + context + '][' + condition.other_field_id + '][]"]:not(:disabled)');
554        }
555
556        // Reference method
557        var method = condition.method_option;
558
559        // Reference field not found on page
560        if (!other_field.length) {
561
562            // Negative conditions
563            if (['is_empty', 'does_not_contain', 'does_not_equal', 'is_not_checked'].indexOf(method) !== -1) {
564                return true;
565            }
566            // Positive conditions
567            else {
568                return false;
569            }
570        }
571
572        // Is Empty
573        if (method === 'is_empty') {
574            return is_empty(form, other_field);
575        }
576
577        // Is Not Empty
578        else if (method === 'is_not_empty') {
579            return !is_empty(form, other_field);
580        }
581
582        // Contains
583        else if (method === 'contains') {
584            return contains(form, other_field, condition.text);
585        }
586
587        // Does Not Contain
588        else if (method === 'does_not_contain') {
589            return !contains(form, other_field, condition.text);
590        }
591
592        // Equals
593        else if (method === 'equals') {
594            return equals(form, other_field, condition.text);
595        }
596
597        // Does Not Equal
598        else if (method === 'does_not_equal') {
599            return !equals(form, other_field, condition.text);
600        }
601
602        // Less Than
603        else if (method === 'less_than') {
604            return less_than(form, other_field, condition.text);
605        }
606
607        // Less Or Equal To
608        else if (method === 'less_or_equal_to') {
609            return !more_than(form, other_field, condition.text);
610        }
611
612        // More Than
613        else if (method === 'more_than') {
614            return more_than(form, other_field, condition.text);
615        }
616
617        // More Or Equal
618        else if (method === 'more_or_equal') {
619            return !less_than(form, other_field, condition.text);
620        }
621
622        // Is Checked
623        else if (method === 'is_checked') {
624            return is_checked(form, other_field);
625        }
626
627        // Is Not Checked
628        else if (method === 'is_not_checked') {
629            return !is_checked(form, other_field);
630        }
631    }
632
633    /**
634     * Check if field element is empty
635     */
636    function is_empty(form, field)
637    {
638        // Get value of field that we are checking against
639        var field_value = get_value(form, field);
640
641        // Check if value is empty
642        return (field_value === '' || field_value === null || field_value.length === 0);
643    }
644
645    /**
646     * Check if field element contains string
647     */
648    function contains(form, field, value)
649    {
650        // Get value of field that we are checking against
651        var field_value = get_value(form, field);
652
653        // Will check subscring in string and whole array value in array (for select fields)
654        if (field_value !== null && field_value.indexOf(value) > -1) {
655            return true;
656        }
657
658        return false;
659    }
660
661    /**
662     * Check if field element equals string
663     */
664    function equals(form, field, value)
665    {
666        // Get value of field that we are checking against
667        var field_value = get_string_value(get_value(form, field));
668
669        // Check if it equals given string
670        if (field_value !== false && field_value === value) {
671            return true;
672        }
673
674        return false;
675    }
676
677    /**
678     * Check if field element is less than value
679     */
680    function less_than(form, field, value)
681    {
682        // Get value of field that we are checking against
683        var field_value = get_number_value(get_string_value(get_value(form, field)));
684
685        // Check if value is less than given value
686        if (field_value !== false && field_value < value) {
687            return true;
688        }
689
690        return false;
691    }
692
693    /**
694     * Check if field element is more than value
695     */
696    function more_than(form, field, value)
697    {
698        // Get value of field that we are checking against
699        var field_value = get_number_value(get_string_value(get_value(form, field)));
700
701        // Check if value is less than given value
702        if (field_value !== false && field_value > value) {
703            return true;
704        }
705
706        return false;
707    }
708
709    /**
710     * Get number value from string value
711     */
712    function get_number_value(value)
713    {
714        // Empty string equals zero
715        if (value === '') {
716            return 0;
717        }
718
719        // Try parsing value to float
720        var float_value = parseFloat(value);
721
722        // Value appears to be a number
723        if (!isNaN(float_value)) {
724            return float_value;
725        }
726
727        // Value does not appear to be a number
728        return false;
729    }
730
731    /**
732     * Check if field element is checked
733     */
734    function is_checked(form, field)
735    {
736        // In case of radio or checkbox
737        if (field.is(':checkbox') || field.is(':radio')) {
738            if (field.is(':checked')) {
739                return true;
740            }
741        }
742
743        // In case of other fields - make sure it's not empty
744        else {
745            if (!is_empty(form, field)) {
746                return true;
747            }
748        }
749
750        return false;
751    }
752
753    /**
754     * Get value depending on field type
755     */
756    function get_value(form, field)
757    {
758        // Special handling for checkboxes and radio buttons
759        if (field.is(':radio') || field.is(':checkbox')) {
760
761            // Store values
762            var values = [];
763
764            // Iterate over options and pick those that are checked
765            field.each(function() {
766                if (jQuery(this).is(':checked')) {
767                    values.push(jQuery(this).val());
768                }
769            });
770
771            // Return values
772            return values.length > 0 ? values : '';
773        }
774        // Special handling for file fields
775        else if (field.is(':file')) {
776
777            // Get uploaded files
778            var upload_list = field.closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').find('.wccf_file_upload_list');
779
780            // No uploaded files found
781            if (!upload_list.length) {
782                return '';
783            }
784
785            // Store values
786            var values = [];
787
788            // Iterate over uploaded files
789            upload_list.each(function() {
790                if (jQuery(this).find('._' + field.prop('id')).length) {
791                    var upload_value = jQuery(this).find('._' + field.prop('id')).val();
792
793                    if (upload_value) {
794                        values.push(upload_value);
795                    }
796                }
797            });
798
799            // Return values
800            return values.length > 0 ? values : '';
801        }
802        // Field selected successfully - return value
803        else {
804            return (typeof field.val() === 'string' ? field.val().trim() : field.val());
805        }
806    }
807
808    /**
809     * Get string value from any value
810     */
811    function get_string_value(value)
812    {
813        // In case of string
814        if (typeof value === 'string') {
815            return value.trim();
816        }
817
818        // In case of array with single element
819        if (value !== null && typeof value === 'object' && value.length === 1 && typeof value[0] === 'string') {
820            return value[0].trim();
821        }
822
823        return false;
824    }
825
826    /**
827     * Clear field value
828     */
829    function clear_field_value(field)
830    {
831        if (field.is('select')) {
832            field.prop('selectedIndex', 0);
833            if (field.hasClass('rightpress_select2')) {
834                field.val('').change();
835            }
836        }
837        else if (field.is(':radio, :checkbox')) {
838            field.removeAttr('checked');
839        }
840        else {
841            field.val('');
842        }
843    }
844
845    /**
846     * Initialize frontend condition checks
847     */
848    function wccf_initialize_frontend_condition_checks(field, form)
849    {
850            // Key up
851            field.keyup(function() {
852                wccf_check_frontend_conditions(form);
853            });
854
855            // Value change
856            field.change(function() {
857                wccf_check_frontend_conditions(form);
858            });
859    }
860    jQuery('form:has(.wccf)').each(function() {
861
862        // Get reference
863        var form = jQuery(this);
864
865        // Check conditions on page load
866        wccf_check_frontend_conditions(form);
867
868        // Check conditions on interaction with elements
869        form.find('.wccf').each(function() {
870            wccf_initialize_frontend_condition_checks(jQuery(this), form);
871        });
872    });
873
874    /**
875     * Handle file uploads
876     */
877    function wccf_set_up_file_upload_handler_for_field(field, preserve_existing_files_on_server)
878    {
879
880        // Get field container for current element
881        var field_container = field.closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container');
882        var form = field.closest('form');
883
884        // Get current element properties
885        var field_id = field.prop('id');
886        var is_multiple = field.prop('multiple');
887
888        // Prepare hidden field name and id
889        var hidden_field_class = '_' + field_id;
890        var hidden_field_name = field.prop('name').replace('wccf_ignore[', 'wccf[');
891
892        // Hidden field name must always accept multiple values so we get them in an array on server and processing is the same in all cases
893        if (hidden_field_name.indexOf('[]', (hidden_field_name.length - 2)) === -1) {
894            hidden_field_name += '[]';
895        }
896
897        // Track progress of individual file uploads
898        var wccf_upload_progress_ids = [];
899        var wccf_upload_progress_id = 0;
900
901        // Keep track of uploaded file sizes for concurrent file upload validation
902        field_container.data('wccf_uploaded_file_sizes', {});
903
904        // Define additional data to be passed to server
905        var additional_data = [{
906            name:       'action',
907            value:      'wccf_file_upload'
908        }];
909
910        // Add post ID if present
911        jQuery('form[name="post"] input[name="post_ID"], form#your-profile input[name="user_id"]').each(function() {
912            additional_data.push({
913                name:   'item_id',
914                value:  jQuery(this).val()
915            });
916        });
917
918        var jqXHR = null;
919
920        // Abort any previous uploads and reset upload progress file counter
921        field.change(function() {
922            if (jqXHR !== null) {
923                jqXHR.abort();
924            }
925        });
926
927        // Initialize file upload handler
928        field.fileupload({
929            url: wccf_general_config.ajaxurl,
930            sequentialUploads: true,
931            type: 'POST',
932            formData: function () {
933                return additional_data.concat([{name: 'form_data', value: form.serialize()}]);
934            },
935            dataType: 'json',
936            dataFilter: function(raw_response) {
937                return parse_ajax_json_response(raw_response, true);
938            },
939            add: function (e, data) {
940
941                // Increment upload progress identifier
942                wccf_upload_progress_id++;
943
944                // Set identifier to data object
945                data.wccf_upload_progress_id = wccf_upload_progress_id;
946                wccf_upload_progress_ids.push(wccf_upload_progress_id);
947
948                // Clear previous file if multiple files are not allowed
949                if (!is_multiple) {
950                    field_container.find('.wccf_file_upload_list').html('');
951                    field_container.find('.wccf_file_upload_status').remove();
952                }
953
954                // Get message
955                var message = wccf_general_config.messages.file_uploading + ' ' + data.files[0].name;
956
957                // Append progress bar
958                field_container.find('input[type="file"]').before('<div class="wccf_file_upload_status wccf_file_upload_status_' + wccf_upload_progress_id + '"><small class="wccf_file_upload_message">' + message + '</small><small class="wccf_file_upload_progress_wrapper"><small class="wccf_file_upload_progress"></small></small></div>');
959
960                // Start uploading
961                jqXHR = data.submit();
962            },
963            progress: function (e, data) {
964
965                // Select correct status element
966                var status_element = field_container.find('.wccf_file_upload_status_' + data.wccf_upload_progress_id);
967
968                // Get progress
969                var progress = parseInt(data.loaded / data.total * 100, 10);
970
971                // Update progress
972                status_element.find('small.wccf_file_upload_progress').css('width', progress + '%');
973            },
974            done: function (e, data) {
975
976                // Remove from tracked progress ids array
977                if (wccf_upload_progress_ids.indexOf(data.wccf_upload_progress_id) !== -1) {
978                    wccf_upload_progress_ids.splice(wccf_upload_progress_ids.indexOf(data.wccf_upload_progress_id), 1);
979                }
980
981                // Handle error
982                if (typeof data.result !== 'object' || typeof data.result.result === 'undefined' || data.result.result !== 'success') {
983                    handle_file_upload_error(data, field_container);
984                    return;
985                }
986
987                // Add file size to array
988                var current_file_sizes = field_container.data('wccf_uploaded_file_sizes');
989                current_file_sizes[data.result.access_key] = data.result.file_size;
990                field_container.data('wccf_uploaded_file_sizes', current_file_sizes);
991
992                // Display uploaded file
993                field_container.find('.wccf_file_upload_status_' + data.wccf_upload_progress_id).animate({'opacity': 0}, 500, function() {
994
995                    // Add file list container if needed
996                    if (field_container.find('.wccf_file_upload_list').length === 0) {
997
998                        // Special handling for user fields in backend user edit page
999                        if (field_container.hasClass('wccf_user_profile_field_container')) {
1000                            field_container.find('.wccf_user_field_file').before('<div class="wccf_file_upload_list"></div>');
1001                        }
1002                        else {
1003                            field_container.find('label[for="' + field_id + '"]').after('<div class="wccf_file_upload_list"></div>');
1004                        }
1005                    }
1006
1007                    // Add file to list
1008                    field_container.find('.wccf_file_upload_list').append('<small class="wccf_file_upload_item">' + data.files[0].name + ' <span class="wccf_file_upload_delete">' + wccf_general_config.messages.file_upload_delete + '</span></small>');
1009
1010                    // Maybe add left border class
1011                    toggle_file_upload_list_border(field_container);
1012
1013                    // Select last element in list
1014                    field_container.find('.wccf_file_upload_list .wccf_file_upload_item').last().each(function() {
1015
1016                        // Inject hidden field that will submit file access keys along with regular form submit
1017                        jQuery(this).append('<input type="hidden" class="' + hidden_field_class + '" name="' + hidden_field_name + '" value="' + data.result.access_key + '" data-wccf-file-access-key="' + data.result.access_key + '">');
1018
1019                        // Handle file removal
1020                        file_removal_setup(jQuery(this), field_container, false);
1021                    });
1022
1023                    // Remove upload status message
1024                    jQuery(this).remove();
1025
1026                    // Trigger live product update
1027                    form.trigger('rightpress_live_product_update_trigger');
1028
1029                    // Refresh checkout view if this is a checkout field with pricing
1030                    refresh_checkout_on_checkout_file_field_change(field);
1031
1032                    // Toggle required property for file field in backend
1033                    toggle_file_required(field_container.find('input[data-wccf-field-id]').first());
1034
1035                    // Recheck frontend conditions
1036                    wccf_check_frontend_conditions(field_container.closest('form'));
1037                });
1038            },
1039            fail: function (e, data) {
1040
1041                // Remove from tracked progress ids array
1042                if (wccf_upload_progress_ids.indexOf(data.wccf_upload_progress_id) !== -1) {
1043                    wccf_upload_progress_ids.splice(wccf_upload_progress_ids.indexOf(data.wccf_upload_progress_id), 1);
1044                }
1045
1046                // Handle error
1047                handle_file_upload_error(data, field_container);
1048
1049                // Toggle required property for file field in backend
1050                toggle_file_required(field_container.find('input[data-wccf-field-id]').first());
1051
1052                // Recheck frontend conditions
1053                wccf_check_frontend_conditions(field_container.closest('form'));
1054            },
1055            send: function (e, data) {
1056                data.data.append('wccf_uploaded_file_sizes', JSON.stringify(field_container.data('wccf_uploaded_file_sizes')));
1057            }
1058        });
1059
1060        // Disable form submit if there's at least one file uploading
1061        form.submit(function(e) {
1062            if (wccf_upload_progress_ids.length) {
1063                e.preventDefault();
1064            }
1065        });
1066
1067        // Set up file removal for any existing files
1068        field_container.find('.wccf_file_upload_list .wccf_file_upload_item').each(function() {
1069            file_removal_setup(jQuery(this), field_container, preserve_existing_files_on_server);
1070        });
1071    }
1072    jQuery('form .wccf_file').each(function() {
1073        wccf_set_up_file_upload_handler_for_field(jQuery(this), false);
1074    });
1075
1076    /**
1077     * Handle file upload error
1078     */
1079    function handle_file_upload_error(data, field_container)
1080    {
1081        // Select correct status element
1082        var status_element = field_container.find('.wccf_file_upload_status_' + data.wccf_upload_progress_id);
1083
1084        // Handle abort
1085        if (typeof data === 'object' && typeof data.errorThrown !== 'undefined' && data.errorThrown === 'abort') {
1086            status_element.remove();
1087        }
1088
1089        // Get error message
1090        if (typeof data === 'object' && typeof data.result === 'object' && typeof data.result.error_message !== 'undefined') {
1091            var error_message = data.result.error_message;
1092        }
1093        else if (typeof data === 'object' && typeof data.files === 'object' && typeof data.files[0] === 'object' && typeof data.files[0].name !== 'undefined') {
1094            var error_message = wccf_general_config.messages.file_upload_error + ' ' + data.files[0].name;
1095        }
1096        else {
1097            var error_message = wccf_general_config.messages.file_upload_error;
1098        }
1099
1100        // Remove progess message
1101        status_element.find('.wccf_file_upload_message').remove();
1102
1103        // Add error message
1104        status_element.prepend('<small class="wccf_file_upload_error">' + error_message + '</small>');
1105
1106        // Remove upload status div with animation
1107        status_element.find('.wccf_file_upload_progress_wrapper').css('background-color', '#e44b23');
1108        status_element.find('.wccf_file_upload_progress').animate({'width': 0}, 500, function() {
1109            status_element.delay(2500).animate({'opacity': 0}).animate({'width': 0, 'height': 0}, 500, function() {
1110                jQuery(this).remove();
1111            });
1112        });
1113    }
1114
1115    /**
1116     * Toggle file upload list border
1117     * Displayed when multiple items are present
1118     */
1119    function toggle_file_upload_list_border(field_container)
1120    {
1121        var list = field_container.find('.wccf_file_upload_list');
1122
1123        if (list.find('.wccf_file_upload_item').length > 1) {
1124            if (!list.hasClass('wccf_file_upload_left_border')) {
1125                list.addClass('wccf_file_upload_left_border');
1126            }
1127        }
1128        else {
1129            list.removeClass('wccf_file_upload_left_border');
1130        }
1131    }
1132
1133    /**
1134     * Set up file removal
1135     */
1136    function file_removal_setup(file_upload_item, field_container, preserve_existing_files_on_server)
1137    {
1138
1139        file_upload_item.find('.wccf_file_upload_delete').click(function() {
1140
1141            // Reference hidden field
1142            var hidden_field = file_upload_item.closest('.wccf_file_upload_item').find('input[data-wccf-file-access-key]');
1143
1144            if (hidden_field) {
1145
1146                // Get access key and field id
1147                var access_key = hidden_field.val();
1148                var field_id = hidden_field.closest('.wccf_file_upload_list').parent().find('input[data-wccf-field-id]').data('wccf-field-id');
1149
1150                // Check if file can be removed from server
1151                if (!preserve_existing_files_on_server) {
1152
1153                    // Send request to remove from server
1154                    jQuery.post(
1155                        wccf_general_config.ajaxurl,
1156                        {
1157                            action:     'wccf_remove_file',
1158                            access_key: access_key,
1159                            field_id:   field_id
1160                        }
1161                    );
1162                }
1163
1164                // Remove from file sizes list
1165                var current_file_sizes = field_container.data('wccf_uploaded_file_sizes');
1166
1167                if (typeof current_file_sizes[access_key] !== 'undefined') {
1168                    delete current_file_sizes[access_key];
1169                    field_container.data('wccf_uploaded_file_sizes', current_file_sizes);
1170                }
1171            }
1172
1173            // Remove from page
1174            file_upload_item.closest('.wccf_file_upload_item').animate({'opacity': 0}, 500, function() {
1175
1176                // Trigger live product update
1177                file_upload_item.closest('form').trigger('rightpress_live_product_update_trigger');
1178
1179                // Remove file
1180                file_upload_item.remove();
1181
1182                // Maybe remove left border class
1183                toggle_file_upload_list_border(field_container);
1184
1185                // Toggle required property for file field
1186                toggle_file_required(field_container.find('input[data-wccf-field-id]').first());
1187
1188                // Recheck frontend conditions
1189                wccf_check_frontend_conditions(field_container.closest('form'));
1190
1191                // Refresh checkout view if this is a checkout field with pricing
1192                refresh_checkout_on_checkout_file_field_change(field_container.find('input.wccf_file'));
1193            });
1194        });
1195    }
1196
1197    /**
1198     * Set up existing file removal
1199     */
1200    jQuery('.wccf_meta_box_field_container, .wccf_user_profile_field_container, .wccf_field_container_user_field, .wccf_field_container_file').each(function() {
1201
1202        var field_container = jQuery(this);
1203
1204        field_container.find('.wccf_file_upload_list .wccf_file_upload_item').each(function() {
1205            file_removal_setup(jQuery(this), field_container, false);
1206        });
1207    });
1208
1209    /**
1210     * Refresh checkout view if this is a checkout field with pricing
1211     */
1212    function refresh_checkout_on_checkout_file_field_change(file_upload_field)
1213    {
1214        if (file_upload_field.hasClass('wccf_checkout_field') && file_upload_field.data('wccf-checkout-pricing')) {
1215            jQuery('body').trigger('update_checkout');
1216        }
1217    }
1218    /**
1219     * Checkout field view update
1220     */
1221    jQuery(document.body).on('updated_checkout', function() {
1222
1223        // Take reference of the form
1224        var form = jQuery('form.checkout');
1225
1226        // Get list of field ids displayed in page
1227        var field_ids = [];
1228
1229        jQuery('.wccf_checkout_field[data-wccf-field-id]').each(function() {
1230            field_ids.push(jQuery(this).data('wccf-field-id'));
1231        });
1232
1233        // Send request
1234        jQuery.ajax({
1235            type:       'POST',
1236            url:        wccf_general_config.ajaxurl,
1237            data: {
1238                action:     'wccf_update_checkout_field_view',
1239                field_ids:  field_ids
1240            },
1241            dataType:   'json',
1242            dataFilter: jQuery.rightpress.sanitize_json_response,
1243            success: function (response) {
1244
1245                // Check if request was successful
1246                if (typeof response === 'object' && response !== null && typeof response.result !== 'undefined' && response.result === 'success') {
1247
1248                    // Check if there are fields to remove
1249                    if (typeof response.remove === 'object') {
1250
1251                        // Iterate over fields to remove
1252                        for (var field_id in response.remove) {
1253                            if (response.remove.hasOwnProperty(field_id)) {
1254
1255                                // Remove field
1256                                jQuery('.wccf_checkout_field[data-wccf-field-id="' + field_id + '"]').closest('.wccf_field_container').fadeOut(function() {
1257                                    remove_checkout_field(jQuery(this));
1258                                });
1259                            }
1260                        }
1261                    }
1262
1263                    // Check if there are fields to add
1264                    if (typeof response.add === 'object') {
1265
1266                        // Iterate over positions
1267                        for (var position in response.add) {
1268                            if (response.add.hasOwnProperty(position)) {
1269
1270                                // Get position properties
1271                                var position_method     = response.add[position]['position']['method'];
1272                                var position_selector   = response.add[position]['position']['selector'];
1273
1274                                // Get fields to add to current position
1275                                var fields = response.add[position]['fields'];
1276
1277                                // Maybe reverse order of fields
1278                                if (position_method === 'after' || position_method === 'append') {
1279                                    fields.reverse();
1280                                }
1281
1282                                // Iterate over fields of current position
1283                                jQuery.each(fields, function(index, field) {
1284
1285                                    // Insert field html into DOM
1286                                    jQuery(position_selector)[position_method](field.html);
1287
1288                                    // Reference new field
1289                                    var new_field = jQuery('.wccf_checkout_field[data-wccf-field-id="' + field.id + '"]').closest('.wccf_field_container');
1290
1291                                    // Animate field
1292                                    new_field.hide().fadeIn();
1293
1294                                    // Set up field functionality after dynamic field view update
1295                                    set_up_field_functionality_after_view_update(new_field, form, false);
1296                                });
1297                            }
1298                        }
1299
1300                        // Recheck frontend conditions
1301                        wccf_check_frontend_conditions(form);
1302                    }
1303                }
1304            }
1305        });
1306    });
1307
1308    /**
1309     * Remove checkout field
1310     */
1311    function remove_checkout_field(field)
1312    {
1313
1314        // Remove color picker mouseup event listener from document
1315        if (field.find('.wccf_checkout_field_color').length) {
1316            unbind_color_picker_toggle(field.find('.wccf_checkout_field_color').last());
1317        }
1318
1319        // Remove field
1320        field.remove();
1321    }
1322
1323    /**
1324     * Trigger checkout update on payment method change
1325     */
1326    function set_up_payment_method_checkout_update()
1327    {
1328        jQuery('form.checkout input[name="payment_method"]').each(function() {
1329            if (typeof jQuery(this).data('wccf_payment_method_checkout_update') === 'undefined') {
1330                jQuery(this).change(function() {
1331                    jQuery('body').trigger('update_checkout');
1332                });
1333                jQuery(this).data('wccf_payment_method_checkout_update', true);
1334            }
1335        });
1336    }
1337    jQuery('body').on('updated_checkout', set_up_payment_method_checkout_update);
1338    set_up_payment_method_checkout_update();
1339
1340    /**
1341     * Backend field editing
1342     */
1343    jQuery('span.wccf_backend_editing_value[data-wccf-backend-editing="1"]').each(function() {
1344
1345        // Bind click event
1346        jQuery(this).click(function() {
1347
1348            var value_element = jQuery(this);
1349
1350            // Get field element via ajax
1351            jQuery.post(
1352                wccf_general_config.ajaxurl,
1353                {
1354                    action:         'wccf_get_backend_editing_field',
1355                    field_id:       value_element.data('wccf-field-id'),
1356                    item_id:        value_element.data('wccf-item-id'),
1357                    quantity_index: value_element.data('wccf-quantity-index')
1358                },
1359                function(response) {
1360
1361                    // Parse response
1362                    response = parse_ajax_json_response(response);
1363
1364                    // Check for errors
1365                    if (typeof response === 'object' && typeof response.result !== 'undefined' && response.result === 'success' && typeof response.field !== 'undefined') {
1366
1367                        // Display field and hide value
1368                        value_element.after(response.field).hide();
1369
1370                        // Date/time picker setup
1371                        jQuery.each(['date', 'time', 'datetime'], function(index, type) {
1372                            value_element.parent().find('.wccf_backend_editing_' + type).each(function() {
1373                                set_up_datetimepicker(jQuery(this), type);
1374                            });
1375                        });
1376
1377                        // Color picker setup
1378                        value_element.parent().find('.wccf_backend_editing_color').each(function() {
1379                            set_up_color_picker(jQuery(this));
1380                        });
1381                    }
1382                }
1383            );
1384        });
1385    });
1386
1387    /**
1388     * Enable form validation on user edit page
1389     */
1390    jQuery('form#your-profile, form#createuser, form#adduser').filter(':has(.wccf)').removeAttr('novalidate');
1391
1392    /**
1393     * Parse Ajax JSON response
1394     */
1395    function parse_ajax_json_response(response, return_raw_data)
1396    {
1397        // Check if we need to return parsed object or potentially fixed raw data
1398        var return_raw_data = (typeof return_raw_data !== 'undefined') ?  return_raw_data : false;
1399
1400        try {
1401
1402            // Attempt to parse data
1403            var parsed = jQuery.parseJSON(response);
1404
1405            // Return appropriate value
1406            return return_raw_data ? response : parsed;
1407        }
1408        catch (e) {
1409
1410            // Attempt to fix malformed JSON string
1411            var valid_response = response.match(/{"result.*"}/);
1412
1413            // Check if we were able to fix it
1414            if (valid_response !== null) {
1415                response = valid_response[0];
1416            }
1417        }
1418
1419        // Second attempt to parse response data
1420        return return_raw_data ? response : jQuery.parseJSON(response);
1421    }
1422
1423    /**
1424     * Fix HTML5 required validation for file uploads
1425     */
1426    function toggle_file_required(file_field)
1427    {
1428        // Get uploaded files
1429        var upload_list = file_field.closest('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').find('.wccf_file_upload_list').children();
1430
1431        // Remove required property
1432        if (upload_list.length > 0 && file_field.prop('required')) {
1433            file_field.prop('required', false);
1434            file_field.data('wccf-file-field-was-required', 1);
1435        }
1436        // Add required property back
1437        else if (upload_list.length == 0 && file_field.data('wccf-file-field-was-required')) {
1438            file_field.prop('required', true);
1439            file_field.data('wccf-file-field-was-required', 0);
1440        }
1441    }
1442    jQuery('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').find('.wccf_file[required]').each(function() {
1443        toggle_file_required(jQuery(this));
1444    });
1445
1446    /**
1447     * Fix HTML5 required validation for checkbox set
1448     */
1449    jQuery('.wccf_field_container, .wccf_meta_box_field_container, .wccf_user_profile_field_container').has('.wccf_checkbox').each(function() {
1450
1451        // Reference all checkboxes
1452        var checkboxes = jQuery(this).find('.wccf_checkbox');
1453
1454        // Remove required attribute from all checkboxes when at least one is checked
1455        if (checkboxes.length > 1 && checkboxes.prop('required')) {
1456
1457            function toggle_checkboxes_required()
1458            {
1459                // Get min selected value
1460                var min_selected = checkboxes.data('wccf-min-selected');
1461                min_selected = typeof min_selected !== 'undefined' ? min_selected : 1;
1462
1463                // Check if enough checkboxes were selected
1464                if (checkboxes.filter(':checked').length >= min_selected) {
1465                    checkboxes.prop('required', false);
1466                }
1467                else {
1468                    checkboxes.prop('required', true);
1469                }
1470            }
1471            checkboxes.change(function() {
1472                toggle_checkboxes_required();
1473            });
1474            toggle_checkboxes_required();
1475        }
1476    });
1477
1478    /**
1479     * Fix HTML5 min selected validation for multiselect fields
1480     */
1481    jQuery('.wccf_multiselect[data-wccf-min-selected]').each(function() {
1482
1483        // Reference field
1484        var multiselect = jQuery(this);
1485
1486        // Get min selected value
1487        var min_selected = multiselect.data('wccf-min-selected');
1488
1489        // Handle form submit
1490        multiselect.closest('form').submit(function(e) {
1491
1492            // More options need to be selected
1493            if (multiselect.find('option:selected').length < min_selected) {
1494                jQuery(this).find('option:selected').prop('selected', false);
1495                e.preventDefault();
1496            }
1497        });
1498    });
1499
1500    /**
1501     * Set up color picker
1502     */
1503    function set_up_color_picker(input)
1504    {
1505
1506        // Get current input value
1507        var value = input.val();
1508
1509        // Get color to show
1510        var color = (typeof value !== 'undefined' && value !== '') ? value : '#000000';
1511
1512        // Set color on input element
1513        input.css('border-left', ('29px solid ' + color));
1514
1515        // Change callback
1516        wccf_color_picker_config.change = function(event, ui) {
1517
1518            // Update value
1519            input.val(ui.color.toString());
1520
1521            // Update color
1522            input.css('border-left', ('29px solid ' + ui.color.toString()));
1523
1524            // Trigger change event
1525            input.change();
1526        };
1527
1528        // Initialize Iris color picker
1529        input.iris(wccf_color_picker_config);
1530
1531        // Fix position
1532        input.closest('.wccf_field_container').find('.iris-picker').css('position', 'absolute').css('z-index', '999');
1533
1534        // Bind color picker toggle event
1535        bind_color_picker_toggle(input);
1536    }
1537
1538    /**
1539     * Bind color picker toggle event
1540     */
1541    function bind_color_picker_toggle(input)
1542    {
1543
1544        // Format dynamic function name
1545        var function_name = 'toggle_color_picker_' + input.prop('id');
1546
1547        // Make sure function does not exist yet
1548        if (typeof checkout_color_picker_handlers[function_name] === 'undefined') {
1549
1550            // Add function with dynamic reference
1551            checkout_color_picker_handlers[function_name] = function(event) {
1552                toggle_color_picker(event);
1553            };
1554
1555            // Bind mouseup event
1556            jQuery(document).on('mouseup', '*', {input: input}, checkout_color_picker_handlers[function_name]);
1557        }
1558    }
1559
1560    /**
1561     * Unbind color picker toggle event
1562     */
1563    function unbind_color_picker_toggle(input)
1564    {
1565
1566        // Format dynamic function name
1567        var function_name = 'toggle_color_picker_' + input.prop('id');
1568
1569        // Check if function is defined
1570        if (typeof checkout_color_picker_handlers[function_name] !== 'undefined') {
1571
1572            // Unbind mouseup event
1573            jQuery(document).off('mouseup', '*', checkout_color_picker_handlers[function_name]);
1574
1575            // Remove function
1576            delete checkout_color_picker_handlers[function_name];
1577        }
1578    }
1579
1580    /**
1581     * Color picker state toggling
1582     */
1583    function toggle_color_picker(event)
1584    {
1585
1586        var container = jQuery(event.data.input).closest('.wccf_field_container');
1587
1588        // Clicked on color input
1589        if (jQuery(event.target).is(event.data.input)) {
1590            event.data.input.iris('show');
1591        }
1592        // Clicked outside input and container
1593        else if (!container.is(event.target) && !container.has(event.target).length) {
1594            event.data.input.iris('hide');
1595        }
1596    }
1597
1598    /**
1599     * Set up datetime picker
1600     */
1601    function set_up_datetimepicker(element, type)
1602    {
1603
1604        // Select config
1605        if (type === 'date') {
1606            var config = wccf_datetimepicker_date_config.x;
1607        }
1608        else if (type === 'time') {
1609            var config = wccf_datetimepicker_time_config.x;
1610        }
1611        else {
1612            var config = wccf_datetimepicker_datetime_config.x;
1613        }
1614
1615        // Set z-index
1616        if (typeof config.style !== 'undefined') {
1617            config.style += ' z-index: 999999999;';
1618        }
1619        else {
1620            config.style = 'z-index: 999999999';
1621        }
1622
1623        // Initialize datepicker
1624        element.datetimepicker(config);
1625
1626        // Set locale
1627        jQuery.datetimepicker.setLocale(wccf_datetimepicker_locale.x);
1628    }
1629
1630    /**
1631     * Checkout validation
1632     */
1633    jQuery('form.checkout').submit(function() {
1634        unset_field_invalid_flags(jQuery(document.body));
1635    });
1636
1637    if (jQuery('form.checkout').length) {
1638        jQuery(document.body).on('checkout_error', function() {
1639            set_field_invalid_flags(jQuery(document.body));
1640        });
1641    }
1642
1643    /**
1644     * Set field invalid flags
1645     *
1646     * Note: This is used for both checkout field validation and cart item product field editing validation
1647     */
1648    function set_field_invalid_flags(parent)
1649    {
1650
1651        // Find active error messages
1652        parent.find('[data-wccf-error-field-id]').each(function() {
1653
1654            // Get field id
1655            var field_id = jQuery(this).data('wccf-error-field-id');
1656
1657            // Select field by id
1658            parent.find('.wccf[data-wccf-field-id="' + field_id + '"]').each(function() {
1659
1660                // Reference input and container
1661                var input       = jQuery(this);
1662                var container   = input.closest('.wccf_field_container');
1663
1664                // Add invalid class to container
1665                container.addClass('wccf_field_invalid');
1666
1667                // Remove invalid class on input focus or change
1668                input.on('focus change keyup', function() {
1669                    container.removeClass('wccf_field_invalid');
1670                });
1671            });
1672        });
1673    }
1674
1675    /**
1676     * Unset field invalid flags
1677     *
1678     * Note: This is used for both checkout field validation and cart item product field editing validation
1679     */
1680    function unset_field_invalid_flags(parent)
1681    {
1682        parent.find('.wccf_field_invalid').removeClass('wccf_field_invalid');
1683    }
1684
1685    /**
1686     * Simple product field frontend validation
1687     */
1688    jQuery('#wccf_product_field_master_container').closest('form').submit(function(e) {
1689
1690        var focused = false;
1691
1692        // Clear all error states
1693        jQuery(this).find('.wccf_field_invalid').removeClass('wccf_field_invalid');
1694
1695        // Select all required visible non-disabled product field inputs
1696        jQuery(this).find('.wccf_product_field[data-wccf-required-field]:not(:disabled):visible').each(function() {
1697
1698            // Reference input and container
1699            var input       = jQuery(this);
1700            var container   = input.closest('.wccf_field_container');
1701
1702            // Check if field has value
1703            if (input.prop('type') === 'file') {
1704                var has_value = !!container.find('.wccf_file_upload_item').length;
1705            }
1706            else if (input.prop('type') === 'checkbox' || input.prop('type') === 'radio') {
1707                var has_value = !!container.find('.wccf_product_field:checked').length;
1708            }
1709            else {
1710                var has_value = !!jQuery(this).val();
1711            }
1712
1713            // Check if field is empty
1714            if (!has_value) {
1715
1716                // Focus on first input with error
1717                if (!focused) {
1718                    input.focus();
1719                    focused = true;
1720                }
1721
1722                // Add invalid class
1723                container.addClass('wccf_field_invalid');
1724
1725                // Print error message
1726                input.after('<div class="wccf_required_field_error">' + wccf_general_config.messages.required_field + '</div>');
1727
1728                // Clean up on focus or value change
1729                input.on('focus change keyup', function() {
1730
1731                    // Remove invalid class
1732                    container.removeClass('wccf_field_invalid');
1733
1734                    // Remove error message
1735                    container.find('.wccf_required_field_error').remove();
1736                });
1737
1738                // Do not submit form
1739                e.preventDefault();
1740            }
1741        });
1742    });
1743
1744    /**
1745     * Cart item product field editing
1746     */
1747    if (jQuery('#wccf_cart_item_product_field_editing_modal').length) {
1748
1749        // Reference modal
1750        var modal   = jQuery('#wccf_cart_item_product_field_editing_modal');
1751        var wrapper = modal.find('.wccf_modal_wrapper');
1752        var content = modal.find('.wccf_modal_content');
1753        var close   = modal.find('.wccf_modal_close');
1754        var form    = null;
1755
1756        // Define preloader
1757        var preloader = '<div class="wccf_modal_preloader"></div>';
1758
1759        // Open modal on click
1760        jQuery('form.woocommerce-cart-form .wccf_cart_item_product_field_editing').click(function(e) {
1761
1762            // Get field id and cart item key
1763            var field_id        = jQuery(this).data('wccf-cart-item-edit-field-id');
1764            var cart_item_key   = jQuery(this).data('wccf-cart-item-key');
1765
1766            // Open modal
1767            cart_item_product_field_editing_open(field_id, cart_item_key);
1768
1769            // Prevent file download
1770            e.preventDefault();
1771        });
1772
1773        // Close on close target click
1774        close.on('click', cart_item_product_field_editing_close);
1775
1776        function cart_item_product_field_editing_open(field_id, cart_item_key)
1777        {
1778
1779            // Make field ids array
1780            // Note: currently we support single field editing but this can be changed to support multiple fields
1781            var field_ids = [field_id];
1782
1783            // Add preloader
1784            content.html(preloader);
1785
1786            // Load fields
1787            load_fields(field_ids, cart_item_key);
1788
1789            // Fade in modal
1790            modal.css('opacity', 0).css('display', 'table').animate({opacity: 1}, function() {
1791
1792                // Close on click outside of modal
1793                jQuery(document).on('mousedown', cart_item_product_field_editing_close_callback);
1794            });
1795        }
1796
1797        function cart_item_product_field_editing_close_callback(event)
1798        {
1799
1800            if (!wrapper.is(event.target) && !wrapper.has(event.target).length) {
1801                cart_item_product_field_editing_close();
1802            }
1803        }
1804
1805        function cart_item_product_field_editing_close()
1806        {
1807
1808            // Fade out modal
1809            modal.animate({opacity: 0}, function() {
1810
1811                // Hide modal
1812                modal.css('display', 'none');
1813
1814                // Clear content
1815                content.html('');
1816
1817                // Unset form
1818                form = null;
1819
1820                // Remove click outside of modal callback
1821                jQuery(document).off('mousedown', cart_item_product_field_editing_close_callback);
1822            });
1823        }
1824
1825        function load_fields(field_ids, cart_item_key)
1826        {
1827
1828            // Send request
1829            jQuery.ajax({
1830                type:       'POST',
1831                url:        wccf_general_config.ajaxurl,
1832                data: {
1833                    action:         'wccf_get_cart_item_product_field_editing_view',
1834                    field_ids:      field_ids,
1835                    cart_item_key:  cart_item_key,
1836                },
1837                dataType:   'json',
1838                dataFilter: jQuery.rightpress.sanitize_json_response,
1839                success: function (response) {
1840
1841                    // Check if request was successful
1842                    if (typeof response === 'object' && response !== null && typeof response.result !== 'undefined' && response.result === 'success') {
1843
1844                        // Set fields html
1845                        content.html(response.html);
1846
1847                        // Set form
1848                        form = content.find('form#wccf_cart_item_product_field_editing_form');
1849
1850                        // Set up advanced field functionality on all inputs
1851                        form.find('.wccf_field_container').each(function() {
1852                            set_up_field_functionality_after_view_update(jQuery(this), form, true);
1853                        });
1854
1855                        // Reference first input
1856                        var first_input = form.find('.wccf_product_field').first();
1857
1858                        // Focus on first element and move typing cursor to the back for some file types
1859                        if ((first_input.is('input') && ['text', 'password', 'email', 'number'].indexOf(first_input.prop('type')) !== -1) || first_input.is('textarea')) {
1860                            var field_value = first_input.val();
1861                            first_input.focus().val('').val(field_value);
1862                        }
1863
1864                        // Handle submit event
1865                        form.submit(function(e) {
1866
1867                            // Handle submit
1868                            update_cart_item_field_values();
1869
1870                            // Do not navigate away from page
1871                            e.preventDefault();
1872                        });
1873                    }
1874                    // Unable to get fields view
1875                    else {
1876
1877                        handle_load_fields_error();
1878                    }
1879                },
1880                error: function () {
1881
1882                    handle_load_fields_error();
1883                }
1884            });
1885
1886            function update_cart_item_field_values()
1887            {
1888
1889                // Unset current error messages
1890                form.find('.wccf_modal_validation_error').fadeOut(function() { jQuery(this).remove(); });
1891                unset_field_invalid_flags(form);
1892
1893                // Prepare data
1894                var form_data = form.serialize();
1895
1896                // Send request
1897                jQuery.ajax({
1898                    type:       'POST',
1899                    url:        wccf_general_config.ajaxurl,
1900                    data: {
1901                        action: 'wccf_update_cart_item_product_field_values',
1902                        data:   form_data,
1903                    },
1904                    dataType:   'json',
1905                    dataFilter: jQuery.rightpress.sanitize_json_response,
1906                    success: function (response) {
1907
1908                        // Request processed successfully
1909                        if (typeof response === 'object' && response !== null && typeof response.result !== 'undefined' && response.result === 'success') {
1910
1911                            // Show success message
1912                            content.html('<div class="wccf_modal_success">' + wccf_general_config.messages.field_values_updated + '<br>' + wccf_general_config.messages.page_reload + '</div>');
1913
1914                            // Reload page after a pause
1915                            setTimeout(function(){
1916                                window.location.reload(true);
1917                            }, 1500);
1918                        }
1919                        // Field validation error
1920                        else if (typeof response === 'object' && response !== null && typeof response.result !== 'undefined' && response.result === 'error' && typeof response.validation_error_messages === 'object' && response.validation_error_messages !== null && response.validation_error_messages.length) {
1921
1922                            // Handle validation errors
1923                            handle_validation_errors(response.validation_error_messages);
1924                        }
1925                        // Unknown error occurred while processing request
1926                        else {
1927
1928                            handle_update_cart_item_field_values_error();
1929                        }
1930                    },
1931                    error: function () {
1932
1933                        handle_update_cart_item_field_values_error();
1934                    }
1935                });
1936            }
1937
1938            function handle_load_fields_error()
1939            {
1940
1941                // Display error message
1942                content.html('<div class="wccf_modal_error">' + wccf_general_config.messages.error_loading_field_view + '<br>' + wccf_general_config.messages.try_again + '</div>');
1943
1944                // Close modal after a pause
1945                setTimeout(function(){
1946                    cart_item_product_field_editing_close();
1947                }, 2500);
1948            }
1949
1950            function handle_update_cart_item_field_values_error()
1951            {
1952
1953                // Display error message
1954                content.html('<div class="wccf_modal_error">' + wccf_general_config.messages.error_updating_field_values + '<br>' + wccf_general_config.messages.try_again + '</div>');
1955
1956                // Close modal after a pause
1957                setTimeout(function(){
1958                    cart_item_product_field_editing_close();
1959                }, 2500);
1960            }
1961
1962            function handle_validation_errors(messages)
1963            {
1964
1965                // Iterate over validation error messages
1966                for (var message_index in messages) {
1967                    if (messages.hasOwnProperty(message_index)) {
1968
1969                        // Add validation error
1970                        add_validation_error(messages[message_index]);
1971                    }
1972                }
1973
1974                // Show errors
1975                form.find('.wccf_modal_validation_error').fadeIn();
1976
1977                // Set invalid flags
1978                set_field_invalid_flags(form);
1979            }
1980
1981            function add_validation_error(message)
1982            {
1983
1984                form.prepend('<div class="wccf_modal_validation_error">' + message + '</div>');
1985            }
1986        }
1987    }
1988
1989
1990
1991
1992
1993
1994
1995});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.