PluginProbe
Repeater Fields for Gravity Forms / 3.2.1
Repeater Fields for Gravity Forms v3.2.1
3.2.2 3.2.1 3.2.0 3.1.1 3.1.0 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 2.5.1 2.5.0 2.4.6 trunk 2.0.5 2.0.9 2.1.0 2.3.2 2.3.7 2.4.1 2.4.3 2.4.4 2.4.5
← All changes | libs/wp_repeater.js +182 -36 2.5.0 → 3.2.1 View file →
@@ -171,8 +171,17 @@
171 171 repeater.val(val_repeater + ", " + link);
172 172 }
173 173 return html.replace("onclick", "data-onclick");;
174 174 });
175 + if (window.gform && typeof window.gform.addFilter === 'function') {
176 + window.gform.addFilter('gform_datepicker_options_pre_init', function (options, formId, fieldId) {
177 + if (typeof fieldId === 'string' && fieldId.indexOf('-') > -1) {
178 + var originalFieldId = fieldId.split('-')[0];
179 + return window.gform.applyFilters('gform_datepicker_options_pre_init', options, formId, originalFieldId);
180 + }
181 + return options;
182 + }, 9);
183 + }
175 184 var repeater_fields_htmls = {};
176 185 //condition out repeater out
177 186 gform.addAction('gform_post_conditional_logic_field_action1', function (formId, action, targetId, defaultValues, isInit) {
178 187 if (targetId != "") {
@@ -285,9 +294,9 @@
285 294 }
286 295 }
287 296 }
288 297 var input_ids = [];
289 - function change_name_and_ids(item, field_end = null, key = null) {
298 + function change_name_and_ids(item, field_end = null, key = null, row_data = null) {
290 299 if (key == null) {
291 300 var id_rand = Math.floor(Math.random() * 10000);
292 301 } else {
293 302 var id_rand = key;
@@ -298,8 +307,29 @@
298 307 datas.id = datas_ids;
299 308 field_end.find(".gf-field-repeater-data").val(JSON.stringify(datas));
300 309 item = $(item);
301 310 item.attr("data-id", id_rand);
311 +
312 + function get_saved_field_val(fieldName) {
313 + if (!row_data || typeof row_data !== 'object') {
314 + return null;
315 + }
316 + var clean = fieldName.replace(/\[\]$/, '');
317 + if (row_data[clean] !== undefined) {
318 + return row_data[clean];
319 + }
320 + var withUnderscore = clean.replace(/\./g, '_');
321 + if (row_data[withUnderscore] !== undefined) {
322 + return row_data[withUnderscore];
323 + }
324 + for (var rk in row_data) {
325 + if (rk === clean || rk === withUnderscore || rk.replace(/\./g, '_') === withUnderscore) {
326 + return row_data[rk];
327 + }
328 + }
329 + return null;
330 + }
331 +
302 332 $(".gfield_visibility_visible", item).each(function () {
303 333 var id = $(this).attr("id");
304 334 $(this).attr("id", id + "-" + id_rand);
305 335 $(this).attr("data-js-reload", id + "-" + id_rand);
@@ -335,8 +365,9 @@
335 365 }
336 366 var type = $(this).attr("type");
337 367 var id = $(this).attr("id");
338 368 input_ids.push(id);
369 + var original_name = name;
339 370 if (name != "" && name.endsWith('[]')) {
340 371 name = name.replace(/\[\]$/, '');
341 372 $(this).attr("name", name + "__" + id_rand + "[]");
342 373 } else {
@@ -342,31 +373,89 @@
342 373 } else {
343 374 $(this).attr("name", name + "__" + id_rand);
344 375 }
345 376 $(this).attr("id", id + "-" + id_rand);
377 + if ($(this).hasClass('gform-datepicker') || $(this).hasClass('datepicker')) {
378 + $(this).removeAttr('data-initialized').removeData('initialized').removeClass('hasDatepicker');
379 + if (this.dataset) {
380 + delete this.dataset.initialized;
381 + delete this.dataset.toggleClickedBeforeInit;
382 + }
383 + var $toggleBtn = $(this).siblings('.gform-datepicker-toggle');
384 + if (!$toggleBtn.length) {
385 + $toggleBtn = $(this).closest('.ginput_container_date').find('.gform-datepicker-toggle');
386 + }
387 + if ($toggleBtn.length) {
388 + $toggleBtn.attr('id', 'datepicker_toggle_' + id + '-' + id_rand);
389 + $toggleBtn.attr('aria-controls', id + '-' + id_rand);
390 + }
391 + var $kbd = $(this).siblings('#keyboardHint_' + id);
392 + if (!$kbd.length) {
393 + $kbd = $(this).closest('.ginput_container_date').find('#keyboardHint_' + id);
394 + }
395 + if ($kbd.length) {
396 + $kbd.attr('id', 'keyboardHint_' + id + '-' + id_rand);
397 + }
398 + }
346 399 var value_check = localStorage.getItem(id + "-" + id_rand);
400 + var saved_val = get_saved_field_val(name + "__" + id_rand);
401 + if (saved_val === null) {
402 + saved_val = get_saved_field_val(original_name + "__" + id_rand);
403 + }
347 404 if (type == "checkbox") {
348 405 $(this).closest("div").find("label").attr("for", id + "-" + id_rand);
349 - if (value_check != null) {
350 - $(this).attr("checked", "checked");
406 + if (saved_val !== null) {
407 + var is_checked = false;
408 + if (Array.isArray(saved_val)) {
409 + is_checked = saved_val.indexOf($(this).val()) !== -1;
410 + } else {
411 + is_checked = ($(this).val() == saved_val);
412 + }
413 + if (is_checked) {
414 + $(this).prop("checked", true).attr("checked", "checked");
415 + } else {
416 + $(this).prop("checked", false).removeAttr("checked");
417 + }
418 + } else if (value_check != null) {
419 + $(this).prop("checked", true).attr("checked", "checked");
351 420 }
352 421 } else if (type == "file") {
353 422 $(this).attr("id", id + "-" + id_rand);
423 + if (saved_val && typeof saved_val === 'string' && saved_val.indexOf('http') === 0) {
424 + var $fileContainer = $(this).closest('.ginput_container_fileupload');
425 + if ($fileContainer.length && !$fileContainer.find('.gf-repeater-saved-file').length) {
426 + var fileName = saved_val.split('/').pop();
427 + $fileContainer.append('<div class="gf-repeater-saved-file" style="margin-top:5px;font-size:13px;">' +
428 + '<a href="' + encodeURI(saved_val) + '" target="_blank" rel="noopener">' + (fileName || saved_val) + '</a>' +
429 + '<input type="hidden" name="' + name + '__' + id_rand + '" value="' + saved_val + '" />' +
430 + '</div>');
431 + }
432 + }
354 433 } else if (type == "radio") {
355 434 $(this).attr("id", id + "-" + id_rand);
356 435 $(this).closest("div, li").find("label").attr("for", id + "-" + id_rand);
357 - var value_check = localStorage.getItem(name + "__" + id_rand);
358 - if (value_check != null) {
359 - var old_check = $(this).val();
360 - if (old_check == value_check) {
361 - $(this).attr("checked", true);
436 + if (saved_val !== null) {
437 + if ($(this).val() == saved_val) {
438 + $(this).prop("checked", true).attr("checked", "checked");
439 + } else {
440 + $(this).prop("checked", false).removeAttr("checked");
362 441 }
442 + } else {
443 + var value_check = localStorage.getItem(name + "__" + id_rand);
444 + if (value_check != null) {
445 + var old_check = $(this).val();
446 + if (old_check == value_check) {
447 + $(this).prop("checked", true).attr("checked", "checked");
448 + }
449 + }
363 450 }
364 451 }
365 452 else {
366 453 // ensure the field label's "for" attribute corresponds with the field's ID
367 454 $(this).closest("div").parent().find("label").attr("for", id + "-" + id_rand);
368 - if (value_check != null) {
455 + if (saved_val !== null) {
456 + $(this).val(saved_val);
457 + } else if (value_check != null) {
369 458 $(this).val(value_check);
370 459 }
371 460 }
372 461 })
@@ -376,11 +465,16 @@
376 465 $(this).attr("name", name + "__" + id_rand);
377 466 $(this).attr("id", id + "-" + id_rand);
378 467 // ensure the field label's "for" attribute corresponds with the field's ID
379 468 $(this).closest("div").parent().find("label").attr("for", id + "-" + id_rand);
380 - var value_check = localStorage.getItem(id + "-" + id_rand);
381 - if (value_check != null) {
382 - $(this).val(value_check);
469 + var saved_val = get_saved_field_val(name + "__" + id_rand);
470 + if (saved_val !== null) {
471 + $(this).val(saved_val);
472 + } else {
473 + var value_check = localStorage.getItem(id + "-" + id_rand);
474 + if (value_check != null) {
475 + $(this).val(value_check);
476 + }
383 477 }
384 478 })
385 479 $("select", item).each(function () {
386 480 var name = $(this).attr("name");
@@ -393,16 +487,21 @@
393 487 }
394 488 $(this).attr("id", id + "-" + id_rand);
395 489 // ensure the field label's "for" attribute corresponds with the field's ID
396 490 $(this).closest("div").parent().find("label").attr("for", id + "-" + id_rand);
397 - var value_check = localStorage.getItem(id + "-" + id_rand);
398 - if (value_check != null) {
399 - $(this).val(value_check);
491 + var saved_val = get_saved_field_val(name + "__" + id_rand);
492 + if (saved_val !== null) {
493 + $(this).val(saved_val);
494 + } else {
495 + var value_check = localStorage.getItem(id + "-" + id_rand);
496 + if (value_check != null) {
497 + $(this).val(value_check);
498 + }
400 499 }
401 500 })
402 501 return item;
403 502 }
404 - function add_repeater_data(button, key = null) {
503 + function add_repeater_data(button, key = null, row_data = null) {
405 504 var start_field;
406 505 if (key == null) {
407 506 var key = Math.floor(Math.random() * 10000);
408 507 }
@@ -413,13 +512,14 @@
413 512 start_field = $(this);
414 513 return false;
415 514 }
416 515 })
417 - var html_field = get_repeater_data(button, key);
516 + var html_field = get_repeater_data(button, key, row_data);
418 517 var header = get_repeater_data_header(start_field);
419 518 item.find(".repeater-field-header").append(header);
420 519 item.find(".repeater-field-content").append(html_field);
421 520 button.find(".repeater-field-warp-item").append(item);
521 + item.find("input[type=radio][checked], input[type=checkbox][checked]").prop("checked", true);
422 522 update_repeater_count_header();
423 523 $("input").trigger("done_load_repeater");
424 524 var form_ids = button.attr("id").split("_");
425 525 if (yeeaddons_gf_repeater_data.pro == "ok") {
@@ -487,12 +587,45 @@
487 587 conditional_logic_custom(key);
488 588 input_ids = [];
489 589 $('.gform-datepicker').each(function () {
490 590 var $element = $(this);
491 - initSingleDatepicker($element);
492 - $element.addClass('initialized');
591 + if (typeof $.fn.datepicker === 'function') {
592 + // Legacy GF < 3.0: jQuery UI datepicker available
593 + if (!$element.hasClass('hasDatepicker')) {
594 + initSingleDatepicker($element);
595 + $element.addClass('initialized');
596 + }
597 + }
493 598 });
599 + // GF 3.0+: Re-trigger datepicker initialization for the form
600 + // so GF's theme script loads the datepicker chunk and sets up delegation
601 + reinitGF3Datepicker(button);
494 602 }
603 + /**
604 + * Re-trigger GF 3.0 datepicker initialization.
605 + * GF 3.0 uses lazy-loading: the datepicker chunk is only loaded when
606 + * gform/post_render fires and .gform-datepicker elements exist in the DOM.
607 + * Since the repeater removes original fields before GF checks for them,
608 + * we need to re-dispatch the event after creating new rows.
609 + */
610 + var _gf3DatepickerTriggered = {};
611 + function reinitGF3Datepicker(el) {
612 + if (typeof $.fn.datepicker === 'function') {
613 + return; // Legacy jQuery UI datepicker is available, no need for GF 3.0 re-init
614 + }
615 + var $wrapper = el.closest('.gform_wrapper');
616 + if (!$wrapper.length) return;
617 + var formId = parseInt($wrapper.attr('id').replace('gform_wrapper_', ''));
618 + if (!formId || isNaN(formId)) return;
619 + // Only trigger once per form per page load to avoid duplicate event listeners
620 + if (_gf3DatepickerTriggered[formId]) return;
621 + _gf3DatepickerTriggered[formId] = true;
622 + // Dispatch gform/post_render so GF's theme script detects datepicker fields
623 + // and lazy-loads the accessible datepicker chunk
624 + document.dispatchEvent(new CustomEvent('gform/post_render', {
625 + detail: { formId: formId, currentPage: 1 }
626 + }));
627 + }
495 628 function yeeaddons_change_id_logic(value, key) {
496 629 var field_rules_inner = [];
497 630 if (value && value.field && Array.isArray(value.field.rules)) { // added this line to fix js issue in add more to autofill multiple data
498 631 $.each(value.field.rules, function (key_2, value_2) {
@@ -523,14 +656,14 @@
523 656 if (datas.length > 1) {
524 657 conditional_logic_custom(datas[1]);
525 658 }
526 659 }, 10);
527 - function get_repeater_data(step_field, key = null) {
660 + function get_repeater_data(step_field, key = null, row_data = null) {
528 661 var data_html = step_field.find(".gf-field-repeater-data-html").val();
529 662 if (data_html == "") {
530 663 data_html = step_field.find(".gf-field-repeater-data-html").attr('value');
531 664 }
532 - var html_step = change_name_and_ids(data_html, step_field, key);
665 + var html_step = change_name_and_ids(data_html, step_field, key, row_data);
533 666 return html_step;
534 667 }
535 668 function get_repeater_data_name() {
536 669 var i = 1;
@@ -609,16 +742,27 @@
609 742 if (initial_rows === undefined || initial_rows === "") {
610 743 initial_rows = 1;
611 744 }
612 745 if (value != "") {
613 - value = JSON.parse(value);
614 - initial_rows = value.count;
615 - var data_arr_ids = value.id;
616 - setTimeout(function () {
617 - for (var j = 0; j < initial_rows; j++) {
618 - add_repeater_data(step_field.closest(".gfield--type-repeater_end"), data_arr_ids[j]);
619 - }
620 - }, 100);
746 + try {
747 + var parsed_val = typeof value === "string" ? JSON.parse(value) : value;
748 + initial_rows = parsed_val.count !== undefined ? parsed_val.count : (parsed_val.id ? parsed_val.id.length : 1);
749 + var data_arr_ids = parsed_val.id || [];
750 + var saved_values = parsed_val.saved_values || null;
751 + setTimeout(function () {
752 + for (var j = 0; j < initial_rows; j++) {
753 + var row_id = data_arr_ids[j] !== undefined ? data_arr_ids[j] : null;
754 + var row_data = (saved_values && row_id && saved_values[row_id]) ? saved_values[row_id] : null;
755 + add_repeater_data(step_field.closest(".gfield--type-repeater_end"), row_id, row_data);
756 + }
757 + }, 100);
758 + } catch (e) {
759 + setTimeout(function () {
760 + for (var j = 0; j < initial_rows; j++) {
761 + add_repeater_data(step_field.closest(".gfield--type-repeater_end"));
762 + }
763 + }, 100);
764 + }
621 765 } else {
622 766 setTimeout(function () {
623 767 for (var j = 0; j < initial_rows; j++) {
624 768 add_repeater_data(step_field.closest(".gfield--type-repeater_end"));
@@ -877,15 +1021,17 @@
877 1021 }
878 1022 inputId = inputId.split('_');
879 1023 // allow the user to override the datepicker options object
880 1024 optionsObj = gform.applyFilters('gform_datepicker_options_pre_init', optionsObj, inputId[1], inputId[2], $element);
881 - $element.datepicker(optionsObj);
882 - // We give the input focus after selecting a date which differs from default Datepicker behavior; this prevents
883 - // users from clicking on the input again to open the datepicker. Let's add a manual click event to handle this.
884 - if ($element.is(':input')) {
885 - $element.click(function () {
886 - $element.datepicker('show');
887 - });
1025 + if (typeof $element.datepicker === 'function') {
1026 + $element.datepicker(optionsObj);
1027 + // We give the input focus after selecting a date which differs from default Datepicker behavior; this prevents
1028 + // users from clicking on the input again to open the datepicker. Let's add a manual click event to handle this.
1029 + if ($element.is(':input')) {
1030 + $element.click(function () {
1031 + $element.datepicker('show');
1032 + });
1033 + }
888 1034 }
889 1035 }
890 1036 })
891 1037 $(document).on("change", ".gfield--type-repeater_end input,.gfield--type-repeater_end textarea,.gfield--type-repeater_end select", function (event) {