';
60 | }
61 | }
62 |
63 | public static function gform_editor() {
64 | echo "";
72 | }
73 |
74 | public static function gform_tooltips( $tooltips ) {
75 | $tooltips['form_field_repeater2_end_add'] = "The HTML to replace the default add button. This HTML will be placed inside of a span tag with the class 'gf_repeater2_add'.";
76 | $tooltips['form_field_repeater2_end_remove'] = "The HTML to replace the default remove button. This HTML will be placed inside of a span tag with the class 'gf_repeater2_remove'.";
77 | $tooltips['form_field_repeater2_end_hideButtons'] = "If enabled, no add and remove buttons will be displayed. This is useful if you plan on using custom javascript to trigger the repeater2.";
78 |
79 | return $tooltips;
80 | }
81 |
82 | public function get_field_content( $value, $force_frontend_label, $form ) {
83 | if (is_admin()) {
84 | $admin_buttons = $this->get_admin_buttons();
85 |
86 | $field_content = "{$admin_buttons}
87 |
";
92 | }
93 | }
94 |
95 | public static function gform_appearance_settings($position, $form_id) {
96 | if ($position == 400) {
97 | echo "
98 |
99 |
104 |
";
105 | }
106 | }
107 |
108 | public static function gform_editor() {
109 | echo "";
118 | }
119 |
120 | public static function gform_tooltips($tooltips) {
121 | $tooltips['form_field_repeater2_start'] = "The number of times the repeater2 will be repeated when the form is rendered. Leaving this field blank or setting it to a number higher than the maximum number is the same as setting it to 1.";
122 | $tooltips['form_field_repeater2_min'] = "The minimum number of times the repeater2 is allowed to be repeated. Leaving this field blank or setting it to a number higher than the maximum field is the same as setting it to 1.";
123 | $tooltips['form_field_repeater2_max'] = "The maximum number of times the repeater2 is allowed to be repeated. Leaving this field blank or setting it to a number lower than the minimum field is the same as setting it to unlimited.";
124 | $tooltips['form_field_repeater2_hideLabel'] = "If this is checked, the repeater2 label and description will not be shown to users on the form.";
125 | return $tooltips;
126 | }
127 |
128 | function validate($value, $form) {
129 | $repeater2_required = $this->repeater2RequiredChildren;
130 |
131 | if (!empty($repeater2_required)) {
132 | $dataArray = json_decode($value, true);
133 |
134 | foreach ($form['fields'] as $key=>$value) {
135 | $fieldKeys[$value['id']] = $key;
136 |
137 | if (is_array($value['inputs'])) {
138 | foreach ($value['inputs'] as $inputKey=>$inputValue) {
139 | $inputKeys[$value['id']][$inputValue['id']] = $inputKey;
140 | }
141 | }
142 | }
143 |
144 | if ($dataArray['repeatCount'] < $this->min) {
145 | $this->failed_validation = true;
146 | $this->validation_message = "A minimum number of ".$this->min." is required.";
147 | return;
148 | }
149 |
150 | if ($this->max && $dataArray['repeatCount'] > $this->max) {
151 | $this->failed_validation = true;
152 | $this->validation_message = "A maximum number of ".$this->max." is allowed.";
153 | return;
154 | }
155 |
156 | for ($i = 1; $i < $dataArray['repeatCount'] + 1; $i++) {
157 | foreach ($dataArray['children'] as $field_id=>$field) {
158 | $inputNames = $field['inputs'];
159 | $repeatSkips = rgars($field, 'conditionalLogic/skip');
160 |
161 |
162 | if (!is_array($inputNames)) { continue; }
163 |
164 | if (is_array($repeatSkips)) {
165 | if (in_array($i, $repeatSkips) || in_array('all', $repeatSkips)) { continue; }
166 | }
167 |
168 | foreach ($inputNames as $inputName) {
169 | if (is_array($inputName)) { $inputName = reset($inputName); }
170 |
171 | if (substr($inputName, -2) == '[]') {
172 | $getInputName = substr($inputName, 0, strlen($inputName) - 2).'-'.$dataArray['repeater2Id'].'-'.$i;
173 | } else {
174 | $getInputName = $inputName.'-'.$dataArray['repeater2Id'].'-'.$i;
175 | }
176 |
177 | $getInputName = str_replace('.', '_', strval($getInputName));
178 | $getInputData = rgpost($getInputName);
179 | $getInputIdNum = preg_split("/(_|-)/", $getInputName);
180 |
181 | if (in_array($getInputIdNum[1], $repeater2_required)) {
182 | $fieldKey = $fieldKeys[$getInputIdNum[1]];
183 | $fieldType = $form['fields'][$fieldKey]['type'];
184 | $failedValidation = false;
185 |
186 | switch($fieldType) {
187 | case 'name':
188 | $requiredIDs = array(3, 6);
189 | if (in_array($getInputIdNum[2], $requiredIDs) && empty($getInputData)) { $failedValidation = true; }
190 | break;
191 | case 'address':
192 | $skipIDs = array(2);
193 | if (!in_array($getInputIdNum[2], $skipIDs) && empty($getInputData)) { $failedValidation = true; }
194 | break;
195 | default:
196 | if (empty($getInputData)) { $failedValidation = true; }
197 | }
198 |
199 | if ($failedValidation) {
200 | $this->failed_validation = true;
201 | if ($this->errorMessage) { $this->validation_message = $this->errorMessage; } else { $this->validation_message = "A required field was left blank."; }
202 | return;
203 | }
204 | }
205 | }
206 | }
207 | }
208 | }
209 | }
210 |
211 | public function get_field_content( $value, $force_frontend_label, $form ) {
212 | if ( is_admin() ) {
213 | $admin_buttons = $this->get_admin_buttons();
214 | $field_content = "{$admin_buttons}
215 |
");
387 | }
388 | } else {
389 | repeater2ChildElement
390 | .removeClass('gfield_error')
391 | .find('.validation_message').remove();
392 | }
393 | }
394 | }
395 | }
396 | }
397 |
398 | /*
399 | gfRepeater_resetRepeaterChildrenAttrs(formId, repeater2Id)
400 | Resets all repeatId's so that they are chronological.
401 | */
402 | function gfRepeater_resetRepeaterChildrenAttrs(formId, repeater2Id) {
403 | var repeater2Children = gfRepeater_select(formId, repeater2Id);
404 | var x = 0;
405 |
406 | jQuery(repeater2Children).each(function(){
407 |
408 |
409 | if (jQuery(this).attr('data-repeater2-childid') == 1) {
410 | x += 1;
411 | }
412 |
413 | if (jQuery(this).attr('data-repeater2-repeatid') !== x) {
414 | gfRepeater_setRepeaterChildAttrs(formId, repeater2Id, jQuery(this), x);
415 |
416 | }
417 | });
418 | }
419 |
420 | /*
421 | gfRepeater_conditionalLogic_set(formId, repeater2Id, repeater2ChildId, repeatId)
422 | Runs 'gfRepeater_conditionalLogic_do' and assigns change event for all fields involed with the repeater2ChildElement's conditional logic.
423 | */
424 | function gfRepeater_conditionalLogic_set(formId, repeater2Id, repeater2ChildId, repeatId) {
425 | gfRepeater_conditionalLogic_do(formId, repeater2Id, repeater2ChildId, repeatId);
426 |
427 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
428 | var repeater2Child = repeater2['children'][repeater2ChildId]
429 | var conditionalLogic = repeater2Child['conditionalLogic'];
430 |
431 | jQuery.each(conditionalLogic['rules'], function(key, value){
432 | var fieldId = value['fieldId'];
433 | var childId = gfRepeater_getIndex(repeater2['children'], 'idNum', fieldId);
434 |
435 | if (childId !== false) {
436 | var inputs = gfRepeater_select(formId, repeater2Id, repeatId, childId, '*');
437 | } else {
438 | var inputs = jQuery('#field_' + formId + '_' + fieldId + ' :input');
439 | repeatId = null;
440 | }
441 |
442 | jQuery.each(inputs, function(key, input){
443 | jQuery(this).bind('propertychange change click keyup input paste', function(){
444 | gfRepeater_conditionalLogic_do(formId, repeater2Id, repeater2ChildId, repeatId);
445 | });
446 | });
447 | });
448 | }
449 |
450 | /*
451 | gfRepeater_conditionalLogic_setAll(formId, repeater2Id, repeatId)
452 | Sets conditionalLogic for all children inside of a repeatId.
453 | */
454 | function gfRepeater_conditionalLogic_setAll(formId, repeater2Id, repeatId) {
455 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
456 | jQuery.each(repeater2['children'], function(key, value){
457 | if (this.conditionalLogic) {
458 | gfRepeater_conditionalLogic_set(formId, repeater2Id, key, repeatId);
459 | }
460 | });
461 | }
462 |
463 | /*
464 | gfRepeater_conditionalLogic_do(formId, repeater2Id, repeater2ChildId, repeatId)
465 | Hides or Shows repeater2ChildElement depending on conditional logic.
466 | */
467 | function gfRepeater_conditionalLogic_do(formId, repeater2Id, repeater2ChildId, repeatId) {
468 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
469 | var repeater2Child = repeater2['children'][repeater2ChildId]
470 | var conditionalLogic = repeater2Child['conditionalLogic'];
471 | var effectedIds = [repeater2ChildId];
472 | var conditions = [];
473 | var conditionsPassed = false;
474 | var hideField = false;
475 |
476 | jQuery.each(conditionalLogic['rules'], function(key, value){
477 | var condition = false;
478 | var fieldId = value['fieldId'];
479 | var childId = gfRepeater_getIndex(repeater2['children'], 'idNum', fieldId);
480 |
481 | if (childId !== false) {
482 | var child = repeater2['children'][childId];
483 | var childElement = gfRepeater_select(formId, repeater2Id, repeatId, childId);
484 |
485 | if (child['type'] == 'checkbox' || child['type'] == 'radio') {
486 | var inputValue = gfRepeater_getChoiceValue(childElement);
487 | var multiInput = true;
488 | } else {
489 | var inputElement = gfRepeater_select(formId, repeater2Id, repeatId, childId, 1);
490 | var inputValue = gfRepeater_getInputValue(inputElement);
491 | var multiInput = false;
492 | }
493 | } else {
494 | var fieldElement = jQuery('#field_' + formId + '_' + fieldId);
495 | var firstInput = fieldElement.find(':input').first();
496 |
497 | if (firstInput.is(':checkbox, :radio')) {
498 | var inputValue = gfRepeater_getChoiceValue(fieldElement);
499 | var multiInput = true;
500 | } else {
501 | var inputValue = gfRepeater_getInputValue(firstInput);
502 | var multiInput = false;
503 | }
504 | }
505 |
506 | if (multiInput) {
507 | if (jQuery.inArray(value['value'], inputValue) !== -1) { inputValue = value['value']; } else { inputValue = false; }
508 | }
509 |
510 | condition = gf_matches_operation(inputValue, value['value'], value['operator']);
511 | conditions.push(condition);
512 | });
513 |
514 | if (conditionalLogic['logicType'] == 'all') {
515 | if (jQuery.inArray(false, conditions) == -1) { conditionsPassed = true; }
516 | } else {
517 | if (jQuery.inArray(true, conditions) !== -1) { conditionsPassed = true; }
518 | }
519 |
520 | if ((conditionsPassed && conditionalLogic['actionType'] !== 'show') || (!conditionsPassed && conditionalLogic['actionType'] == 'show')) {
521 | hideField = true;
522 | }
523 |
524 | if (repeater2Child['type'] == 'section') {
525 | var sectionChildren = gfRepeater_getIndex(repeater2['children'], 'parentSection', repeater2ChildId, true);
526 | if (sectionChildren !== false) { effectedIds = effectedIds.concat(sectionChildren); }
527 | }
528 |
529 | jQuery.each(effectedIds, function(key, value){
530 | var effectedChild = repeater2['children'][value];
531 | var effectedLogic = effectedChild['conditionalLogic'];
532 | var effectedElement = gfRepeater_select(formId, repeater2Id, repeatId, value);
533 | var skipId = repeatId;
534 |
535 | if (skipId == null) { skipId = 'all'; }
536 |
537 | if (effectedElement.length) {
538 | if (hideField) {
539 | effectedElement.hide();
540 |
541 | if (effectedLogic) {
542 | if (jQuery.inArray(skipId, effectedLogic['skip']) == -1) {
543 | effectedLogic['skip'].push(skipId);
544 | }
545 | }
546 | } else {
547 | effectedElement.show();
548 |
549 | if (effectedLogic) {
550 | if (jQuery.inArray(skipId, effectedLogic['skip']) !== -1) {
551 | var skipIndex = effectedLogic['skip'].indexOf(skipId);
552 | effectedLogic['skip'].splice(skipIndex, 1);
553 | }
554 | }
555 | }
556 | }
557 | });
558 |
559 | gfRepeater_updateDataElement(formId, repeater2Id);
560 | }
561 |
562 | /*
563 | gfRepeater_doShortcode(element, shortcode, value)
564 | Finds the 'shortcode' inside of 'element' and replaces it's contents with 'value'.
565 |
566 | element The element to search inside.
567 | shortcode The shortcode to search for.
568 | value The value to put inside the shortcode.
569 | */
570 | function gfRepeater_doShortcode(element, shortcode, value) {
571 | element.find('.gfRepeater-shortcode-'+shortcode).each(function(){
572 | jQuery(this).html(value);
573 | });
574 | }
575 |
576 | /*
577 | gfRepeater_replaceShortcodes(element)
578 | Replaces any repeater2 shortcodes with spans for those shortcodes.
579 |
580 | element The element to search and replace.
581 | */
582 | function gfRepeater_replaceShortcodes(element) {
583 | var shortcodes = ['count', 'buttons', 'add', 'remove'];
584 |
585 | jQuery.each(shortcodes, function(key, shortcode){
586 | var html = element.html();
587 | element.html(html.replace('[gfRepeater-'+shortcode+']', ''));
588 | });
589 | }
590 |
591 | /*
592 | gfRepeater_repeatRepeater(formId, repeater2Id)
593 | Repeats the repeater2 once.
594 |
595 | formId The form Id.
596 | repeater2Id The repeater2 ID number to repeat.
597 | */
598 | function gfRepeater_repeatRepeater(formId, repeater2Id) {
599 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
600 | var repeatId = repeater2['data']['repeatCount'] + 1;
601 | if (repeater2['settings']['max'] && repeater2['data']['repeatCount'] >= repeater2['settings']['max']) { return; }
602 |
603 | jQuery(repeater2['controllers']['start'])
604 | .parents('form')
605 | .trigger('gform_repeater2_before_repeat', [repeater2Id, repeatId]);
606 |
607 | var lastElement = gfRepeater_select(formId, repeater2Id).last();
608 |
609 | jQuery.each(repeater2['children'], function(key, value){
610 | var clonedElement = jQuery(this.element).clone();
611 |
612 | gfRepeater_resetInputs(formId, repeater2Id, key, clonedElement);
613 | gfRepeater_setRepeaterChildAttrs(formId, repeater2Id, clonedElement);
614 |
615 | clonedElement.insertAfter(lastElement);
616 | lastElement.find('.datepicker').removeClass('initialized');
617 | lastElement = clonedElement;
618 | });
619 |
620 | gfRepeater_conditionalLogic_setAll(formId, repeater2Id, repeatId);
621 |
622 | repeater2['data']['repeatCount'] += 1;
623 | gfRepeater_updateDataElement(formId, repeater2Id);
624 | gfRepeater_updateRepeaterControls(formId, repeater2Id);
625 |
626 | if (window['gformInitDatepicker']) { gformInitDatepicker(); }
627 |
628 | jQuery(repeater2['controllers']['start'])
629 | .parents('form')
630 | .trigger('gform_repeater2_after_repeat', [repeater2Id, repeatId]);
631 |
632 | }
633 |
634 | /*
635 | gfRepeater_unrepeatRepeater(formId, repeater2Id, repeatId)
636 | Un-repeats the repeater2 once.
637 |
638 | formId The form Id.
639 | repeater2Id The repeater2 ID number to unrepeat.
640 | repeatId (Optional) The repeat ID number to unrepeat. If an ID number is not specified, the last one will be chosen.
641 | */
642 | function gfRepeater_unrepeatRepeater(formId, repeater2Id, repeatId) {
643 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
644 | if (repeater2['data']['repeatCount'] <= repeater2['settings']['min']) { return; }
645 | if (!repeatId) { var repeatId = repeater2['data']['repeatCount']; }
646 |
647 | jQuery(repeater2['controllers']['start'])
648 | .parents('form')
649 | .trigger('gform_repeater2_before_unrepeat', [repeater2Id, repeatId]);
650 |
651 | jQuery.each(repeater2['children'], function(childId, value){
652 | gfRepeater_select(formId, repeater2Id, repeatId, childId).remove();
653 | });
654 |
655 | repeater2['data']['repeatCount'] -= 1;
656 | gfRepeater_updateDataElement(formId, repeater2Id);
657 | gfRepeater_updateRepeaterControls(formId, repeater2Id);
658 |
659 | if (repeatId !== repeater2['data']['repeatCount'] + 1) {
660 | gfRepeater_resetRepeaterChildrenAttrs(formId, repeater2Id);
661 | }
662 |
663 | jQuery(repeater2['controllers']['start'])
664 | .parents('form')
665 | .trigger('gform_repeater2_after_unrepeat', [repeater2Id, repeatId]);
666 | }
667 |
668 | /*
669 | gfRepeater_repeatRepeaterTimes(formId, repeater2Id, timesX)
670 | Repeats the repeater2 a multiple number of times depeneding on the 'timesX' variable.
671 |
672 | formId The form Id.
673 | repeater2Id The repeater2 ID number to repeat.
674 | timesX (Optional) The number of times to repeat the repeater2. Default is 1.
675 | */
676 | function gfRepeater_repeatRepeaterTimes(formId, repeater2Id, timesX) {
677 | if (!timesX) { var timesX = 1; }
678 | for (i = 0; i < timesX; i++) {
679 | gfRepeater_repeatRepeater(formId, repeater2Id);
680 | }
681 | }
682 |
683 | /*
684 | gfRepeater_unrepeatRepeaterTimes(formId, repeater2Id, timesX)
685 | UnRepeats the repeater2 a multiple number of times depeneding on the 'timesX' variable.
686 |
687 | formId The form Id.
688 | repeater2Id The repeater2 ID number to unrepeat.
689 | timesX (Optional) The number of times to unrepeat the repeater2. Default is 1.
690 | */
691 | function gfRepeater_unrepeatRepeaterTimes(formId, repeater2Id, timesX) {
692 | if (!timesX) { var timesX = 1; }
693 | for (i = 0; i < timesX; i++) {
694 | gfRepeater_unrepeatRepeater(formId, repeater2Id);
695 | }
696 | }
697 |
698 | /*
699 | gfRepeater_setRepeater(formId, repeater2Id, timesX)
700 | Repeats or unrepeats the repeater2 to set it to timesX.
701 |
702 | formId The form Id.
703 | repeater2Id The repeater2 ID number to repeat or unrepeat.
704 | timesX The number to set the repeater2 to.
705 | */
706 | function gfRepeater_setRepeater(formId, repeater2Id, timesX) {
707 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
708 | var currentRepeatCount = repeater2['data']['repeatCount'];
709 |
710 | if (timesX == currentRepeatCount) {
711 | return;
712 | } else if (timesX > currentRepeatCount) {
713 | var timesY = timesX - currentRepeatCount;
714 | gfRepeater_repeatRepeaterTimes(formId, repeater2Id, timesY);
715 | } else if (timesX < currentRepeatCount) {
716 | var timesY = currentRepeatCount - timesX;
717 | gfRepeater_unrepeatRepeaterTimes(formId, repeater2Id, timesY);
718 | }
719 | }
720 |
721 | /*
722 | gfRepeater_updateRepeaterControls(formId, repeater2Id)
723 | Updates the add and remove buttons for the repeater2. If the minimum repeat number has been reached, the remove button is hidden. If the maximum number has been reached, the add button is hidden.
724 |
725 | formId The form Id.
726 | repeater2Id The repeater2 ID number to update the controls for.
727 | */
728 | function gfRepeater_updateRepeaterControls(formId, repeater2Id) {
729 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
730 |
731 | if (repeater2['settings']['max']) {
732 | if (repeater2['data']['repeatCount'] >= repeater2['settings']['max']) {
733 | jQuery(repeater2['controllers']['add']).hide();
734 | } else {
735 | jQuery(repeater2['controllers']['add']).show();
736 | }
737 | }
738 |
739 | if (repeater2['data']['repeatCount'] <= repeater2['settings']['min']) {
740 | jQuery(repeater2['controllers']['remove']).hide();
741 | } else {
742 | jQuery(repeater2['controllers']['remove']).show();
743 | }
744 | }
745 |
746 | /*
747 | gfRepeater_resetInputs(formId, repeater2Id, childId, repeater2ChildElement)
748 | Resets all input elements inside of a repeater2 child.
749 |
750 | formId The form Id.
751 | repeater2Id The repeater2 ID.
752 | childId The repeater2 child ID number.
753 | repeater2ChildElement The repeater2 child element.
754 | */
755 | function gfRepeater_resetInputs(formId, repeater2Id, childId, repeater2ChildElement) {
756 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
757 | jQuery.each(repeater2['children'][childId]['inputs'], function(key, value){
758 | var inputId = this['id'];
759 | var inputName = this['name'];
760 | var inputDefaultValue = this['defaultValue'];
761 | var inputElement = gfRepeater_findElementByNameOrId(repeater2ChildElement, inputName, inputId);
762 |
763 | if (inputElement) {
764 | gfRepeater_setInputValue(inputElement, inputDefaultValue);
765 | }
766 | });
767 | }
768 |
769 | /*
770 | gfRepeater_select(formId, repeater2Id, repeatId, childId, inputId)
771 | Selects an element depending on the variables passed.
772 |
773 | formId The form Id.
774 | repeater2Id (Optional) The repeater2 Id.
775 | repeatId (Optional) The repeat Id.
776 | childId (Optional) The child Id.
777 | inputId (Optional) The input Id. Also accepts '*' to select all inputs.
778 | */
779 | function gfRepeater_select(formId, repeater2Id, repeatId, childId, inputId) {
780 | var selector = 'div#gform_wrapper_'+formId+'>form#gform_'+formId;
781 | if (repeater2Id || repeatId || childId || inputId) { selector += '>.gform_body .gform_fields>.gfield.gf_repeater2_child_field'; }
782 | if (repeater2Id) { selector += '[data-repeater2-parentid='+repeater2Id+']'; }
783 | if (repeatId) { selector += '[data-repeater2-repeatid='+repeatId+']'; }
784 | if (childId) { selector += '[data-repeater2-childid='+childId+']'; }
785 | if (inputId) {
786 | if (inputId == '*') {
787 | selector += ' [data-repeater2-inputid]';
788 | } else {
789 | selector += ' [data-repeater2-inputid='+inputId+']';
790 | }
791 | }
792 | return jQuery(selector);
793 | }
794 |
795 | /*
796 | gfRepeater_findElementByNameOrId(searchElement, elementName, elementId)
797 | Searches for an an element inside of another element by ID or Name. If both an ID and a Name are supplied it will first try the Name and then the ID.
798 |
799 | searchElement Element to search inside.
800 | inputName (Optional) A element name to search for.
801 | inputId (Optional) A element ID to search for.
802 | */
803 | function gfRepeater_findElementByNameOrId(searchElement, elementName, elementId) {
804 | if (elementName) { var foundElement = jQuery(searchElement).find("[name^='"+elementName+"']"); }
805 | if (!foundElement && elementId) { var foundElement = jQuery(searchElement).find("[id^='"+elementId+"']"); }
806 | if (foundElement) { return foundElement; } else { return false; }
807 | }
808 |
809 | /*
810 | gfRepeater_getIndex
811 | Searches 'object' where 'key' equals 'value'.
812 | Returns first result if multiple is false.
813 | Returns array with all key results if multiple is true.
814 | Returns false if nothing was found.
815 |
816 | object Object or array to search through.
817 | key Key to search for.
818 | value Value to search for.
819 | multiple Set to true to return all results in an array.
820 | */
821 | function gfRepeater_getIndex(object, key, value, multiple) {
822 | var keys = [];
823 |
824 | jQuery.each(object, function(fieldKey, fieldValue){
825 | if (fieldValue[key] == value) {
826 | keys.push(fieldKey);
827 | if (!multiple) { return false; }
828 | }
829 | });
830 |
831 | if (keys.length) {
832 | if (multiple) {
833 | return keys;
834 | } else { return keys[0]; }
835 | } else { return false; }
836 | }
837 |
838 | /*
839 | gfRepeater_getChoiceValue(fieldElement)
840 | Searches 'fieldElement' for checkboxes and radios. Returns an array with the labels of all the values that are 'checked'.
841 |
842 | fieldElement The element to search in.
843 | */
844 | function gfRepeater_getChoiceValue(fieldElement) {
845 | var value = [];
846 | jQuery(fieldElement).find(':checkbox, :radio').each(function(){
847 | if (jQuery(this).prop('checked') == true) {
848 | var id = this.id;
849 | var label = jQuery(this).siblings('label').first().text();
850 | value.push(label);
851 | }
852 | });
853 | return value;
854 | }
855 |
856 | /*
857 | gfRepeater_getInputValue(inputElement)
858 | Gets the value of an input.
859 |
860 | inputElement The input element.
861 | */
862 | function gfRepeater_getInputValue(inputElement) {
863 | if (inputElement.is(':checkbox, :radio')) {
864 | if (inputElement.prop('checked') == true) { return true; } else { return false; }
865 | } else {
866 | return inputElement.val();
867 | }
868 | }
869 |
870 | /*
871 | gfRepeater_setInputValue(inputElement, value)
872 | Sets the value of an input.
873 |
874 | inputElement The input element.
875 | inputValue The value to set to the input.
876 | */
877 | function gfRepeater_setInputValue(inputElement, inputValue) {
878 | if (inputElement.is(':checkbox, :radio')) {
879 | if (inputValue == 'on' || inputElement.prop('value') === inputValue) { inputElement.prop('checked', true) } else { inputElement.prop('checked', false) }
880 | } else {
881 | inputElement.val(inputValue);
882 | }
883 | }
884 |
885 | /*
886 | gfRepeater_updateDataElement(formId, repeater2Id)
887 | Updates the data element for the repater. The data element stores information that is passed to PHP for processing.
888 |
889 | formId The form Id.
890 | repeater2Id The repeater2 ID number to update the data element for.
891 | */
892 | function gfRepeater_updateDataElement(formId, repeater2Id) {
893 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
894 | var dataElement = jQuery(repeater2['controllers']['data']);
895 |
896 | var dataArray = jQuery(dataElement).val();
897 | if (dataArray) { dataArray = JSON.parse(dataArray); }
898 |
899 | dataArray['repeater2Id'] = repeater2Id;
900 | dataArray['repeatCount'] = repeater2['data']['repeatCount'];
901 |
902 | jQuery.each(dataArray['children'], function(key, value){
903 | if (Array.isArray(this)) { dataArray['children'][key] = {}; }
904 | var inputData = repeater2['data']['inputData'][key];
905 | if (inputData && inputData.length) {
906 | dataArray['children'][key]['inputs'] = inputData;
907 | }
908 | var fieldIndex = gfRepeater_getIndex(repeater2['children'], 'idNum', key);
909 | // TODO: Temporarily comment this line out
910 | //dataArray['children'][key]['conditionalLogic'] = repeater2['children'][fieldIndex]['conditionalLogic'];
911 | });
912 |
913 | dataArray = JSON.stringify(dataArray);
914 | jQuery(dataElement).val(dataArray);
915 | }
916 |
917 | /*
918 | gfRepeater_start()
919 | Runs the gfRepeater_setRepeaterChildAttrs function for the first set of repeater2 children and then repeats the repeater2 a number of times depending on the repeater2 setting. - Second phase of setup.
920 | */
921 | function gfRepeater_start() {
922 | jQuery.each(gfRepeater_repeater2s, function(key, repeater2){
923 | var formId = key;
924 | var form = gfRepeater_select(formId);
925 |
926 | jQuery.each(repeater2, function(key, value){
927 | var repeater2Id = key;
928 | var repeater2 = gfRepeater_repeater2s[formId][repeater2Id];
929 | var repeatCount = repeater2['settings']['start'];
930 | var paremCount = repeater2['data']['paremCount'];
931 |
932 | if (repeater2['controllers']['data'].attr('data-required')) { repeater2['controllers']['start'].addClass('gfield_contains_required'); }
933 |
934 | jQuery.each(repeater2['children'], function(key, value){
935 |
936 | gfRepeater_setRepeaterChildAttrs(formId, repeater2Id, jQuery(repeater2['children'][key]['element']), 1);
937 | if (this.conditionalLogic) { gfRepeater_conditionalLogic_set(formId, repeater2Id, key, 1); }
938 | });
939 |
940 | if (gfRepeater_submitted) {
941 | repeatCount = repeater2['data']['prevRepeatCount'];
942 | } else if (paremCount > repeatCount) {
943 | repeatCount = paremCount;
944 | }
945 |
946 | gfRepeater_setRepeater(formId, repeater2Id, repeatCount);
947 | gfRepeater_updateRepeaterControls(formId, repeater2Id);
948 | gfRepeater_updateDataElement(formId, repeater2Id);
949 | });
950 |
951 | jQuery(form).trigger('gform_repeater2_init_done');
952 | });
953 |
954 | if (window['gformInitDatepicker']) { gformInitDatepicker(); }
955 | }
956 |
957 | // Initiation after gravity forms has rendered.
958 | // This will fire each time a form is rendered, but we only need it the first time.
959 | jQuery(document).bind('gform_post_render', function() {
960 | if(!gfRepeater_repeater2s_is_set) {
961 | if (gfRepeater_getRepeaters()) {
962 | gfRepeater_start();
963 | jQuery(window).trigger('gform_repeater2_init_done');
964 | } else {
965 | console.log('There was an error with one of your repeater2s. This is usually caused by forgetting to include a repeater2-end field or by trying to nest repeater2s.');
966 | }
967 | gfRepeater_repeater2s_is_set = true;
968 | }
969 | });
970 |
--------------------------------------------------------------------------------
/js/gf-repeater2.min.js:
--------------------------------------------------------------------------------
1 | var gfRepeater_repeater2s={},gfRepeater_submitted=!1,gfRepeater_repeater2s_is_set=!1;function gfRepeater_getRepeaters(){var repeater2Data;return!!jQuery(".gform_wrapper").each((function(){var repeater2s={},formId=this.id.split("_")[2],form=jQuery(this).children("form").first(),repeater2Id=0,repeater2Found=0,repeater2ChildCount=0,repeater2ParemCount=0,parentSection=null,repeater2Info={},repeater2Children={},repeater2ChildrenInputData={},capturedData={},dataElement,startElement;return jQuery(this).find(".gfield").each((function(){if(0==repeater2Found)jQuery(this).has(".ginput_container_repeater2").length&&(repeater2Id+=1,startElement=jQuery(this),dataElement=startElement.find(".gform_repeater2"),(repeater2Info=jQuery(dataElement).val())&&(repeater2Info=JSON.parse(repeater2Info)),jQuery.captures()&&(capturedData=jQuery.captures(dataElement.attr("name")))&&(capturedData=JSON.parse(capturedData),1==repeater2Id&&capturedData.formId==formId&&(gfRepeater_submitted=!0)),1==repeater2Id&&jQuery(form).capture(),repeater2Found=1);else{if(jQuery(this).has(".ginput_container_repeater2").length)return!1;if(jQuery(this).has(".ginput_container_repeater2-end").length){var repeater2Controllers={},endElement=jQuery(this),addElement=endElement.find(".gf_repeater2_add"),removeElement=endElement.find(".gf_repeater2_remove"),addFunction="gfRepeater_repeatRepeater("+formId+","+repeater2Id+");",removeFunction="gfRepeater_unrepeatRepeater("+formId+","+repeater2Id+");";jQuery(addElement).attr({onclick:addFunction,onkeypress:addFunction}),jQuery(removeElement).attr({onclick:removeFunction,onkeypress:removeFunction}),repeater2Controllers={add:addElement,remove:removeElement,data:dataElement,start:startElement,end:endElement};var repeater2Settings={},repeater2Start=Number(repeater2Info.start),repeater2Min=Number(repeater2Info.min),repeater2Max=Number(repeater2Info.max);(!repeater2Start||repeater2Max&&repeater2Start>repeater2Max)&&(repeater2Start=1),(!repeater2Min||repeater2Max&&repeater2Min>repeater2Max)&&(repeater2Min=1),(!repeater2Max||repeater2Min&&repeater2Max&&repeater2Min>repeater2Max)&&(repeater2Max=null),repeater2Settings={start:repeater2Start,min:repeater2Min,max:repeater2Max};var repeater2data={},repeater2TabIndex=Number(dataElement.attr("tabindex")),prevRepeatCount=null;gfRepeater_submitted&&capturedData&&(prevRepeatCount=capturedData.repeatCount),repeater2data={repeatCount:1,prevRepeatCount:prevRepeatCount,childrenCount:repeater2ChildCount,paremCount:repeater2ParemCount,tabIndex:repeater2TabIndex,inputData:repeater2ChildrenInputData},repeater2s[repeater2Id]={data:repeater2data,settings:repeater2Settings,controllers:repeater2Controllers,children:repeater2Children},repeater2Found=0,repeater2ChildCount=0,repeater2ParemCount=0,parentSection=null,repeater2Children={},repeater2ChildrenInputData={},repeater2ChildrenPrePopulate={},repeater2RequiredChildren=null}else{repeater2ChildCount+=1;var childElement=jQuery(this),childLabel=jQuery(this).children(".gfield_label").text(),childId=jQuery(this).attr("id"),childIdNum=childId.split("_")[2],childInputs={},childInputNames=[],childInputCount=0,childRequired=!1,childInfo=repeater2Info.children[childIdNum],childParentSection=parentSection,childType,inputMask,conditionalLogic;if(void 0===childInfo)return;if(jQuery(this).has(".ginput_container").length){var childContainerClasses=jQuery(this).find(".ginput_container").attr("class").split(/\s+/),searchFor="ginput_container_";jQuery.each(childContainerClasses,(function(key,value){value.slice(0,searchFor.length)==searchFor&&(childType=value.slice(searchFor.length,value.length))}))}else jQuery(this).hasClass("gform_hidden")?childType="hidden":jQuery(this).hasClass("gsection")&&(childType="section");"section"==childType&&(parentSection=repeater2ChildCount,childParentSection=null),childInfo.required&&(childRequired=!0),childInfo.inputMask&&(inputMask=childInfo.inputMask),childInfo.conditionalLogic&&((conditionalLogic=childInfo.conditionalLogic).skip=[]),jQuery(this).find(":input").each((function(){childInputCount+=1;var inputElement=jQuery(this),inputId=jQuery(this).attr("id"),inputName=jQuery(this).attr("name"),inputName2,inputDefaultValue=gfRepeater_getInputValue(inputElement),inputPrePopulate={};inputName&&(-1==jQuery.inArray(inputName,childInputNames)&&childInputNames.push(inputName),inputName2="[]"==inputName.slice(-2)?inputName.slice(0,inputName.length-2):inputName,childInfo.prePopulate&&("checkbox"==childType||"radio"==childType?inputPrePopulate=childInfo.prePopulate:childInfo.prePopulate[inputName2.split("_")[1]]&&(inputPrePopulate=childInfo.prePopulate[inputName2.split("_")[1]]),inputPrePopulate&&jQuery.each(inputPrePopulate,(function(key,value){key>repeater2ParemCount&&(repeater2ParemCount=Number(key))})))),childInputs[childInputCount]={element:inputElement,id:inputId,name:inputName,defaultValue:inputDefaultValue,prePopulate:inputPrePopulate}})),repeater2Children[repeater2ChildCount]={element:childElement,id:childId,idNum:childIdNum,inputs:childInputs,inputCount:childInputCount,required:childRequired,type:childType,inputMask:inputMask,conditionalLogic:conditionalLogic,parentSection:childParentSection},repeater2ChildrenInputData[childIdNum]=childInputNames}}})),0===repeater2Found&&(repeater2s?(gfRepeater_repeater2s[formId]=repeater2s,!0):void 0)}))}function gfRepeater_setRepeaterChildAttrs(formId,repeater2Id,repeater2ChildElement,repeatId){var repeater2=gfRepeater_repeater2s[formId][repeater2Id];if(!repeatId)var repeatId=repeater2.data.repeatCount+1;var childId=jQuery(repeater2ChildElement).attr("id").split("-")[0],childKey=gfRepeater_getIndex(repeater2.children,"id",childId),checkValidation=jQuery("#gform_wrapper_"+formId).hasClass("gform_validation_error");if(childKey){var failedValidation=!1,child=repeater2.children[childKey],childRequired=child.required,childType=child.type,inputCount=child.inputCount,inputMask=child.inputMask,tabindex=repeater2.data.tabIndex,newRootId=childId+"-"+repeater2Id+"-"+repeatId;jQuery(repeater2ChildElement).attr("id",newRootId).attr("data-repeater2-parentId",repeater2Id).attr("data-repeater2-repeatId",repeatId).attr("data-repeater2-childId",childKey).addClass("gf_repeater2_child_field"),gfRepeater_replaceShortcodes(repeater2ChildElement),gfRepeater_doShortcode(repeater2ChildElement,"count",repeatId),gfRepeater_doShortcode(repeater2ChildElement,"buttons",repeater2.controllers.add.parent().clone()),gfRepeater_doShortcode(repeater2ChildElement,"add",repeater2.controllers.add.clone()),gfRepeater_doShortcode(repeater2ChildElement,"remove",repeater2.controllers.remove.clone());var removeFunction="gfRepeater_unrepeatRepeater("+formId+","+repeater2Id+","+repeatId+");";if(jQuery(repeater2ChildElement).find(".gf_repeater2_remove").attr({onclick:removeFunction,onkeypress:removeFunction}).show(),jQuery(repeater2ChildElement).find(".gf_repeater2_add").show(),jQuery.each(repeater2.children[childKey].inputs,(function(key,value){var inputId=this.id,inputName=this.name,prePopulate="";if("radio"==childType)var inputElement=gfRepeater_findElementByNameOrId(repeater2ChildElement,null,inputId);else var inputElement=gfRepeater_findElementByNameOrId(repeater2ChildElement,inputName,inputId);if(inputElement.attr("data-repeater2-inputId",key),inputId){var newInputId=inputId+"-"+repeater2Id+"-"+repeatId;jQuery(inputElement).attr("id",newInputId),jQuery(repeater2ChildElement).find("label[for^='"+inputId+"']").attr("for",newInputId)}if(inputName){if("[]"==inputName.slice(-2))var newInputName=inputName.slice(0,inputName.length-2)+"-"+repeater2Id+"-"+repeatId+"[]";else var newInputName=inputName+"-"+repeater2Id+"-"+repeatId;jQuery(inputElement).attr("name",newInputName).attr("tabindex",tabindex)}if(inputMask&&jQuery(inputElement).mask(inputMask),this.prePopulate[repeatId]?prePopulate=this.prePopulate[repeatId]:this.prePopulate[0]&&(prePopulate=this.prePopulate[0]),prePopulate&&("checkbox"!=childType&&"radio"!=childType||(prePopulateValues=prePopulate.split(","),prePopulate=-1!==jQuery.inArray(key,prePopulateValues)),gfRepeater_setInputValue(inputElement,prePopulate)),window.gformInitDatepicker&&"date"==childType&&2==inputCount&&1==key&&jQuery(inputElement).removeClass("hasDatepicker").datepicker("destroy").siblings(".ui-datepicker-trigger").remove(),gfRepeater_submitted&&checkValidation){if(newInputName){var savedValue=jQuery.captures(newInputName);savedValue&&gfRepeater_setInputValue(inputElement,savedValue)}if(childRequired){if(newInputName){var splitName=newInputName.replace(".","_").split(/(_|-)/);if("name"==childType&&-1==jQuery.inArray(splitName[4],["3","6"]))return!0;if("address"==childType&&-1!==jQuery.inArray(splitName[4],["2"]))return!0}var inputValue;!gfRepeater_getInputValue(inputElement)&&repeatId<=repeater2.data.prevRepeatCount&&(failedValidation=!0)}}})),childRequired){var childLabel=repeater2ChildElement.children(".gfield_label");repeater2ChildElement.addClass("gfield_contains_required"),childLabel.has(".gfield_required").length||childLabel.append('*'),gfRepeater_submitted&&checkValidation&&(failedValidation?(repeater2ChildElement.addClass("gfield_error"),repeater2ChildElement.has(".validation_message").length||repeater2ChildElement.append('
This field is required.
')):repeater2ChildElement.removeClass("gfield_error").find(".validation_message").remove())}}}function gfRepeater_resetRepeaterChildrenAttrs(formId,repeater2Id){var repeater2Children=gfRepeater_select(formId,repeater2Id),x=0;jQuery(repeater2Children).each((function(){1==jQuery(this).attr("data-repeater2-childid")&&(x+=1),jQuery(this).attr("data-repeater2-repeatid")!==x&&gfRepeater_setRepeaterChildAttrs(formId,repeater2Id,jQuery(this),x)}))}function gfRepeater_conditionalLogic_set(formId,repeater2Id,repeater2ChildId,repeatId){gfRepeater_conditionalLogic_do(formId,repeater2Id,repeater2ChildId,repeatId);var repeater2=gfRepeater_repeater2s[formId][repeater2Id],repeater2Child,conditionalLogic=repeater2.children[repeater2ChildId].conditionalLogic;jQuery.each(conditionalLogic.rules,(function(key,value){var fieldId=value.fieldId,childId=gfRepeater_getIndex(repeater2.children,"idNum",fieldId);if(!1!==childId)var inputs=gfRepeater_select(formId,repeater2Id,repeatId,childId,"*");else{var inputs=jQuery("#field_"+formId+"_"+fieldId+" :input");repeatId=null}jQuery.each(inputs,(function(key,input){jQuery(this).bind("propertychange change click keyup input paste",(function(){gfRepeater_conditionalLogic_do(formId,repeater2Id,repeater2ChildId,repeatId)}))}))}))}function gfRepeater_conditionalLogic_setAll(formId,repeater2Id,repeatId){var repeater2=gfRepeater_repeater2s[formId][repeater2Id];jQuery.each(repeater2.children,(function(key,value){this.conditionalLogic&&gfRepeater_conditionalLogic_set(formId,repeater2Id,key,repeatId)}))}function gfRepeater_conditionalLogic_do(formId,repeater2Id,repeater2ChildId,repeatId){var repeater2=gfRepeater_repeater2s[formId][repeater2Id],repeater2Child=repeater2.children[repeater2ChildId],conditionalLogic=repeater2Child.conditionalLogic,effectedIds=[repeater2ChildId],conditions=[],conditionsPassed=!1,hideField=!1;if(jQuery.each(conditionalLogic.rules,(function(key,value){var condition=!1,fieldId=value.fieldId,childId=gfRepeater_getIndex(repeater2.children,"idNum",fieldId);if(!1!==childId){var child=repeater2.children[childId],childElement=gfRepeater_select(formId,repeater2Id,repeatId,childId);if("checkbox"==child.type||"radio"==child.type)var inputValue=gfRepeater_getChoiceValue(childElement),multiInput=!0;else var inputElement,inputValue=gfRepeater_getInputValue(gfRepeater_select(formId,repeater2Id,repeatId,childId,1)),multiInput=!1}else{var fieldElement=jQuery("#field_"+formId+"_"+fieldId),firstInput=fieldElement.find(":input").first();if(firstInput.is(":checkbox, :radio"))var inputValue=gfRepeater_getChoiceValue(fieldElement),multiInput=!0;else var inputValue=gfRepeater_getInputValue(firstInput),multiInput=!1}multiInput&&(inputValue=-1!==jQuery.inArray(value.value,inputValue)&&value.value),condition=gf_matches_operation(inputValue,value.value,value.operator),conditions.push(condition)})),"all"==conditionalLogic.logicType?-1==jQuery.inArray(!1,conditions)&&(conditionsPassed=!0):-1!==jQuery.inArray(!0,conditions)&&(conditionsPassed=!0),(conditionsPassed&&"show"!==conditionalLogic.actionType||!conditionsPassed&&"show"==conditionalLogic.actionType)&&(hideField=!0),"section"==repeater2Child.type){var sectionChildren=gfRepeater_getIndex(repeater2.children,"parentSection",repeater2ChildId,!0);!1!==sectionChildren&&(effectedIds=effectedIds.concat(sectionChildren))}jQuery.each(effectedIds,(function(key,value){var effectedChild,effectedLogic=repeater2.children[value].conditionalLogic,effectedElement=gfRepeater_select(formId,repeater2Id,repeatId,value),skipId=repeatId;if(null==skipId&&(skipId="all"),effectedElement.length)if(hideField)effectedElement.hide(),effectedLogic&&-1==jQuery.inArray(skipId,effectedLogic.skip)&&effectedLogic.skip.push(skipId);else if(effectedElement.show(),effectedLogic&&-1!==jQuery.inArray(skipId,effectedLogic.skip)){var skipIndex=effectedLogic.skip.indexOf(skipId);effectedLogic.skip.splice(skipIndex,1)}})),gfRepeater_updateDataElement(formId,repeater2Id)}function gfRepeater_doShortcode(element,shortcode,value){element.find(".gfRepeater-shortcode-"+shortcode).each((function(){jQuery(this).html(value)}))}function gfRepeater_replaceShortcodes(element){var shortcodes=["count","buttons","add","remove"];jQuery.each(shortcodes,(function(key,shortcode){var html=element.html();element.html(html.replace("[gfRepeater-"+shortcode+"]",''))}))}function gfRepeater_repeatRepeater(formId,repeater2Id){var repeater2=gfRepeater_repeater2s[formId][repeater2Id],repeatId=repeater2.data.repeatCount+1;if(!(repeater2.settings.max&&repeater2.data.repeatCount>=repeater2.settings.max)){jQuery(repeater2.controllers.start).parents("form").trigger("gform_repeater2_before_repeat",[repeater2Id,repeatId]);var lastElement=gfRepeater_select(formId,repeater2Id).last();jQuery.each(repeater2.children,(function(key,value){var clonedElement=jQuery(this.element).clone();gfRepeater_resetInputs(formId,repeater2Id,key,clonedElement),gfRepeater_setRepeaterChildAttrs(formId,repeater2Id,clonedElement),clonedElement.insertAfter(lastElement),lastElement.find(".datepicker").removeClass("initialized"),lastElement=clonedElement})),gfRepeater_conditionalLogic_setAll(formId,repeater2Id,repeatId),repeater2.data.repeatCount+=1,gfRepeater_updateDataElement(formId,repeater2Id),gfRepeater_updateRepeaterControls(formId,repeater2Id),window.gformInitDatepicker&&gformInitDatepicker(),jQuery(repeater2.controllers.start).parents("form").trigger("gform_repeater2_after_repeat",[repeater2Id,repeatId])}}function gfRepeater_unrepeatRepeater(formId,repeater2Id,repeatId){var repeater2=gfRepeater_repeater2s[formId][repeater2Id];if(!(repeater2.data.repeatCount<=repeater2.settings.min)){if(!repeatId)var repeatId=repeater2.data.repeatCount;jQuery(repeater2.controllers.start).parents("form").trigger("gform_repeater2_before_unrepeat",[repeater2Id,repeatId]),jQuery.each(repeater2.children,(function(childId,value){gfRepeater_select(formId,repeater2Id,repeatId,childId).remove()})),repeater2.data.repeatCount-=1,gfRepeater_updateDataElement(formId,repeater2Id),gfRepeater_updateRepeaterControls(formId,repeater2Id),repeatId!==repeater2.data.repeatCount+1&&gfRepeater_resetRepeaterChildrenAttrs(formId,repeater2Id),jQuery(repeater2.controllers.start).parents("form").trigger("gform_repeater2_after_unrepeat",[repeater2Id,repeatId])}}function gfRepeater_repeatRepeaterTimes(formId,repeater2Id,timesX){if(!timesX)var timesX=1;for(i=0;icurrentRepeatCount)gfRepeater_repeatRepeaterTimes(formId,repeater2Id,timesY=timesX-currentRepeatCount);else if(timesX=repeater2.settings.max?jQuery(repeater2.controllers.add).hide():jQuery(repeater2.controllers.add).show()),repeater2.data.repeatCount<=repeater2.settings.min?jQuery(repeater2.controllers.remove).hide():jQuery(repeater2.controllers.remove).show()}function gfRepeater_resetInputs(formId,repeater2Id,childId,repeater2ChildElement){var repeater2=gfRepeater_repeater2s[formId][repeater2Id];jQuery.each(repeater2.children[childId].inputs,(function(key,value){var inputId=this.id,inputName=this.name,inputDefaultValue=this.defaultValue,inputElement=gfRepeater_findElementByNameOrId(repeater2ChildElement,inputName,inputId);inputElement&&gfRepeater_setInputValue(inputElement,inputDefaultValue)}))}function gfRepeater_select(formId,repeater2Id,repeatId,childId,inputId){var selector="div#gform_wrapper_"+formId+">form#gform_"+formId;return(repeater2Id||repeatId||childId||inputId)&&(selector+=">.gform_body .gform_fields>.gfield.gf_repeater2_child_field"),repeater2Id&&(selector+="[data-repeater2-parentid="+repeater2Id+"]"),repeatId&&(selector+="[data-repeater2-repeatid="+repeatId+"]"),childId&&(selector+="[data-repeater2-childid="+childId+"]"),inputId&&(selector+="*"==inputId?" [data-repeater2-inputid]":" [data-repeater2-inputid="+inputId+"]"),jQuery(selector)}function gfRepeater_findElementByNameOrId(searchElement,elementName,elementId){if(elementName)var foundElement=jQuery(searchElement).find("[name^='"+elementName+"']");if(!foundElement&&elementId)var foundElement=jQuery(searchElement).find("[id^='"+elementId+"']");return foundElement||!1}function gfRepeater_getIndex(object,key,value,multiple){var keys=[];return jQuery.each(object,(function(fieldKey,fieldValue){if(fieldValue[key]==value&&(keys.push(fieldKey),!multiple))return!1})),!!keys.length&&(multiple?keys:keys[0])}function gfRepeater_getChoiceValue(fieldElement){var value=[];return jQuery(fieldElement).find(":checkbox, :radio").each((function(){if(1==jQuery(this).prop("checked")){var id=this.id,label=jQuery(this).siblings("label").first().text();value.push(label)}})),value}function gfRepeater_getInputValue(inputElement){return inputElement.is(":checkbox, :radio")?1==inputElement.prop("checked"):inputElement.val()}function gfRepeater_setInputValue(inputElement,inputValue){inputElement.is(":checkbox, :radio")?"on"==inputValue||inputElement.prop("value")===inputValue?inputElement.prop("checked",!0):inputElement.prop("checked",!1):inputElement.val(inputValue)}function gfRepeater_updateDataElement(formId,repeater2Id){var repeater2=gfRepeater_repeater2s[formId][repeater2Id],dataElement=jQuery(repeater2.controllers.data),dataArray=jQuery(dataElement).val();dataArray&&(dataArray=JSON.parse(dataArray)),dataArray.repeater2Id=repeater2Id,dataArray.repeatCount=repeater2.data.repeatCount,jQuery.each(dataArray.children,(function(key,value){Array.isArray(this)&&(dataArray.children[key]={});var inputData=repeater2.data.inputData[key];inputData&&inputData.length&&(dataArray.children[key].inputs=inputData);var fieldIndex=gfRepeater_getIndex(repeater2.children,"idNum",key)})),dataArray=JSON.stringify(dataArray),jQuery(dataElement).val(dataArray)}function gfRepeater_start(){jQuery.each(gfRepeater_repeater2s,(function(key,repeater2){var formId=key,form=gfRepeater_select(formId);jQuery.each(repeater2,(function(key,value){var repeater2Id=key,repeater2=gfRepeater_repeater2s[formId][repeater2Id],repeatCount=repeater2.settings.start,paremCount=repeater2.data.paremCount;repeater2.controllers.data.attr("data-required")&&repeater2.controllers.start.addClass("gfield_contains_required"),jQuery.each(repeater2.children,(function(key,value){gfRepeater_setRepeaterChildAttrs(formId,repeater2Id,jQuery(repeater2.children[key].element),1),this.conditionalLogic&&gfRepeater_conditionalLogic_set(formId,repeater2Id,key,1)})),gfRepeater_submitted?repeatCount=repeater2.data.prevRepeatCount:paremCount>repeatCount&&(repeatCount=paremCount),gfRepeater_setRepeater(formId,repeater2Id,repeatCount),gfRepeater_updateRepeaterControls(formId,repeater2Id),gfRepeater_updateDataElement(formId,repeater2Id)})),jQuery(form).trigger("gform_repeater2_init_done")})),window.gformInitDatepicker&&gformInitDatepicker()}jQuery(document).bind("gform_post_render",(function(){gfRepeater_repeater2s_is_set||(gfRepeater_getRepeaters()?(gfRepeater_start(),jQuery(window).trigger("gform_repeater2_init_done")):console.log("There was an error with one of your repeater2s. This is usually caused by forgetting to include a repeater2-end field or by trying to nest repeater2s."),gfRepeater_repeater2s_is_set=!0)}));
--------------------------------------------------------------------------------
/js/jquery.postcapture.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery Post Capture - v0.0.1 - 7/2/2014
2 | * https://github.com/ssut/jQuery-PostCapture
3 | * Copyright (c) 2014 ssut (SuHun Han); Licensed BSD 3-Clause */
4 |
5 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(window.jQuery)}(function(a){function b(a){return i.raw?a:encodeURIComponent(a)}function c(a){return i.raw?a:decodeURIComponent(a)}function d(a){return b(i.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(h," ")),i.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=i.raw?b:e(b);return a.isFunction(c)?c(d):d}function g(a){("/"===a.slice(0,1)||"http:"!==a.slice(0,5)&&"https:"!==a.slice(0,6))&&(current=g(location.href),a=current.protocol+"//"+current.host+a);var b=a.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/);return b&&{protocol:b[1],host:b[2],hostname:b[3],port:b[4],pathname:b[5],search:b[6],hash:b[7]}}var h=/\+/g,i=a.cookie=function(e,g,h){if(void 0!==g&&!a.isFunction(g)){if(h=a.extend({},i.defaults,h),"number"==typeof h.expires){var j=h.expires,k=h.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),h.expires?"; expires="+h.expires.toUTCString():"",h.path?"; path="+h.path:"",h.domain?"; domain="+h.domain:"",h.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};i.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))};var j=function(){var a={};this.set=function(b,c){b=b.split(",");for(var d=0;d-1?(c.hasOwnProperty(i)||(c[i]=[]),f.is(":checked")&&c[i].push(n)):f.is(":checked")&&(c[i]="on");else if("radio"==k)f.is(":checked")&&(c[i]=""!==n?n:"on");else if("file"==k){if(c.hasOwnProperty(i)||(c[i]=[]),""!==n)if(void 0!==h.files)for(var o=0;o
2 |
3 | WordPress Coding Standards ruleset
4 |
5 | .
6 |
7 |
8 | *.js
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 0
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Gravity Forms Repeater Add-On
2 | A Gravity Forms add-on that allows specified groups of fields to be repeated by the user.
3 |
4 |
5 | ### Supported Fields
6 | * Address
7 | * Checkboxes
8 | * Date
9 | * Drop Down
10 | * Email
11 | * Hidden
12 | * HTML
13 | * MultiSelect
14 | * Name
15 | * Number
16 | * Paragraph Text
17 | * Phone
18 | * Radio
19 | * Section
20 | * Single Line Text
21 | * Time
22 | * Website
23 |
24 | ### Features
25 | * Repeat groups of fields multiple times
26 | * Use multiple repeaters on the same form
27 | * Use shortcodes to display data to the user
28 | * Use Javascript to manipulate the repeater
29 | * Customize the add and remove button's HTML
30 | * Use Gravity Forms pre-populate hooks and filters like normal
31 | * Supports Conditional Logic!
32 |
33 | ### Issues
34 | * Not all fields are currently supported.
35 | * Ajax enabled forms are not yet supported. (Ajax will be automatically disabled on forms with repeaters)
36 |
37 | ### Shortcodes
38 | You can place shortcodes inside of input labels, input descriptions, and HTML blocks!
39 | * [gfRepeater-count] - Will output the current repeat number.
40 | * [gfRepeater-buttons] - Will output both the '+' and '-' buttons.
41 | * [gfRepeater-add] - Will output the '+' button.
42 | * [gfRepeater-remove] - Will output the '-' button.
43 |
44 | ### CSS Classes
45 | You can use these CSS classes in the in the "Custom CSS Class" setting to do different things.
46 | * gf_repeater2_hide_add - Will hide the '+' button if placed in the repeater end css setting.
47 | * gf_repeater2_hide_remove - Will hide the '-' button if placed in the repeater end css setting.
48 |
49 | ### Javascript
50 | ##### Functions
51 | You can use Javascript to manipulate the repeater.
52 | * formId is your form's Id number assigned by Gravity Forms.
53 | * repeater2Id will depened on how many repeaters you have in your form. (The first repeater's ID is 1, second is 2, etc.)
54 | * repeater2ChildId works the same way and depends on how many times the repeater has been repeated.
55 | * These functions will not allow you to repeat more than the set max and unrepeat more than the set min.
56 | #
57 | ```
58 | gfRepeater_repeatRepeater(formId, repeater2Id);
59 | Repeats the repeater once.
60 | ```
61 | ```
62 | gfRepeater_unrepeatRepeater(formId, repeater2Id, repeater2ChildId);
63 | Un-repeats the repeater once. repeater2ChildId is optional.
64 | ```
65 | ```
66 | gfRepeater_repeatRepeaterTimes(formId, repeater2Id, timesX);
67 | Repeats the repeater a number of times.
68 | ```
69 | ```
70 | gfRepeater_unrepeatRepeaterTimes(formId, repeater2Id, timesX);
71 | UnRepeats the repeater a number of times.
72 | ```
73 | ```
74 | gfRepeater_setRepeater(formId, repeater2Id, timesX);
75 | Repeats or unrepeats the repeater to get it to whatever timesX is.
76 | ```
77 | ```
78 | gfRepeater_select(formId, repeater2Id, repeatId, childId, inputId);
79 | Selects an element depending on the variables passed. All variables are optional besides formId. inputId also accepts '*' to select all inputs.
80 | ```
81 |
82 | ##### Triggers
83 | These triggers are assigned to the form will be fired during different repeater related events with the repeater2Id and repeater2ChildId attached to them.
84 | ```
85 | gform_repeater2_before_repeat - Fires right before a repeater is about to be repeated.
86 | gform_repeater2_after_repeat - Fires right after a repeater has been repeated.
87 | gform_repeater2_before_unrepeat - Fires right before a repeater is about to be un-repeated.
88 | gform_repeater2_after_unrepeat - Fires right after a repeater has been un-repeated.
89 | gform_repeater2_init_done - Fires after the repeater is done setting up. Also fires on the window when all repeaters are done setting up.
90 | ```
91 |
92 | ##### Information
93 | You can access all repeater information using the object `gfRepeater_repeater2s`.
94 | ```
95 | gfRepeater_repeater2s
96 | [formId]
97 | [repeater2Id]
98 | ['data'] - Contains different data to keep track of.
99 | ['repeatCount'] - The number of times the repeater has been repeated.
100 | ['prevRepeatCount'] - If the form has already been submitted and failed validation, this will contain the repeatCount from before the from was submitted, otherwise it will be null.
101 | ['childrenCount'] - The number of children fields that get cloned everytime the repeater is repeated.
102 | ['paremCount'] - The highest field ID that has had their prepopulate paremeter set.
103 | ['tabIndex'] - The tabindex that was assigned to the repeater start field and will be assigned to all children.
104 | ['inputData'] - Contains an array with all of the input names in the repeater.
105 | ['settings'] - Contains the different settings that are set in the form editor.
106 | ['start'] - The number of times the repeater should be repeated when the form is loaded.
107 | ['min'] - The minimum number of times the repeater can be repeated.
108 | ['max'] - The maximum number of times the repeater can be repeated.
109 | ['controllers'] - Contains different elements that I like to call controllers.
110 | ['add'] - The add button.
111 | ['remove'] - The remove button.
112 | ['data'] - The hidden element that stores data to be passed to PHP.
113 | ['start'] - The start of the repeater.
114 | ['end'] - The end of the repeater.
115 | ['children'] - Contains the children fields that get cloned everytime the repeater is repeated.
116 | [childId]
117 | ['element'] - The field element.
118 | ['id'] - The field HTML id.
119 | ['idNum'] - The field ID number.
120 | ['inputCount'] - The number of inputs the child contains.
121 | ['inputMask'] - The field's input mask.
122 | ['required'] - True or False depeneding on if the field is required or not.
123 | ['type'] - The field type.
124 | ['inputs'] - Contains all of the inputs that the child contains.
125 | [inputId]
126 | ['element'] - The input element.
127 | ['id'] - The input HTML id.
128 | ['name'] - The input name.
129 | ['defaultValue'] - The default value for the input.
130 | ['prePopulate'] - Array containing prepopulate values.
131 | ```
132 |
133 | ##### Usage Examples
134 | Repeat the repeater a number of times depending on the value of a drop down field:
135 | ```
136 | jQuery('#gform_6 #input_6_7').change(function(){
137 | var attendees = jQuery(this).val();
138 | gfRepeater_setRepeater(6, 1, attendees);
139 | });
140 | ```
141 |
142 | Change the value of a field if the repeater is repeated or un-repeated:
143 | ```
144 | jQuery('#gform_9').on('gform_repeater2_after_repeat gform_repeater2_after_unrepeat', function(event, repeater2Id, repeatId){
145 | if (repeater2Id == 1) {
146 | var repeatCount = gfRepeater_repeater2s[9][1]['data']['repeatCount'];
147 | var totalPrice = 27.47 * repeatCount;
148 | jQuery('#gform_9 #input_9_4').val('$'+totalPrice);
149 | }
150 | });
151 | ```
152 |
153 |
154 | ### Prepopulate Fields
155 | You can set Parameter Names to prepopulate repeated fields like usual with the added ability to specify which repeated set will be prepopulated. For example, let's say we have set the parameter name for one of our repeated fields to "parem":
156 |
157 | `?param=hello+world` will result in that field being set to "hello world" regardless of how many times it has been repeated.
158 |
159 | `?param3=hello+world` will result in that field being set to "hello world" only when repeated a third time.
160 |
161 | You can use filters as well!
162 |
163 | ```
164 | add_filter( 'gform_field_value_parem', 'your_function_name' );
165 | ```
166 |
167 | ```
168 | add_filter( 'gform_field_value_parem3', 'your_function_name' );
169 | ```
170 |
171 | Also, setting the prepopulate parameter on the repeater start field will override the `start` setting.
172 |
173 |
174 | ### Frequently Asked Questions
175 | ##### Can I use multiple repeaters in one form?
176 | Yes!
177 |
178 | ##### Can I nest repeaters?
179 | Nesting repeaters is not supported.
180 |
181 | ##### Can I change the `+` and `-` buttons to text links?
182 | Yes! Just go to the form editor and change the `Add HTML` and `Remove HTML` settings to `Your Link Text` and they should appear as regular links on your form!
183 |
184 | ### Installation & Update
185 |
186 | ##### Method 1 (Automatic Updates)
187 | It is possible to keep this version up to date automatically by using [Andy Fragen](https://github.com/afragen)'s [GitHub Updater](https://github.com/afragen/github-updater)!
188 |
189 | 1. Download & Install [GitHub Updater](https://github.com/afragen/github-updater).
190 | 2. Go to Settings -> GitHub Updater.
191 | 3. Enable Branch Switching.
192 | 4. Go to Plugins.
193 |
194 | and that's it! You will receive the latest version of this plugin!
195 |
196 | ##### Method 2 (Manual)
197 |
198 | 1. Deactivate & Uninstall current version of Gravity Forms Repeater Add-On.
199 | 2. Download the [latest version](https://github.com/wolffe/repeater2-for-gravity-forms/releases).
200 | 3. Go to Plugins.
201 | 4. Select "Add New".
202 | 5. Select "Upload Plugin".
203 | 6. Select "Choose File" and find the zip file that you downloaded in step 2.
204 | 7. Select "Install Now".
205 |
206 | and you're good to go!
207 |
208 | ### Requirements
209 | * Wordpress 5.5 or later
210 | * Gravity Forms 2.5 or later
211 |
212 | ### Installation
213 | 1. Upload the `repeater2-add-on-for-gravity-forms` folder to the `/wp-content/plugins/` directory.
214 | 1. Activate the plugin through the 'Plugins' menu in WordPress.
215 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === Repeater2 for Gravity Forms ===
2 | Contributors: butterflymedia
3 | Tags: gravityforms, gravity, forms, form, gravityforms, repeater, repeater2, repeat, duplicate, duplication, field, fields
4 | Requires at least: 5.5
5 | Tested up to: 6.1.1
6 | Stable tag: 2.1.1
7 |
8 | A Gravity Forms add-on that allows specified groups of fields to be repeated by the user.
9 |
10 | == Description ==
11 |
12 | A Gravity Forms add-on that allows specified groups of fields to be repeated by the user.
13 |
--------------------------------------------------------------------------------
/repeater2-for-gravity-forms.php:
--------------------------------------------------------------------------------
1 | 'gf_repeater2_js_admin',
34 | 'src' => $this->get_base_url() . '/js/gf-repeater2-admin.js',
35 | 'version' => $this->_version,
36 | 'deps' => [ 'jquery' ],
37 | 'in_footer' => false,
38 | 'callback' => [ $this, 'localize_scripts' ],
39 | 'strings' => [ 'page' => rgget( 'page' ) ],
40 | 'enqueue' => [
41 | [
42 | 'admin_page' => [ 'form_editor', 'entry_view', 'entry_detail' ]
43 | ],
44 | ],
45 | ],
46 | ];
47 |
48 | return array_merge( parent::scripts(), $scripts );
49 | }
50 |
51 | public function init_admin() {
52 | parent::init_admin();
53 |
54 | GF_Field_Repeater2::init_admin();
55 | GF_Field_Repeater2_End::init_admin();
56 | }
57 |
58 | public function init_frontend() {
59 | parent::init_frontend();
60 |
61 | GF_Field_Repeater2::init_frontend();
62 | }
63 | }
64 |
65 | new GFRepeater();
66 |
67 | require_once 'class-gf-field-repeater2.php';
68 | require_once 'class-gf-field-repeater2-end.php';
69 | }
70 |
--------------------------------------------------------------------------------