PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.4.2
Secure Custom Fields v6.4.2
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / assets / src / js / _acf-validation.js
secure-custom-fields / assets / src / js Last commit date
pro 1 year ago _acf-compatibility.js 1 year ago _acf-condition-types.js 1 year ago _acf-condition.js 1 year ago _acf-conditions.js 1 year ago _acf-field-accordion.js 1 year ago _acf-field-button-group.js 1 year ago _acf-field-checkbox.js 1 year ago _acf-field-color-picker.js 1 year ago _acf-field-date-picker.js 1 year ago _acf-field-date-time-picker.js 1 year ago _acf-field-file.js 1 year ago _acf-field-google-map.js 1 year ago _acf-field-icon-picker.js 1 year ago _acf-field-image.js 1 year ago _acf-field-link.js 1 year ago _acf-field-oembed.js 1 year ago _acf-field-page-link.js 1 year ago _acf-field-post-object.js 1 year ago _acf-field-radio.js 1 year ago _acf-field-range.js 1 year ago _acf-field-relationship.js 1 year ago _acf-field-select.js 1 year ago _acf-field-tab.js 1 year ago _acf-field-taxonomy.js 1 year ago _acf-field-time-picker.js 1 year ago _acf-field-true-false.js 1 year ago _acf-field-url.js 1 year ago _acf-field-user.js 1 year ago _acf-field-wysiwyg.js 1 year ago _acf-field.js 1 year ago _acf-fields.js 1 year ago _acf-helpers.js 1 year ago _acf-hooks.js 1 year ago _acf-internal-post-type.js 1 year ago _acf-media.js 1 year ago _acf-modal.js 1 year ago _acf-model.js 1 year ago _acf-notice.js 1 year ago _acf-panel.js 1 year ago _acf-popup.js 1 year ago _acf-postbox.js 1 year ago _acf-screen.js 1 year ago _acf-select2.js 1 year ago _acf-tinymce.js 1 year ago _acf-tooltip.js 1 year ago _acf-unload.js 1 year ago _acf-validation.js 1 year ago _acf.js 1 year ago _browse-fields-modal.js 1 year ago _field-group-compatibility.js 1 year ago _field-group-conditions.js 1 year ago _field-group-field.js 1 year ago _field-group-fields.js 1 year ago _field-group-locations.js 1 year ago _field-group-settings.js 1 year ago _field-group.js 1 year ago acf-escaped-html-notice.js 1 year ago acf-field-group.js 1 year ago acf-input.js 1 year ago acf-internal-post-type.js 1 year ago acf.js 1 year ago
_acf-validation.js
1163 lines
1 ( function ( $, undefined ) {
2 /**
3 * Validator
4 *
5 * The model for validating forms
6 *
7 * @date 4/9/18
8 * @since ACF 5.7.5
9 *
10 * @param void
11 * @return void
12 */
13 var Validator = acf.Model.extend( {
14 /** @var string The model identifier. */
15 id: 'Validator',
16
17 /** @var object The model data. */
18 data: {
19 /** @var array The form errors. */
20 errors: [],
21
22 /** @var object The form notice. */
23 notice: null,
24
25 /** @var string The form status. loading, invalid, valid */
26 status: '',
27 },
28
29 /** @var object The model events. */
30 events: {
31 'changed:status': 'onChangeStatus',
32 },
33
34 /**
35 * addErrors
36 *
37 * Adds errors to the form.
38 *
39 * @date 4/9/18
40 * @since ACF 5.7.5
41 *
42 * @param array errors An array of errors.
43 * @return void
44 */
45 addErrors: function ( errors ) {
46 errors.map( this.addError, this );
47 },
48
49 /**
50 * addError
51 *
52 * Adds and error to the form.
53 *
54 * @date 4/9/18
55 * @since ACF 5.7.5
56 *
57 * @param object error An error object containing input and message.
58 * @return void
59 */
60 addError: function ( error ) {
61 this.data.errors.push( error );
62 },
63
64 /**
65 * hasErrors
66 *
67 * Returns true if the form has errors.
68 *
69 * @date 4/9/18
70 * @since ACF 5.7.5
71 *
72 * @param void
73 * @return bool
74 */
75 hasErrors: function () {
76 return this.data.errors.length;
77 },
78
79 /**
80 * clearErrors
81 *
82 * Removes any errors.
83 *
84 * @date 4/9/18
85 * @since ACF 5.7.5
86 *
87 * @param void
88 * @return void
89 */
90 clearErrors: function () {
91 return ( this.data.errors = [] );
92 },
93
94 /**
95 * getErrors
96 *
97 * Returns the forms errors.
98 *
99 * @date 4/9/18
100 * @since ACF 5.7.5
101 *
102 * @param void
103 * @return array
104 */
105 getErrors: function () {
106 return this.data.errors;
107 },
108
109 /**
110 * getFieldErrors
111 *
112 * Returns the forms field errors.
113 *
114 * @date 4/9/18
115 * @since ACF 5.7.5
116 *
117 * @param void
118 * @return array
119 */
120 getFieldErrors: function () {
121 // vars
122 var errors = [];
123 var inputs = [];
124
125 // loop
126 this.getErrors().map( function ( error ) {
127 // bail early if global
128 if ( ! error.input ) return;
129
130 // update if exists
131 var i = inputs.indexOf( error.input );
132 if ( i > -1 ) {
133 errors[ i ] = error;
134
135 // update
136 } else {
137 errors.push( error );
138 inputs.push( error.input );
139 }
140 } );
141
142 // return
143 return errors;
144 },
145
146 /**
147 * getGlobalErrors
148 *
149 * Returns the forms global errors (errors without a specific input).
150 *
151 * @date 4/9/18
152 * @since ACF 5.7.5
153 *
154 * @param void
155 * @return array
156 */
157 getGlobalErrors: function () {
158 // return array of errors that contain no input
159 return this.getErrors().filter( function ( error ) {
160 return ! error.input;
161 } );
162 },
163
164 /**
165 * showErrors
166 *
167 * Displays all errors for this form.
168 *
169 * @since ACF 5.7.5
170 *
171 * @param {string} [location=before] - The location to add the error, before or after the input. Default before. Since 6.3.
172 * @return void
173 */
174 showErrors: function ( location = 'before' ) {
175 // bail early if no errors
176 if ( ! this.hasErrors() ) {
177 return;
178 }
179
180 // vars
181 var fieldErrors = this.getFieldErrors();
182 var globalErrors = this.getGlobalErrors();
183
184 // vars
185 var errorCount = 0;
186 var $scrollTo = false;
187
188 // loop
189 fieldErrors.map( function ( error ) {
190 // get input
191 var $input = this.$( '[name="' + error.input + '"]' ).first();
192
193 // if $_POST value was an array, this $input may not exist
194 if ( ! $input.length ) {
195 $input = this.$( '[name^="' + error.input + '"]' ).first();
196 }
197
198 // bail early if input doesn't exist
199 if ( ! $input.length ) {
200 return;
201 }
202
203 // increase
204 errorCount++;
205
206 // get field
207 var field = acf.getClosestField( $input );
208
209 // make sure the postbox containing this field is not hidden by screen options
210 ensureFieldPostBoxIsVisible( field.$el );
211
212 // show error
213 field.showError( error.message, location );
214
215 // set $scrollTo
216 if ( ! $scrollTo ) {
217 $scrollTo = field.$el;
218 }
219 }, this );
220
221 // errorMessage
222 var errorMessage = acf.__( 'Validation failed' );
223 globalErrors.map( function ( error ) {
224 errorMessage += '. ' + error.message;
225 } );
226 if ( errorCount == 1 ) {
227 errorMessage += '. ' + acf.__( '1 field requires attention' );
228 } else if ( errorCount > 1 ) {
229 errorMessage += '. ' + acf.__( '%d fields require attention' ).replace( '%d', errorCount );
230 }
231
232 // notice
233 if ( this.has( 'notice' ) ) {
234 this.get( 'notice' ).update( {
235 type: 'error',
236 text: errorMessage,
237 } );
238 } else {
239 var notice = acf.newNotice( {
240 type: 'error',
241 text: errorMessage,
242 target: this.$el,
243 } );
244 this.set( 'notice', notice );
245 }
246
247 // If in a modal, don't try to scroll.
248 if ( this.$el.parents( '.acf-popup-box' ).length ) {
249 return;
250 }
251
252 // if no $scrollTo, set to message
253 if ( ! $scrollTo ) {
254 $scrollTo = this.get( 'notice' ).$el;
255 }
256
257 // timeout
258 setTimeout( function () {
259 $( 'html, body' ).animate(
260 {
261 scrollTop: $scrollTo.offset().top - $( window ).height() / 2,
262 },
263 500
264 );
265 }, 10 );
266 },
267
268 /**
269 * onChangeStatus
270 *
271 * Update the form class when changing the 'status' data
272 *
273 * @date 4/9/18
274 * @since ACF 5.7.5
275 *
276 * @param object e The event object.
277 * @param jQuery $el The form element.
278 * @param string value The new status.
279 * @param string prevValue The old status.
280 * @return void
281 */
282 onChangeStatus: function ( e, $el, value, prevValue ) {
283 this.$el.removeClass( 'is-' + prevValue ).addClass( 'is-' + value );
284 },
285
286 /**
287 * validate
288 *
289 * Vaildates the form via AJAX.
290 *
291 * @date 4/9/18
292 * @since ACF 5.7.5
293 *
294 * @param object args A list of settings to customize the validation process.
295 * @return bool True if the form is valid.
296 */
297 validate: function ( args ) {
298 // default args
299 args = acf.parseArgs( args, {
300 // trigger event
301 event: false,
302
303 // reset the form after submit
304 reset: false,
305
306 // loading callback
307 loading: function () {},
308
309 // complete callback
310 complete: function () {},
311
312 // failure callback
313 failure: function () {},
314
315 // success callback
316 success: function ( $form ) {
317 $form.submit();
318 },
319 } );
320
321 // return true if is valid - allows form submit
322 if ( this.get( 'status' ) == 'valid' ) {
323 return true;
324 }
325
326 // return false if is currently validating - prevents form submit
327 if ( this.get( 'status' ) == 'validating' ) {
328 return false;
329 }
330
331 // return true if no ACF fields exist (no need to validate)
332 if ( ! this.$( '.acf-field' ).length ) {
333 return true;
334 }
335
336 // if event is provided, create a new success callback.
337 if ( args.event ) {
338 var event = $.Event( null, args.event );
339 args.success = function () {
340 acf.enableSubmit( $( event.target ) ).trigger( event );
341 };
342 }
343
344 // action for 3rd party
345 acf.doAction( 'validation_begin', this.$el );
346
347 // lock form
348 acf.lockForm( this.$el );
349
350 // loading callback
351 args.loading( this.$el, this );
352
353 // update status
354 this.set( 'status', 'validating' );
355
356 // success callback
357 var onSuccess = function ( json ) {
358 // validate
359 if ( ! acf.isAjaxSuccess( json ) ) {
360 return;
361 }
362
363 // filter
364 var data = acf.applyFilters( 'validation_complete', json.data, this.$el, this );
365
366 // add errors
367 if ( ! data.valid ) {
368 this.addErrors( data.errors );
369 }
370 };
371
372 // complete
373 var onComplete = function () {
374 // unlock form
375 acf.unlockForm( this.$el );
376
377 // failure
378 if ( this.hasErrors() ) {
379 // update status
380 this.set( 'status', 'invalid' );
381
382 // action
383 acf.doAction( 'validation_failure', this.$el, this );
384
385 // display errors
386 this.showErrors();
387
388 // failure callback
389 args.failure( this.$el, this );
390
391 // success
392 } else {
393 // update status
394 this.set( 'status', 'valid' );
395
396 // remove previous error message
397 if ( this.has( 'notice' ) ) {
398 this.get( 'notice' ).update( {
399 type: 'success',
400 text: acf.__( 'Validation successful' ),
401 timeout: 1000,
402 } );
403 }
404
405 // action
406 acf.doAction( 'validation_success', this.$el, this );
407 acf.doAction( 'submit', this.$el );
408
409 // success callback (submit form)
410 args.success( this.$el, this );
411
412 // lock form
413 acf.lockForm( this.$el );
414
415 // reset
416 if ( args.reset ) {
417 this.reset();
418 }
419 }
420
421 // complete callback
422 args.complete( this.$el, this );
423
424 // clear errors
425 this.clearErrors();
426 };
427
428 // serialize form data
429 var data = acf.serialize( this.$el );
430 data.action = 'acf/validate_save_post';
431
432 // ajax
433 $.ajax( {
434 url: acf.get( 'ajaxurl' ),
435 data: acf.prepareForAjax( data, true ),
436 type: 'post',
437 dataType: 'json',
438 context: this,
439 success: onSuccess,
440 complete: onComplete,
441 } );
442
443 // return false to fail validation and allow AJAX
444 return false;
445 },
446
447 /**
448 * setup
449 *
450 * Called during the constructor function to setup this instance
451 *
452 * @date 4/9/18
453 * @since ACF 5.7.5
454 *
455 * @param jQuery $form The form element.
456 * @return void
457 */
458 setup: function ( $form ) {
459 // set $el
460 this.$el = $form;
461 },
462
463 /**
464 * reset
465 *
466 * Rests the validation to be used again.
467 *
468 * @date 6/9/18
469 * @since ACF 5.7.5
470 *
471 * @param void
472 * @return void
473 */
474 reset: function () {
475 // reset data
476 this.set( 'errors', [] );
477 this.set( 'notice', null );
478 this.set( 'status', '' );
479
480 // unlock form
481 acf.unlockForm( this.$el );
482 },
483 } );
484
485 /**
486 * getValidator
487 *
488 * Returns the instance for a given form element.
489 *
490 * @date 4/9/18
491 * @since ACF 5.7.5
492 *
493 * @param jQuery $el The form element.
494 * @return object
495 */
496 var getValidator = function ( $el ) {
497 // instantiate
498 var validator = $el.data( 'acf' );
499 if ( ! validator ) {
500 validator = new Validator( $el );
501 }
502
503 // return
504 return validator;
505 };
506
507 /**
508 * A helper function to generate a Validator for a block form, so .addErrors can be run via block logic.
509 *
510 * @since ACF 6.3
511 *
512 * @param $el The jQuery block form wrapper element.
513 * @return bool
514 */
515 acf.getBlockFormValidator = function ( $el ) {
516 return getValidator( $el );
517 };
518
519 /**
520 * A helper function for the Validator.validate() function.
521 * Returns true if form is valid, or fetches a validation request and returns false.
522 *
523 * @since ACF 5.6.9
524 *
525 * @param object args A list of settings to customize the validation process.
526 * @return bool
527 */
528 acf.validateForm = function ( args ) {
529 return getValidator( args.form ).validate( args );
530 };
531
532 /**
533 * acf.enableSubmit
534 *
535 * Enables a submit button and returns the element.
536 *
537 * @date 30/8/18
538 * @since ACF 5.7.4
539 *
540 * @param jQuery $submit The submit button.
541 * @return jQuery
542 */
543 acf.enableSubmit = function ( $submit ) {
544 return $submit.removeClass( 'disabled' ).removeAttr( 'disabled' );
545 };
546
547 /**
548 * acf.disableSubmit
549 *
550 * Disables a submit button and returns the element.
551 *
552 * @date 30/8/18
553 * @since ACF 5.7.4
554 *
555 * @param jQuery $submit The submit button.
556 * @return jQuery
557 */
558 acf.disableSubmit = function ( $submit ) {
559 return $submit.addClass( 'disabled' ).attr( 'disabled', true );
560 };
561
562 /**
563 * acf.showSpinner
564 *
565 * Shows the spinner element.
566 *
567 * @date 4/9/18
568 * @since ACF 5.7.5
569 *
570 * @param jQuery $spinner The spinner element.
571 * @return jQuery
572 */
573 acf.showSpinner = function ( $spinner ) {
574 $spinner.addClass( 'is-active' ); // add class (WP > 4.2)
575 $spinner.css( 'display', 'inline-block' ); // css (WP < 4.2)
576 return $spinner;
577 };
578
579 /**
580 * acf.hideSpinner
581 *
582 * Hides the spinner element.
583 *
584 * @date 4/9/18
585 * @since ACF 5.7.5
586 *
587 * @param jQuery $spinner The spinner element.
588 * @return jQuery
589 */
590 acf.hideSpinner = function ( $spinner ) {
591 $spinner.removeClass( 'is-active' ); // add class (WP > 4.2)
592 $spinner.css( 'display', 'none' ); // css (WP < 4.2)
593 return $spinner;
594 };
595
596 /**
597 * acf.lockForm
598 *
599 * Locks a form by disabeling its primary inputs and showing a spinner.
600 *
601 * @date 4/9/18
602 * @since ACF 5.7.5
603 *
604 * @param jQuery $form The form element.
605 * @return jQuery
606 */
607 acf.lockForm = function ( $form ) {
608 // vars
609 var $wrap = findSubmitWrap( $form );
610 var $submit = $wrap.find( '.button, [type="submit"]' ).not( '.acf-nav, .acf-repeater-add-row' );
611 var $spinner = $wrap.find( '.spinner, .acf-spinner' );
612
613 // hide all spinners (hides the preview spinner)
614 acf.hideSpinner( $spinner );
615
616 // lock
617 acf.disableSubmit( $submit );
618 acf.showSpinner( $spinner.last() );
619 return $form;
620 };
621
622 /**
623 * acf.unlockForm
624 *
625 * Unlocks a form by enabeling its primary inputs and hiding all spinners.
626 *
627 * @date 4/9/18
628 * @since ACF 5.7.5
629 *
630 * @param jQuery $form The form element.
631 * @return jQuery
632 */
633 acf.unlockForm = function ( $form ) {
634 // vars
635 var $wrap = findSubmitWrap( $form );
636 var $submit = $wrap.find( '.button, [type="submit"]' ).not( '.acf-nav, .acf-repeater-add-row' );
637 var $spinner = $wrap.find( '.spinner, .acf-spinner' );
638
639 // unlock
640 acf.enableSubmit( $submit );
641 acf.hideSpinner( $spinner );
642 return $form;
643 };
644
645 /**
646 * findSubmitWrap
647 *
648 * An internal function to find the 'primary' form submit wrapping element.
649 *
650 * @date 4/9/18
651 * @since ACF 5.7.5
652 *
653 * @param jQuery $form The form element.
654 * @return jQuery
655 */
656 var findSubmitWrap = function ( $form ) {
657 // default post submit div
658 var $wrap = $form.find( '#submitdiv' );
659 if ( $wrap.length ) {
660 return $wrap;
661 }
662
663 // 3rd party publish box
664 var $wrap = $form.find( '#submitpost' );
665 if ( $wrap.length ) {
666 return $wrap;
667 }
668
669 // term, user
670 var $wrap = $form.find( 'p.submit' ).last();
671 if ( $wrap.length ) {
672 return $wrap;
673 }
674
675 // front end form
676 var $wrap = $form.find( '.acf-form-submit' );
677 if ( $wrap.length ) {
678 return $wrap;
679 }
680
681 // ACF 6.2 options page modal
682 var $wrap = $( '#acf-create-options-page-form .acf-actions' );
683 if ( $wrap.length ) {
684 return $wrap;
685 }
686
687 // ACF 6.0+ headerbar submit
688 var $wrap = $( '.acf-headerbar-actions' );
689 if ( $wrap.length ) {
690 return $wrap;
691 }
692
693 // default
694 return $form;
695 };
696
697 /**
698 * A debounced function to trigger a form submission.
699 *
700 * @date 15/07/2020
701 * @since ACF 5.9.0
702 *
703 * @param type Var Description.
704 * @return type Description.
705 */
706 var submitFormDebounced = acf.debounce( function ( $form ) {
707 $form.submit();
708 } );
709
710 /**
711 * Ensure field is visible for validation errors
712 *
713 * @date 20/10/2021
714 * @since ACF 5.11.0
715 */
716 var ensureFieldPostBoxIsVisible = function ( $el ) {
717 // Find the postbox element containing this field.
718 var $postbox = $el.parents( '.acf-postbox' );
719 if ( $postbox.length ) {
720 var acf_postbox = acf.getPostbox( $postbox );
721 if ( acf_postbox && acf_postbox.isHiddenByScreenOptions() ) {
722 // Rather than using .show() here, we don't want the field to appear next reload.
723 // So just temporarily show the field group so validation can complete.
724 acf_postbox.$el.removeClass( 'hide-if-js' );
725 acf_postbox.$el.css( 'display', '' );
726 }
727 }
728 };
729
730 /**
731 * Ensure metaboxes which contain browser validation failures are visible.
732 *
733 * @date 20/10/2021
734 * @since ACF 5.11.0
735 */
736 var ensureInvalidFieldVisibility = function () {
737 // Load each ACF input field and check it's browser validation state.
738 var $inputs = $( '.acf-field input' );
739 $inputs.each( function () {
740 if ( ! this.checkValidity() ) {
741 // Field is invalid, so we need to make sure it's metabox is visible.
742 ensureFieldPostBoxIsVisible( $( this ) );
743 }
744 } );
745 };
746
747 /**
748 * acf.validation
749 *
750 * Global validation logic
751 *
752 * @date 4/4/18
753 * @since ACF 5.6.9
754 *
755 * @param void
756 * @return void
757 */
758
759 acf.validation = new acf.Model( {
760 /** @var string The model identifier. */
761 id: 'validation',
762
763 /** @var bool The active state. Set to false before 'prepare' to prevent validation. */
764 active: true,
765
766 /** @var string The model initialize time. */
767 wait: 'prepare',
768
769 /** @var object The model actions. */
770 actions: {
771 ready: 'addInputEvents',
772 append: 'addInputEvents',
773 },
774
775 /** @var object The model events. */
776 events: {
777 'click input[type="submit"]': 'onClickSubmit',
778 'click button[type="submit"]': 'onClickSubmit',
779 'click #save-post': 'onClickSave',
780 'submit form#post': 'onSubmitPost',
781 'submit form': 'onSubmit',
782 },
783
784 /**
785 * initialize
786 *
787 * Called when initializing the model.
788 *
789 * @date 4/9/18
790 * @since ACF 5.7.5
791 *
792 * @param void
793 * @return void
794 */
795 initialize: function () {
796 // check 'validation' setting
797 if ( ! acf.get( 'validation' ) ) {
798 this.active = false;
799 this.actions = {};
800 this.events = {};
801 }
802 },
803
804 /**
805 * enable
806 *
807 * Enables validation.
808 *
809 * @date 4/9/18
810 * @since ACF 5.7.5
811 *
812 * @param void
813 * @return void
814 */
815 enable: function () {
816 this.active = true;
817 },
818
819 /**
820 * disable
821 *
822 * Disables validation.
823 *
824 * @date 4/9/18
825 * @since ACF 5.7.5
826 *
827 * @param void
828 * @return void
829 */
830 disable: function () {
831 this.active = false;
832 },
833
834 /**
835 * reset
836 *
837 * Rests the form validation to be used again
838 *
839 * @date 6/9/18
840 * @since ACF 5.7.5
841 *
842 * @param jQuery $form The form element.
843 * @return void
844 */
845 reset: function ( $form ) {
846 getValidator( $form ).reset();
847 },
848
849 /**
850 * addInputEvents
851 *
852 * Adds 'invalid' event listeners to HTML inputs.
853 *
854 * @date 4/9/18
855 * @since ACF 5.7.5
856 *
857 * @param jQuery $el The element being added / readied.
858 * @return void
859 */
860 addInputEvents: function ( $el ) {
861 // Bug exists in Safari where custom "invalid" handling prevents draft from saving.
862 if ( acf.get( 'browser' ) === 'safari' ) return;
863
864 // vars
865 var $inputs = $( '.acf-field [name]', $el );
866
867 // check
868 if ( $inputs.length ) {
869 this.on( $inputs, 'invalid', 'onInvalid' );
870 }
871 },
872
873 /**
874 * onInvalid
875 *
876 * Callback for the 'invalid' event.
877 *
878 * @date 4/9/18
879 * @since ACF 5.7.5
880 *
881 * @param object e The event object.
882 * @param jQuery $el The input element.
883 * @return void
884 */
885 onInvalid: function ( e, $el ) {
886 // prevent default
887 // - prevents browser error message
888 // - also fixes chrome bug where 'hidden-by-tab' field throws focus error
889 e.preventDefault();
890
891 // vars
892 var $form = $el.closest( 'form' );
893
894 // check form exists
895 if ( $form.length ) {
896 // add error to validator
897 getValidator( $form ).addError( {
898 input: $el.attr( 'name' ),
899 message: acf.strEscape( e.target.validationMessage ),
900 } );
901
902 // trigger submit on $form
903 // - allows for "save", "preview" and "publish" to work
904 submitFormDebounced( $form );
905 }
906 },
907
908 /**
909 * onClickSubmit
910 *
911 * Callback when clicking submit.
912 *
913 * @date 4/9/18
914 * @since ACF 5.7.5
915 *
916 * @param object e The event object.
917 * @param jQuery $el The input element.
918 * @return void
919 */
920 onClickSubmit: function ( e, $el ) {
921 // Some browsers (safari) force their browser validation before our AJAX validation,
922 // so we need to make sure fields are visible earlier than showErrors()
923 ensureInvalidFieldVisibility();
924
925 // store the "click event" for later use in this.onSubmit()
926 this.set( 'originalEvent', e );
927 },
928
929 /**
930 * onClickSave
931 *
932 * Set ignore to true when saving a draft.
933 *
934 * @date 4/9/18
935 * @since ACF 5.7.5
936 *
937 * @param object e The event object.
938 * @param jQuery $el The input element.
939 * @return void
940 */
941 onClickSave: function ( e, $el ) {
942 this.set( 'ignore', true );
943 },
944
945 /**
946 * onSubmitPost
947 *
948 * Callback when the 'post' form is submit.
949 *
950 * @date 5/3/19
951 * @since ACF 5.7.13
952 *
953 * @param object e The event object.
954 * @param jQuery $el The input element.
955 * @return void
956 */
957 onSubmitPost: function ( e, $el ) {
958 // Check if is preview.
959 if ( $( 'input#wp-preview' ).val() === 'dopreview' ) {
960 // Ignore validation.
961 this.set( 'ignore', true );
962
963 // Unlock form to fix conflict with core "submit.edit-post" event causing all submit buttons to be disabled.
964 acf.unlockForm( $el );
965 }
966 },
967
968 /**
969 * onSubmit
970 *
971 * Callback when the form is submit.
972 *
973 * @date 4/9/18
974 * @since ACF 5.7.5
975 *
976 * @param object e The event object.
977 * @param jQuery $el The input element.
978 * @return void
979 */
980 onSubmit: function ( e, $el ) {
981 // Allow form to submit if...
982 if (
983 // Validation has been disabled.
984 ! this.active ||
985 // Or this event is to be ignored.
986 this.get( 'ignore' ) ||
987 // Or this event has already been prevented.
988 e.isDefaultPrevented()
989 ) {
990 // Return early and call reset function.
991 return this.allowSubmit();
992 }
993
994 // Validate form.
995 var valid = acf.validateForm( {
996 form: $el,
997 event: this.get( 'originalEvent' ),
998 } );
999
1000 // If not valid, stop event to prevent form submit.
1001 if ( ! valid ) {
1002 e.preventDefault();
1003 }
1004 },
1005
1006 /**
1007 * allowSubmit
1008 *
1009 * Resets data during onSubmit when the form is allowed to submit.
1010 *
1011 * @date 5/3/19
1012 * @since ACF 5.7.13
1013 *
1014 * @param void
1015 * @return void
1016 */
1017 allowSubmit: function () {
1018 // Reset "ignore" state.
1019 this.set( 'ignore', false );
1020
1021 // Reset "originalEvent" object.
1022 this.set( 'originalEvent', false );
1023
1024 // Return true
1025 return true;
1026 },
1027 } );
1028
1029 var gutenbergValidation = new acf.Model( {
1030 wait: 'prepare',
1031 initialize: function () {
1032 // Bail early if not Gutenberg.
1033 if ( ! acf.isGutenberg() ) {
1034 return;
1035 }
1036
1037 // Custommize the editor.
1038 this.customizeEditor();
1039 },
1040 customizeEditor: function () {
1041 // Extract vars.
1042 var editor = wp.data.dispatch( 'core/editor' );
1043 var editorSelect = wp.data.select( 'core/editor' );
1044 var notices = wp.data.dispatch( 'core/notices' );
1045
1046 // Backup original method.
1047 var savePost = editor.savePost;
1048
1049 // Listen for changes to post status and perform actions:
1050 // a) Enable validation for "publish" action.
1051 // b) Remember last non "publish" status used for restoring after validation fail.
1052 var useValidation = false;
1053 var lastPostStatus = '';
1054 wp.data.subscribe( function () {
1055 var postStatus = editorSelect.getEditedPostAttribute( 'status' );
1056 useValidation = postStatus === 'publish' || postStatus === 'future';
1057 lastPostStatus = postStatus !== 'publish' ? postStatus : lastPostStatus;
1058 } );
1059
1060 // Create validation version.
1061 editor.savePost = function ( options ) {
1062 options = options || {};
1063
1064 // Backup vars.
1065 var _this = this;
1066 var _args = arguments;
1067
1068 // Perform validation within a Promise.
1069 return new Promise( function ( resolve, reject ) {
1070 // Bail early if is autosave or preview.
1071 if ( options.isAutosave || options.isPreview ) {
1072 return resolve( 'Validation ignored (autosave).' );
1073 }
1074
1075 // Bail early if validation is not needed.
1076 if ( ! useValidation ) {
1077 return resolve( 'Validation ignored (draft).' );
1078 }
1079
1080 // Check if we've currently got an ACF block selected which is failing validation, but might not be presented yet.
1081 if ( 'undefined' !== typeof acf.blockInstances ) {
1082 const selectedBlockId = wp.data.select( 'core/block-editor' ).getSelectedBlockClientId();
1083
1084 if ( selectedBlockId && selectedBlockId in acf.blockInstances ) {
1085 const acfBlockState = acf.blockInstances[ selectedBlockId ];
1086
1087 if ( acfBlockState.validation_errors ) {
1088 // Deselect the block to show the error and lock the save.
1089 acf.debug(
1090 'Rejecting save because the block editor has a invalid ACF block selected.'
1091 );
1092 notices.createErrorNotice(
1093 acf.__( 'An ACF Block on this page requires attention before you can save.' ),
1094 {
1095 id: 'acf-validation',
1096 isDismissible: true,
1097 }
1098 );
1099
1100 wp.data.dispatch( 'core/editor' ).lockPostSaving( 'acf/block/' + selectedBlockId );
1101 wp.data.dispatch( 'core/block-editor' ).selectBlock( false );
1102
1103 return reject( 'ACF Validation failed for selected block.' );
1104 }
1105 }
1106 }
1107
1108 // Validate the editor form.
1109 var valid = acf.validateForm( {
1110 form: $( '#editor' ),
1111 reset: true,
1112 complete: function ( $form, validator ) {
1113 // Always unlock the form after AJAX.
1114 editor.unlockPostSaving( 'acf' );
1115 },
1116 failure: function ( $form, validator ) {
1117 // Get validation error and append to Gutenberg notices.
1118 var notice = validator.get( 'notice' );
1119 notices.createErrorNotice( notice.get( 'text' ), {
1120 id: 'acf-validation',
1121 isDismissible: true,
1122 } );
1123 notice.remove();
1124
1125 // Restore last non "publish" status.
1126 if ( lastPostStatus ) {
1127 editor.editPost( {
1128 status: lastPostStatus,
1129 } );
1130 }
1131
1132 // Rejext promise and prevent savePost().
1133 reject( 'Validation failed.' );
1134 },
1135 success: function () {
1136 notices.removeNotice( 'acf-validation' );
1137
1138 // Resolve promise and allow savePost().
1139 resolve( 'Validation success.' );
1140 },
1141 } );
1142
1143 // Resolve promise and allow savePost() if no validation is needed.
1144 if ( valid ) {
1145 resolve( 'Validation bypassed.' );
1146
1147 // Otherwise, lock the form and wait for AJAX response.
1148 } else {
1149 editor.lockPostSaving( 'acf' );
1150 }
1151 } ).then(
1152 function () {
1153 return savePost.apply( _this, _args );
1154 },
1155 ( err ) => {
1156 // Nothing to do here, user is alerted of validation issues.
1157 }
1158 );
1159 };
1160 },
1161 } );
1162 } )( jQuery );
1163